Merge "Stop using SiteConfiguration::isLocalVHost()"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Tue, 16 Feb 2016 23:19:38 +0000 (23:19 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Tue, 16 Feb 2016 23:19:38 +0000 (23:19 +0000)
783 files changed:
.jshintrc
.travis.yml
Gemfile
Gemfile.lock
INSTALL
RELEASE-NOTES-1.27
autoload.php
composer.json
docs/contenthandler.txt
docs/hooks.txt
images/.htaccess
includes/DefaultSettings.php
includes/EditPage.php
includes/EventRelayerGroup.php [new file with mode: 0644]
includes/GlobalFunctions.php
includes/HttpFunctions.php
includes/LinkTarget.php [new file with mode: 0644]
includes/Linker.php
includes/MediaWiki.php
includes/MergeHistory.php [new file with mode: 0644]
includes/Message.php
includes/OutputPage.php
includes/PHPVersionCheck.php
includes/PageProps.php
includes/PrefixSearch.php
includes/Revision.php
includes/Setup.php
includes/Title.php
includes/WebResponse.php
includes/WebStart.php
includes/actions/EditAction.php
includes/actions/HistoryAction.php
includes/actions/InfoAction.php
includes/api/ApiBase.php
includes/api/ApiDelete.php
includes/api/ApiEditPage.php
includes/api/ApiFormatBase.php
includes/api/ApiFormatJson.php
includes/api/ApiFormatPhp.php
includes/api/ApiFormatXml.php
includes/api/ApiHelp.php
includes/api/ApiMain.php
includes/api/ApiMergeHistory.php [new file with mode: 0644]
includes/api/ApiMessage.php
includes/api/ApiOpenSearch.php
includes/api/ApiPageSet.php
includes/api/ApiParamInfo.php
includes/api/ApiQueryInfo.php
includes/api/ApiQueryPageProps.php
includes/api/ApiQueryPrefixSearch.php
includes/api/ApiRollback.php
includes/api/ApiTag.php
includes/api/ApiUpload.php
includes/api/i18n/de.json
includes/api/i18n/en.json
includes/api/i18n/es.json
includes/api/i18n/fi.json
includes/api/i18n/fr.json
includes/api/i18n/gl.json
includes/api/i18n/he.json
includes/api/i18n/it.json
includes/api/i18n/ja.json
includes/api/i18n/lb.json
includes/api/i18n/nap.json
includes/api/i18n/qqq.json
includes/api/i18n/ru.json
includes/api/i18n/sh.json [new file with mode: 0644]
includes/api/i18n/sk.json [new file with mode: 0644]
includes/api/i18n/sr-el.json
includes/api/i18n/tr.json
includes/api/i18n/zh-hans.json
includes/cache/LinkBatch.php
includes/cache/LinkCache.php
includes/cache/MessageCache.php
includes/changes/ChangesList.php
includes/changes/RecentChange.php
includes/changetags/ChangeTags.php
includes/changetags/ChangeTagsLogItem.php
includes/changetags/ChangeTagsRevisionItem.php
includes/content/ContentHandler.php
includes/context/RequestContext.php
includes/db/Database.php
includes/db/DatabaseMysqlBase.php
includes/db/DatabaseSqlite.php
includes/debug/logger/LegacyLogger.php
includes/debug/logger/monolog/AvroFormatter.php
includes/debug/logger/monolog/WikiProcessor.php
includes/deferred/CdnCacheUpdate.php
includes/deferred/LinksUpdate.php
includes/diff/DiffFormatter.php
includes/diff/DifferenceEngine.php
includes/diff/TableDiffFormatter.php
includes/diff/UnifiedDiffFormatter.php
includes/filebackend/SwiftFileBackend.php
includes/filerepo/ForeignAPIRepo.php
includes/installer/CliInstaller.php
includes/installer/Installer.php
includes/installer/InstallerSessionProvider.php [new file with mode: 0644]
includes/installer/LocalSettingsGenerator.php
includes/installer/WebInstallerExistingWiki.php
includes/installer/i18n/ar.json
includes/installer/i18n/cs.json
includes/installer/i18n/en.json
includes/installer/i18n/eu.json
includes/installer/i18n/fa.json
includes/installer/i18n/fi.json
includes/installer/i18n/he.json
includes/installer/i18n/hu.json
includes/installer/i18n/is.json
includes/installer/i18n/lb.json
includes/installer/i18n/nap.json
includes/installer/i18n/nb.json
includes/installer/i18n/pl.json
includes/installer/i18n/ps.json
includes/installer/i18n/qqq.json
includes/installer/i18n/sh.json
includes/installer/i18n/tt-cyrl.json
includes/installer/i18n/war.json
includes/interwiki/Interwiki.php
includes/jobqueue/JobQueueDB.php
includes/jobqueue/JobQueueMemory.php
includes/jobqueue/JobQueueRedis.php
includes/jobqueue/jobs/PublishStashedFileJob.php
includes/json/FormatJson.php
includes/libs/CSSMin.php
includes/libs/ReplacementArray.php
includes/libs/objectcache/BagOStuff.php
includes/libs/objectcache/CachedBagOStuff.php [new file with mode: 0644]
includes/libs/objectcache/MemcachedClient.php
includes/logging/LogEventsList.php
includes/mail/EmailNotification.php
includes/mail/UserMailer.php
includes/media/Bitmap.php
includes/media/XMPValidate.php
includes/page/Article.php
includes/page/CategoryPage.php
includes/page/ImageHistoryList.php [new file with mode: 0644]
includes/page/ImageHistoryPseudoPager.php [new file with mode: 0644]
includes/page/ImagePage.php
includes/page/WikiPage.php
includes/parser/LinkHolderArray.php
includes/parser/Parser.php
includes/parser/ParserOptions.php
includes/parser/Preprocessor_DOM.php
includes/parser/Preprocessor_Hash.php
includes/resourceloader/ResourceLoader.php
includes/resourceloader/ResourceLoaderModule.php
includes/resourceloader/ResourceLoaderSkinModule.php
includes/resourceloader/ResourceLoaderStartUpModule.php
includes/revisiondelete/RevDelRevisionItem.php
includes/search/SearchEngine.php
includes/search/SearchSuggestion.php [new file with mode: 0644]
includes/search/SearchSuggestionSet.php [new file with mode: 0644]
includes/session/BotPasswordSessionProvider.php
includes/session/CookieSessionProvider.php
includes/session/MetadataMergeException.php [new file with mode: 0644]
includes/session/PHPSessionHandler.php
includes/session/Session.php
includes/session/SessionBackend.php
includes/session/SessionInfo.php
includes/session/SessionManager.php
includes/session/SessionProvider.php
includes/skins/BaseTemplate.php
includes/specialpage/QueryPage.php
includes/specialpage/SpecialPage.php
includes/specialpage/SpecialPageFactory.php
includes/specials/SpecialActiveusers.php
includes/specials/SpecialAllPages.php
includes/specials/SpecialApiSandbox.php [new file with mode: 0644]
includes/specials/SpecialChangeContentModel.php
includes/specials/SpecialContributions.php
includes/specials/SpecialExport.php
includes/specials/SpecialFileDuplicateSearch.php
includes/specials/SpecialLog.php
includes/specials/SpecialMergeHistory.php
includes/specials/SpecialMovepage.php
includes/specials/SpecialNewpages.php
includes/specials/SpecialPageLanguage.php
includes/specials/SpecialPrefixindex.php
includes/specials/SpecialRecentchangeslinked.php
includes/specials/SpecialUndelete.php
includes/specials/SpecialUpload.php
includes/specials/SpecialUserlogin.php
includes/specials/SpecialUserrights.php
includes/specials/SpecialVersion.php
includes/specials/SpecialWatchlist.php
includes/specials/SpecialWhatlinkshere.php
includes/title/MediaWikiPageLinkRenderer.php
includes/title/MediaWikiTitleCodec.php
includes/title/PageLinkRenderer.php
includes/title/TitleFormatter.php
includes/title/TitleValue.php
includes/user/BotPassword.php
includes/user/User.php
includes/utils/AutoloadGenerator.php
includes/utils/IP.php
includes/utils/MWCryptRand.php
jsduck.json
languages/FakeConverter.php
languages/Language.php
languages/LanguageConverter.php
languages/classes/LanguageGan.php
languages/classes/LanguageIu.php
languages/classes/LanguageKk.php
languages/classes/LanguageKu.php
languages/classes/LanguageShi.php
languages/classes/LanguageSr.php
languages/classes/LanguageUz.php
languages/classes/LanguageZh.php
languages/data/Names.php
languages/i18n/ar.json
languages/i18n/arq.json
languages/i18n/ast.json
languages/i18n/az.json
languages/i18n/azb.json
languages/i18n/ba.json
languages/i18n/be-tarask.json
languages/i18n/bg.json
languages/i18n/bn.json
languages/i18n/ca.json
languages/i18n/ce.json
languages/i18n/ckb.json
languages/i18n/crh-cyrl.json
languages/i18n/cs.json
languages/i18n/cu.json
languages/i18n/cv.json
languages/i18n/da.json
languages/i18n/de.json
languages/i18n/diq.json
languages/i18n/el.json
languages/i18n/en.json
languages/i18n/eo.json
languages/i18n/es.json
languages/i18n/et.json
languages/i18n/eu.json
languages/i18n/fa.json
languages/i18n/fi.json
languages/i18n/fr.json
languages/i18n/frr.json
languages/i18n/gd.json
languages/i18n/gl.json
languages/i18n/gom-deva.json
languages/i18n/gom-latn.json
languages/i18n/gsw.json
languages/i18n/gu.json
languages/i18n/hak.json
languages/i18n/he.json
languages/i18n/hi.json
languages/i18n/hr.json
languages/i18n/hu.json
languages/i18n/hy.json
languages/i18n/ia.json
languages/i18n/id.json
languages/i18n/ilo.json
languages/i18n/is.json
languages/i18n/it.json
languages/i18n/ja.json
languages/i18n/jam.json
languages/i18n/ka.json
languages/i18n/ko.json
languages/i18n/ksh.json
languages/i18n/ku-latn.json
languages/i18n/la.json
languages/i18n/lb.json
languages/i18n/lrc.json
languages/i18n/lt.json
languages/i18n/mai.json
languages/i18n/mk.json
languages/i18n/ml.json
languages/i18n/mn.json
languages/i18n/ms.json
languages/i18n/my.json
languages/i18n/nap.json
languages/i18n/nb.json
languages/i18n/nl.json
languages/i18n/nn.json
languages/i18n/or.json
languages/i18n/pl.json
languages/i18n/pms.json
languages/i18n/ps.json
languages/i18n/pt-br.json
languages/i18n/pt.json
languages/i18n/qqq.json
languages/i18n/ro.json
languages/i18n/roa-tara.json
languages/i18n/ru.json
languages/i18n/sah.json
languages/i18n/scn.json
languages/i18n/sh.json
languages/i18n/sk.json
languages/i18n/sl.json
languages/i18n/sr-ec.json
languages/i18n/sr-el.json
languages/i18n/su.json
languages/i18n/sv.json
languages/i18n/szl.json
languages/i18n/tcy.json
languages/i18n/th.json
languages/i18n/tl.json
languages/i18n/tr.json
languages/i18n/tt-cyrl.json
languages/i18n/tyv.json
languages/i18n/uk.json
languages/i18n/ur.json
languages/i18n/vi.json
languages/i18n/vo.json
languages/i18n/war.json
languages/i18n/wuu.json
languages/i18n/zh-hans.json
languages/i18n/zh-hant.json
languages/messages/MessagesEn.php
languages/messages/MessagesGom.php
languages/messages/MessagesGom_deva.php
languages/messages/MessagesLki.php [new file with mode: 0644]
maintenance/backup.inc
maintenance/cleanupTitles.php
maintenance/dumpIterator.php
maintenance/dumpTextPass.php
maintenance/findHooks.php
maintenance/importDump.php
maintenance/jsduck/custom_tags.rb
maintenance/namespaceDupes.php
maintenance/renderDump.php
maintenance/resources/update-oojs-ui.sh
maintenance/storage/checkStorage.php
maintenance/updateSearchIndex.php
mw-config/index.php
package.json
resources/Resources.php
resources/ResourcesOOUI.php
resources/lib/oojs-ui/i18n/cdo.json [new file with mode: 0644]
resources/lib/oojs-ui/i18n/eo.json
resources/lib/oojs-ui/i18n/hy.json
resources/lib/oojs-ui/i18n/it.json
resources/lib/oojs-ui/i18n/om.json
resources/lib/oojs-ui/i18n/sd.json
resources/lib/oojs-ui/i18n/sh.json
resources/lib/oojs-ui/i18n/sr-el.json
resources/lib/oojs-ui/i18n/xmf.json
resources/lib/oojs-ui/i18n/zh-hans.json
resources/lib/oojs-ui/oojs-ui-apex-noimages.css [deleted file]
resources/lib/oojs-ui/oojs-ui-apex.js
resources/lib/oojs-ui/oojs-ui-core-apex.css [new file with mode: 0644]
resources/lib/oojs-ui/oojs-ui-core-mediawiki.css [new file with mode: 0644]
resources/lib/oojs-ui/oojs-ui-core.js [new file with mode: 0644]
resources/lib/oojs-ui/oojs-ui-mediawiki-noimages.css [deleted file]
resources/lib/oojs-ui/oojs-ui-mediawiki.js
resources/lib/oojs-ui/oojs-ui-toolbars-apex.css [new file with mode: 0644]
resources/lib/oojs-ui/oojs-ui-toolbars-mediawiki.css [new file with mode: 0644]
resources/lib/oojs-ui/oojs-ui-toolbars.js [new file with mode: 0644]
resources/lib/oojs-ui/oojs-ui-widgets-apex.css [new file with mode: 0644]
resources/lib/oojs-ui/oojs-ui-widgets-mediawiki.css [new file with mode: 0644]
resources/lib/oojs-ui/oojs-ui-widgets.js [new file with mode: 0644]
resources/lib/oojs-ui/oojs-ui-windows-apex.css [new file with mode: 0644]
resources/lib/oojs-ui/oojs-ui-windows-mediawiki.css [new file with mode: 0644]
resources/lib/oojs-ui/oojs-ui-windows.js [new file with mode: 0644]
resources/lib/oojs-ui/oojs-ui.js [deleted file]
resources/lib/oojs-ui/themes/apex/icons-content.json
resources/lib/oojs-ui/themes/apex/icons-editing-advanced.json
resources/lib/oojs-ui/themes/apex/images/icons/attachment-ltr.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/attachment-ltr.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/attachment-rtl.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/attachment-rtl.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/language-ltr.png
resources/lib/oojs-ui/themes/apex/images/icons/language-ltr.svg
resources/lib/oojs-ui/themes/apex/images/icons/language-rtl.png
resources/lib/oojs-ui/themes/apex/images/icons/language-rtl.svg
resources/lib/oojs-ui/themes/apex/images/icons/upload-ltr.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/upload-ltr.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/upload-rtl.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/upload-rtl.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/icons-accessibility.json
resources/lib/oojs-ui/themes/mediawiki/icons-alerts.json
resources/lib/oojs-ui/themes/mediawiki/icons-content.json
resources/lib/oojs-ui/themes/mediawiki/icons-editing-advanced.json
resources/lib/oojs-ui/themes/mediawiki/icons-editing-core.json
resources/lib/oojs-ui/themes/mediawiki/icons-editing-list.json
resources/lib/oojs-ui/themes/mediawiki/icons-editing-styling.json
resources/lib/oojs-ui/themes/mediawiki/icons-interactions.json
resources/lib/oojs-ui/themes/mediawiki/icons-layout.json
resources/lib/oojs-ui/themes/mediawiki/icons-location.json
resources/lib/oojs-ui/themes/mediawiki/icons-media.json
resources/lib/oojs-ui/themes/mediawiki/icons-moderation.json
resources/lib/oojs-ui/themes/mediawiki/icons-movement.json
resources/lib/oojs-ui/themes/mediawiki/icons-wikimedia.json
resources/lib/oojs-ui/themes/mediawiki/icons.json
resources/lib/oojs-ui/themes/mediawiki/images/icons/add-constructive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/add-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/advanced-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/alert-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/alert-warning.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/align-center-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/align-float-left-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/align-float-right-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/arched-arrow-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/arched-arrow-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/arrow-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/arrow-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/article-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/article-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/articleCheck-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/articleCheck-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/articleSearch-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/articleSearch-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/attachment-ltr-invert.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/attachment-ltr-invert.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/attachment-ltr.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/attachment-ltr.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/attachment-rtl-invert.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/attachment-rtl-invert.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/attachment-rtl.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/attachment-rtl.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/bell-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bellOn-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bellOn-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/beta-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/betaLaunch-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bigger-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bigger-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/block-destructive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/block-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/blockUndo-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/blockUndo-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-a-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-arab-ain-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-arab-dad-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-armn-to-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-b-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-cyrl-be-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-cyrl-te-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-cyrl-zhe-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-f-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-g-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-geor-man-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-l-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-n-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bold-v-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/book-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/book-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bookmark-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bookmark-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bright-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/browser-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/browser-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/calendar-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/calendar-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/cancel-destructive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/cancel-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/caret-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/caret-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/caretDown-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/caretUp-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/case-sensitive-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/check-constructive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/check-destructive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/check-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/check-progressive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/circle-constructive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/circle-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/citeArticle-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/citeArticle-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/clear-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/clock-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/close-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/close-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/code-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/collapse-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/comment-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/die-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/die-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/downTriangle-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/download-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/download-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/edit-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/edit-ltr-progressive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/edit-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/edit-rtl-progressive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/editLock-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/editLock-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/editUndo-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/editUndo-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/ellipsis-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/expand-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/external-link-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/external-link-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/eye-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/eyeClosed-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/find-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/find-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/flag-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/flag-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/flagUndo-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/flagUndo-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/folderPlaceholder-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/folderPlaceholder-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/funnel-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/funnel-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/halfBright-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/heart-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/help-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/help-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/history-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/image-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/image-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/imageAdd-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/imageAdd-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/imageGallery-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/imageGallery-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/imageLock-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/imageLock-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/indent-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/indent-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/info-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-a-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-arab-keheh-jeem-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-arab-meem-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-armn-sha-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-c-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-d-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-e-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-geor-kan-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-i-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-k-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/italic-s-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/journal-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/journal-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/key-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/key-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/keyboard-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/keyboard-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/language-ltr-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/language-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/language-ltr.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/language-ltr.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/language-rtl-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/language-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/language-rtl.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/language-rtl.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/largerText-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/largerText-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/layout-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/layout-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/link-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/link-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/listBullet-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/listBullet-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/listNumbered-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/listNumbered-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/lock-ltr-destructive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/lock-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/lock-rtl-destructive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/lock-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/logOut-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/logOut-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/logo-cc-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/logo-wikimediaCommons-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/logo-wikipedia-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/map-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/map-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/mapPin-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/mapPinAdd-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/mapPinAdd-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/menu-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/message-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/message-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/moon-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/move-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/move-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/move-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/newWindow-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/newWindow-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/newline-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/newline-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/newspaper-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/newspaper-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/notBright-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/notice-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/ongoingConversation-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/ongoingConversation-ltr-progressive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/ongoingConversation-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/ongoingConversation-rtl-progressive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/outdent-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/outdent-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/outline-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/outline-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/play-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/play-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/printer-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/printer-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/puzzle-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/puzzle-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/quotes-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/quotes-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/quotesAdd-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/quotesAdd-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/regular-expression-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/ribbonPrize-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/search-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/search-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/secure-link-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/settings-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/signature-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/signature-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/smaller-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/smaller-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/smallerText-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/smallerText-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/specialCharacter-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubble-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubble-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbleAdd-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbleAdd-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbles-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/speechBubbles-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/star-constructive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/star-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/stop-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/strikethrough-a-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/strikethrough-s-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/strikethrough-y-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeFlow-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeFlow-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeSideMenu-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeSummary-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeSummary-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeToC-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeToC-ltr-progressive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeToC-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/stripeToC-rtl-progressive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/subscript-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/subscript-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/sun-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/sun-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/superscript-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/superscript-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/table-caption-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-column-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-column-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-row-after-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/table-insert-row-before-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/table-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/table-merge-cells-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/tag-constructive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/tag-destructive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/tag-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/tag-progressive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/tag-warning.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/templateAdd-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/templateAdd-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/text-dir-lefttoright-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/text-dir-righttoleft-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/text-style-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/trash-destructive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/trash-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/trashUndo-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/trashUndo-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/unLock-ltr-destructive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/unLock-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/unLock-rtl-destructive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/unLock-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/unStar-constructive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/unStar-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/underline-a-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/underline-u-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/upTriangle-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/upload-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/upload-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/viewCompact-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/viewDetails-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/viewDetails-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/visionSimulator-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/watchlist-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/watchlist-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/wikiText-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/wikitrail-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/wikitrail-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/window-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/indicators/alert-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-down-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-up-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/indicators/clear-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/indicators/required-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/indicators/search-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/indicators/search-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/indicators.json
resources/src/jquery/jquery.tablesorter.less
resources/src/mediawiki.action/mediawiki.action.edit.preview.js
resources/src/mediawiki.legacy/commonPrint.css
resources/src/mediawiki.less/mediawiki.mixins.less
resources/src/mediawiki.skinning/content.css
resources/src/mediawiki.skinning/images/sort_both_readonly.png [deleted file]
resources/src/mediawiki.skinning/images/sort_both_readonly.svg [deleted file]
resources/src/mediawiki.special/mediawiki.special.apisandbox.css [new file with mode: 0644]
resources/src/mediawiki.special/mediawiki.special.apisandbox.js [new file with mode: 0644]
resources/src/mediawiki.special/mediawiki.special.apisandbox.top.css [new file with mode: 0644]
resources/src/mediawiki.widgets/mw.widgets.CategoryCapsuleItemWidget.js
resources/src/mediawiki.widgets/mw.widgets.CategorySelector.js
resources/src/mediawiki/api/parse.js
resources/src/mediawiki/api/upload.js
resources/src/mediawiki/bookletlayout/option2/ccbysa.png [deleted file]
resources/src/mediawiki/bookletlayout/option2/ccbysa.svg [deleted file]
resources/src/mediawiki/bookletlayout/option2/noderiv.png [deleted file]
resources/src/mediawiki/bookletlayout/option2/noderiv.svg [deleted file]
resources/src/mediawiki/bookletlayout/option2/ownwork.png [deleted file]
resources/src/mediawiki/bookletlayout/option2/ownwork.svg [deleted file]
resources/src/mediawiki/bookletlayout/option2/useful.png [deleted file]
resources/src/mediawiki/bookletlayout/option2/useful.svg [deleted file]
resources/src/mediawiki/bookletlayout/option4/camera.png [deleted file]
resources/src/mediawiki/bookletlayout/option4/camera.svg [deleted file]
resources/src/mediawiki/bookletlayout/option4/graphics.png [deleted file]
resources/src/mediawiki/bookletlayout/option4/graphics.svg [deleted file]
resources/src/mediawiki/bookletlayout/option4/guide.html [deleted file]
resources/src/mediawiki/bookletlayout/option4/search-ltr.png [deleted file]
resources/src/mediawiki/bookletlayout/option4/search-ltr.svg [deleted file]
resources/src/mediawiki/bookletlayout/option4/search-rtl.png [deleted file]
resources/src/mediawiki/bookletlayout/option4/search-rtl.svg [deleted file]
resources/src/mediawiki/bookletlayout/option4/website-ltr.png [deleted file]
resources/src/mediawiki/bookletlayout/option4/website-ltr.svg [deleted file]
resources/src/mediawiki/bookletlayout/option4/website-rtl.png [deleted file]
resources/src/mediawiki/bookletlayout/option4/website-rtl.svg [deleted file]
resources/src/mediawiki/mediawiki.ForeignStructuredUpload.BookletLayout.js
resources/src/mediawiki/mediawiki.ForeignStructuredUpload.BookletLayout.less
resources/src/mediawiki/mediawiki.Title.js
resources/src/mediawiki/mediawiki.Upload.BookletLayout.css [new file with mode: 0644]
resources/src/mediawiki/mediawiki.Upload.BookletLayout.js
resources/src/mediawiki/mediawiki.Upload.js
resources/src/mediawiki/mediawiki.apipretty.css
resources/src/mediawiki/mediawiki.checkboxtoggle.js
resources/src/mediawiki/mediawiki.feedback.js
resources/src/mediawiki/mediawiki.js
resources/src/mediawiki/mediawiki.userSuggest.js
resources/src/oojs-ui-styles-skip.js [new file with mode: 0644]
resources/src/startup.js
tests/parser/parserTests.txt
tests/phpunit/MediaWikiTestCase.php
tests/phpunit/includes/EditPageTest.php
tests/phpunit/includes/HtmlTest.php
tests/phpunit/includes/MergeHistoryTest.php [new file with mode: 0644]
tests/phpunit/includes/PagePropsTest.php
tests/phpunit/includes/api/ApiMessageTest.php
tests/phpunit/includes/content/ContentHandlerTest.php
tests/phpunit/includes/debug/logger/monolog/AvroFormatterTest.php
tests/phpunit/includes/libs/ArrayUtilsTest.php
tests/phpunit/includes/libs/objectcache/CachedBagOStuffTest.php [new file with mode: 0644]
tests/phpunit/includes/media/IPTCTest.php
tests/phpunit/includes/parser/PreprocessorTest.php
tests/phpunit/includes/resourceloader/ResourceLoaderTest.php
tests/phpunit/includes/search/SearchEnginePrefixTest.php [new file with mode: 0644]
tests/phpunit/includes/search/SearchSuggestionSetTest.php [new file with mode: 0644]
tests/phpunit/includes/session/BotPasswordSessionProviderTest.php
tests/phpunit/includes/session/CookieSessionProviderTest.php
tests/phpunit/includes/session/ImmutableSessionProviderWithCookieTest.php
tests/phpunit/includes/session/PHPSessionHandlerTest.php
tests/phpunit/includes/session/SessionBackendTest.php
tests/phpunit/includes/session/SessionInfoTest.php
tests/phpunit/includes/session/SessionManagerTest.php
tests/phpunit/includes/session/SessionProviderTest.php
tests/phpunit/includes/session/SessionTest.php
tests/phpunit/includes/session/TestBagOStuff.php
tests/phpunit/includes/session/TestUtils.php
tests/phpunit/includes/site/CachingSiteStoreTest.php
tests/phpunit/includes/site/SiteImporterTest.php
tests/phpunit/languages/classes/LanguageGanTest.php [new file with mode: 0644]
tests/phpunit/languages/classes/LanguageIuTest.php [new file with mode: 0644]
tests/phpunit/languages/classes/LanguageKkTest.php [new file with mode: 0644]
tests/phpunit/languages/classes/LanguageKuTest.php [new file with mode: 0644]
tests/phpunit/languages/classes/LanguageShiTest.php [new file with mode: 0644]
tests/phpunit/languages/classes/LanguageTgTest.php [new file with mode: 0644]
tests/phpunit/languages/classes/LanguageZhTest.php [new file with mode: 0644]
tests/phpunit/phpunit.php
tests/phpunit/structure/ApiDocumentationTest.php
tests/phpunit/structure/AutoLoaderTest.php
tests/phpunit/tests/MediaWikiTestCaseTest.php
tests/qunit/data/testrunner.js
tests/qunit/suites/resources/mediawiki.api/mediawiki.api.parse.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.jqueryMsg.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.test.js

index b776e8f..441c4e3 100644 (file)
--- a/.jshintrc
+++ b/.jshintrc
@@ -2,14 +2,15 @@
        // Enforcing
        "bitwise": true,
        "eqeqeq": true,
-       "es3": true,
+       "esversion": 3,
        "freeze": true,
-       "latedef": true,
+       "futurehostile": true,
+       "latedef": "nofunc",
        "noarg": true,
        "nonew": true,
+       "strict": false,
        "undef": true,
        "unused": true,
-       "strict": false,
 
        // Relaxing
        "laxbreak": true,
@@ -19,6 +20,8 @@
        "browser": true,
 
        "globals": {
+               "require": false,
+               "module": false,
                "mediaWiki": true,
                "JSON": true,
                "OO": true,
index 2d07596..9062194 100644 (file)
@@ -12,9 +12,9 @@ matrix:
   fast_finish: true
   include:
     - env: dbtype=mysql
-      php: 5.3
+      php: 5.5
     - env: dbtype=postgres
-      php: 5.3
+      php: 5.5
     - env: dbtype=mysql
       php: hhvm
     - env: dbtype=mysql
diff --git a/Gemfile b/Gemfile
index ee09906..636d4ee 100644 (file)
--- a/Gemfile
+++ b/Gemfile
@@ -1,4 +1,4 @@
 source 'https://rubygems.org'
 
-gem 'mediawiki_selenium', '~> 1.6.3'
+gem 'mediawiki_selenium', '~> 1.6.5'
 gem 'rubocop', '~> 0.32.1', require: false
index 4d0203a..8684be9 100644 (file)
@@ -5,7 +5,7 @@ GEM
     astrolabe (1.3.0)
       parser (>= 2.2.0.pre.3, < 3.0)
     builder (3.2.2)
-    childprocess (0.5.8)
+    childprocess (0.5.9)
       ffi (~> 1.0, >= 1.0.11)
     cucumber (1.3.20)
       builder (>= 2.1.2)
@@ -17,7 +17,7 @@ GEM
       faker (>= 1.1.2)
       yml_reader (>= 0.6)
     diff-lcs (1.2.5)
-    domain_name (0.5.25)
+    domain_name (0.5.20160128)
       unf (>= 0.0.5, < 1.0.0)
     faker (1.6.1)
       i18n (~> 0.5)
@@ -37,7 +37,7 @@ GEM
     mediawiki_api (0.5.0)
       faraday (~> 0.9, >= 0.9.0)
       faraday-cookie_jar (~> 0.0, >= 0.0.6)
-    mediawiki_selenium (1.6.3)
+    mediawiki_selenium (1.6.5)
       cucumber (~> 1.3, >= 1.3.20)
       headless (~> 2.0, >= 2.1.0)
       json (~> 1.8, >= 1.8.1)
@@ -53,7 +53,7 @@ GEM
     multi_test (0.1.2)
     multipart-post (2.0.0)
     netrc (0.11.0)
-    page-object (1.1.0)
+    page-object (1.1.1)
       page_navigation (>= 0.9)
       selenium-webdriver (>= 2.44.0)
       watir-webdriver (>= 0.6.11)
@@ -78,7 +78,7 @@ GEM
       ruby-progressbar (~> 1.4)
     ruby-progressbar (1.7.5)
     rubyzip (1.1.7)
-    selenium-webdriver (2.48.1)
+    selenium-webdriver (2.50.0)
       childprocess (~> 0.5)
       multi_json (~> 1.0)
       rubyzip (~> 1.0)
@@ -91,11 +91,11 @@ GEM
     watir-webdriver (0.9.1)
       selenium-webdriver (>= 2.46.2)
     websocket (1.2.2)
-    yml_reader (0.6)
+    yml_reader (0.7)
 
 PLATFORMS
   ruby
 
 DEPENDENCIES
-  mediawiki_selenium (~> 1.6.3)
+  mediawiki_selenium (~> 1.6.5)
   rubocop (~> 0.32.1)
diff --git a/INSTALL b/INSTALL
index 2054a57..0e8eb92 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -6,7 +6,7 @@ Starting with MediaWiki 1.2.0, it's possible to install and configure the wiki
 "in-place", as long as you have the necessary prerequisites available.
 
 Required software:
-* Web server with PHP 5.3.3 or higher.
+* Web server with PHP 5.5.9 or higher.
 * A SQL server, the following types are supported
 ** MySQL 5.0.3 or higher
 ** PostgreSQL 8.3 or higher
@@ -18,8 +18,7 @@ MediaWiki is developed and tested mainly on Unix/Linux platforms, but should
 work on Windows as well.
 
 If your PHP is configured as a CGI plug-in rather than an Apache module you may
-experience problems, as this configuration is not well tested. safe_mode is also
-not tested and unlikely to work.
+experience problems, as this configuration is not well tested.
 
 Support for rendering mathematical formulas requires installing the Math extension,
 see https://www.mediawiki.org/wiki/Extension:Math
index 2636f54..3a83b36 100644 (file)
@@ -1,6 +1,3 @@
-Security reminder: If you have PHP's register_globals option set, you must
-turn it off. MediaWiki will not work with it enabled.
-
 == MediaWiki 1.27 ==
 
 THIS IS NOT A RELEASE YET
@@ -8,6 +5,10 @@ THIS IS NOT A RELEASE YET
 MediaWiki 1.27 is an alpha-quality branch and is not recommended for use in
 production.
 
+=== PHP version requirement ===
+As of 1.27, MediaWiki now requires PHP 5.5.9 or higher. This corresponds with
+HHVM 3.1.
+
 === Configuration changes in 1.27 ===
 * $wgUseLinkNamespaceDBFields was removed.
 * Deprecated $wgResourceLoaderMinifierStatementsOnOwnLine and
@@ -95,6 +96,8 @@ production.
   return a MediaWiki\Session\Token, and tokens must be checked using that
   class's methods.
 * $wgEnotifUseJobQ was removed and the job queue is always used.
+* The functionality of the ApiSandbox extension has been merged into core. The
+  extension should no longer be used.
 
 === New features in 1.27 ===
 * $wgDataCenterUpdateStickTTL was also added. This decides how long a user
@@ -156,6 +159,13 @@ production.
   aria-describedby, aria-flowto, aria-label, aria-labelledby, aria-owns.
 * Removed "presentation" restriction on the HTML role attribute in wikitext.
   All values are now allowed for the role attribute.
+* $wgContentHandlers now also supports callbacks to create an instance of the
+  appropriate ContentHandler subclass.
+* Added $wgAuthenticationTokenVersion, which if non-null prevents the
+  user_token database field from being exposed in cookies. Setting this would
+  be a good idea, but will log out all current sessions.
+* $wgEventRelayerConfig was added, for managing PubSub event relay configuration,
+  specifically for reliable CDN url purges.
 
 === External library changes in 1.27 ===
 
@@ -307,42 +317,10 @@ changes to languages because of Phabricator reports.
   rather than consume everything until the end of the page.
 * New maintenance script resetUserEmail.php allows sysadmins to reset user emails in case
   a user forgot password/account was stolen.
-* Article::checkFlags was removed. Use Wikipage.
-* Article::clearPreparedEdit was removed. Use Wikipage.
-* Article::doDeleteArticleReal was removed. Use Wikipage.
-* Article::doDeleteUpdates was removed. Use Wikipage.
-* Article::doQuickEditContent was removed. Use Wikipage.
-* Article::doViewUpdates was removed. Use Wikipage.
-* Article::getAutoDeleteReason was removed. Use Wikipage.
-* Article::getContributors was removed. Use Wikipage.
-* Article::getCreator was removed. Use Wikipage.
-* Article::getDeletionUpdates was removed. Use Wikipage.
-* Article::getHiddenCategories was removed. Use Wikipage.
-* Article::getLinksTimestamp was removed. Use Wikipage.
-* Article::getMinorEdit was removed. Use Wikipage.
-* Article::getRedirectURL was removed. Use Wikipage.
-* Article::getUndoContent was removed. Use Wikipage.
-* Article::getUndoText was removed. Use Wikipage.
-* Article::insertProtectNullRevision was removed. Use Wikipage.
-* Article::insertRedirect was removed. Use Wikipage.
-* Article::insertRedirectEntry was removed. Use Wikipage.
-* Article::loadPageData was removed. Use Wikipage.
-* Article::lockAndGetLatest was removed. Use Wikipage.
-* Article::pageDataFromId was removed. Use Wikipage.
-* Article::pageDataFromTitle was removed. Use Wikipage.
-* Article::protectDescription was removed. Use Wikipage.
-* Article::protectDescriptionLog was removed. Use Wikipage.
-* Article::replaceSectionAtRev was removed. Use Wikipage.
-* Article::shouldCheckParserCache was removed. Use Wikipage.
-* Article::supportsSections was removed. Use Wikipage.
-* Article::triggerOpportunisticLinksUpdate was removed. Use Wikipage.
-* Article::updateCategoryCounts was removed. Use Wikipage.
-* Article::updateIfNewerOn was removed. Use Wikipage.
-* Article::updateRedirectOn was removed. Use Wikipage.
 
 == Compatibility ==
 
-MediaWiki 1.27 requires PHP 5.3.3 or later. There is experimental support for
+MediaWiki 1.27 requires PHP 5.5.9 or later. There is experimental support for
 HHVM 3.6.5 or later.
 
 MySQL is the recommended DBMS. PostgreSQL or SQLite can also be used, but
index e66a4a0..83ca3b3 100644 (file)
@@ -52,7 +52,9 @@ $wgAutoloadLocalClasses = array(
        'ApiLogout' => __DIR__ . '/includes/api/ApiLogout.php',
        'ApiMain' => __DIR__ . '/includes/api/ApiMain.php',
        'ApiManageTags' => __DIR__ . '/includes/api/ApiManageTags.php',
+       'ApiMergeHistory' => __DIR__ . '/includes/api/ApiMergeHistory.php',
        'ApiMessage' => __DIR__ . '/includes/api/ApiMessage.php',
+       'ApiMessageTrait' => __DIR__ . '/includes/api/ApiMessage.php',
        'ApiModuleManager' => __DIR__ . '/includes/api/ApiModuleManager.php',
        'ApiMove' => __DIR__ . '/includes/api/ApiMove.php',
        'ApiOpenSearch' => __DIR__ . '/includes/api/ApiOpenSearch.php',
@@ -190,6 +192,7 @@ $wgAutoloadLocalClasses = array(
        'CacheHelper' => __DIR__ . '/includes/cache/CacheHelper.php',
        'CacheTime' => __DIR__ . '/includes/parser/CacheTime.php',
        'CachedAction' => __DIR__ . '/includes/actions/CachedAction.php',
+       'CachedBagOStuff' => __DIR__ . '/includes/libs/objectcache/CachedBagOStuff.php',
        'CachingSiteStore' => __DIR__ . '/includes/site/CachingSiteStore.php',
        'CapsCleanup' => __DIR__ . '/maintenance/cleanupCaps.php',
        'Category' => __DIR__ . '/includes/Category.php',
@@ -393,6 +396,7 @@ $wgAutoloadLocalClasses = array(
        'EraseArchivedFile' => __DIR__ . '/maintenance/eraseArchivedFile.php',
        'ErrorPageError' => __DIR__ . '/includes/exception/ErrorPageError.php',
        'EventRelayer' => __DIR__ . '/includes/libs/eventrelayer/EventRelayer.php',
+       'EventRelayerGroup' => __DIR__ . '/includes/EventRelayerGroup.php',
        'EventRelayerMCRD' => __DIR__ . '/includes/libs/eventrelayer/EventRelayerMCRD.php',
        'EventRelayerNull' => __DIR__ . '/includes/libs/eventrelayer/EventRelayer.php',
        'Exif' => __DIR__ . '/includes/media/Exif.php',
@@ -567,8 +571,8 @@ $wgAutoloadLocalClasses = array(
        'ImageGallery' => __DIR__ . '/includes/gallery/TraditionalImageGallery.php',
        'ImageGalleryBase' => __DIR__ . '/includes/gallery/ImageGalleryBase.php',
        'ImageHandler' => __DIR__ . '/includes/media/ImageHandler.php',
-       'ImageHistoryList' => __DIR__ . '/includes/page/ImagePage.php',
-       'ImageHistoryPseudoPager' => __DIR__ . '/includes/page/ImagePage.php',
+       'ImageHistoryList' => __DIR__ . '/includes/page/ImageHistoryList.php',
+       'ImageHistoryPseudoPager' => __DIR__ . '/includes/page/ImageHistoryPseudoPager.php',
        'ImageListPager' => __DIR__ . '/includes/specials/SpecialListfiles.php',
        'ImagePage' => __DIR__ . '/includes/page/ImagePage.php',
        'ImageQueryPage' => __DIR__ . '/includes/specialpage/ImageQueryPage.php',
@@ -589,6 +593,7 @@ $wgAutoloadLocalClasses = array(
        'InstallDocFormatter' => __DIR__ . '/includes/installer/InstallDocFormatter.php',
        'Installer' => __DIR__ . '/includes/installer/Installer.php',
        'InstallerOverrides' => __DIR__ . '/mw-config/overrides.php',
+       'InstallerSessionProvider' => __DIR__ . '/includes/installer/InstallerSessionProvider.php',
        'Interwiki' => __DIR__ . '/includes/interwiki/Interwiki.php',
        'InvalidPassword' => __DIR__ . '/includes/password/InvalidPassword.php',
        'IteratorDecorator' => __DIR__ . '/includes/utils/iterators/IteratorDecorator.php',
@@ -693,6 +698,7 @@ $wgAutoloadLocalClasses = array(
        'LinkFilter' => __DIR__ . '/includes/LinkFilter.php',
        'LinkHolderArray' => __DIR__ . '/includes/parser/LinkHolderArray.php',
        'LinkSearchPage' => __DIR__ . '/includes/specials/SpecialLinkSearch.php',
+       'LinkTarget' => __DIR__ . '/includes/LinkTarget.php',
        'Linker' => __DIR__ . '/includes/Linker.php',
        'LinksDeletionUpdate' => __DIR__ . '/includes/deferred/LinksDeletionUpdate.php',
        'LinksUpdate' => __DIR__ . '/includes/deferred/LinksUpdate.php',
@@ -791,6 +797,7 @@ $wgAutoloadLocalClasses = array(
        'MediaWiki\\Session\\BotPasswordSessionProvider' => __DIR__ . '/includes/session/BotPasswordSessionProvider.php',
        'MediaWiki\\Session\\CookieSessionProvider' => __DIR__ . '/includes/session/CookieSessionProvider.php',
        'MediaWiki\\Session\\ImmutableSessionProviderWithCookie' => __DIR__ . '/includes/session/ImmutableSessionProviderWithCookie.php',
+       'MediaWiki\\Session\\MetadataMergeException' => __DIR__ . '/includes/session/MetadataMergeException.php',
        'MediaWiki\\Session\\PHPSessionHandler' => __DIR__ . '/includes/session/PHPSessionHandler.php',
        'MediaWiki\\Session\\Session' => __DIR__ . '/includes/session/Session.php',
        'MediaWiki\\Session\\SessionBackend' => __DIR__ . '/includes/session/SessionBackend.php',
@@ -823,6 +830,7 @@ $wgAutoloadLocalClasses = array(
        'MemcachedPhpBagOStuff' => __DIR__ . '/includes/libs/objectcache/MemcachedPhpBagOStuff.php',
        'MemoizedCallable' => __DIR__ . '/includes/libs/MemoizedCallable.php',
        'MemoryFileBackend' => __DIR__ . '/includes/filebackend/MemoryFileBackend.php',
+       'MergeHistory' => __DIR__ . '/includes/MergeHistory.php',
        'MergeHistoryPager' => __DIR__ . '/includes/specials/SpecialMergeHistory.php',
        'MergeLogFormatter' => __DIR__ . '/includes/logging/MergeLogFormatter.php',
        'MergeMessageFileList' => __DIR__ . '/maintenance/mergeMessageFileList.php',
@@ -1134,6 +1142,8 @@ $wgAutoloadLocalClasses = array(
        'SearchResult' => __DIR__ . '/includes/search/SearchResult.php',
        'SearchResultSet' => __DIR__ . '/includes/search/SearchResultSet.php',
        'SearchSqlite' => __DIR__ . '/includes/search/SearchSqlite.php',
+       'SearchSuggestion' => __DIR__ . '/includes/search/SearchSuggestion.php',
+       'SearchSuggestionSet' => __DIR__ . '/includes/search/SearchSuggestionSet.php',
        'SearchUpdate' => __DIR__ . '/includes/deferred/SearchUpdate.php',
        'SectionProfileCallback' => __DIR__ . '/includes/profiler/SectionProfiler.php',
        'SectionProfiler' => __DIR__ . '/includes/profiler/SectionProfiler.php',
@@ -1167,6 +1177,7 @@ $wgAutoloadLocalClasses = array(
        'SpecialAllMyUploads' => __DIR__ . '/includes/specials/SpecialMyRedirectPages.php',
        'SpecialAllPages' => __DIR__ . '/includes/specials/SpecialAllPages.php',
        'SpecialApiHelp' => __DIR__ . '/includes/specials/SpecialApiHelp.php',
+       'SpecialApiSandbox' => __DIR__ . '/includes/specials/SpecialApiSandbox.php',
        'SpecialBlankpage' => __DIR__ . '/includes/specials/SpecialBlankpage.php',
        'SpecialBlock' => __DIR__ . '/includes/specials/SpecialBlock.php',
        'SpecialBlockList' => __DIR__ . '/includes/specials/SpecialBlockList.php',
index da088d3..0b50c2a 100644 (file)
@@ -21,9 +21,9 @@
                "ext-iconv": "*",
                "liuggio/statsd-php-client": "1.0.18",
                "mediawiki/at-ease": "1.1.0",
-               "oojs/oojs-ui": "0.15.1",
-               "oyejorge/less.php": "1.7.0.9",
-               "php": ">=5.3.3",
+               "oojs/oojs-ui": "0.15.3",
+               "oyejorge/less.php": "1.7.0.10",
+               "php": ">=5.5.9",
                "psr/log": "1.0.0",
                "wikimedia/assert": "0.2.2",
                "wikimedia/base-convert": "1.0.1",
index 5f9a0b0..f1f478e 100644 (file)
@@ -148,7 +148,8 @@ using a model or format different from the default will result in an error.
 
 There are some new globals that can be used to control the behavior of the ContentHandler facility:
 
-* $wgContentHandlers associates content model IDs with the names of the appropriate ContentHandler subclasses.
+* $wgContentHandlers associates content model IDs with the names of the appropriate ContentHandler subclasses
+  or callbacks that create an instance of the appropriate ContentHandler subclass.
 
 * $wgNamespaceContentModels maps namespace IDs to a content model that should be the default for that namespace.
 
index bc03714..8822e0c 100644 (file)
@@ -2076,17 +2076,6 @@ $rev: the new revision
 $baseID: the revision ID this was based off, if any
 $user: the editing user
 
-'NormalizeMessageKey': Called before the software gets the text of a message
-(stuff in the MediaWiki: namespace), useful for changing WHAT message gets
-displayed.
-&$key: the message being looked up. Change this to something else to change
-  what message gets displayed (string)
-&$useDB: whether or not to look up the message in the database (bool)
-&$langCode: the language code to get the message for (string) - or -
-  whether to use the content language (true) or site language (false) (bool)
-&$transform: whether or not to expand variables and templates
-  in the message (bool)
-
 'OldChangesListRecentChangesLine': Customize entire recent changes line, or
 return false to omit the line from RecentChanges and Watchlist special pages.
 &$changeslist: The OldChangesList instance.
@@ -2799,6 +2788,11 @@ $id: User id number, only provided for backwards-compatibility
 $user: User object representing user contributions are being fetched for
 $sp: SpecialPage instance, providing context
 
+'SpecialContributions::getForm::filters': Called with a list of filters to render
+on Special:Contributions.
+$sp: SpecialContributions object, for context
+&$filters: List of filters rendered as HTML
+
 'SpecialListusersDefaultQuery': Called right before the end of
 UsersPager::getDefaultQuery().
 $pager: The UsersPager instance
@@ -3280,6 +3274,8 @@ $user: User whose groups changed
 $added: Groups added
 $removed: Groups removed
 $performer: User who performed the change, false if via autopromotion
+$reason: The reason, if any, given by the user performing the change,
+false if via autopromotion.
 
 'UserIsBlockedFrom': Check if a user is blocked from a specific page (for
 specific block exemptions).
@@ -3478,7 +3474,7 @@ Return false to prevent setting of the cookie.
 &$name: Cookie name passed to WebResponse::setcookie()
 &$value: Cookie value passed to WebResponse::setcookie()
 &$expire: Cookie expiration, as for PHP's setcookie()
-$options: Options passed to WebResponse::setcookie()
+&$options: Options passed to WebResponse::setcookie()
 
 'wfShellWikiCmd': Called when generating a shell-escaped command line string to
 run a MediaWiki cli script.
index 3f3d41e..4e253b6 100644 (file)
@@ -1,4 +1,4 @@
-# Protect against bug 28235
+# Protect against bug T30235
 <IfModule rewrite_module>
        RewriteEngine On
        RewriteOptions inherit
index 4385f0e..e3c7713 100644 (file)
@@ -75,7 +75,7 @@ $wgConfigRegistry = array(
  * MediaWiki version number
  * @since 1.2
  */
-$wgVersion = '1.27alpha';
+$wgVersion = '1.27.0-alpha';
 
 /**
  * Name of the site. It must be changed in LocalSettings.php
@@ -535,12 +535,6 @@ $wgUseInstantCommons = false;
  */
 $wgForeignUploadTargets = array();
 
-/**
- * Cross-wiki upload A/B test configuration.
- */
-$wgForeignUploadTestEnabled = false;
-$wgForeignUploadTestDefault = 1;
-
 /**
  * File backend structure configuration.
  *
@@ -904,7 +898,8 @@ $wgMediaHandlers = array(
 
 /**
  * Plugins for page content model handling.
- * Each entry in the array maps a model id to a class name.
+ * Each entry in the array maps a model id to a class name or callback
+ * that creates an instance of the appropriate ContentHandler subclass.
  *
  * @since 1.21
  */
@@ -1560,7 +1555,6 @@ $wgSMTP = false;
 
 /**
  * Additional email parameters, will be passed as the last argument to mail() call.
- * If using safe_mode this has no effect
  */
 $wgAdditionalMailParams = null;
 
@@ -2300,10 +2294,36 @@ $wgSessionHandler = null;
 
 /**
  * Whether to use PHP session handling ($_SESSION and session_*() functions)
- * @var string 'enable', 'warn', or 'disable'
+ * @since 1.27
+ * @var string
+ *  - 'enable': Integrate with PHP's session handling as much as possible.
+ *  - 'warn': Integrate but log warnings if anything changes $_SESSION.
+ *  - 'disable': Throw exceptions if PHP session handling is used.
  */
 $wgPHPSessionHandling = 'enable';
 
+/**
+ * The number of different IPs in the same session within a period of $wgSuspiciousIpExpiry
+ * that should cause warnings to be logged. This is meant more for debugging errors in the
+ * authentication system than for detecting abuse.
+ * @since 1.27
+ */
+$wgSuspiciousIpPerSessionLimit = 2;
+
+/**
+ * Like $wgSuspiciousIpPerSessionLimit but over all requests from the same user within
+ * $wgSuspiciousIpExpiry, whether they are in the same session or not.
+ * @since 1.27
+ */
+$wgSuspiciousIpPerUserLimit = 5;
+
+/**
+ * Time in seconds to remember IPs for, for the purposes of $wgSuspiciousIpPerSessionLimit and
+ * $wgSuspiciousIpPerUserLimit.
+ * @since 1.27
+ */
+$wgSuspiciousIpExpiry = 600;
+
 /**
  * If enabled, will send MemCached debugging information to $wgDebugLogFile
  */
@@ -4632,6 +4652,18 @@ $wgUserrightsInterwikiDelimiter = '@';
  */
 $wgSecureLogin = false;
 
+/**
+ * Versioning for authentication tokens.
+ *
+ * If non-null, this is combined with the user's secret (the user_token field
+ * in the DB) to generate the token cookie. Changing this will invalidate all
+ * active sessions (i.e. it will log everyone out).
+ *
+ * @since 1.27
+ * @var string|null
+ */
+$wgAuthenticationTokenVersion = null;
+
 /**
  * MediaWiki\Session\SessionProvider configuration.
  *
@@ -6648,6 +6680,14 @@ $wgExportFromNamespaces = false;
  */
 $wgExportAllowAll = false;
 
+/**
+ * Maximum number of pages returned by the GetPagesFromCategory and
+ * GetPagesFromNamespace functions.
+ *
+ * @since 1.27
+ */
+$wgExportPagelistLimit = 5000;
+
 /** @} */ # end of import/export }
 
 /*************************************************************************//**
@@ -7944,6 +7984,25 @@ $wgPopularPasswordFile = __DIR__ . '/../serialized/commonpasswords.cdb';
  */
 $wgMaxUserDBWriteDuration = false;
 
+/**
+ * Mapping of event channels 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
+ * can run on each CDN cache node and perfom local purges based on the URL purge channel events.
+ *
+ * The 'default' channel is for all channels without an explicit entry here.
+ *
+ * @since 1.27
+ */
+$wgEventRelayerConfig = array(
+       'default' => array(
+               'class' => 'EventRelayerNull',
+       )
+);
+
 /**
  * For really cool vim folding this needs to be at the end:
  * vim: foldmarker=@{,@} foldmethod=marker
index 277a6cc..caeb57c 100644 (file)
@@ -366,7 +366,7 @@ class EditPage {
        public $contentFormat = null;
 
        /** @var null|array */
-       public $changeTags = null;
+       private $changeTags = null;
 
        # Placeholders for text injection by hooks (must be HTML)
        # extensions should take care to _append_ to the present value
@@ -2013,7 +2013,8 @@ class EditPage {
                        $flags,
                        false,
                        $wgUser,
-                       $content->getDefaultFormat()
+                       $content->getDefaultFormat(),
+                       $this->changeTags
                );
 
                if ( !$doEditStatus->isOK() ) {
@@ -2040,17 +2041,6 @@ class EditPage {
 
                $this->updateWatchlist();
 
-               if ( $this->changeTags && isset( $doEditStatus->value['revision'] ) ) {
-                       // If a revision was created, apply any change tags that were requested
-                       $addTags = $this->changeTags;
-                       $revId = $doEditStatus->value['revision']->getId();
-                       // Defer this both for performance and so that addTags() sees the rc_id
-                       // since the recentchange entry addition is deferred first (bug T100248)
-                       DeferredUpdates::addCallableUpdate( function() use ( $addTags, $revId ) {
-                               ChangeTags::addTags( $addTags, null, $revId );
-                       } );
-               }
-
                // If the content model changed, add a log entry
                if ( $changingContentModel ) {
                        $this->addContentModelChangeLogEntry(
@@ -2232,8 +2222,6 @@ class EditPage {
                        $wgOut->addModules( 'mediawiki.action.edit.stash' );
                }
 
-               $wgOut->setRobotPolicy( 'noindex,nofollow' );
-
                # Enabled article-related sidebar, toplinks, etc.
                $wgOut->setArticleRelated( true );
 
diff --git a/includes/EventRelayerGroup.php b/includes/EventRelayerGroup.php
new file mode 100644 (file)
index 0000000..3af756d
--- /dev/null
@@ -0,0 +1,58 @@
+<?php
+/**
+ * Factory class for spawning EventRelayer objects using configuration
+ *
+ * @author Aaron Schulz
+ * @since 1.27
+ */
+class EventRelayerGroup {
+       /** @var array[] */
+       protected $configByChannel = array();
+
+       /** @var EventRelayer[] */
+       protected $relayers = array();
+
+       /** @var EventRelayerGroup */
+       protected static $instance = null;
+
+       /**
+        * @param Config $config
+        */
+       protected function __construct( Config $config ) {
+               $this->configByChannel = $config->get( 'EventRelayerConfig' );
+       }
+
+       /**
+        * @return EventRelayerGroup
+        */
+       public static function singleton() {
+               if ( !self::$instance ) {
+                       self::$instance = new self( RequestContext::getMain()->getConfig() );
+               }
+
+               return self::$instance;
+       }
+
+       /**
+        * @param string $channel
+        * @return EventRelayer Relayer instance that handles the given channel
+        */
+       public function getRelayer( $channel ) {
+               $channelKey = isset( $this->configByChannel[$channel] )
+                       ? $channel
+                       : 'default';
+
+               if ( !isset( $this->relayers[$channelKey] ) ) {
+                       if ( !isset( $this->configByChannel[$channelKey] ) ) {
+                               throw new UnexpectedValueException( "No config for '$channelKey'" );
+                       }
+
+                       $config = $this->configByChannel[$channelKey];
+                       $class = $config['class'];
+
+                       $this->relayers[$channelKey] = new $class( $config );
+               }
+
+               return $this->relayers[$channelKey];
+       }
+}
index 4066945..a1ea936 100644 (file)
@@ -34,7 +34,7 @@ use MediaWiki\Session\SessionManager;
 /**
  * Compatibility functions
  *
- * We support PHP 5.3.3 and up.
+ * We support PHP 5.5.9 and up.
  * Re-implementations of newer functions or functions in non-standard
  * PHP extensions may be included here.
  */
@@ -92,19 +92,6 @@ if ( !function_exists( 'mb_strrpos' ) ) {
        }
 }
 
-// gzdecode function only exists in PHP >= 5.4.0
-// http://php.net/gzdecode
-if ( !function_exists( 'gzdecode' ) ) {
-       /**
-        * @codeCoverageIgnore
-        * @param string $data
-        * @return string
-        */
-       function gzdecode( $data ) {
-               return gzinflate( substr( $data, 10, -8 ) );
-       }
-}
-
 // hash_equals function only exists in PHP >= 5.6.0
 // http://php.net/hash_equals
 if ( !function_exists( 'hash_equals' ) ) {
@@ -2379,16 +2366,13 @@ function wfEscapeShellArg( /*...*/ ) {
 /**
  * Check if wfShellExec() is effectively disabled via php.ini config
  *
- * @return bool|string False or one of (safemode,disabled)
+ * @return bool|string False or 'disabled'
  * @since 1.22
  */
 function wfShellExecDisabled() {
        static $disabled = null;
        if ( is_null( $disabled ) ) {
-               if ( wfIniGetBool( 'safe_mode' ) ) {
-                       wfDebug( "wfShellExec can't run in safe_mode, PHP's exec functions are too broken.\n" );
-                       $disabled = 'safemode';
-               } elseif ( !function_exists( 'proc_open' ) ) {
+               if ( !function_exists( 'proc_open' ) ) {
                        wfDebug( "proc_open() is disabled\n" );
                        $disabled = 'disabled';
                } else {
@@ -2429,9 +2413,7 @@ function wfShellExec( $cmd, &$retval = null, $environ = array(),
        $disabled = wfShellExecDisabled();
        if ( $disabled ) {
                $retval = 1;
-               return $disabled == 'safemode' ?
-                       'Unable to run external programs in safe mode.' :
-                       'Unable to run external programs, proc_open() is disabled.';
+               return 'Unable to run external programs, proc_open() is disabled.';
        }
 
        $includeStderr = isset( $options['duplicateStderr'] ) && $options['duplicateStderr'];
@@ -2672,10 +2654,8 @@ function wfInitShellLocale() {
        }
        $done = true;
        global $wgShellLocale;
-       if ( !wfIniGetBool( 'safe_mode' ) ) {
-               putenv( "LC_CTYPE=$wgShellLocale" );
-               setlocale( LC_CTYPE, $wgShellLocale );
-       }
+       putenv( "LC_CTYPE=$wgShellLocale" );
+       setlocale( LC_CTYPE, $wgShellLocale );
 }
 
 /**
@@ -3082,7 +3062,6 @@ function wfSetupSession( $sessionId = false ) {
        if ( session_id() !== $session->getId() ) {
                session_id( $session->getId() );
        }
-       MediaWiki\quietCall( 'session_cache_limiter', 'private, must-revalidate' );
        MediaWiki\quietCall( 'session_start' );
 }
 
index a31b157..e1cd5c7 100644 (file)
@@ -95,7 +95,7 @@ class Http {
         * @param string $url
         * @param array $options
         * @param string $caller The method making this request, for profiling
-        * @return string
+        * @return string|bool false on error
         */
        public static function get( $url, $options = array(), $caller = __METHOD__ ) {
                $args = func_get_args();
@@ -118,7 +118,7 @@ class Http {
         * @param string $url
         * @param array $options
         * @param string $caller The method making this request, for profiling
-        * @return string
+        * @return string|bool false on error
         */
        public static function post( $url, $options = array(), $caller = __METHOD__ ) {
                return Http::request( 'POST', $url, $options, $caller );
@@ -816,7 +816,7 @@ class CurlHttpRequest extends MWHttpRequest {
                        MediaWiki\suppressWarnings();
                        if ( !curl_setopt( $curlHandle, CURLOPT_FOLLOWLOCATION, true ) ) {
                                wfDebug( __METHOD__ . ": Couldn't set CURLOPT_FOLLOWLOCATION. " .
-                                       "Probably safe_mode or open_basedir is set.\n" );
+                                       "Probably open_basedir is set.\n" );
                                // Continue the processing. If it were in curl_setopt_array,
                                // processing would have halted on its entry
                        }
@@ -861,8 +861,8 @@ class CurlHttpRequest extends MWHttpRequest {
                }
 
                if ( version_compare( PHP_VERSION, '5.6.0', '<' ) ) {
-                       if ( strval( ini_get( 'open_basedir' ) ) !== '' || wfIniGetBool( 'safe_mode' ) ) {
-                               wfDebug( "Cannot follow redirects in safe mode\n" );
+                       if ( strval( ini_get( 'open_basedir' ) ) !== '' ) {
+                               wfDebug( "Cannot follow redirects when open_basedir is set\n" );
                                return false;
                        }
                }
diff --git a/includes/LinkTarget.php b/includes/LinkTarget.php
new file mode 100644 (file)
index 0000000..1ce5f32
--- /dev/null
@@ -0,0 +1,41 @@
+<?php
+
+/**
+ * @author Addshore
+ *
+ * @since 1.27
+ */
+interface LinkTarget {
+
+       /**
+        * Get the namespace index
+        *
+        * @return int Namespace index
+        */
+       public function getNamespace();
+
+       /**
+        * Get the link fragment (i.e.\ the bit after the #) in text form
+        *
+        * @return string link fragment
+        */
+       public function getFragment();
+
+       /**
+        * Get the main part with underscores
+        *
+        * @return string Main part of the link, with underscores (for use in hrf attributes)
+        */
+       public function getDBkey();
+
+       /**
+        * Returns the link in text form,
+        * without namespace prefix or fragment.
+        *
+        * This is computed from the DB key by replacing any underscores with spaces.
+        *
+        * @return string
+        */
+       public function getText();
+
+}
index 69550d3..f0a2963 100644 (file)
@@ -188,6 +188,7 @@ class Linker {
         *       Has compatibility issues on some setups, so avoid wherever possible.
         *     'http': Force a full URL with http:// as the scheme.
         *     'https': Force a full URL with https:// as the scheme.
+        *     'stubThreshold' => (int): Stub threshold to use when determining link classes.
         * @return string HTML <a> attribute
         */
        public static function link(
@@ -218,7 +219,7 @@ class Linker {
                $target = self::normaliseSpecialPage( $target );
 
                # If we don't know whether the page exists, let's find out.
-               if ( !in_array( 'known', $options ) && !in_array( 'broken', $options ) ) {
+               if ( !in_array( 'known', $options, true ) && !in_array( 'broken', $options, true ) ) {
                        if ( $target->isKnown() ) {
                                $options[] = 'known';
                        } else {
@@ -227,14 +228,14 @@ class Linker {
                }
 
                $oldquery = array();
-               if ( in_array( "forcearticlepath", $options ) && $query ) {
+               if ( in_array( "forcearticlepath", $options, true ) && $query ) {
                        $oldquery = $query;
                        $query = array();
                }
 
                # Note: we want the href attribute first, for prettiness.
                $attribs = array( 'href' => self::linkUrl( $target, $query, $options ) );
-               if ( in_array( 'forcearticlepath', $options ) && $oldquery ) {
+               if ( in_array( 'forcearticlepath', $options, true ) && $oldquery ) {
                        $attribs['href'] = wfAppendQuery( $attribs['href'], $oldquery );
                }
 
@@ -277,7 +278,7 @@ class Linker {
        private static function linkUrl( $target, $query, $options ) {
                # We don't want to include fragments for broken links, because they
                # generally make no sense.
-               if ( in_array( 'broken', $options ) && $target->hasFragment() ) {
+               if ( in_array( 'broken', $options, true ) && $target->hasFragment() ) {
                        $target = clone $target;
                        $target->setFragment( '' );
                }
@@ -285,15 +286,15 @@ class Linker {
                # If it's a broken link, add the appropriate query pieces, unless
                # there's already an action specified, or unless 'edit' makes no sense
                # (i.e., for a nonexistent special page).
-               if ( in_array( 'broken', $options ) && empty( $query['action'] )
+               if ( in_array( 'broken', $options, true ) && empty( $query['action'] )
                        && !$target->isSpecialPage() ) {
                        $query['action'] = 'edit';
                        $query['redlink'] = '1';
                }
 
-               if ( in_array( 'http', $options ) ) {
+               if ( in_array( 'http', $options, true ) ) {
                        $proto = PROTO_HTTP;
-               } elseif ( in_array( 'https', $options ) ) {
+               } elseif ( in_array( 'https', $options, true ) ) {
                        $proto = PROTO_HTTPS;
                } else {
                        $proto = PROTO_RELATIVE;
@@ -316,11 +317,11 @@ class Linker {
                global $wgUser;
                $defaults = array();
 
-               if ( !in_array( 'noclasses', $options ) ) {
+               if ( !in_array( 'noclasses', $options, true ) ) {
                        # Now build the classes.
                        $classes = array();
 
-                       if ( in_array( 'broken', $options ) ) {
+                       if ( in_array( 'broken', $options, true ) ) {
                                $classes[] = 'new';
                        }
 
@@ -328,8 +329,11 @@ class Linker {
                                $classes[] = 'extiw';
                        }
 
-                       if ( !in_array( 'broken', $options ) ) { # Avoid useless calls to LinkCache (see r50387)
-                               $colour = self::getLinkColour( $target, $wgUser->getStubThreshold() );
+                       if ( !in_array( 'broken', $options, true ) ) { # Avoid useless calls to LinkCache (see r50387)
+                               $colour = self::getLinkColour(
+                                       $target,
+                                       isset( $options['stubThreshold'] ) ? $options['stubThreshold'] : $wgUser->getStubThreshold()
+                               );
                                if ( $colour !== '' ) {
                                        $classes[] = $colour; # mw-redirect or stub
                                }
@@ -343,7 +347,7 @@ class Linker {
                if ( $target->getPrefixedText() == '' ) {
                        # A link like [[#Foo]].  This used to mean an empty title
                        # attribute, but that's silly.  Just don't output a title.
-               } elseif ( in_array( 'known', $options ) ) {
+               } elseif ( in_array( 'known', $options, true ) ) {
                        $defaults['title'] = $target->getPrefixedText();
                } else {
                        // This ends up in parser cache!
@@ -1845,7 +1849,7 @@ class Linker {
                }
 
                $editCount = false;
-               if ( in_array( 'verify', $options ) ) {
+               if ( in_array( 'verify', $options, true ) ) {
                        $editCount = self::getRollbackEditCount( $rev, true );
                        if ( $editCount === false ) {
                                return '';
@@ -1854,7 +1858,7 @@ class Linker {
 
                $inner = self::buildRollbackLink( $rev, $context, $editCount );
 
-               if ( !in_array( 'noBrackets', $options ) ) {
+               if ( !in_array( 'noBrackets', $options, true ) ) {
                        $inner = $context->msg( 'brackets' )->rawParams( $inner )->escaped();
                }
 
@@ -2108,7 +2112,7 @@ class Linker {
        /**
         * Returns HTML for the "hidden categories on this page" list.
         *
-        * @param array $hiddencats Array of hidden categories from WikiPage::getHiddenCategories
+        * @param array $hiddencats Array of hidden categories from Article::getHiddenCategories
         *   or similar
         * @return string HTML output
         */
index 8385a06..7f2f737 100644 (file)
@@ -247,29 +247,31 @@ class MediaWiki {
                        // Prevent information leak via Special:MyPage et al (T109724)
                        if ( $title->isSpecialPage() ) {
                                $specialPage = SpecialPageFactory::getPage( $title->getDBKey() );
-                               if ( $specialPage instanceof RedirectSpecialPage
-                                       && $this->config->get( 'HideIdentifiableRedirects' )
-                                       && $specialPage->personallyIdentifiableTarget()
-                               ) {
-                                       list( , $subpage ) = SpecialPageFactory::resolveAlias( $title->getDBKey() );
-                                       $target = $specialPage->getRedirect( $subpage );
-                                       // target can also be true. We let that case fall through to normal processing.
-                                       if ( $target instanceof Title ) {
-                                               $query = $specialPage->getRedirectQuery() ?: array();
-                                               $request = new DerivativeRequest( $this->context->getRequest(), $query );
-                                               $request->setRequestURL( $this->context->getRequest()->getRequestURL() );
-                                               $this->context->setRequest( $request );
-                                               // Do not varnish cache these. May vary even for anons
-                                               $this->context->getOutput()->lowerCdnMaxage( 0 );
-                                               $this->context->setTitle( $target );
-                                               $wgTitle = $target;
-                                               // Reset action type cache. (Special pages have only view)
-                                               $this->action = null;
-                                               $title = $target;
-                                               $output->addJsConfigVars( array(
-                                                       'wgInternalRedirectTargetUrl' => $target->getFullURL( $query ),
-                                               ) );
-                                               $output->addModules( 'mediawiki.action.view.redirect' );
+                               if ( $specialPage instanceof RedirectSpecialPage ) {
+                                       $specialPage->setContext( $this->context );
+                                       if ( $this->config->get( 'HideIdentifiableRedirects' )
+                                               && $specialPage->personallyIdentifiableTarget()
+                                       ) {
+                                               list( , $subpage ) = SpecialPageFactory::resolveAlias( $title->getDBKey() );
+                                               $target = $specialPage->getRedirect( $subpage );
+                                               // target can also be true. We let that case fall through to normal processing.
+                                               if ( $target instanceof Title ) {
+                                                       $query = $specialPage->getRedirectQuery() ?: array();
+                                                       $request = new DerivativeRequest( $this->context->getRequest(), $query );
+                                                       $request->setRequestURL( $this->context->getRequest()->getRequestURL() );
+                                                       $this->context->setRequest( $request );
+                                                       // Do not varnish cache these. May vary even for anons
+                                                       $this->context->getOutput()->lowerCdnMaxage( 0 );
+                                                       $this->context->setTitle( $target );
+                                                       $wgTitle = $target;
+                                                       // Reset action type cache. (Special pages have only view)
+                                                       $this->action = null;
+                                                       $title = $target;
+                                                       $output->addJsConfigVars( array(
+                                                               'wgInternalRedirectTargetUrl' => $target->getFullURL( $query ),
+                                                       ) );
+                                                       $output->addModules( 'mediawiki.action.view.redirect' );
+                                               }
                                        }
                                }
                        }
diff --git a/includes/MergeHistory.php b/includes/MergeHistory.php
new file mode 100644 (file)
index 0000000..a3861ee
--- /dev/null
@@ -0,0 +1,351 @@
+<?php
+
+/**
+ *
+ *
+ * Created on Dec 29, 2015
+ *
+ * Copyright © 2015 Geoffrey Mon <geofbot@gmail.com>
+ *
+ * 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
+ */
+
+/**
+ * Handles the backend logic of merging the histories of two
+ * pages.
+ *
+ * @since 1.27
+ */
+class MergeHistory {
+
+       /** @const int Maximum number of revisions that can be merged at once (avoid too much slave lag) */
+       const REVISION_LIMIT = 5000;
+
+       /** @var Title Page from which history will be merged */
+       protected $source;
+
+       /** @var Title Page to which history will be merged */
+       protected $dest;
+
+       /** @var DatabaseBase Database that we are using */
+       protected $dbw;
+
+       /** @var MWTimestamp Maximum timestamp that we can use (oldest timestamp of dest) */
+       protected $maxTimestamp;
+
+       /** @var string SQL WHERE condition that selects source revisions to insert into destination */
+       protected $timeWhere;
+
+       /** @var MWTimestamp|boolean Timestamp upto which history from the source will be merged */
+       protected $timestampLimit;
+
+       /** @var integer Number of revisions merged (for Special:MergeHistory success message) */
+       protected $revisionsMerged;
+
+       /**
+        * MergeHistory constructor.
+        * @param Title $source Page from which history will be merged
+        * @param Title $dest Page to which history will be merged
+        * @param string|boolean $timestamp Timestamp up to which history from the source will be merged
+        */
+       public function __construct( Title $source, Title $dest, $timestamp = false ) {
+               // Save the parameters
+               $this->source = $source;
+               $this->dest = $dest;
+
+               // Get the database
+               $this->dbw = wfGetDB( DB_MASTER );
+
+               // Max timestamp should be min of destination page
+               $firstDestTimestamp = $this->dbw->selectField(
+                       'revision',
+                       'MIN(rev_timestamp)',
+                       array( 'rev_page' => $this->dest->getArticleID() ),
+                       __METHOD__
+               );
+               $this->maxTimestamp = new MWTimestamp( $firstDestTimestamp );
+
+               // Get the timestamp pivot condition
+               try {
+                       if ( $timestamp ) {
+                               // If we have a requested timestamp, use the
+                               // latest revision up to that point as the insertion point
+                               $mwTimestamp = new MWTimestamp( $timestamp );
+                               $lastWorkingTimestamp = $this->dbw->selectField(
+                                       'revision',
+                                       'MAX(rev_timestamp)',
+                                       array(
+                                               'rev_timestamp <= ' . $this->dbw->timestamp( $mwTimestamp ),
+                                               'rev_page' => $this->source->getArticleID()
+                                       ),
+                                       __METHOD__
+                               );
+                               $mwLastWorkingTimestamp = new MWTimestamp( $lastWorkingTimestamp );
+
+                               $timeInsert = $mwLastWorkingTimestamp;
+                               $this->timestampLimit = $mwLastWorkingTimestamp;
+                       } else {
+                               // If we don't, merge entire source page history into the
+                               // beginning of destination page history
+
+                               // Get the latest timestamp of the source
+                               $lastSourceTimestamp = $this->dbw->selectField(
+                                       array( 'page', 'revision' ),
+                                       'rev_timestamp',
+                                       array( 'page_id' => $this->source->getArticleID(),
+                                               'page_latest = rev_id'
+                                       ),
+                                       __METHOD__
+                               );
+                               $lasttimestamp = new MWTimestamp( $lastSourceTimestamp );
+
+                               $timeInsert = $this->maxTimestamp;
+                               $this->timestampLimit = $lasttimestamp;
+                       }
+
+                       $this->timeWhere = "rev_timestamp <= {$this->dbw->timestamp( $timeInsert )}";
+               } catch ( TimestampException $ex ) {
+                       // The timestamp we got is screwed up and merge cannot continue
+                       // This should be detected by $this->isValidMerge()
+                       $this->timestampLimit = false;
+               }
+       }
+
+       /**
+        * Get the number of revisions that will be moved
+        * @return int
+        */
+       public function getRevisionCount() {
+               $count = $this->dbw->selectRowCount( 'revision', '1',
+                       array( 'rev_page' => $this->source->getArticleID(), $this->timeWhere ),
+                       __METHOD__,
+                       array( 'LIMIT' => self::REVISION_LIMIT + 1 )
+               );
+
+               return $count;
+       }
+
+       /**
+        * Get the number of revisions that were moved
+        * Used in the SpecialMergeHistory success message
+        * @return int
+        */
+       public function getMergedRevisionCount() {
+               return $this->revisionsMerged;
+       }
+
+       /**
+        * Check if the merge is possible
+        * @param User $user
+        * @param string $reason
+        * @return Status
+        */
+       public function checkPermissions( User $user, $reason ) {
+               $status = new Status();
+
+               // Check if user can edit both pages
+               $errors = wfMergeErrorArrays(
+                       $this->source->getUserPermissionsErrors( 'edit', $user ),
+                       $this->dest->getUserPermissionsErrors( 'edit', $user )
+               );
+
+               // Convert into a Status object
+               if ( $errors ) {
+                       foreach ( $errors as $error ) {
+                               call_user_func_array( array( $status, 'fatal' ), $error );
+                       }
+               }
+
+               // Anti-spam
+               if ( EditPage::matchSummarySpamRegex( $reason ) !== false ) {
+                       // This is kind of lame, won't display nice
+                       $status->fatal( 'spamprotectiontext' );
+               }
+
+               // Check mergehistory permission
+               if ( !$user->isAllowed( 'mergehistory' ) ) {
+                       // User doesn't have the right to merge histories
+                       $status->fatal( 'mergehistory-fail-permission' );
+               }
+
+               return $status;
+       }
+
+       /**
+        * Does various sanity checks that the merge is
+        * valid. Only things based on the two pages
+        * should be checked here.
+        *
+        * @return Status
+        */
+       public function isValidMerge() {
+               $status = new Status();
+
+               // If either article ID is 0, then revisions cannot be reliably selected
+               if ( $this->source->getArticleID() === 0 ) {
+                       $status->fatal( 'mergehistory-fail-invalid-source' );
+               }
+               if ( $this->dest->getArticleID() === 0 ) {
+                       $status->fatal( 'mergehistory-fail-invalid-dest' );
+               }
+
+               // Make sure page aren't the same
+               if ( $this->source->equals( $this->dest ) ) {
+                       $status->fatal( 'mergehistory-fail-self-merge' );
+               }
+
+               // Make sure the timestamp is valid
+               if ( !$this->timestampLimit ) {
+                       $status->fatal( 'mergehistory-fail-bad-timestamp' );
+               }
+
+               // $this->timestampLimit must be older than $this->maxTimestamp
+               if ( $this->timestampLimit > $this->maxTimestamp ) {
+                       $status->fatal( 'mergehistory-fail-timestamps-overlap' );
+               }
+
+               // Check that there are not too many revisions to move
+               if ( $this->timestampLimit && $this->getRevisionCount() > self::REVISION_LIMIT ) {
+                       $status->fatal( 'mergehistory-fail-toobig', Message::numParam( self::REVISION_LIMIT ) );
+               }
+
+               return $status;
+       }
+
+       /**
+        * Actually attempt the history move
+        *
+        * @todo if all versions of page A are moved to B and then a user
+        * tries to do a reverse-merge via the "unmerge" log link, then page
+        * A will still be a redirect (as it was after the original merge),
+        * though it will have the old revisions back from before (as expected).
+        * The user may have to "undo" the redirect manually to finish the "unmerge".
+        * Maybe this should delete redirects at the source page of merges?
+        *
+        * @param User $user
+        * @param string $reason
+        * @return Status status of the history merge
+        */
+       public function merge( User $user, $reason = '' ) {
+               $status = new Status();
+
+               // Check validity and permissions required for merge
+               $validCheck = $this->isValidMerge(); // Check this first to check for null pages
+               if ( !$validCheck->isOK() ) {
+                       return $validCheck;
+               }
+               $permCheck = $this->checkPermissions( $user, $reason );
+               if ( !$permCheck->isOK() ) {
+                       return $permCheck;
+               }
+
+               $this->dbw->update(
+                       'revision',
+                       array( 'rev_page' => $this->dest->getArticleID() ),
+                       array( 'rev_page' => $this->source->getArticleID(), $this->timeWhere ),
+                       __METHOD__
+               );
+
+               // Check if this did anything
+               $this->revisionsMerged = $this->dbw->affectedRows();
+               if ( $this->revisionsMerged < 1 ) {
+                       $status->fatal( 'mergehistory-fail-no-change' );
+                       return $status;
+               }
+
+               // Make the source page a redirect if no revisions are left
+               $haveRevisions = $this->dbw->selectField(
+                       'revision',
+                       'rev_timestamp',
+                       array( 'rev_page' => $this->source->getArticleID() ),
+                       __METHOD__,
+                       array( 'FOR UPDATE' )
+               );
+               if ( !$haveRevisions ) {
+                       if ( $reason ) {
+                               $reason = wfMessage(
+                                       'mergehistory-comment',
+                                       $this->source->getPrefixedText(),
+                                       $this->dest->getPrefixedText(),
+                                       $reason
+                               )->inContentLanguage()->text();
+                       } else {
+                               $reason = wfMessage(
+                                       'mergehistory-autocomment',
+                                       $this->source->getPrefixedText(),
+                                       $this->dest->getPrefixedText()
+                               )->inContentLanguage()->text();
+                       }
+
+                       $contentHandler = ContentHandler::getForTitle( $this->source );
+                       $redirectContent = $contentHandler->makeRedirectContent(
+                               $this->dest,
+                               wfMessage( 'mergehistory-redirect-text' )->inContentLanguage()->plain()
+                       );
+
+                       if ( $redirectContent ) {
+                               $redirectPage = WikiPage::factory( $this->source );
+                               $redirectRevision = new Revision( array(
+                                       'title' => $this->source,
+                                       'page' => $this->source->getArticleID(),
+                                       'comment' => $reason,
+                                       'content' => $redirectContent ) );
+                               $redirectRevision->insertOn( $this->dbw );
+                               $redirectPage->updateRevisionOn( $this->dbw, $redirectRevision );
+
+                               // Now, we record the link from the redirect to the new title.
+                               // It should have no other outgoing links...
+                               $this->dbw->delete(
+                                       'pagelinks',
+                                       array( 'pl_from' => $this->dest->getArticleID() ),
+                                       __METHOD__
+                               );
+                               $this->dbw->insert( 'pagelinks',
+                                       array(
+                                               'pl_from' => $this->dest->getArticleID(),
+                                               'pl_from_namespace' => $this->dest->getNamespace(),
+                                               'pl_namespace' => $this->dest->getNamespace(),
+                                               'pl_title' => $this->dest->getDBkey() ),
+                                       __METHOD__
+                               );
+                       } else {
+                               // Warning if we couldn't create the redirect
+                               $status->warning( 'mergehistory-warning-redirect-not-created' );
+                       }
+               } else {
+                       $this->source->invalidateCache(); // update histories
+               }
+               $this->dest->invalidateCache(); // update histories
+
+               // Update our logs
+               $logEntry = new ManualLogEntry( 'merge', 'merge' );
+               $logEntry->setPerformer( $user );
+               $logEntry->setComment( $reason );
+               $logEntry->setTarget( $this->source );
+               $logEntry->setParameters( array(
+                       '4::dest' => $this->dest->getPrefixedText(),
+                       '5::mergepoint' => $this->timestampLimit->getTimestamp( TS_MW )
+               ) );
+               $logId = $logEntry->insert();
+               $logEntry->publish( $logId );
+
+               Hooks::run( 'ArticleMergeComplete', array( $this->source, $this->dest ) );
+
+               return $status;
+       }
+}
index 54efd26..c71a953 100644 (file)
@@ -271,7 +271,7 @@ class Message implements MessageSpecifier, Serializable {
        public function serialize() {
                return serialize( array(
                        'interface' => $this->interface,
-                       'language' => $this->language->getCode(),
+                       'language' => $this->language instanceof StubUserLang ? false : $this->language->getCode(),
                        'key' => $this->key,
                        'keysToTry' => $this->keysToTry,
                        'parameters' => $this->parameters,
@@ -287,6 +287,8 @@ class Message implements MessageSpecifier, Serializable {
         * @param string $serialized
         */
        public function unserialize( $serialized ) {
+               global $wgLang;
+
                $data = unserialize( $serialized );
                $this->interface = $data['interface'];
                $this->key = $data['key'];
@@ -294,7 +296,7 @@ class Message implements MessageSpecifier, Serializable {
                $this->parameters = $data['parameters'];
                $this->format = $data['format'];
                $this->useDatabase = $data['useDatabase'];
-               $this->language = Language::factory( $data['language'] );
+               $this->language = $data['language'] ? Language::factory( $data['language'] ) : $wgLang;
                $this->title = $data['title'];
        }
 
index 93ba702..459fb4d 100644 (file)
@@ -1310,6 +1310,9 @@ class OutputPage extends ContextSource {
                if ( $this->getConfig()->get( 'ContentHandlerUseDB' ) ) {
                        $fields[] = 'page_content_model';
                }
+               if ( $this->getConfig()->get( 'PageLanguageUseDB' ) ) {
+                       $fields[] = 'page_lang';
+               }
 
                $res = $dbr->select( array( 'page', 'page_props' ),
                        $fields,
@@ -1573,11 +1576,42 @@ class OutputPage extends ContextSource {
         * @return ParserOptions
         */
        public function parserOptions( $options = null ) {
+               if ( $options !== null && !empty( $options->isBogus ) ) {
+                       // Someone is trying to set a bogus pre-$wgUser PO. Check if it has
+                       // been changed somehow, and keep it if so.
+                       $anonPO = ParserOptions::newFromAnon();
+                       $anonPO->setEditSection( false );
+                       if ( !$options->matches( $anonPO ) ) {
+                               wfLogWarning( __METHOD__ . ': Setting a changed bogus ParserOptions: ' . wfGetAllCallers( 5 ) );
+                               $options->isBogus = false;
+                       }
+               }
+
                if ( !$this->mParserOptions ) {
+                       if ( !$this->getContext()->getUser()->isSafeToLoad() ) {
+                               // $wgUser isn't unstubbable yet, so don't try to get a
+                               // ParserOptions for it. And don't cache this ParserOptions
+                               // either.
+                               $po = ParserOptions::newFromAnon();
+                               $po->setEditSection( false );
+                               $po->isBogus = true;
+                               if ( $options !== null ) {
+                                       $this->mParserOptions = empty( $options->isBogus ) ? $options : null;
+                               }
+                               return $po;
+                       }
+
                        $this->mParserOptions = ParserOptions::newFromContext( $this->getContext() );
                        $this->mParserOptions->setEditSection( false );
                }
-               return wfSetVar( $this->mParserOptions, $options );
+
+               if ( $options !== null && !empty( $options->isBogus ) ) {
+                       // They're trying to restore the bogus pre-$wgUser PO. Do the right
+                       // thing.
+                       return wfSetVar( $this->mParserOptions, null, true );
+               } else {
+                       return wfSetVar( $this->mParserOptions, $options );
+               }
        }
 
        /**
@@ -3613,8 +3647,6 @@ class OutputPage extends ContextSource {
         */
        public function addStyle( $style, $media = '', $condition = '', $dir = '' ) {
                $options = array();
-               // Even though we expect the media type to be lowercase, but here we
-               // force it to lowercase to be safe.
                if ( $media ) {
                        $options['media'] = $media;
                }
@@ -3815,6 +3847,58 @@ class OutputPage extends ContextSource {
                return $link;
        }
 
+       /**
+        * Transform path to web-accessible static resource.
+        *
+        * This is used to add a validation hash as query string.
+        * This aids various behaviors:
+        *
+        * - Put long Cache-Control max-age headers on responses for improved
+        *   cache performance.
+        * - Get the correct version of a file as expected by the current page.
+        * - Instantly get the updated version of a file after deployment.
+        *
+        * Avoid using this for urls included in HTML as otherwise clients may get different
+        * versions of a resource when navigating the site depending on when the page was cached.
+        * If changes to the url propagate, this is not a problem (e.g. if the url is in
+        * an external stylesheet).
+        *
+        * @since 1.27
+        * @param Config $config
+        * @param string $path Path-absolute URL to file (from document root, must start with "/")
+        * @return string URL
+        */
+       public static function transformResourcePath( Config $config, $path ) {
+               global $IP;
+               $remotePath = $config->get( 'ResourceBasePath' );
+               if ( strpos( $path, $remotePath ) !== 0 ) {
+                       // Path is outside wgResourceBasePath, ignore.
+                       return $path;
+               }
+               $path = RelPath\getRelativePath( $path, $remotePath );
+               return self::transformFilePath( $remotePath, $IP, $path );
+       }
+
+       /**
+        * Utility method for transformResourceFilePath().
+        *
+        * Caller is responsible for ensuring the file exists. Emits a PHP warning otherwise.
+        *
+        * @since 1.27
+        * @param string $remotePath URL path that points to $localPath
+        * @param string $localPath File directory exposed at $remotePath
+        * @param string $file Path to target file relative to $localPath
+        * @return string URL
+        */
+       public static function transformFilePath( $remotePath, $localPath, $file ) {
+               $hash = md5_file( "$localPath/$file" );
+               if ( $hash === false ) {
+                       wfLogWarning( __METHOD__ . ": Failed to hash $localPath/$file" );
+                       $hash = '';
+               }
+               return "$remotePath/$file?" . substr( $hash, 0, 5 );
+       }
+
        /**
         * Transform "media" attribute based on request parameters
         *
@@ -3999,11 +4083,14 @@ class OutputPage extends ContextSource {
                        $this->getLanguage()->getDir()
                );
                $this->addModuleStyles( array(
-                       'oojs-ui.styles',
+                       'oojs-ui-core.styles',
                        'oojs-ui.styles.icons',
                        'oojs-ui.styles.indicators',
                        'oojs-ui.styles.textures',
                        'mediawiki.widgets.styles',
                ) );
+               // Used by 'skipFunction' of the four 'oojs-ui.styles.*' modules. Please don't treat this as a
+               // public API or you'll be severely disappointed when T87871 is fixed and it disappears.
+               $this->addMeta( 'X-OOUI-PHP', '1' );
        }
 }
index eaab9c8..1eafcfa 100644 (file)
@@ -31,7 +31,7 @@
  */
 function wfEntryPointCheck( $entryPoint ) {
        $mwVersion = '1.27';
-       $minimumVersionPHP = '5.3.3';
+       $minimumVersionPHP = '5.5.9';
        $phpVersion = PHP_VERSION;
 
        if ( !function_exists( 'version_compare' )
index 0a3a324..2d6e535 100644 (file)
@@ -58,53 +58,76 @@ class PageProps {
        }
 
        /**
-        * Given one or more Titles and the name of a property, returns an
-        * associative array mapping page ID to property value. Pages in the
-        * provided set of Titles that do not have a value for the given
-        * property will not appear in the returned array. If a single Title
-        * is provided, it does not need to be passed in an array, but an array
-        * will always be returned. An empty array will be returned if no
-        * matching properties were found.
-        *
-        * @param array|Title $titles
-        * @param string $propertyName
+        * Given one or more Titles and one or more names of properties,
+        * returns an associative array mapping page ID to property value.
+        * Pages in the provided set of Titles that do not have a value for
+        * the given properties will not appear in the returned array. If a
+        * single Title is provided, it does not need to be passed in an array,
+        * but an array will always be returned. If a single property name is
+        * provided, it does not need to be passed in an array. In that case,
+        * an associtive array mapping page ID to property value will be
+        * returned; otherwise, an associative array mapping page ID to
+        * an associative array mapping property name to property value will be
+        * returned. An empty array will be returned if no matching properties
+        * were found.
         *
+        * @param Title[]|Title $titles
+        * @param string[]|string $propertyNames
         * @return array associative array mapping page ID to property value
-        *
         */
-       public function getProperty( $titles, $propertyName ) {
+       public function getProperties( $titles, $propertyNames ) {
+               if ( is_array( $propertyNames ) ) {
+                       $gotArray = true;
+               } else {
+                       $propertyNames = array( $propertyNames );
+                       $gotArray = false;
+               }
+
                $values = array();
                $goodIDs = $this->getGoodIDs( $titles );
                $queryIDs = array();
                foreach ( $goodIDs as $pageID ) {
-                       $propertyValue = $this->getCachedProperty( $pageID, $propertyName );
-                       if ( $propertyValue === false ) {
-                               $queryIDs[] = $pageID;
-                       } else {
-                               $values[$pageID] = $propertyValue;
+                       foreach ( $propertyNames as $propertyName ) {
+                               $propertyValue = $this->getCachedProperty( $pageID, $propertyName );
+                               if ( $propertyValue === false ) {
+                                       $queryIDs[] = $pageID;
+                                       break;
+                               } else {
+                                       if ( $gotArray ) {
+                                               $values[$pageID][$propertyName] = $propertyValue;
+                                       } else {
+                                               $values[$pageID] = $propertyValue;
+                                       }
+                               }
                        }
                }
 
-               if ( $queryIDs != array() ) {
+               if ( $queryIDs ) {
                        $dbr = wfGetDB( DB_SLAVE );
                        $result = $dbr->select(
                                'page_props',
                                array(
                                        'pp_page',
+                                       'pp_propname',
                                        'pp_value'
                                ),
                                array(
                                        'pp_page' => $queryIDs,
-                                       'pp_propname' => $propertyName
+                                       'pp_propname' => $propertyNames
                                ),
                                __METHOD__
                        );
 
                        foreach ( $result as $row ) {
                                $pageID = $row->pp_page;
+                               $propertyName = $row->pp_propname;
                                $propertyValue = $row->pp_value;
                                $this->cacheProperty( $pageID, $propertyName, $propertyValue );
-                               $values[$pageID] = $propertyValue;
+                               if ( $gotArray ) {
+                                       $values[$pageID][$propertyName] = $propertyValue;
+                               } else {
+                                       $values[$pageID] = $propertyValue;
+                               }
                        }
                }
 
@@ -121,12 +144,10 @@ class PageProps {
         * will always be returned. An empty array will be returned if no
         * matching properties were found.
         *
-        * @param array|Title $titles
-        *
+        * @param Title[]|Title $titles
         * @return array associative array mapping page ID to property value array
-        *
         */
-       public function getProperties( $titles ) {
+       public function getAllProperties( $titles ) {
                $values = array();
                $goodIDs = $this->getGoodIDs( $titles );
                $queryIDs = array();
@@ -178,10 +199,8 @@ class PageProps {
        }
 
        /**
-        * @param array|Title $titles
-        *
+        * @param Title[]|Title $titles
         * @return array array of good page IDs
-        *
         */
        private function getGoodIDs( $titles ) {
                $result = array();
@@ -206,9 +225,7 @@ class PageProps {
         *
         * @param int $pageID page ID of page being queried
         * @param string $propertyName name of property being queried
-        *
         * @return string|bool property value array or false if not found
-        *
         */
        private function getCachedProperty( $pageID, $propertyName ) {
                if ( $this->cache->has( $pageID, $propertyName, self::CACHE_TTL ) ) {
@@ -227,9 +244,7 @@ class PageProps {
         * Get properties from the cache.
         *
         * @param int $pageID page ID of page being queried
-        *
         * @return string|bool property value array or false if not found
-        *
         */
        private function getCachedProperties( $pageID ) {
                if ( $this->cache->has( 0, $pageID, self::CACHE_TTL ) ) {
@@ -244,7 +259,6 @@ class PageProps {
         * @param int $pageID page ID of page being cached
         * @param string $propertyName name of property being cached
         * @param mixed $propertyValue value of property
-        *
         */
        private function cacheProperty( $pageID, $propertyName, $propertyValue ) {
                $this->cache->set( $pageID, $propertyName, $propertyValue );
@@ -254,8 +268,7 @@ class PageProps {
         * Save properties to the cache.
         *
         * @param int $pageID page ID of page being cached
-        * @param array $pageProperties associative array of page properties to be cached
-        *
+        * @param string[] $pageProperties associative array of page properties to be cached
         */
        private function cacheProperties( $pageID, $pageProperties ) {
                $this->cache->clear( $pageID );
index c6f187d..5f36cf5 100644 (file)
@@ -23,6 +23,7 @@
 /**
  * Handles searching prefixes of titles and finding any page
  * names that match. Used largely by the OpenSearch implementation.
+ * @deprecated Since 1.27, Use SearchEngine::prefixSearchSubpages or SearchEngine::completionSearch
  *
  * @ingroup Search
  */
@@ -259,14 +260,17 @@ abstract class PrefixSearch {
         * @param int $offset Number of items to skip
         * @return array Array of Title objects
         */
-       protected function defaultSearchBackend( $namespaces, $search, $limit, $offset ) {
+       public function defaultSearchBackend( $namespaces, $search, $limit, $offset ) {
                $ns = array_shift( $namespaces ); // support only one namespace
-               if ( in_array( NS_MAIN, $namespaces ) ) {
+               if ( is_null( $ns ) || in_array( NS_MAIN, $namespaces ) ) {
                        $ns = NS_MAIN; // if searching on many always default to main
                }
 
-               $t = Title::newFromText( $search, $ns );
+               if ( $ns == NS_SPECIAL ) {
+                       return $this->specialSearch( $search, $limit, $offset );
+               }
 
+               $t = Title::newFromText( $search, $ns );
                $prefix = $t ? $t->getDBkey() : '';
                $dbr = wfGetDB( DB_SLAVE );
                $res = $dbr->select( 'page',
@@ -318,6 +322,7 @@ abstract class PrefixSearch {
 
 /**
  * Performs prefix search, returning Title objects
+ * @deprecated Since 1.27, Use SearchEngine::prefixSearchSubpages or SearchEngine::completionSearch
  * @ingroup Search
  */
 class TitlePrefixSearch extends PrefixSearch {
@@ -337,6 +342,7 @@ class TitlePrefixSearch extends PrefixSearch {
 
 /**
  * Performs prefix search, returning strings
+ * @deprecated Since 1.27, Use SearchEngine::prefixSearchSubpages or SearchEngine::completionSearch
  * @ingroup Search
  */
 class StringPrefixSearch extends PrefixSearch {
index f4f6dca..e76d19e 100644 (file)
@@ -101,22 +101,22 @@ class Revision implements IDBAccessObject {
 
        /**
         * Load either the current, or a specified, revision
-        * that's attached to a given title. If not attached
-        * to that title, will return null.
+        * that's attached to a given link target. If not attached
+        * to that link target, will return null.
         *
         * $flags include:
         *      Revision::READ_LATEST  : Select the data from the master
         *      Revision::READ_LOCKING : Select & lock the data from the master
         *
-        * @param Title $title
+        * @param LinkTarget $linkTarget
         * @param int $id (optional)
         * @param int $flags Bitfield (optional)
         * @return Revision|null
         */
-       public static function newFromTitle( $title, $id = 0, $flags = 0 ) {
+       public static function newFromTitle( LinkTarget $linkTarget, $id = 0, $flags = 0 ) {
                $conds = array(
-                       'page_namespace' => $title->getNamespace(),
-                       'page_title' => $title->getDBkey()
+                       'page_namespace' => $linkTarget->getNamespace(),
+                       'page_title' => $linkTarget->getDBkey()
                );
                if ( $id ) {
                        // Use the specified ID
index 6c85638..3ceb558 100644 (file)
@@ -738,7 +738,6 @@ if ( !defined( 'MW_NO_SESSION' ) && !$wgCommandLineMode ) {
        ) {
                // Start the PHP-session for backwards compatibility
                session_id( $session->getId() );
-               MediaWiki\quietCall( 'session_cache_limiter', 'private, must-revalidate' );
                MediaWiki\quietCall( 'session_start' );
        }
 }
@@ -797,16 +796,23 @@ foreach ( $wgExtensionFunctions as $func ) {
 
 // If the session user has a 0 id but a valid name, that means we need to
 // autocreate it.
-$sessionUser = MediaWiki\Session\SessionManager::getGlobalSession()->getUser();
-if ( $sessionUser->getId() === 0 && User::isValidUserName( $sessionUser->getName() ) ) {
-       $ps_autocreate = Profiler::instance()->scopedProfileIn( $fname . '-autocreate' );
-       MediaWiki\Session\SessionManager::autoCreateUser( $sessionUser );
-       Profiler::instance()->scopedProfileOut( $ps_autocreate );
+if ( !defined( 'MW_NO_SESSION' ) && !$wgCommandLineMode ) {
+       $sessionUser = MediaWiki\Session\SessionManager::getGlobalSession()->getUser();
+       if ( $sessionUser->getId() === 0 && User::isValidUserName( $sessionUser->getName() ) ) {
+               $ps_autocreate = Profiler::instance()->scopedProfileIn( $fname . '-autocreate' );
+               MediaWiki\Session\SessionManager::autoCreateUser( $sessionUser );
+               Profiler::instance()->scopedProfileOut( $ps_autocreate );
+       }
+       unset( $sessionUser );
 }
-unset( $sessionUser );
 
 wfDebug( "Fully initialised\n" );
 $wgFullyInitialised = true;
 
+// T125455
+if ( !defined( 'MW_NO_SESSION' ) && !$wgCommandLineMode ) {
+       MediaWiki\Session\SessionManager::singleton()->checkIpLimits();
+}
+
 Profiler::instance()->scopedProfileOut( $ps_extensions );
 Profiler::instance()->scopedProfileOut( $ps_setup );
index 882b7dd..806def2 100644 (file)
@@ -30,7 +30,7 @@
  * @note Consider using a TitleValue object instead. TitleValue is more lightweight
  *       and does not rely on global state or the database.
  */
-class Title {
+class Title implements LinkTarget {
        /** @var HashBagOStuff */
        static private $titleCache = null;
 
@@ -145,8 +145,9 @@ class Title {
        /** @var bool The (string) language code of the page's language and content code. */
        private $mPageLanguage = false;
 
-       /** @var string The page language code from the database */
-       private $mDbPageLanguage = null;
+       /** @var string|boolean|null The page language code from the database, null if not saved in
+        * the database or false if not loaded, yet. */
+       private $mDbPageLanguage = false;
 
        /** @var TitleValue A corresponding TitleValue object */
        private $mTitleValue = null;
@@ -161,9 +162,9 @@ class Title {
         * Avoid usage of this singleton by using TitleValue
         * and the associated services when possible.
         *
-        * @return TitleParser
+        * @return MediaWikiTitleCodec
         */
-       private static function getTitleParser() {
+       private static function getMediaWikiTitleCodec() {
                global $wgContLang, $wgLocalInterwikis;
 
                static $titleCodec = null;
@@ -200,9 +201,9 @@ class Title {
         * @return TitleFormatter
         */
        private static function getTitleFormatter() {
-               // NOTE: we know that getTitleParser() returns a MediaWikiTitleCodec,
+               // NOTE: we know that getMediaWikiTitleCodec() returns a MediaWikiTitleCodec,
                //      which implements TitleFormatter.
-               return self::getTitleParser();
+               return self::getMediaWikiTitleCodec();
        }
 
        function __construct() {
@@ -236,10 +237,21 @@ class Title {
         * @return Title
         */
        public static function newFromTitleValue( TitleValue $titleValue ) {
+               return self::newFromLinkTarget( $titleValue );
+       }
+
+       /**
+        * Create a new Title from a LinkTarget
+        *
+        * @param LinkTarget $linkTarget Assumed to be safe.
+        *
+        * @return Title
+        */
+       public static function newFromLinkTarget( LinkTarget $linkTarget ) {
                return self::makeTitle(
-                       $titleValue->getNamespace(),
-                       $titleValue->getText(),
-                       $titleValue->getFragment() );
+                       $linkTarget->getNamespace(),
+                       $linkTarget->getText(),
+                       $linkTarget->getFragment() );
        }
 
        /**
@@ -374,7 +386,7 @@ class Title {
         * @return array
         */
        protected static function getSelectFields() {
-               global $wgContentHandlerUseDB;
+               global $wgContentHandlerUseDB, $wgPageLanguageUseDB;
 
                $fields = array(
                        'page_namespace', 'page_title', 'page_id',
@@ -385,6 +397,10 @@ class Title {
                        $fields[] = 'page_content_model';
                }
 
+               if ( $wgPageLanguageUseDB ) {
+                       $fields[] = 'page_lang';
+               }
+
                return $fields;
        }
 
@@ -1250,6 +1266,7 @@ class Title {
         * show "inactive" CSS or JS.
         *
         * @return bool
+        * @todo FIXME: Rename to isSiteConfigPage() and remove deprecated hook
         */
        public function isCssOrJsPage() {
                $isCssOrJsPage = NS_MEDIAWIKI == $this->mNamespace
@@ -1268,6 +1285,7 @@ class Title {
        /**
         * Is this a .css or .js subpage of a user page?
         * @return bool
+        * @todo FIXME: Rename to isUserConfigPage()
         */
        public function isCssJsSubpage() {
                return ( NS_USER == $this->mNamespace && $this->isSubpage()
@@ -3290,7 +3308,7 @@ class Title {
                $this->mContentModel = false;
                $this->mEstimateRevisions = null;
                $this->mPageLanguage = false;
-               $this->mDbPageLanguage = null;
+               $this->mDbPageLanguage = false;
                $this->mIsBigDeletion = null;
        }
 
@@ -3342,7 +3360,7 @@ class Title {
                // @note: splitTitleString() is a temporary hack to allow MediaWikiTitleCodec to share
                //        the parsing code with Title, while avoiding massive refactoring.
                // @todo: get rid of secureAndSplit, refactor parsing code.
-               $titleParser = self::getTitleParser();
+               $titleParser = self::getMediaWikiTitleCodec();
                // MalformedTitleException can be thrown here
                $parts = $titleParser->splitTitleString( $dbkey, $this->getDefaultNamespace() );
 
@@ -4604,6 +4622,27 @@ class Title {
                return $unprefixed;
        }
 
+       /**
+        * Returns the page language code saved in the database, if $wgPageLanguageUseDB is set
+        * to true in LocalSettings.php, otherwise returns false. If there is no language saved in
+        * the db, it will return NULL.
+        *
+        * @return string|null|boolean
+        */
+       private function getDbPageLanguageCode() {
+               global $wgPageLanguageUseDB;
+
+               // check, if the page language could be saved in the database, and if so and
+               // the value is not requested already, lookup the page language using LinkCache
+               if ( $wgPageLanguageUseDB && $this->mDbPageLanguage === false ) {
+                       $linkCache = LinkCache::singleton();
+                       $linkCache->addLinkObj( $this );
+                       $this->mDbPageLanguage = $linkCache->getGoodLinkFieldObj( $this, 'lang' );
+               }
+
+               return $this->mDbPageLanguage;
+       }
+
        /**
         * Get the language in which the content of this page is written in
         * wikitext. Defaults to $wgContLang, but in certain cases it can be
@@ -4620,8 +4659,9 @@ class Title {
                }
 
                // Checking if DB language is set
-               if ( $this->mDbPageLanguage ) {
-                       return wfGetLangObj( $this->mDbPageLanguage );
+               $dbPageLanguage = $this->getDbPageLanguageCode();
+               if ( $dbPageLanguage ) {
+                       return wfGetLangObj( $dbPageLanguage );
                }
 
                if ( !$this->mPageLanguage || $this->mPageLanguage[1] !== $wgLanguageCode ) {
index fd48005..e46d86f 100644 (file)
@@ -128,7 +128,7 @@ class WebResponse {
 
                $func = $options['raw'] ? 'setrawcookie' : 'setcookie';
 
-               if ( Hooks::run( 'WebResponseSetCookie', array( &$name, &$value, &$expire, $options ) ) ) {
+               if ( Hooks::run( 'WebResponseSetCookie', array( &$name, &$value, &$expire, &$options ) ) ) {
                        $cookie = $options['prefix'] . $name;
                        $data = array(
                                'name' => (string)$cookie,
index fb6c3e6..7bc3039 100644 (file)
  * @file
  */
 
-# Die if register_globals is enabled (PHP <=5.3)
-# This must be done before any globals are set by the code
-if ( ini_get( 'register_globals' ) ) {
-       die( 'MediaWiki does not support installations where register_globals is enabled. Please see '
-               . '<a href="https://www.mediawiki.org/wiki/register_globals">mediawiki.org</a> '
-               . 'for help on how to disable it.' );
-}
-
-if ( function_exists( 'get_magic_quotes_gpc' ) && get_magic_quotes_gpc() ) {
-       die( 'MediaWiki does not function when magic quotes are enabled. Please see the '
-               . '<a href="https://php.net/manual/security.magicquotes.disabling.php">PHP Manual</a> '
-               . 'for help on how to disable magic quotes.' );
-}
 
 # bug 15461: Make IE8 turn off content sniffing. Everybody else should ignore this
 # We're adding it here so that it's *always* set, even for alternate entry
 # points and when $wgOut gets disabled or overridden.
 header( 'X-Content-Type-Options: nosniff' );
 
-# Approximate $_SERVER['REQUEST_TIME_FLOAT'] for PHP<5.4
-if ( !isset( $_SERVER['REQUEST_TIME_FLOAT'] ) ) {
-       $_SERVER['REQUEST_TIME_FLOAT'] = microtime( true );
-}
-
 /**
  * @var float Request start time as fractional seconds since epoch
  * @deprecated since 1.25; use $_SERVER['REQUEST_TIME_FLOAT'] or
index 643d1c4..9b70994 100644 (file)
@@ -43,8 +43,9 @@ class EditAction extends FormlessAction {
        public function show() {
                $this->useTransactionalTimeLimit();
 
+               $out = $this->getOutput();
+               $out->setRobotPolicy( 'noindex,nofollow' );
                if ( $this->getContext()->getConfig()->get( 'UseMediaWikiUIEverywhere' ) ) {
-                       $out = $this->getOutput();
                        $out->addModuleStyles( array(
                                'mediawiki.ui.input',
                                'mediawiki.ui.checkbox',
index 43260d0..33a69fb 100644 (file)
@@ -744,7 +744,11 @@ class HistoryPager extends ReverseChronologicalPager {
                }
 
                # Tags
-               list( $tagSummary, $newClasses ) = ChangeTags::formatSummaryRow( $row->ts_tags, 'history' );
+               list( $tagSummary, $newClasses ) = ChangeTags::formatSummaryRow(
+                       $row->ts_tags,
+                       'history',
+                       $this->getContext()
+               );
                $classes = array_merge( $classes, $newClasses );
                if ( $tagSummary !== '' ) {
                        $s2 .= " $tagSummary";
index 6f33db7..4027b35 100644 (file)
@@ -204,7 +204,7 @@ class InfoAction extends FormlessAction {
                $pageCounts = $this->pageCounts( $this->page );
 
                $pageProperties = array();
-               $props = PageProps::getInstance()->getProperties( $title );
+               $props = PageProps::getInstance()->getAllProperties( $title );
                if ( isset( $props[$id] ) ) {
                        $pageProperties = $props[$id];
                }
@@ -410,9 +410,9 @@ class InfoAction extends FormlessAction {
                // Is this page affected by the cascading protection of something which includes it?
                if ( $title->isCascadeProtected() ) {
                        $cascadingFrom = '';
-                       $sources = $title->getCascadeProtectionSources(); // Array deferencing is in PHP 5.4 :(
+                       $sources = $title->getCascadeProtectionSources()[0];
 
-                       foreach ( $sources[0] as $sourceTitle ) {
+                       foreach ( $sources as $sourceTitle ) {
                                $cascadingFrom .= Html::rawElement(
                                        'li', array(), Linker::linkKnown( $sourceTitle ) );
                        }
index 02720c0..a6da823 100644 (file)
@@ -74,6 +74,8 @@ abstract class ApiBase extends ContextSource {
         * - string: Any non-empty string, not expected to be very long or contain newlines.
         *   <input type="text"> would be an appropriate HTML form field.
         * - submodule: The name of a submodule of this module, see PARAM_SUBMODULE_MAP.
+        * - tags: A string naming an existing, explicitly-defined tag. Should usually be
+        *   used with PARAM_ISMULTI.
         * - text: Any non-empty string, expected to be very long or contain newlines.
         *   <textarea> would be an appropriate HTML form field.
         * - timestamp: A timestamp in any format recognized by MWTimestamp, or the
@@ -1063,6 +1065,16 @@ abstract class ApiBase extends ContextSource {
                                                break;
                                        case 'upload': // nothing to do
                                                break;
+                                       case 'tags':
+                                               // If change tagging was requested, check that the tags are valid.
+                                               if ( !is_array( $value ) && !$multi ) {
+                                                       $value = array( $value );
+                                               }
+                                               $tagsStatus = ChangeTags::canAddTagsAccompanyingChange( $value );
+                                               if ( !$tagsStatus->isGood() ) {
+                                                       $this->dieStatus( $tagsStatus );
+                                               }
+                                               break;
                                        default:
                                                ApiBase::dieDebug( __METHOD__, "Param $encParamName's type is unknown - $type" );
                                }
index edcee86..b0bf5dd 100644 (file)
@@ -188,7 +188,7 @@ class ApiDelete extends ApiBase {
                        ),
                        'reason' => null,
                        'tags' => array(
-                               ApiBase::PARAM_TYPE => ChangeTags::listExplicitlyDefinedTags(),
+                               ApiBase::PARAM_TYPE => 'tags',
                                ApiBase::PARAM_ISMULTI => true,
                        ),
                        'watch' => array(
index 59264e8..4a83129 100644 (file)
@@ -363,10 +363,11 @@ class ApiEditPage extends ApiBase {
 
                // Apply change tags
                if ( count( $params['tags'] ) ) {
-                       if ( $user->isAllowed( 'applychangetags' ) ) {
+                       $tagStatus = ChangeTags::canAddTagsAccompanyingChange( $params['tags'], $user );
+                       if ( $tagStatus->isOk() ) {
                                $requestArray['wpChangeTags'] = implode( ',', $params['tags'] );
                        } else {
-                               $this->dieUsage( 'You don\'t have permission to set change tags.', 'taggingnotallowed' );
+                               $this->dieStatus( $tagStatus );
                        }
                }
 
@@ -579,7 +580,7 @@ class ApiEditPage extends ApiBase {
                        ),
                        'summary' => null,
                        'tags' => array(
-                               ApiBase::PARAM_TYPE => ChangeTags::listExplicitlyDefinedTags(),
+                               ApiBase::PARAM_TYPE => 'tags',
                                ApiBase::PARAM_ISMULTI => true,
                        ),
                        'minor' => false,
index be68310..69cedd7 100644 (file)
@@ -32,6 +32,7 @@
 abstract class ApiFormatBase extends ApiBase {
        private $mIsHtml, $mFormat, $mUnescapeAmps, $mHelp;
        private $mBuffer, $mDisabled = false;
+       private $mIsWrappedHtml = false;
        protected $mForceDefaultParams = false;
 
        /**
@@ -45,6 +46,7 @@ abstract class ApiFormatBase extends ApiBase {
                $this->mIsHtml = ( substr( $format, -2, 2 ) === 'fm' ); // ends with 'fm'
                if ( $this->mIsHtml ) {
                        $this->mFormat = substr( $format, 0, -2 ); // remove ending 'fm'
+                       $this->mIsWrappedHtml = $this->getMain()->getCheck( 'wrappedhtml' );
                } else {
                        $this->mFormat = $format;
                }
@@ -79,6 +81,15 @@ abstract class ApiFormatBase extends ApiBase {
                return $this->mIsHtml;
        }
 
+       /**
+        * Returns true when the special wrapped mode is enabled.
+        * @since 1.27
+        * @return bool
+        */
+       protected function getIsWrappedHtml() {
+               return $this->mIsWrappedHtml;
+       }
+
        /**
         * Disable the formatter.
         *
@@ -145,7 +156,9 @@ abstract class ApiFormatBase extends ApiBase {
                        return;
                }
 
-               $mime = $this->getIsHtml() ? 'text/html' : $this->getMimeType();
+               $mime = $this->getIsWrappedHtml()
+                       ? 'text/mediawiki-api-prettyprint-wrapped'
+                       : ( $this->getIsHtml() ? 'text/html' : $this->getMimeType() );
 
                // Some printers (ex. Feed) do their own header settings,
                // in which case $mime will be set to null
@@ -185,19 +198,21 @@ abstract class ApiFormatBase extends ApiBase {
                        $out->addModuleStyles( 'mediawiki.apipretty' );
                        $out->setPageTitle( $context->msg( 'api-format-title' ) );
 
-                       // When the format without suffix 'fm' is defined, there is a non-html version
-                       if ( $this->getMain()->getModuleManager()->isDefined( $lcformat, 'format' ) ) {
-                               $msg = $context->msg( 'api-format-prettyprint-header' )->params( $format, $lcformat );
-                       } else {
-                               $msg = $context->msg( 'api-format-prettyprint-header-only-html' )->params( $format );
-                       }
+                       if ( !$this->getIsWrappedHtml() ) {
+                               // When the format without suffix 'fm' is defined, there is a non-html version
+                               if ( $this->getMain()->getModuleManager()->isDefined( $lcformat, 'format' ) ) {
+                                       $msg = $context->msg( 'api-format-prettyprint-header' )->params( $format, $lcformat );
+                               } else {
+                                       $msg = $context->msg( 'api-format-prettyprint-header-only-html' )->params( $format );
+                               }
 
-                       $header = $msg->parseAsBlock();
-                       $out->addHTML(
-                               Html::rawElement( 'div', array( 'class' => 'api-pretty-header' ),
-                                       ApiHelp::fixHelpLinks( $header )
-                               )
-                       );
+                               $header = $msg->parseAsBlock();
+                               $out->addHTML(
+                                       Html::rawElement( 'div', array( 'class' => 'api-pretty-header' ),
+                                               ApiHelp::fixHelpLinks( $header )
+                                       )
+                               );
+                       }
 
                        if ( Hooks::run( 'ApiFormatHighlight', array( $context, $result, $mime, $format ) ) ) {
                                $out->addHTML(
@@ -205,10 +220,38 @@ abstract class ApiFormatBase extends ApiBase {
                                );
                        }
 
-                       // API handles its own clickjacking protection.
-                       // Note, that $wgBreakFrames will still override $wgApiFrameOptions for format mode.
-                       $out->allowClickjacking();
-                       $out->output();
+                       if ( $this->getIsWrappedHtml() ) {
+                               // This is a special output mode mainly intended for ApiSandbox use
+                               $time = microtime( true ) - $this->getConfig()->get( 'RequestTime' );
+                               $json = FormatJson::encode(
+                                       array(
+                                               'html' => $out->getHTML(),
+                                               'modules' => array_values( array_unique( array_merge(
+                                                       $out->getModules(),
+                                                       $out->getModuleScripts(),
+                                                       $out->getModuleStyles()
+                                               ) ) ),
+                                               'time' => round( $time * 1000 ),
+                                       ),
+                                       false, FormatJson::ALL_OK
+                               );
+
+                               // Bug 66776: wfMangleFlashPolicy() is needed to avoid a nasty bug in
+                               // Flash, but what it does isn't friendly for the API, so we need to
+                               // work around it.
+                               if ( preg_match( '/\<\s*cross-domain-policy\s*\>/i', $json ) ) {
+                                       $json = preg_replace(
+                                               '/\<(\s*cross-domain-policy\s*)\>/i', '\\u003C$1\\u003E', $json
+                                       );
+                               }
+
+                               echo $json;
+                       } else {
+                               // API handles its own clickjacking protection.
+                               // Note, that $wgBreakFrames will still override $wgApiFrameOptions for format mode.
+                               $out->allowClickjacking();
+                               $out->output();
+                       }
                } else {
                        // For non-HTML output, clear all errors that might have been
                        // displayed if display_errors=On
@@ -234,6 +277,18 @@ abstract class ApiFormatBase extends ApiBase {
                return $this->mBuffer;
        }
 
+       public function getAllowedParams() {
+               $ret = array();
+               if ( $this->getIsHtml() ) {
+                       $ret['wrappedhtml'] = array(
+                               ApiBase::PARAM_DFLT => false,
+                               ApiBase::PARAM_HELP_MSG => 'apihelp-format-param-wrappedhtml',
+
+                       );
+               }
+               return $ret;
+       }
+
        protected function getExamplesMessages() {
                return array(
                        'action=query&meta=siteinfo&siprop=namespaces&format=' . $this->getModuleName()
index a319be3..1566a0f 100644 (file)
@@ -121,10 +121,10 @@ class ApiFormatJson extends ApiFormatBase {
 
        public function getAllowedParams() {
                if ( $this->isRaw ) {
-                       return array();
+                       return parent::getAllowedParams();
                }
 
-               $ret = array(
+               $ret = parent::getAllowedParams() + array(
                        'callback' => array(
                                ApiBase::PARAM_HELP_MSG => 'apihelp-json-param-callback',
                        ),
index df9d581..f5f2504 100644 (file)
@@ -78,7 +78,7 @@ class ApiFormatPhp extends ApiFormatBase {
        }
 
        public function getAllowedParams() {
-               $ret = array(
+               $ret = parent::getAllowedParams() + array(
                        'formatversion' => array(
                                ApiBase::PARAM_TYPE => array( 1, 2, 'latest' ),
                                ApiBase::PARAM_DFLT => 1,
index e8ad387..b4a478c 100644 (file)
@@ -288,7 +288,7 @@ class ApiFormatXml extends ApiFormatBase {
        }
 
        public function getAllowedParams() {
-               return array(
+               return parent::getAllowedParams() + array(
                        'xslt' => array(
                                ApiBase::PARAM_HELP_MSG => 'apihelp-xml-param-xslt',
                        ),
index bbea20b..092e3e6 100644 (file)
@@ -533,6 +533,17 @@ class ApiHelp extends ApiBase {
                                                                        $type = null;
                                                                        break;
 
+                                                               case 'tags':
+                                                                       $tags = ChangeTags::listExplicitlyDefinedTags();
+                                                                       $count = count( $tags );
+                                                                       $info[] = $context->msg( 'api-help-param-list' )
+                                                                               ->params( $multi ? 2 : 1 )
+                                                                               ->params( $context->getLanguage()->commaList( $tags ) )
+                                                                               ->parse();
+                                                                       $hintPipeSeparated = false;
+                                                                       $type = null;
+                                                                       break;
+
                                                                case 'limit':
                                                                        if ( isset( $settings[ApiBase::PARAM_MAX2] ) ) {
                                                                                $info[] = $context->msg( 'api-help-param-limit2' )
@@ -690,9 +701,12 @@ class ApiHelp extends ApiBase {
                                        ) );
 
                                        $link = wfAppendQuery( wfScript( 'api' ), $qs );
+                                       $sandbox = SpecialPage::getTitleFor( 'ApiSandbox' )->getLocalURL() . '#' . $qs;
                                        $help['examples'] .= Html::rawElement( 'dt', null, $msg->parse() );
                                        $help['examples'] .= Html::rawElement( 'dd', null,
-                                               Html::element( 'a', array( 'href' => $link ), "api.php?$qs" )
+                                               Html::element( 'a', array( 'href' => $link ), "api.php?$qs" ) . ' ' .
+                                               Html::rawElement( 'a', array( 'href' => $sandbox ),
+                                                       $context->msg( 'api-help-open-in-apisandbox' )->parse() )
                                        );
                                }
                                $help['examples'] .= Html::closeElement( 'dl' );
index 458fd18..3b62541 100644 (file)
@@ -90,6 +90,7 @@ class ApiMain extends ApiBase {
                'revisiondelete' => 'ApiRevisionDelete',
                'managetags' => 'ApiManageTags',
                'tag' => 'ApiTag',
+               'mergehistory' => 'ApiMergeHistory',
        );
 
        /**
@@ -1180,27 +1181,44 @@ class ApiMain extends ApiBase {
                        && in_array( 'bot', $this->getUser()->getGroups() )
                        && wfGetLB()->getServerCount() > 1
                ) {
-                       // Figure out how many servers have passed the lag threshold
-                       $numLagged = 0;
-                       $lagLimit = $this->getConfig()->get( 'APIMaxLagThreshold' );
-                       foreach ( wfGetLB()->getLagTimes() as $lag ) {
-                               if ( $lag > $lagLimit ) {
-                                       ++$numLagged;
-                               }
-                       }
-                       // If a majority of slaves are too lagged then disallow writes
-                       $slaveCount = wfGetLB()->getServerCount() - 1;
-                       if ( $numLagged >= ceil( $slaveCount / 2 ) ) {
-                               $parsed = $this->parseMsg( array( 'readonlytext' ) );
-                               $this->dieUsage(
-                                       $parsed['info'],
-                                       $parsed['code'],
-                                       /* http error */
-                                       0,
-                                       array( 'readonlyreason' => "Waiting for $numLagged lagged database(s)" )
-                               );
+                       $this->checkBotReadOnly();
+               }
+       }
+
+       /**
+        * Check whether we are readonly for bots
+        */
+       private function checkBotReadOnly() {
+               // Figure out how many servers have passed the lag threshold
+               $numLagged = 0;
+               $lagLimit = $this->getConfig()->get( 'APIMaxLagThreshold' );
+               $laggedServers = array();
+               $loadBalancer = wfGetLB();
+               foreach ( $loadBalancer->getLagTimes() as $serverIndex => $lag ) {
+                       if ( $lag > $lagLimit ) {
+                               ++$numLagged;
+                               $laggedServers[] = $loadBalancer->getServerName( $serverIndex ) . " ({$lag}s)";
                        }
                }
+
+               // If a majority of slaves are too lagged then disallow writes
+               $slaveCount = wfGetLB()->getServerCount() - 1;
+               if ( $numLagged >= ceil( $slaveCount / 2 ) ) {
+                       $laggedServers = join( ', ', $laggedServers );
+                       wfDebugLog(
+                               'api-readonly',
+                               "Api request failed as read only because the following DBs are lagged: $laggedServers"
+                       );
+
+                       $parsed = $this->parseMsg( array( 'readonlytext' ) );
+                       $this->dieUsage(
+                               $parsed['info'],
+                               $parsed['code'],
+                               /* http error */
+                               0,
+                               array( 'readonlyreason' => "Waiting for $numLagged lagged database(s)" )
+                       );
+               }
        }
 
        /**
@@ -1263,7 +1281,9 @@ class ApiMain extends ApiBase {
                $module = $this->setupModule();
                $this->mModule = $module;
 
-               $this->setRequestExpectations( $module );
+               if ( !$this->mInternalMode ) {
+                       $this->setRequestExpectations( $module );
+               }
 
                $this->checkExecutePermissions( $module );
 
diff --git a/includes/api/ApiMergeHistory.php b/includes/api/ApiMergeHistory.php
new file mode 100644 (file)
index 0000000..8fa9d28
--- /dev/null
@@ -0,0 +1,143 @@
+<?php
+/**
+ *
+ *
+ * Created on Dec 29, 2015
+ *
+ * Copyright © 2015 Geoffrey Mon <geofbot@gmail.com>
+ *
+ * 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
+ */
+
+/**
+ * API Module to merge page histories
+ * @ingroup API
+ */
+class ApiMergeHistory extends ApiBase {
+
+       public function execute() {
+               $this->useTransactionalTimeLimit();
+
+               $user = $this->getUser();
+               $params = $this->extractRequestParams();
+
+               $this->requireOnlyOneParameter( $params, 'from', 'fromid' );
+               $this->requireOnlyOneParameter( $params, 'to', 'toid' );
+
+               // Get page objects (nonexistant pages get caught in MergeHistory::isValidMerge())
+               if ( isset( $params['from'] ) ) {
+                       $fromTitle = Title::newFromText( $params['from'] );
+                       if ( !$fromTitle || $fromTitle->isExternal() ) {
+                               $this->dieUsageMsg( array( 'invalidtitle', $params['from'] ) );
+                       }
+               } elseif ( isset( $params['fromid'] ) ) {
+                       $fromTitle = Title::newFromID( $params['fromid'] );
+                       if ( !$fromTitle ) {
+                               $this->dieUsageMsg( array( 'nosuchpageid', $params['fromid'] ) );
+                       }
+               }
+
+               if ( isset( $params['to'] ) ) {
+                       $toTitle = Title::newFromText( $params['to'] );
+                       if ( !$toTitle || $toTitle->isExternal() ) {
+                               $this->dieUsageMsg( array( 'invalidtitle', $params['to'] ) );
+                       }
+               } elseif ( isset( $params['toid'] ) ) {
+                       $toTitle = Title::newFromID( $params['toid'] );
+                       if ( !$toTitle ) {
+                               $this->dieUsageMsg( array( 'nosuchpageid', $params['toid'] ) );
+                       }
+               }
+
+               $reason = $params['reason'];
+               $timestamp = $params['timestamp'];
+
+               // Merge!
+               $status = $this->merge( $fromTitle, $toTitle, $timestamp, $reason );
+               if ( !$status->isOK() ) {
+                       $this->dieStatus( $status );
+               }
+
+               $r = array(
+                       'from' => $fromTitle->getPrefixedText(),
+                       'to' => $toTitle->getPrefixedText(),
+                       'timestamp' => wfTimestamp( TS_ISO_8601, $params['timestamp'] ),
+                       'reason' => $params['reason']
+               );
+               $result = $this->getResult();
+
+               $result->addValue( null, $this->getModuleName(), $r );
+       }
+
+       /**
+        * @param Title $from
+        * @param Title $to
+        * @param string $timestamp
+        * @param string $reason
+        * @return Status
+        */
+       protected function merge( Title $from, Title $to, $timestamp, $reason ) {
+               $mh = new MergeHistory( $from, $to, $timestamp );
+
+               return $mh->merge( $this->getUser(), $reason );
+       }
+
+       public function mustBePosted() {
+               return true;
+       }
+
+       public function isWriteMode() {
+               return true;
+       }
+
+       public function getAllowedParams() {
+               return array(
+                       'from' => null,
+                       'fromid' => array(
+                               ApiBase::PARAM_TYPE => 'integer'
+                       ),
+                       'to' => null,
+                       'toid' => array(
+                               ApiBase::PARAM_TYPE => 'integer'
+                       ),
+                       'timestamp' => array(
+                               ApiBase::PARAM_TYPE => 'timestamp'
+                       ),
+                       'reason' => '',
+               );
+       }
+
+       public function needsToken() {
+               return 'csrf';
+       }
+
+       protected function getExamplesMessages() {
+               return array(
+                       'action=mergehistory&from=Oldpage&to=Newpage&token=123ABC&' .
+                       'reason=Reason'
+                       => 'apihelp-mergehistory-example-merge',
+                       'action=mergehistory&from=Oldpage&to=Newpage&token=123ABC&' .
+                       'reason=Reason&timestamp=2015-12-31T04%3A37%3A41Z' // TODO
+                       => 'apihelp-mergehistory-example-merge-timestamp',
+               );
+       }
+
+       public function getHelpUrls() {
+               return 'https://www.mediawiki.org/wiki/API:Mergehistory';
+       }
+}
index 5c3434f..f24a7cc 100644 (file)
@@ -63,15 +63,57 @@ interface IApiMessage extends MessageSpecifier {
        public function setApiData( array $data );
 }
 
+/**
+ * Trait to implement the IApiMessage interface for Message subclasses
+ * @since 1.27
+ * @ingroup API
+ */
+trait ApiMessageTrait {
+       protected $apiCode = null;
+       protected $apiData = array();
+
+       public function getApiCode() {
+               return $this->apiCode === null ? $this->getKey() : $this->apiCode;
+       }
+
+       public function setApiCode( $code, array $data = null ) {
+               $this->apiCode = $code;
+               if ( $data !== null ) {
+                       $this->setApiData( $data );
+               }
+       }
+
+       public function getApiData() {
+               return $this->apiData;
+       }
+
+       public function setApiData( array $data ) {
+               $this->apiData = $data;
+       }
+
+       public function serialize() {
+               return serialize( array(
+                       'parent' => parent::serialize(),
+                       'apiCode' => $this->apiCode,
+                       'apiData' => $this->apiData,
+               ) );
+       }
+
+       public function unserialize( $serialized ) {
+               $data = unserialize( $serialized );
+               parent::unserialize( $data['parent'] );
+               $this->apiCode = $data['apiCode'];
+               $this->apiData = $data['apiData'];
+       }
+}
+
 /**
  * Extension of Message implementing IApiMessage
  * @since 1.25
  * @ingroup API
- * @todo: Would be nice to use a Trait here to avoid code duplication
  */
 class ApiMessage extends Message implements IApiMessage {
-       protected $apiCode = null;
-       protected $apiData = array();
+       use ApiMessageTrait;
 
        /**
         * Create an IApiMessage for the message
@@ -119,51 +161,15 @@ class ApiMessage extends Message implements IApiMessage {
                $this->apiCode = $code;
                $this->apiData = (array)$data;
        }
-
-       public function getApiCode() {
-               return $this->apiCode === null ? $this->getKey() : $this->apiCode;
-       }
-
-       public function setApiCode( $code, array $data = null ) {
-               $this->apiCode = $code;
-               if ( $data !== null ) {
-                       $this->setApiData( $data );
-               }
-       }
-
-       public function getApiData() {
-               return $this->apiData;
-       }
-
-       public function setApiData( array $data ) {
-               $this->apiData = $data;
-       }
-
-       public function serialize() {
-               return serialize( array(
-                       'parent' => parent::serialize(),
-                       'apiCode' => $this->apiCode,
-                       'apiData' => $this->apiData,
-               ) );
-       }
-
-       public function unserialize( $serialized ) {
-               $data = unserialize( $serialized );
-               parent::unserialize( $data['parent'] );
-               $this->apiCode = $data['apiCode'];
-               $this->apiData = $data['apiData'];
-       }
 }
 
 /**
  * Extension of RawMessage implementing IApiMessage
  * @since 1.25
  * @ingroup API
- * @todo: Would be nice to use a Trait here to avoid code duplication
  */
 class ApiRawMessage extends RawMessage implements IApiMessage {
-       protected $apiCode = null;
-       protected $apiData = array();
+       use ApiMessageTrait;
 
        /**
         * @param RawMessage|string|array $msg
@@ -189,38 +195,4 @@ class ApiRawMessage extends RawMessage implements IApiMessage {
                $this->apiCode = $code;
                $this->apiData = (array)$data;
        }
-
-       public function getApiCode() {
-               return $this->apiCode === null ? $this->getKey() : $this->apiCode;
-       }
-
-       public function setApiCode( $code, array $data = null ) {
-               $this->apiCode = $code;
-               if ( $data !== null ) {
-                       $this->setApiData( $data );
-               }
-       }
-
-       public function getApiData() {
-               return $this->apiData;
-       }
-
-       public function setApiData( array $data ) {
-               $this->apiData = $data;
-       }
-
-       public function serialize() {
-               return serialize( array(
-                       'parent' => parent::serialize(),
-                       'apiCode' => $this->apiCode,
-                       'apiData' => $this->apiData,
-               ) );
-       }
-
-       public function unserialize( $serialized ) {
-               $data = unserialize( $serialized );
-               parent::unserialize( $data['parent'] );
-               $this->apiCode = $data['apiCode'];
-               $this->apiData = $data['apiData'];
-       }
 }
index 5ce43cc..ff5707e 100644 (file)
@@ -123,9 +123,12 @@ class ApiOpenSearch extends ApiBase {
         * @param array &$results Put results here. Keys have to be integers.
         */
        protected function search( $search, $limit, $namespaces, $resolveRedir, &$results ) {
-               // Find matching titles as Title objects
-               $searcher = new TitlePrefixSearch;
-               $titles = $searcher->searchWithVariants( $search, $limit, $namespaces );
+
+               $searchEngine = SearchEngine::create();
+               $searchEngine->setLimitOffset( $limit );
+               $searchEngine->setNamespaces( $namespaces );
+               $titles = $searchEngine->extractTitles( $searchEngine->completionSearchWithVariants( $search ) );
+
                if ( !$titles ) {
                        return;
                }
index c6abf40..cb08ec1 100644 (file)
@@ -305,6 +305,10 @@ class ApiPageSet extends ApiBase {
                        $pageFlds['page_content_model'] = null;
                }
 
+               if ( $this->getConfig()->get( 'PageLanguageUseDB' ) ) {
+                       $pageFlds['page_lang'] = null;
+               }
+
                // only store non-default fields
                $this->mRequestedPageFields = array_diff_key( $this->mRequestedPageFields, $pageFlds );
 
index 18ca0ab..a8e5629 100644 (file)
@@ -338,6 +338,8 @@ class ApiParamInfo extends ApiBase {
                                        if ( isset( $settings[ApiBase::PARAM_SUBMODULE_PARAM_PREFIX] ) ) {
                                                $item['submoduleparamprefix'] = $settings[ApiBase::PARAM_SUBMODULE_PARAM_PREFIX];
                                        }
+                               } elseif ( $settings[ApiBase::PARAM_TYPE] === 'tags' ) {
+                                       $item['type'] = ChangeTags::listExplicitlyDefinedTags();
                                } else {
                                        $item['type'] = $settings[ApiBase::PARAM_TYPE];
                                }
index 286c18e..27690ff 100644 (file)
@@ -38,7 +38,14 @@ class ApiQueryInfo extends ApiQueryBase {
                $fld_notificationtimestamp = false,
                $fld_preload = false, $fld_displaytitle = false;
 
-       private $params, $titles, $missing, $everything;
+       private $params;
+
+       /** @var Title[] */
+       private $titles;
+       /** @var Title[] */
+       private $missing;
+       /** @var Title[] */
+       private $everything;
 
        private $pageRestrictions, $pageIsRedir, $pageIsNew, $pageTouched,
                $pageLatest, $pageLength;
index 1f992f8..ca85fe5 100644 (file)
@@ -40,63 +40,41 @@ class ApiQueryPageProps extends ApiQueryBase {
        public function execute() {
                # Only operate on existing pages
                $pages = $this->getPageSet()->getGoodTitles();
-               if ( !count( $pages ) ) {
-                       # Nothing to do
-                       return;
-               }
 
                $this->params = $this->extractRequestParams();
-
-               $this->addTables( 'page_props' );
-               $this->addFields( array( 'pp_page', 'pp_propname', 'pp_value' ) );
-               $this->addWhereFld( 'pp_page', array_keys( $pages ) );
-
                if ( $this->params['continue'] ) {
-                       $this->addWhere( 'pp_page >=' . intval( $this->params['continue'] ) );
-               }
-
-               if ( $this->params['prop'] ) {
-                       $this->addWhereFld( 'pp_propname', $this->params['prop'] );
+                       $continueValue = intval( $this->params['continue'] );
+                       $this->dieContinueUsageIf( strval( $continueValue ) !== $this->params['continue'] );
+                       $filteredPages = array();
+                       foreach ( $pages as $id => $page ) {
+                               if ( $id >= $continueValue ) {
+                                       $filteredPages[$id] = $page;
+                               }
+                       }
+                       $pages = $filteredPages;
                }
 
-               # Force a sort order to ensure that properties are grouped by page
-               # But only if pp_page is not constant in the WHERE clause.
-               if ( count( $pages ) > 1 ) {
-                       $this->addOption( 'ORDER BY', 'pp_page' );
+               if ( !count( $pages ) ) {
+                       # Nothing to do
+                       return;
                }
 
-               $res = $this->select( __METHOD__ );
-               $currentPage = 0; # Id of the page currently processed
+               $pageProps = PageProps::getInstance();
                $props = array();
                $result = $this->getResult();
+               if ( $this->params['prop'] ) {
+                       $propnames = $this->params['prop'];
+                       $properties = $pageProps->getProperties( $pages, $propnames );
+               } else {
+                       $properties = $pageProps->getAllProperties( $pages );
+               }
 
-               foreach ( $res as $row ) {
-                       if ( $currentPage != $row->pp_page ) {
-                               # Different page than previous row, so add the properties to
-                               # the result and save the new page id
-
-                               if ( $currentPage ) {
-                                       if ( !$this->addPageProps( $result, $currentPage, $props ) ) {
-                                               # addPageProps() indicated that the result did not fit
-                                               # so stop adding data. Reset props so that it doesn't
-                                               # get added again after loop exit
-
-                                               $props = array();
-                                               break;
-                                       }
-
-                                       $props = array();
-                               }
+               ksort( $properties );
 
-                               $currentPage = $row->pp_page;
+               foreach ( $properties as $page => $props ) {
+                       if ( !$this->addPageProps( $result, $page, $props ) ) {
+                               break;
                        }
-
-                       $props[$row->pp_propname] = $row->pp_value;
-               }
-
-               if ( count( $props ) ) {
-                       # Add any remaining properties to the results
-                       $this->addPageProps( $result, $currentPage, $props );
                }
        }
 
index 25ff07c..1dac740 100644 (file)
@@ -45,8 +45,11 @@ class ApiQueryPrefixSearch extends ApiQueryGeneratorBase {
                $namespaces = $params['namespace'];
                $offset = $params['offset'];
 
-               $searcher = new TitlePrefixSearch;
-               $titles = $searcher->searchWithVariants( $search, $limit + 1, $namespaces, $offset );
+               $searchEngine = SearchEngine::create();
+               $searchEngine->setLimitOffset( $limit + 1, $offset );
+               $searchEngine->setNamespaces( $namespaces );
+               $titles = $searchEngine->extractTitles( $searchEngine->completionSearchWithVariants( $search ) );
+
                if ( $resultPageSet ) {
                        $resultPageSet->setRedirectMergePolicy( function( array $current, array $new ) {
                                if ( !isset( $current['index'] ) || $new['index'] < $current['index'] ) {
index 0fa2e31..ab2c4ef 100644 (file)
@@ -75,7 +75,8 @@ class ApiRollback extends ApiBase {
                        $token,
                        $params['markbot'],
                        $details,
-                       $user
+                       $user,
+                       $params['tags']
                );
 
                if ( $retval ) {
@@ -91,10 +92,6 @@ class ApiRollback extends ApiBase {
                // Watch pages
                $this->setWatch( $watch, $titleObj, 'watchrollback' );
 
-               if ( count( $params['tags'] ) ) {
-                       ChangeTags::addTags( $params['tags'], null, intval( $details['newid'] ), null, null );
-               }
-
                $info = array(
                        'title' => $titleObj->getPrefixedText(),
                        'pageid' => intval( $details['current']->getPage() ),
@@ -122,7 +119,7 @@ class ApiRollback extends ApiBase {
                                ApiBase::PARAM_TYPE => 'integer'
                        ),
                        'tags' => array(
-                               ApiBase::PARAM_TYPE => ChangeTags::listExplicitlyDefinedTags(),
+                               ApiBase::PARAM_TYPE => 'tags',
                                ApiBase::PARAM_ISMULTI => true,
                        ),
                        'user' => array(
index 4bf799e..c1a6810 100644 (file)
  */
 class ApiTag extends ApiBase {
 
-       protected function getAvailableTags() {
-               return ChangeTags::listExplicitlyDefinedTags();
-       }
-
        public function execute() {
                $params = $this->extractRequestParams();
                $user = $this->getUser();
@@ -150,7 +146,7 @@ class ApiTag extends ApiBase {
                                ApiBase::PARAM_ISMULTI => true,
                        ),
                        'add' => array(
-                               ApiBase::PARAM_TYPE => $this->getAvailableTags(),
+                               ApiBase::PARAM_TYPE => 'tags',
                                ApiBase::PARAM_ISMULTI => true,
                        ),
                        'remove' => array(
index 82f66a8..d674846 100644 (file)
@@ -703,6 +703,13 @@ class ApiUpload extends ApiBase {
                        $watch = true;
                }
 
+               if ( $this->mParams['tags'] ) {
+                       $status = ChangeTags::canAddTagsAccompanyingChange( $this->mParams['tags'], $this->getUser() );
+                       if ( !$status->isOK() ) {
+                               $this->dieStatus( $status );
+                       }
+               }
+
                // No errors, no warnings: do the upload
                if ( $this->mParams['async'] ) {
                        $progress = UploadBase::getSessionStatus( $this->getUser(), $this->mParams['filekey'] );
@@ -720,6 +727,7 @@ class ApiUpload extends ApiBase {
                                        'filename' => $this->mParams['filename'],
                                        'filekey' => $this->mParams['filekey'],
                                        'comment' => $this->mParams['comment'],
+                                       'tags' => $this->mParams['tags'],
                                        'text' => $this->mParams['text'],
                                        'watch' => $watch,
                                        'session' => $this->getContext()->exportSession()
@@ -730,7 +738,7 @@ class ApiUpload extends ApiBase {
                } else {
                        /** @var $status Status */
                        $status = $this->mUpload->performUpload( $this->mParams['comment'],
-                               $this->mParams['text'], $watch, $this->getUser() );
+                               $this->mParams['text'], $watch, $this->getUser(), $this->mParams['tags'] );
 
                        if ( !$status->isGood() ) {
                                $error = $status->getErrorsArray();
@@ -764,6 +772,10 @@ class ApiUpload extends ApiBase {
                        'comment' => array(
                                ApiBase::PARAM_DFLT => ''
                        ),
+                       'tags' => array(
+                               ApiBase::PARAM_TYPE => 'tags',
+                               ApiBase::PARAM_ISMULTI => true,
+                       ),
                        'text' => array(
                                ApiBase::PARAM_TYPE => 'text',
                        ),
index 1680544..0d965e6 100644 (file)
@@ -19,7 +19,7 @@
                        "Luke081515"
                ]
        },
-       "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:API:Main_page/de|Dokumentation]]\n* [[mw:API:FAQ/de|Häufig gestellte Fragen]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Mailingliste]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API-Ankündigungen]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Fehlerberichte und Anfragen]\n</div>\n<strong>Status:</strong> Alle auf dieser Seite gezeigten Funktionen sollten funktionieren, allerdings ist die API in aktiver Entwicklung und kann sich zu jeder Zeit ändern. Abonniere die [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ MediaWiki-API-Ankündigungs-Mailingliste], um über Aktualisierungen informiert zu werden.\n\n<strong>Fehlerhafte Anfragen:</strong> Wenn fehlerhafte Anfragen an die API gesendet werden, wird ein HTTP-Header mit dem Schlüssel „MediaWiki-API-Error“ gesendet. Der Wert des Headers und der Fehlercode werden auf den gleichen Wert gesetzt. Für weitere Informationen siehe [[mw:API:Errors_and_warnings|API: Fehler und Warnungen]].",
+       "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:API:Main_page|Dokumentation]]\n* [[mw:API:FAQ|Häufig gestellte Fragen]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Mailingliste]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API-Ankündigungen]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Fehlerberichte und Anfragen]\n</div>\n<strong>Status:</strong> Alle auf dieser Seite gezeigten Funktionen sollten funktionieren, allerdings ist die API in aktiver Entwicklung und kann sich zu jeder Zeit ändern. Abonniere die [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ MediaWiki-API-Ankündigungs-Mailingliste], um über Aktualisierungen informiert zu werden.\n\n<strong>Fehlerhafte Anfragen:</strong> Wenn fehlerhafte Anfragen an die API gesendet werden, wird ein HTTP-Header mit dem Schlüssel „MediaWiki-API-Error“ gesendet. Der Wert des Headers und der Fehlercode werden auf den gleichen Wert gesetzt. Für weitere Informationen siehe [[mw:API:Errors_and_warnings|API: Fehler und Warnungen]].\n\n<strong>Testen:</strong> Zum einfachen Testen von API-Anfragen, siehe [[Special:ApiSandbox]].",
        "apihelp-main-param-action": "Auszuführende Aktion.",
        "apihelp-main-param-format": "Format der Ausgabe.",
        "apihelp-main-param-maxlag": "maxlag kann verwendet werden, wenn MediaWiki auf einem datenbankreplizierten Cluster installiert ist. Um weitere Replikationsrückstände zu verhindern, lässt dieser Parameter den Client warten, bis der Replikationsrückstand kleiner als der angegebene Wert (in Sekunden) ist. Bei einem größerem Rückstand wird der Fehlercode <samp>maxlag</samp> zurückgegeben mit einer Nachricht wie <samp>Waiting for $host: $lag seconds lagged</samp>.<br />Siehe [[mw:Manual:Maxlag_parameter|Handbuch: Maxlag parameter]] für weitere Informationen.",
        "apihelp-managetags-example-delete": "Löscht die <kbd>vandlaism</kbd>-Markierung mit der Begründung <kbd>Misspelt</kbd>.",
        "apihelp-managetags-example-activate": "Aktiviert eine Markierung namens <kbd>spam</kbd> mit der Begründung <kbd>For use in edit patrolling</kbd> (für die Eingangskontrolle).",
        "apihelp-managetags-example-deactivate": "Deaktiviert eine Markierung namens <kbd>spam</kbd> mit der Begründung <kbd>No longer required</kbd> (nicht mehr benötigt).",
+       "apihelp-mergehistory-description": "Führt Versionsgeschichten von Seiten zusammen.",
        "apihelp-move-description": "Eine Seite verschieben.",
        "apihelp-move-param-from": "Titel der zu verschiebenden Seite. Kann nicht zusammen mit <var>$1fromid</var> verwendet werden.",
        "apihelp-move-param-fromid": "Seitenkennung der zu verschiebenden Seite. Kann nicht zusammen mit <var>$1from</var> verwendet werden.",
        "apihelp-query+iwbacklinks-paramvalue-prop-iwtitle": "Ergänzt den Titel des Interwikis.",
        "apihelp-query+iwbacklinks-param-dir": "Die Auflistungsrichtung.",
        "apihelp-query+iwlinks-paramvalue-prop-url": "Ergänzt die vollständige URL.",
+       "apihelp-query+iwlinks-param-limit": "Wie viele Interwiki-Links zurückgegeben werden sollen.",
+       "apihelp-query+iwlinks-param-prefix": "Gibt nur Interwiki-Links mit diesem Präfix zurück.",
        "apihelp-query+iwlinks-param-dir": "Die Auflistungsrichtung.",
        "apihelp-query+langbacklinks-param-limit": "Wie viele Gesamtseiten zurückgegeben werden sollen.",
        "apihelp-query+langbacklinks-param-dir": "Die Auflistungsrichtung.",
        "apihelp-query+langlinks-param-limit": "Wie viele Sprachlinks zurückgegeben werden sollen.",
        "apihelp-query+langlinks-paramvalue-prop-url": "Ergänzt die vollständige URL.",
        "apihelp-query+langlinks-param-dir": "Die Auflistungsrichtung.",
+       "apihelp-query+links-param-limit": "Wie viele Links zurückgegeben werden sollen.",
        "apihelp-query+links-param-dir": "Die Auflistungsrichtung.",
        "apihelp-query+links-example-simple": "Links von der <kbd>Hauptseite</kbd> abrufen",
        "apihelp-query+linkshere-description": "Alle Seiten finden, die auf die angegebenen Seiten verlinken.",
        "apihelp-query+linkshere-paramvalue-prop-pageid": "Die Seitenkennung jeder Seite.",
+       "apihelp-query+linkshere-paramvalue-prop-title": "Titel jeder Seite.",
        "apihelp-query+logevents-description": "Ereignisse von den Logbüchern abrufen.",
+       "apihelp-query+logevents-paramvalue-prop-type": "Ergänzt den Typ des Logbuchereignisses.",
+       "apihelp-query+logevents-paramvalue-prop-comment": "Ergänzt den Kommentar des Logbuchereignisses.",
        "apihelp-query+logevents-example-simple": "Listet die letzten Logbuch-Ereignisse auf.",
        "apihelp-query+pageswithprop-paramvalue-prop-ids": "Fügt die Seitenkennung hinzu.",
        "apihelp-query+pageswithprop-param-limit": "Die maximale Anzahl zurückzugebender Seiten.",
        "apihelp-query+prefixsearch-param-search": "Such-Zeichenfolge.",
        "apihelp-query+prefixsearch-param-offset": "Anzahl der zu überspringenden Ergebnisse.",
+       "apihelp-query+querypage-param-limit": "Anzahl der zurückzugebenden Ergebnisse.",
+       "apihelp-query+recentchanges-description": "Listet die letzten Änderungen auf.",
        "apihelp-query+recentchanges-paramvalue-prop-timestamp": "Ergänzt den Zeitstempel für die Bearbeitung.",
        "apihelp-query+recentchanges-paramvalue-prop-tags": "Listet Markierungen für den Eintrag auf.",
        "apihelp-query+recentchanges-example-simple": "Listet die letzten Änderungen auf.",
        "apihelp-unblock-param-id": "ID der Sperre zum Entsperren (über <kbd>list=blocks</kbd> erhalten). Darf nicht zusammen mit <var>$1user</var> verwendet werden.",
        "apihelp-unblock-param-reason": "Grund für die Freigabe.",
        "apihelp-unblock-example-id": "Sperrkennung #<kbd>105</kbd> freigeben.",
+       "apihelp-undelete-param-title": "Titel der wiederherzustellenden Seite.",
        "apihelp-undelete-param-reason": "Grund für die Wiederherstellung.",
        "apihelp-upload-param-filename": "Ziel-Dateiname.",
        "apihelp-upload-param-text": "Erster Seitentext für neue Dateien.",
        "api-help-permissions": "{{PLURAL:$1|Berechtigung|Berechtigungen}}:",
        "api-help-permissions-granted-to": "{{PLURAL:$1|Gewährt an}}: $2",
        "api-help-right-apihighlimits": "Höhere Beschränkungen in API-Anfragen verwenden (langsame Anfragen: $1; schnelle Anfragen: $2). Die Beschränkungen für langsame Anfragen werden auch auf Mehrwertparameter angewandt.",
+       "api-help-open-in-apisandbox": "<small>[in Spielwiese öffnen]</small>",
        "api-credits-header": "Danksagungen",
        "api-credits": "API-Entwickler:\n* Roan Kattouw (Hauptentwickler von September 2007 bis 2009)\n* Victor Vasiliev\n* Bryan Tong Minh\n* Sam Reed\n* Yuri Astrakhan (Autor, Hauptentwickler von September 2006 bis September 2007)\n* Brad Jorsch (Hauptentwickler seit 2013)\n\nBitte sende deine Kommentare, Vorschläge und Fragen an mediawiki-api@lists.wikimedia.org\noder reiche einen Fehlerbericht auf https://phabricator.wikimedia.org/ ein."
 }
index 1af53fa..6ea643a 100644 (file)
@@ -6,7 +6,7 @@
                ]
        },
 
-       "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:API:Main_page|Documentation]]\n* [[mw:API:FAQ|FAQ]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Mailing list]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API Announcements]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Bugs & requests]\n</div>\n<strong>Status:</strong> All features shown on this page should be working, but the API is still in active development, and may change at any time. Subscribe to [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ the mediawiki-api-announce mailing list] for notice of updates.\n\n<strong>Erroneous requests:</strong> When erroneous requests are sent to the API, an HTTP header will be sent with the key \"MediaWiki-API-Error\" and then both the value of the header and the error code sent back will be set to the same value. For more information see [[mw:API:Errors_and_warnings|API: Errors and warnings]].",
+       "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:API:Main_page|Documentation]]\n* [[mw:API:FAQ|FAQ]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Mailing list]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API Announcements]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Bugs & requests]\n</div>\n<strong>Status:</strong> All features shown on this page should be working, but the API is still in active development, and may change at any time. Subscribe to [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ the mediawiki-api-announce mailing list] for notice of updates.\n\n<strong>Erroneous requests:</strong> When erroneous requests are sent to the API, an HTTP header will be sent with the key \"MediaWiki-API-Error\" and then both the value of the header and the error code sent back will be set to the same value. For more information see [[mw:API:Errors_and_warnings|API: Errors and warnings]].\n\n<strong>Testing:</strong> For ease of testing API requests, see [[Special:ApiSandbox]].",
        "apihelp-main-param-action": "Which action to perform.",
        "apihelp-main-param-format": "The format of the output.",
        "apihelp-main-param-maxlag": "Maximum lag can be used when MediaWiki is installed on a database replicated cluster. To save actions causing any more site replication lag, this parameter can make the client wait until the replication lag is less than the specified value. In case of excessive lag, error code <samp>maxlag</samp> is returned with a message like <samp>Waiting for $host: $lag seconds lagged</samp>.<br />See [[mw:Manual:Maxlag_parameter|Manual: Maxlag parameter]] for more information.",
        "apihelp-managetags-example-activate": "Activate a tag named <kbd>spam</kbd> with the reason <kbd>For use in edit patrolling</kbd>",
        "apihelp-managetags-example-deactivate": "Deactivate a tag named <kbd>spam</kbd> with the reason <kbd>No longer required</kbd>",
 
+       "apihelp-mergehistory-description": "Merge page histories.",
+       "apihelp-mergehistory-param-from": "Title of the page from which history will be merged. Cannot be used together with <var>$1fromid</var>.",
+       "apihelp-mergehistory-param-fromid": "Page ID of the page from which history will be merged. Cannot be used together with <var>$1from</var>.",
+       "apihelp-mergehistory-param-to": "Title of the page to which history will be merged. Cannot be used together with <var>$1toid</var>.",
+       "apihelp-mergehistory-param-toid": "Page ID of the page to which history will be merged. Cannot be used together with <var>$1to</var>.",
+       "apihelp-mergehistory-param-timestamp": "Timestamp up to which revisions will be moved from the source page's history to the destination page's history. If omitted, the entire page history of the source page will be merged into the destination page.",
+       "apihelp-mergehistory-param-reason": "Reason for the history merge.",
+       "apihelp-mergehistory-example-merge": "Merge the entire history of <kbd>Oldpage</kbd> into <kbd>Newpage</kbd>.",
+       "apihelp-mergehistory-example-merge-timestamp": "Merge the page revisions of <kbd>Oldpage</kbd> dating up to <kbd>2015-12-31T04:37:41Z</kbd> into <kbd>Newpage</kbd>.",
+
        "apihelp-move-description": "Move a page.",
        "apihelp-move-param-from": "Title of the page to rename. Cannot be used together with <var>$1fromid</var>.",
        "apihelp-move-param-fromid": "Page ID of the page to rename. Cannot be used together with <var>$1from</var>.",
        "apihelp-upload-description": "Upload a file, or get the status of pending uploads.\n\nSeveral methods are available:\n* Upload file contents directly, using the <var>$1file</var> parameter.\n* Upload the file in pieces, using the <var>$1filesize</var>, <var>$1chunk</var>, and <var>$1offset</var> parameters.\n* Have the MediaWiki server fetch a file from a URL, using the <var>$1url</var> parameter.\n* Complete an earlier upload that failed due to warnings, using the <var>$1filekey</var> parameter.\nNote that the HTTP POST must be done as a file upload (i.e. using <code>multipart/form-data</code>) when sending the <var>$1file</var>.",
        "apihelp-upload-param-filename": "Target filename.",
        "apihelp-upload-param-comment": "Upload comment. Also used as the initial page text for new files if <var>$1text</var> is not specified.",
+       "apihelp-upload-param-tags": "Change tags to apply to the upload log entry and file page revision.",
        "apihelp-upload-param-text": "Initial page text for new files.",
        "apihelp-upload-param-watch": "Watch the page.",
        "apihelp-upload-param-watchlist": "Unconditionally add or remove the page from the current user's watchlist, use preferences or do not change watch.",
        "apihelp-watch-example-generator": "Watch the first few pages in the main namespace.",
 
        "apihelp-format-example-generic": "Return the query result in the $1 format.",
+       "apihelp-format-param-wrappedhtml": "Return the pretty-printed HTML and associated ResourceLoader modules as a JSON object.",
        "apihelp-json-description": "Output data in JSON format.",
        "apihelp-json-param-callback": "If specified, wraps the output into a given function call. For safety, all user-specific data will be restricted.",
        "apihelp-json-param-utf8": "If specified, encodes most (but not all) non-ASCII characters as UTF-8 instead of replacing them with hexadecimal escape sequences. Default when <var>formatversion</var> is not <kbd>1</kbd>.",
        "api-help-permissions": "{{PLURAL:$1|Permission|Permissions}}:",
        "api-help-permissions-granted-to": "{{PLURAL:$1|Granted to}}: $2",
        "api-help-right-apihighlimits": "Use higher limits in API queries (slow queries: $1; fast queries: $2). The limits for slow queries also apply to multivalue parameters.",
+       "api-help-open-in-apisandbox": "<small>[open in sandbox]</small>",
 
        "api-credits-header": "Credits",
        "api-credits": "API developers:\n* Yuri Astrakhan (creator, lead developer Sep 2006–Sep 2007)\n* Roan Kattouw (lead developer Sep 2007–2009)\n* Victor Vasiliev\n* Bryan Tong Minh\n* Sam Reed\n* Brad Jorsch (lead developer 2013–present)\n\nPlease send your comments, suggestions and questions to mediawiki-api@lists.wikimedia.org\nor file a bug report at https://phabricator.wikimedia.org/."
index 253edc9..9e60fb9 100644 (file)
                        "Csbotero",
                        "Chris TR",
                        "Ncontinanza",
-                       "Poco a poco"
+                       "Poco a poco",
+                       "YoViajo"
                ]
        },
-       "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:API:Main_page|Documentación]]\n* [[mw:API:FAQ|Preguntas frecuentes]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Lista de correos]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API de anuncios]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Errores y peticiones]\n</div>\n<strong>Estado:</strong> Todas las características que se muestran en esta página debería funcionar, pero la API aún está en desarrollo activo y puede cambiar en cualquier momento. Suscríbete a [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ la lista de correo de mediawiki-api-announce] para estar al día de las actualizaciones.\n\n<strong>Solicitudes erróneas:</strong> Cuando se envían solicitudes erróneas a la API, se envía un encabezado HTTP con la clave \"MediaWiki-API-Error\" y ambos valores, del encabezado y el código de error, se establecerán en el mismo valor. Para más información, véase [[mw:API:Errors_and_warnings|API: Errores y advertencias]].",
+       "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:API:Main_page|Documentación]]\n* [[mw:API:FAQ|Preguntas frecuentes]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Lista de correos]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API de anuncios]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Errores y peticiones]\n</div>\n<strong>Estado:</strong> Todas las características que se muestran en esta página debería funcionar, pero la API aún está en desarrollo activo y puede cambiar en cualquier momento. Suscríbete a [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ la lista de correo de mediawiki-api-announce] para estar al día de las actualizaciones.\n\n<strong>Solicitudes erróneas:</strong> Cuando se envían solicitudes erróneas a la API, se envía un encabezado HTTP con la clave \"MediaWiki-API-Error\" y ambos valores, del encabezado y el código de error, se establecerán en el mismo valor. Para más información, véase [[mw:API:Errors_and_warnings|API: Errores y advertencias]].\n\n<strong>Pruebas:</strong> para facilitar las pruebas de solicitudes a la API, consulta [[Special:ApiSandbox]].",
        "apihelp-main-param-action": "Qué acción se realizará.",
        "apihelp-main-param-format": "El formato de la salida.",
        "apihelp-main-param-smaxage": "Establece el encabezado <code>s-maxage</code> durante estos segundos. Los errores nunca se almacenan en caché.",
        "apihelp-managetags-example-delete": "Eliminar la etiqueta <kbd>vandlaismo</kbd> con el motivo <kbd>mal deletreado</kbd>",
        "apihelp-managetags-example-activate": "Activar una etiqueta llamada <kbd>spam</kbd> con el motivo <kbd>For use in edit patrolling</kbd>",
        "apihelp-managetags-example-deactivate": "Desactivar una etiqueta llamada <kbd>spam</kbd> con el motivo <kbd>No longer required</kbd>",
+       "apihelp-mergehistory-description": "Fusionar historiales de páginas.",
+       "apihelp-mergehistory-param-reason": "Motivo para la fusión del historial.",
        "apihelp-move-description": "Trasladar una página.",
        "apihelp-move-param-from": "Título de la página a renombrar. No se puede utilizar con <var>$1fromid</var>.",
        "apihelp-move-param-fromid": "ID de la página a renombrar. No se puede utilizar con <var>$1from</var>.",
        "apihelp-query+allrevisions-param-namespace": "Listar solo las páginas en este espacio de nombres.",
        "apihelp-query+allrevisions-example-user": "Listar las últimas 50 contribuciones del usuario <kbd>Example</kbd>.",
        "apihelp-query+allrevisions-example-ns-main": "Listar las primeras 50 revisiones en el espacio de nombres principal.",
+       "apihelp-query+mystashedfiles-param-limit": "Cuántos archivos obtener.",
        "apihelp-query+alltransclusions-param-prefix": "Buscar todos los títulos transcluidos que comiencen con este valor.",
        "apihelp-query+alltransclusions-param-prop": "Qué piezas de información incluir:",
        "apihelp-query+alltransclusions-example-unique": "Listar títulos transcluidos de forma única.",
index 42b86d8..265fb66 100644 (file)
@@ -4,13 +4,35 @@
                        "Nike",
                        "MrTapsa",
                        "Pitke",
-                       "Stryn"
+                       "Stryn",
+                       "Jaakkoh"
                ]
        },
        "apihelp-block-description": "Estä käyttäjä.",
        "apihelp-block-param-reason": "Eston syy.",
+       "apihelp-compare-param-fromtitle": "Ensimmäinen vertailtava otsikko.",
+       "apihelp-createaccount-param-name": "Käyttäjätunnus.",
+       "apihelp-createaccount-param-email": "Käyttäjän sähköpostiosoite (valinnainen).",
+       "apihelp-createaccount-param-realname": "Käyttäjän oikea nimi (valinnainen).",
        "apihelp-delete-description": "Poista sivu.",
+       "apihelp-delete-param-watch": "Lisää sivu nykyisen käyttäjän tarkkailulistalle.",
+       "apihelp-edit-description": "Luo ja muokkaa sivuja.",
+       "apihelp-edit-param-text": "Sivun sisältö.",
+       "apihelp-edit-param-minor": "Vähäinen muokkaus.",
+       "apihelp-edit-param-notminor": "Ei-vähäinen muokkaus.",
+       "apihelp-edit-example-edit": "Muokkaa sivua.",
+       "apihelp-emailuser-description": "Lähetä sähköpostia käyttäjälle.",
+       "apihelp-emailuser-param-subject": "Otsikko.",
+       "apihelp-emailuser-param-ccme": "Lähetä kopio tästä viestistä minulle.",
        "apihelp-emailuser-example-email": "Lähetä käyttäjälle <kbd>WikiSysop</kbd> sähköposti, jossa lukee <kbd>Content</kbd>.",
+       "apihelp-expandtemplates-param-title": "Sivun otsikko.",
+       "apihelp-feedrecentchanges-param-limit": "Kerralla näytettävien tulosten enimmäismäärän",
+       "apihelp-feedrecentchanges-param-hideminor": "Piilota vähäiset muokkaukset.",
+       "apihelp-feedrecentchanges-param-hideanons": "Piilota anonyymien käyttäjien tekemät muokkaukset.",
+       "apihelp-feedrecentchanges-param-hideliu": "Piilota rekisteröityneiden käyttäjien tekemät muokkaukset.",
+       "apihelp-filerevert-param-filename": "Kohteen tiedostonimi, ilman \"File:\"-etuliitettä.",
+       "apihelp-filerevert-param-comment": "Lähetä kommentti.",
+       "apihelp-imagerotate-description": "Käännä kuva tai kuvia.",
        "apihelp-query+linkshere-param-show": "Näytä vain kohteet, jotka täyttävät nämä kriteerit:\n;redirect:Näytä vain uudelleenohjaukset.\n;!redirect:Näytä vain ei-uudelleenohjaukset",
        "apihelp-tag-example-rev": "Lisää tunniste <kbd>vandalism</kbd> versioon 123 antamatta perustelua.",
        "apihelp-upload-param-stash": "Mikäli valittu, palvelin säilöö tiedoston väliaikaisesti tallentamisen sijaan."
index 01cc1f7..309724c 100644 (file)
                        "Ash Crow",
                        "L",
                        "Umherirrender",
-                       "Elfix"
+                       "Elfix",
+                       "Lbayle"
                ]
        },
-       "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:API:Main_page|Documentation]]\n* [[mw:API:FAQ|FAQ]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Liste de diffusion]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce Annonces de l’API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Bogues et demandes]\n</div>\n<strong>État :</strong> Toutes les fonctionnalités affichées sur cette page devraient fonctionner, mais l’API est encore en cours de développement et peut changer à tout moment. Inscrivez-vous à [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ la liste de diffusion mediawiki-api-announce] pour être informé des mises à jour.\n\n<strong>Requêtes erronées :</strong> Si des requêtes erronées sont envoyées à l’API, un en-tête HTTP sera renvoyé avec la clé « MediaWiki-API-Error ». La valeur de cet en-tête et le code d’erreur renvoyé prendront la même valeur. Pour plus d’information, voyez [[mw:API:Errors_and_warnings|API: Errors and warnings]].",
+       "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:API:Main_page|Documentation]]\n* [[mw:API:FAQ|FAQ]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Liste de diffusion]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce Annonces de l’API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Bogues et demandes]\n</div>\n<strong>État :</strong> Toutes les fonctionnalités affichées sur cette page devraient fonctionner, mais l’API est encore en cours de développement et peut changer à tout moment. Inscrivez-vous à [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ la liste de diffusion mediawiki-api-announce] pour être informé des mises à jour.\n\n<strong>Requêtes erronées :</strong> Si des requêtes erronées sont envoyées à l’API, un en-tête HTTP sera renvoyé avec la clé « MediaWiki-API-Error ». La valeur de cet en-tête et le code d’erreur renvoyé prendront la même valeur. Pour plus d’information, voyez [[mw:API:Errors_and_warnings|API: Errors and warnings]].\n\n<strong>Test :</strong> Pour faciliter le test des requêtes de l’API, voyez [[Special:ApiSandbox]].",
        "apihelp-main-param-action": "Quelle action effectuer.",
        "apihelp-main-param-format": "Le format de sortie.",
        "apihelp-main-param-maxlag": "La latence maximale peut être utilisée quand MédiaWiki est installé sur un cluster de base de données répliqué. Pour éviter des actions provoquant un supplément de latence de réplication de site, ce paramètre peut faire attendre le client jusqu’à ce que la latence de réplication soit inférieure à une valeur spécifiée. En cas de latence excessive, le code d’erreur <samp>maxlag</samp> est renvoyé avec un message tel que <samp>Attente de $host : $lag secondes de délai</samp>.<br />Voyez [[mw:Manual:Maxlag_parameter|Manuel: Maxlag parameter]] pour plus d’information.",
        "apihelp-managetags-example-delete": "Supprimer la balise <kbd>vandlaism</kbd> avec le motif <kbd>Misspelt</kbd>",
        "apihelp-managetags-example-activate": "Activer une balise nommée <kbd>spam</kbd> avec le motif <kbd>For use in edit patrolling</kbd>",
        "apihelp-managetags-example-deactivate": "Désactiver une balise nommée <kbd>spam</kbd> avec le motif <kbd>No longer required</kbd>",
+       "apihelp-mergehistory-description": "Fusionner les historiques des pages.",
+       "apihelp-mergehistory-param-from": "Titre de la page depuis laquelle l’historique sera fusionné. Impossible à utiliser avec <var>$1fromid</var>.",
+       "apihelp-mergehistory-param-fromid": "ID de la page depuis laquelle l’historique sera fusionné. Impossible à utiliser avec <var>$1from</var>.",
+       "apihelp-mergehistory-param-to": "Titre de la page vers laquelle l’historique sera fusionné. Impossible à utiliser avec <var>$1toid</var>.",
+       "apihelp-mergehistory-param-toid": "ID de la page vers laquelle l’historique sera fusionné. Impossible à utiliser avec <var>$1to</var>.",
+       "apihelp-mergehistory-param-timestamp": "Horodatage jusqu’auquel les révisions seront déplacées de l’historique de la page source vers l’historique de la page de destination. S’il est omis, tout l’historique de la page source sera fusionné avec celui de la page de destination.",
+       "apihelp-mergehistory-param-reason": "Raison pour fusionner l’historique.",
+       "apihelp-mergehistory-example-merge": "Fusionner l’historique complet de  <kbd>AnciennePage</kbd> dans <kbd>NouvellePage</kbd>.",
+       "apihelp-mergehistory-example-merge-timestamp": "Fusionner les révisions de la page <kbd>AnciennePage</kbd> jusqu’au <kbd>2015-12-31T04:37:41Z</kbd> dans <kbd>NouvellePage</kbd>.",
        "apihelp-move-description": "Déplacer une page.",
        "apihelp-move-param-from": "Titre de la page à renommer. Impossible de l’utiliser avec <var>$1fromid</var>.",
        "apihelp-move-param-fromid": "ID de la page à renommer. Impossible à utiliser avec <var>$1from</var>.",
        "apihelp-watch-example-unwatch": "Ne plus suivre la page <kbd>Page principale</kbd>.",
        "apihelp-watch-example-generator": "Suivre les quelques premières pages de l’espace de nom principal",
        "apihelp-format-example-generic": "Renvoyer le résultat de la requête dans le format $1.",
+       "apihelp-format-param-wrappedhtml": "Renvoyer le HTML avec une jolie mise en forme et les modules ResourceLoader associés comme un objet JSON.",
        "apihelp-json-description": "Extraire les données au format JSON.",
        "apihelp-json-param-callback": "Si spécifié, inclut la sortie dans l’appel d’une fonction fournie. Pour plus de sûreté, toutes les données spécifiques à l’utilisateur seront restreintes.",
        "apihelp-json-param-utf8": "Si spécifié, encode la plupart (mais pas tous) des caractères non ASCII en URF-8 au lieu de les remplacer par leur séquence d’échappement hexadécimale. Valeur par défaut quand <var>formatversion</var> ne vaut pas <kbd>1</kbd>.",
        "api-help-permissions": "{{PLURAL:$1|Droit|Droits}} :",
        "api-help-permissions-granted-to": "{{PLURAL:$1|Accordé à}} : $2",
        "api-help-right-apihighlimits": "Utiliser des valeurs plus hautes dans les requêtes de l’API (requêtes lentes : $1 ; requêtes rapides : $2). Les limites pour les requêtes lentes s’appliquent aussi aux paramètres multivalués.",
+       "api-help-open-in-apisandbox": "<small>[ouvrir dans le bac à sable]</small>",
        "api-credits-header": "Remerciements",
        "api-credits": "Développeurs de l’API :\n* Roan Kattouw (développeur en chef Sept. 2007–2009)\n* Victor Vasiliev\n* Bryan Tong Minh\n* Sam Reed\n* Yuri Astrakhan (créateur, développeur en chef Sept. 2006–Sept. 2007)\n* Brad Jorsch (développeur en chef depuis 2013)\n\nVeuillez envoyer vos commentaires, suggestions et questions à mediawiki-api@lists.wikimedia.org\nou remplir un rapport de bogue sur https://phabricator.wikimedia.org/."
 }
index 9e881d8..e66a900 100644 (file)
@@ -13,7 +13,7 @@
                        "Macofe"
                ]
        },
-       "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:API:Main_page|Documentación]]\n* [[mw:API:FAQ|FAQ]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Lista de discusión]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce Anuncios da API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Erros e solicitudes]\n</div>\n<strong>Estado:</strong> Tódalas funcionalidades mostradas nesta páxina deberían estar funcionanado, pero a API aínda está desenrolo, e pode ser modificada en calquera momento. Apúntese na [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ lista de discusión mediawiki-api-announce] para estar informado acerca das actualizacións.\n\n<strong>Solicitudes incorrectas:</strong> Cando se envían solicitudes incorrectas á API, envíase unha cabeceira HTTP coa chave \"MediaWiki-API-Error\" e, a seguir, tanto o valor da cabeceira como o código de erro retornado serán definidos co mesmo valor. Para máis información, consulte [[mw:API:Errors_and_warnings|API: Erros e avisos]].",
+       "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:API:Main_page|Documentación]]\n* [[mw:API:FAQ|FAQ]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Lista de discusión]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce Anuncios da API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Erros e solicitudes]\n</div>\n<strong>Estado:</strong> Tódalas funcionalidades mostradas nesta páxina deberían estar funcionanado, pero a API aínda está desenrolo, e pode ser modificada en calquera momento. Apúntese na [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ lista de discusión mediawiki-api-announce] para estar informado acerca das actualizacións.\n\n<strong>Solicitudes incorrectas:</strong> Cando se envían solicitudes incorrectas á API, envíase unha cabeceira HTTP coa chave \"MediaWiki-API-Error\" e, a seguir, tanto o valor da cabeceira como o código de erro retornado serán definidos co mesmo valor. Para máis información, consulte [[mw:API:Errors_and_warnings|API: Erros e avisos]].\n\n<strong>Test:</strong> Para facilitar as probas das peticións da API, consulte [[Special:ApiSandbox]].",
        "apihelp-main-param-action": "Que acción se realizará.",
        "apihelp-main-param-format": "O formato de saída.",
        "apihelp-main-param-maxlag": "O retardo máximo pode usarse cando MediaWiki está instalada nun cluster de base de datos replicadas. Para gardar accións que causen calquera retardo máis de replicación do sitio, este parámetro pode facer que o cliente espere ata que o retardo de replicación sexa menor que o valor especificado. No caso de retardo excesivo, é devolto o código de erro <samp>maxlag</samp> cunha mensaxe como <samp>esperando por $host: $lag segundos de retardo</samp>.<br />Para máis información, ver [[mw:Manual:Maxlag_parameter|Manual: Maxlag parameter]].",
        "apihelp-managetags-example-delete": "Borrar a etiqueta <kbd>vandalismo</kbd> coa razón <kbd>Erros ortográficos</kbd>",
        "apihelp-managetags-example-activate": "Activar a etiqueta chamada <kbd>spam</kbd> coa razón <kbd>For use in edit patrolling</kbd>",
        "apihelp-managetags-example-deactivate": "Desactivar a etiqueta chamada <kbd>spam</kbd> coa razón <kbd>No longer required</kbd>",
+       "apihelp-mergehistory-description": "Fusionar os historiais das páxinas.",
+       "apihelp-mergehistory-param-from": "Título da páxina desde a que se fusionará o historial. Non pode usarse xunto con <var>$1fromid</var>.",
+       "apihelp-mergehistory-param-fromid": "Identificador da páxina desde a que se fusionará o historial. Non pode usarse xunto con <var>$1from</var>.",
+       "apihelp-mergehistory-param-to": "Título da páxina á que se fusionará o historial. Non pode usarse xunto con <var>$1toid</var>.",
+       "apihelp-mergehistory-param-toid": "Identificador da páxina á que se fusionará o historial. Non pode usarse xunto con <var>$1to</var>.",
+       "apihelp-mergehistory-param-reason": "Razón para a fusión de historiais.",
+       "apihelp-mergehistory-example-merge": "Fusionar o historial enteiro de <kbd>PáxinaVella</kbd> en <kbd>PáxinaNova</kbd>.",
+       "apihelp-mergehistory-example-merge-timestamp": "Fusionar as revisións da páxina <kbd>PáxinaVella</kbd> con data <kbd>2015-12-31T04:37:41Z</kbd> en <kbd>PáxinaNova</kbd>.",
        "apihelp-move-description": "Mover unha páxina.",
        "apihelp-move-param-from": "Título da páxina que quere renomear. Non pode usarse xunto con <var>$1fromid</var>.",
        "apihelp-move-param-fromid": "Identificador da páxina que quere renomear. Non pode usarse xunto con <var>$1from</var>.",
        "apihelp-query+allrevisions-param-generatetitles": "Usado como xenerador, xenera títulos no canto de IDs de revisión.",
        "apihelp-query+allrevisions-example-user": "Listar as últimas 50 contribucións do usuario <kbd>Example</kbd>.",
        "apihelp-query+allrevisions-example-ns-main": "Listar as 50 primeiras revisións do espazo de nomes principal.",
+       "apihelp-query+mystashedfiles-description": "Obter unha lista dos ficheiros da caché de carga do usuario actual.",
        "apihelp-query+mystashedfiles-param-prop": "Que propiedades obter para os ficheiros.",
        "apihelp-query+mystashedfiles-paramvalue-prop-size": "Consultar o tamaño de ficheiro e as dimensións da imaxe.",
        "apihelp-query+mystashedfiles-paramvalue-prop-type": "Consultar o tipo MIME do ficheiro e tipo multimedia.",
        "apihelp-query+mystashedfiles-param-limit": "Cantos ficheiros devolver.",
+       "apihelp-query+mystashedfiles-example-simple": "Obter a clave de ficheiro, tamaño de ficheiro, e tamaño en pixels dos ficheiros na caché de carga do usuario actual.",
        "apihelp-query+alltransclusions-description": "Listar todas as transclusións (páxinas integradas usando &#123;&#123;x&#125;&#125;), incluíndo as eliminadas.",
        "apihelp-query+alltransclusions-param-from": "Título da transclusión na que comezar a enumerar.",
        "apihelp-query+alltransclusions-param-to": "Título da transclusión na que rematar de enumerar.",
        "apihelp-query+info-paramvalue-prop-talkid": "O ID de páxina da páxina de conversa para cada páxina que non é páxina de conversa.",
        "apihelp-query+info-paramvalue-prop-watched": "Listar o estado de vixiancia de cada páxina.",
        "apihelp-query+info-paramvalue-prop-watchers": "O número de vixiantes, se está permitido.",
+       "apihelp-query+info-paramvalue-prop-visitingwatchers": "O nome dos usuarios que vixían cada páxina e que teñen visitado os cambios recentes a esta páxina, se está autorizado.",
        "apihelp-query+info-paramvalue-prop-notificationtimestamp": "O selo de tempo de notificación da lista de vixiancia de cada páxina.",
        "apihelp-query+info-paramvalue-prop-subjectid": "O ID de páxina da páxina pai para cada páxina de conversa.",
        "apihelp-query+info-paramvalue-prop-url": "Devolve unha URL completa, unha URL de modificación, e a URL canónica de cada páxina.",
        "api-help-permissions": "{{PLURAL:$1|Permiso|Permisos}}:",
        "api-help-permissions-granted-to": "{{PLURAL:$1|Concedida a|Concedidas a}}: $2",
        "api-help-right-apihighlimits": "Usar os valores superiores das consultas da API (consultas lentas: $1; consultas rápidas: $2). Os límites para as consultas lentas tamén se aplican ós parámetros multivaluados.",
+       "api-help-open-in-apisandbox": "<small>[abrir en zona de probas]</small>",
        "api-credits-header": "Créditos",
        "api-credits": "Desenvolvedores da API:\n* Roan Kattouw (desenvolvedor principal, set. 2007-2009)\n* Victor Vasiliev\n* Bryan Tong Minh\n* Sam Reed\n* Yuri Astrakhan (creador e desenvolvedor principal, set. 2006-sep. 2007)\n* Brad Jorsch (desenvolvedor principal, 2013-actualidade)\n\nEnvía comentarios, suxerencias e preguntas a mediawiki-api@lists.wikimedia.org\nou informa dun erro en https://phabricator.wikimedia.org/."
 }
index bb8a074..a759054 100644 (file)
@@ -13,7 +13,7 @@
                        "Macofe"
                ]
        },
-       "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:API:Main_page|תיעוד]]\n* [[mw:API:FAQ|שו\"ת]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api רשימת דיוור]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce הודעות על API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R באגים ובקשות]\n</div>\n<strong>מצב:</strong> כל האפשרויות שמוצגות בדף הזה אמורות לעבוד, אבל ה־API עדיין בפיתוח פעיל, ויכול להשתנות בכל זמן. עשו מינוי ל [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ רשימת הדיוור mediawiki-api-announce] להודעות על עדכונים.\n\n<strong>בקשות שגויות:</strong> כשבקשות שגויות נשלחות ל־API, תישלח כותרת HTTP עם המפתח \"MediaWiki-API-Error\" ואז גם הערך של הכותרת וגם קוד השגיאה יוגדרו לאותו ערך. למידע נוסף ר' [[mw:API:Errors_and_warnings|API: שגיאות ואזהרות]].",
+       "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:API:Main_page|תיעוד]]\n* [[mw:API:FAQ|שו\"ת]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api רשימת דיוור]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce הודעות על API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R באגים ובקשות]\n</div>\n<strong>מצב:</strong> כל האפשרויות שמוצגות בדף הזה אמורות לעבוד, אבל ה־API עדיין בפיתוח פעיל, ויכול להשתנות בכל זמן. עשו מינוי ל[https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ רשימת הדיוור mediawiki-api-announce] להודעות על עדכונים.\n\n<strong>בקשות שגויות:</strong> כשבקשות שגויות נשלחות ל־API, תישלח כותרת HTTP עם המפתח \"MediaWiki-API-Error\" ואז גם הערך של הכותרת וגם קוד השגיאה יוגדרו לאותו ערך. למידע נוסף ר' [[mw:API:Errors_and_warnings|API: שגיאות ואזהרות]].\n\n<strong>בדיקה:</strong> לבדיקה קלה יותר של בקשות ר' [[Special:ApiSandbox]].",
        "apihelp-main-param-action": "איזו פעולה לבצע.",
        "apihelp-main-param-format": "תסדיר הפלט.",
        "apihelp-main-param-maxlag": "שיהוי מרבי יכול לשמש כשמדיה־ויקי מותקנת בצביר עם מסד נתונים משוכפל. כדי לחסוך בפעולות שגורמות יותר שיהוי בשכפול אתר, הפרמטר הזה יכול לגרום ללקוח להמתין עד ששיהוי השכפול יורד מתחת לערך שצוין. במקרה של שיהוי מוגזם, קוד השגיאה <samp>maxlag</samp> מוחזר עם הודעה כמו <samp>Waiting for $host: $lag seconds lagged</samp>.<br />ר' [[mw:Manual:Maxlag_parameter|מדריך למשתמש: פרמטר maxlag]] למידע נוסף.",
        "apihelp-managetags-example-delete": "מחיקת התג <kbd>vandlaism</kbd> עם הסיבה <kbd>Misspelt</kbd>",
        "apihelp-managetags-example-activate": "הפעלת התג <kbd>spam</kbd> עם הסיבה <kbd>For use in edit patrolling</kbd>",
        "apihelp-managetags-example-deactivate": "כיבוי התג <kbd>spam</kbd> עם הסיבה <kbd>No longer required</kbd>",
+       "apihelp-mergehistory-description": "מיזוג גרסאות של דפים.",
+       "apihelp-mergehistory-param-from": "כותרת הדף שההיסטוריה שלו תמוזג. לא ניתן להשתמש בזה יחד עם <var>$1fromid</var>.",
+       "apihelp-mergehistory-param-fromid": "מזהה הדף שממנו תמוזג ההיסטוריה. לא ניתן להשתמש בזה יחד עם <var>$1from</var>.",
+       "apihelp-mergehistory-param-to": "כותרת הדף שההיסטוריה תמוזג אליו. לא ניתן להשתמש בזה יחד עם <var>$1toid</var>.",
+       "apihelp-mergehistory-param-toid": "מזהה הדף שההיסטוריה תמוזג אליו. לא ניתן להשתמש בזה יחד עם <var>$1to</var>.",
+       "apihelp-mergehistory-param-timestamp": "חותם־הזמן שהגרסאות עד אליו יועברו מההיסטוריה של דף המקור על ההיסטוריה של דף היעד. אם מושמט, כל ההיסטוריה של דף המקור תמוזג עם דף היעד.",
+       "apihelp-mergehistory-param-reason": "סיבה למיזוג ההיסטוריה.",
+       "apihelp-mergehistory-example-merge": "מיזוג כל ההיסטוריה של <kbd>Oldpage</kbd> אל <kbd>Newpage</kbd>.",
+       "apihelp-mergehistory-example-merge-timestamp": "מיזוג גרסאות הדפים של <kbd>Oldpage</kbd> עד <kbd dir=\"ltr\">2015-12-31T04:37:41Z</kbd> אל <kbd>Newpage</kbd>.",
        "apihelp-move-description": "העברת עמוד.",
        "apihelp-move-param-from": "שם הדף ששמו ישונה. לא יכול לשמש יחד עם <var>$1fromid</var>.",
        "apihelp-move-param-fromid": "מזהה הדף של הדף שצריך לשנות את שמו. לא יכול לשמש עם <var>$1from</var>.",
        "apihelp-watch-example-unwatch": "להפסיק את המעקב אחרי הדף <kbd>Main Page</kbd>.",
        "apihelp-watch-example-generator": "לעקוב אחרי הדפים הראשונים במרחב הראשי.",
        "apihelp-format-example-generic": "להחזיר את תוצאות השאילתה בתסדיר $1.",
+       "apihelp-format-param-wrappedhtml": "החזרת HTML מעוצב ומודולי ResourceLoader משויכים בתור עצם JSON.",
        "apihelp-json-description": "לפלוט נתונים בתסדיר JSON.",
        "apihelp-json-param-callback": "אם זה צוין, עוטף את הפלט לתוך קריאת פונקציה נתונה. למען הבטיחות, כל הנתונים הייחודיים למשתמש יוגבלו.",
        "apihelp-json-param-utf8": "אם זה צוין, רוב התווים שאינם ASCII (אבל לא כולם) יקודדו בתור UTF-8 במקום להתחלף בסדרות חילוף הקסדצימליות. זאת בררת המחדל אם הערך של <var>formatversion</var> הוא לא <kbd>1</kbd>.",
        "api-help-permissions": "{{PLURAL:$1|הרשאה|הרשאות}}:",
        "api-help-permissions-granted-to": "{{PLURAL:$1|הוענק ל|הוענקו ל}}: $2",
        "api-help-right-apihighlimits": "להשתמש במגבלות גבוהות יותר בשאילתות API (שאילתות אטיות: $1; שאילתות מהירות: $2). המגבלות לשאילתות אטיות חלות גם על פרמטרים מרובי־ערכים.",
+       "api-help-open-in-apisandbox": "<small>[פתיחה בארגז חול]</small>",
        "api-credits-header": "קרדיטים",
        "api-credits": "מפתחי ה־API:\n* רואן קטאו (מפתח מוביל 2007–2009)\n* ויקטור וסילייב\n* בריאן טונג מין\n* סאם ריד\n* יורי אסטרחן (יוצר, מפתח מוביל מספטמבר 2006 עד ספטמבר 2007)\n* בראד יורש (מפתח מוביל מאז 2013)\n\nאנא שלחו הערות, הצעות ושאלות לכתובת mediawiki-api@lists.wikimedia.org או כתבו דיווח באג באתר https://phabricator.wikimedia.org."
 }
index dd3c80c..ae4c71a 100644 (file)
@@ -14,7 +14,7 @@
                        "JackLantern"
                ]
        },
-       "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:API:Main_page|Documentazione (in inglese)]]\n* [[mw:API:FAQ|FAQ (in inglese)]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Mailing list]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce Annunci sull'API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Bug & richieste]\n</div>\n<strong>Stato:</strong> Tutte le funzioni e caratteristiche mostrate su questa pagina dovrebbero funzionare, ma l'API è ancora in fase d'attivo sviluppo, e potrebbe cambiare in qualsiasi momenento. Iscriviti alla [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ the mediawiki-api-announce mailing list] per essere informato sugli aggiornamenti.\n\n<strong>Istruzioni sbagliate:</strong> quando vengono impartite all'API delle istruzioni sbagliate, un'intestazione HTTP verrà inviata col messaggio \"MediaWiki-API-Error\" e sia al valore dell'intestazione sia al codice d'errore verrà impostato lo stesso valore. Per maggiori informazioni leggi [[mw:API:Errors_and_warnings|API:Errori ed avvertimenti (in inglese)]].",
+       "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:API:Main_page|Documentazione (in inglese)]]\n* [[mw:API:FAQ|FAQ (in inglese)]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Mailing list]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce Annunci sull'API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Bug & richieste]\n</div>\n<strong>Stato:</strong> Tutte le funzioni e caratteristiche mostrate su questa pagina dovrebbero funzionare, ma l'API è ancora in fase d'attivo sviluppo, e potrebbe cambiare in qualsiasi momenento. Iscriviti alla [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ the mediawiki-api-announce mailing list] per essere informato sugli aggiornamenti.\n\n<strong>Istruzioni sbagliate:</strong> quando vengono impartite all'API delle istruzioni sbagliate, un'intestazione HTTP verrà inviata col messaggio \"MediaWiki-API-Error\" e sia al valore dell'intestazione sia al codice d'errore verrà impostato lo stesso valore. Per maggiori informazioni leggi [[mw:API:Errors_and_warnings|API:Errori ed avvertimenti (in inglese)]].\n\n<strong>Test:</strong> per testare facilmente le richieste API, vedi [[Special:ApiSandbox]].",
        "apihelp-main-param-action": "Azione da compiere.",
        "apihelp-main-param-format": "Formato dell'output.",
        "apihelp-main-param-assert": "Verifica che l'utente sia loggato se si è impostato <kbd>utente</kbd>, o che abbia i permessi di bot se si è impostato <kbd>bot</kbd>.",
        "apihelp-login-param-password": "Password.",
        "apihelp-login-param-domain": "Dominio (opzionale).",
        "apihelp-login-example-login": "Entra.",
+       "apihelp-mergehistory-description": "Unisce cronologie pagine.",
+       "apihelp-mergehistory-param-from": "Il titolo della pagina da cui cronologia sarà unita. Non può essere usato insieme a <var>$1fromid</var>.",
+       "apihelp-mergehistory-param-fromid": "L'ID della pagina da cui cronologia sarà unita. Non può essere usato insieme a <var>$1from</var>.",
+       "apihelp-mergehistory-param-to": "Il titolo della pagina in cui cronologia sarà unita. Non può essere usato insieme a <var>$1toid</var>.",
+       "apihelp-mergehistory-param-toid": "L'ID della pagina in cui cronologia sarà unita. Non può essere usato insieme a <var>$1to</var>.",
+       "apihelp-mergehistory-param-timestamp": "Il timestamp fino a cui le versioni saranno spostate dalla cronologia della pagina di origine a quella della pagina di destinazione. Se omesso, l'intera cronologia della pagina di origine sarà unita nella pagina di destinazione.",
+       "apihelp-mergehistory-param-reason": "Motivo per l'unione della cronologia.",
+       "apihelp-mergehistory-example-merge": "Unisci l'intera cronologia di <kbd>Oldpage</kbd> in <kbd>Newpage</kbd>.",
+       "apihelp-mergehistory-example-merge-timestamp": "Unisci le versioni della pagina <kbd>Oldpage</kbd> fino a <kbd>2015-12-31T04:37:41Z</kbd> in <kbd>Newpage</kbd>.",
        "apihelp-move-description": "Sposta una pagina.",
        "apihelp-move-param-reason": "Motivo della rinomina.",
        "apihelp-move-param-movetalk": "Rinomina la pagina di discussione, se esiste.",
        "apihelp-query+allrevisions-example-ns-main": "Elenca solo le prime 50 versioni nel namespace principale.",
        "apihelp-query+mystashedfiles-param-prop": "Quali proprietà recuperare per il file.",
        "apihelp-query+mystashedfiles-paramvalue-prop-size": "Recupera la dimensione del file e le dimensioni dell'immagine.",
+       "apihelp-query+mystashedfiles-paramvalue-prop-type": "Recupera il tipo MIME del file e il tipo media.",
        "apihelp-query+mystashedfiles-param-limit": "Quanti file restituire.",
        "apihelp-query+alltransclusions-paramvalue-prop-title": "Aggiunge il titolo dell'inclusione.",
        "apihelp-query+alltransclusions-param-limit": "Quanti elementi totali restituire.",
        "apihelp-userrights-param-user": "Nome utente.",
        "apihelp-userrights-param-userid": "ID utente.",
        "apihelp-watch-description": "Aggiunge o rimuove pagine dagli osservati speciali dell'utente attuale.",
+       "apihelp-format-param-wrappedhtml": "Restituisce l'HTML ben formattato e i moduli ResourceLoader associati come un oggetto JSON.",
        "api-pageset-param-titles": "Un elenco di titoli su cui lavorare.",
        "api-pageset-param-pageids": "Un elenco di ID pagina su cui lavorare.",
        "api-pageset-param-revids": "Un elenco di ID versioni su cui lavorare.",
        "api-help-param-no-description": "<span class=\"apihelp-empty\">(nessuna descrizione)</span>",
        "api-help-examples": "{{PLURAL:$1|Esempio|Esempi}}:",
        "api-help-permissions": "{{PLURAL:$1|Permesso|Permessi}}:",
+       "api-help-open-in-apisandbox": "<small>[apri in una sandbox]</small>",
        "api-credits-header": "Crediti"
 }
index 949f4aa..c811f56 100644 (file)
@@ -11,7 +11,7 @@
                        "Macofe"
                ]
        },
-       "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:API:Main_page|Documentation]]\n* [[mw:API:FAQ|FAQ]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api メーリングリスト]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API 告知]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R バグの報告とリクエスト]\n</div>\n<strong>状態:</strong> このページに表示されている機能は全て動作するはずですが、この API は未だ活発に開発されており、変更される可能性があります。アップデートの通知を受け取るには、[https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ the mediawiki-api-announce メーリングリスト]に参加してください。\n\n<strong>誤ったリクエスト:</strong> 誤ったリクエストが API に贈られた場合、\"MediaWiki-API-Error\" HTTP ヘッダーが送信され、そのヘッダーの値と送り返されるエラーコードは同じ値にセットされます。より詳しい情報は [[mw:API:Errors_and_warnings|API: Errors and warnings]] を参照してください。",
+       "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:API:Main_page|説明文書]]\n* [[mw:API:FAQ|よくある質問]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api メーリングリスト]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API 告知]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R バグの報告とリクエスト]\n</div>\n<strong>状態:</strong> このページに表示されている機能は全て動作するはずですが、この API は未だ活発に開発されており、変更される可能性があります。アップデートの通知を受け取るには、[https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ the mediawiki-api-announce メーリングリスト]に参加してください。\n\n<strong>誤ったリクエスト:</strong> 誤ったリクエストが API に送られた場合、\"MediaWiki-API-Error\" HTTP ヘッダーが送信され、そのヘッダーの値と送り返されるエラーコードは同じ値にセットされます。より詳しい情報は [[mw:API:Errors_and_warnings|API: Errors and warnings]] を参照してください。\n\n<strong>テスト:</strong> API のリクエストのテストは、[[Special:ApiSandbox]]で簡単に行えます。",
        "apihelp-main-param-action": "実行する操作です。",
        "apihelp-main-param-format": "出力する形式です。",
        "apihelp-main-param-smaxage": "<code>s-maxage</code> HTTP キャッシュ コントロール ヘッダー に、この秒数を設定します。エラーがキャッシュされることはありません。",
        "apihelp-managetags-example-delete": "<kbd>vandlaism</kbd> タグを <kbd>Misspelt</kbd> という理由で削除する",
        "apihelp-managetags-example-activate": "<kbd>spam</kbd> という名前のタグを <kbd>For use in edit patrolling</kbd> という理由で有効化する",
        "apihelp-managetags-example-deactivate": "<kbd>No longer required</kbd> という理由でタグ <kbd>spam</kbd> を無効化する",
+       "apihelp-mergehistory-description": "ページの履歴を統合する。",
        "apihelp-move-description": "ページを移動します。",
        "apihelp-move-param-from": "移動するページのページ名です。<var>$1fromid</var> とは同時に使用できません。",
        "apihelp-move-param-fromid": "移動するページのページIDです。<var>$1from</var> とは同時に使用できません。",
        "api-help-examples": "{{PLURAL:$1|例}}:",
        "api-help-permissions": "{{PLURAL:$1|権限}}:",
        "api-help-permissions-granted-to": "{{PLURAL:$1|権限を持つグループ}}: $2",
+       "api-help-open-in-apisandbox": "<small>[サンドボックスで開く]</small>",
        "api-credits-header": "クレジット",
        "api-credits": "API の開発者:\n* Roan Kattouw (2007年9月-2009年の主任開発者)\n* Victor Vasiliev\n* Bryan Tong Minh\n* Sam Reed\n* Yuri Astrakhan (作成者、2006年9月-2007年9月の主任開発者)\n* Brad Jorsch (2013年-現在の主任開発者)\n\nコメント、提案、質問は mediawiki-api@lists.wikimedia.org にお送りください。\nバグはこちらへご報告ください: https://phabricator.wikimedia.org/"
 }
index e7bfbe1..8ac4d51 100644 (file)
        "api-help-datatypes-header": "Datentypen",
        "api-help-param-type-user": "Typ: {{PLURAL:$1|1=Benotzernumm|2=Lëscht vu Benotzernimm}}",
        "api-help-examples": "{{PLURAL:$1|Beispill|Beispiler}}:",
-       "api-help-permissions": "{{PLURAL:$1|Autorisatioun|Autorisatiounen}}:"
+       "api-help-permissions": "{{PLURAL:$1|Autorisatioun|Autorisatiounen}}:",
+       "api-help-open-in-apisandbox": "<small>[an der Sandkëscht opmaachen]</small>"
 }
index b300f88..1ce88e4 100644 (file)
        "apihelp-block-param-noemail": "Scanza st'utente 'e mannà mmasciate pe' bbìa d' 'o wiki. (Servisse 'o <code>blockemail</code> buono).",
        "apihelp-block-param-hidename": "Annascunne 'o nomme utente d' 'o riggistro 'e blocche (Addimanna 'e premmesse 'e <code>hideuser</code>).",
        "apihelp-block-param-reblock": "Si l'utente è già bluccato, sovrascrive 'o blocco esistente.",
+       "apihelp-block-param-watchuser": "Vide 'a paggena utente o ll'indirizzo IP 'e ll'utente e paggene 'e chiacchiera.",
+       "apihelp-block-example-ip-simple": "Blocca l'indirizzo IP <kbd>192.0.2.5</kbd> pe' tre gghiuorne p' 'o mutivo <kbd>First strike</kbd>.",
+       "apihelp-block-example-user-complex": "Blocca l'utente <kbd>Vandal</kbd> a tiempo indeterminato c' 'o mutivo <kbd>Vandalism</kbd>, nun 'o ffà crià cunte nuove nè mannà mmasciate e-mail.",
+       "apihelp-checktoken-description": "Cuntrolla 'a validità 'e nu token 'a <kbd>[[Special:ApiHelp/query+tokens|action=query&meta=tokens]]</kbd>.",
        "apihelp-checktoken-param-type": "Tipo 'e token ncurzo 'e test.",
        "apihelp-checktoken-param-token": "Token 'a testà.",
        "apihelp-checktoken-param-maxtokenage": "Massima ammaturità cunzentuta p' 'o token, 'n secunde.",
        "apihelp-createaccount-param-name": "Nomme utente.",
        "apihelp-createaccount-param-password": "Password (sarrà gnurata se mpustato nu <var>$1mailpassword</var>).",
        "apihelp-createaccount-param-domain": "Dumminio pe' ffà autenticaziona 'a fore (opzionale).",
+       "apihelp-createaccount-param-token": "'A criazione 'e token p' 'o cunto se ngarraje dint'a primma richiesta.",
+       "apihelp-createaccount-param-email": "Indirizzo Email 'e ll'utente (opzionale).",
+       "apihelp-createaccount-param-realname": "Nomme overo 'e ll'utente (opzionale).",
+       "apihelp-createaccount-param-mailpassword": "Si mpustato a qualunque valore, na password casuale sarrà mannat'a ll'utente.",
+       "apihelp-createaccount-param-reason": "Raggiona, a facoltativa, d' 'a criaziona 'e nu cunto a mpizzà int' 'e reggistre.",
+       "apihelp-createaccount-param-language": "Codece 'e llengua a mpustà comme predefinita pe' n'utente (opzionale, 'e default fosse 'a lengue d' 'e cuntenute).",
        "apihelp-delete-description": "Scancella 'na paggena.",
        "apihelp-edit-example-edit": "Cagna paggena.",
        "apihelp-emailuser-description": "E-mail a n'utente.",
index 4d4614c..2108b33 100644 (file)
        "apihelp-managetags-example-delete": "{{doc-apihelp-example|managetags|info={{doc-important|The text \"vandlaism\" in this message is intentionally misspelled; the example being documented by this message is the deletion of a misspelled tag.}}}}",
        "apihelp-managetags-example-activate": "{{doc-apihelp-example|managetags}}",
        "apihelp-managetags-example-deactivate": "{{doc-apihelp-example|managetags}}",
+       "apihelp-mergehistory-description": "{{doc-apihelp-description|mergehistory}}",
+       "apihelp-mergehistory-param-from": "{{doc-apihelp-param|mergehistory|from}}",
+       "apihelp-mergehistory-param-fromid": "{{doc-apihelp-param|mergehistory|fromid}}",
+       "apihelp-mergehistory-param-to": "{{doc-apihelp-param|mergehistory|to}}",
+       "apihelp-mergehistory-param-toid": "{{doc-apihelp-param|mergehistory|toid}}",
+       "apihelp-mergehistory-param-timestamp": "{{doc-apihelp-param|mergehistory|timestamp}}",
+       "apihelp-mergehistory-param-reason": "{{doc-apihelp-param|mergehistory|reason}}",
+       "apihelp-mergehistory-example-merge": "{{doc-apihelp-example|mergehistory}}",
+       "apihelp-mergehistory-example-merge-timestamp": "{{doc-apihelp-example|mergehistory}}",
        "apihelp-move-description": "{{doc-apihelp-description|move}}",
        "apihelp-move-param-from": "{{doc-apihelp-param|move|from}}",
        "apihelp-move-param-fromid": "{{doc-apihelp-param|move|fromid}}",
        "apihelp-upload-description": "{{doc-apihelp-description|upload}}",
        "apihelp-upload-param-filename": "{{doc-apihelp-param|upload|filename}}",
        "apihelp-upload-param-comment": "{{doc-apihelp-param|upload|comment}}",
+       "apihelp-upload-param-tags": "{{doc-apihelp-param|upload|tags}}",
        "apihelp-upload-param-text": "{{doc-apihelp-param|upload|text}}",
        "apihelp-upload-param-watch": "{{doc-apihelp-param|upload|watch}}",
        "apihelp-upload-param-watchlist": "{{doc-apihelp-param|upload|watchlist}}",
        "apihelp-watch-example-unwatch": "{{doc-apihelp-example|watch}}",
        "apihelp-watch-example-generator": "{{doc-apihelp-example|watch}}",
        "apihelp-format-example-generic": "{{doc-apihelp-example|format|params=* $1 - Format name|paramstart=2|noseealso=1}}",
+       "apihelp-format-param-wrappedhtml": "{{doc-apihelp-param|format|wrappedhtml|description=the \"wrappedhtml\" parameter in pretty-printing format modules}}",
        "apihelp-json-description": "{{doc-apihelp-description|json|seealso=* {{msg-mw|apihelp-jsonfm-description}}}}",
        "apihelp-json-param-callback": "{{doc-apihelp-param|json|callback}}",
        "apihelp-json-param-utf8": "{{doc-apihelp-param|json|utf8}}",
        "api-help-permissions": "Label for the \"permissions\" section in the main module's help output.\n\nParameters:\n* $1 - Number of permissions displayed\n{{Identical|Permission}}",
        "api-help-permissions-granted-to": "Used to introduce the list of groups each permission is assigned to.\n\nParameters:\n* $1 - Number of groups\n* $2 - List of group names, comma-separated",
        "api-help-right-apihighlimits": "{{technical}}{{doc-right|apihighlimits|prefix=api-help}}\nThis message is used instead of {{msg-mw|right-apihighlimits}} in the API help to display the actual limits.\n\nParameters:\n* $1 - Limit for slow queries\n* $2 - Limit for fast queries",
+       "api-help-open-in-apisandbox": "Text for the link to open an API example in [[Special:ApiSandbox]].",
        "api-credits-header": "Header for the API credits section in the API help output\n{{Identical|Credit}}",
        "api-credits": "API credits text, displayed in the API help output"
 }
index 508a4c4..a692ff2 100644 (file)
@@ -12,7 +12,8 @@
                        "Nzeemin",
                        "INS Pirat",
                        "Macofe",
-                       "Краснорядцева Елена"
+                       "Краснорядцева Елена",
+                       "Iniquity"
                ]
        },
        "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:API:Main_page|Документация]]\n* [[mw:API:FAQ|ЧаВО]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Почтовая рассылка]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce Новости API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Ошибки и запросы]\n</div>\n<strong>Статус:</strong> Все отображаемые на этой странице функции должны работать, однако API находится в статусе активной разработки, и может измениться в любой момент. Подпишитесь на  [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ почтовую рассылку mediawiki-api-announce], чтобы быть в курсе обновлений.\n\n<strong>Ошибочные запросы:</strong> Если API получает запрос с ошибкой, вернётся заголовок HTTP с ключом \"MediaWiki-API-Error\", после чего значение заголовка и код ошибки будут отправлены обратно и установлены в то же значение. Более подробную информацию см. [[mw:API:Errors_and_warnings|API: Ошибки и предупреждения]].",
        "apihelp-login-param-domain": "Домен (необязательно).",
        "apihelp-login-example-login": "Войти",
        "apihelp-logout-description": "Выйти и очистить данные сессии.",
+       "apihelp-mergehistory-description": "Объединение историй правок",
        "apihelp-move-description": "Переместить страницу.",
        "apihelp-move-param-to": "Заголовок, в который следует переименовать страницу.",
        "apihelp-move-param-reason": "Причина переименования.",
        "apihelp-upload-param-url": "URL-Адрес для извлечения файла из.",
        "apihelp-upload-param-offset": "Смещение блока в байтах.",
        "apihelp-upload-param-chunk": "Кусок содержимого.",
-       "apihelp-upload-param-asyncdownload": "Сделать извлечение URL-адреса асинхронно",
        "apihelp-upload-example-url": "Загрузить через URL",
        "apihelp-userrights-description": "Изменить членство в группе пользователей.",
        "apihelp-userrights-param-user": "Имя пользователя",
diff --git a/includes/api/i18n/sh.json b/includes/api/i18n/sh.json
new file mode 100644 (file)
index 0000000..19d31d4
--- /dev/null
@@ -0,0 +1,8 @@
+{
+       "@metadata": {
+               "authors": [
+                       "Conquistador"
+               ]
+       },
+       "apihelp-login-param-password": "Lozinka."
+}
diff --git a/includes/api/i18n/sk.json b/includes/api/i18n/sk.json
new file mode 100644 (file)
index 0000000..823e29f
--- /dev/null
@@ -0,0 +1,8 @@
+{
+       "@metadata": {
+               "authors": [
+                       "Teslaton"
+               ]
+       },
+       "apihelp-main-param-format": "Formát výstupu."
+}
index 55611f0..f6d1b62 100644 (file)
@@ -7,5 +7,6 @@
        "apihelp-block-description": "Blokiraj korisnika.",
        "apihelp-block-param-reason": "Razlog za blokiranje.",
        "apihelp-delete-description": "Obriši stranicu.",
-       "apihelp-edit-param-minor": "Manja izmena."
+       "apihelp-edit-param-minor": "Manja izmena.",
+       "apihelp-feedrecentchanges-param-hidepatrolled": "Sakrij patrolirane izmene."
 }
index a10452d..f611160 100644 (file)
@@ -6,11 +6,13 @@
                        "Uğurkent",
                        "Gorizon",
                        "HakanIST",
-                       "Imabadplayer"
+                       "Imabadplayer",
+                       "İnternion"
                ]
        },
        "apihelp-block-description": "Bir kullanıcıyı engelle.",
        "apihelp-block-param-reason": "Engelleme sebebi.",
+       "apihelp-createaccount-description": "Yeni bir kullanıcı hesabı oluşturun.",
        "apihelp-createaccount-param-name": "Kullanıcı adı.",
        "apihelp-createaccount-param-password": "Parola (ignored if <var>$1mailpassword</var> is set).",
        "apihelp-createaccount-param-email": "Kullanıcının e-posta adresi (isteğe bağlı).",
index 6b36dd8..41ce9c4 100644 (file)
                        "RyRubyy",
                        "Umherirrender",
                        "Apflu",
-                       "Hzy980512"
+                       "Hzy980512",
+                       "PhiLiP"
                ]
        },
-       "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:API:Main_page|文档]]\n* [[mw:API:FAQ|常见问题]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api 邮件列表]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API公告]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R 程序错误与功能请求]\n</div>\n<strong>状态信息:</strong>本页所展示的所有特性都应正常工作,但是API仍在开发当中,将会随时变化。请订阅[https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ mediawiki-api-announce 邮件列表]以便获得更新通知。\n\n<strong>错误请求:</strong>当API收到错误请求时,HTTP header将会返回一个包含\"MediaWiki-API-Error\"的值,随后header的值与error code将会送回并设置为相同的值。详细信息请参阅[[mw:API:Errors_and_warnings|API: 错误与警告]]。",
+       "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:API:Main_page|文档]]\n* [[mw:API:FAQ|常见问题]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api 邮件列表]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API公告]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R 程序错误与功能请求]\n</div>\n<strong>状态信息:</strong>本页所展示的所有特性都应正常工作,但是API仍在开发当中,将会随时变化。请订阅[https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ mediawiki-api-announce 邮件列表]以便获得更新通知。\n\n<strong>错误请求:</strong>当API收到错误请求时,HTTP header将会返回一个包含\"MediaWiki-API-Error\"的值,随后header的值与error code将会送回并设置为相同的值。详细信息请参阅[[mw:API:Errors_and_warnings|API: 错误与警告]]。\n\n<strong>测试中:</strong>测试API请求的易用性,请参见[[Special:ApiSandbox]]。",
        "apihelp-main-param-action": "要执行的操作。",
        "apihelp-main-param-format": "输出的格式。",
        "apihelp-main-param-maxlag": "最大延迟可被用于MediaWiki安装于数据库复制集中。要保存导致更多网站复制延迟的操作,此参数可使客户端等待直到复制延迟少于指定值时。万一发生过多延迟,错误代码<samp>maxlag</samp>会返回消息,例如<samp>等待$host中:延迟$lag秒</samp>。<br />参见[[mw:Manual:Maxlag_parameter|Manual: Maxlag parameter]]以获取更多信息。",
        "apihelp-managetags-example-delete": "删除<kbd>vandlaism</kbd>标签,原因<kbd>Misspelt</kbd>",
        "apihelp-managetags-example-activate": "激活一个名为<kbd>spam</kbd>的标签,原因<kbd>For use in edit patrolling</kbd>",
        "apihelp-managetags-example-deactivate": "停用一个名为<kbd>spam</kbd>的标签,原因<kbd>No longer required</kbd>",
+       "apihelp-mergehistory-description": "合并页面历史。",
+       "apihelp-mergehistory-param-from": "将被合并历史的页面的标题。不能与<var>$1fromid</var>一起使用。",
+       "apihelp-mergehistory-param-fromid": "将被合并历史的页面的页面ID。不能与<var>$1from</var>一起使用。",
+       "apihelp-mergehistory-param-to": "将要合并历史的页面的标题。不能与<var>$1toid</var>一起使用。",
+       "apihelp-mergehistory-param-toid": "将要合并历史的页面的页面ID。不能与<var>$1to</var>一起使用。",
+       "apihelp-mergehistory-param-reason": "历史合并的原因。",
+       "apihelp-mergehistory-example-merge": "将<kbd>Oldpage</kbd>的完整历史合并至<kbd>Newpage</kbd>。",
+       "apihelp-mergehistory-example-merge-timestamp": "将<kbd>Oldpage</kbd>直到<kbd>2015-12-31T04:37:41Z</kbd>的页面修订版本合并至<kbd>Newpage</kbd>。",
        "apihelp-move-description": "移动一个页面。",
        "apihelp-move-param-from": "要重命名的页面标题。不能与<var>$1fromid</var>一起使用。",
        "apihelp-move-param-fromid": "您希望移动的页面ID。不能与<var>$1from</var>一起使用。",
        "apihelp-query+allrevisions-param-generatetitles": "当作为生成器使用时,生成标题而不是修订ID。",
        "apihelp-query+allrevisions-example-user": "列出由用户<kbd>Example</kbd>作出的最近50次贡献。",
        "apihelp-query+allrevisions-example-ns-main": "列举主名字空间中的前50次修订。",
-       "apihelp-query+mystashedfiles-description": "获取当前用户的上传藏匿中的文件列表。",
+       "apihelp-query+mystashedfiles-description": "获取当前用户上传暂存库中的文件列表。",
        "apihelp-query+mystashedfiles-param-prop": "要检索文件的属性。",
+       "apihelp-query+mystashedfiles-paramvalue-prop-size": "检索文件大小和图片尺寸。",
        "apihelp-query+mystashedfiles-paramvalue-prop-type": "检索文件的MIME类型和媒体类型。",
-       "apihelp-query+mystashedfiles-param-limit": "获取多少文件。",
+       "apihelp-query+mystashedfiles-param-limit": "要获取文件的数量。",
+       "apihelp-query+mystashedfiles-example-simple": "获取当前用户上传暂存库中的文件的filekey、大小和像素尺寸。",
        "apihelp-query+alltransclusions-description": "列出所有嵌入页面(使用&#123;&#123;x&#125;&#125;嵌入的页面),包括不存在的。",
        "apihelp-query+alltransclusions-param-from": "要列举的起始嵌入标题。",
        "apihelp-query+alltransclusions-param-to": "要列举的最终嵌入标题。",
        "apihelp-query+filearchive-paramvalue-prop-archivename": "添加用于非最新版本的存档版本的文件名。",
        "apihelp-query+filearchive-example-simple": "显示已删除文件列表。",
        "apihelp-query+filerepoinfo-description": "返回有关wiki配置的图片存储库的元信息。",
-       "apihelp-query+filerepoinfo-param-prop": "要获取的存储库属性(这在一些wiki上可能有更多可用选项):\n;apiurl:链接至API的URL - 对从主机获取图片信息有用。\n;name:The key of the repository - used in e.g. <var>[[mw:Manual:$wgForeignFileRepos|$wgForeignFileRepos]]</var> and [[Special:ApiHelp/query+imageinfo|imageinfo]] return values.\n;displayname:The human-readable name of the repository wiki.\n;rooturl:Root URL for image paths.\n;local:Whether that repository is the local one or not.",
+       "apihelp-query+filerepoinfo-param-prop": "要获取的存储库属性(这在一些wiki上可能有更多可用选项):\n;apiurl:链接至API的URL - 对从主机获取图片信息有用。\n;name:存储库关键词 - 用于例如<var>[[mw:Manual:$wgForeignFileRepos|$wgForeignFileRepos]]</var>,并且[[Special:ApiHelp/query+imageinfo|imageinfo]]会返回值。\n;displayname:人类可读的存储库wiki名称。\n;rooturl:图片路径的根URL。\n;local:存储库是否在本地。",
        "apihelp-query+filerepoinfo-example-simple": "获得有关文件存储库的信息。",
        "apihelp-query+fileusage-description": "查找所有使用指定文件的页面。",
        "apihelp-query+fileusage-param-prop": "要获取的属性:",
        "apihelp-query+imageinfo-param-urlwidth": "如果$2prop=url被设定,将返回至缩放到此宽度的一张图片的URL。\n由于性能原因,如果此消息被使用,将不会返回超过$1张被缩放的图片。",
        "apihelp-query+imageinfo-param-urlheight": "与$1urlwidth类似。",
        "apihelp-query+imageinfo-param-metadataversion": "要使用的元数据版本。如果<kbd>latest</kbd>被指定,则使用最新版本。默认为<kbd>1</kbd>以便向下兼容。",
-       "apihelp-query+imageinfo-param-extmetadatalanguage": "要取得extmetadata的语言。This affects both which translation to fetch, if multiple are available, as well as how things like numbers and various values are formatted.",
+       "apihelp-query+imageinfo-param-extmetadatalanguage": "要取得extmetadata的语言。这会影响到抓取翻译的选择,如果有多个可用的话,还会影响到数字等数值的格式。",
        "apihelp-query+imageinfo-param-extmetadatamultilang": "如果用于extmetadata属性的翻译可用,则全部取得。",
        "apihelp-query+imageinfo-param-extmetadatafilter": "如果指定且非空,则只为$1prop=extmetadata返回这些键。",
        "apihelp-query+imageinfo-param-urlparam": "处理器特定的参数字符串。例如PDF可能使用<kbd>page15-100px</kbd>。<var>$1urlwidth</var>必须被使用,并与<var>$1urlparam</var>一致。",
        "apihelp-query+info-paramvalue-prop-talkid": "每个非讨论页面的讨论页的页面ID。",
        "apihelp-query+info-paramvalue-prop-watched": "列出每个页面的被监视状态。",
        "apihelp-query+info-paramvalue-prop-watchers": "监视人员数,如果允许。",
+       "apihelp-query+info-paramvalue-prop-visitingwatchers": "访问了每个页面的最近编辑的监视者数量,如果允许。",
        "apihelp-query+info-paramvalue-prop-notificationtimestamp": "每个页面的监视列表通知时间戳。",
        "apihelp-query+info-paramvalue-prop-subjectid": "每个讨论页的母页面的页面ID。",
        "apihelp-query+info-paramvalue-prop-url": "为每个页面提供一个完整URL、一个编辑URL和规范URL。",
        "apihelp-query+pageswithprop-param-dir": "排序的方向。",
        "apihelp-query+pageswithprop-example-simple": "列出前10个使用<code>&#123;&#123;DISPLAYTITLE:&#125;&#125;</code>的页面。",
        "apihelp-query+pageswithprop-example-generator": "获取有关前10个使用<code>_&#95;NOTOC_&#95;</code>的页面的额外信息。",
+       "apihelp-query+prefixsearch-description": "为页面标题执行前缀搜索。\n\nDespite the similarity in names, this module is not intended to be equivalent to [[Special:PrefixIndex]]; for that, see <kbd>[[Special:ApiHelp/query+allpages|action=query&list=allpages]]</kbd> with the <kbd>apprefix</kbd> parameter. The purpose of this module is similar to <kbd>[[Special:ApiHelp/opensearch|action=opensearch]]</kbd>: to take user input and provide the best-matching titles. Depending on the search engine backend, this might include typo correction, redirect avoidance, or other heuristics.",
        "apihelp-query+prefixsearch-param-search": "搜索字符串。",
        "apihelp-query+prefixsearch-param-namespace": "搜索的名字空间。",
        "apihelp-query+prefixsearch-param-limit": "要返回的结果最大数。",
        "apihelp-query+protectedtitles-paramvalue-prop-level": "添加保护级别。",
        "apihelp-query+protectedtitles-example-simple": "受保护标题列表。",
        "apihelp-query+protectedtitles-example-generator": "找到主命名空间中已保护的标题的链接。",
+       "apihelp-query+querypage-description": "获取由基于QueryPage的特殊页面提供的列表。",
+       "apihelp-query+querypage-param-page": "特殊页面的名称。注意其区分大小写。",
        "apihelp-query+querypage-param-limit": "返回的结果数。",
        "apihelp-query+querypage-example-ancientpages": "返回[[Special:Ancientpages]]的结果。",
        "apihelp-query+random-description": "获取一组随机页面。\n\n页面列举在一个固定序列中,只有起始点是随机的。这意味着如果<samp>Main Page</samp>是列表中第一个随机页面的话,<samp>List of fictional monkeys</samp>将<em>总是</em>第二个,<samp>List of people on stamps of Vanuatu</samp>是第三个等。",
        "apihelp-query+recentchanges-param-excludeuser": "不要列出此用户的更改。",
        "apihelp-query+recentchanges-param-tag": "只列出带此标签的更改。",
        "apihelp-query+recentchanges-param-prop": "包含的额外信息束:",
-       "apihelp-query+recentchanges-paramvalue-prop-user": "Adds the user responsible for the edit and tags if they are an IP.",
+       "apihelp-query+recentchanges-paramvalue-prop-user": "为编辑和标签添加用户责任,如果它们是IP的话。",
        "apihelp-query+recentchanges-paramvalue-prop-userid": "为编辑添加用户ID责任。",
        "apihelp-query+recentchanges-paramvalue-prop-comment": "为编辑添加摘要。",
-       "apihelp-query+recentchanges-paramvalue-prop-parsedcomment": "Adds the parsed comment for the edit.",
+       "apihelp-query+recentchanges-paramvalue-prop-parsedcomment": "为编辑添加解析的摘要。",
        "apihelp-query+recentchanges-paramvalue-prop-flags": "为编辑添加标记。",
        "apihelp-query+recentchanges-paramvalue-prop-timestamp": "添加编辑的时间戳。",
        "apihelp-query+recentchanges-paramvalue-prop-title": "添加编辑的页面标题。",
        "apihelp-query+recentchanges-paramvalue-prop-ids": "添加页面ID、最近更改ID和新旧修订的ID。",
-       "apihelp-query+recentchanges-paramvalue-prop-sizes": "Adds the new and old page length in bytes.",
+       "apihelp-query+recentchanges-paramvalue-prop-sizes": "添加新旧页面长度(字节)。",
        "apihelp-query+recentchanges-paramvalue-prop-redirect": "如果页面是重定向的话,标记编辑。",
        "apihelp-query+recentchanges-paramvalue-prop-patrolled": "Tags patrollable edits as being patrolled or unpatrolled.",
        "apihelp-query+recentchanges-paramvalue-prop-loginfo": "Adds log information (log ID, log type, etc) to log entries.",
        "apihelp-query+redirects-param-show": "只显示符合这些标准的项目:\n;fragment:只显示带碎片的重定向。\n;!fragment:只显示不带碎片的重定向。",
        "apihelp-query+redirects-example-simple": "获取至[[Main Page]]的重定向列表。",
        "apihelp-query+redirects-example-generator": "获取所有重定向至[[Main Page]]的信息。",
+       "apihelp-query+revisions-description": "获取修订版本信息。\n\n可用于以下几个方面:\n# 通过设置标题或页面ID获取一批页面(最新修订)的数据。\n# 通过使用带start、end或limit的标题或页面ID获取给定页面的多个修订。\n# 通过revid设置一批修订的ID获取它们的数据。",
        "apihelp-query+revisions-paraminfo-singlepageonly": "可能只能与单一页面使用(模式#2)。",
        "apihelp-query+revisions-param-startid": "从哪个修订版本ID开始列举。",
        "apihelp-query+revisions-param-endid": "在此修订版本ID停止修订列举。",
        "apihelp-query+revisions-param-user": "只包含由用户做出的修订。",
        "apihelp-query+revisions-param-excludeuser": "不包括由用户做出的修订。",
        "apihelp-query+revisions-param-tag": "只列出被此标签标记的修订。",
+       "apihelp-query+revisions-param-token": "要为每个修订版本获得的令牌。",
        "apihelp-query+revisions-example-content": "获取带内容的数据,用于标题<kbd>API</kbd>和<kbd>Main Page</kbd>的最近修订。",
        "apihelp-query+revisions-example-last5": "获取<kbd>Main Page</kbd>的最近5次修订。",
        "apihelp-query+revisions-example-first5": "获取<kbd>Main Page</kbd>的前5次修订。",
        "apihelp-query+search-param-info": "要返回的元数据。",
        "apihelp-query+search-param-prop": "要返回的属性:",
        "apihelp-query+search-paramvalue-prop-size": "添加页面大小,单位为字节。",
-       "apihelp-query+search-paramvalue-prop-wordcount": "Adds the word count of the page.",
-       "apihelp-query+search-paramvalue-prop-timestamp": "Adds the timestamp of when the page was last edited.",
+       "apihelp-query+search-paramvalue-prop-wordcount": "添加页面的字数。",
+       "apihelp-query+search-paramvalue-prop-timestamp": "添加页面上次编辑时的时间戳。",
        "apihelp-query+search-paramvalue-prop-snippet": "Adds a parsed snippet of the page.",
        "apihelp-query+search-paramvalue-prop-titlesnippet": "Adds a parsed snippet of the page title.",
        "apihelp-query+search-paramvalue-prop-redirectsnippet": "Adds a parsed snippet of the redirect title.",
        "apihelp-query+search-paramvalue-prop-sectiontitle": "Adds the title of the matching section.",
        "apihelp-query+search-paramvalue-prop-categorysnippet": "Adds a parsed snippet of the matching category.",
        "apihelp-query+search-paramvalue-prop-isfilematch": "Adds a boolean indicating if the search matched file content.",
-       "apihelp-query+search-paramvalue-prop-score": "<span class=\"apihelp-deprecated\">Deprecated and ignored.</span>",
+       "apihelp-query+search-paramvalue-prop-score": "<span class=\"apihelp-deprecated\">已弃用并已忽略。</span>",
        "apihelp-query+search-paramvalue-prop-hasrelated": "<span class=\"apihelp-deprecated\">Deprecated and ignored.</span>",
        "apihelp-query+search-param-limit": "返回的总计页面数。",
        "apihelp-query+search-param-interwiki": "搜索结果中包含跨wiki结果,如果可用。",
        "apihelp-query+search-param-backend": "要使用的搜索后端,如果没有则为默认。",
+       "apihelp-query+search-param-enablerewrites": "启用内部查询重写。一些搜索后端可以重写查询到它认为会给出更好结果的地方,例如纠正拼写错误。",
        "apihelp-query+search-example-simple": "搜索<kbd>meaning</kbd>。",
        "apihelp-query+search-example-text": "搜索文本<kbd>meaning</kbd>。",
        "apihelp-query+search-example-generator": "获得有关搜索<kbd>meaning</kbd>返回页面的页面信息。",
        "apihelp-query+stashimageinfo-param-filekey": "用于识别一次临时藏匿的早前上传的关键字。",
        "apihelp-query+stashimageinfo-param-sessionkey": "$1filekey的别名,用于向后兼容。",
        "apihelp-query+stashimageinfo-example-simple": "返回藏匿文件的信息。",
+       "apihelp-query+stashimageinfo-example-params": "返回两个藏匿文件的缩略图。",
        "apihelp-query+tags-description": "列出更改标签。",
        "apihelp-query+tags-param-limit": "列出标签的最大数量。",
        "apihelp-query+tags-param-prop": "要获取哪个属性:",
        "apihelp-query+usercontribs-param-start": "返回的起始时间戳。",
        "apihelp-query+usercontribs-param-end": "返回的最终时间戳。",
        "apihelp-query+usercontribs-param-user": "要检索贡献的用户。",
+       "apihelp-query+usercontribs-param-userprefix": "取得所有用户名以这个值开头的用户的贡献。覆盖$1user。",
        "apihelp-query+usercontribs-param-namespace": "只列出这些名字空间的贡献。",
        "apihelp-query+usercontribs-param-prop": "包含额外的信息束:",
        "apihelp-query+usercontribs-paramvalue-prop-ids": "添加页面ID和修订ID。",
        "apihelp-query+userinfo-paramvalue-prop-blockinfo": "如果当前用户被封禁就标记,并注明是谁封禁,以何种原因封禁的。",
        "apihelp-query+userinfo-paramvalue-prop-hasmsg": "如果当前用户有等待中的消息的话,添加标签<samp>messages</samp>。",
        "apihelp-query+userinfo-paramvalue-prop-groups": "列举当前用户隶属的所有群组。",
-       "apihelp-query+userinfo-paramvalue-prop-implicitgroups": "Lists all the groups the current user is automatically a member of.",
-       "apihelp-query+userinfo-paramvalue-prop-rights": "Lists all the rights the current user has.",
+       "apihelp-query+userinfo-paramvalue-prop-implicitgroups": "列举当前用户的所有自动成为成员的用户组。",
+       "apihelp-query+userinfo-paramvalue-prop-rights": "列举当前用户拥有的所有权限。",
        "apihelp-query+userinfo-paramvalue-prop-changeablegroups": "Lists the groups the current user can add to and remove from.",
        "apihelp-query+userinfo-paramvalue-prop-options": "Lists all preferences the current user has set.",
        "apihelp-query+userinfo-paramvalue-prop-preferencestoken": "<span class=\"apihelp-deprecated\">已弃用。</span>获取令牌以更改当前用户的参数设置。",
        "apihelp-query+users-paramvalue-prop-blockinfo": "如果用户被封禁就标记,并注明是谁封禁,以何种原因封禁的。",
        "apihelp-query+users-paramvalue-prop-groups": "列举每位用户属于的所有组。",
        "apihelp-query+users-paramvalue-prop-implicitgroups": "Lists all the groups a user is automatically a member of.",
-       "apihelp-query+users-paramvalue-prop-rights": "Lists all the rights each user has.",
+       "apihelp-query+users-paramvalue-prop-rights": "列举每位用户拥有的所有权限。",
        "apihelp-query+users-paramvalue-prop-editcount": "添加用户的编辑计数。",
        "apihelp-query+users-paramvalue-prop-registration": "添加用户的注册时间戳。",
        "apihelp-query+users-paramvalue-prop-emailable": "Tags if the user can and wants to receive email through [[Special:Emailuser]].",
-       "apihelp-query+users-paramvalue-prop-gender": "Tags the gender of the user. Returns \"male\", \"female\", or \"unknown\".",
+       "apihelp-query+users-paramvalue-prop-gender": "标记用户性别。返回“male”、“female”或“unknown”。",
        "apihelp-query+users-paramvalue-prop-centralids": "添加中心ID并为用户附加状态。",
        "apihelp-query+users-param-attachedwiki": "与<kbd>$1prop=centralids</kbd>一起使用,表明用户是否附加于此ID定义的wiki。",
        "apihelp-query+users-param-users": "要获取信息的用户列表。",
        "apihelp-watch-example-unwatch": "取消监视页面<kbd>Main Page</kbd>。",
        "apihelp-watch-example-generator": "监视主名字空间中的最少几个页面。",
        "apihelp-format-example-generic": "返回查询结果为$1格式。",
+       "apihelp-format-param-wrappedhtml": "作为一个JSON对象返回渲染好的HTML和关联的ResouceLoader模块。",
        "apihelp-json-description": "输出数据为JSON格式。",
        "apihelp-json-param-callback": "如果指定,将输出内容包裹在一个指定的函数调用中。出于安全考虑,所有用户相关的数据将被限制。",
        "apihelp-json-param-utf8": "如果指定,使用十六进制转义序列将大多数(但不是全部)非ASCII的字符编码为UTF-8,而不是替换它们。默认当<var>formatversion</var>不是<kbd>1</kbd>时。",
        "api-help-permissions": "{{PLURAL:$1|权限}}:",
        "api-help-permissions-granted-to": "{{PLURAL:$1|授予}}:$2",
        "api-help-right-apihighlimits": "在API查询中使用更高的上限(慢查询:$1;快查询:$2)。慢查询的限制也适用于多值参数。",
+       "api-help-open-in-apisandbox": "<small>[在沙盒中打开]</small>",
        "api-credits-header": "制作人员",
        "api-credits": "API 开发人员:\n* Yuri Astrakhan(创建者,2006年9月~2007年9月的开发组领导)\n* Roan Kattouw(2007年9月~2009年的开发组领导)\n* Victor Vasiliev\n* Bryan Tong Minh\n* Sam Reed\n* Brad Jorsch(2013年至今的开发组领导)\n\n请将您的评论、建议和问题发送至mediawiki-api@lists.wikimedia.org,或提交错误请求至https://phabricator.wikimedia.org/。"
 }
index d6c610a..88d8b5a 100644 (file)
@@ -56,13 +56,13 @@ class LinkBatch {
        }
 
        /**
-        * @param Title|TitleValue $title
+        * @param LinkTarget $linkTarget
         */
-       public function addObj( $title ) {
-               if ( is_object( $title ) ) {
-                       $this->add( $title->getNamespace(), $title->getDBkey() );
+       public function addObj( $linkTarget ) {
+               if ( is_object( $linkTarget ) ) {
+                       $this->add( $linkTarget->getNamespace(), $linkTarget->getDBkey() );
                } else {
-                       wfDebug( "Warning: LinkBatch::addObj got invalid Title or TitleValue object\n" );
+                       wfDebug( "Warning: LinkBatch::addObj got invalid LinkTarget object\n" );
                }
        }
 
@@ -178,7 +178,7 @@ class LinkBatch {
         * @return bool|ResultWrapper
         */
        public function doQuery() {
-               global $wgContentHandlerUseDB;
+               global $wgContentHandlerUseDB, $wgPageLanguageUseDB;
 
                if ( $this->isEmpty() ) {
                        return false;
@@ -193,6 +193,9 @@ class LinkBatch {
                if ( $wgContentHandlerUseDB ) {
                        $fields[] = 'page_content_model';
                }
+               if ( $wgPageLanguageUseDB ) {
+                       $fields[] = 'page_lang';
+               }
 
                $conds = $this->constructSet( 'page', $dbr );
 
index 5ea926b..f199891 100644 (file)
@@ -150,9 +150,10 @@ class LinkCache {
         * @param int $redir Whether the page is a redirect
         * @param int $revision Latest revision's ID
         * @param string|null $model Latest revision's content model ID
+        * @param string|null $lang Language code of the page, if not the content language
         */
        public function addGoodLinkObj( $id, Title $title, $len = -1, $redir = null,
-               $revision = 0, $model = null
+               $revision = 0, $model = null, $lang = null
        ) {
                $dbkey = $title->getPrefixedDBkey();
                $this->mGoodLinks->set( $dbkey, array(
@@ -161,6 +162,7 @@ class LinkCache {
                        'redirect' => (int)$redir,
                        'revision' => (int)$revision,
                        'model' => $model ? (string)$model : null,
+                       'lang' => $lang ? (string)$lang : null,
                ) );
        }
 
@@ -179,6 +181,7 @@ class LinkCache {
                        'redirect' => intval( $row->page_is_redirect ),
                        'revision' => intval( $row->page_latest ),
                        'model' => !empty( $row->page_content_model ) ? strval( $row->page_content_model ) : null,
+                       'lang' => !empty( $row->page_lang ) ? strval( $row->page_lang ) : null,
                ) );
        }
 
@@ -226,7 +229,7 @@ class LinkCache {
         * @return int Page ID or zero
         */
        public function addLinkObj( Title $nt ) {
-               global $wgContentHandlerUseDB;
+               global $wgContentHandlerUseDB, $wgPageLanguageUseDB;
 
                $key = $nt->getPrefixedDBkey();
                if ( $this->isBadLink( $key ) || $nt->isExternal() ) {
@@ -248,6 +251,9 @@ class LinkCache {
                if ( $wgContentHandlerUseDB ) {
                        $fields[] = 'page_content_model';
                }
+               if ( $wgPageLanguageUseDB ) {
+                       $fields[] = 'page_lang';
+               }
 
                $row = $db->selectRow( 'page', $fields,
                        array( 'page_namespace' => $nt->getNamespace(), 'page_title' => $nt->getDBkey() ),
index 6b938f1..2fae4e3 100644 (file)
@@ -168,14 +168,14 @@ class MessageCache {
         * @return ParserOptions
         */
        function getParserOptions() {
-               global $wgFullyInitialised, $wgContLang;
+               global $wgUser;
 
                if ( !$this->mParserOptions ) {
-                       if ( !$wgFullyInitialised ) {
+                       if ( !$wgUser->isSafeToLoad() ) {
                                // $wgUser isn't unstubbable yet, so don't try to get a
                                // ParserOptions for it. And don't cache this ParserOptions
                                // either.
-                               $po = new ParserOptions( new User, $wgContLang );
+                               $po = ParserOptions::newFromAnon();
                                $po->setEditSection( false );
                                return $po;
                        }
index 1741e64..1c7d4f0 100644 (file)
@@ -491,10 +491,9 @@ class ChangesList extends ContextSource {
                        return '';
                }
                $cache = $this->watchMsgCache;
-               $that = $this;
                return $cache->getWithSetCallback( $count, $cache::TTL_INDEFINITE,
-                       function () use ( $that, $count ) {
-                               return $that->msg( 'number_of_watching_users_RCview' )
+                       function () use ( $count ) {
+                               return $this->msg( 'number_of_watching_users_RCview' )
                                        ->numParams( $count )->escaped();
                        }
                );
@@ -590,7 +589,8 @@ class ChangesList extends ContextSource {
 
                list( $tagSummary, $newClasses ) = ChangeTags::formatSummaryRow(
                        $rc->mAttribs['ts_tags'],
-                       'changeslist'
+                       'changeslist',
+                       $this->getContext()
                );
                $classes = array_merge( $classes, $newClasses );
                $s .= ' ' . $tagSummary;
index 9ae30c0..c33a94b 100644 (file)
@@ -538,11 +538,13 @@ class RecentChange {
         * @param int $newSize
         * @param int $newId
         * @param int $patrol
+        * @param array $tags
         * @return RecentChange
         */
        public static function notifyEdit(
                $timestamp, &$title, $minor, &$user, $comment, $oldId, $lastTimestamp,
-               $bot, $ip = '', $oldSize = 0, $newSize = 0, $newId = 0, $patrol = 0
+               $bot, $ip = '', $oldSize = 0, $newSize = 0, $newId = 0, $patrol = 0,
+               $tags = array()
        ) {
                $rc = new RecentChange;
                $rc->mTitle = $title;
@@ -581,11 +583,15 @@ class RecentChange {
                        'pageStatus' => 'changed'
                );
 
-               DeferredUpdates::addCallableUpdate( function() use ( $rc ) {
+               DeferredUpdates::addCallableUpdate( function() use ( $rc, $tags ) {
                        $rc->save();
                        if ( $rc->mAttribs['rc_patrolled'] ) {
                                PatrolLog::record( $rc, true, $rc->getPerformer() );
                        }
+                       if ( count( $tags ) ) {
+                               ChangeTags::addTags( $tags, $rc->mAttribs['rc_id'],
+                                       $rc->mAttribs['rc_this_oldid'], null, null );
+                       }
                } );
 
                return $rc;
@@ -605,11 +611,12 @@ class RecentChange {
         * @param int $size
         * @param int $newId
         * @param int $patrol
+        * @param array $tags
         * @return RecentChange
         */
        public static function notifyNew(
                $timestamp, &$title, $minor, &$user, $comment, $bot,
-               $ip = '', $size = 0, $newId = 0, $patrol = 0
+               $ip = '', $size = 0, $newId = 0, $patrol = 0, $tags = array()
        ) {
                $rc = new RecentChange;
                $rc->mTitle = $title;
@@ -648,11 +655,15 @@ class RecentChange {
                        'pageStatus' => 'created'
                );
 
-               DeferredUpdates::addCallableUpdate( function() use ( $rc ) {
+               DeferredUpdates::addCallableUpdate( function() use ( $rc, $tags ) {
                        $rc->save();
                        if ( $rc->mAttribs['rc_patrolled'] ) {
                                PatrolLog::record( $rc, true, $rc->getPerformer() );
                        }
+                       if ( count( $tags ) ) {
+                               ChangeTags::addTags( $tags, $rc->mAttribs['rc_id'],
+                                       $rc->mAttribs['rc_this_oldid'], null, null );
+                       }
                } );
 
                return $rc;
index a8c9f7b..305eeab 100644 (file)
@@ -35,16 +35,20 @@ class ChangeTags {
         * @param string $tags Comma-separated list of tags
         * @param string $page A label for the type of action which is being displayed,
         *   for example: 'history', 'contributions' or 'newpages'
+        * @param IContextSource|null $context
+        * @note Even though it takes null as a valid argument, an IContextSource is preferred
+        *       in a new code, as the null value is subject to change in the future
         * @return array Array with two items: (html, classes)
         *   - html: String: HTML for displaying the tags (empty string when param $tags is empty)
         *   - classes: Array of strings: CSS classes used in the generated html, one class for each tag
         */
-       public static function formatSummaryRow( $tags, $page ) {
-               global $wgLang;
-
+       public static function formatSummaryRow( $tags, $page, IContextSource $context = null ) {
                if ( !$tags ) {
                        return array( '', array() );
                }
+               if ( !$context ) {
+                       $context = RequestContext::getMain();
+               }
 
                $classes = array();
 
@@ -71,9 +75,9 @@ class ChangeTags {
                        return array( '', array() );
                }
 
-               $markers = wfMessage( 'tag-list-wrapper' )
+               $markers = $context->msg( 'tag-list-wrapper' )
                        ->numParams( count( $displayTags ) )
-                       ->rawParams( $wgLang->commaList( $displayTags ) )
+                       ->rawParams( $context->getLanguage()->commaList( $displayTags ) )
                        ->parse();
                $markers = Xml::tags( 'span', array( 'class' => 'mw-tag-markers' ), $markers );
 
index 565d159..24bded7 100644 (file)
@@ -91,7 +91,11 @@ class ChangeTagsLogItem extends RevisionItemBase {
                $attribs = array();
                $tags = $this->getTags();
                if ( $tags ) {
-                       list( $tagSummary, $classes ) = ChangeTags::formatSummaryRow( $tags, 'edittags' );
+                       list( $tagSummary, $classes ) = ChangeTags::formatSummaryRow(
+                               $tags,
+                               'edittags',
+                               $this->list->getContext()
+                       );
                        $content .= " $tagSummary";
                        $attribs['class'] = implode( ' ', $classes );
                }
index e90a1b4..37d6a1a 100644 (file)
@@ -49,7 +49,11 @@ class ChangeTagsRevisionItem extends RevisionItem {
                $attribs = array();
                $tags = $this->getTags();
                if ( $tags ) {
-                       list( $tagSummary, $classes ) = ChangeTags::formatSummaryRow( $tags, 'edittags' );
+                       list( $tagSummary, $classes ) = ChangeTags::formatSummaryRow(
+                               $tags,
+                               'edittags',
+                               $this->list->getContext()
+                       );
                        $content .= " $tagSummary";
                        $attribs['class'] = implode( ' ', $classes );
                }
index acaa288..e898e72 100644 (file)
@@ -357,11 +357,16 @@ abstract class ContentHandler {
                                throw new MWException( "ContentHandlerForModelID must supply a ContentHandler instance" );
                        }
                } else {
-                       $class = $wgContentHandlers[$modelId];
-                       $handler = new $class( $modelId );
+                       $classOrCallback = $wgContentHandlers[$modelId];
+
+                       if ( is_callable( $classOrCallback ) ) {
+                               $handler = call_user_func( $classOrCallback, $modelId );
+                       } else {
+                               $handler = new $classOrCallback( $modelId );
+                       }
 
                        if ( !( $handler instanceof ContentHandler ) ) {
-                               throw new MWException( "$class from \$wgContentHandlers is not " .
+                               throw new MWException( "$classOrCallback from \$wgContentHandlers is not " .
                                        "compatible with ContentHandler" );
                        }
                }
index 73e11b5..8056b4d 100644 (file)
@@ -595,7 +595,6 @@ class RequestContext implements IContextSource, MutableContext {
                        $wgUser = $context->getUser(); // b/c
                        if ( $session && MediaWiki\Session\PHPSessionHandler::isEnabled() ) {
                                session_id( $session->getId() );
-                               MediaWiki\quietCall( 'session_cache_limiter', 'private, must-revalidate' );
                                MediaWiki\quietCall( 'session_start' );
                        }
                        $request = new FauxRequest( array(), false, $session );
index a4d0ad0..ef4a7b7 100644 (file)
@@ -1925,14 +1925,10 @@ abstract class DatabaseBase implements IDatabase {
        /**
         * Get the name of an index in a given table.
         *
-        * @protected Don't use outside of DatabaseBase and childs
         * @param string $index
         * @return string
         */
-       public function indexName( $index ) {
-               // @FIXME: Make this protected once we move away from PHP 5.3
-               // Needs to be public because of usage in closure (in DatabaseBase::replaceVars)
-
+       protected function indexName( $index ) {
                // Backwards-compatibility hack
                $renamed = array(
                        'ar_usertext_timestamp' => 'usertext_timestamp',
@@ -2658,7 +2654,10 @@ abstract class DatabaseBase implements IDatabase {
                        if ( !$this->mTrxLevel ) {
                                return; // nothing to do
                        } elseif ( !$this->mTrxAutomatic ) {
-                               wfWarn( "$fname: Flushing an explicit transaction, getting out of sync!" );
+                               throw new DBUnexpectedError(
+                                       $this,
+                                       "$fname: Flushing an explicit transaction, getting out of sync!"
+                               );
                        }
                } else {
                        if ( !$this->mTrxLevel ) {
@@ -3100,7 +3099,6 @@ abstract class DatabaseBase implements IDatabase {
         * @return string The new SQL statement with variables replaced
         */
        protected function replaceVars( $ins ) {
-               $that = $this;
                $vars = $this->getSchemaVars();
                return preg_replace_callback(
                        '!
@@ -3109,19 +3107,19 @@ abstract class DatabaseBase implements IDatabase {
                                `\{\$ (\w+) }`                    | # 4. addIdentifierQuotes
                                /\*\$ (\w+) \*/                     # 5. leave unencoded
                        !x',
-                       function ( $m ) use ( $that, $vars ) {
+                       function ( $m ) use ( $vars ) {
                                // Note: Because of <https://bugs.php.net/bug.php?id=51881>,
                                // check for both nonexistent keys *and* the empty string.
                                if ( isset( $m[1] ) && $m[1] !== '' ) {
                                        if ( $m[1] === 'i' ) {
-                                               return $that->indexName( $m[2] );
+                                               return $this->indexName( $m[2] );
                                        } else {
-                                               return $that->tableName( $m[2] );
+                                               return $this->tableName( $m[2] );
                                        }
                                } elseif ( isset( $m[3] ) && $m[3] !== '' && array_key_exists( $m[3], $vars ) ) {
-                                       return $that->addQuotes( $vars[$m[3]] );
+                                       return $this->addQuotes( $vars[$m[3]] );
                                } elseif ( isset( $m[4] ) && $m[4] !== '' && array_key_exists( $m[4], $vars ) ) {
-                                       return $that->addIdentifierQuotes( $vars[$m[4]] );
+                                       return $this->addIdentifierQuotes( $vars[$m[4]] );
                                } elseif ( isset( $m[5] ) && $m[5] !== '' && array_key_exists( $m[5], $vars ) ) {
                                        return $vars[$m[5]];
                                } else {
@@ -3179,10 +3177,9 @@ abstract class DatabaseBase implements IDatabase {
                        return null;
                }
 
-               $that = $this;
-               $unlocker = new ScopedCallback( function () use ( $that, $lockKey, $fname ) {
-                       $that->commit( __METHOD__, 'flush' );
-                       $that->unlock( $lockKey, $fname );
+               $unlocker = new ScopedCallback( function () use ( $lockKey, $fname ) {
+                       $this->commit( __METHOD__, 'flush' );
+                       $this->unlock( $lockKey, $fname );
                } );
 
                $this->commit( __METHOD__, 'flush' );
index 29106ab..c5aafea 100644 (file)
@@ -692,17 +692,16 @@ abstract class DatabaseMysqlBase extends Database {
                        $this->getLBInfo( 'clusterMasterHost' ) ?: $this->getServer()
                );
 
-               $that = $this;
                return $cache->getWithSetCallback(
                        $key,
                        $cache::TTL_INDEFINITE,
-                       function () use ( $that, $cache, $key ) {
+                       function () use ( $cache, $key ) {
                                // Get and leave a lock key in place for a short period
                                if ( !$cache->lock( $key, 0, 10 ) ) {
                                        return false; // avoid master connection spike slams
                                }
 
-                               $conn = $that->getLazyMasterHandle();
+                               $conn = $this->getLazyMasterHandle();
                                if ( !$conn ) {
                                        return false; // something is misconfigured
                                }
index bb3028d..664484b 100644 (file)
@@ -429,7 +429,7 @@ class DatabaseSqlite extends Database {
         * @param string $index
         * @return string
         */
-       function indexName( $index ) {
+       protected function indexName( $index ) {
                return $index;
        }
 
index 34ea641..23c3102 100644 (file)
@@ -282,13 +282,7 @@ class LegacyLogger extends AbstractLogger {
                        $cachedTimezone = new DateTimeZone( $wgDBerrorLogTZ );
                }
 
-               // Workaround for https://bugs.php.net/bug.php?id=52063
-               // Can be removed when min PHP > 5.3.6
-               if ( $cachedTimezone === null ) {
-                       $d = date_create( 'now' );
-               } else {
-                       $d = date_create( 'now', $cachedTimezone );
-               }
+               $d = date_create( 'now', $cachedTimezone );
                $date = $d->format( 'D M j G:i:s T Y' );
 
                $host = wfHostname();
index fc24e82..eb1a0d0 100644 (file)
@@ -85,7 +85,7 @@ class AvroFormatter implements FormatterInterface {
                $this->io->truncate();
                $schema = $this->getSchema( $record['channel'] );
                $revId = $this->getSchemaRevisionId( $record['channel'] );
-               if ( $schema === null ) {
+               if ( $schema === null || $revId === null ) {
                        trigger_error( "The schema for channel '{$record['channel']}' is not available" );
                        return null;
                }
@@ -97,11 +97,7 @@ class AvroFormatter implements FormatterInterface {
                        trigger_error( "Avro failed to serialize record for {$record['channel']} : {$json}" );
                        return null;
                }
-               if ( $revId !== null ) {
-                       return chr( self::MAGIC ) . $this->encode_long( $revId ) . $this->io->string();
-               }
-               // @todo: remove backward compat code and do not send messages without rev id.
-               return $this->io->string();
+               return chr( self::MAGIC ) . $this->encodeLong( $revId ) . $this->io->string();
        }
 
        /**
@@ -132,27 +128,23 @@ class AvroFormatter implements FormatterInterface {
                if ( !isset( $this->schemas[$channel] ) ) {
                        return null;
                }
-               $schemaDetails = &$this->schemas[$channel];
-               $schema = null;
-               if ( isset( $schemaDetails['revision'] ) && isset( $schemaDetails['schema'] ) ) {
-                       $schema = &$schemaDetails['schema'];
-               } else {
-                       // @todo: Remove backward compat code
-                       $schema = &$schemaDetails;
+               if ( !isset( $this->schemas[$channel]['revision'], $this->schemas[$channel]['schema'] ) ) {
+                       return null;
                }
 
-               if ( !$schema instanceof AvroSchema ) {
+               if ( !$this->schemas[$channel]['schema'] instanceof AvroSchema ) {
+                       $schema = $this->schemas[$channel]['schema'];
                        if ( is_string( $schema ) ) {
-                               $schema = AvroSchema::parse( $schema );
+                               $this->schemas[$channel]['schema'] = AvroSchema::parse( $schema );
                        } else {
-                               $schema = AvroSchema::real_parse(
-                                       $this->schemas[$channel],
+                               $this->schemas[$channel]['schema'] = AvroSchema::real_parse(
+                                       $schema,
                                        null,
                                        new AvroNamedSchemata()
                                );
                        }
                }
-               return $schema;
+               return $this->schemas[$channel]['schema'];
        }
 
        /**
@@ -162,10 +154,7 @@ class AvroFormatter implements FormatterInterface {
         * @return int|null
         */
        public function getSchemaRevisionId( $channel ) {
-               // @todo: remove backward compat code
-               if ( isset( $this->schemas[$channel] )
-                               && is_array( $this->schemas[$channel] )
-                               && isset( $this->schemas[$channel]['revision'] ) ) {
+               if ( isset( $this->schemas[$channel]['revision'] ) ) {
                        return (int) $this->schemas[$channel]['revision'];
                }
                return null;
@@ -177,7 +166,7 @@ class AvroFormatter implements FormatterInterface {
         * @param int $id
         * @return string the binary representation of $id
         */
-       private function encode_long( $id ) {
+       private function encodeLong( $id ) {
                $high   = ( $id & 0xffffffff00000000 ) >> 32;
                $low    = $id & 0x00000000ffffffff;
                return pack( 'NN', $high, $low );
index a52f636..9a357ee 100644 (file)
@@ -21,7 +21,7 @@
 namespace MediaWiki\Logger\Monolog;
 
 /**
- * Injects `wfHostname()` and `wfWikiID()` in all records.
+ * Injects `wfHostname()`, `wfWikiID()` and `$wgVersion` in all records.
  *
  * @since 1.25
  * @author Bryan Davis <bd808@wikimedia.org>
@@ -34,11 +34,13 @@ class WikiProcessor {
         * @return array
         */
        public function __invoke( array $record ) {
+               global $wgVersion;
                $record['extra'] = array_merge(
                        $record['extra'],
                        array(
                                'host' => wfHostname(),
                                'wiki' => wfWikiID(),
+                               'mwversion' => $wgVersion,
                        )
                );
                return $record;
index 9f7d8ca..b5e323b 100644 (file)
@@ -108,10 +108,22 @@ class CdnCacheUpdate implements DeferrableUpdate, MergeableUpdate {
 
                wfDebugLog( 'squid', __METHOD__ . ': ' . implode( ' ', $urlArr ) );
 
+               // Reliably broadcast the purge to all edge nodes
+               $relayer = EventRelayerGroup::singleton()->getRelayer( 'cdn-url-purges' );
+               $relayer->notify(
+                       'cdn-url-purges',
+                       array(
+                               'urls' => array_values( $urlArr ), // JSON array
+                               'timestamp' => microtime( true )
+                       )
+               );
+
+               // Send lossy UDP broadcasting if enabled
                if ( $wgHTCPRouting ) {
                        self::HTCPPurge( $urlArr );
                }
 
+               // Do direct server purges if enabled (this does not scale very well)
                if ( $wgSquidServers ) {
                        // Maximum number of parallel connections per squid
                        $maxSocketsPerSquid = 8;
index 3021af1..9cd20fc 100644 (file)
@@ -143,9 +143,8 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
                Hooks::run( 'LinksUpdate', array( &$this ) );
                $this->doIncrementalUpdate();
 
-               $that = $this;
-               $this->mDb->onTransactionIdle( function() use ( $that ) {
-                       Hooks::run( 'LinksUpdateComplete', array( &$that ) );
+               $this->mDb->onTransactionIdle( function() {
+                       Hooks::run( 'LinksUpdateComplete', array( &$this ) );
                } );
        }
 
index 33ca931..23e39ea 100644 (file)
@@ -49,6 +49,9 @@ abstract class DiffFormatter {
         */
        protected $trailingContextLines = 0;
 
+       /** @var string The output buffer; holds the output while it is built. */
+       private $result = '';
+
        /**
         * Format a diff.
         *
@@ -146,15 +149,24 @@ abstract class DiffFormatter {
        }
 
        protected function startDiff() {
-               ob_start();
+               $this->result = '';
+       }
+
+       /**
+        * Writes a string to the output buffer.
+        *
+        * @param string $text
+        */
+       protected function writeOutput( $text ) {
+               $this->result .= $text;
        }
 
        /**
         * @return string
         */
        protected function endDiff() {
-               $val = ob_get_contents();
-               ob_end_clean();
+               $val = $this->result;
+               $this->result = '';
 
                return $val;
        }
@@ -185,7 +197,7 @@ abstract class DiffFormatter {
         * @param string $header
         */
        protected function startBlock( $header ) {
-               echo $header . "\n";
+               $this->writeOutput( $header . "\n" );
        }
 
        /**
@@ -203,7 +215,7 @@ abstract class DiffFormatter {
         */
        protected function lines( $lines, $prefix = ' ' ) {
                foreach ( $lines as $line ) {
-                       echo "$prefix $line\n";
+                       $this->writeOutput( "$prefix $line\n" );
                }
        }
 
@@ -236,7 +248,7 @@ abstract class DiffFormatter {
         */
        protected function changed( $orig, $closing ) {
                $this->deleted( $orig );
-               echo "---\n";
+               $this->writeOutput( "---\n" );
                $this->added( $closing );
        }
 
index d588d51..f59fd61 100644 (file)
@@ -342,7 +342,7 @@ class DifferenceEngine extends ContextSource {
 
                        $ldel = $this->revisionDeleteLink( $this->mOldRev );
                        $oldRevisionHeader = $this->getRevisionHeader( $this->mOldRev, 'complete' );
-                       $oldChangeTags = ChangeTags::formatSummaryRow( $this->mOldTags, 'diff' );
+                       $oldChangeTags = ChangeTags::formatSummaryRow( $this->mOldTags, 'diff', $this->getContext() );
 
                        $oldHeader = '<div id="mw-diff-otitle1"><strong>' . $oldRevisionHeader . '</strong></div>' .
                                '<div id="mw-diff-otitle2">' .
@@ -403,7 +403,7 @@ class DifferenceEngine extends ContextSource {
                }
                $newRevisionHeader = $this->getRevisionHeader( $this->mNewRev, 'complete' ) .
                        ' ' . implode( ' ', $formattedRevisionTools );
-               $newChangeTags = ChangeTags::formatSummaryRow( $this->mNewTags, 'diff' );
+               $newChangeTags = ChangeTags::formatSummaryRow( $this->mNewTags, 'diff', $this->getContext() );
 
                $newHeader = '<div id="mw-diff-ntitle1"><strong>' . $newRevisionHeader . '</strong></div>' .
                        '<div id="mw-diff-ntitle2">' . Linker::revUserTools( $this->mNewRev, !$this->unhide ) .
index be38e87..f1826ed 100644 (file)
@@ -80,7 +80,7 @@ class TableDiffFormatter extends DiffFormatter {
         * @param string $header
         */
        protected function startBlock( $header ) {
-               echo $header;
+               $this->writeOutput( $header );
        }
 
        protected function endBlock() {
@@ -157,9 +157,9 @@ class TableDiffFormatter extends DiffFormatter {
         */
        protected function added( $lines ) {
                foreach ( $lines as $line ) {
-                       echo '<tr>' . $this->emptyLine() .
+                       $this->writeOutput( '<tr>' . $this->emptyLine() .
                                $this->addedLine( '<ins class="diffchange">' .
-                                       htmlspecialchars( $line ) . '</ins>' ) . "</tr>\n";
+                                       htmlspecialchars( $line ) . '</ins>' ) . "</tr>\n" );
                }
        }
 
@@ -170,9 +170,9 @@ class TableDiffFormatter extends DiffFormatter {
         */
        protected function deleted( $lines ) {
                foreach ( $lines as $line ) {
-                       echo '<tr>' . $this->deletedLine( '<del class="diffchange">' .
+                       $this->writeOutput( '<tr>' . $this->deletedLine( '<del class="diffchange">' .
                                        htmlspecialchars( $line ) . '</del>' ) .
-                               $this->emptyLine() . "</tr>\n";
+                               $this->emptyLine() . "</tr>\n" );
                }
        }
 
@@ -183,9 +183,9 @@ class TableDiffFormatter extends DiffFormatter {
         */
        protected function context( $lines ) {
                foreach ( $lines as $line ) {
-                       echo '<tr>' .
+                       $this->writeOutput( '<tr>' .
                                $this->contextLine( htmlspecialchars( $line ) ) .
-                               $this->contextLine( htmlspecialchars( $line ) ) . "</tr>\n";
+                               $this->contextLine( htmlspecialchars( $line ) ) . "</tr>\n" );
                }
        }
 
@@ -207,13 +207,13 @@ class TableDiffFormatter extends DiffFormatter {
                $line = array_shift( $del );
                while ( $line ) {
                        $aline = array_shift( $add );
-                       echo '<tr>' . $this->deletedLine( $line ) .
-                               $this->addedLine( $aline ) . "</tr>\n";
+                       $this->writeOutput( '<tr>' . $this->deletedLine( $line ) .
+                               $this->addedLine( $aline ) . "</tr>\n" );
                        $line = array_shift( $del );
                }
                foreach ( $add as $line ) { # If any leftovers
-                       echo '<tr>' . $this->emptyLine() .
-                               $this->addedLine( $line ) . "</tr>\n";
+                       $this->writeOutput( '<tr>' . $this->emptyLine() .
+                               $this->addedLine( $line ) . "</tr>\n" );
                }
        }
 
index 5f3ad3d..72f1a66 100644 (file)
@@ -42,7 +42,7 @@ class UnifiedDiffFormatter extends DiffFormatter {
         */
        protected function lines( $lines, $prefix = ' ' ) {
                foreach ( $lines as $line ) {
-                       echo "{$prefix}{$line}\n";
+                       $this->writeOutput( "{$prefix}{$line}\n" );
                }
        }
 
index 93d8d07..1f64692 100644 (file)
@@ -278,16 +278,15 @@ class SwiftFileBackend extends FileBackendStore {
                        'body' => $params['content']
                ) );
 
-               $that = $this;
                $method = __METHOD__;
-               $handler = function ( array $request, Status $status ) use ( $that, $method, $params ) {
+               $handler = function ( array $request, Status $status ) use ( $method, $params ) {
                        list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $request['response'];
                        if ( $rcode === 201 ) {
                                // good
                        } elseif ( $rcode === 412 ) {
                                $status->fatal( 'backend-fail-contenttype', $params['dst'] );
                        } else {
-                               $that->onError( $status, $method, $params, $rerr, $rcode, $rdesc );
+                               $this->onError( $status, $method, $params, $rerr, $rcode, $rdesc );
                        }
                };
 
@@ -343,16 +342,15 @@ class SwiftFileBackend extends FileBackendStore {
                        'body' => $handle // resource
                ) );
 
-               $that = $this;
                $method = __METHOD__;
-               $handler = function ( array $request, Status $status ) use ( $that, $method, $params ) {
+               $handler = function ( array $request, Status $status ) use ( $method, $params ) {
                        list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $request['response'];
                        if ( $rcode === 201 ) {
                                // good
                        } elseif ( $rcode === 412 ) {
                                $status->fatal( 'backend-fail-contenttype', $params['dst'] );
                        } else {
-                               $that->onError( $status, $method, $params, $rerr, $rcode, $rdesc );
+                               $this->onError( $status, $method, $params, $rerr, $rcode, $rdesc );
                        }
                };
 
@@ -392,16 +390,15 @@ class SwiftFileBackend extends FileBackendStore {
                        ) + $this->sanitizeHdrs( $params ), // extra headers merged into object
                ) );
 
-               $that = $this;
                $method = __METHOD__;
-               $handler = function ( array $request, Status $status ) use ( $that, $method, $params ) {
+               $handler = function ( array $request, Status $status ) use ( $method, $params ) {
                        list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $request['response'];
                        if ( $rcode === 201 ) {
                                // good
                        } elseif ( $rcode === 404 ) {
                                $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] );
                        } else {
-                               $that->onError( $status, $method, $params, $rerr, $rcode, $rdesc );
+                               $this->onError( $status, $method, $params, $rerr, $rcode, $rdesc );
                        }
                };
 
@@ -450,9 +447,8 @@ class SwiftFileBackend extends FileBackendStore {
                        );
                }
 
-               $that = $this;
                $method = __METHOD__;
-               $handler = function ( array $request, Status $status ) use ( $that, $method, $params ) {
+               $handler = function ( array $request, Status $status ) use ( $method, $params ) {
                        list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $request['response'];
                        if ( $request['method'] === 'PUT' && $rcode === 201 ) {
                                // good
@@ -461,7 +457,7 @@ class SwiftFileBackend extends FileBackendStore {
                        } elseif ( $rcode === 404 ) {
                                $status->fatal( 'backend-fail-move', $params['src'], $params['dst'] );
                        } else {
-                               $that->onError( $status, $method, $params, $rerr, $rcode, $rdesc );
+                               $this->onError( $status, $method, $params, $rerr, $rcode, $rdesc );
                        }
                };
 
@@ -491,9 +487,8 @@ class SwiftFileBackend extends FileBackendStore {
                        'headers' => array()
                ) );
 
-               $that = $this;
                $method = __METHOD__;
-               $handler = function ( array $request, Status $status ) use ( $that, $method, $params ) {
+               $handler = function ( array $request, Status $status ) use ( $method, $params ) {
                        list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $request['response'];
                        if ( $rcode === 204 ) {
                                // good
@@ -502,7 +497,7 @@ class SwiftFileBackend extends FileBackendStore {
                                        $status->fatal( 'backend-fail-delete', $params['src'] );
                                }
                        } else {
-                               $that->onError( $status, $method, $params, $rerr, $rcode, $rdesc );
+                               $this->onError( $status, $method, $params, $rerr, $rcode, $rdesc );
                        }
                };
 
@@ -550,16 +545,15 @@ class SwiftFileBackend extends FileBackendStore {
                        'headers' => $metaHdrs + $customHdrs
                ) );
 
-               $that = $this;
                $method = __METHOD__;
-               $handler = function ( array $request, Status $status ) use ( $that, $method, $params ) {
+               $handler = function ( array $request, Status $status ) use ( $method, $params ) {
                        list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $request['response'];
                        if ( $rcode === 202 ) {
                                // good
                        } elseif ( $rcode === 404 ) {
                                $status->fatal( 'backend-fail-describe', $params['src'] );
                        } else {
-                               $that->onError( $status, $method, $params, $rerr, $rcode, $rdesc );
+                               $this->onError( $status, $method, $params, $rerr, $rcode, $rdesc );
                        }
                };
 
index a8d37a1..d7241e9 100644 (file)
@@ -50,7 +50,6 @@ class ForeignAPIRepo extends FileRepo {
         */
        protected static $imageInfoProps = array(
                'url',
-               'thumbnail',
                'timestamp',
        );
 
index 7290740..55ec8df 100644 (file)
@@ -75,6 +75,7 @@ class CliInstaller extends Installer {
                        $wgContLang = Language::factory( $option['lang'] );
                        $wgLang = Language::factory( $option['lang'] );
                        $wgLanguageCode = $option['lang'];
+                       RequestContext::getMain()->setLanguage( $wgLang );
                }
 
                $this->setVar( 'wgSitename', $siteName );
index de84199..ded45c2 100644 (file)
@@ -116,11 +116,8 @@ abstract class Installer {
         */
        protected $envChecks = array(
                'envCheckDB',
-               'envCheckRegisterGlobals',
                'envCheckBrokenXML',
-               'envCheckMagicQuotes',
                'envCheckMbstring',
-               'envCheckSafeMode',
                'envCheckXML',
                'envCheckPCRE',
                'envCheckMemory',
@@ -196,7 +193,6 @@ abstract class Installer {
        protected $internalDefaults = array(
                '_UserLang' => 'en',
                '_Environment' => false,
-               '_SafeMode' => false,
                '_RaiseMemory' => false,
                '_UpgradeDone' => false,
                '_InstallDone' => false,
@@ -223,6 +219,7 @@ abstract class Installer {
                // $wgLogo is probably wrong (bug 48084); set something that will work.
                // Single quotes work fine here, as LocalSettingsGenerator outputs this unescaped.
                'wgLogo' => '$wgResourceBasePath/resources/assets/wiki.png',
+               'wgAuthenticationTokenVersion' => 1,
        );
 
        /**
@@ -360,6 +357,10 @@ abstract class Installer {
        public function __construct() {
                global $wgMessagesDirs, $wgUser;
 
+               // Don't attempt to load user language options (T126177)
+               // This will be overridden in the web installer with the user-specified language
+               RequestContext::getMain()->setLanguage( 'en' );
+
                // Disable the i18n cache
                Language::getLocalisationCache()->disableBackend();
                // Disable LoadBalancer and wfGetDB etc.
@@ -384,6 +385,7 @@ abstract class Installer {
 
                // Having a user with id = 0 safeguards us from DB access via User::loadOptions().
                $wgUser = User::newFromId( 0 );
+               RequestContext::getMain()->setUser( $wgUser );
 
                $this->settings = $this->internalDefaults;
 
@@ -404,7 +406,7 @@ abstract class Installer {
                }
 
                $this->parserTitle = Title::newFromText( 'Installer' );
-               $this->parserOptions = new ParserOptions; // language will be wrong :(
+               $this->parserOptions = new ParserOptions( $wgUser ); // language will be wrong :(
                $this->parserOptions->setEditSection( false );
        }
 
@@ -730,20 +732,6 @@ abstract class Installer {
                return true;
        }
 
-       /**
-        * Environment check for register_globals.
-        * Prevent installation if enabled
-        * @return bool
-        */
-       protected function envCheckRegisterGlobals() {
-               if ( wfIniGetBool( 'register_globals' ) ) {
-                       $this->showMessage( 'config-register-globals-error' );
-                       return false;
-               }
-
-               return true;
-       }
-
        /**
         * Some versions of libxml+PHP break < and > encoding horribly
         * @return bool
@@ -759,22 +747,6 @@ abstract class Installer {
                return true;
        }
 
-       /**
-        * Environment check for magic_quotes_(gpc|runtime|sybase).
-        * @return bool
-        */
-       protected function envCheckMagicQuotes() {
-               $status = true;
-               foreach ( array( 'gpc', 'runtime', 'sybase' ) as $magicJunk ) {
-                       if ( wfIniGetBool( "magic_quotes_$magicJunk" ) ) {
-                               $this->showError( "config-magic-quotes-$magicJunk" );
-                               $status = false;
-                       }
-               }
-
-               return $status;
-       }
-
        /**
         * Environment check for mbstring.func_overload.
         * @return bool
@@ -789,19 +761,6 @@ abstract class Installer {
                return true;
        }
 
-       /**
-        * Environment check for safe_mode.
-        * @return bool
-        */
-       protected function envCheckSafeMode() {
-               if ( wfIniGetBool( 'safe_mode' ) ) {
-                       $this->setVar( '_SafeMode', true );
-                       $this->showMessage( 'config-safe-mode' );
-               }
-
-               return true;
-       }
-
        /**
         * Environment check for the XML module.
         * @return bool
@@ -1779,6 +1738,15 @@ abstract class Installer {
 
                // Some of the environment checks make shell requests, remove limits
                $GLOBALS['wgMaxShellMemory'] = 0;
+
+               $GLOBALS['wgSessionProviders'] = array(
+                       array(
+                               'class' => 'InstallerSessionProvider',
+                               'args' => array( array(
+                                       'priority' => 1,
+                               ) )
+                       )
+               );
        }
 
        /**
diff --git a/includes/installer/InstallerSessionProvider.php b/includes/installer/InstallerSessionProvider.php
new file mode 100644 (file)
index 0000000..2b9f418
--- /dev/null
@@ -0,0 +1,60 @@
+<?php
+/**
+ * Session provider which always provides the same session ID and doesn't
+ * persist the session. For use in the installer when ObjectCache doesn't
+ * work anyway.
+ *
+ * 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 Deployment
+ */
+
+use MediaWiki\Session\SessionProvider;
+use MediaWiki\Session\SessionBackend;
+use MediaWiki\Session\SessionInfo;
+
+class InstallerSessionProvider extends SessionProvider {
+       /**
+        * Pretend there is a session, to avoid MWCryptRand overhead
+        */
+       public function provideSessionInfo( WebRequest $request ) {
+               return new SessionInfo( 1, array(
+                       'provider' => $this,
+                       'id' => str_repeat( 'x', 32 ),
+               ) );
+       }
+
+       /**
+        * Yes we will treat your data with great care!
+        */
+       public function persistsSessionId() {
+               return true;
+       }
+
+       /**
+        * Sure, you can be whoever you want, as long as you have ID 0
+        */
+       public function canChangeUser() {
+               return true;
+       }
+
+       public function persistSession( SessionBackend $session, WebRequest $request ) {
+       }
+
+       public function unpersistSession( WebRequest $request ) {
+       }
+}
index 3b6a37f..e054709 100644 (file)
@@ -33,7 +33,6 @@ class LocalSettingsGenerator {
        protected $values = array();
        protected $groupPermissions = array();
        protected $dbSettings = '';
-       protected $safeMode = false;
        protected $IP;
 
        /**
@@ -65,7 +64,7 @@ class LocalSettingsGenerator {
                                'wgRightsText', '_MainCacheType', 'wgEnableUploads',
                                '_MemCachedServers', 'wgDBserver', 'wgDBuser',
                                'wgDBpassword', 'wgUseInstantCommons', 'wgUpgradeKey', 'wgDefaultSkin',
-                               'wgMetaNamespace', 'wgLogo',
+                               'wgMetaNamespace', 'wgLogo', 'wgAuthenticationTokenVersion',
                        ),
                        $db->getGlobalNames()
                );
@@ -91,7 +90,6 @@ class LocalSettingsGenerator {
                }
 
                $this->dbSettings = $db->getLocalSettings();
-               $this->safeMode = $installer->getVar( '_SafeMode' );
                $this->values['wgEmergencyContact'] = $this->values['wgPasswordSender'];
        }
 
@@ -248,7 +246,6 @@ class LocalSettingsGenerator {
                        $locale = '';
                }
 
-               $hashedUploads = $this->safeMode ? '' : '#';
                $metaNamespace = '';
                if ( $this->values['wgMetaNamespace'] !== $this->values['wgSitename'] ) {
                        $metaNamespace = "\$wgMetaNamespace = \"{$this->values['wgMetaNamespace']}\";\n";
@@ -380,12 +377,6 @@ ${serverSetting}
 ## available UTF-8 locale
 {$locale}\$wgShellLocale = \"{$this->values['wgShellLocale']}\";
 
-## If you want to use image uploads under safe mode,
-## create the directories images/archive, images/thumb and
-## images/temp, and make them all writable. Then uncomment
-## this, if it's not already uncommented:
-{$hashedUploads}\$wgHashedUploadDirectory = false;
-
 ## Set \$wgCacheDirectory to a writable directory on the web server
 ## to make your wiki go slightly faster. The directory should not
 ## be publically accessible from the web.
@@ -396,6 +387,9 @@ ${serverSetting}
 
 \$wgSecretKey = \"{$this->values['wgSecretKey']}\";
 
+# Changing this will log out all existing sessions.
+\$wgAuthenticationTokenVersion = \"{$this->values['wgAuthenticationTokenVersion']}\";
+
 # Site upgrade key. Must be set to a string (default provided) to turn on the
 # web installer while LocalSettings.php is in place
 \$wgUpgradeKey = \"{$this->values['wgUpgradeKey']}\";
index 2c08c9c..1d17c94 100644 (file)
@@ -178,6 +178,13 @@ class WebInstallerExistingWiki extends WebInstallerPage {
                // All good
                $this->setVar( '_ExistingDBSettings', true );
 
+               // Copy $wgAuthenticationTokenVersion too, if it exists
+               $this->setVar( 'wgAuthenticationTokenVersion',
+                       isset( $vars['wgAuthenticationTokenVersion'] )
+                               ? $vars['wgAuthenticationTokenVersion']
+                               : null
+               );
+
                return $status;
        }
 
index 2fadfce..838c953 100644 (file)
@@ -16,7 +16,7 @@
        "config-localsettings-upgrade": "<code>LocalSettings.php</code> قد تم كشف ملف.\nلترقية هذا التنصيب، رجاء أدخل قيمة <code>$wgUpgradeKey</code> في الصندوق أدناه.\nستجده في <code>LocalSettings.php</code>.",
        "config-localsettings-cli-upgrade": "<code>LocalSettings.php</code> قد تم كشف ملف.\nلترقية هذا التنصيب، رجاء قم بتفعيل <code>update.php</code> عوضًا عن ذلك",
        "config-localsettings-key": "مفتاح ترقية:",
-       "config-localsettings-badkey": "المفتاح الذي قدمته غير صحيح.",
+       "config-localsettings-badkey": "مفتاح الترقية الذي قدمته غير صحيح.",
        "config-upgrade-key-missing": "تنصيب موجود للميدياويكي قد تم اكتشافه.\nلترقية هذا التنصيب، الرجاء وضع السطر أسفل <code>LocalSettings.php</code> الخاصة بك:\n\n$1",
        "config-localsettings-incomplete": "صفحة <code>LocalSettings.php</code> يبدو أنها ناقصة.\nالمتغير $1 لم يتم تعيينه.\nالرجاء تغيير <code>LocalSettings.php</code> لكي يتم تعيين المتغير، ثم اضغط على \"{{int:Config-continue}}\".",
        "config-localsettings-connection-error": "تمت مصادفة خطأ أثناء الاتصال بقاعدة البيانات باستخدام الإعدادات المحددة في <code>LocalSettings.php</code> أو <code>AdminSettings.php</code>. الرجاء إصلاح هذه الإعدادات وحاول مجددًا.\n\n$1",
        "config-license-pd": "ملكية عامة",
        "config-license-cc-choose": "اختر ترخيص مشاع إبداعي مخصص",
        "config-email-settings": "إعدادات البريد الإلكتروني",
+       "config-email-user": "تفعيل البريد الإلكتروني من المستخدم إلى مستخدم آخر",
+       "config-email-user-help": "يتيح لكل المستخدمين إرسال رسائل بريد إلكتروني إلى بعضهم البعض إذا فعَّلوا هذا الخيار في تفضيلاتهم.",
        "config-email-usertalk": "فعل إخطارات صفحات نقاش المستخدمين",
        "config-email-watchlist": "تمكين إشعارات قائمة المراقبة",
        "config-email-sender": "يرجع عنوان البريد الإلكتروني:",
        "config-install-tables": "إنشاء الجداول",
        "config-install-stats": "بدء الإحصاءات",
        "config-install-keys": "توليد المفاتيح السرية",
+       "config-install-sysop": "إنشاء حساب مستخدم إداري",
        "config-install-mainpage": "إنشاء صفحة رئيسية بالمحتوى الافتراضي",
        "config-help": "مساعدة",
        "config-help-tooltip": "اضغط للتوسيع",
index e9ca48b..6265a84 100644 (file)
@@ -16,7 +16,7 @@
        "config-localsettings-upgrade": "Byl nalezen soubor <code>LocalSettings.php</code>.\nPokud chcete stávající instalaci aktualizovat, zadejte hodnotu <code>$wgUpgradeKey</code>, kterou naleznete v souboru <code>LocalSettings.php</code>, do následujícího rámečku.",
        "config-localsettings-cli-upgrade": "Byl detekován soubor <code><code>LocalSettings.php</code></code>\nPro aktualizaci spusťte místo instalace skript <code>update.php</code>.",
        "config-localsettings-key": "Klíč pro aktualizaci:",
-       "config-localsettings-badkey": "Zadaný klíč je nesprávný.",
+       "config-localsettings-badkey": "Zadaný klíč pro aktualizaci je nesprávný.",
        "config-upgrade-key-missing": "Byla detekována existující instalace MediaWiki.\nPokud ji chcete aktualizovat, přidejte následující řádku na konec souboru <code>LocalSettings.php</code>:\n\n$1",
        "config-localsettings-incomplete": "Existující soubor <code>LocalSettings.php</code> vypadá neúplný.\nNení nastavena proměnná $1.\nUpravte soubor <code>LocalSettings.php</code> tak, aby tuto proměnnou obsahoval, a klikněte na „{{int:Config-continue}}“.",
        "config-localsettings-connection-error": "Při připojování k databázi s využitím nastavení uvedených v <code>LocalSettings.php</code> došlo k chybě. Opravte tato nastavení a zkuste to znovu.\n\n$1",
        "config-no-db": "Nepodařilo se nalézt vhodný databázový ovladač! Musíte nainstalovat databázový ovladač pro PHP.\n{{PLURAL:$2|Je podporován následující typ databáze|Jsou podporovány následující typy databází}}: $1.\n\nPokud jste si PHP přeložili sami, překonfigurujte ho se zapnutým databázovým klientem, například pomocí <code>./configure --with-mysql</code>.\nPokud jste PHP nainstalovali z balíčku Debian či Ubuntu, potřebujete nainstalovat také modul <code>php5-mysql</code>.",
        "config-outdated-sqlite": "'''Upozornění''': Máte SQLite $1, které je starší než minimálně vyžadovaná verze $2. SQLite nebude dostupné.",
        "config-no-fts3": "'''Upozornění''': SQLite bylo přeloženo bez [//sqlite.org/fts3.html modulu FTS3], funkce pro vyhledávání zde nebudou dostupné.",
-       "config-register-globals-error": "<strong>Chyba: PHP nastavení <code>[http://php.net/register_globals register_globals]</code> je zapnuto. Pro pokračování v instalaci musí být vypnuto.</strong>\nRady, jak toho dosáhnout, najdete na [https://www.mediawiki.org/wiki/Register_globals https://www.mediawiki.org/wiki/register_globals].",
-       "config-magic-quotes-gpc": "<strong>Kritická chyba: Je zapnuto [http://www.php.net/manual/en/ref.info.php#ini.magic-quotes-gpc magic_quotes_gpc]!</strong>\nToto nastavení nepředvídatelně poškozuje vstupní data.\nMediaWiki nelze nainstalovat ani používat, dokud není toto nastavení vypnuto.",
-       "config-magic-quotes-runtime": "'''Kritická chyba: Je zapnuto [http://www.php.net/manual/en/ref.info.php#ini.magic-quotes-runtime magic_quotes_runtime]!'''\nToto nastavení nepředvídatelně poškozuje vstupní data.\nMediaWiki nelze nainstalovat ani používat, dokud není toto nastavení vypnuto.",
-       "config-magic-quotes-sybase": "'''Kritická chyba: Je zapnuto [http://www.php.net/manual/en/ref.info.php#ini.magic-quotes-sybase magic_quotes_sybase]!'''\nToto nastavení nepředvídatelně poškozuje vstupní data.\nMediaWiki nelze nainstalovat ani používat, dokud není toto nastavení vypnuto.",
        "config-mbstring": "'''Kritická chyba: Je zapnuto [http://www.php.net/manual/en/ref.mbstring.php#mbstring.overload mbstring.func_overload]!'''\nToto nastavení způsobuje chyby a může nepředvídatelně poškozovat vstupní data.\nMediaWiki nelze nainstalovat ani používat, dokud není toto nastavení vypnuto.",
-       "config-safe-mode": "'''Upozornění:''' Je aktivní [http://www.php.net/features.safe-mode bezpečný režim] PHP.\nMůže způsobovat potíže, zejména při použití načítání souborů a podpory <code>math</code>.",
        "config-xml-bad": "Chybí XML modul pro PHP.\nMediaWiki potřebuje funkce v tomto modulu a v této konfiguraci nebude fungovat.\nMožná si budete muset nainstalovat RPM balíček php-xml.",
        "config-pcre-old": "'''Kritická chyba:''' Je vyžadováno PCRE verze $1 nebo novější.\nVaše binárka PHP obsahuje PCRE $2.\n[https://www.mediawiki.org/wiki/Manual:Errors_and_symptoms/PCRE Více informací.]",
        "config-pcre-no-utf8": "'''Kritická chyba''': PHP modul PCRE byl zřejmě přeložen bez podpory PCRE_UTF8.\nMediaWiki vyžaduje ke správné funkci podporu UTF-8.",
index 8d2040d..ea03831 100644 (file)
        "config-no-db": "Could not find a suitable database driver! You need to install a database driver for PHP.\nThe following database {{PLURAL:$2|type is|types are}} supported: $1.\n\nIf you compiled PHP yourself, reconfigure it with a database client enabled, for example, using <code>./configure --with-mysqli</code>.\nIf you installed PHP from a Debian or Ubuntu package, then you also need to install, for example, the <code>php5-mysql</code> package.",
        "config-outdated-sqlite": "<strong>Warning:</strong> you have SQLite $1, which is lower than minimum required version $2. SQLite will be unavailable.",
        "config-no-fts3": "<strong>Warning:</strong> SQLite is compiled without the [//sqlite.org/fts3.html FTS3 module], search features will be unavailable on this backend.",
-       "config-register-globals-error": "<strong>Error: PHP's <code>[http://php.net/register_globals register_globals]</code> option is enabled.\nIt must be disabled to continue with the installation.</strong>\nSee [https://www.mediawiki.org/wiki/register_globals https://www.mediawiki.org/wiki/register_globals] for help on how to do so.",
-       "config-magic-quotes-gpc": "<strong>Fatal: [http://www.php.net/manual/en/ref.info.php#ini.magic-quotes-gpc magic_quotes_gpc] is active!</strong>\nThis option corrupts data input unpredictably.\nYou cannot install or use MediaWiki unless this option is disabled.",
-       "config-magic-quotes-runtime": "<strong>Fatal: [http://www.php.net/manual/en/ref.info.php#ini.magic-quotes-runtime magic_quotes_runtime] is active!'</strong>\nThis option corrupts data input unpredictably.\nYou cannot install or use MediaWiki unless this option is disabled.",
-       "config-magic-quotes-sybase": "<strong>Fatal: [http://www.php.net/manual/en/ref.info.php#ini.magic-quotes-sybase magic_quotes_sybase] is active!</strong>\nThis option corrupts data input unpredictably.\nYou cannot install or use MediaWiki unless this option is disabled.",
        "config-mbstring": "<strong>Fatal: [http://www.php.net/manual/en/ref.mbstring.php#mbstring.overload mbstring.func_overload] is active!</strong>\nThis option causes errors and may corrupt data unpredictably.\nYou cannot install or use MediaWiki unless this option is disabled.",
-       "config-safe-mode": "<strong>Warning:</strong> PHP's [http://www.php.net/features.safe-mode safe mode] is active.\nIt may cause problems, particularly if using file uploads and <code>math</code> support.",
        "config-xml-bad": "PHP's XML module is missing.\nMediaWiki requires functions in this module and will not work in this configuration.\nYou may need to install the php-xml RPM package.",
        "config-pcre-old": "<strong>Fatal:</strong> PCRE $1 or later is required.\nYour PHP binary is linked with PCRE $2.\n[https://www.mediawiki.org/wiki/Manual:Errors_and_symptoms/PCRE More information].",
        "config-pcre-no-utf8": "<strong>Fatal:</strong> PHP's PCRE module seems to be compiled without PCRE_UTF8 support.\nMediaWiki requires UTF-8 support to function correctly.",
index 75475ad..a0f3ed7 100644 (file)
@@ -43,6 +43,7 @@
        "config-wincache": "[http://www.iis.net/download/WinCacheForPhp WinCache] instalatuta dago",
        "config-diff3-bad": "GNU diff3 ez da aurkitu.",
        "config-db-type": "Datu-base mota:",
+       "config-db-host": "Datu-basearen zerbitzaria:",
        "config-db-host-oracle": "Datu-baseko TNS:",
        "config-db-wiki-settings": "Wiki hau identifikatu",
        "config-db-name": "Datu-base izena:",
index fc99677..b5ed996 100644 (file)
@@ -20,7 +20,7 @@
        "config-localsettings-upgrade": "یک پرونده <code>LocalSettings.php</code> شناسایی شده‌است.\nبرای ارتقاء این نصب لطفاً مقدار <code>$wgUpgradeKey</code> در جعبه زیر وارد کنید.\nشما می‌توانید آن را در <code>LocalSettings.php</code> پیدا کنید.",
        "config-localsettings-cli-upgrade": "یک پرونده <code>LocalSettings.php</code> شناسایی شده است.\nبرای ارتقاء این نصب، لطفاً <code>update.php</code> را اجرا کنید.",
        "config-localsettings-key": "کلید ارتقا:",
-       "config-localsettings-badkey": "کلیدی که شما ارائه کردید نادرست است.",
+       "config-localsettings-badkey": "کلید به‌روزرسانی‌ای که شما ارائه کردید نادرست است.",
        "config-upgrade-key-missing": "نصب موجود مدیاویکی شناسایی شده‌است.\nبرای بروزرسانی این نصب، لطفاً خط زیر را در آخر کد \nقرار دادن به نصب ارتقاء داده شده، به خط زیر لطفاً در پایین خود را <code>LocalSettings.php</code> قرار دهید:\n\n$1",
        "config-localsettings-incomplete": "وجود <code>LocalSettings.php</code> به نظر ناقص می‌رسد.\nمتغیر $1 تنظیم نشده‌است.\nبرای اینکه این متغیر تنظیم شود لطفاً <code>LocalSettings.php</code> را تغییر دهید، و \"{{int:Config-continue}}\" را کلیک کنید.",
        "config-localsettings-connection-error": "هنگام اتصال به پایگاه اطلاعاتی که ازتنظیمات مشخص شده در<code>LocalSettings.php</code> استفاده می‌کند، خطایی رخ داد. لطفاً این تنظیمات را نصب کنید و دوباره تلاش کنید.\n$1",
        "config-no-db": "درایور پایگاه اطلاعاتی مناسب پیدا نشد! شما لازم دارید یک درایور پایگاه اطلاعاتی  برای پی‌اچ‌پی نصب کنید.انواع پایگاه اطلاعاتی زیر پشتیبانی شده‌اند:$1.\nاگر شما در گروه اشتراک‌گذاری هستید، از تهیه کنندهٔ گروه خود برای نصب یک درایور پایگاه اطلاعاتی مناسب {{PLURAL:$2|سوأل کنید.|سوأل کنید.}}\nاگر خود، پی‌اچ‌پی را تهیه کرده‌اید، با یک پردازشگر فعال دوباره پیکربندی کنید، برای مثال از <code>./configure --with-mysql</code> استفاده کنید.\nاگر پی‌اچ‌پی را از یک بستهٔ دبیان یا آبونتو نصب کرده‌اید، بنابراین لازم دارید بخش php5-mysql را نصب کنید.",
        "config-outdated-sqlite": "''' هشدار:''' شما اس‌کیولایت $1 دارید، که پایین‌تر از حداقل نسخهٔ $2 مورد نیاز است.اس‌کیولایت در دسترس نخواهد بود.",
        "config-no-fts3": "'''هشدار:''' اس‌کیولایت بدون [//sqlite.org/fts3.html FTS3 module] تهیه شده‌است ، جستجوی ویژگی‌ها در این بخش پیشین در دسترس نخواهد‌بود.",
-       "config-register-globals-error": "<strong>خطا:  پی‌اچ‌پی<code>[http://php.net/register_globals register_globals]</code> گزینه فعال است.\nبرای ادامه نصب باید غیر فعال باشد.</strong>\n[Https://www.mediawiki.org/wiki/register_globals https://www.mediawiki.org/wiki/register_globals] را برای کمک در مورد نحوه انجام این کار ببینید.",
-       "config-magic-quotes-gpc": "<strong>هشدار مهم: [http://www.php.net/manual/en/ref.info.php#ini.magic-quotes-gpc magic_quotes_gpc] فعال شده‌است!</strong>\nاین گزینه اطلاعات داده شده به رایانه را به طور غیر‌قابل پیش‌بینی از بین می‌برد.\nشما نمی‌توانید مدیاویکی را نصب یا استفاده کنید مگر اینکه این گزینه غیر‌فعال باشد.",
-       "config-magic-quotes-runtime": "'''مخرب: [http://www.php.net/manual/en/ref.info.php#ini.magic-quotes-runtime magic_quotes_runtime] فعال است.\nاین گزینه اطلاعات داده شده به رایانه را به طور غیر‌قابل پیش‌بینی از بین می‌برد.\nشما نمی‌توانید مدیاویکی را نصب یا استفاده کنید مگر اینکه این گزینه غیر‌فعال باشد.",
-       "config-magic-quotes-sybase": "'''مخرب: [http://www.php.net/manual/en/ref.info.php#ini.magic-quotes-sybase magic_quotes_sybase] فعال است.\nاین گزینه اطلاعات داده شده به رایانه را به طور غیر‌قابل پیش‌بینی از بین می‌برد.\nشما نمی‌توانید مدیاویکی را نصب یا استفاده کنید مگر اینکه این گزینه غیر‌فعال باشد.",
        "config-mbstring": "''' مخرب:[http://www.php.net/manual/en/ref.mbstring.php#mbstring.overload mbstring.func_overload] فعال است.\nاین گزینه باعث ایجاد خطا می‌شود و ممکن است اطلاعات را به طور غیر‌قابل پیش‌بینی از بین ببرد.\nشما نمی‌توانید مدیاویکی را نصب یا استفاده کنید مگر اینکه این گزینه غیر‌فعال باشد.",
-       "config-safe-mode": "'''هشدار:'''  PHP's [http://www.php.net/features.safe-mode safe mode] فعال است.\nممکن است باعث ایجاد مشکلاتی شود، مخصوصاً اگر از ارسال پرونده استفاده شود و <code>math</code> پشتیبانی شود.",
        "config-xml-bad": "ماژول اکس‌ام‌ال پی‌اچ‌پی کار نمی‌کند.\nمدیاویکی نیازمند عملیاتی در این ماژول است و در این پیکربندی کار نخواهد‌کرد.\nشاید نیاز باشد که بستهٔ نرم افزاریِ آرپی‌ام پی‌اچ‌پی-ایکس‌ام‌ال را نصب کنید.",
        "config-pcre-old": "''' خطای اساسی:'' ' PCRE  $1  یا بعدا مورد نیاز است.\nکد باینری پی‌اچ‌پی‌تان با PCRE  $2 پیوند دارد.\n[https://www.mediawiki.org/wiki/Manual:Errors_and_symptoms/PCRE اطلاعات بیشتر].",
        "config-pcre-no-utf8": "'''مخرب:''' به‌ نظر می‌رسد ماژول پی‌سی‌آرایی پی‌اچ‌پی بدون پشتیبانی پی‌سی‌آرایی_یو‌تی‌اف۸ تهیه شده‌است.\nمدیاویکی برای درست عمل کردن نیازمند پشتیبانی یوتی‌اف-۸ است.",
index 2b897f1..223955d 100644 (file)
@@ -16,7 +16,8 @@
                        "Stryn",
                        "SMAUG",
                        "SuperPete",
-                       "McSalama"
+                       "McSalama",
+                       "Jaakkoh"
                ]
        },
        "config-desc": "MediaWiki-asennin",
@@ -63,7 +64,6 @@
        "config-env-hhvm": "HHVM $1 on asennettu.",
        "config-no-db": "Sopivaa tietokanta-ajuria ei löytynyt! Sinun täytyy asentaa tietokanta-ajurit PHP:lle.\nSeuraavat tietokantatyypit ovat tuettuja: $1.",
        "config-outdated-sqlite": "<strong>Varoitus:</strong> sinulla on käytössä SQLite $1, joke on vanhempi kuin vähintään vaadittava versio $2. SQLite ei ole saatavilla.",
-       "config-safe-mode": "'''Varoitus:''' PHP:n [http://www.php.net/features.safe-mode safe mode] -tila on aktiivinen.\nSe voi aiheuttaa ongelmia erityisesti tiedostojen tallentamisen ja matemaattisten kaavojen kanssa.",
        "config-xml-bad": "PHP:n XML-moduulia ei löydy.\nMediaWiki käyttää tämän moduulin funktioita, eikä toimi tässä kokoonpanossa.\nJos käytät Mandrakea, asenna php-xml paketti.",
        "config-memory-raised": "PHP:n <code>memory_limit</code> on $1, nostetaan arvoon $2.",
        "config-memory-bad": "'''Varoitus:''' PHP:n <code>memory_limit</code> on $1.\nTämä on luultavasti liian alhainen.\nAsennus saattaa epäonnistua!",
        "config-upload-deleted-help": "Valitse hakemisto johon poistetut tiedostot arkistoidaan.\nHakemiston ei tulisi olla käytettävissä internetverkosta.",
        "config-logo": "Logon URL-osoite",
        "config-cc-again": "Valitse uudelleen...",
+       "config-advanced-settings": "Lisäasetukset",
        "config-extensions": "Laajennukset",
        "config-extensions-help": "Yllä luetellut laajennukset löytyvät <code>./extensions</code> hakemistosta.\n\nNe saattavat vaatia lisäasetuksia, mutta voit ottaa ne käyttöön nyt.",
        "config-skins": "Ulkoasut",
index 4e09904..1c9fe79 100644 (file)
@@ -17,7 +17,7 @@
        "config-localsettings-upgrade": "זוהה קובץ <code>LocalSettings.php</code>.\nכדי לשדרג את ההתקנה הזאת, נא להקליד את הערך של <code>$wgUpgradeKey</code> בתיבה להלן.\nאפשר למצוא אותו בקובץ <code>LocalSettings.php</code>.",
        "config-localsettings-cli-upgrade": "זוהה קובץ <code>LocalSettings.php</code>.\nכדי לשדרג את ההתקנה הזאת, יש להריץ את <code>update.php</code> ולא את התהליך הזה.",
        "config-localsettings-key": "מפתח השדרוג:",
-       "config-localsettings-badkey": "×\94×\9eפת×\97 שהקלדת שגוי.",
+       "config-localsettings-badkey": "×\9eפת×\97 ×\94ש×\93ר×\95×\92 שהקלדת שגוי.",
        "config-upgrade-key-missing": "זוהתה התקנה קיימת של מדיה־ויקי.\nכדי לשדרג את ההתקנה הזאת, יש לכתוב את השורה הבא בתחתית קובץ <code>LocalSettings.php</code> שלך:\n\n$1",
        "config-localsettings-incomplete": "נראה שקובץ <code>LocalSettings.php</code> הקיים אינו שלם.\nהמשתנה $1 אינו מוגדר.\nנא לשנות את הקובץ <code>LocalSettings.php</code> כך שהמשתנה הזה יהיה מוגדר וללחוץ \"{{int:Config-continue}}\".",
        "config-localsettings-connection-error": "אירעה שגיאה בעת חיבור למסד נתונים עם הגדרות ב־<code>LocalSettings.php</code>. נא לתקן את ההגדרות האלו ולנסות שוב.\n\n$1",
        "config-no-db": "לא נמצא דרייבר מסד נתונים מתאים. יש להתקין דרייבר מסד נתונים ל־PHP.\n{{PLURAL:$2|נתמך הסוג הבא של מסד נתונים|נתמכים הסוגים הבאים של מסדי נתונים}}: $1.\n\nאם קִמפלת את PHP בעצמך, יש להגדיר אותו מחדש ולהפעיל את לקוח מסד נתונים, למשל באמצעות <code dir=\"ltr\">./configure --with-mysqli</code>.\nאם התקנת את PHP מחבילה של דביאן או של אובונטו, יש להתקין, למשל, גם את המודול <code dir=\"ltr\">php5-mysql</code>.",
        "config-outdated-sqlite": "'''אזהרה''': במערכת מתוקן SQLite $1. גרסה זו לא נתמכת ולשימוש ב־SQLite נדרשת גרסה $2 לפחות. SQLlite לא יהיה זמין.",
        "config-no-fts3": "'''אזהרה''': SQLite מקומפל ללא [//sqlite.org/fts3.html מודול FTS]. יכולות חיפוש לא יהיו זמינות בהתקנה הזאת.",
-       "config-register-globals-error": "<strong>שגיאה: האפשרות <code>[http://php.net/register_globals register_globals]</code> של PHP מופעלת.\nצריך לכבות אותה כדי להמשיך בהתקנה.</strong>\nר' [https://www.mediawiki.org/wiki/register_globals https://www.mediawiki.org/wiki/register_globals] להסבר איך לעשות את זה.",
-       "config-magic-quotes-gpc": "<strong>סופני: האפשרות [http://www.php.net/manual/en/ref.info.php#ini.magic-quotes-gpc magic_quotes_gpc] פעילה!</strong>\nהאפשרות הזאת מקלקלת נתוני קלט באופן בלתי־ניתן לחיזוי.\nלא ניתן להתקין את מדיה־ויקי אם האפשרות הזאת אינה כבויה.",
-       "config-magic-quotes-runtime": "<strong>שגיאה סופנית: האפשרות [http://www.php.net/manual/en/ref.info.php#ini.magic-quotes-runtime magic_quotes_runtime] פעילה!</strong>\nהאפשרות הזאת מעוותת את נתוני הקלט באופן בלתי־צפוי.\nלא ניתן להתקין את מדיה־ויקי אלא אם האפשרות הזאת תכובה.",
-       "config-magic-quotes-sybase": "'''שגיאה סופנית''': האפשרות [http://www.php.net/manual/en/ref.info.php#ini.magic-quotes-sybase magic_quotes_sybase] פעילה!'''\nהאפשרות הזאת מעוותת את נתוני הקלט באופן בלתי־צפוי.\nלא ניתן להתקין את מדיה־ויקי או להשתמש בה אלא אם האפשרות הזאת תכובה.",
        "config-mbstring": "'''שגיאה סופנית''': האפשרות [http://www.php.net/manual/en/ref.mbstring.php#mbstring.overload mbstring.func_overload] פעילה!'''\nהאפשרות הזאת גורמת לשגיאות ומעוותת את נתוני הקלט באופן בלתי־צפוי.\nלא ניתן להתקין את מדיה־ויקי או להשתמש בה אלא אם האפשרות הזאת תכובה.",
-       "config-safe-mode": "'''אזהרה:''' האפשרות [http://www.php.net/features.safe-mode safe mode] של PHP פעילה.\nהיא יכולה לגרום לבעיות, במיוחד אם אתם משתמשים בהעלאת קבצים או ב־<code>math</code>.",
        "config-xml-bad": "מודול XML של PHP חסר.\nמדיה־ויקי דורשת פונקציות של המודול ולא תעבוד עם הגדרות כאלו.\nייתכן שצריך להתקין באמצעות RPM את חבילת php-xml.",
        "config-pcre-old": "<strong>שגיאה סופנית:</strong> חובה להתקין PCRE מגרסה $1 או גרסה חדשה יותר.\nקובץ הרצת ה־PHP שלך מקושר עם PCRE מגרסה $2.\n[https://www.mediawiki.org/wiki/Manual:Errors_and_symptoms/PCRE מידע נוסף].",
        "config-pcre-no-utf8": "'''שגיאה סופנית''': נראה שמודול PCRE של PHP מקומפל ללא תמיכה ב־PCRE_UTF8.\nמדיה־ויקי דורשת תמיכה ב־UTF-8 לפעילות נכונה.",
@@ -78,6 +73,7 @@
        "config-apc": "[http://www.php.net/apc APC] מותקן",
        "config-wincache": "[http://www.iis.net/download/WinCacheForPhp WinCache] מותקן",
        "config-no-cache": "'''אזהרה:''' אחת מהתוכנות הבאות לא נמצאה: [http://www.php.net/apc APC]&rlm;, [http://xcache.lighttpd.net/ XCache] או [http://www.iis.net/download/WinCacheForPhp WinCache].\nמטמון עצמים לא מופעל.",
+       "config-no-cache-apcu": "<strong>אזהרה:</strong> לא נמצא [http://www.php.net/apcu APCu]‏, [http://xcache.lighttpd.net/ XCache] או [http://www.iis.net/download/WinCacheForPhp WinCache].\nמטמון עצמים לא מופעל.",
        "config-mod-security": "'''אזהרה''': בשרת הווב שלך מופעל [http://modsecurity.org/ mod_security]. אם הוא לא מוגדר טוב, זה יכול לגרום לבעיות במדיה־ויקי ובתכנה אחרת שמאפשרת למשתמשים לשלוח תוכן שרירותי.\nיש לקרוא את [http://modsecurity.org/documentation/ התיעוד של mod_security] או ליצור קשר עם אנשי התמיכה של שירותי האירוח שלכם אם מופיעות לך שגיאות אקראיות.",
        "config-diff3-bad": "GNU diff3 לא נמצא.",
        "config-git": "נמצאה Git, תכנת בקרת התצורה: <code dir=\"ltr\">$1</code>.",
index 2021cfc..3f92528 100644 (file)
@@ -8,7 +8,8 @@
                        "Misibacsi",
                        "Tacsipacsi",
                        "Dorgan",
-                       "Macofe"
+                       "Macofe",
+                       "Máté"
                ]
        },
        "config-desc": "A MediaWiki telepítője",
@@ -17,7 +18,7 @@
        "config-localsettings-upgrade": "Már létezik a <code>LocalSettings.php</code> fájl.\nA telepített szoftver frissítéséhez írd be az alábbi mezőbe a <code>$wgUpgradeKey</code> beállítás értékét, melyet a <code>LocalSettings.php</code> nevű fájlban találhatsz meg.",
        "config-localsettings-cli-upgrade": "A <code>LocalSettings.php</code> fájl megtalálható.\nA telepített rendszer frissítéséhez futtasd az <code>update.php</code>-t.",
        "config-localsettings-key": "Frissítési kulcs:",
-       "config-localsettings-badkey": "A megadott kulcs érvénytelen.",
+       "config-localsettings-badkey": "A megadott frissítési kulcs érvénytelen.",
        "config-upgrade-key-missing": "A telepítő a MediaWiki meglévő példányát észlelte.\nA telepített rendszer frissítéséhez helyezd el az alábbi sort a <code>LocalSettings.php</code> végére:\n\n$1",
        "config-localsettings-incomplete": "A meglévő <code>LocalSettings.php</code> hiányosnak tűnik.\nA(z) $1 változó értéke nincs beállítva.\nMódosítsd a <code>LocalSettings.php</code> fájlt úgy, hogy ez a változó be legyen állítva, majd kattints a „{{int:Config-continue}}” gombra.",
        "config-localsettings-connection-error": "Nem sikerült csatlakozni az adatbázishoz a <code>LocalSettings.php</code>-ben megadott adatokkal. Ellenőrizd a beállításokat, majd próbáld újra.\n\n$1",
        "config-no-db": "Nem sikerült egyetlen használható adatbázis-illesztőprogramot sem találni. Telepítened kell egyet a PHP-hez.\nA következő {{PLURAL:$2|adatbázistípus támogatott|adatbázistípusok támogatottak}}: $1.\n\nHa a PHP-t magad fordítottad, konfiguráld újra úgy, hogy engedélyezve legyen egy adatbáziskliens, pl. a <code>./configure --with-mysql</code> parancs használatával.\nHa a PHP-t Debian vagy Ubuntu csomaggal telepítetted, akkor szükséged lesz például a php5-mysql csomagra is.",
        "config-outdated-sqlite": "<strong>Figyelmeztetés:</strong> SQLite $1 verziód van, ami alacsonyabb a legalább szükséges $2 verziónál. Az SQLite nem lesz elérhető.",
        "config-no-fts3": "'''Figyelmeztetés''': Az SQLite [//sqlite.org/fts3.html FTS3 modul] nélkül lett fordítva, a keresési funkciók nem fognak működni ezen a rendszeren.",
-       "config-magic-quotes-runtime": "'''Kritikus hiba: a [http://www.php.net/manual/en/ref.info.php#ini.magic-quotes-runtime magic_quotes_runtime] aktív!'''\nEz a beállítás kiszámíthatatlan károkat okoz a bevitt adatokban.\nA MediaWiki csak akkor telepíthető, ha ki van kapcsolva.",
-       "config-magic-quotes-sybase": "'''Kritikus hiba: a [http://www.php.net/manual/en/ref.info.php#ini.magic-quotes-runtime magic_quotes_sybase] aktív!'''\nEz a beállítás kiszámíthatatlan károkat okoz a bevitt adatokban.\nA MediaWiki csak akkor telepíthető, ha ki van kapcsolva.",
        "config-mbstring": "'''Kritikus hiba: az [http://www.php.net/manual/en/ref.info.php#ini.magic-quotes-runtime mbstring.func_overload] aktív!'''\nEz a beállítás hibákat okoz és kiszámíthatatlanul károsíthatja bevitt adatokat.\nA MediaWiki csak akkor telepíthető, ha ki van kapcsolva.",
-       "config-safe-mode": "'''Figyelmeztetés:''' A PHP [http://www.php.net/features.safe-mode safe mode]-ja be van kapcsolva.\nProblémákat okozhat, különösen a fájlfeltöltéseknél és a <code>math</code>-támogatás használatánál.",
        "config-xml-bad": "A PHP XML-modulja hiányzik.\nEgyes MediaWiki-funkciók, melyek ezt a modult igénylik, nem fognak működni ilyen konfigurációban.\nSzükséges lehet a php-xml RPM-csomag telepítése.",
        "config-pcre-old": "<strong>Kritikus hiba:</strong> PCRE $1 vagy későbbi szükséges.\nA Te PHP binárisod PCRE $2-vel lett linkelve.\n[https://www.mediawiki.org/wiki/Manual:Errors_and_symptoms/PCRE További információ].",
        "config-pcre-no-utf8": "'''Kritikus hiba''': Úgy tűnik, hogy a PHP PRCE modulja PRCE_UTF8 támogatás nélkül lett fordítva.\nA MediaWikinek UTF-8-támogatásra van szüksége a helyes működéshez.",
index 17b5658..f524b32 100644 (file)
@@ -1,9 +1,38 @@
 {
        "@metadata": {
                "authors": [
-                       "Snævar"
+                       "Snævar",
+                       "Sveinn í Felli"
                ]
        },
+       "config-information": "Upplýsingar",
+       "config-your-language": "Tungumálið þitt:",
+       "config-your-language-help": "Veldu tungumál að nota við uppsetninguna.",
+       "config-back": "← Til baka",
+       "config-continue": "Halda áfram →",
+       "config-page-language": "Tungumál",
+       "config-page-welcome": "Velkomin í MediaWiki!",
+       "config-page-dbconnect": "Tengjast gagnagrunni",
+       "config-page-name": "Heiti",
+       "config-page-options": "Valkostir",
+       "config-page-install": "Setja upp",
+       "config-page-complete": "Lokið!",
+       "config-page-restart": "Byrja uppsetningu aftur",
+       "config-page-readme": "Lesa meira",
+       "config-page-releasenotes": "Athugasemdir með útgáfu",
+       "config-page-copying": "Afritun",
+       "config-page-upgradedoc": "Uppfærsla",
+       "config-env-php": "PHP $1 er uppsett.",
+       "config-env-hhvm": "HHVM $1 er uppsett.",
+       "config-mysql-utf8": "UTF-8",
+       "config-ns-generic": "Verkefni",
+       "config-admin-name": "Notandanafnið þitt:",
+       "config-admin-password": "Lykilorð:",
+       "config-admin-password-confirm": "Lykilorðið aftur:",
+       "config-admin-email": "Tölvupóstfang:",
+       "config-license-pd": "Almenningseign",
+       "config-install-step-done": "lokið",
+       "config-install-step-failed": "mistókst",
        "mainpagetext": "'''Uppsetning á MediaWiki heppnaðist.'''",
        "mainpagedocfooter": "Ráðfærðu þig við [//meta.wikimedia.org/wiki/Help:Contents Notandahandbókina] fyrir frekari upplýsingar um notkun wiki-hugbúnaðarins.\n\n== Fyrir byrjendur ==\n\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Listi yfir uppsetningarstillingar]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki Algengar spurningar MediaWiki]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Póstlisti MediaWiki-útgáfa]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Læra hvernig á að berjast við amapóst á þínum wiki]"
 }
index bf7df55..fbbeb66 100644 (file)
@@ -12,7 +12,7 @@
        "config-localsettings-upgrade": "'''Opgepasst''': E Fichier <code>LocalSettings.php</code> gouf fonnt.\nÄr Software kann aktualiséiert ginn, setzt w.e.g. de Wäert vum <code>$wgUpgradeKey</code> an d'Këscht.\nDir fannt en am <code>LocalSettings.php</code>.",
        "config-localsettings-cli-upgrade": "E Fichier <code>LocalSettings.php</code> gouf fonnt.\nFir dës Installatioun z'aktuaéliséieren start w.e.g. <code>update.php</code>",
        "config-localsettings-key": "Aktualisatiounsschlëssel:",
-       "config-localsettings-badkey": "De Schlëssel deen Dir aginn hutt ass net korrekt",
+       "config-localsettings-badkey": "Den Aktualisatiouns-Schlëssel deen Dir aginn hutt ass net korrekt",
        "config-localsettings-incomplete": "De Fichier <code>LocalSettings.php</code> schéngt net komplett ze sinn.\nD'Variabel $1 ass net definéiert.\nÄnnert w.e.g. de Fichier <code>LocalSettings.php</code> sou datt déi Variabel definéiert ass a klickt op \"{{int:Config-continue}}\".",
        "config-session-error": "Feeler beim Starte vun der Sessioun: $1",
        "config-no-session": "D'Donnéeë vun ärer Sessioun si verluergaangen!\nKuckt Är php.ini no a vergewëssert Iech datt <code>session.save_path</code>  op adequate REpertoire agestallt ass.",
index 9b62368..9366983 100644 (file)
@@ -12,7 +12,7 @@
        "config-localsettings-upgrade": "È stato rilevato nu file <code>LocalSettings.php</code>.\nP'agghiurnà sta installazione, pe' piacere nzertàte 'o valore 'e <code>$wgUpgradeKey</code> dint' 'a cascia ccà abbascio.\n'O putite truvà dint'a <code>LocalSettings.php</code>.",
        "config-localsettings-cli-upgrade": "È stato scummigliato nu file <code>LocalSettings.php</code>.\nPe l'agghiurnà sta installazione, secutate <code>update.php</code>",
        "config-localsettings-key": "Chiave d'agghiurnamiento:",
-       "config-localsettings-badkey": "'A chiave c'avete dato nun è curretta.",
+       "config-localsettings-badkey": "'A chiave 'agghiurnamento c'avite dato nun è curretta.",
        "config-upgrade-key-missing": "S'è scummigliata n'installazione 'e MediaWiki ch'esisteva già.\nPe' ll'agghiurnà, nzertate pe' piacere sta riga ccà abbascio dint' 'a parta vascia d' 'o <code>LocalSettings.php</code> vuosto:\n\n$1",
        "config-localsettings-incomplete": "'O file <code>LocalSettings.php</code> esistente pare ca fosse cumpleto a metà.\n'A variabbele $1 nun è mpustata.\nCagnate <code>LocalSettings.php</code> in modo ca sta variabbele fosse mpustata e facite clic ncopp'a \"{{int:Config-continue}}\".",
        "config-localsettings-connection-error": "S'è truvato n'errore pe' tramente ca se faceva 'a connessione a 'o database ausanno 'e mpustaziune specificate dint'a <code>LocalSettings.php</code>. Pe' piacere curriggite sti mpustaziuni e provate n'ata vota.\n\n$1",
        "config-no-db": "Nun se può truvà nu driver adatto p' 'o database! È necessario installare nu driver p' 'o PHP.\n{{PLURAL:$2|'O furmato suppurtato|'E furmate suppurtate}} 'e database ccà annanze: $1.\n\nSi cumpilate PHP autonomamente, riaccunciatevello attivando nu client database, p'esempio ausannoo <code>./configure --with-mysqli</code>.\nQuanno fosse installato PHP pe' bbìa 'e nu pacchetto Debian o Ubuntu, allora avite 'a installà pure 'o pacchetto <code>php5-mysql</code>.",
        "config-outdated-sqlite": "'''Attenziò''': tenite 'o SQLite $1 pe' tramente ca ce vulesse 'a verziona $2, SQLite nun sarrà a disposizione.",
        "config-no-fts3": "'''Attenziò''': SQLite è cumpilato senza 'o [//sqlite.org/fts3.html modulo FTS3], 'e funziune 'e p'ascià dinto nun sarranno a disposizione ncopp'a stu backend.",
-       "config-register-globals-error": "<strong>Errore: l'opzione PHP <code>[http://php.net/register_globals register_globals]</code> è apicciata.\nS'avesse 'a stutà pe' cuntinuà c' 'a installazione.</strong>\nVide [https://www.mediawiki.org/wiki/register_globals https://www.mediawiki.org/wiki/register_globals] pe' n'avé n'aiuto ncopp'a comme s'avess'a ffà.",
-       "config-magic-quotes-gpc": "<strong>Fatale: [http://www.php.net/manual/en/ref.info.php#ini.magic-quotes-gpc magic_quotes_gpc] è attivo!</strong>\nChest'opzione scassa 'e date d'input 'n modo scanusciuto.\nNun putite installare o utilizzare MediaWiki, si nun stutate st'opzione.",
-       "config-magic-quotes-runtime": "<strong>Fatale: [http://www.php.net/manual/en/ref.info.php#ini.magic-quotes-runtime magic_quotes_runtime] è attivo!'</strong>\nSt'opzione scassa 'e date 'e na manera scanusciuta.\nNun se può installà o ausà MediaWiki si nun se stuta st'opzione.",
-       "config-magic-quotes-sybase": "<strong>Fatale: [http://www.php.net/manual/en/ref.info.php#ini.magic-quotes-sybase magic_quotes_sybase] è attivo!'</strong>\nSt'opzione scassa 'e date 'e na manera scanusciuta.\nNun se può installà o ausà MediaWiki si nun se stuta st'opzione.",
        "config-mbstring": "<strong>Fatale: [http://www.php.net/manual/en/ref.mbstring.php#mbstring.overload mbstring.func_overload] è attivo!'</strong>\nSt'opzione scassa 'e date 'e na manera scanusciuta.\nNun se può installà o ausà MediaWiki si nun se stuta st'opzione.",
-       "config-safe-mode": "<strong>Warning:</strong> PHP's [http://www.php.net/features.safe-mode safe mode] è attivato.\nPutesse fà cocche probblema, specialmente si state ausanno 'a funziona 'e carrecà file e 'o supporto d' ' e funziune <code>math</code>.",
        "config-xml-bad": "'O modulo XML 'e PHP è mancante.\nA MediaWiki servessero 'e funziune prisente dint'a stu modulo e nun faticarrà c' 'a configurazione 'e mò.\nSi se sta eseguenno Mandrake, installare 'o pacco php-xml RPM.",
        "config-pcre-old": "<strong>Errore fatale:</strong> s'addimanna PCRE  $1 o succiessivo.\n'O file vuosto binario PHP è acucchiato c' 'o PCRE $2.\n[https://www.mediawiki.org/wiki/Manual:Errors_and_symptoms/PCRE Cchiù nfurmaziune].",
        "config-pcre-no-utf8": "<strong>Fatale:</strong> 'E module PCRE d' 'o PHP pare ca se so' compilate senza PCRE_UTF8 supporto.\nA MediaWiki serve nu supporto UTF-8 pe' putè funziunà apposto.",
@@ -73,6 +68,7 @@
        "config-apc": "[http://www.php.net/apc APC] è installato",
        "config-wincache": "[http://www.iis.net/download/WinCacheForPhp WinCache] è installato",
        "config-no-cache": "'''Attenziò:''' [http://www.php.net/apc APC], [http://xcache.lighttpd.net/ XCache] o [http://www.iis.net/download/WinCacheForPhp WinCache] nun so' state truvate.\n'A funziona caching 'e ll'oggette non è apicciata.",
+       "config-no-cache-apcu": "<strong>Attenziò:</strong> [http://www.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] o [http://www.iis.net/download/WinCacheForPhp WinCache] nun so' state truvate.\n'A funziona caching 'e ll'oggette non è apicciata.",
        "config-mod-security": "<strong>Attenziò:</strong> 'O servitore web vuosto téne [http://modsecurity.org/ mod_security]/mod_security2 appicciato. Ce stanno tante mpustaziune commune ca 'o facessero causà prubbleme a MediaWiki e ll'ati software ca permettessero ll'utente 'e pubbrecà cuntenute.\nSi putite, stutate sta funziona. Sinò, riferite 'a [http://modsecurity.org/documentation/ documentaziona ncopp' 'o mod_security] o cuntattate 'o host vuosto pe' ve dà supporto quanno se scummogliasse cocch'errore.",
        "config-diff3-bad": "GNU diff3 nun truvato.",
        "config-git": "Truvato software 'e cuntrollo d' 'a verziona Git: <code>$1</code>.",
index c86698b..51d0afa 100644 (file)
@@ -6,7 +6,8 @@
                        "아라",
                        "Danmichaelo",
                        "Jeblad",
-                       "Macofe"
+                       "Macofe",
+                       "SuperPotato"
                ]
        },
        "config-desc": "Installasjonsprogrammet for MediaWiki",
@@ -15,7 +16,7 @@
        "config-localsettings-upgrade": "En <code>LocalSettings.php</code>-fil har blitt oppdaget.\nFor å oppgradere denne installasjonen, skriv inn verdien av <code>$wgUpgradeKey</code> i boksen nedenfor.\nDu finner denne i <code>LocalSettings.php</code>.",
        "config-localsettings-cli-upgrade": "Filen ''<code>LocalSettings.php</code>'' er funnet.\nFor å oppgradere denne installasjonen, vennligst kjør ''update.php'' i stedet",
        "config-localsettings-key": "Oppgraderingsnøkkel:",
-       "config-localsettings-badkey": "Nøkkelen du oppga er feil.",
+       "config-localsettings-badkey": "Oppgraderingsnøkkelen du oppga er feil.",
        "config-upgrade-key-missing": "En eksisterende installasjon av MediaWiki er funnet.\nFor å oppgradere denne installasjonen, vær vennlig å legge til følgende linje helt til slutt i din ''<code>LocalSettings.php</code>''-fil:\n\n$1",
        "config-localsettings-incomplete": "Den eksisterende ''<code>LocalSettings.php</code>'' ser ut til å være ufullstendig.\nVariabelen $1 har ingen verdi.\nVær vennlig å endre ''<code>LocalSettings.php</code>'' slik at variabelen får en verdi, og klikk ''{{int:Config-continue}}''.",
        "config-localsettings-connection-error": "Det ble funnet en feil ved tilknytning av databasen med innstillingene i ''<code>LocalSettings.php</code>'' eller ''<code>AdminSettings.php</code>''. Vær vennlig å rette opp disse innstillingene og prøv igjen.\n\n$1",
        "config-no-db": "Fant ingen passende databasedriver! Du må installere en databasedriver for PHP.\nFølgende {{PLURAL:$2|databasetype|databasetyper}} støttes: $1\n\nOm du kompilerte PHP selv, rekonfigurer den med en aktivert databaseklient, for eksempel ved å bruke <code>./configure --with-mysql</code>.\nOm du installerte PHP fra en Debian- eller Ubuntu-pakke, må du også installere for eksempel <code>php5-mysql</code>-pakken.",
        "config-outdated-sqlite": "'''Advarsel''': Du har SQLite $1, som er en eldre versjon enn minimumskravet SQLite $2. SQLite vil ikke være tilgjengelig.",
        "config-no-fts3": "'''Advarsel''': SQLite er kompilert uten [//sqlite.org/fts3.html FTS3-modulen], søkefunksjoner vil ikke være tilgjengelig på dette bakstykket.",
-       "config-register-globals-error": "<strong>Feil: PHPs <code>[http://php.net/register_globals register_globals]</code>-valg er aktivt.\nDet må deaktiveres for å kunne fortsette med installeringen.</strong>\nSe [https://www.mediawiki.org/wiki/register_globals https://www.mediawiki.org/wiki/register_globals] for å få hjelp til å gjøre dette.",
-       "config-magic-quotes-gpc": "<strong>Fatalt: [http://www.php.net/manual/en/ref.info.php#ini.magic-quotes-gpc magic_quotes_gpc] er aktiv!</strong>\nDette valget kan ødelegge inndata på en uforutsigelig måte.\nDu kan ikke installere eller bruke MediaWiki uten at denne valgmuligheten er slått av.",
-       "config-magic-quotes-runtime": "'''Kritisk: [http://www.php.net/manual/en/ref.info.php#ini.magic-quotes-runtime magic_quotes_runtime] er aktiv!'''\nDette alternativet ødelegger inndata på en uforutsigbar måte.\nDu kan ikke installere eller bruke MediaWiki med mindre dette alternativet deaktiveres.",
-       "config-magic-quotes-sybase": "'''Kritisk: [http://www.php.net/manual/en/ref.info.php#ini.magic-quotes-sybase magic_quotes_sybase] er aktiv!'''\nDette alternativet ødelegger inndata på en uforutsigbar måte.\nDu kan ikke installere eller bruke MediaWiki med mindre dette alternativet deaktiveres.",
        "config-mbstring": "'''Kritisk: [http://www.php.net/manual/en/ref.mbstring.php#mbstring.overload mbstring.func_overload] er aktiv!'''\nDette alternativet fører til feil og kan ødelegge data på en uforutsigbar måte.\nDu kan ikke installere eller bruke MediaWiki med mindre dette alternativet deaktiveres.",
-       "config-safe-mode": "'''Advarsel:''' PHPs [http://www.php.net/features.safe-mode safe mode] er aktiv.\nDet kan føre til problem, spesielt hvis du bruker støtte for filopplastinger og <code>math</code>.",
-       "config-xml-bad": "PHPs XML-modul mangler.\nMediaWiki krever funksjonene i denne modulen og vil ikke virke i denne konfigurasjonen.\nHvis du kjører Mandrak, installer pakken php-xml.",
+       "config-xml-bad": "PHPs XML-modul mangler.\nMediaWiki krever funksjonene i denne modulen og vil ikke virke i denne konfigurasjonen.\nDu må kanskje laste ned php-xml RPM pakken.",
        "config-pcre-old": "'''Alvorlig:''' PCRE $1 eller senere kreves.\nDin PHP-kode er lenket med PCRE $2.\n[https://www.mediawiki.org/wiki/Manual:Errors_and_symptoms/PCRE Nærmere informasjon].",
        "config-pcre-no-utf8": "'''Fatal''': PHPs PCRE modul ser ut til å være kompilert uten PCRE_UTF8-støtte.\nMediaWiki krever UTF-8-støtte for å fungere riktig.",
        "config-memory-raised": "PHPs <code>memory_limit</code> er $1, økt til $2.",
@@ -76,6 +72,7 @@
        "config-apc": "[http://www.php.net/apc APC] er innstallert",
        "config-wincache": "[http://www.iis.net/download/WinCacheForPhp WinCache] er installert",
        "config-no-cache": "'''Advarsel:''' Kunne ikke finne [http://www.php.net/apc APC], [http://xcache.lighttpd.net/ XCache] eller [http://www.iis.net/download/WinCacheForPhp WinCache].\nObjekthurtiglagring er ikke aktivert.",
+       "config-no-cache-apcu": "<strong>Advarsel:</strong> Kunne ikke finne [http://www.php.net/apc APC], [http://xcache.lighttpd.net/ XCache] eller [http://www.iis.net/download/WinCacheForPhp WinCache].\nObjekthurtiglagring er ikke aktivert.",
        "config-mod-security": "'''Advarsel''': Din web-tjener har [http://modsecurity.org/ mod_security] påslått. Hvis denne er feilinnstilt, kan det gi problemer for MediaWiki eller annen programvare som tillater brukere å poste vilkårlig innhold.\nSjekk [http://modsecurity.org/documentation/ mod_security-dokumentasjonen] eller ta kontakt med din nettleverandør hvis du opplever tilfeldige feil.",
        "config-diff3-bad": "GNU diff3 ikke funnet.",
        "config-git": "Har funnet Git version control software: <code>$1</code>.",
        "config-nofile": "Filen \"$1\" ble ikke funnet. Kan den være blitt slettet?",
        "config-extension-link": "Visste du at wikien din kan brukes sammen med en mengde [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions utvidelser]?\n\nDu kan sjekke gjennom [//www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category utvidelser per kategori] eller [//www.mediawiki.org/wiki/Extension_Matrix utvidelsesmatrisen] for å se den komplette listen av utvidelser.",
        "mainpagetext": "'''MediaWiki-programvaren er nå installert.'''",
-       "mainpagedocfooter": "Sjekk [//meta.wikimedia.org/wiki/Help:Contents brukerveiledningen] for å få informasjon om hvordan du bruker wiki-programvaren.\n\n==Hvordan komme igang==\n*[//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Innstillingsliste]\n*[//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Ofte stilte spørsmål om MediaWiki]\n*[https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce MediaWiki e-postliste]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Tilpass MediaWiki for ditt språk]"
+       "mainpagedocfooter": "Sjekk [//meta.wikimedia.org/wiki/Help:Contents brukerveiledningen] for å få informasjon om hvordan du bruker wiki-programvaren.\n\n==Hvordan komme igang==\n*[//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Innstillingsliste]\n*[//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Ofte stilte spørsmål om MediaWiki]\n*[https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce MediaWiki e-postliste]\n*[//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Tilpass MediaWiki for ditt språk]\n*[//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Lær deg å beskytte deg mot spam på wikien din]"
 }
index b3999f6..cd75013 100644 (file)
@@ -29,7 +29,7 @@
        "config-localsettings-upgrade": "Plik <code>LocalSettings.php</code> istnieje.\nAby oprogramowanie zostało zaktualizowane musisz wstawić wartość <code>$wgUpgradeKey</code> w poniższe pole.\nOdnajdziesz ją w <code>LocalSettings.php</code>.",
        "config-localsettings-cli-upgrade": "Wykryto obecność pliku <code>LocalSettings.php</code>.\nAktualizację należy wykonać poprzez uruchomienie <code>update.php</code>",
        "config-localsettings-key": "Klucz aktualizacji:",
-       "config-localsettings-badkey": "Podany klucz jest nieprawidłowy",
+       "config-localsettings-badkey": "Podany klucz aktualizacji jest nieprawidłowy.",
        "config-upgrade-key-missing": "Wykryto zainstalowane wcześniej MediaWiki.\nJeśli chcesz je zaktualizować dodaj na koniec pliku <code>LocalSettings.php</code> poniższą linię tekstu.\n\n$1",
        "config-localsettings-incomplete": "Istniejący plik <code>LocalSettings.php</code> wygląda na niekompletny.\nBrak wartości zmiennej $1.\nZmień plik <code>LocalSettings.php</code>, tak by zawierał deklarację wartości tej zmiennej, a następnie kliknij „{{int:Config-continue}}”.",
        "config-localsettings-connection-error": "Wystąpił błąd podczas łączenia z bazą danych używając ustawień podanych w <code>LocalSettings.php</code>\nNapraw te ustawienia i spróbuj ponownie.\n\n$1",
        "config-no-db": "Nie można odnaleźć właściwego sterownika bazy danych! Musisz zainstalować sterownik bazy danych dla PHP.\nMożna użyć {{PLURAL:$2|następującego typu bazy|następujących typów baz} danych: $1.\n\nJeśli skompilowałeś PHP samodzielnie, skonfiguruj je ponownie z włączonym klientem bazy danych, na przykład za pomocą polecenia <code>./configure --with-mysqli</code>.\nJeśli zainstalowałeś PHP jako pakiet Debiana lub Ubuntu, musisz również zainstalować np. moduł <code>php5-mysql</code>.",
        "config-outdated-sqlite": "'''Ostrzeżenie''': masz SQLite  $1, która jest niższa od minimalnej wymaganej wersji  $2 . SQLite będzie niedostępne.",
        "config-no-fts3": "'''Uwaga''' – SQLite został skompilowany bez [//sqlite.org/fts3.html modułu FTS3] – funkcje wyszukiwania nie będą dostępne.",
-       "config-register-globals-error": "<strong>Błąd: dyrektywa PHP <code>[http://php.net/register_globals register_globals]</code> jest włączona.\nAby kontynuować instalację musi zostać wyłączona.</strong>\nPrzeczytaj [https://www.mediawiki.org/wiki/register_globals https://www.mediawiki.org/wiki/register_globals], aby dowiedzieć się, jak to zrobić.",
-       "config-magic-quotes-gpc": "<strong>Błąd krytyczny – dyrektywa [http://www.php.net/manual/en/ref.info.php#ini.magic-quotes-sybase magic_quotes_sybase] jest włączona!</strong>\nTa opcja powoduje nieprzewidywalne uszkodzenia wprowadzanych danych.\nNie możesz instalować lub korzystać z MediaWiki, dopóki ta opcja nie zostanie wyłączona.",
-       "config-magic-quotes-runtime": "'''Błąd krytyczny – włączono [http://www.php.net/manual/en/ref.info.php#ini.magic-quotes-runtime magic_quotes_runtime]!'''\nTa opcja powoduje nieprzewidywalne uszkodzenia wprowadzanych danych.\nZainstalować lub korzystać z MediaWiki można pod warunkiem, że ta opcja jest wyłączona.",
-       "config-magic-quotes-sybase": "'''Błąd krytyczny – włączono [http://www.php.net/manual/en/ref.info.php#ini.magic-quotes-sybase magic_quotes_sybase]!'''\nTa opcja powoduje nieprzewidywalne uszkodzenia wprowadzanych danych.\nZainstalować lub korzystać z MediaWiki można pod warunkiem, że ta opcja jest wyłączona.",
        "config-mbstring": "'''Błąd krytyczny – włączono [http://www.php.net/manual/en/ref.mbstring.php#mbstring.overload mbstring.func_overload]!'''\nTa opcja powoduje błędy i może wywołać nieprzewidywalne uszkodzenia wprowadzanych danych.\nZainstalować lub korzystać z MediaWiki można pod warunkiem, że ta opcja jest wyłączona.",
-       "config-safe-mode": "'''Ostrzeżenie''' – uaktywniono [http://www.php.net/features.safe-mode tryb awaryjny] PHP.\nOpcja ta może powodować problemy, szczególnie w przypadku korzystania z przesyłania plików i używania znacznika <code>math</code>.",
        "config-xml-bad": "Brak modułu XML dla PHP.\nMediaWiki wymaga funkcji z tego modułu i nie może działać w tej konfiguracji.\nZainstaluj pakiet RPM php-xml.",
        "config-pcre-old": "<strong>Błąd krytyczny:</strong> Wymagany jest PCRE w wersji $1 lub nowszej.\nTwój plik wykonywalny PHP jest powiązany z wersją PCRE $2.\n[https://www.mediawiki.org/wiki/Manual:Errors_and_symptoms/PCRE Więcej informacji].",
        "config-pcre-no-utf8": "'''Błąd krytyczny''' – wydaje się, że moduł PCRE w PHP został skompilowany bez wsparcia dla UTF‐8.\nMediaWiki wymaga wsparcia dla UTF‐8 do prawidłowego działania.",
        "config-instantcommons-help": "[//www.mediawiki.org/wiki/InstantCommons Instant Commons] jest funkcją, która pozwala wiki używać obrazów, dźwięków i innych mediów znalezionych na  witrynie [//commons.wikimedia.org/ Wikimedia Commons].\nAby to zrobić, MediaWiki wymaga dostępu do internetu.\n\nAby uzyskać więcej informacji na temat tej funkcji, w tym instrukcje dotyczące sposobu ustawiania go na wiki innych niż Wikimedia Commons, sprawdź w [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgForeignFileRepos podręczniku].",
        "config-cc-error": "Wybieranie licencji Creative Commons nie dało wyniku.\nWpisz nazwę licencji ręcznie.",
        "config-cc-again": "Wybierz jeszcze raz...",
-       "config-cc-not-chosen": "Wybierz którą chcesz licencję Creative Commons i kliknij „proceed”.",
+       "config-cc-not-chosen": "Wybierz, którą chcesz licencję Creative Commons i kliknij „proceed”.",
        "config-advanced-settings": "Konfiguracja zaawansowana",
        "config-cache-options": "Ustawienia buforowania obiektów:",
        "config-cache-help": "Buforowanie obiekto jest używane aby przyspieszyć MediaWiki przez trzymanie w pamięci podręcznej często używanych danych.\nŚrednie oraz duże witryny są wysoce zachęcane by je włączyć, a małe witryny także dostrzegą korzyści.",
index 7bcb58f..4a6526c 100644 (file)
@@ -8,7 +8,7 @@
        "config-title": "مېډياويکي $1 نصبېدنه",
        "config-information": "مالومات",
        "config-localsettings-key": "کونجۍ نومهالول:",
-       "config-localsettings-badkey": "کومه کونجۍ مو چې ورکړه ناسمه ده.",
+       "config-localsettings-badkey": "کومه اوسمهاله شوې کونجۍ مو چې ورکړې، ناسمه ده.",
        "config-your-language": "ستاسې ژبه:",
        "config-wiki-language": "د ويکي ژبه:",
        "config-back": "← پر شا تلل",
index 372b228..e20f230 100644 (file)
        "config-no-db": "{{doc-important|Do not translate \"<code>./configure --with-mysqli</code>\" and \"<code>php5-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* $1 - the version of SQLite that has been installed\n* $2 - 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-register-globals-error": "Error message in the MediaWiki installer environment checks.",
-       "config-magic-quotes-gpc": "{{Related|Config-fatal}}",
-       "config-magic-quotes-runtime": "{{Related|Config-fatal}}",
-       "config-magic-quotes-sybase": "{{Related|Config-fatal}}",
        "config-mbstring": "{{Related|Config-fatal}}",
-       "config-safe-mode": "Status message in the MediaWiki installer environment checks.",
        "config-xml-bad": "Status message in the MediaWiki installer environment checks.",
        "config-pcre-old": "Parameters:\n* $1 - minimum PCRE version number\n* $2 - the installed version of [[wikipedia:PCRE|PCRE]]\n{{Related|Config-fatal}}",
        "config-pcre-no-utf8": "PCRE is a name of a programmers' library for supporting regular expressions. It can probably be translated without change.\n{{Related|Config-fatal}}",
index da8a16e..0e6ecb9 100644 (file)
@@ -2,9 +2,11 @@
        "@metadata": {
                "authors": [
                        "OC Ripper",
-                       "Seb35"
+                       "Seb35",
+                       "Conquistador"
                ]
        },
-       "mainpagetext": "'''MediaWiki softver is uspješno instaliran.'''",
-       "mainpagedocfooter": "Kontaktirajte [//meta.wikimedia.org/wiki/Help:Contents uputstva za korisnike] za informacije o upotrebi wiki programa.\n\n== Početak ==\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Lista postavki]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki najčešće postavljana pitanja]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Lista E-Mail adresa MediaWiki]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Localise MediaWiki for your language]"
+       "config-admin-password": "Lozinka:",
+       "mainpagetext": "<strong>MediaWiki je uspješno instaliran.</strong>",
+       "mainpagedocfooter": "Za informacije o korištenju wiki softvera konzultirajte [//meta.wikimedia.org/wiki/Help:Contents Vodič za korisnike].\n\n== Uvod u rad ==\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Lista konfiguracije postavki]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki FAQ]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Lista primatelja izdanja MediaWikija]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Lokalizirajte MediaWiki za svoj jezik]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Saznajte kako se boriti protiv spama na svojem wikiju]"
 }
index 0f62503..aba3d34 100644 (file)
@@ -9,10 +9,66 @@
        "config-desc": "MediaWiki йөкләүче",
        "config-title": "MediaWiki $1 куелышы",
        "config-information": "Мәгълүмат",
+       "config-localsettings-key": "Яңарту ачкычы:",
+       "config-your-language": "Телегез:",
+       "config-wiki-language": "Вики теле:",
        "config-back": "← Артка",
        "config-continue": "Киләсе →",
        "config-page-language": "Тел",
        "config-page-welcome": "MediaWiki проектына рәхим итегез!",
+       "config-page-name": "Исем",
+       "config-page-options": "Көйләнмәләр",
+       "config-page-install": "Урнаштыру",
+       "config-page-complete": "Тәмам!",
+       "config-page-restart": "Урнаштыруны яңадан башлау",
+       "config-page-readme": "Укып чык",
+       "config-page-releasenotes": "Юрама турында мәгълүмат",
+       "config-page-copying": "Лицензия",
+       "config-page-upgradedoc": "Яңарту",
+       "config-page-existingwiki": "Хәзерге вики",
+       "config-restart": "Әйе, яңадан башларга",
+       "config-xcache": "[http://xcache.lighttpd.net/ XCache] куелды",
+       "config-apc": "[http://www.php.net/apc APC] куелды",
+       "config-wincache": "[http://www.iis.net/download/WinCacheForPhp WinCache] куелды",
+       "config-diff3-bad": "GNU diff3 табылмады.",
+       "config-git": "Git юрамалар идарә итү системасы табылды: <code>$1</code>.",
+       "config-db-type": "Мәгълүмат базасы төре:",
+       "config-db-host": "Мәгълүмат базасы хосты:",
+       "config-db-host-oracle": "TNS мәгълүмат базасы:",
+       "config-db-name-oracle": "Мәгълүмат базасы төзелеше:",
+       "config-db-username": "Мәгълүмат базасын кулланучы исеме:",
+       "config-db-password": "Мәгълүмат базасының серсүзе:",
+       "config-charset-mysql5-binary": "MySQL 4.1/5.0 бинарлы",
+       "config-charset-mysql5": "MySQL 4.1/5.0 UTF-8",
+       "config-charset-mysql4": "MySQL 4.0 һәм UTF-8 үзара бәйләнешле",
+       "config-db-port": "Мәгълүмат базасы порты:",
+       "config-db-schema": "MediaWiki өчен төзелеш:",
+       "config-mysql-innodb": "InnoDB",
+       "config-mysql-myisam": "MyISAM",
+       "config-mysql-binary": "Икеле",
+       "config-mysql-utf8": "UTF-8",
+       "config-ns-generic": "Проект",
+       "config-ns-other": "Башка (күрсәтегез)",
+       "config-ns-other-default": "MyWiki",
+       "config-admin-password": "Серсүз:",
+       "config-admin-password-confirm": "Серсүзне кабатлагыз:",
+       "config-admin-email": "Электрон почта адресы:",
+       "config-profile-wiki": "Ачык вики",
+       "config-profile-private": "Ябык вики",
+       "config-license": "Автор хокуклары һәм лицензияләр:",
+       "config-license-cc-by-sa": "Creative Commons Attribution Share Alike",
+       "config-license-cc-by": "Creative Commons Attribution",
+       "config-license-cc-by-nc-sa": "Creative Commons Attribution Non-Commercial Share Alike",
+       "config-license-cc-0": "Creative Commons Zero (җәмгыять мирасы)",
+       "config-license-gfdl": "GNU Free Documentation License 1.3 яки яңарагы",
+       "config-license-pd": "Җәмгыять мирасы",
+       "config-logo": "Логотип URL:",
+       "config-cc-again": "Кабат сайлагыз...",
+       "config-extensions": "Киңәйтүләр",
+       "config-skins": "Бизәлеш",
+       "config-install-step-done": "әзер",
+       "config-install-step-failed": "булмады",
+       "config-help": "ярдәм",
        "mainpagetext": "<strong>«MediaWiki» уңышлы куелды.</strong>",
        "mainpagedocfooter": "Бу вики турында мәгълүматны [//meta.wikimedia.org/wiki/Help:Contents биредә] табып була.\n\n== Кайбер файдалы ресурслар ==\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Көйләнмәләр исемлеге (инг.)];\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki турында еш бирелгән сораулар һәм җаваплар (инг.)];\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce MediaWiki'ның яңа версияләре турында хәбәрләр яздырып алу];\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Localise MediaWiki for your language].\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Learn how to combat spam on your wiki]"
 }
index 71c6c17..bbecf18 100644 (file)
        "config-page-copying": "Nagkokopya",
        "config-restart": "Oo, utroha patikanga",
        "config-welcome": "=== Mga pagpanginano panlibong ===\nMagkakamay-ada yano nga panginano para masabtan kun ini nga libong in naaangay para hiton pagtataod hiton MediaWiki. Hinumdomi iton paglakip hinin nga impormasyon kun karuyag mo mangaro hin suporta kun paunan-on humanon an pagtataod.",
+       "config-env-php": "Gin-install an PHP $1.",
+       "config-env-hhvm": "Gin-install an HHVM $1.",
+       "config-unicode-using-intl": "Gamita an [http://pecl.php.net/intl intl PECL extension] para han normalisasyon han Unicode.",
+       "config-unicode-pure-php-warning": "<strong>Pahimatngon:</strong> An [http://pecl.php.net/intl intl PECL extension] in waray akos kumapot hin Unicode normalization, tungod hini mabalik ha mahinay nga puro-PHP nga implementasyon.\nKun nagpapadalagan ka hin high-traffic site, alayon pagbasa hin guti han [//www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations Unicode normalization].",
        "config-no-db": "Diri nakakabiling hin naaangay nga database driver! Kinahanglan mo magtaod hin uska database driver para han PHP. An masunod nga mga klase hin database in ginsusuporatahan: $1.\n\nKun ikaw mismo an nag-compile han PHP, kinahanglan ma-reconfigure iton nga para maapandar an database client, pananglitan, han paggamit han <code>./configure --with-mysqli</code>.\nKun gintaod mo an PHP tikang ha uska Debian o Ubuntu nga pakete, kinahanglan nimo magtaod liwat, pananglitan, hiton an <code>php5-mysql</code> nga pakete.",
        "config-pcre-old": "<strong>Nangangarat-an:</strong> Nagkikinahanglan hin PCRE $1 o mas urhi pa.\nAn imo PHP nga binaryo in nakasumpay hin PCRE $2. [https://www.mediawiki.org/wiki/Manual:Errors_and_symptoms/PCRE More information].",
        "config-db-name": "Ngaran han database:",
index be16bcf..d50e381 100644 (file)
@@ -281,18 +281,16 @@ class Interwiki {
         * @since 1.19
         */
        protected static function getAllPrefixesCached( $local ) {
-               global $wgInterwikiCache, $wgInterwikiScopes, $wgInterwikiFallbackSite;
-               static $db, $site;
+               global $wgInterwikiScopes, $wgInterwikiFallbackSite;
+               static $site;
 
                wfDebug( __METHOD__ . "()\n" );
                $data = array();
                try {
-                       if ( !$db ) {
-                               $db = CdbReader::open( $wgInterwikiCache );
-                       }
                        /* Resolve site name */
                        if ( $wgInterwikiScopes >= 3 && !$site ) {
-                               $site = $db->get( '__sites:' . wfWikiID() );
+                               $site = self::getCacheValue( '__sites:' . wfWikiID() );
+
                                if ( $site == '' ) {
                                        $site = $wgInterwikiFallbackSite;
                                }
@@ -311,9 +309,9 @@ class Interwiki {
                        $sources[] = wfWikiID();
 
                        foreach ( $sources as $source ) {
-                               $list = $db->get( "__list:{$source}" );
+                               $list = self::getCacheValue( '__list:' . $source );
                                foreach ( explode( ' ', $list ) as $iw_prefix ) {
-                                       $row = $db->get( "{$source}:{$iw_prefix}" );
+                                       $row = self::getCacheValue( "{$source}:{$iw_prefix}" );
                                        if ( !$row ) {
                                                continue;
                                        }
index f10866e..51dec65 100644 (file)
@@ -181,11 +181,10 @@ class JobQueueDB extends JobQueue {
        protected function doBatchPush( array $jobs, $flags ) {
                $dbw = $this->getMasterDB();
 
-               $that = $this;
                $method = __METHOD__;
                $dbw->onTransactionIdle(
-                       function () use ( $dbw, $that, $jobs, $flags, $method ) {
-                               $that->doBatchPushInternal( $dbw, $jobs, $flags, $method );
+                       function () use ( $dbw, $jobs, $flags, $method ) {
+                               $this->doBatchPushInternal( $dbw, $jobs, $flags, $method );
                        }
                );
        }
index 7dad748..7e9c0c9 100644 (file)
@@ -175,11 +175,10 @@ class JobQueueMemory extends JobQueue {
                        return new ArrayIterator( array() );
                }
 
-               $that = $this;
                return new MappedIterator(
                        $unclaimed,
-                       function ( $value ) use ( $that ) {
-                               $that->jobFromSpecInternal( $value );
+                       function ( $value ) {
+                               $this->jobFromSpecInternal( $value );
                        }
                );
        }
@@ -195,11 +194,10 @@ class JobQueueMemory extends JobQueue {
                        return new ArrayIterator( array() );
                }
 
-               $that = $this;
                return new MappedIterator(
                        $claimed,
-                       function ( $value ) use ( $that ) {
-                               $that->jobFromSpecInternal( $value );
+                       function ( $value ) {
+                               $this->jobFromSpecInternal( $value );
                        }
                );
        }
index eda3e9c..408828d 100644 (file)
@@ -573,12 +573,10 @@ LUA;
         * @return MappedIterator
         */
        protected function getJobIterator( RedisConnRef $conn, array $uids ) {
-               $that = $this;
-
                return new MappedIterator(
                        $uids,
-                       function ( $uid ) use ( $that, $conn ) {
-                               return $that->getJobFromUidInternal( $uid, $conn );
+                       function ( $uid ) use ( $conn ) {
+                               return $this->getJobFromUidInternal( $uid, $conn );
                        },
                        array( 'accept' => function ( $job ) {
                                return is_object( $job );
index 59166e8..5f8af8b 100644 (file)
@@ -79,7 +79,8 @@ class PublishStashedFileJob extends Job {
                                $this->params['comment'],
                                $this->params['text'],
                                $this->params['watch'],
-                               $user
+                               $user,
+                               isset( $this->params['tags'] ) ? $this->params['tags'] : array()
                        );
                        if ( !$status->isGood() ) {
                                UploadBase::setSessionStatus(
index f85ee92..285f1ec 100644 (file)
@@ -129,11 +129,46 @@ class FormatJson {
                        $pretty = $pretty ? '    ' : false;
                }
 
-               if ( defined( 'JSON_UNESCAPED_UNICODE' ) ) {
-                       return self::encode54( $value, $pretty, $escaping );
+               static $bug66021;
+               if ( $pretty !== false && $bug66021 === null ) {
+                       $bug66021 = json_encode( array(), JSON_PRETTY_PRINT ) !== '[]';
+               }
+
+               // PHP escapes '/' to prevent breaking out of inline script blocks using '</script>',
+               // which is hardly useful when '<' and '>' are escaped (and inadequate), and such
+               // escaping negatively impacts the human readability of URLs and similar strings.
+               $options = JSON_UNESCAPED_SLASHES;
+               $options |= $pretty !== false ? JSON_PRETTY_PRINT : 0;
+               $options |= ( $escaping & self::UTF8_OK ) ? JSON_UNESCAPED_UNICODE : 0;
+               $options |= ( $escaping & self::XMLMETA_OK ) ? 0 : ( JSON_HEX_TAG | JSON_HEX_AMP );
+               $json = json_encode( $value, $options );
+               if ( $json === false ) {
+                       return false;
+               }
+
+               if ( $pretty !== false ) {
+                       // Workaround for <https://bugs.php.net/bug.php?id=66021>
+                       if ( $bug66021 ) {
+                               $json = preg_replace( self::WS_CLEANUP_REGEX, '', $json );
+                       }
+                       if ( $pretty !== '    ' ) {
+                               // Change the four-space indent to a tab indent
+                               $json = str_replace( "\n    ", "\n\t", $json );
+                               while ( strpos( $json, "\t    " ) !== false ) {
+                                       $json = str_replace( "\t    ", "\t\t", $json );
+                               }
+
+                               if ( $pretty !== "\t" ) {
+                                       // Change the tab indent to the provided indent
+                                       $json = str_replace( "\t", $pretty, $json );
+                               }
+                       }
+               }
+               if ( $escaping & self::UTF8_OK ) {
+                       $json = str_replace( self::$badChars, self::$badCharsEscaped, $json );
                }
 
-               return self::encode53( $value, $pretty, $escaping );
+               return $json;
        }
 
        /**
@@ -226,141 +261,6 @@ class FormatJson {
                return Status::newFatal( $msg );
        }
 
-       /**
-        * JSON encoder wrapper for PHP >= 5.4, which supports useful encoding options.
-        *
-        * @param mixed $value
-        * @param string|bool $pretty
-        * @param int $escaping
-        * @return string|false
-        */
-       private static function encode54( $value, $pretty, $escaping ) {
-               static $bug66021;
-               if ( $pretty !== false && $bug66021 === null ) {
-                       $bug66021 = json_encode( array(), JSON_PRETTY_PRINT ) !== '[]';
-               }
-
-               // PHP escapes '/' to prevent breaking out of inline script blocks using '</script>',
-               // which is hardly useful when '<' and '>' are escaped (and inadequate), and such
-               // escaping negatively impacts the human readability of URLs and similar strings.
-               $options = JSON_UNESCAPED_SLASHES;
-               $options |= $pretty !== false ? JSON_PRETTY_PRINT : 0;
-               $options |= ( $escaping & self::UTF8_OK ) ? JSON_UNESCAPED_UNICODE : 0;
-               $options |= ( $escaping & self::XMLMETA_OK ) ? 0 : ( JSON_HEX_TAG | JSON_HEX_AMP );
-               $json = json_encode( $value, $options );
-               if ( $json === false ) {
-                       return false;
-               }
-
-               if ( $pretty !== false ) {
-                       // Workaround for <https://bugs.php.net/bug.php?id=66021>
-                       if ( $bug66021 ) {
-                               $json = preg_replace( self::WS_CLEANUP_REGEX, '', $json );
-                       }
-                       if ( $pretty !== '    ' ) {
-                               // Change the four-space indent to a tab indent
-                               $json = str_replace( "\n    ", "\n\t", $json );
-                               while ( strpos( $json, "\t    " ) !== false ) {
-                                       $json = str_replace( "\t    ", "\t\t", $json );
-                               }
-
-                               if ( $pretty !== "\t" ) {
-                                       // Change the tab indent to the provided indent
-                                       $json = str_replace( "\t", $pretty, $json );
-                               }
-                       }
-               }
-               if ( $escaping & self::UTF8_OK ) {
-                       $json = str_replace( self::$badChars, self::$badCharsEscaped, $json );
-               }
-
-               return $json;
-       }
-
-       /**
-        * JSON encoder wrapper for PHP 5.3, which lacks native support for some encoding options.
-        * Therefore, the missing options are implemented here purely in PHP code.
-        *
-        * @param mixed $value
-        * @param string|bool $pretty
-        * @param int $escaping
-        * @return string|false
-        */
-       private static function encode53( $value, $pretty, $escaping ) {
-               $options = ( $escaping & self::XMLMETA_OK ) ? 0 : ( JSON_HEX_TAG | JSON_HEX_AMP );
-               $json = json_encode( $value, $options );
-               if ( $json === false ) {
-                       return false;
-               }
-
-               // Emulate JSON_UNESCAPED_SLASHES. Because the JSON contains no unescaped slashes
-               // (only escaped slashes), a simple string replacement works fine.
-               $json = str_replace( '\/', '/', $json );
-
-               if ( $escaping & self::UTF8_OK ) {
-                       // JSON hex escape sequences follow the format \uDDDD, where DDDD is four hex digits
-                       // indicating the equivalent UTF-16 code unit's value. To most efficiently unescape
-                       // them, we exploit the JSON extension's built-in decoder.
-                       // * We escape the input a second time, so any such sequence becomes \\uDDDD.
-                       // * To avoid interpreting escape sequences that were in the original input,
-                       //   each double-escaped backslash (\\\\) is replaced with \\\u005c.
-                       // * We strip one of the backslashes from each of the escape sequences to unescape.
-                       // * Then the JSON decoder can perform the actual unescaping.
-                       $json = str_replace( "\\\\\\\\", "\\\\\\u005c", addcslashes( $json, '\"' ) );
-                       $json = json_decode( preg_replace( "/\\\\\\\\u(?!00[0-7])/", "\\\\u", "\"$json\"" ) );
-                       $json = str_replace( self::$badChars, self::$badCharsEscaped, $json );
-               }
-
-               if ( $pretty !== false ) {
-                       return self::prettyPrint( $json, $pretty );
-               }
-
-               return $json;
-       }
-
-       /**
-        * Adds non-significant whitespace to an existing JSON representation of an object.
-        * Only needed for PHP < 5.4, which lacks the JSON_PRETTY_PRINT option.
-        *
-        * @param string $json
-        * @param string $indentString
-        * @return string
-        */
-       private static function prettyPrint( $json, $indentString ) {
-               $buf = '';
-               $indent = 0;
-               $json = strtr( $json, array( '\\\\' => '\\\\', '\"' => "\x01" ) );
-               for ( $i = 0, $n = strlen( $json ); $i < $n; $i += $skip ) {
-                       $skip = 1;
-                       switch ( $json[$i] ) {
-                               case ':':
-                                       $buf .= ': ';
-                                       break;
-                               case '[':
-                               case '{':
-                                       ++$indent;
-                                       // falls through
-                               case ',':
-                                       $buf .= $json[$i] . "\n" . str_repeat( $indentString, $indent );
-                                       break;
-                               case ']':
-                               case '}':
-                                       $buf .= "\n" . str_repeat( $indentString, --$indent ) . $json[$i];
-                                       break;
-                               case '"':
-                                       $skip = strcspn( $json, '"', $i + 1 ) + 2;
-                                       $buf .= substr( $json, $i, $skip );
-                                       break;
-                               default:
-                                       $skip = strcspn( $json, ',]}"', $i + 1 ) + 1;
-                                       $buf .= substr( $json, $i, $skip );
-                       }
-               }
-               $buf = preg_replace( self::WS_CLEANUP_REGEX, '', $buf );
-
-               return str_replace( "\x01", '\"', $buf );
-       }
-
        /**
         * Remove multiline and single line comments from an otherwise valid JSON
         * input string. This can be used as a preprocessor for to allow JSON
index 6ca0fed..246de75 100644 (file)
@@ -451,15 +451,19 @@ class CSSMin {
                        // Path to the actual file on the filesystem
                        $localFile = "{$local}/{$file}";
                        if ( file_exists( $localFile ) ) {
-                               // Add version parameter as the first five hex digits
-                               // of the MD5 hash of the file's contents.
-                               $url .= '?' . substr( md5_file( $localFile ), 0, 5 );
                                if ( $embed ) {
                                        $data = self::encodeImageAsDataURI( $localFile );
                                        if ( $data !== false ) {
                                                return $data;
                                        }
                                }
+                               if ( method_exists( 'OutputPage', 'transformFilePath' ) ) {
+                                       $url = OutputPage::transformFilePath( $remote, $local, $file );
+                               } else {
+                                       // Add version parameter as the first five hex digits
+                                       // of the MD5 hash of the file's contents.
+                                       $url .= '?' . substr( md5_file( $localFile ), 0, 5 );
+                               }
                        }
                        // If any of these conditions failed (file missing, we don't want to embed it
                        // or it's not embeddable), return the URL (possibly with ?timestamp part)
index ea50a85..02762f3 100644 (file)
  */
 
 /**
- * Replacement array for FSS with fallback to strtr()
- * Supports lazy initialisation of FSS resource
+ * Wrapper around strtr() that holds replacements
  */
 class ReplacementArray {
        private $data = false;
-       private $fss = false;
 
        /**
         * Create an object with the specified replacement array
         * The array should have the same form as the replacement array for strtr()
         * @param array $data
         */
-       public function __construct( $data = array() ) {
+       public function __construct( $data = [] ) {
                $this->data = $data;
        }
 
@@ -42,17 +40,12 @@ class ReplacementArray {
                return array( 'data' );
        }
 
-       public function __wakeup() {
-               $this->fss = false;
-       }
-
        /**
         * Set the whole replacement array at once
         * @param array $data
         */
        public function setArray( $data ) {
                $this->data = $data;
-               $this->fss = false;
        }
 
        /**
@@ -69,7 +62,6 @@ class ReplacementArray {
         */
        public function setPair( $from, $to ) {
                $this->data[$from] = $to;
-               $this->fss = false;
        }
 
        /**
@@ -77,7 +69,6 @@ class ReplacementArray {
         */
        public function mergeArray( $data ) {
                $this->data = $data + $this->data;
-               $this->fss = false;
        }
 
        /**
@@ -85,7 +76,6 @@ class ReplacementArray {
         */
        public function merge( ReplacementArray $other ) {
                $this->data = $other->data + $this->data;
-               $this->fss = false;
        }
 
        /**
@@ -93,7 +83,6 @@ class ReplacementArray {
         */
        public function removePair( $from ) {
                unset( $this->data[$from] );
-               $this->fss = false;
        }
 
        /**
@@ -103,7 +92,6 @@ class ReplacementArray {
                foreach ( $data as $from => $to ) {
                        $this->removePair( $from );
                }
-               $this->fss = false;
        }
 
        /**
@@ -111,18 +99,6 @@ class ReplacementArray {
         * @return string
         */
        public function replace( $subject ) {
-               if (
-                       function_exists( 'fss_prep_replace' ) &&
-                       version_compare( PHP_VERSION, '5.5.0' ) < 0
-               ) {
-                       if ( $this->fss === false ) {
-                               $this->fss = fss_prep_replace( $this->data );
-                       }
-                       $result = fss_exec_replace( $this->fss, $subject );
-               } else {
-                       $result = strtr( $subject, $this->data );
-               }
-
-               return $result;
+               return strtr( $subject, $this->data );
        }
 }
index b9be43d..a7e8a47 100644 (file)
@@ -69,6 +69,7 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
        const READ_VERIFIED = 2; // promise that caller can tell when keys are stale
        /** Bitfield constants for set()/merge() */
        const WRITE_SYNC = 1; // synchronously write to all locations for replicated stores
+       const WRITE_CACHE_ONLY = 2; // Only change state of the in-memory cache
 
        public function __construct( array $params = array() ) {
                if ( isset( $params['logger'] ) ) {
@@ -396,18 +397,15 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
                }
 
                $lSince = microtime( true ); // lock timestamp
-               // PHP 5.3: Can't use $this in a closure
-               $that = $this;
-               $logger = $this->logger;
 
-               return new ScopedCallback( function() use ( $that, $logger, $key, $lSince, $expiry ) {
+               return new ScopedCallback( function() use ( $key, $lSince, $expiry ) {
                        $latency = .050; // latency skew (err towards keeping lock present)
                        $age = ( microtime( true ) - $lSince + $latency );
                        if ( ( $age + $latency ) >= $expiry ) {
-                               $logger->warning( "Lock for $key held too long ($age sec)." );
+                               $this->logger->warning( "Lock for $key held too long ($age sec)." );
                                return; // expired; it's not "safe" to delete the key
                        }
-                       $that->unlock( $key );
+                       $this->unlock( $key );
                } );
        }
 
diff --git a/includes/libs/objectcache/CachedBagOStuff.php b/includes/libs/objectcache/CachedBagOStuff.php
new file mode 100644 (file)
index 0000000..fc15618
--- /dev/null
@@ -0,0 +1,114 @@
+<?php
+/**
+ * Wrapper around a BagOStuff that caches data in memory
+ *
+ * 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 Cache
+ */
+
+use Psr\Log\LoggerInterface;
+
+/**
+ * Wrapper around a BagOStuff that caches data in memory
+ *
+ * The differences between CachedBagOStuff and MultiWriteBagOStuff are:
+ * * CachedBagOStuff supports only one "backend".
+ * * There's a flag for writes to only go to the in-memory cache.
+ * * The in-memory cache is always updated.
+ * * Locks go to the backend cache (with MultiWriteBagOStuff, it would wind
+ *   up going to the HashBagOStuff used for the in-memory cache).
+ *
+ * @ingroup Cache
+ */
+class CachedBagOStuff extends HashBagOStuff {
+       /** @var BagOStuff */
+       protected $backend;
+
+       /**
+        * @param BagOStuff $backend Permanent backend to use
+        * @param array $params Parameters for HashBagOStuff
+        */
+       function __construct( BagOStuff $backend, $params = array() ) {
+               $this->backend = $backend;
+               parent::__construct( $params );
+       }
+
+       protected function doGet( $key, $flags = 0 ) {
+               $ret = parent::doGet( $key, $flags );
+               if ( $ret === false ) {
+                       $ret = $this->backend->doGet( $key, $flags );
+                       if ( $ret !== false ) {
+                               $this->set( $key, $ret, 0, self::WRITE_CACHE_ONLY );
+                       }
+               }
+               return $ret;
+       }
+
+       public function set( $key, $value, $exptime = 0, $flags = 0 ) {
+               parent::set( $key, $value, $exptime, $flags );
+               if ( !( $flags & self::WRITE_CACHE_ONLY ) ) {
+                       $this->backend->set( $key, $value, $exptime, $flags & ~self::WRITE_CACHE_ONLY );
+               }
+               return true;
+       }
+
+       public function delete( $key, $flags = 0 ) {
+               unset( $this->bag[$key] );
+               if ( !( $flags & self::WRITE_CACHE_ONLY ) ) {
+                       $this->backend->delete( $key );
+               }
+
+               return true;
+       }
+
+       public function setLogger( LoggerInterface $logger ) {
+               parent::setLogger( $logger );
+               $this->backend->setLogger( $logger );
+       }
+
+       public function setDebug( $bool ) {
+               parent::setDebug( $bool );
+               $this->backend->setDebug( $bool );
+       }
+
+       public function lock( $key, $timeout = 6, $expiry = 6, $rclass = '' ) {
+               return $this->backend->lock( $key, $timeout, $expiry, $rclass );
+       }
+
+       public function unlock( $key ) {
+               return $this->backend->unlock( $key );
+       }
+
+       public function deleteObjectsExpiringBefore( $date, $progressCallback = false ) {
+               parent::deleteObjectsExpiringBefore( $date, $progressCallback );
+               return $this->backend->deleteObjectsExpiringBefore( $date, $progressCallback );
+       }
+
+       public function getLastError() {
+               return $this->backend->getLastError();
+       }
+
+       public function clearLastError() {
+               $this->backend->clearLastError();
+       }
+
+       public function modifySimpleRelayEvent( array $event ) {
+               return $this->backend->modifySimpleRelayEvent( $event );
+       }
+
+}
index 8007a53..ae82ca1 100644 (file)
@@ -350,7 +350,7 @@ class MemcachedClient {
                $res = $this->_fgets( $sock );
 
                if ( $this->_debug ) {
-                       $this->_debugprint( sprintf( "MemCache: delete %s (%s)\n", $key, $res ) );
+                       $this->_debugprint( sprintf( "MemCache: delete %s (%s)", $key, $res ) );
                }
 
                if ( $res == "DELETED" || $res == "NOT_FOUND" ) {
@@ -429,7 +429,7 @@ class MemcachedClient {
        public function get( $key, &$casToken = null ) {
 
                if ( $this->_debug ) {
-                       $this->_debugprint( "get($key)\n" );
+                       $this->_debugprint( "get($key)" );
                }
 
                if ( !is_array( $key ) && strval( $key ) === '' ) {
@@ -464,7 +464,7 @@ class MemcachedClient {
 
                if ( $this->_debug ) {
                        foreach ( $val as $k => $v ) {
-                               $this->_debugprint( sprintf( "MemCache: sock %s got %s\n", serialize( $sock ), $k ) );
+                               $this->_debugprint( sprintf( "MemCache: sock %s got %s", serialize( $sock ), $k ) );
                        }
                }
 
@@ -532,7 +532,7 @@ class MemcachedClient {
 
                if ( $this->_debug ) {
                        foreach ( $val as $k => $v ) {
-                               $this->_debugprint( sprintf( "MemCache: got %s\n", $k ) );
+                               $this->_debugprint( sprintf( "MemCache: got %s", $k ) );
                        }
                }
 
@@ -756,7 +756,7 @@ class MemcachedClient {
                        MediaWiki\restoreWarnings();
                }
                if ( !$sock ) {
-                       $this->_error_log( "Error connecting to $host: $errstr\n" );
+                       $this->_error_log( "Error connecting to $host: $errstr" );
                        $this->_dead_host( $host );
                        return false;
                }
@@ -1037,7 +1037,7 @@ class MemcachedClient {
                        $val = serialize( $val );
                        $flags |= self::SERIALIZED;
                        if ( $this->_debug ) {
-                               $this->_debugprint( sprintf( "client: serializing data as it is not scalar\n" ) );
+                               $this->_debugprint( sprintf( "client: serializing data as it is not scalar" ) );
                        }
                }
 
@@ -1051,7 +1051,7 @@ class MemcachedClient {
 
                        if ( $c_len < $len * ( 1 - self::COMPRESSION_SAVINGS ) ) {
                                if ( $this->_debug ) {
-                                       $this->_debugprint( sprintf( "client: compressing data; was %d bytes is now %d bytes\n", $len, $c_len ) );
+                                       $this->_debugprint( sprintf( "client: compressing data; was %d bytes is now %d bytes", $len, $c_len ) );
                                }
                                $val = $c_val;
                                $len = $c_len;
@@ -1071,7 +1071,7 @@ class MemcachedClient {
                $line = $this->_fgets( $sock );
 
                if ( $this->_debug ) {
-                       $this->_debugprint( sprintf( "%s %s (%s)\n", $cmd, $key, $line ) );
+                       $this->_debugprint( sprintf( "%s %s (%s)", $cmd, $key, $line ) );
                }
                if ( $line == "STORED" ) {
                        return true;
@@ -1173,7 +1173,7 @@ class MemcachedClient {
                        }
                }
                $msg = str_replace( '$1', $peer, $msg );
-               $this->_error_log( "$msg\n" );
+               $this->_error_log( "$msg" );
                $this->_dead_sock( $sock );
        }
 
index dcd7a45..e93030f 100644 (file)
@@ -331,7 +331,11 @@ class LogEventsList extends ContextSource {
                $del = $this->getShowHideLinks( $row );
 
                // Any tags...
-               list( $tagDisplay, $newClasses ) = ChangeTags::formatSummaryRow( $row->ts_tags, 'logevent' );
+               list( $tagDisplay, $newClasses ) = ChangeTags::formatSummaryRow(
+                       $row->ts_tags,
+                       'logevent',
+                       $this->getContext()
+               );
                $classes = array_merge(
                        array( 'mw-logline-' . $entry->getType() ),
                        $newClasses
index 8bac6b8..9bda12c 100644 (file)
@@ -74,11 +74,15 @@ class EmailNotification {
        /**
         * @param User $editor The editor that triggered the update.  Their notification
         *  timestamp will not be updated(they have already seen it)
-        * @param Title $title The title to update timestamps for
+        * @param LinkTarget $linkTarget The link target of the title to update timestamps for
         * @param string $timestamp Set the update timestamp to this value
         * @return int[] Array of user IDs
         */
-       public static function updateWatchlistTimestamp( User $editor, Title $title, $timestamp ) {
+       public static function updateWatchlistTimestamp(
+               User $editor,
+               LinkTarget $linkTarget,
+               $timestamp
+       ) {
                global $wgEnotifWatchlist, $wgShowUpdatedMarker;
 
                if ( !$wgEnotifWatchlist && !$wgShowUpdatedMarker ) {
@@ -90,8 +94,8 @@ class EmailNotification {
                        array( 'wl_user' ),
                        array(
                                'wl_user != ' . intval( $editor->getID() ),
-                               'wl_namespace' => $title->getNamespace(),
-                               'wl_title' => $title->getDBkey(),
+                               'wl_namespace' => $linkTarget->getNamespace(),
+                               'wl_title' => $linkTarget->getDBkey(),
                                'wl_notificationtimestamp IS NULL',
                        ), __METHOD__
                );
@@ -105,14 +109,14 @@ class EmailNotification {
                        // Update wl_notificationtimestamp for all watching users except the editor
                        $fname = __METHOD__;
                        $dbw->onTransactionIdle(
-                               function () use ( $dbw, $timestamp, $watchers, $title, $fname ) {
+                               function () use ( $dbw, $timestamp, $watchers, $linkTarget, $fname ) {
                                        $dbw->update( 'watchlist',
                                                array( /* SET */
                                                        'wl_notificationtimestamp' => $dbw->timestamp( $timestamp )
                                                ), array( /* WHERE */
                                                        'wl_user' => $watchers,
-                                                       'wl_namespace' => $title->getNamespace(),
-                                                       'wl_title' => $title->getDBkey(),
+                                                       'wl_namespace' => $linkTarget->getNamespace(),
+                                                       'wl_title' => $linkTarget->getDBkey(),
                                                ), $fname
                                        );
                                }
index 85595f1..b5a09bd 100644 (file)
@@ -409,20 +409,14 @@ class UserMailer {
                        set_error_handler( 'UserMailer::errorHandler' );
 
                        try {
-                               $safeMode = wfIniGetBool( 'safe_mode' );
-
                                foreach ( $to as $recip ) {
-                                       if ( $safeMode ) {
-                                               $sent = mail( $recip, self::quotedPrintable( $subject ), $body, $headers );
-                                       } else {
-                                               $sent = mail(
-                                                       $recip,
-                                                       self::quotedPrintable( $subject ),
-                                                       $body,
-                                                       $headers,
-                                                       $extraParams
-                                               );
-                                       }
+                                       $sent = mail(
+                                               $recip,
+                                               self::quotedPrintable( $subject ),
+                                               $body,
+                                               $headers,
+                                               $extraParams
+                                       );
                                }
                        } catch ( Exception $e ) {
                                restore_error_handler();
index 9ac5e6b..c2b82d8 100644 (file)
@@ -78,6 +78,14 @@ class BitmapHandler extends TransformationalImageHandler {
                return $params;
        }
 
+       function validateParam( $name, $value ) {
+               if ( $name === 'interlace' ) {
+                       return $value === false || $value === true;
+               } else {
+                       return parent::validateParam( $name, $value );
+               }
+       }
+
        /**
         * @param File $image
         * @param array $params
index 519c420..e1f69db 100644 (file)
@@ -70,7 +70,7 @@ class XMPValidate implements LoggerAwareInterface {
                        return;
                }
                if ( $val !== 'True' && $val !== 'False' ) {
-                       $this->debug->info( __METHOD__ . " Expected True or False but got $val" );
+                       $this->logger->info( __METHOD__ . " Expected True or False but got $val" );
                        $val = null;
                }
        }
index a87b478..e81fd96 100644 (file)
@@ -521,7 +521,7 @@ class Article implements Page {
                # Try client and file cache
                if ( !$wgDebugToolbar && $oldid === 0 && $this->mPage->checkTouched() ) {
                        if ( $wgUseETag ) {
-                               $outputPage->setETag( $parserCache->getETag( $this, $parserOptions ) );
+                               $outputPage->setETag( $parserCache->getETag( $this->mPage, $parserOptions ) );
                        }
 
                        # Use the greatest of the page's timestamp or the timestamp of any
@@ -595,7 +595,7 @@ class Article implements Page {
 
                                        # Try the parser cache
                                        if ( $useParserCache ) {
-                                               $this->mParserOutput = $parserCache->get( $this, $parserOptions );
+                                               $this->mParserOutput = $parserCache->get( $this->mPage, $parserOptions );
 
                                                if ( $this->mParserOutput !== false ) {
                                                        if ( $oldid ) {
@@ -2083,6 +2083,14 @@ class Article implements Page {
                }
        }
 
+       /**
+        * Call to WikiPage function for backwards compatibility.
+        * @see WikiPage::checkFlags
+        */
+       public function checkFlags( $flags ) {
+               return $this->mPage->checkFlags( $flags );
+       }
+
        /**
         * Call to WikiPage function for backwards compatibility.
         * @see WikiPage::checkTouched
@@ -2091,6 +2099,34 @@ class Article implements Page {
                return $this->mPage->checkTouched();
        }
 
+       /**
+        * Call to WikiPage function for backwards compatibility.
+        * @see WikiPage::clearPreparedEdit
+        */
+       public function clearPreparedEdit() {
+               $this->mPage->clearPreparedEdit();
+       }
+
+       /**
+        * Call to WikiPage function for backwards compatibility.
+        * @see WikiPage::doDeleteArticleReal
+        */
+       public function doDeleteArticleReal(
+               $reason, $suppress = false, $u1 = null, $u2 = null, &$error = '', User $user = null
+       ) {
+               return $this->mPage->doDeleteArticleReal(
+                       $reason, $suppress, $u1, $u2, $error, $user
+               );
+       }
+
+       /**
+        * Call to WikiPage function for backwards compatibility.
+        * @see WikiPage::doDeleteUpdates
+        */
+       public function doDeleteUpdates( $id, Content $content = null ) {
+               return $this->mPage->doDeleteUpdates( $id, $content );
+       }
+
        /**
         * Call to WikiPage function for backwards compatibility.
         * @see WikiPage::doEdit
@@ -2128,6 +2164,26 @@ class Article implements Page {
                return $this->mPage->doPurge();
        }
 
+       /**
+        * Call to WikiPage function for backwards compatibility.
+        * @see WikiPage::doQuickEditContent
+        */
+       public function doQuickEditContent(
+               Content $content, User $user, $comment = '', $minor = false, $serialFormat = null
+       ) {
+               return $this->mPage->doQuickEditContent(
+                       $content, $user, $comment, $minor, $serialFormat
+               );
+       }
+
+       /**
+        * Call to WikiPage function for backwards compatibility.
+        * @see WikiPage::doViewUpdates
+        */
+       public function doViewUpdates( User $user, $oldid = 0 ) {
+               $this->mPage->doViewUpdates( $user, $oldid );
+       }
+
        /**
         * Call to WikiPage function for backwards compatibility.
         * @see WikiPage::exists
@@ -2152,6 +2208,14 @@ class Article implements Page {
                return $this->mPage->getActionOverrides();
        }
 
+       /**
+        * Call to WikiPage function for backwards compatibility.
+        * @see WikiPage::getAutoDeleteReason
+        */
+       public function getAutoDeleteReason( &$hasHistory ) {
+               return $this->mPage->getAutoDeleteReason( $hasHistory );
+       }
+
        /**
         * Call to WikiPage function for backwards compatibility.
         * @see WikiPage::getCategories
@@ -2184,6 +2248,38 @@ class Article implements Page {
                return $this->mPage->getContentModel();
        }
 
+       /**
+        * Call to WikiPage function for backwards compatibility.
+        * @see WikiPage::getContributors
+        */
+       public function getContributors() {
+               return $this->mPage->getContributors();
+       }
+
+       /**
+        * Call to WikiPage function for backwards compatibility.
+        * @see WikiPage::getCreator
+        */
+       public function getCreator( $audience = Revision::FOR_PUBLIC, User $user = null ) {
+               return $this->mPage->getCreator( $audience, $user );
+       }
+
+       /**
+        * Call to WikiPage function for backwards compatibility.
+        * @see WikiPage::getDeletionUpdates
+        */
+       public function getDeletionUpdates( Content $content = null ) {
+               return $this->mPage->getDeletionUpdates( $content );
+       }
+
+       /**
+        * Call to WikiPage function for backwards compatibility.
+        * @see WikiPage::getHiddenCategories
+        */
+       public function getHiddenCategories() {
+               return $this->mPage->getHiddenCategories();
+       }
+
        /**
         * Call to WikiPage function for backwards compatibility.
         * @see WikiPage::getId
@@ -2200,6 +2296,22 @@ class Article implements Page {
                return $this->mPage->getLatest();
        }
 
+       /**
+        * Call to WikiPage function for backwards compatibility.
+        * @see WikiPage::getLinksTimestamp
+        */
+       public function getLinksTimestamp() {
+               return $this->mPage->getLinksTimestamp();
+       }
+
+       /**
+        * Call to WikiPage function for backwards compatibility.
+        * @see WikiPage::getMinorEdit
+        */
+       public function getMinorEdit() {
+               return $this->mPage->getMinorEdit();
+       }
+
        /**
         * Call to WikiPage function for backwards compatibility.
         * @see WikiPage::getOldestRevision
@@ -2216,6 +2328,14 @@ class Article implements Page {
                return $this->mPage->getRedirectTarget();
        }
 
+       /**
+        * Call to WikiPage function for backwards compatibility.
+        * @see WikiPage::getRedirectURL
+        */
+       public function getRedirectURL( $rt ) {
+               return $this->mPage->getRedirectURL( $rt );
+       }
+
        /**
         * Call to WikiPage function for backwards compatibility.
         * @see WikiPage::getRevision
@@ -2249,6 +2369,23 @@ class Article implements Page {
                return $this->mPage->getTouched();
        }
 
+       /**
+        * Call to WikiPage function for backwards compatibility.
+        * @see WikiPage::getUndoContent
+        */
+       public function getUndoContent( Revision $undo, Revision $undoafter = null ) {
+               return $this->mPage->getUndoContent( $undo, $undoafter );
+       }
+
+       /**
+        * Call to WikiPage function for backwards compatibility.
+        * @see WikiPage::getUndoText
+        */
+       public function getUndoText( Revision $undo, Revision $undoafter = null ) {
+               ContentHandler::deprecated( __METHOD__, '1.21' );
+               return $this->mPage->getUndoText( $undo, $undoafter );
+       }
+
        /**
         * Call to WikiPage function for backwards compatibility.
         * @see WikiPage::getUser
@@ -2281,6 +2418,34 @@ class Article implements Page {
                return $this->mPage->insertOn( $dbw, $pageId );
        }
 
+       /**
+        * Call to WikiPage function for backwards compatibility.
+        * @see WikiPage::insertProtectNullRevision
+        */
+       public function insertProtectNullRevision( $revCommentMsg, array $limit,
+               array $expiry, $cascade, $reason, $user = null
+       ) {
+               return $this->mPage->insertProtectNullRevision( $revCommentMsg, $limit,
+                       $expiry, $cascade, $reason, $user
+               );
+       }
+
+       /**
+        * Call to WikiPage function for backwards compatibility.
+        * @see WikiPage::insertRedirect
+        */
+       public function insertRedirect() {
+               return $this->mPage->insertRedirect();
+       }
+
+       /**
+        * Call to WikiPage function for backwards compatibility.
+        * @see WikiPage::insertRedirectEntry
+        */
+       public function insertRedirectEntry( Title $rt, $oldLatest = null ) {
+               return $this->mPage->insertRedirectEntry( $rt, $oldLatest );
+       }
+
        /**
         * Call to WikiPage function for backwards compatibility.
         * @see WikiPage::isCountable
@@ -2305,6 +2470,22 @@ class Article implements Page {
                return $this->mPage->loadFromRow( $data, $from );
        }
 
+       /**
+        * Call to WikiPage function for backwards compatibility.
+        * @see WikiPage::loadPageData
+        */
+       public function loadPageData( $from = 'fromdb' ) {
+               $this->mPage->loadPageData( $from );
+       }
+
+       /**
+        * Call to WikiPage function for backwards compatibility.
+        * @see WikiPage::lockAndGetLatest
+        */
+       public function lockAndGetLatest() {
+               return $this->mPage->lockAndGetLatest();
+       }
+
        /**
         * Call to WikiPage function for backwards compatibility.
         * @see WikiPage::makeParserOptions
@@ -2313,6 +2494,22 @@ class Article implements Page {
                return $this->mPage->makeParserOptions( $context );
        }
 
+       /**
+        * Call to WikiPage function for backwards compatibility.
+        * @see WikiPage::pageDataFromId
+        */
+       public function pageDataFromId( $dbr, $id, $options = array() ) {
+               return $this->mPage->pageDataFromId( $dbr, $id, $options );
+       }
+
+       /**
+        * Call to WikiPage function for backwards compatibility.
+        * @see WikiPage::pageDataFromTitle
+        */
+       public function pageDataFromTitle( $dbr, $title, $options = array() ) {
+               return $this->mPage->pageDataFromTitle( $dbr, $title, $options );
+       }
+
        /**
         * Call to WikiPage function for backwards compatibility.
         * @see WikiPage::prepareContentForEdit
@@ -2335,6 +2532,22 @@ class Article implements Page {
                return $this->mPage->prepareTextForEdit( $text, $revid, $user );
        }
 
+       /**
+        * Call to WikiPage function for backwards compatibility.
+        * @see WikiPage::protectDescription
+        */
+       public function protectDescription( array $limit, array $expiry ) {
+               return $this->mPage->protectDescription( $limit, $expiry );
+       }
+
+       /**
+        * Call to WikiPage function for backwards compatibility.
+        * @see WikiPage::protectDescriptionLog
+        */
+       public function protectDescriptionLog( array $limit, array $expiry ) {
+               return $this->mPage->protectDescriptionLog( $limit, $expiry );
+       }
+
        /**
         * Call to WikiPage function for backwards compatibility.
         * @see WikiPage::replaceSection
@@ -2348,6 +2561,18 @@ class Article implements Page {
                );
        }
 
+       /**
+        * Call to WikiPage function for backwards compatibility.
+        * @see WikiPage::replaceSectionAtRev
+        */
+       public function replaceSectionAtRev( $sectionId, Content $sectionContent,
+               $sectionTitle = '', $baseRevId = null
+       ) {
+               return $this->mPage->replaceSectionAtRev( $sectionId, $sectionContent,
+                       $sectionTitle, $baseRevId
+               );
+       }
+
        /**
         * Call to WikiPage function for backwards compatibility.
         * @see WikiPage::replaceSectionContent
@@ -2368,6 +2593,54 @@ class Article implements Page {
                return $this->mPage->setTimestamp( $ts );
        }
 
+       /**
+        * Call to WikiPage function for backwards compatibility.
+        * @see WikiPage::shouldCheckParserCache
+        */
+       public function shouldCheckParserCache( ParserOptions $parserOptions, $oldId ) {
+               return $this->mPage->shouldCheckParserCache( $parserOptions, $oldId );
+       }
+
+       /**
+        * Call to WikiPage function for backwards compatibility.
+        * @see WikiPage::supportsSections
+        */
+       public function supportsSections() {
+               return $this->mPage->supportsSections();
+       }
+
+       /**
+        * Call to WikiPage function for backwards compatibility.
+        * @see WikiPage::triggerOpportunisticLinksUpdate
+        */
+       public function triggerOpportunisticLinksUpdate( ParserOutput $parserOutput ) {
+               return $this->mPage->triggerOpportunisticLinksUpdate( $parserOutput );
+       }
+
+       /**
+        * Call to WikiPage function for backwards compatibility.
+        * @see WikiPage::updateCategoryCounts
+        */
+       public function updateCategoryCounts( array $added, array $deleted ) {
+               return $this->mPage->updateCategoryCounts( $added, $deleted );
+       }
+
+       /**
+        * Call to WikiPage function for backwards compatibility.
+        * @see WikiPage::updateIfNewerOn
+        */
+       public function updateIfNewerOn( $dbw, $revision ) {
+               return $this->mPage->updateIfNewerOn( $dbw, $revision );
+       }
+
+       /**
+        * Call to WikiPage function for backwards compatibility.
+        * @see WikiPage::updateRedirectOn
+        */
+       public function updateRedirectOn( $dbw, $redirectTitle, $lastRevIsRedirect = null ) {
+               return $this->mPage->updateRedirectOn( $dbw, $redirectTitle, $lastRevIsRedirect = null );
+       }
+
        /**
         * Call to WikiPage function for backwards compatibility.
         * @see WikiPage::updateRevisionOn
@@ -2380,6 +2653,7 @@ class Article implements Page {
                );
        }
 
+
        /**
         * @param array $limit
         * @param array $expiry
index caebcd7..50cb96c 100644 (file)
@@ -29,6 +29,11 @@ class CategoryPage extends Article {
        # Subclasses can change this to override the viewer class.
        protected $mCategoryViewerClass = 'CategoryViewer';
 
+       /**
+        * @var WikiCategoryPage
+        */
+       protected $mPage;
+
        /**
         * @param Title $title
         * @return WikiCategoryPage
diff --git a/includes/page/ImageHistoryList.php b/includes/page/ImageHistoryList.php
new file mode 100644 (file)
index 0000000..4c36729
--- /dev/null
@@ -0,0 +1,327 @@
+<?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
+ */
+
+/**
+ * Builds the image revision log shown on image pages
+ *
+ * @ingroup Media
+ */
+class ImageHistoryList extends ContextSource {
+
+       /**
+        * @var Title
+        */
+       protected $title;
+
+       /**
+        * @var File
+        */
+       protected $img;
+
+       /**
+        * @var ImagePage
+        */
+       protected $imagePage;
+
+       /**
+        * @var File
+        */
+       protected $current;
+
+       protected $repo, $showThumb;
+       protected $preventClickjacking = false;
+
+       /**
+        * @param ImagePage $imagePage
+        */
+       public function __construct( $imagePage ) {
+               global $wgShowArchiveThumbnails;
+               $this->current = $imagePage->getPage()->getFile();
+               $this->img = $imagePage->getDisplayedFile();
+               $this->title = $imagePage->getTitle();
+               $this->imagePage = $imagePage;
+               $this->showThumb = $wgShowArchiveThumbnails && $this->img->canRender();
+               $this->setContext( $imagePage->getContext() );
+       }
+
+       /**
+        * @return ImagePage
+        */
+       public function getImagePage() {
+               return $this->imagePage;
+       }
+
+       /**
+        * @return File
+        */
+       public function getFile() {
+               return $this->img;
+       }
+
+       /**
+        * @param string $navLinks
+        * @return string
+        */
+       public function beginImageHistoryList( $navLinks = '' ) {
+               return Xml::element( 'h2', array( 'id' => 'filehistory' ), $this->msg( 'filehist' )->text() )
+               . "\n"
+               . "<div id=\"mw-imagepage-section-filehistory\">\n"
+               . $this->msg( 'filehist-help' )->parseAsBlock()
+               . $navLinks . "\n"
+               . Xml::openElement( 'table', array( 'class' => 'wikitable filehistory' ) ) . "\n"
+               . '<tr><th></th>'
+               . ( $this->current->isLocal()
+               && ( $this->getUser()->isAllowedAny( 'delete', 'deletedhistory' ) ) ? '<th></th>' : '' )
+               . '<th>' . $this->msg( 'filehist-datetime' )->escaped() . '</th>'
+               . ( $this->showThumb ? '<th>' . $this->msg( 'filehist-thumb' )->escaped() . '</th>' : '' )
+               . '<th>' . $this->msg( 'filehist-dimensions' )->escaped() . '</th>'
+               . '<th>' . $this->msg( 'filehist-user' )->escaped() . '</th>'
+               . '<th>' . $this->msg( 'filehist-comment' )->escaped() . '</th>'
+               . "</tr>\n";
+       }
+
+       /**
+        * @param string $navLinks
+        * @return string
+        */
+       public function endImageHistoryList( $navLinks = '' ) {
+               return "</table>\n$navLinks\n</div>\n";
+       }
+
+       /**
+        * @param bool $iscur
+        * @param File $file
+        * @return string
+        */
+       public function imageHistoryLine( $iscur, $file ) {
+               global $wgContLang;
+
+               $user = $this->getUser();
+               $lang = $this->getLanguage();
+               $timestamp = wfTimestamp( TS_MW, $file->getTimestamp() );
+               $img = $iscur ? $file->getName() : $file->getArchiveName();
+               $userId = $file->getUser( 'id' );
+               $userText = $file->getUser( 'text' );
+               $description = $file->getDescription( File::FOR_THIS_USER, $user );
+
+               $local = $this->current->isLocal();
+               $row = $selected = '';
+
+               // Deletion link
+               if ( $local && ( $user->isAllowedAny( 'delete', 'deletedhistory' ) ) ) {
+                       $row .= '<td>';
+                       # Link to remove from history
+                       if ( $user->isAllowed( 'delete' ) ) {
+                               $q = array( 'action' => 'delete' );
+                               if ( !$iscur ) {
+                                       $q['oldimage'] = $img;
+                               }
+                               $row .= Linker::linkKnown(
+                                       $this->title,
+                                       $this->msg( $iscur ? 'filehist-deleteall' : 'filehist-deleteone' )->escaped(),
+                                       array(), $q
+                               );
+                       }
+                       # Link to hide content. Don't show useless link to people who cannot hide revisions.
+                       $canHide = $user->isAllowed( 'deleterevision' );
+                       if ( $canHide || ( $user->isAllowed( 'deletedhistory' ) && $file->getVisibility() ) ) {
+                               if ( $user->isAllowed( 'delete' ) ) {
+                                       $row .= '<br />';
+                               }
+                               // If file is top revision or locked from this user, don't link
+                               if ( $iscur || !$file->userCan( File::DELETED_RESTRICTED, $user ) ) {
+                                       $del = Linker::revDeleteLinkDisabled( $canHide );
+                               } else {
+                                       list( $ts, ) = explode( '!', $img, 2 );
+                                       $query = array(
+                                               'type' => 'oldimage',
+                                               'target' => $this->title->getPrefixedText(),
+                                               'ids' => $ts,
+                                       );
+                                       $del = Linker::revDeleteLink( $query,
+                                               $file->isDeleted( File::DELETED_RESTRICTED ), $canHide );
+                               }
+                               $row .= $del;
+                       }
+                       $row .= '</td>';
+               }
+
+               // Reversion link/current indicator
+               $row .= '<td>';
+               if ( $iscur ) {
+                       $row .= $this->msg( 'filehist-current' )->escaped();
+               } elseif ( $local && $this->title->quickUserCan( 'edit', $user )
+                       && $this->title->quickUserCan( 'upload', $user )
+               ) {
+                       if ( $file->isDeleted( File::DELETED_FILE ) ) {
+                               $row .= $this->msg( 'filehist-revert' )->escaped();
+                       } else {
+                               $row .= Linker::linkKnown(
+                                       $this->title,
+                                       $this->msg( 'filehist-revert' )->escaped(),
+                                       array(),
+                                       array(
+                                               'action' => 'revert',
+                                               'oldimage' => $img,
+                                               'wpEditToken' => $user->getEditToken( $img )
+                                       )
+                               );
+                       }
+               }
+               $row .= '</td>';
+
+               // Date/time and image link
+               if ( $file->getTimestamp() === $this->img->getTimestamp() ) {
+                       $selected = "class='filehistory-selected'";
+               }
+               $row .= "<td $selected style='white-space: nowrap;'>";
+               if ( !$file->userCan( File::DELETED_FILE, $user ) ) {
+                       # Don't link to unviewable files
+                       $row .= '<span class="history-deleted">'
+                               . $lang->userTimeAndDate( $timestamp, $user ) . '</span>';
+               } elseif ( $file->isDeleted( File::DELETED_FILE ) ) {
+                       if ( $local ) {
+                               $this->preventClickjacking();
+                               $revdel = SpecialPage::getTitleFor( 'Revisiondelete' );
+                               # Make a link to review the image
+                               $url = Linker::linkKnown(
+                                       $revdel,
+                                       $lang->userTimeAndDate( $timestamp, $user ),
+                                       array(),
+                                       array(
+                                               'target' => $this->title->getPrefixedText(),
+                                               'file' => $img,
+                                               'token' => $user->getEditToken( $img )
+                                       )
+                               );
+                       } else {
+                               $url = $lang->userTimeAndDate( $timestamp, $user );
+                       }
+                       $row .= '<span class="history-deleted">' . $url . '</span>';
+               } elseif ( !$file->exists() ) {
+                       $row .= '<span class="mw-file-missing">'
+                               . $lang->userTimeAndDate( $timestamp, $user ) . '</span>';
+               } else {
+                       $url = $iscur ? $this->current->getUrl() : $this->current->getArchiveUrl( $img );
+                       $row .= Xml::element(
+                               'a',
+                               array( 'href' => $url ),
+                               $lang->userTimeAndDate( $timestamp, $user )
+                       );
+               }
+               $row .= "</td>";
+
+               // Thumbnail
+               if ( $this->showThumb ) {
+                       $row .= '<td>' . $this->getThumbForLine( $file ) . '</td>';
+               }
+
+               // Image dimensions + size
+               $row .= '<td>';
+               $row .= htmlspecialchars( $file->getDimensionsString() );
+               $row .= $this->msg( 'word-separator' )->escaped();
+               $row .= '<span style="white-space: nowrap;">';
+               $row .= $this->msg( 'parentheses' )->sizeParams( $file->getSize() )->escaped();
+               $row .= '</span>';
+               $row .= '</td>';
+
+               // Uploading user
+               $row .= '<td>';
+               // Hide deleted usernames
+               if ( $file->isDeleted( File::DELETED_USER ) ) {
+                       $row .= '<span class="history-deleted">'
+                               . $this->msg( 'rev-deleted-user' )->escaped() . '</span>';
+               } else {
+                       if ( $local ) {
+                               $row .= Linker::userLink( $userId, $userText );
+                               $row .= '<span style="white-space: nowrap;">';
+                               $row .= Linker::userToolLinks( $userId, $userText );
+                               $row .= '</span>';
+                       } else {
+                               $row .= htmlspecialchars( $userText );
+                       }
+               }
+               $row .= '</td>';
+
+               // Don't show deleted descriptions
+               if ( $file->isDeleted( File::DELETED_COMMENT ) ) {
+                       $row .= '<td><span class="history-deleted">' .
+                               $this->msg( 'rev-deleted-comment' )->escaped() . '</span></td>';
+               } else {
+                       $row .= '<td dir="' . $wgContLang->getDir() . '">' .
+                               Linker::formatComment( $description, $this->title ) . '</td>';
+               }
+
+               $rowClass = null;
+               Hooks::run( 'ImagePageFileHistoryLine', array( $this, $file, &$row, &$rowClass ) );
+               $classAttr = $rowClass ? " class='$rowClass'" : '';
+
+               return "<tr{$classAttr}>{$row}</tr>\n";
+       }
+
+       /**
+        * @param File $file
+        * @return string
+        */
+       protected function getThumbForLine( $file ) {
+               $lang = $this->getLanguage();
+               $user = $this->getUser();
+               if ( $file->allowInlineDisplay() && $file->userCan( File::DELETED_FILE, $user )
+                       && !$file->isDeleted( File::DELETED_FILE )
+               ) {
+                       $params = array(
+                               'width' => '120',
+                               'height' => '120',
+                       );
+                       $timestamp = wfTimestamp( TS_MW, $file->getTimestamp() );
+
+                       $thumbnail = $file->transform( $params );
+                       $options = array(
+                               'alt' => $this->msg( 'filehist-thumbtext',
+                                       $lang->userTimeAndDate( $timestamp, $user ),
+                                       $lang->userDate( $timestamp, $user ),
+                                       $lang->userTime( $timestamp, $user ) )->text(),
+                               'file-link' => true,
+                       );
+
+                       if ( !$thumbnail ) {
+                               return $this->msg( 'filehist-nothumb' )->escaped();
+                       }
+
+                       return $thumbnail->toHtml( $options );
+               } else {
+                       return $this->msg( 'filehist-nothumb' )->escaped();
+               }
+       }
+
+       /**
+        * @param bool $enable
+        */
+       protected function preventClickjacking( $enable = true ) {
+               $this->preventClickjacking = $enable;
+       }
+
+       /**
+        * @return bool
+        */
+       public function getPreventClickjacking() {
+               return $this->preventClickjacking;
+       }
+}
diff --git a/includes/page/ImageHistoryPseudoPager.php b/includes/page/ImageHistoryPseudoPager.php
new file mode 100644 (file)
index 0000000..f541387
--- /dev/null
@@ -0,0 +1,205 @@
+<?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
+ */
+
+class ImageHistoryPseudoPager extends ReverseChronologicalPager {
+       protected $preventClickjacking = false;
+
+       /**
+        * @var File
+        */
+       protected $mImg;
+
+       /**
+        * @var Title
+        */
+       protected $mTitle;
+
+       /**
+        * @param ImagePage $imagePage
+        */
+       function __construct( $imagePage ) {
+               parent::__construct( $imagePage->getContext() );
+               $this->mImagePage = $imagePage;
+               $this->mTitle = clone $imagePage->getTitle();
+               $this->mTitle->setFragment( '#filehistory' );
+               $this->mImg = null;
+               $this->mHist = array();
+               $this->mRange = array( 0, 0 ); // display range
+       }
+
+       /**
+        * @return Title
+        */
+       function getTitle() {
+               return $this->mTitle;
+       }
+
+       function getQueryInfo() {
+               return false;
+       }
+
+       /**
+        * @return string
+        */
+       function getIndexField() {
+               return '';
+       }
+
+       /**
+        * @param object $row
+        * @return string
+        */
+       function formatRow( $row ) {
+               return '';
+       }
+
+       /**
+        * @return string
+        */
+       function getBody() {
+               $s = '';
+               $this->doQuery();
+               if ( count( $this->mHist ) ) {
+                       if ( $this->mImg->isLocal() ) {
+                               // Do a batch existence check for user pages and talkpages
+                               $linkBatch = new LinkBatch();
+                               for ( $i = $this->mRange[0]; $i <= $this->mRange[1]; $i++ ) {
+                                       $file = $this->mHist[$i];
+                                       $user = $file->getUser( 'text' );
+                                       $linkBatch->add( NS_USER, $user );
+                                       $linkBatch->add( NS_USER_TALK, $user );
+                               }
+                               $linkBatch->execute();
+                       }
+
+                       $list = new ImageHistoryList( $this->mImagePage );
+                       # Generate prev/next links
+                       $navLink = $this->getNavigationBar();
+                       $s = $list->beginImageHistoryList( $navLink );
+                       // Skip rows there just for paging links
+                       for ( $i = $this->mRange[0]; $i <= $this->mRange[1]; $i++ ) {
+                               $file = $this->mHist[$i];
+                               $s .= $list->imageHistoryLine( !$file->isOld(), $file );
+                       }
+                       $s .= $list->endImageHistoryList( $navLink );
+
+                       if ( $list->getPreventClickjacking() ) {
+                               $this->preventClickjacking();
+                       }
+               }
+               return $s;
+       }
+
+       function doQuery() {
+               if ( $this->mQueryDone ) {
+                       return;
+               }
+               $this->mImg = $this->mImagePage->getPage()->getFile(); // ensure loading
+               if ( !$this->mImg->exists() ) {
+                       return;
+               }
+               $queryLimit = $this->mLimit + 1; // limit plus extra row
+               if ( $this->mIsBackwards ) {
+                       // Fetch the file history
+                       $this->mHist = $this->mImg->getHistory( $queryLimit, null, $this->mOffset, false );
+                       // The current rev may not meet the offset/limit
+                       $numRows = count( $this->mHist );
+                       if ( $numRows <= $this->mLimit && $this->mImg->getTimestamp() > $this->mOffset ) {
+                               $this->mHist = array_merge( array( $this->mImg ), $this->mHist );
+                       }
+               } else {
+                       // The current rev may not meet the offset
+                       if ( !$this->mOffset || $this->mImg->getTimestamp() < $this->mOffset ) {
+                               $this->mHist[] = $this->mImg;
+                       }
+                       // Old image versions (fetch extra row for nav links)
+                       $oiLimit = count( $this->mHist ) ? $this->mLimit : $this->mLimit + 1;
+                       // Fetch the file history
+                       $this->mHist = array_merge( $this->mHist,
+                               $this->mImg->getHistory( $oiLimit, $this->mOffset, null, false ) );
+               }
+               $numRows = count( $this->mHist ); // Total number of query results
+               if ( $numRows ) {
+                       # Index value of top item in the list
+                       $firstIndex = $this->mIsBackwards ?
+                               $this->mHist[$numRows - 1]->getTimestamp() : $this->mHist[0]->getTimestamp();
+                       # Discard the extra result row if there is one
+                       if ( $numRows > $this->mLimit && $numRows > 1 ) {
+                               if ( $this->mIsBackwards ) {
+                                       # Index value of item past the index
+                                       $this->mPastTheEndIndex = $this->mHist[0]->getTimestamp();
+                                       # Index value of bottom item in the list
+                                       $lastIndex = $this->mHist[1]->getTimestamp();
+                                       # Display range
+                                       $this->mRange = array( 1, $numRows - 1 );
+                               } else {
+                                       # Index value of item past the index
+                                       $this->mPastTheEndIndex = $this->mHist[$numRows - 1]->getTimestamp();
+                                       # Index value of bottom item in the list
+                                       $lastIndex = $this->mHist[$numRows - 2]->getTimestamp();
+                                       # Display range
+                                       $this->mRange = array( 0, $numRows - 2 );
+                               }
+                       } else {
+                               # Setting indexes to an empty string means that they will be
+                               # omitted if they would otherwise appear in URLs. It just so
+                               # happens that this  is the right thing to do in the standard
+                               # UI, in all the relevant cases.
+                               $this->mPastTheEndIndex = '';
+                               # Index value of bottom item in the list
+                               $lastIndex = $this->mIsBackwards ?
+                                       $this->mHist[0]->getTimestamp() : $this->mHist[$numRows - 1]->getTimestamp();
+                               # Display range
+                               $this->mRange = array( 0, $numRows - 1 );
+                       }
+               } else {
+                       $firstIndex = '';
+                       $lastIndex = '';
+                       $this->mPastTheEndIndex = '';
+               }
+               if ( $this->mIsBackwards ) {
+                       $this->mIsFirst = ( $numRows < $queryLimit );
+                       $this->mIsLast = ( $this->mOffset == '' );
+                       $this->mLastShown = $firstIndex;
+                       $this->mFirstShown = $lastIndex;
+               } else {
+                       $this->mIsFirst = ( $this->mOffset == '' );
+                       $this->mIsLast = ( $numRows < $queryLimit );
+                       $this->mLastShown = $lastIndex;
+                       $this->mFirstShown = $firstIndex;
+               }
+               $this->mQueryDone = true;
+       }
+
+       /**
+        * @param bool $enable
+        */
+       protected function preventClickjacking( $enable = true ) {
+               $this->preventClickjacking = $enable;
+       }
+
+       /**
+        * @return bool
+        */
+       public function getPreventClickjacking() {
+               return $this->preventClickjacking;
+       }
+
+}
index 3638aed..d171e89 100644 (file)
@@ -38,6 +38,11 @@ class ImagePage extends Article {
        /** @var bool */
        protected $mExtraDescription = false;
 
+       /**
+        * @var WikiFilePage
+        */
+       protected $mPage;
+
        /**
         * @param Title $title
         * @return WikiFilePage
@@ -1204,498 +1209,36 @@ EOT
                return $thumbSizes;
        }
 
-}
-
-/**
- * Builds the image revision log shown on image pages
- *
- * @ingroup Media
- */
-class ImageHistoryList extends ContextSource {
-
        /**
-        * @var Title
-        */
-       protected $title;
-
-       /**
-        * @var File
-        */
-       protected $img;
-
-       /**
-        * @var ImagePage
-        */
-       protected $imagePage;
-
-       /**
-        * @var File
-        */
-       protected $current;
-
-       protected $repo, $showThumb;
-       protected $preventClickjacking = false;
-
-       /**
-        * @param ImagePage $imagePage
-        */
-       public function __construct( $imagePage ) {
-               global $wgShowArchiveThumbnails;
-               $this->current = $imagePage->getFile();
-               $this->img = $imagePage->getDisplayedFile();
-               $this->title = $imagePage->getTitle();
-               $this->imagePage = $imagePage;
-               $this->showThumb = $wgShowArchiveThumbnails && $this->img->canRender();
-               $this->setContext( $imagePage->getContext() );
-       }
-
-       /**
-        * @return ImagePage
-        */
-       public function getImagePage() {
-               return $this->imagePage;
-       }
-
-       /**
-        * @return File
+        * @see WikiFilePage::getFile
+        * @return bool|File
         */
        public function getFile() {
-               return $this->img;
-       }
-
-       /**
-        * @param string $navLinks
-        * @return string
-        */
-       public function beginImageHistoryList( $navLinks = '' ) {
-               return Xml::element( 'h2', array( 'id' => 'filehistory' ), $this->msg( 'filehist' )->text() )
-                       . "\n"
-                       . "<div id=\"mw-imagepage-section-filehistory\">\n"
-                       . $this->msg( 'filehist-help' )->parseAsBlock()
-                       . $navLinks . "\n"
-                       . Xml::openElement( 'table', array( 'class' => 'wikitable filehistory' ) ) . "\n"
-                       . '<tr><th></th>'
-                       . ( $this->current->isLocal()
-                               && ( $this->getUser()->isAllowedAny( 'delete', 'deletedhistory' ) ) ? '<th></th>' : '' )
-                       . '<th>' . $this->msg( 'filehist-datetime' )->escaped() . '</th>'
-                       . ( $this->showThumb ? '<th>' . $this->msg( 'filehist-thumb' )->escaped() . '</th>' : '' )
-                       . '<th>' . $this->msg( 'filehist-dimensions' )->escaped() . '</th>'
-                       . '<th>' . $this->msg( 'filehist-user' )->escaped() . '</th>'
-                       . '<th>' . $this->msg( 'filehist-comment' )->escaped() . '</th>'
-                       . "</tr>\n";
-       }
-
-       /**
-        * @param string $navLinks
-        * @return string
-        */
-       public function endImageHistoryList( $navLinks = '' ) {
-               return "</table>\n$navLinks\n</div>\n";
-       }
-
-       /**
-        * @param bool $iscur
-        * @param File $file
-        * @return string
-        */
-       public function imageHistoryLine( $iscur, $file ) {
-               global $wgContLang;
-
-               $user = $this->getUser();
-               $lang = $this->getLanguage();
-               $timestamp = wfTimestamp( TS_MW, $file->getTimestamp() );
-               $img = $iscur ? $file->getName() : $file->getArchiveName();
-               $userId = $file->getUser( 'id' );
-               $userText = $file->getUser( 'text' );
-               $description = $file->getDescription( File::FOR_THIS_USER, $user );
-
-               $local = $this->current->isLocal();
-               $row = $selected = '';
-
-               // Deletion link
-               if ( $local && ( $user->isAllowedAny( 'delete', 'deletedhistory' ) ) ) {
-                       $row .= '<td>';
-                       # Link to remove from history
-                       if ( $user->isAllowed( 'delete' ) ) {
-                               $q = array( 'action' => 'delete' );
-                               if ( !$iscur ) {
-                                       $q['oldimage'] = $img;
-                               }
-                               $row .= Linker::linkKnown(
-                                       $this->title,
-                                       $this->msg( $iscur ? 'filehist-deleteall' : 'filehist-deleteone' )->escaped(),
-                                       array(), $q
-                               );
-                       }
-                       # Link to hide content. Don't show useless link to people who cannot hide revisions.
-                       $canHide = $user->isAllowed( 'deleterevision' );
-                       if ( $canHide || ( $user->isAllowed( 'deletedhistory' ) && $file->getVisibility() ) ) {
-                               if ( $user->isAllowed( 'delete' ) ) {
-                                       $row .= '<br />';
-                               }
-                               // If file is top revision or locked from this user, don't link
-                               if ( $iscur || !$file->userCan( File::DELETED_RESTRICTED, $user ) ) {
-                                       $del = Linker::revDeleteLinkDisabled( $canHide );
-                               } else {
-                                       list( $ts, ) = explode( '!', $img, 2 );
-                                       $query = array(
-                                               'type' => 'oldimage',
-                                               'target' => $this->title->getPrefixedText(),
-                                               'ids' => $ts,
-                                       );
-                                       $del = Linker::revDeleteLink( $query,
-                                               $file->isDeleted( File::DELETED_RESTRICTED ), $canHide );
-                               }
-                               $row .= $del;
-                       }
-                       $row .= '</td>';
-               }
-
-               // Reversion link/current indicator
-               $row .= '<td>';
-               if ( $iscur ) {
-                       $row .= $this->msg( 'filehist-current' )->escaped();
-               } elseif ( $local && $this->title->quickUserCan( 'edit', $user )
-                       && $this->title->quickUserCan( 'upload', $user )
-               ) {
-                       if ( $file->isDeleted( File::DELETED_FILE ) ) {
-                               $row .= $this->msg( 'filehist-revert' )->escaped();
-                       } else {
-                               $row .= Linker::linkKnown(
-                                       $this->title,
-                                       $this->msg( 'filehist-revert' )->escaped(),
-                                       array(),
-                                       array(
-                                               'action' => 'revert',
-                                               'oldimage' => $img,
-                                               'wpEditToken' => $user->getEditToken( $img )
-                                       )
-                               );
-                       }
-               }
-               $row .= '</td>';
-
-               // Date/time and image link
-               if ( $file->getTimestamp() === $this->img->getTimestamp() ) {
-                       $selected = "class='filehistory-selected'";
-               }
-               $row .= "<td $selected style='white-space: nowrap;'>";
-               if ( !$file->userCan( File::DELETED_FILE, $user ) ) {
-                       # Don't link to unviewable files
-                       $row .= '<span class="history-deleted">'
-                               . $lang->userTimeAndDate( $timestamp, $user ) . '</span>';
-               } elseif ( $file->isDeleted( File::DELETED_FILE ) ) {
-                       if ( $local ) {
-                               $this->preventClickjacking();
-                               $revdel = SpecialPage::getTitleFor( 'Revisiondelete' );
-                               # Make a link to review the image
-                               $url = Linker::linkKnown(
-                                       $revdel,
-                                       $lang->userTimeAndDate( $timestamp, $user ),
-                                       array(),
-                                       array(
-                                               'target' => $this->title->getPrefixedText(),
-                                               'file' => $img,
-                                               'token' => $user->getEditToken( $img )
-                                       )
-                               );
-                       } else {
-                               $url = $lang->userTimeAndDate( $timestamp, $user );
-                       }
-                       $row .= '<span class="history-deleted">' . $url . '</span>';
-               } elseif ( !$file->exists() ) {
-                       $row .= '<span class="mw-file-missing">'
-                               . $lang->userTimeAndDate( $timestamp, $user ) . '</span>';
-               } else {
-                       $url = $iscur ? $this->current->getUrl() : $this->current->getArchiveUrl( $img );
-                       $row .= Xml::element(
-                               'a',
-                               array( 'href' => $url ),
-                               $lang->userTimeAndDate( $timestamp, $user )
-                       );
-               }
-               $row .= "</td>";
-
-               // Thumbnail
-               if ( $this->showThumb ) {
-                       $row .= '<td>' . $this->getThumbForLine( $file ) . '</td>';
-               }
-
-               // Image dimensions + size
-               $row .= '<td>';
-               $row .= htmlspecialchars( $file->getDimensionsString() );
-               $row .= $this->msg( 'word-separator' )->escaped();
-               $row .= '<span style="white-space: nowrap;">';
-               $row .= $this->msg( 'parentheses' )->sizeParams( $file->getSize() )->escaped();
-               $row .= '</span>';
-               $row .= '</td>';
-
-               // Uploading user
-               $row .= '<td>';
-               // Hide deleted usernames
-               if ( $file->isDeleted( File::DELETED_USER ) ) {
-                       $row .= '<span class="history-deleted">'
-                               . $this->msg( 'rev-deleted-user' )->escaped() . '</span>';
-               } else {
-                       if ( $local ) {
-                               $row .= Linker::userLink( $userId, $userText );
-                               $row .= '<span style="white-space: nowrap;">';
-                               $row .= Linker::userToolLinks( $userId, $userText );
-                               $row .= '</span>';
-                       } else {
-                               $row .= htmlspecialchars( $userText );
-                       }
-               }
-               $row .= '</td>';
-
-               // Don't show deleted descriptions
-               if ( $file->isDeleted( File::DELETED_COMMENT ) ) {
-                       $row .= '<td><span class="history-deleted">' .
-                               $this->msg( 'rev-deleted-comment' )->escaped() . '</span></td>';
-               } else {
-                       $row .= '<td dir="' . $wgContLang->getDir() . '">' .
-                               Linker::formatComment( $description, $this->title ) . '</td>';
-               }
-
-               $rowClass = null;
-               Hooks::run( 'ImagePageFileHistoryLine', array( $this, $file, &$row, &$rowClass ) );
-               $classAttr = $rowClass ? " class='$rowClass'" : '';
-
-               return "<tr{$classAttr}>{$row}</tr>\n";
-       }
-
-       /**
-        * @param File $file
-        * @return string
-        */
-       protected function getThumbForLine( $file ) {
-               $lang = $this->getLanguage();
-               $user = $this->getUser();
-               if ( $file->allowInlineDisplay() && $file->userCan( File::DELETED_FILE, $user )
-                       && !$file->isDeleted( File::DELETED_FILE )
-               ) {
-                       $params = array(
-                               'width' => '120',
-                               'height' => '120',
-                       );
-                       $timestamp = wfTimestamp( TS_MW, $file->getTimestamp() );
-
-                       $thumbnail = $file->transform( $params );
-                       $options = array(
-                               'alt' => $this->msg( 'filehist-thumbtext',
-                                       $lang->userTimeAndDate( $timestamp, $user ),
-                                       $lang->userDate( $timestamp, $user ),
-                                       $lang->userTime( $timestamp, $user ) )->text(),
-                               'file-link' => true,
-                       );
-
-                       if ( !$thumbnail ) {
-                               return $this->msg( 'filehist-nothumb' )->escaped();
-                       }
-
-                       return $thumbnail->toHtml( $options );
-               } else {
-                       return $this->msg( 'filehist-nothumb' )->escaped();
-               }
-       }
-
-       /**
-        * @param bool $enable
-        */
-       protected function preventClickjacking( $enable = true ) {
-               $this->preventClickjacking = $enable;
+               return $this->mPage->getFile();
        }
 
        /**
+        * @see WikiFilePage::isLocal
         * @return bool
         */
-       public function getPreventClickjacking() {
-               return $this->preventClickjacking;
-       }
-}
-
-class ImageHistoryPseudoPager extends ReverseChronologicalPager {
-       protected $preventClickjacking = false;
-
-       /**
-        * @var File
-        */
-       protected $mImg;
-
-       /**
-        * @var Title
-        */
-       protected $mTitle;
-
-       /**
-        * @param ImagePage $imagePage
-        */
-       function __construct( $imagePage ) {
-               parent::__construct( $imagePage->getContext() );
-               $this->mImagePage = $imagePage;
-               $this->mTitle = clone $imagePage->getTitle();
-               $this->mTitle->setFragment( '#filehistory' );
-               $this->mImg = null;
-               $this->mHist = array();
-               $this->mRange = array( 0, 0 ); // display range
-       }
-
-       /**
-        * @return Title
-        */
-       function getTitle() {
-               return $this->mTitle;
-       }
-
-       function getQueryInfo() {
-               return false;
+       public function isLocal() {
+               return $this->mPage->isLocal();
        }
 
        /**
-        * @return string
+        * @see WikiFilePage::getDuplicates
+        * @return array|null
         */
-       function getIndexField() {
-               return '';
+       public function getDuplicates() {
+               return $this->mPage->getDuplicates();
        }
 
        /**
-        * @param object $row
-        * @return string
-        */
-       function formatRow( $row ) {
-               return '';
-       }
-
-       /**
-        * @return string
-        */
-       function getBody() {
-               $s = '';
-               $this->doQuery();
-               if ( count( $this->mHist ) ) {
-                       if ( $this->mImg->isLocal() ) {
-                               // Do a batch existence check for user pages and talkpages
-                               $linkBatch = new LinkBatch();
-                               for ( $i = $this->mRange[0]; $i <= $this->mRange[1]; $i++ ) {
-                                       $file = $this->mHist[$i];
-                                       $user = $file->getUser( 'text' );
-                                       $linkBatch->add( NS_USER, $user );
-                                       $linkBatch->add( NS_USER_TALK, $user );
-                               }
-                               $linkBatch->execute();
-                       }
-
-                       $list = new ImageHistoryList( $this->mImagePage );
-                       # Generate prev/next links
-                       $navLink = $this->getNavigationBar();
-                       $s = $list->beginImageHistoryList( $navLink );
-                       // Skip rows there just for paging links
-                       for ( $i = $this->mRange[0]; $i <= $this->mRange[1]; $i++ ) {
-                               $file = $this->mHist[$i];
-                               $s .= $list->imageHistoryLine( !$file->isOld(), $file );
-                       }
-                       $s .= $list->endImageHistoryList( $navLink );
-
-                       if ( $list->getPreventClickjacking() ) {
-                               $this->preventClickjacking();
-                       }
-               }
-               return $s;
-       }
-
-       function doQuery() {
-               if ( $this->mQueryDone ) {
-                       return;
-               }
-               $this->mImg = $this->mImagePage->getFile(); // ensure loading
-               if ( !$this->mImg->exists() ) {
-                       return;
-               }
-               $queryLimit = $this->mLimit + 1; // limit plus extra row
-               if ( $this->mIsBackwards ) {
-                       // Fetch the file history
-                       $this->mHist = $this->mImg->getHistory( $queryLimit, null, $this->mOffset, false );
-                       // The current rev may not meet the offset/limit
-                       $numRows = count( $this->mHist );
-                       if ( $numRows <= $this->mLimit && $this->mImg->getTimestamp() > $this->mOffset ) {
-                               $this->mHist = array_merge( array( $this->mImg ), $this->mHist );
-                       }
-               } else {
-                       // The current rev may not meet the offset
-                       if ( !$this->mOffset || $this->mImg->getTimestamp() < $this->mOffset ) {
-                               $this->mHist[] = $this->mImg;
-                       }
-                       // Old image versions (fetch extra row for nav links)
-                       $oiLimit = count( $this->mHist ) ? $this->mLimit : $this->mLimit + 1;
-                       // Fetch the file history
-                       $this->mHist = array_merge( $this->mHist,
-                               $this->mImg->getHistory( $oiLimit, $this->mOffset, null, false ) );
-               }
-               $numRows = count( $this->mHist ); // Total number of query results
-               if ( $numRows ) {
-                       # Index value of top item in the list
-                       $firstIndex = $this->mIsBackwards ?
-                               $this->mHist[$numRows - 1]->getTimestamp() : $this->mHist[0]->getTimestamp();
-                       # Discard the extra result row if there is one
-                       if ( $numRows > $this->mLimit && $numRows > 1 ) {
-                               if ( $this->mIsBackwards ) {
-                                       # Index value of item past the index
-                                       $this->mPastTheEndIndex = $this->mHist[0]->getTimestamp();
-                                       # Index value of bottom item in the list
-                                       $lastIndex = $this->mHist[1]->getTimestamp();
-                                       # Display range
-                                       $this->mRange = array( 1, $numRows - 1 );
-                               } else {
-                                       # Index value of item past the index
-                                       $this->mPastTheEndIndex = $this->mHist[$numRows - 1]->getTimestamp();
-                                       # Index value of bottom item in the list
-                                       $lastIndex = $this->mHist[$numRows - 2]->getTimestamp();
-                                       # Display range
-                                       $this->mRange = array( 0, $numRows - 2 );
-                               }
-                       } else {
-                               # Setting indexes to an empty string means that they will be
-                               # omitted if they would otherwise appear in URLs. It just so
-                               # happens that this  is the right thing to do in the standard
-                               # UI, in all the relevant cases.
-                               $this->mPastTheEndIndex = '';
-                               # Index value of bottom item in the list
-                               $lastIndex = $this->mIsBackwards ?
-                                       $this->mHist[0]->getTimestamp() : $this->mHist[$numRows - 1]->getTimestamp();
-                               # Display range
-                               $this->mRange = array( 0, $numRows - 1 );
-                       }
-               } else {
-                       $firstIndex = '';
-                       $lastIndex = '';
-                       $this->mPastTheEndIndex = '';
-               }
-               if ( $this->mIsBackwards ) {
-                       $this->mIsFirst = ( $numRows < $queryLimit );
-                       $this->mIsLast = ( $this->mOffset == '' );
-                       $this->mLastShown = $firstIndex;
-                       $this->mFirstShown = $lastIndex;
-               } else {
-                       $this->mIsFirst = ( $this->mOffset == '' );
-                       $this->mIsLast = ( $numRows < $queryLimit );
-                       $this->mLastShown = $lastIndex;
-                       $this->mFirstShown = $firstIndex;
-               }
-               $this->mQueryDone = true;
-       }
-
-       /**
-        * @param bool $enable
-        */
-       protected function preventClickjacking( $enable = true ) {
-               $this->preventClickjacking = $enable;
-       }
-
-       /**
-        * @return bool
+        * @see WikiFilePage::getForeignCategories
+        * @return TitleArray|Title[]
         */
-       public function getPreventClickjacking() {
-               return $this->preventClickjacking;
+       public function getForeignCategories() {
+               $this->mPage->getForeignCategories();
        }
 
 }
index c02f975..cfd8d6b 100644 (file)
@@ -21,7 +21,7 @@
  */
 
 /**
- * Abstract class for type hinting (accepts WikiPage, Article, ImagePage, CategoryPage)
+ * Interface for type hinting (accepts WikiPage, Article, ImagePage, CategoryPage)
  */
 interface Page {
 }
@@ -1636,6 +1636,9 @@ class WikiPage implements Page, IDBAccessObject {
         * @param User $user The user doing the edit
         * @param string $serialFormat Format for storing the content in the
         *   database.
+        * @param array|null $tags Change tags to apply to this edit
+        * Callers are responsible for permission checks
+        * (with ChangeTags::canAddTagsAccompanyingChange)
         *
         * @throws MWException
         * @return Status Possible errors:
@@ -1657,7 +1660,7 @@ class WikiPage implements Page, IDBAccessObject {
         */
        public function doEditContent(
                Content $content, $summary, $flags = 0, $baseRevId = false,
-               User $user = null, $serialFormat = null
+               User $user = null, $serialFormat = null, $tags = null
        ) {
                global $wgUser, $wgUseAutomaticEditSummaries;
 
@@ -1719,7 +1722,8 @@ class WikiPage implements Page, IDBAccessObject {
                        'oldContent' => $old_content,
                        'oldId' => $this->getLatest(),
                        'oldIsRedirect' => $this->isRedirect(),
-                       'oldCountable' => $this->isCountable()
+                       'oldCountable' => $this->isCountable(),
+                       'tags' => ( $tags !== null ) ? (array)$tags : array()
                );
 
                // Actually create the revision and create/update the page
@@ -1793,6 +1797,8 @@ class WikiPage implements Page, IDBAccessObject {
 
                $changed = !$content->equals( $oldContent );
 
+               $dbw = wfGetDB( DB_MASTER );
+
                if ( $changed ) {
                        $prepStatus = $content->prepareSave( $this, $flags, $oldid, $user );
                        $status->merge( $prepStatus );
@@ -1800,14 +1806,13 @@ class WikiPage implements Page, IDBAccessObject {
                                return $status;
                        }
 
-                       $dbw = wfGetDB( DB_MASTER );
-                       $dbw->begin( __METHOD__ );
+                       $dbw->startAtomic( __METHOD__ );
                        // Get the latest page_latest value while locking it.
                        // Do a CAS style check to see if it's the same as when this method
                        // started. If it changed then bail out before touching the DB.
                        $latestNow = $this->lockAndGetLatest();
                        if ( $latestNow != $oldid ) {
-                               $dbw->commit( __METHOD__ );
+                               $dbw->endAtomic( __METHOD__ );
                                // Page updated or deleted in the mean time
                                $status->fatal( 'edit-conflict' );
 
@@ -1849,13 +1854,14 @@ class WikiPage implements Page, IDBAccessObject {
                                        $oldContent ? $oldContent->getSize() : 0,
                                        $newsize,
                                        $revisionId,
-                                       $patrolled
+                                       $patrolled,
+                                       $meta['tags']
                                );
                        }
 
                        $user->incEditCount();
 
-                       $dbw->commit( __METHOD__ );
+                       $dbw->endAtomic( __METHOD__ );
                        $this->mTimestamp = $now;
                } else {
                        // Bug 32948: revision ID must be set to page {{REVISIONID}} and
@@ -1863,17 +1869,6 @@ class WikiPage implements Page, IDBAccessObject {
                        $revision->setId( $this->getLatest() );
                }
 
-               // Update links tables, site stats, etc.
-               $this->doEditUpdates(
-                       $revision,
-                       $user,
-                       array(
-                               'changed' => $changed,
-                               'oldcountable' => $meta['oldCountable'],
-                               'oldrevision' => $meta['oldRevision']
-                       )
-               );
-
                if ( $changed ) {
                        // Return the new revision to the caller
                        $status->value['revision'] = $revision;
@@ -1884,11 +1879,32 @@ class WikiPage implements Page, IDBAccessObject {
                        $this->mTitle->invalidateCache( $now );
                }
 
-               // Trigger post-save hook
-               $hook_args = array( &$this, &$user, $content, $summary,
-                       $flags & EDIT_MINOR, null, null, &$flags, $revision, &$status, $meta['baseRevId'] );
-               ContentHandler::runLegacyHooks( 'ArticleSaveComplete', $hook_args );
-               Hooks::run( 'PageContentSaveComplete', $hook_args );
+               // Do secondary updates once the main changes have been committed...
+               $that = $this;
+               $dbw->onTransactionIdle(
+                       function () use (
+                               $dbw, &$that, $revision, &$user, $content, $summary, &$flags,
+                               $changed, $meta, &$status
+                       ) {
+                               // Do per-page updates in a transaction
+                               $dbw->setFlag( DBO_TRX );
+                               // Update links tables, site stats, etc.
+                               $that->doEditUpdates(
+                                       $revision,
+                                       $user,
+                                       array(
+                                               'changed' => $changed,
+                                               'oldcountable' => $meta['oldCountable'],
+                                               'oldrevision' => $meta['oldRevision']
+                                       )
+                               );
+                               // Trigger post-save hook
+                               $params = array( &$that, &$user, $content, $summary, $flags & EDIT_MINOR,
+                                       null, null, &$flags, $revision, &$status, $meta['baseRevId'] );
+                               ContentHandler::runLegacyHooks( 'ArticleSaveComplete', $params );
+                               Hooks::run( 'PageContentSaveComplete', $params );
+                       }
+               );
 
                return $status;
        }
@@ -1978,7 +1994,8 @@ class WikiPage implements Page, IDBAccessObject {
                                '',
                                $newsize,
                                $revisionId,
-                               $patrolled
+                               $patrolled,
+                               $meta['tags']
                        );
                }
 
@@ -2200,7 +2217,7 @@ class WikiPage implements Page, IDBAccessObject {
         *   - 'no-change': don't update the article count, ever
         */
        public function doEditUpdates( Revision $revision, User $user, array $options = array() ) {
-               global $wgRCWatchCategoryMembership;
+               global $wgRCWatchCategoryMembership, $wgContLang;
 
                $options += array(
                        'changed' => true,
@@ -2329,6 +2346,10 @@ class WikiPage implements Page, IDBAccessObject {
                        }
 
                        MessageCache::singleton()->replace( $shortTitle, $msgtext );
+
+                       if ( $wgContLang->hasVariants() ) {
+                               $wgContLang->updateConversionTable( $this->mTitle );
+                       }
                }
 
                if ( $options['created'] ) {
@@ -3048,13 +3069,17 @@ class WikiPage implements Page, IDBAccessObject {
         *    success        : 'summary' (str), 'current' (rev), 'target' (rev)
         *
         * @param User $user The user performing the rollback
+        * @param array|null $tags Change tags to apply to the rollback
+        * Callers are responsible for permission checks
+        * (with ChangeTags::canAddTagsAccompanyingChange)
+        *
         * @return array Array of errors, each error formatted as
         *   array(messagekey, param1, param2, ...).
         * On success, the array is empty.  This array can also be passed to
         * OutputPage::showPermissionsErrorPage().
         */
        public function doRollback(
-               $fromP, $summary, $token, $bot, &$resultDetails, User $user
+               $fromP, $summary, $token, $bot, &$resultDetails, User $user, $tags = null
        ) {
                $resultDetails = null;
 
@@ -3076,7 +3101,7 @@ class WikiPage implements Page, IDBAccessObject {
                        return $errors;
                }
 
-               return $this->commitRollback( $fromP, $summary, $bot, $resultDetails, $user );
+               return $this->commitRollback( $fromP, $summary, $bot, $resultDetails, $user, $tags );
        }
 
        /**
@@ -3093,9 +3118,15 @@ class WikiPage implements Page, IDBAccessObject {
         *
         * @param array $resultDetails Contains result-specific array of additional values
         * @param User $guser The user performing the rollback
+        * @param array|null $tags Change tags to apply to the rollback
+        * Callers are responsible for permission checks
+        * (with ChangeTags::canAddTagsAccompanyingChange)
+        *
         * @return array
         */
-       public function commitRollback( $fromP, $summary, $bot, &$resultDetails, User $guser ) {
+       public function commitRollback( $fromP, $summary, $bot,
+               &$resultDetails, User $guser, $tags = null
+       ) {
                global $wgUseRCPatrol, $wgContLang;
 
                $dbw = wfGetDB( DB_MASTER );
@@ -3190,7 +3221,9 @@ class WikiPage implements Page, IDBAccessObject {
                        $summary,
                        $flags,
                        $target->getId(),
-                       $guser
+                       $guser,
+                       null,
+                       $tags
                );
 
                // Set patrolling and bot flag on the edits, which gets rollbacked.
@@ -3276,6 +3309,8 @@ class WikiPage implements Page, IDBAccessObject {
         * @param Title $title
         */
        public static function onArticleDelete( Title $title ) {
+               global $wgContLang;
+
                // Update existence markers on article/talk tabs...
                $other = $title->getOtherPage();
 
@@ -3291,6 +3326,10 @@ class WikiPage implements Page, IDBAccessObject {
                // Messages
                if ( $title->getNamespace() == NS_MEDIAWIKI ) {
                        MessageCache::singleton()->replace( $title->getDBkey(), false );
+
+                       if ( $wgContLang->hasVariants() ) {
+                               $wgContLang->updateConversionTable( $title );
+                       }
                }
 
                // Images
index 6329fd7..dfc4b53 100644 (file)
@@ -282,7 +282,7 @@ class LinkHolderArray {
                        return;
                }
 
-               global $wgContLang, $wgContentHandlerUseDB;
+               global $wgContLang, $wgContentHandlerUseDB, $wgPageLanguageUseDB;
 
                $colours = array();
                $linkCache = LinkCache::singleton();
@@ -348,6 +348,9 @@ class LinkHolderArray {
                        if ( $wgContentHandlerUseDB ) {
                                $fields[] = 'page_content_model';
                        }
+                       if ( $wgPageLanguageUseDB ) {
+                               $fields[] = 'page_lang';
+                       }
 
                        $res = $dbr->select(
                                'page',
@@ -440,8 +443,11 @@ class LinkHolderArray {
                # Make interwiki link HTML
                $output = $this->parent->getOutput();
                $replacePairs = array();
+               $options = array(
+                       'stubThreshold' => $this->parent->getOptions()->getStubThreshold(),
+               );
                foreach ( $this->interwikis as $key => $link ) {
-                       $replacePairs[$key] = Linker::link( $link['title'], $link['text'] );
+                       $replacePairs[$key] = Linker::link( $link['title'], $link['text'], array(), array(), $options );
                        $output->addInterwikiLink( $link['title'] );
                }
                $replacer = new HashtableReplacer( $replacePairs, 1 );
@@ -457,7 +463,7 @@ class LinkHolderArray {
         * @param array $colours
         */
        protected function doVariants( &$colours ) {
-               global $wgContLang, $wgContentHandlerUseDB;
+               global $wgContLang, $wgContentHandlerUseDB, $wgPageLanguageUseDB;
                $linkBatch = new LinkBatch();
                $variantMap = array(); // maps $pdbkey_Variant => $keys (of link holders)
                $output = $this->parent->getOutput();
@@ -552,6 +558,9 @@ class LinkHolderArray {
                        if ( $wgContentHandlerUseDB ) {
                                $fields[] = 'page_content_model';
                        }
+                       if ( $wgPageLanguageUseDB ) {
+                               $fields[] = 'page_lang';
+                       }
 
                        $varRes = $dbr->select( 'page',
                                $fields,
index 5f7e89c..bec83e1 100644 (file)
@@ -1113,15 +1113,15 @@ class Parser {
                                        $line = substr( $line, 1 );
                                }
 
+                               // Implies both are valid for table headings.
                                if ( $first_character === '!' ) {
                                        $line = str_replace( '!!', '||', $line );
                                }
 
                                # Split up multiple cells on the same line.
                                # FIXME : This can result in improper nesting of tags processed
-                               # by earlier parser steps, but should avoid splitting up eg
-                               # attribute values containing literal "||".
-                               $cells = StringUtils::explodeMarkup( '||', $line );
+                               # by earlier parser steps.
+                               $cells = explode( '||', $line );
 
                                $outLine = '';
 
@@ -6408,9 +6408,8 @@ class Parser {
                }
                $this->mInParse = true;
 
-               $that = $this;
-               $recursiveCheck = new ScopedCallback( function() use ( $that ) {
-                       $that->mInParse = false;
+               $recursiveCheck = new ScopedCallback( function() {
+                       $this->mInParse = false;
                } );
 
                return $recursiveCheck;
index e6d5274..49c6ce9 100644 (file)
@@ -599,6 +599,16 @@ class ParserOptions {
                $this->initialiseFromUser( $user, $lang );
        }
 
+       /**
+        * Get a ParserOptions object for an anonymous user
+        * @since 1.27
+        * @return ParserOptions
+        */
+       public static function newFromAnon() {
+               global $wgContLang;
+               return new ParserOptions( new User, $wgContLang );
+       }
+
        /**
         * Get a ParserOptions object from a given user.
         * Language will be taken from $wgLang.
index 817f153..4ca3a87 100644 (file)
@@ -237,8 +237,6 @@ class Preprocessor_DOM extends Preprocessor {
                $inHeading = false;
                // True if there are no more greater-than (>) signs right of $i
                $noMoreGT = false;
-               // Map of tag name => true if there are no more closing tags of given type right of $i
-               $noMoreClosingTag = array();
                // True to ignore all input up to the next <onlyinclude>
                $findOnlyinclude = $enableOnlyinclude;
                // Do a line-start run without outputting an LF character
@@ -459,21 +457,17 @@ class Preprocessor_DOM extends Preprocessor {
                                } else {
                                        $attrEnd = $tagEndPos;
                                        // Find closing tag
-                                       if (
-                                               !isset( $noMoreClosingTag[$name] ) &&
-                                               preg_match( "/<\/" . preg_quote( $name, '/' ) . "\s*>/i",
+                                       if ( preg_match( "/<\/" . preg_quote( $name, '/' ) . "\s*>/i",
                                                        $text, $matches, PREG_OFFSET_CAPTURE, $tagEndPos + 1 )
                                        ) {
                                                $inner = substr( $text, $tagEndPos + 1, $matches[0][1] - $tagEndPos - 1 );
                                                $i = $matches[0][1] + strlen( $matches[0][0] );
                                                $close = '<close>' . htmlspecialchars( $matches[0][0] ) . '</close>';
                                        } else {
-                                               // No end tag -- don't match the tag, treat opening tag as literal and resume parsing.
-                                               $i = $tagEndPos + 1;
-                                               $accum .= htmlspecialchars( substr( $text, $tagStartPos, $tagEndPos + 1 - $tagStartPos ) );
-                                               // Cache results, otherwise we have O(N^2) performance for input like <foo><foo><foo>...
-                                               $noMoreClosingTag[$name] = true;
-                                               continue;
+                                               // No end tag -- let it run out to the end of the text.
+                                               $inner = substr( $text, $tagEndPos + 1 );
+                                               $i = $lengthText;
+                                               $close = '';
                                        }
                                }
                                // <includeonly> and <noinclude> just become <ignore> tags
index 28c49fd..50eaefb 100644 (file)
@@ -160,8 +160,6 @@ class Preprocessor_Hash extends Preprocessor {
                $inHeading = false;
                // True if there are no more greater-than (>) signs right of $i
                $noMoreGT = false;
-               // Map of tag name => true if there are no more closing tags of given type right of $i
-               $noMoreClosingTag = array();
                // True to ignore all input up to the next <onlyinclude>
                $findOnlyinclude = $enableOnlyinclude;
                // Do a line-start run without outputting an LF character
@@ -382,21 +380,17 @@ class Preprocessor_Hash extends Preprocessor {
                                } else {
                                        $attrEnd = $tagEndPos;
                                        // Find closing tag
-                                       if (
-                                               !isset( $noMoreClosingTag[$name] ) &&
-                                               preg_match( "/<\/" . preg_quote( $name, '/' ) . "\s*>/i",
+                                       if ( preg_match( "/<\/" . preg_quote( $name, '/' ) . "\s*>/i",
                                                        $text, $matches, PREG_OFFSET_CAPTURE, $tagEndPos + 1 )
                                        ) {
                                                $inner = substr( $text, $tagEndPos + 1, $matches[0][1] - $tagEndPos - 1 );
                                                $i = $matches[0][1] + strlen( $matches[0][0] );
                                                $close = $matches[0][0];
                                        } else {
-                                               // No end tag -- don't match the tag, treat opening tag as literal and resume parsing.
-                                               $i = $tagEndPos + 1;
-                                               $accum->addLiteral( substr( $text, $tagStartPos, $tagEndPos + 1 - $tagStartPos ) );
-                                               // Cache results, otherwise we have O(N^2) performance for input like <foo><foo><foo>...
-                                               $noMoreClosingTag[$name] = true;
-                                               continue;
+                                               // No end tag -- let it run out to the end of the text.
+                                               $inner = substr( $text, $tagEndPos + 1 );
+                                               $i = $lengthText;
+                                               $close = null;
                                        }
                                }
                                // <includeonly> and <noinclude> just become <ignore> tags
index d7b51b8..51a6225 100644 (file)
@@ -618,11 +618,8 @@ class ResourceLoader implements LoggerAwareInterface {
                if ( !$modules ) {
                        return '';
                }
-               // Support: PHP 5.3 ("$this" for anonymous functions was added in PHP 5.4.0)
-               // http://php.net/functions.anonymous
-               $rl = $this;
-               $hashes = array_map( function ( $module ) use ( $rl, $context ) {
-                       return $rl->getModule( $module )->getVersionHash( $context );
+               $hashes = array_map( function ( $module ) use ( $context ) {
+                       return $this->getModule( $module )->getVersionHash( $context );
                }, $modules );
                return self::makeHash( implode( $hashes ) );
        }
index bcd159f..14132d6 100644 (file)
@@ -434,16 +434,28 @@ abstract class ResourceLoaderModule implements LoggerAwareInterface {
                try {
                        // If the list has been modified since last time we cached it, update the cache
                        if ( $localFileRefs !== $this->getFileDependencies( $context ) ) {
+                               $cache = ObjectCache::getLocalClusterInstance();
+                               $key = $cache->makeKey( __METHOD__, $this->getName() );
+                               $scopeLock = $cache->getScopedLock( $key, 0 );
+                               if ( !$scopeLock ) {
+                                       return; // T124649; avoid write slams
+                               }
+
                                $vary = $context->getSkin() . '|' . $context->getLanguage();
                                $dbw = wfGetDB( DB_MASTER );
                                $dbw->replace( 'module_deps',
-                                       array( array( 'md_module', 'md_skin' ) ), array(
+                                       array( array( 'md_module', 'md_skin' ) ),
+                                       array(
                                                'md_module' => $this->getName(),
                                                'md_skin' => $vary,
                                                // Use relative paths to avoid ghost entries when $IP changes (T111481)
                                                'md_deps' => FormatJson::encode( self::getRelativePaths( $localFileRefs ) ),
                                        )
                                );
+
+                               $dbw->onTransactionIdle( function () use ( &$scopeLock ) {
+                                       ScopedCallback::consume( $scopeLock ); // release after commit
+                               } );
                        }
                } catch ( Exception $e ) {
                        wfDebugLog( 'resourceloader', __METHOD__ . ": failed to update DB: $e" );
index e1df6d9..490a4ab 100644 (file)
@@ -30,11 +30,17 @@ class ResourceLoaderSkinModule extends ResourceLoaderFileModule {
         * @return array
         */
        public function getStyles( ResourceLoaderContext $context ) {
-               $logo = $this->getConfig()->get( 'Logo' );
-               $logoHD = $this->getConfig()->get( 'LogoHD' );
+               $conf = $this->getConfig();
+               $logo = $conf->get( 'Logo' );
+               $logoHD = $conf->get( 'LogoHD' );
+
+               $logo1 = OutputPage::transformResourcePath( $conf, $logo );
+               $logo15 = OutputPage::transformResourcePath( $conf, $logoHD['1.5x'] );
+               $logo2 = OutputPage::transformResourcePath( $conf, $logoHD['2x'] );
+
                $styles = parent::getStyles( $context );
                $styles['all'][] = '.mw-wiki-logo { background-image: ' .
-                       CSSMin::buildUrlValue( $logo ) .
+                       CSSMin::buildUrlValue( $logo1 ) .
                        '; }';
                if ( $logoHD ) {
                        if ( isset( $logoHD['1.5x'] ) ) {
@@ -44,7 +50,7 @@ class ResourceLoaderSkinModule extends ResourceLoaderFileModule {
                                        '(min-resolution: 1.5dppx), ' .
                                        '(min-resolution: 144dpi)'
                                ][] = '.mw-wiki-logo { background-image: ' .
-                               CSSMin::buildUrlValue( $logoHD['1.5x'] ) . ';' .
+                               CSSMin::buildUrlValue( $logo15 ) . ';' .
                                'background-size: 135px auto; }';
                        }
                        if ( isset( $logoHD['2x'] ) ) {
@@ -54,7 +60,7 @@ class ResourceLoaderSkinModule extends ResourceLoaderFileModule {
                                        '(min-resolution: 2dppx), ' .
                                        '(min-resolution: 192dpi)'
                                ][] = '.mw-wiki-logo { background-image: ' .
-                               CSSMin::buildUrlValue( $logoHD['2x'] ) . ';' .
+                               CSSMin::buildUrlValue( $logo2 ) . ';' .
                                'background-size: 135px auto; }';
                        }
                }
index 4a68f8e..c9f515e 100644 (file)
@@ -104,8 +104,6 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule {
                        'wgResourceLoaderLegacyModules' => self::getLegacyModules(),
                        'wgForeignUploadTargets' => $conf->get( 'ForeignUploadTargets' ),
                        'wgEnableUploads' => $conf->get( 'EnableUploads' ),
-                       'wgForeignUploadTestEnabled' => $conf->get( 'ForeignUploadTestEnabled' ),
-                       'wgForeignUploadTestDefault' => $conf->get( 'ForeignUploadTestDefault' ),
                );
 
                Hooks::run( 'ResourceLoaderGetConfigVars', array( &$vars ) );
index e52d07c..754e68f 100644 (file)
@@ -164,7 +164,11 @@ class RevDelRevisionItem extends RevDelItem {
                $attribs = array();
                $tags = $this->getTags();
                if ( $tags ) {
-                       list( $tagSummary, $classes ) = ChangeTags::formatSummaryRow( $tags, 'revisiondelete' );
+                       list( $tagSummary, $classes ) = ChangeTags::formatSummaryRow(
+                               $tags,
+                               'revisiondelete',
+                               $this->list->getContext()
+                       );
                        $content .= " $tagSummary";
                        $attribs['class'] = implode( ' ', $classes );
                }
index 3c8d56e..81b850a 100644 (file)
@@ -296,6 +296,15 @@ class SearchEngine {
         * @param int[]|null $namespaces
         */
        function setNamespaces( $namespaces ) {
+               if ( $namespaces ) {
+                       // Filter namespaces to only keep valid ones
+                       $validNs = $this->searchableNamespaces();
+                       $namespaces = array_filter( $namespaces, function( $ns ) use( $validNs ) {
+                               return $ns < 0 || isset( $validNs[$ns] );
+                       } );
+               } else {
+                       $namespaces = array();
+               }
                $this->namespaces = $namespaces;
        }
 
@@ -570,6 +579,201 @@ class SearchEngine {
        public function textAlreadyUpdatedForIndex() {
                return false;
        }
+
+       /**
+        * Makes search simple string if it was namespaced.
+        * Sets namespaces of the search to namespaces extracted from string.
+        * @param string $search
+        * @return $string Simplified search string
+        */
+       protected function normalizeNamespaces( $search ) {
+               // Find a Title which is not an interwiki and is in NS_MAIN
+               $title = Title::newFromText( $search );
+               $ns = $this->namespaces;
+               if ( $title && !$title->isExternal() ) {
+                       $ns = array( $title->getNamespace() );
+                       $search = $title->getText();
+                       if ( $ns[0] == NS_MAIN ) {
+                               $ns = $this->namespaces; // no explicit prefix, use default namespaces
+                               Hooks::run( 'PrefixSearchExtractNamespace', array( &$ns, &$search ) );
+                       }
+               } else {
+                       $title = Title::newFromText( $search . 'Dummy' );
+                       if ( $title && $title->getText() == 'Dummy'
+                                       && $title->getNamespace() != NS_MAIN
+                                       && !$title->isExternal() )
+                       {
+                               $ns = array( $title->getNamespace() );
+                               $search = '';
+                       } else {
+                               Hooks::run( 'PrefixSearchExtractNamespace', array( &$ns, &$search ) );
+                       }
+               }
+
+               $ns = array_map( function( $space ) {
+                       return $space == NS_MEDIA ? NS_FILE : $space;
+               }, $ns );
+
+               $this->setNamespaces( $ns );
+               return $search;
+       }
+
+       /**
+        * Perform a completion search.
+        * Does not resolve namespaces and does not check variants.
+        * Search engine implementations may want to override this function.
+        * @param string $search
+        * @return SearchSuggestionSet
+        */
+       protected function completionSearchBackend( $search ) {
+               $results = array();
+
+               $search = trim( $search );
+
+               if ( !in_array( NS_SPECIAL, $this->namespaces ) && // We do not run hook on Special: search
+                        !Hooks::run( 'PrefixSearchBackend',
+                               array( $this->namespaces, $search, $this->limit, &$results, $this->offset )
+               ) ) {
+                       // False means hook worked.
+                       // FIXME: Yes, the API is weird. That's why it is going to be deprecated.
+
+                       return SearchSuggestionSet::fromStrings( $results );
+               } else {
+                       // Hook did not do the job, use default simple search
+                       $results = $this->simplePrefixSearch( $search );
+                       return SearchSuggestionSet::fromTitles( $results );
+               }
+       }
+
+       /**
+        * Perform a completion search.
+        * @param string $search
+        * @return SearchSuggestionSet
+        */
+       public function completionSearch( $search ) {
+               if ( trim( $search ) === '' ) {
+                       return SearchSuggestionSet::emptySuggestionSet(); // Return empty result
+               }
+               $search = $this->normalizeNamespaces( $search );
+               return $this->processCompletionResults( $search, $this->completionSearchBackend( $search ) );
+       }
+
+       /**
+        * Perform a completion search with variants.
+        * @param string $search
+        * @return SearchSuggestionSet
+        */
+       public function completionSearchWithVariants( $search ) {
+               if ( trim( $search ) === '' ) {
+                       return SearchSuggestionSet::emptySuggestionSet(); // Return empty result
+               }
+               $search = $this->normalizeNamespaces( $search );
+
+               $results = $this->completionSearchBackend( $search );
+               $fallbackLimit = $this->limit - $results->getSize();
+               if ( $fallbackLimit > 0 ) {
+                       global $wgContLang;
+
+                       $fallbackSearches = $wgContLang->autoConvertToAllVariants( $search );
+                       $fallbackSearches = array_diff( array_unique( $fallbackSearches ), array( $search ) );
+
+                       foreach ( $fallbackSearches as $fbs ) {
+                               $this->setLimitOffset( $fallbackLimit );
+                               $fallbackSearchResult = $this->completionSearch( $fbs );
+                               $results->appendAll( $fallbackSearchResult );
+                               $fallbackLimit -= count( $fallbackSearchResult );
+                               if ( $fallbackLimit <= 0 ) {
+                                       break;
+                               }
+                       }
+               }
+               return $this->processCompletionResults( $search, $results );
+       }
+
+       /**
+        * Extract titles from completion results
+        * @param SearchSuggestionSet $completionResults
+        * @return Title[]
+        */
+       public function extractTitles( SearchSuggestionSet $completionResults ) {
+               return $completionResults->map( function( SearchSuggestion $sugg ) {
+                       return $sugg->getSuggestedTitle();
+               } );
+       }
+
+       /**
+        * Process completion search results.
+        * Resolves the titles and rescores.
+        * @param SearchSuggestionSet $suggestions
+        * @return SearchSuggestionSet
+        */
+       protected function processCompletionResults( $search, SearchSuggestionSet $suggestions ) {
+               if ( $suggestions->getSize() == 0 ) {
+                       // If we don't have anything, don't bother
+                       return $suggestions;
+               }
+               $search = trim( $search );
+               // preload the titles with LinkBatch
+               $titles = $suggestions->map( function( SearchSuggestion $sugg ) {
+                       return $sugg->getSuggestedTitle();
+               } );
+               $lb = new LinkBatch( $titles );
+               $lb->setCaller( __METHOD__ );
+               $lb->execute();
+
+               $results = $suggestions->map( function( SearchSuggestion $sugg ) {
+                       return $sugg->getSuggestedTitle()->getPrefixedText();
+               } );
+
+               // Rescore results with an exact title match
+               $rescorer = new SearchExactMatchRescorer();
+               $rescoredResults = $rescorer->rescore( $search, $this->namespaces, $results, $this->limit );
+
+               if ( count( $rescoredResults ) > 0 ) {
+                       $found = array_search( $rescoredResults[0], $results );
+                       if ( $found === false ) {
+                               // If the first result is not in the previous array it
+                               // means that we found a new exact match
+                               $exactMatch = SearchSuggestion::fromTitle( 0, Title::newFromText( $rescoredResults[0] ) );
+                               $suggestions->prepend( $exactMatch );
+                               $suggestions->shrink( $this->limit );
+                       } else {
+                               // if the first result is not the same we need to rescore
+                               if ( $found > 0 ) {
+                                       $suggestions->rescore( $found );
+                               }
+                       }
+               }
+
+               return $suggestions;
+       }
+
+       /**
+        * Simple prefix search for subpages.
+        * @param string $search
+        * @return Title[]
+        */
+       public function defaultPrefixSearch( $search ) {
+               if ( trim( $search ) === '' ) {
+                       return array();
+               }
+
+               $search = $this->normalizeNamespaces( $search );
+               return $this->simplePrefixSearch( $search );
+       }
+
+       /**
+        * Call out to simple search backend.
+        * Defaults to TitlePrefixSearch.
+        * @param string $search
+        * @return Title[]
+        */
+       protected function simplePrefixSearch( $search ) {
+               // Use default database prefix search
+               $backend = new TitlePrefixSearch;
+               return $backend->defaultSearchBackend( $this->namespaces, $search, $this->limit, $this->offset );
+       }
+
 }
 
 /**
diff --git a/includes/search/SearchSuggestion.php b/includes/search/SearchSuggestion.php
new file mode 100644 (file)
index 0000000..cd9062b
--- /dev/null
@@ -0,0 +1,187 @@
+<?php
+
+/**
+ * Search suggestion
+ *
+ * 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
+ *
+ */
+
+/**
+ * A search suggestion
+ *
+ */
+class SearchSuggestion {
+       /**
+        * @var string the suggestion
+        */
+       private $text;
+
+       /**
+        * @var string the suggestion URL
+        */
+       private $url;
+
+       /**
+        * @var Title|null the suggested title
+        */
+       private $suggestedTitle;
+
+       /**
+        * NOTE: even if suggestedTitle is a redirect suggestedTitleID
+        * is the ID of the target page.
+        * @var int|null the suggested title ID
+        */
+       private $suggestedTitleID;
+
+       /**
+        * @var float|null The suggestion score
+        */
+       private $score;
+
+       /**
+        * Construct a new suggestion
+        * @param float $score the suggestion score
+        * @param string $text|null the suggestion text
+        * @param Title|null $suggestedTitle the suggested title
+        * @param int|null $suggestedTitleID the suggested title ID
+        */
+       public function __construct( $score, $text = null, Title $suggestedTitle = null,
+                       $suggestedTitleID = null ) {
+               $this->score = $score;
+               $this->text = $text;
+               if ( $suggestedTitle ) {
+                       $this->setSuggestedTitle( $suggestedTitle );
+               }
+               $this->suggestedTitleID = $suggestedTitleID;
+       }
+
+       /**
+        * The suggestion text
+        * @return string
+        */
+       public function getText() {
+               return $this->text;
+       }
+
+       /**
+        * Set the suggestion text.
+        * @param string $text
+        * @param bool $setTitle Should we also update the title?
+        */
+       public function setText( $text, $setTitle = true ) {
+               $this->text = $text;
+               if ( $setTitle && $text ) {
+                       $this->setSuggestedTitle( Title::makeTitle( 0, $text ) );
+               }
+       }
+
+       /**
+        * Title object in the case this suggestion is based on a title.
+        * May return null if the suggestion is not a Title.
+        * @return Title|null
+        */
+       public function getSuggestedTitle() {
+               return $this->suggestedTitle;
+       }
+
+       /**
+        * Set the suggested title
+        * @param Title|null $title
+        */
+       public function setSuggestedTitle( Title $title = null ) {
+               $this->suggestedTitle = $title;
+               if ( $title !== null ) {
+                       $this->url = wfExpandUrl( $title->getFullURL(), PROTO_CURRENT );
+               }
+       }
+
+       /**
+        * Title ID in the case this suggestion is based on a title.
+        * May return null if the suggestion is not a Title.
+        * @return int|null
+        */
+       public function getSuggestedTitleID() {
+               return $this->suggestedTitleID;
+       }
+
+       /**
+        * Set the suggested title ID
+        * @param int|null $suggestedTitleID
+        */
+       public function setSuggestedTitleID( $suggestedTitleID = null ) {
+               $this->suggestedTitleID = $suggestedTitleID;
+       }
+
+       /**
+        * Suggestion score
+        * @return float Suggestion score
+        */
+       public function getScore() {
+               return $this->score;
+       }
+
+       /**
+        * Set the suggestion score
+        * @param float $score
+        */
+       public function setScore( $score ) {
+               $this->score = $score;
+       }
+
+       /**
+        * Suggestion URL, can be the link to the Title or maybe in the
+        * future a link to the search results for this search suggestion.
+        * @return string Suggestion URL
+        */
+       public function getURL() {
+               return $this->url;
+       }
+
+       /**
+        * Set the suggestion URL
+        * @param string $url
+        */
+       public function setURL( $url ) {
+               $this->url = $url;
+       }
+
+       /**
+        * Create suggestion from Title
+        * @param float $score Suggestions score
+        * @param Title $title
+        * @return SearchSuggestion
+        */
+       public static function fromTitle( $score, Title $title ) {
+               return new self( $score, $title->getPrefixedText(), $title, $title->getArticleID() );
+       }
+
+       /**
+        * Create suggestion from text
+        * Will also create a title if text if not empty.
+        * @param float $score Suggestions score
+        * @param string $text
+        * @return SearchSuggestion
+        */
+       public static function fromText( $score, $text ) {
+               $suggestion = new self( $score, $text );
+               if ( $text ) {
+                       $suggestion->setSuggestedTitle( Title::makeTitle( 0, $text ) );
+               }
+               return $suggestion;
+       }
+
+}
diff --git a/includes/search/SearchSuggestionSet.php b/includes/search/SearchSuggestionSet.php
new file mode 100644 (file)
index 0000000..a1f9a04
--- /dev/null
@@ -0,0 +1,213 @@
+<?php
+
+/**
+ * Search suggestion sets
+ *
+ * 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
+ *
+ */
+
+/**
+ * A set of search suggestions.
+ * The set is always ordered by score, with the best match first.
+ */
+class SearchSuggestionSet {
+       /**
+        * @var SearchSuggestion[]
+        */
+       private $suggestions = array();
+
+       /**
+        *
+        * @var array
+        */
+       private $pageMap = array();
+
+       /**
+        * Builds a new set of suggestions.
+        *
+        * NOTE: the array should be sorted by score (higher is better),
+        * in descending order.
+        * SearchSuggestionSet will not try to re-order this input array.
+        * Providing an unsorted input array is a mistake and will lead to
+        * unexpected behaviors.
+        *
+        * @param SearchSuggestion[] $suggestions (must be sorted by score)
+        */
+       public function __construct( array $suggestions ) {
+               foreach ( $suggestions as $suggestion ) {
+                       $pageID = $suggestion->getSuggestedTitleID();
+                       if ( $pageID && empty( $this->pageMap[$pageID] ) ) {
+                               $this->pageMap[$pageID] = true;
+                       }
+                       $this->suggestions[] = $suggestion;
+               }
+       }
+
+       /**
+        * Get the list of suggestions.
+        * @return SearchSuggestion[]
+        */
+       public function getSuggestions() {
+               return $this->suggestions;
+       }
+
+       /**
+        * Call array_map on the suggestions array
+        * @param callback $callback
+        * @return array
+        */
+       public function map( $callback ) {
+               return array_map( $callback, $this->suggestions );
+       }
+
+       /**
+        * Add a new suggestion at the end.
+        * If the score of the new suggestion is greater than the worst one,
+        * the new suggestion score will be updated (worst - 1).
+        *
+        * @param SearchSuggestion $suggestion
+        */
+       public function append( SearchSuggestion $suggestion ) {
+               $pageID = $suggestion->getSuggestedTitleID();
+               if ( $pageID && isset( $this->pageMap[$pageID] ) ) {
+                       return;
+               }
+               if ( $this->getSize() > 0 && $suggestion->getScore() >= $this->getWorstScore() ) {
+                       $suggestion->setScore( $this->getWorstScore() - 1 );
+               }
+               $this->suggestions[] = $suggestion;
+               if ( $pageID ) {
+                       $this->pageMap[$pageID] = true;
+               }
+       }
+
+       /**
+        * Add suggestion set to the end of the current one.
+        * @param SearchSuggestionSet $set
+        */
+       public function appendAll( SearchSuggestionSet $set ) {
+               foreach ( $set->getSuggestions() as $sugg ) {
+                       $this->append( $sugg );
+               }
+       }
+
+       /**
+        * Move the suggestion at index $key to the first position
+        */
+       public function rescore( $key ) {
+               $removed = array_splice( $this->suggestions, $key, 1 );
+               unset( $this->pageMap[$removed[0]->getSuggestedTitleID()] );
+               $this->prepend( $removed[0] );
+       }
+
+       /**
+        * Add a new suggestion at the top. If the new suggestion score
+        * is lower than the best one its score will be updated (best + 1)
+        * @param SearchSuggestion $suggestion
+        */
+       public function prepend( SearchSuggestion $suggestion ) {
+               $pageID = $suggestion->getSuggestedTitleID();
+               if ( $pageID && isset( $this->pageMap[$pageID] ) ) {
+                       return;
+               }
+               if ( $this->getSize() > 0 && $suggestion->getScore() <= $this->getBestScore() ) {
+                       $suggestion->setScore( $this->getBestScore() + 1 );
+               }
+               array_unshift( $this->suggestions,  $suggestion );
+               if ( $pageID ) {
+                       $this->pageMap[$pageID] = true;
+               }
+       }
+
+       /**
+        * @return float the best score in this suggestion set
+        */
+       public function getBestScore() {
+               if ( empty( $this->suggestions ) ) {
+                       return 0;
+               }
+               return $this->suggestions[0]->getScore();
+       }
+
+       /**
+        * @return float the worst score in this set
+        */
+       public function getWorstScore() {
+               if ( empty( $this->suggestions ) ) {
+                       return 0;
+               }
+               return end( $this->suggestions )->getScore();
+       }
+
+       /**
+        * @return int the number of suggestion in this set
+        */
+       public function getSize() {
+               return count( $this->suggestions );
+       }
+
+       /**
+        * Remove any extra elements in the suggestions set
+        * @param int $limit the max size of this set.
+        */
+       public function shrink( $limit ) {
+               if ( count( $this->suggestions ) > $limit ) {
+                       $this->suggestions = array_slice( $this->suggestions, 0, $limit );
+               }
+       }
+
+       /**
+        * Builds a new set of suggestion based on a title array.
+        * Useful when using a backend that supports only Titles.
+        *
+        * NOTE: Suggestion scores will be generated.
+        *
+        * @param Title[] $titles
+        * @return SearchSuggestionSet
+        */
+       public static function fromTitles( array $titles ) {
+               $score = count( $titles );
+               $suggestions = array_map( function( $title ) use ( &$score ) {
+                       return SearchSuggestion::fromTitle( $score--, $title );
+               }, $titles );
+               return new SearchSuggestionSet( $suggestions );
+       }
+
+       /**
+        * Builds a new set of suggestion based on a string array.
+        *
+        * NOTE: Suggestion scores will be generated.
+        *
+        * @param string[] $titles
+        * @return SearchSuggestionSet
+        */
+       public static function fromStrings( array $titles ) {
+               $score = count( $titles );
+               $suggestions = array_map( function( $title ) use ( &$score ) {
+                       return SearchSuggestion::fromText( $score--, $title );
+               }, $titles );
+               return new SearchSuggestionSet( $suggestions );
+       }
+
+
+       /**
+        * @return SearchSuggestionSet an empty suggestion set
+        */
+       public static function emptySuggestionSet() {
+               return new SearchSuggestionSet( array() );
+       }
+}
index d9c60c7..81c7ebf 100644 (file)
@@ -118,26 +118,44 @@ class BotPasswordSessionProvider extends ImmutableSessionProviderWithCookie {
                        array_keys( $metadata )
                );
                if ( $missingKeys ) {
-                       $this->logger->info( "Session $info: Missing metadata: " . join( ', ', $missingKeys ) );
+                       $this->logger->info( 'Session "{session}": Missing metadata: {missing}', array(
+                               'session' => $info,
+                               'missing' => join( ', ', $missingKeys ),
+                       ) );
                        return false;
                }
 
                $bp = BotPassword::newFromCentralId( $metadata['centralId'], $metadata['appId'] );
                if ( !$bp ) {
                        $this->logger->info(
-                               "Session $info: No BotPassword for {$metadata['centralId']} {$metadata['appId']}"
-                       );
+                               'Session "{session}": No BotPassword for {centralId} {appId}',
+                               array(
+                                       'session' => $info,
+                                       'centralId' => $metadata['centralId'],
+                                       'appId' => $metadata['appId'],
+                       ) );
                        return false;
                }
 
                if ( !hash_equals( $metadata['token'], $bp->getToken() ) ) {
-                       $this->logger->info( "Session $info: BotPassword token check failed" );
+                       $this->logger->info( 'Session "{session}": BotPassword token check failed', array(
+                               'session' => $info,
+                               'centralId' => $metadata['centralId'],
+                               'appId' => $metadata['appId'],
+                       ) );
                        return false;
                }
 
                $status = $bp->getRestrictions()->check( $request );
                if ( !$status->isOk() ) {
-                       $this->logger->info( "Session $info: Restrictions check failed", $status->getValue() );
+                       $this->logger->info(
+                               'Session "{session}": Restrictions check failed',
+                               array(
+                                       'session' => $info,
+                                       'restrictions' => $status->getValue(),
+                                       'centralId' => $metadata['centralId'],
+                                       'appId' => $metadata['appId'],
+                       ) );
                        return false;
                }
 
index f989cbc..f55c589 100644 (file)
@@ -103,15 +103,15 @@ class CookieSessionProvider extends SessionProvider {
        }
 
        public function provideSessionInfo( WebRequest $request ) {
+               $sessionId = $this->getCookie( $request, $this->params['sessionName'], '' );
                $info = array(
-                       'id' => $this->getCookie( $request, $this->params['sessionName'], '' ),
                        'provider' => $this,
                        'forceHTTPS' => $this->getCookie( $request, 'forceHTTPS', '', false )
                );
-               if ( !SessionManager::validateSessionId( $info['id'] ) ) {
-                       unset( $info['id'] );
+               if ( SessionManager::validateSessionId( $sessionId ) ) {
+                       $info['id'] = $sessionId;
+                       $info['persisted'] = true;
                }
-               $info['persisted'] = isset( $info['id'] );
 
                list( $userId, $userName, $token ) = $this->getUserInfoFromCookies( $request );
                if ( $userId !== null ) {
@@ -123,11 +123,28 @@ class CookieSessionProvider extends SessionProvider {
 
                        // Sanity check
                        if ( $userName !== null && $userInfo->getName() !== $userName ) {
+                               $this->logger->warning(
+                                       'Session "{session}" requested with mismatched UserID and UserName cookies.',
+                                       array(
+                                               'session' => $sessionId,
+                                               'mismatch' => array(
+                                                       'userid' => $userId,
+                                                       'cookie_username' => $userName,
+                                                       'username' => $userInfo->getName(),
+                                               ),
+                               ) );
                                return null;
                        }
 
                        if ( $token !== null ) {
                                if ( !hash_equals( $userInfo->getToken(), $token ) ) {
+                                       $this->logger->warning(
+                                               'Session "{session}" requested with invalid Token cookie.',
+                                               array(
+                                                       'session' => $sessionId,
+                                                       'userid' => $userId,
+                                                       'username' => $userInfo->getName(),
+                                        ) );
                                        return null;
                                }
                                $info['userInfo'] = $userInfo->verified();
@@ -140,6 +157,15 @@ class CookieSessionProvider extends SessionProvider {
                        }
                } elseif ( isset( $info['id'] ) ) {
                        // No UserID cookie, so insist that the session is anonymous.
+                       // Note: this event occurs for several normal activities:
+                       // * anon visits Special:UserLogin
+                       // * anon browsing after seeing Special:UserLogin
+                       // * anon browsing after edit or preview
+                       $this->logger->debug(
+                               'Session "{session}" requested without UserID cookie',
+                               array(
+                                       'session' => $info['id'],
+                       ) );
                        $info['userInfo'] = UserInfo::newAnonymous();
                } else {
                        // No session ID and no user is the same as an empty session, so
diff --git a/includes/session/MetadataMergeException.php b/includes/session/MetadataMergeException.php
new file mode 100644 (file)
index 0000000..9f42c27
--- /dev/null
@@ -0,0 +1,70 @@
+<?php
+/**
+ * @section LICENSE
+ * 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 Session
+ */
+
+namespace MediaWiki\Session;
+
+use UnexpectedValueException;
+
+/**
+ * Subclass of UnexpectedValueException that can be annotated with additional
+ * data for debug logging.
+ *
+ * @author Bryan Davis <bd808@wikimedia.org>
+ * @copyright © 2016 Bryan Davis and Wikimedia Foundation.
+ * @since 1.27
+ */
+class MetadataMergeException extends UnexpectedValueException {
+       /** @var array $context */
+       protected $context;
+
+       /**
+        * @param string $message
+        * @param int $code
+        * @param Exception|null $previous
+        * @param array $context Additional context data
+        */
+       public function __construct(
+               $message = '',
+               $code = 0,
+               Exception $previous = null,
+               array $context = []
+       ) {
+               parent::__construct( $message, $code, $previous );
+               $this->context = $context;
+       }
+
+       /**
+        * Get context data.
+        * @return array
+        */
+       public function getContext() {
+               return $this->context;
+       }
+
+       /**
+        * Set context data.
+        * @param array $context
+        */
+       public function setContext( array $context ) {
+               $this->context = $context;
+       }
+}
index d21bea9..7d7e1cb 100644 (file)
@@ -28,13 +28,10 @@ use BagOStuff;
 
 /**
  * Adapter for PHP's session handling
- * @todo Once we drop support for PHP < 5.4, use SessionHandlerInterface
- *  (should just be a matter of adding "implements SessionHandlerInterface" and
- *  changing the session_set_save_handler() call).
  * @ingroup Session
  * @since 1.27
  */
-class PHPSessionHandler {
+class PHPSessionHandler implements \SessionHandlerInterface {
        /** @var PHPSessionHandler */
        protected static $instance = null;
 
@@ -123,23 +120,18 @@ class PHPSessionHandler {
                ini_set( 'session.use_cookies', 0 );
                ini_set( 'session.use_trans_sid', 0 );
 
+               // T124510: Disable automatic PHP session related cache headers.
+               // MediaWiki adds it's own headers and the default PHP behavior may
+               // set headers such as 'Pragma: no-cache' that cause problems with
+               // some user agents.
+               session_cache_limiter( '' );
+
                // Also set a sane serialization handler
                \Wikimedia\PhpSessionSerializer::setSerializeHandler();
 
-               session_set_save_handler(
-                       array( self::$instance, 'open' ),
-                       array( self::$instance, 'close' ),
-                       array( self::$instance, 'read' ),
-                       array( self::$instance, 'write' ),
-                       array( self::$instance, 'destroy' ),
-                       array( self::$instance, 'gc' )
-               );
-
-               // It's necessary to register a shutdown function to call session_write_close(),
-               // because by the time the request shutdown function for the session module is
-               // called, other needed objects may have already been destroyed. Shutdown functions
-               // registered this way are called before object destruction.
-               register_shutdown_function( array( self::$instance, 'handleShutdown' ) );
+               // Register this as the save handler, and register an appropriate
+               // shutdown function.
+               session_set_save_handler( self::$instance, true );
        }
 
        /**
@@ -241,8 +233,10 @@ class PHPSessionHandler {
                        // This can happen under normal circumstances, if the session exists but is
                        // invalid. Let's emit a log warning instead of a PHP warning.
                        $this->logger->warning(
-                               __METHOD__ . ": Session \"$id\" cannot be loaded, skipping write."
-                       );
+                               __METHOD__ . ': Session "{session}" cannot be loaded, skipping write.',
+                               array(
+                                       'session' => $id,
+                       ) );
                        return true;
                }
 
@@ -360,18 +354,4 @@ class PHPSessionHandler {
                $this->store->deleteObjectsExpiringBefore( $before );
                return true;
        }
-
-       /**
-        * Shutdown function.
-        *
-        * See the comment inside self::install for rationale.
-        * @codeCoverageIgnore
-        * @private For internal use only
-        */
-       public function handleShutdown() {
-               if ( $this->enable ) {
-                       session_write_close();
-               }
-       }
-
 }
index 4ad69ae..d654ff1 100644 (file)
@@ -23,6 +23,7 @@
 
 namespace MediaWiki\Session;
 
+use Psr\Log\LoggerInterface;
 use User;
 use WebRequest;
 
@@ -41,24 +42,28 @@ use WebRequest;
  * The Session object also serves as a replacement for PHP's $_SESSION,
  * managing access to per-session data.
  *
- * @todo Once we drop support for PHP 5.3.3, implementing ArrayAccess would be nice.
  * @ingroup Session
  * @since 1.27
  */
-final class Session implements \Countable, \Iterator {
+final class Session implements \Countable, \Iterator, \ArrayAccess {
        /** @var SessionBackend Session backend */
        private $backend;
 
        /** @var int Session index */
        private $index;
 
+       /** @var LoggerInterface */
+       private $logger;
+
        /**
         * @param SessionBackend $backend
         * @param int $index
+        * @param LoggerInterface $logger
         */
-       public function __construct( SessionBackend $backend, $index ) {
+       public function __construct( SessionBackend $backend, $index, LoggerInterface $logger ) {
                $this->backend = $backend;
                $this->index = $index;
+               $this->logger = $logger;
        }
 
        public function __destruct() {
@@ -271,7 +276,7 @@ final class Session implements \Countable, \Iterator {
        /**
         * Fetch a value from the session
         * @param string|int $key
-        * @param mixed $default
+        * @param mixed $default Returned if $this->exists( $key ) would be false
         * @return mixed
         */
        public function get( $key, $default = null ) {
@@ -281,6 +286,7 @@ final class Session implements \Countable, \Iterator {
 
        /**
         * Test if a value exists in the session
+        * @note Unlike isset(), null values are considered to exist.
         * @param string|int $key
         * @return bool
         */
@@ -419,6 +425,39 @@ final class Session implements \Countable, \Iterator {
                return key( $data ) !== null;
        }
 
+       /**
+        * @note Despite the name, this seems to be intended to implement isset()
+        *  rather than array_key_exists(). So do that.
+        */
+       public function offsetExists( $offset ) {
+               $data = &$this->backend->getData();
+               return isset( $data[$offset] );
+       }
+
+       /**
+        * @note This supports indirect modifications but can't mark the session
+        *  dirty when those happen. SessionBackend::save() checks the hash of the
+        *  data to detect such changes.
+        * @note Accessing a nonexistent key via this mechanism causes that key to
+        *  be created with a null value, and does not raise a PHP warning.
+        */
+       public function &offsetGet( $offset ) {
+               $data = &$this->backend->getData();
+               if ( !array_key_exists( $offset, $data ) ) {
+                       $ex = new \Exception( "Undefined index (auto-adds to session with a null value): $offset" );
+                       $this->logger->debug( $ex->getMessage(), array( 'exception' => $ex ) );
+               }
+               return $data[$offset];
+       }
+
+       public function offsetSet( $offset, $value ) {
+               $this->set( $offset, $value );
+       }
+
+       public function offsetUnset( $offset ) {
+               $this->remove( $offset );
+       }
+
        /**@}*/
 
 }
index 2a13ed2..5cf7869 100644 (file)
@@ -23,7 +23,7 @@
 
 namespace MediaWiki\Session;
 
-use BagOStuff;
+use CachedBagOStuff;
 use Psr\Log\LoggerInterface;
 use User;
 use WebRequest;
@@ -64,10 +64,8 @@ final class SessionBackend {
        /** @var string Used to detect subarray modifications */
        private $dataHash = null;
 
-       /** @var BagOStuff */
-       private $tempStore;
-       /** @var BagOStuff */
-       private $permStore;
+       /** @var CachedBagOStuff */
+       private $store;
 
        /** @var LoggerInterface */
        private $logger;
@@ -99,14 +97,12 @@ final class SessionBackend {
        /**
         * @param SessionId $id Session ID object
         * @param SessionInfo $info Session info to populate from
-        * @param BagOStuff $tempStore In-process data store
-        * @param BagOStuff $permstore Backend data store for persisted sessions
+        * @param CachedBagOStuff $store Backend data store
         * @param LoggerInterface $logger
         * @param int $lifetime Session data lifetime in seconds
         */
        public function __construct(
-               SessionId $id, SessionInfo $info, BagOStuff $tempStore, BagOStuff $permStore,
-               LoggerInterface $logger, $lifetime
+               SessionId $id, SessionInfo $info, CachedBagOStuff $store, LoggerInterface $logger, $lifetime
        ) {
                $phpSessionHandling = \RequestContext::getMain()->getConfig()->get( 'PHPSessionHandling' );
                $this->usePhpSessionHandling = $phpSessionHandling !== 'disable';
@@ -125,8 +121,7 @@ final class SessionBackend {
 
                $this->id = $id;
                $this->user = $info->getUserInfo() ? $info->getUserInfo()->getUser() : new User;
-               $this->tempStore = $tempStore;
-               $this->permStore = $permStore;
+               $this->store = $store;
                $this->logger = $logger;
                $this->lifetime = $lifetime;
                $this->provider = $info->getProvider();
@@ -135,14 +130,7 @@ final class SessionBackend {
                $this->forceHTTPS = $info->forceHTTPS();
                $this->providerMetadata = $info->getProviderMetadata();
 
-               $key = wfMemcKey( 'MWSession', (string)$this->id );
-               $blob = $tempStore->get( $key );
-               if ( $blob === false ) {
-                       $blob = $permStore->get( $key );
-                       if ( $blob !== false ) {
-                               $tempStore->set( $key, $blob );
-                       }
-               }
+               $blob = $store->get( wfMemcKey( 'MWSession', (string)$this->id ) );
                if ( !is_array( $blob ) ||
                        !isset( $blob['metadata'] ) || !is_array( $blob['metadata'] ) ||
                        !isset( $blob['data'] ) || !is_array( $blob['data'] )
@@ -150,7 +138,11 @@ final class SessionBackend {
                        $this->data = array();
                        $this->dataDirty = true;
                        $this->metaDirty = true;
-                       $this->logger->debug( "SessionBackend $this->id is unsaved, marking dirty in constructor" );
+                       $this->logger->debug(
+                               'SessionBackend "{session}" is unsaved, marking dirty in constructor',
+                               array(
+                                       'session' => $this->id,
+                       ) );
                } else {
                        $this->data = $blob['data'];
                        if ( isset( $blob['metadata']['loggedOut'] ) ) {
@@ -161,8 +153,10 @@ final class SessionBackend {
                        } else {
                                $this->metaDirty = true;
                                $this->logger->debug(
-                                       "SessionBackend $this->id metadata dirty due to missing expiration timestamp"
-                               );
+                                       'SessionBackend "{session}" metadata dirty due to missing expiration timestamp',
+                               array(
+                                       'session' => $this->id,
+                               ) );
                        }
                }
                $this->dataHash = md5( serialize( $this->data ) );
@@ -176,7 +170,7 @@ final class SessionBackend {
        public function getSession( WebRequest $request ) {
                $index = ++$this->curIndex;
                $this->requests[$index] = $request;
-               $session = new Session( $this, $index );
+               $session = new Session( $this, $index, $this->logger );
                return $session;
        }
 
@@ -230,8 +224,11 @@ final class SessionBackend {
                        $this->provider->sessionIdWasReset( $this, $oldId );
                        $this->metaDirty = true;
                        $this->logger->debug(
-                               "SessionBackend $this->id metadata dirty due to ID reset (formerly $oldId)"
-                       );
+                               'SessionBackend "{session}" metadata dirty due to ID reset (formerly "{oldId}")',
+                               array(
+                                       'session' => $this->id,
+                                       'oldId' => $oldId,
+                       ) );
 
                        if ( $restart ) {
                                session_id( (string)$this->id );
@@ -241,8 +238,7 @@ final class SessionBackend {
                        $this->autosave();
 
                        // Delete the data for the old session ID now
-                       $this->tempStore->delete( wfMemcKey( 'MWSession', $oldId ) );
-                       $this->permStore->delete( wfMemcKey( 'MWSession', $oldId ) );
+                       $this->store->delete( wfMemcKey( 'MWSession', $oldId ) );
                }
        }
 
@@ -276,7 +272,11 @@ final class SessionBackend {
                        $this->persist = true;
                        $this->forcePersist = true;
                        $this->metaDirty = true;
-                       $this->logger->debug( "SessionBackend $this->id force-persist due to persist()" );
+                       $this->logger->debug(
+                               'SessionBackend "{session}" force-persist due to persist()',
+                               array(
+                                       'session' => $this->id,
+                       ) );
                        $this->autosave();
                } else {
                        $this->renew();
@@ -301,7 +301,11 @@ final class SessionBackend {
                if ( $this->remember !== (bool)$remember ) {
                        $this->remember = (bool)$remember;
                        $this->metaDirty = true;
-                       $this->logger->debug( "SessionBackend $this->id metadata dirty due to remember-user change" );
+                       $this->logger->debug(
+                               'SessionBackend "{session}" metadata dirty due to remember-user change',
+                               array(
+                                       'session' => $this->id,
+                       ) );
                        $this->autosave();
                }
        }
@@ -358,7 +362,11 @@ final class SessionBackend {
 
                $this->user = $user;
                $this->metaDirty = true;
-               $this->logger->debug( "SessionBackend $this->id metadata dirty due to user change" );
+               $this->logger->debug(
+                       'SessionBackend "{session}" metadata dirty due to user change',
+                       array(
+                               'session' => $this->id,
+               ) );
                $this->autosave();
        }
 
@@ -390,7 +398,11 @@ final class SessionBackend {
                if ( $this->forceHTTPS !== (bool)$force ) {
                        $this->forceHTTPS = (bool)$force;
                        $this->metaDirty = true;
-                       $this->logger->debug( "SessionBackend $this->id metadata dirty due to force-HTTPS change" );
+                       $this->logger->debug(
+                               'SessionBackend "{session}" metadata dirty due to force-HTTPS change',
+                               array(
+                                       'session' => $this->id,
+                       ) );
                        $this->autosave();
                }
        }
@@ -413,8 +425,10 @@ final class SessionBackend {
                        $this->loggedOut = $ts;
                        $this->metaDirty = true;
                        $this->logger->debug(
-                               "SessionBackend $this->id metadata dirty due to logged-out-timestamp change"
-                       );
+                               'SessionBackend "{session}" metadata dirty due to logged-out-timestamp change',
+                               array(
+                                       'session' => $this->id,
+                       ) );
                        $this->autosave();
                }
        }
@@ -441,8 +455,10 @@ final class SessionBackend {
                        $this->providerMetadata = $metadata;
                        $this->metaDirty = true;
                        $this->logger->debug(
-                               "SessionBackend $this->id metadata dirty due to provider metadata change"
-                       );
+                               'SessionBackend "{session}" metadata dirty due to provider metadata change',
+                               array(
+                                       'session' => $this->id,
+                       ) );
                        $this->autosave();
                }
        }
@@ -474,8 +490,11 @@ final class SessionBackend {
                                $data[$key] = $value;
                                $this->dataDirty = true;
                                $this->logger->debug(
-                                       "SessionBackend $this->id data dirty due to addData(): " . wfGetAllCallers( 5 )
-                               );
+                                       'SessionBackend "{session}" data dirty due to addData(): {callers}',
+                                       array(
+                                               'session' => $this->id,
+                                               'callers' => wfGetAllCallers( 5 ),
+                               ) );
                        }
                }
        }
@@ -487,8 +506,11 @@ final class SessionBackend {
        public function dirty() {
                $this->dataDirty = true;
                $this->logger->debug(
-                       "SessionBackend $this->id data dirty due to dirty(): " . wfGetAllCallers( 5 )
-               );
+                       'SessionBackend "{session}" data dirty due to dirty(): {callers}',
+                       array(
+                               'session' => $this->id,
+                               'callers' => wfGetAllCallers( 5 ),
+               ) );
        }
 
        /**
@@ -501,13 +523,19 @@ final class SessionBackend {
                if ( time() + $this->lifetime / 2 > $this->expires ) {
                        $this->metaDirty = true;
                        $this->logger->debug(
-                               "SessionBackend $this->id metadata dirty for renew(): " . wfGetAllCallers( 5 )
-                       );
+                               'SessionBackend "{callers}" metadata dirty for renew(): {callers}',
+                               array(
+                                       'session' => $this->id,
+                                       'callers' => wfGetAllCallers( 5 ),
+                       ) );
                        if ( $this->persist ) {
                                $this->forcePersist = true;
                                $this->logger->debug(
-                                       "SessionBackend $this->id force-persist for renew(): " . wfGetAllCallers( 5 )
-                               );
+                                       'SessionBackend "{session}" force-persist for renew(): {callers}',
+                                       array(
+                                               'session' => $this->id,
+                                               'callers' => wfGetAllCallers( 5 ),
+                               ) );
                        }
                }
                $this->autosave();
@@ -521,13 +549,11 @@ final class SessionBackend {
         * @return \ScopedCallback When this goes out of scope, a save will be triggered
         */
        public function delaySave() {
-               $that = $this;
                $this->delaySave++;
-               $ref = &$this->delaySave;
-               return new \ScopedCallback( function () use ( $that, &$ref ) {
-                       if ( --$ref <= 0 ) {
-                               $ref = 0;
-                               $that->save();
+               return new \ScopedCallback( function () {
+                       if ( --$this->delaySave <= 0 ) {
+                               $this->delaySave = 0;
+                               $this->save();
                        }
                } );
        }
@@ -548,9 +574,12 @@ final class SessionBackend {
        public function save( $closing = false ) {
                if ( $this->provider->getManager()->isUserSessionPrevented( $this->user->getName() ) ) {
                        $this->logger->debug(
-                               "SessionBackend $this->id not saving, " .
-                                       "user {$this->user} was passed to SessionManager::preventSessionsForUser"
-                       );
+                               'SessionBackend "{session}" not saving, user {user} was ' .
+                               'passed to SessionManager::preventSessionsForUser',
+                               array(
+                                       'session' => $this->id,
+                                       'user' => $this->user,
+                       ) );
                        return;
                }
 
@@ -559,8 +588,11 @@ final class SessionBackend {
                $anon = $this->user->isAnon();
                if ( !$anon && !$this->user->getToken( false ) ) {
                        $this->logger->debug(
-                               "SessionBackend $this->id creating token for user {$this->user} on save"
-                       );
+                               'SessionBackend "{session}" creating token for user {user} on save',
+                               array(
+                                       'session' => $this->id,
+                                       'user' => $this->user,
+                       ) );
                        $this->user->setToken();
                        if ( !wfReadOnly() ) {
                                $this->user->saveSettings();
@@ -572,8 +604,13 @@ final class SessionBackend {
                if ( !$this->metaDirty && !$this->dataDirty &&
                        $this->dataHash !== md5( serialize( $this->data ) )
                ) {
-                       $this->logger->debug( "SessionBackend $this->id data dirty due to hash mismatch, " .
-                               "$this->dataHash !== " . md5( serialize( $this->data ) ) );
+                       $this->logger->debug(
+                               'SessionBackend "{session}" data dirty due to hash mismatch, {expected} !== {got}',
+                               array(
+                                       'session' => $this->id,
+                                       'expected' => $this->dataHash,
+                                       'got' => md5( serialize( $this->data ) ),
+                       ) );
                        $this->dataDirty = true;
                }
 
@@ -581,11 +618,15 @@ final class SessionBackend {
                        return;
                }
 
-               $this->logger->debug( "SessionBackend $this->id save: " .
-                       'dataDirty=' . (int)$this->dataDirty . ' ' .
-                       'metaDirty=' . (int)$this->metaDirty . ' ' .
-                       'forcePersist=' . (int)$this->forcePersist
-               );
+               $this->logger->debug(
+                       'SessionBackend "{session}" save: dataDirty={dataDirty} ' .
+                       'metaDirty={metaDirty} forcePersist={forcePersist}',
+                       array(
+                               'session' => $this->id,
+                               'dataDirty' => (int)$this->dataDirty,
+                               'metaDirty' => (int)$this->metaDirty,
+                               'forcePersist' => (int)$this->forcePersist,
+               ) );
 
                // Persist to the provider, if flagged
                if ( $this->persist && ( $this->metaDirty || $this->forcePersist ) ) {
@@ -626,24 +667,15 @@ final class SessionBackend {
                        }
                }
 
-               $this->tempStore->set(
+               $this->store->set(
                        wfMemcKey( 'MWSession', (string)$this->id ),
                        array(
                                'data' => $this->data,
                                'metadata' => $metadata,
                        ),
-                       $metadata['expires']
+                       $metadata['expires'],
+                       $this->persist ? 0 : CachedBagOStuff::WRITE_CACHE_ONLY
                );
-               if ( $this->persist ) {
-                       $this->permStore->set(
-                               wfMemcKey( 'MWSession', (string)$this->id ),
-                               array(
-                                       'data' => $this->data,
-                                       'metadata' => $metadata,
-                               ),
-                               $metadata['expires']
-                       );
-               }
 
                $this->metaDirty = false;
                $this->dataDirty = false;
@@ -658,17 +690,19 @@ final class SessionBackend {
        private function checkPHPSession() {
                if ( !$this->checkPHPSessionRecursionGuard ) {
                        $this->checkPHPSessionRecursionGuard = true;
-                       $ref = &$this->checkPHPSessionRecursionGuard;
-                       $reset = new \ScopedCallback( function () use ( &$ref ) {
-                               $ref = false;
+                       $reset = new \ScopedCallback( function () {
+                               $this->checkPHPSessionRecursionGuard = false;
                        } );
 
                        if ( $this->usePhpSessionHandling && session_id() === '' && PHPSessionHandler::isEnabled() &&
                                SessionManager::getGlobalSession()->getId() === (string)$this->id
                        ) {
-                               $this->logger->debug( "SessionBackend $this->id: Taking over PHP session" );
+                               $this->logger->debug(
+                                       'SessionBackend "{session}" Taking over PHP session',
+                                       array(
+                                               'session' => $this->id,
+                               ) );
                                session_id( (string)$this->id );
-                               \MediaWiki\quietCall( 'session_cache_limiter', 'private, must-revalidate' );
                                \MediaWiki\quietCall( 'session_start' );
                        }
                }
index 9fe2cdf..ff40aa5 100644 (file)
 
 namespace MediaWiki\Session;
 
-use Psr\Log\LoggerInterface;
-use BagOStuff;
-use WebRequest;
-
 /**
  * Value object returned by SessionProvider
  *
index 4b38a5c..d3b7a2d 100644 (file)
 namespace MediaWiki\Session;
 
 use Psr\Log\LoggerInterface;
+use Psr\Log\LogLevel;
 use BagOStuff;
+use CachedBagOStuff;
 use Config;
 use FauxRequest;
-use Language;
-use Message;
 use User;
 use WebRequest;
 
@@ -54,11 +54,8 @@ final class SessionManager implements SessionManagerInterface {
        /** @var Config */
        private $config;
 
-       /** @var BagOStuff|null */
-       private $tempStore;
-
-       /** @var BagOStuff|null */
-       private $permStore;
+       /** @var CachedBagOStuff|null */
+       private $store;
 
        /** @var SessionProvider[] */
        private $sessionProviders = null;
@@ -162,18 +159,18 @@ final class SessionManager implements SessionManagerInterface {
                        $this->setLogger( \MediaWiki\Logger\LoggerFactory::getInstance( 'session' ) );
                }
 
-               $this->tempStore = new \HashBagOStuff;
                if ( isset( $options['store'] ) ) {
                        if ( !$options['store'] instanceof BagOStuff ) {
                                throw new \InvalidArgumentException(
                                        '$options[\'store\'] must be an instance of BagOStuff'
                                );
                        }
-                       $this->permStore = $options['store'];
+                       $store = $options['store'];
                } else {
-                       $this->permStore = \ObjectCache::getInstance( $this->config->get( 'SessionCacheType' ) );
-                       $this->permStore->setLogger( $this->logger );
+                       $store = \ObjectCache::getInstance( $this->config->get( 'SessionCacheType' ) );
+                       $store->setLogger( $this->logger );
                }
+               $this->store = $store instanceof CachedBagOStuff ? $store : new CachedBagOStuff( $store );
 
                register_shutdown_function( array( $this, 'shutdown' ) );
        }
@@ -206,14 +203,7 @@ final class SessionManager implements SessionManagerInterface {
                // Test this here to provide a better log message for the common case
                // of "no such ID"
                $key = wfMemcKey( 'MWSession', $id );
-               $existing = $this->tempStore->get( $key );
-               if ( $existing === false ) {
-                       $existing = $this->permStore->get( $key );
-                       if ( $existing !== false ) {
-                               $this->tempStore->set( $key, $existing );
-                       }
-               }
-               if ( is_array( $existing ) ) {
+               if ( is_array( $this->store->get( $key ) ) ) {
                        $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array( 'id' => $id, 'idIsSafe' => true ) );
                        if ( $this->loadSessionInfoFromStore( $info, $request ) ) {
                                $session = $this->getSessionFromInfo( $info, $request );
@@ -225,8 +215,11 @@ final class SessionManager implements SessionManagerInterface {
                        try {
                                $session = $this->getEmptySessionInternal( $request, $id );
                        } catch ( \Exception $ex ) {
-                               $this->logger->error( __METHOD__ . ': failed to create empty session: ' .
-                                       $ex->getMessage() );
+                               $this->logger->error( 'Failed to create empty session: {exception}',
+                                       array(
+                                               'method' => __METHOD__,
+                                               'exception' => $ex,
+                               ) );
                                $session = null;
                        }
                }
@@ -251,14 +244,7 @@ final class SessionManager implements SessionManagerInterface {
                        }
 
                        $key = wfMemcKey( 'MWSession', $id );
-                       $existing = $this->tempStore->get( $key );
-                       if ( $existing === false ) {
-                               $existing = $this->permStore->get( $key );
-                               if ( $existing !== false ) {
-                                       $this->tempStore->set( $key, $existing );
-                               }
-                       }
-                       if ( is_array( $existing ) ) {
+                       if ( is_array( $this->store->get( $key ) ) ) {
                                throw new \InvalidArgumentException( 'Session ID already exists' );
                        }
                }
@@ -478,14 +464,21 @@ final class SessionManager implements SessionManagerInterface {
 
                // Checks passed, create the user...
                $from = isset( $_SERVER['REQUEST_URI'] ) ? $_SERVER['REQUEST_URI'] : 'CLI';
-               $logger->info( __METHOD__ . ": creating new user ($userName) - from: $from" );
+               $logger->info( __METHOD__ . ': creating new user ({username}) - from: {url}',
+                       array(
+                               'username' => $userName,
+                               'url' => $from,
+               ) );
 
                try {
                        // Insert the user into the local DB master
                        $status = $user->addToDatabase();
                        if ( !$status->isOK() ) {
                                // @codeCoverageIgnoreStart
-                               $logger->error( __METHOD__ . ': failed with message ' . $status->getWikiText() );
+                               $logger->error( __METHOD__ . ': failed with message ' . $status->getWikiText(),
+                                       array(
+                                               'username' => $userName,
+                               ) );
                                $user->setId( 0 );
                                $user->loadFromId();
                                return false;
@@ -493,7 +486,10 @@ final class SessionManager implements SessionManagerInterface {
                        }
                } catch ( \Exception $ex ) {
                        // @codeCoverageIgnoreStart
-                       $logger->error( __METHOD__ . ': failed with exception ' . $ex->getMessage() );
+                       $logger->error( __METHOD__ . ': failed with exception {exception}', array(
+                               'exception' => $ex,
+                               'username' => $userName,
+                       ) );
                        // Do not keep throwing errors for a while
                        $cache->set( $backoffKey, 1, 600 );
                        // Bubble up error; which should normally trigger DB rollbacks
@@ -536,13 +532,6 @@ final class SessionManager implements SessionManagerInterface {
        public function preventSessionsForUser( $username ) {
                $this->preventUsers[$username] = true;
 
-               // Reset the user's token to kill existing sessions
-               $user = User::newFromName( $username );
-               if ( $user && $user->getToken( false ) ) {
-                       $user->setToken();
-                       $user->saveSettings();
-               }
-
                // Instruct the session providers to kill any other sessions too.
                foreach ( $this->getProviders() as $provider ) {
                        $provider->preventSessionsForUser( $username );
@@ -678,22 +667,17 @@ final class SessionManager implements SessionManagerInterface {
         */
        private function loadSessionInfoFromStore( SessionInfo &$info, WebRequest $request ) {
                $key = wfMemcKey( 'MWSession', $info->getId() );
-               $blob = $this->tempStore->get( $key );
-               if ( $blob === false ) {
-                       $blob = $this->permStore->get( $key );
-                       if ( $blob !== false ) {
-                               $this->tempStore->set( $key, $blob );
-                       }
-               }
+               $blob = $this->store->get( $key );
 
                $newParams = array();
 
                if ( $blob !== false ) {
                        // Sanity check: blob must be an array, if it's saved at all
                        if ( !is_array( $blob ) ) {
-                               $this->logger->warning( "Session $info: Bad data" );
-                               $this->tempStore->delete( $key );
-                               $this->permStore->delete( $key );
+                               $this->logger->warning( 'Session "{session}": Bad data', array(
+                                       'session' => $info,
+                               ) );
+                               $this->store->delete( $key );
                                return false;
                        }
 
@@ -701,9 +685,10 @@ final class SessionManager implements SessionManagerInterface {
                        if ( !isset( $blob['data'] ) || !is_array( $blob['data'] ) ||
                                !isset( $blob['metadata'] ) || !is_array( $blob['metadata'] )
                        ) {
-                               $this->logger->warning( "Session $info: Bad data structure" );
-                               $this->tempStore->delete( $key );
-                               $this->permStore->delete( $key );
+                               $this->logger->warning( 'Session "{session}": Bad data structure', array(
+                                       'session' => $info,
+                               ) );
+                               $this->store->delete( $key );
                                return false;
                        }
 
@@ -717,9 +702,10 @@ final class SessionManager implements SessionManagerInterface {
                                !array_key_exists( 'userToken', $metadata ) ||
                                !array_key_exists( 'provider', $metadata )
                        ) {
-                               $this->logger->warning( "Session $info: Bad metadata" );
-                               $this->tempStore->delete( $key );
-                               $this->permStore->delete( $key );
+                               $this->logger->warning( 'Session "{session}": Bad metadata', array(
+                                       'session' => $info,
+                               ) );
+                               $this->store->delete( $key );
                                return false;
                        }
 
@@ -728,14 +714,21 @@ final class SessionManager implements SessionManagerInterface {
                        if ( $provider === null ) {
                                $newParams['provider'] = $provider = $this->getProvider( $metadata['provider'] );
                                if ( !$provider ) {
-                                       $this->logger->warning( "Session $info: Unknown provider, " . $metadata['provider'] );
-                                       $this->tempStore->delete( $key );
-                                       $this->permStore->delete( $key );
+                                       $this->logger->warning(
+                                               'Session "{session}": Unknown provider ' . $metadata['provider'],
+                                               array(
+                                                       'session' => $info,
+                                               )
+                                       );
+                                       $this->store->delete( $key );
                                        return false;
                                }
                        } elseif ( $metadata['provider'] !== (string)$provider ) {
-                               $this->logger->warning( "Session $info: Wrong provider, " .
-                                       $metadata['provider'] . ' !== ' . $provider );
+                               $this->logger->warning( 'Session "{session}": Wrong provider ' .
+                                       $metadata['provider'] . ' !== ' . $provider,
+                                       array(
+                                               'session' => $info,
+                               ) );
                                return false;
                        }
 
@@ -752,8 +745,14 @@ final class SessionManager implements SessionManagerInterface {
                                                if ( $newProviderMetadata !== $providerMetadata ) {
                                                        $newParams['metadata'] = $newProviderMetadata;
                                                }
-                                       } catch ( \UnexpectedValueException $ex ) {
-                                               $this->logger->warning( "Session $info: Metadata merge failed: " . $ex->getMessage() );
+                                       } catch ( MetadataMergeException $ex ) {
+                                               $this->logger->warning(
+                                                       'Session "{session}": Metadata merge failed: {exception}',
+                                                       array(
+                                                               'session' => $info,
+                                                               'exception' => $ex,
+                                                       ) + $ex->getContext()
+                                               );
                                                return false;
                                        }
                                }
@@ -772,7 +771,10 @@ final class SessionManager implements SessionManagerInterface {
                                                $userInfo = UserInfo::newAnonymous();
                                        }
                                } catch ( \InvalidArgumentException $ex ) {
-                                       $this->logger->error( "Session $info: " . $ex->getMessage() );
+                                       $this->logger->error( 'Session "{session}": {exception}', array(
+                                               'session' => $info,
+                                               'exception' => $ex,
+                                       ) );
                                        return false;
                                }
                                $newParams['userInfo'] = $userInfo;
@@ -781,8 +783,13 @@ final class SessionManager implements SessionManagerInterface {
                                // is no saved ID and the names match.
                                if ( $metadata['userId'] ) {
                                        if ( $metadata['userId'] !== $userInfo->getId() ) {
-                                               $this->logger->warning( "Session $info: User ID mismatch, " .
-                                                       $metadata['userId'] . ' !== ' . $userInfo->getId() );
+                                               $this->logger->warning(
+                                                       'Session "{session}": User ID mismatch, {uid_a} !== {uid_b}',
+                                                       array(
+                                                               'session' => $info,
+                                                               'uid_a' => $metadata['userId'],
+                                                               'uid_b' => $userInfo->getId(),
+                                               ) );
                                                return false;
                                        }
 
@@ -790,24 +797,35 @@ final class SessionManager implements SessionManagerInterface {
                                        if ( $metadata['userName'] !== null &&
                                                $userInfo->getName() !== $metadata['userName']
                                        ) {
-                                               $this->logger->warning( "Session $info: User ID matched but name didn't (rename?), " .
-                                                       $metadata['userName'] . ' !== ' . $userInfo->getName() );
+                                               $this->logger->warning(
+                                                       'Session "{session}": User ID matched but name didn\'t (rename?), {uname_a} !== {uname_b}',
+                                                       array(
+                                                               'session' => $info,
+                                                               'uname_a' => $metadata['userName'],
+                                                               'uname_b' => $userInfo->getName(),
+                                               ) );
                                                return false;
                                        }
 
                                } elseif ( $metadata['userName'] !== null ) { // Shouldn't happen, but just in case
                                        if ( $metadata['userName'] !== $userInfo->getName() ) {
-                                               $this->logger->warning( "Session $info: User name mismatch, " .
-                                                       $metadata['userName'] . ' !== ' . $userInfo->getName() );
+                                               $this->logger->warning(
+                                                       'Session "{session}": User name mismatch, {uname_a} !== {uname_b}',
+                                                       array(
+                                                               'session' => $info,
+                                                               'uname_a' => $metadata['userName'],
+                                                               'uname_b' => $userInfo->getName(),
+                                               ) );
                                                return false;
                                        }
                                } elseif ( !$userInfo->isAnon() ) {
                                        // Metadata specifies an anonymous user, but the passed-in
                                        // user isn't anonymous.
                                        $this->logger->warning(
-                                               "Session $info: Metadata has an anonymous user, " .
-                                                       'but a non-anon user was provided'
-                                       );
+                                               'Session "{session}": Metadata has an anonymous user, but a non-anon user was provided',
+                                               array(
+                                                       'session' => $info,
+                                       ) );
                                        return false;
                                }
                        }
@@ -816,7 +834,9 @@ final class SessionManager implements SessionManagerInterface {
                        if ( $metadata['userToken'] !== null &&
                                $userInfo->getToken() !== $metadata['userToken']
                        ) {
-                               $this->logger->warning( "Session $info: User token mismatch" );
+                               $this->logger->warning( 'Session "{session}": User token mismatch', array(
+                                       'session' => $info,
+                               ) );
                                return false;
                        }
                        if ( !$userInfo->isVerified() ) {
@@ -839,7 +859,11 @@ final class SessionManager implements SessionManagerInterface {
                } else {
                        // No metadata, so we can't load the provider if one wasn't given.
                        if ( $info->getProvider() === null ) {
-                               $this->logger->warning( "Session $info: Null provider and no metadata" );
+                               $this->logger->warning(
+                                       'Session "{session}": Null provider and no metadata',
+                                       array(
+                                               'session' => $info,
+                               ) );
                                return false;
                        }
 
@@ -849,14 +873,18 @@ final class SessionManager implements SessionManagerInterface {
                                        $newParams['userInfo'] = UserInfo::newAnonymous();
                                } else {
                                        $this->logger->info(
-                                               "Session $info: No user provided and provider cannot set user"
-                                       );
+                                               'Session "{session}": No user provided and provider cannot set user',
+                                               array(
+                                                       'session' => $info,
+                                       ) );
                                        return false;
                                }
                        } elseif ( !$info->getUserInfo()->isVerified() ) {
                                $this->logger->warning(
-                                       "Session $info: Unverified user provided and no metadata to auth it"
-                               );
+                                       'Session "{session}": Unverified user provided and no metadata to auth it',
+                                       array(
+                                               'session' => $info,
+                               ) );
                                return false;
                        }
 
@@ -896,7 +924,9 @@ final class SessionManager implements SessionManagerInterface {
                        'SessionCheckInfo',
                        array( &$reason, $info, $request, $metadata, $data )
                ) ) {
-                       $this->logger->warning( "Session $info: $reason" );
+                       $this->logger->warning( 'Session "{session}": ' . $reason, array(
+                               'session' => $info,
+                       ) );
                        return false;
                }
 
@@ -921,8 +951,7 @@ final class SessionManager implements SessionManagerInterface {
                        $backend = new SessionBackend(
                                $this->allSessionIds[$id],
                                $info,
-                               $this->tempStore,
-                               $this->permStore,
+                               $this->store,
                                $this->logger,
                                $this->config->get( 'ObjectCacheSessionExpiry' )
                        );
@@ -999,9 +1028,7 @@ final class SessionManager implements SessionManagerInterface {
                do {
                        $id = wfBaseConvert( \MWCryptRand::generateHex( 40 ), 16, 32, 32 );
                        $key = wfMemcKey( 'MWSession', $id );
-               } while ( isset( $this->allSessionIds[$id] ) ||
-                       is_array( $this->tempStore->get( $key ) ) || is_array( $this->permStore->get( $key ) )
-               );
+               } while ( isset( $this->allSessionIds[$id] ) || is_array( $this->store->get( $key ) ) );
                return $id;
        }
 
@@ -1011,7 +1038,7 @@ final class SessionManager implements SessionManagerInterface {
         * @param PHPSessionHandler $handler
         */
        public function setupPHPSessionHandler( PHPSessionHandler $handler ) {
-               $handler->setManager( $this, $this->permStore, $this->logger );
+               $handler->setManager( $this, $this->store, $this->logger );
        }
 
        /**
@@ -1028,6 +1055,96 @@ final class SessionManager implements SessionManagerInterface {
                self::$globalSessionRequest = null;
        }
 
+       /**
+        * Do a sanity check to make sure the session is not used from many different IP addresses
+        * and store some data for later sanity checks.
+        * FIXME remove this once SessionManager is considered stable
+        * @private For use in Setup.php only
+        * @param Session $session Defaults to the global session.
+        */
+       public function checkIpLimits( Session $session = null ) {
+               $session = $session ?: self::getGlobalSession();
+
+               try {
+                       $ip = $session->getRequest()->getIP();
+               } catch ( \MWException $e ) {
+                       return;
+               }
+               if ( $ip === '127.0.0.1' || \IP::isConfiguredProxy( $ip ) ) {
+                       return;
+               }
+               $now = time();
+
+               // Record (and possibly log) that the IP is using the current session.
+               // Don't touch the stored data unless we are adding a new IP or re-adding an expired one.
+               // This is slightly inaccurate (when an existing IP is seen again, the expiry is not
+               // extended) but that shouldn't make much difference and limits the session write frequency
+               // to # of IPs / $wgSuspiciousIpExpiry.
+               $data = $session->get( 'SessionManager-ip', array() );
+               if (
+                       !isset( $data[$ip] )
+                       || $data[$ip] < $now
+               ) {
+                       $data[$ip] = time() + $this->config->get( 'SuspiciousIpExpiry' );
+                       foreach ( $data as $key => $expires ) {
+                               if ( $expires < $now ) {
+                                       unset( $data[$key] );
+                               }
+                       }
+                       $session->set( 'SessionManager-ip', $data );
+
+                       $logger = \MediaWiki\Logger\LoggerFactory::getInstance( 'session-ip' );
+                       $logLevel = count( $data ) >= $this->config->get( 'SuspiciousIpPerSessionLimit' )
+                               ? LogLevel::WARNING : ( count( $data ) === 1 ? LogLevel::DEBUG : LogLevel::INFO );
+                       $logger->log(
+                               $logLevel,
+                               'Same session used from {count} IPs',
+                               array(
+                                       'count' => count( $data ),
+                                       'ips' => $data,
+                                       'session' => $session->getId(),
+                                       'user' => $session->getUser()->getName(),
+                                       'persistent' => $session->isPersistent(),
+                               )
+                       );
+               }
+
+               // Now do the same thing globally for the current user.
+               // We are using the object cache and assume it is shared between all wikis of a farm,
+               // and further assume that the same name belongs to the same user on all wikis. (It's either
+               // that or a central ID lookup which would mean an extra SQL query on every request.)
+               if ( $session->getUser()->isLoggedIn() ) {
+                       $userKey = 'SessionManager-ip:' . md5( $session->getUser()->getName() );
+                       $data = $this->store->get( $userKey ) ?: array();
+                       if (
+                               !isset( $data[$ip] )
+                               || $data[$ip] < $now
+                       ) {
+                               $data[$ip] = time() + $this->config->get( 'SuspiciousIpExpiry' );
+                               foreach ( $data as $key => $expires ) {
+                                       if ( $expires < $now ) {
+                                               unset( $data[$key] );
+                                       }
+                               }
+                               $this->store->set( $userKey, $data, $this->config->get( 'SuspiciousIpExpiry' ) );
+                               $logger = \MediaWiki\Logger\LoggerFactory::getInstance( 'session-ip' );
+                               $logLevel = count( $data ) >= $this->config->get( 'SuspiciousIpPerUserLimit' )
+                                       ? LogLevel::WARNING : ( count( $data ) === 1 ? LogLevel::DEBUG : LogLevel::INFO );
+                               $logger->log(
+                                       $logLevel,
+                                       'Same user had sessions from {count} IPs',
+                                       array(
+                                               'count' => count( $data ),
+                                               'ips' => $data,
+                                               'session' => $session->getId(),
+                                               'user' => $session->getUser()->getName(),
+                                               'persistent' => $session->isPersistent(),
+                                       )
+                               );
+                       }
+               }
+       }
+
        /**@}*/
 
 }
index 0fd3a71..db6294d 100644 (file)
@@ -186,12 +186,17 @@ abstract class SessionProvider implements SessionProviderInterface, LoggerAwareI
         * @param array $savedMetadata Saved provider metadata
         * @param array $providedMetadata Provided provider metadata
         * @return array Resulting metadata
-        * @throws \UnexpectedValueException If the metadata cannot be merged
+        * @throws MetadataMergeException If the metadata cannot be merged
         */
        public function mergeMetadata( array $savedMetadata, array $providedMetadata ) {
                foreach ( $providedMetadata as $k => $v ) {
                        if ( array_key_exists( $k, $savedMetadata ) && $savedMetadata[$k] !== $v ) {
-                               throw new \UnexpectedValueException( "Key \"$k\" changed" );
+                               $e = new MetadataMergeException( "Key \"$k\" changed" );
+                               $e->setContext( [
+                                       'old_value' => $savedMetadata[$k],
+                                       'new_value' => $v,
+                               ] );
+                               throw $e;
                        }
                }
                return $providedMetadata;
index 143b621..85b17f9 100644 (file)
@@ -574,7 +574,7 @@ abstract class BaseTemplate extends QuickTemplate {
         * display the text from footericons instead of the images and don't want a
         * duplicate copyright statement because footerlinks already rendered one.
         * @param string $option
-        * @return string
+        * @return array
         */
        function getFooterIcons( $option = null ) {
                // Generate additional footer icons
index 27e645a..bb26cf3 100644 (file)
@@ -336,13 +336,12 @@ abstract class QueryPage extends SpecialPage {
                                        );
                                }
 
-                               $that = $this;
                                $dbw->doAtomicSection(
                                        __METHOD__,
-                                       function ( IDatabase $dbw, $fname ) use ( $that, $vals ) {
+                                       function ( IDatabase $dbw, $fname ) use ( $vals ) {
                                                # Clear out any old cached data
                                                $dbw->delete( 'querycache',
-                                                       array( 'qc_type' => $that->getName() ),
+                                                       array( 'qc_type' => $this->getName() ),
                                                        $fname
                                                );
                                                # Save results into the querycache table on the master
@@ -351,11 +350,11 @@ abstract class QueryPage extends SpecialPage {
                                                }
                                                # Update the querycache_info record for the page
                                                $dbw->delete( 'querycache_info',
-                                                       array( 'qci_type' => $that->getName() ),
+                                                       array( 'qci_type' => $this->getName() ),
                                                        $fname
                                                );
                                                $dbw->insert( 'querycache_info',
-                                                       array( 'qci_type' => $that->getName(),
+                                                       array( 'qci_type' => $this->getName(),
                                                                'qci_timestamp' => $dbw->timestamp() ),
                                                        $fname
                                                );
index 0417146..6158df2 100644 (file)
@@ -328,6 +328,29 @@ class SpecialPage {
                return array();
        }
 
+       /**
+        * Perform a regular substring search for prefixSearchSubpages
+        * @param string $search Prefix to search for
+        * @param int $limit Maximum number of results to return (usually 10)
+        * @param int $offset Number of results to skip (usually 0)
+        * @return string[] Matching subpages
+        */
+       protected function prefixSearchString( $search, $limit, $offset ) {
+               $title = Title::newFromText( $search );
+               if ( !$title || !$title->canExist() ) {
+                       // No prefix suggestion in special and media namespace
+                       return array();
+               }
+
+               $search = SearchEngine::create();
+               $search->setLimitOffset( $limit, $offset );
+               $search->setNamespaces( array() );
+               $result = $search->defaultPrefixSearch( $search );
+               return array_map( function( Title $t ) {
+                       return $t->getPrefixedText();
+               }, $result );
+       }
+
        /**
         * Helper function for implementations of prefixSearchSubpages() that
         * filter the values in memory (as opposed to making a query).
index 2e764ba..030e7e5 100644 (file)
@@ -124,6 +124,7 @@ class SpecialPageFactory {
                'ListDuplicatedFiles' => 'ListDuplicatedFilesPage',
 
                // Data and tools
+               'ApiSandbox' => 'SpecialApiSandbox',
                'Statistics' => 'SpecialStatistics',
                'Allmessages' => 'SpecialAllMessages',
                'Version' => 'SpecialVersion',
index 1f369d8..cbe61bc 100644 (file)
@@ -279,11 +279,12 @@ class SpecialActiveUsers extends SpecialPage {
 
                // Mention the level of cache staleness...
                $dbr = wfGetDB( DB_SLAVE, 'recentchanges' );
-               $rcMax = $dbr->selectField( 'recentchanges', 'MAX(rc_timestamp)' );
+               $rcMax = $dbr->selectField( 'recentchanges', 'MAX(rc_timestamp)', '', __METHOD__ );
                if ( $rcMax ) {
                        $cTime = $dbr->selectField( 'querycache_info',
                                'qci_timestamp',
-                               array( 'qci_type' => 'activeusers' )
+                               array( 'qci_type' => 'activeusers' ),
+                               __METHOD__
                        );
                        if ( $cTime ) {
                                $secondsOld = wfTimestamp( TS_UNIX, $rcMax ) - wfTimestamp( TS_UNIX, $cTime );
index 9e75522..0bf93be 100644 (file)
@@ -365,15 +365,7 @@ class SpecialAllPages extends IncludableSpecialPage {
         * @return string[] Matching subpages
         */
        public function prefixSearchSubpages( $search, $limit, $offset ) {
-               $title = Title::newFromText( $search );
-               if ( !$title || !$title->canExist() ) {
-                       // No prefix suggestion in special and media namespace
-                       return array();
-               }
-               // Autocomplete subpage the same as a normal search
-               $prefixSearcher = new StringPrefixSearch;
-               $result = $prefixSearcher->search( $search, $limit, array(), $offset );
-               return $result;
+               return $this->prefixSearchString( $search, $limit, $offset );
        }
 
        protected function getGroupName() {
diff --git a/includes/specials/SpecialApiSandbox.php b/includes/specials/SpecialApiSandbox.php
new file mode 100644 (file)
index 0000000..42101ba
--- /dev/null
@@ -0,0 +1,58 @@
+<?php
+/**
+ * Implements Special:ApiSandbox
+ *
+ * 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 SpecialPage
+ */
+
+/**
+ * @ingroup SpecialPage
+ * @since 1.27
+ */
+class SpecialApiSandbox extends SpecialPage {
+       public function __construct() {
+               parent::__construct( 'ApiSandbox' );
+       }
+
+       public function execute( $par ) {
+               $this->setHeaders();
+               $out = $this->getOutput();
+
+               if ( !$this->getConfig()->get( 'EnableAPI' ) ) {
+                       $out->showErrorPage( 'error', 'apisandbox-api-disabled' );
+               }
+
+               $out->addJsConfigVars( 'apihighlimits', $this->getUser()->isAllowed( 'apihighlimits' ) );
+               $out->addModuleStyles( array(
+                       'mediawiki.special.apisandbox.styles',
+               ) );
+               $out->addModules( array(
+                       'mediawiki.special.apisandbox',
+                       'mediawiki.apipretty',
+               ) );
+               $out->wrapWikiMsg(
+                       "<div id='mw-apisandbox'><div class='mw-apisandbox-nojs error'>\n$1\n</div></div>",
+                       'apisandbox-jsonly'
+               );
+       }
+
+       protected function getGroupName() {
+               return 'wiki';
+       }
+}
index a9a7f97..1f32e3f 100644 (file)
@@ -234,15 +234,7 @@ class SpecialChangeContentModel extends FormSpecialPage {
         * @return string[] Matching subpages
         */
        public function prefixSearchSubpages( $search, $limit, $offset ) {
-               $title = Title::newFromText( $search );
-               if ( !$title || !$title->canExist() ) {
-                       // No prefix suggestion in special and media namespace
-                       return array();
-               }
-               // Autocomplete subpage the same as a normal search
-               $prefixSearcher = new StringPrefixSearch;
-               $result = $prefixSearcher->search( $search, $limit, array(), $offset );
-               return $result;
+               return $this->prefixSearchString( $search, $limit, $offset );
        }
 
        protected function getGroupName() {
index ab6614b..0e3c9aa 100644 (file)
@@ -571,8 +571,10 @@ class SpecialContributions extends IncludableSpecialPage {
                                )
                );
 
+               $filters = array();
+
                if ( $this->getUser()->isAllowed( 'deletedhistory' ) ) {
-                       $deletedOnlyCheck = Html::rawElement(
+                       $filters[] = Html::rawElement(
                                'span',
                                array( 'class' => 'mw-input-with-label' ),
                                Xml::checkLabel(
@@ -583,11 +585,9 @@ class SpecialContributions extends IncludableSpecialPage {
                                        array( 'class' => 'mw-input' )
                                )
                        );
-               } else {
-                       $deletedOnlyCheck = '';
                }
 
-               $checkLabelTopOnly = Html::rawElement(
+               $filters[] = Html::rawElement(
                        'span',
                        array( 'class' => 'mw-input-with-label' ),
                        Xml::checkLabel(
@@ -598,7 +598,7 @@ class SpecialContributions extends IncludableSpecialPage {
                                array( 'class' => 'mw-input' )
                        )
                );
-               $checkLabelNewOnly = Html::rawElement(
+               $filters[] = Html::rawElement(
                        'span',
                        array( 'class' => 'mw-input-with-label' ),
                        Xml::checkLabel(
@@ -609,10 +609,16 @@ class SpecialContributions extends IncludableSpecialPage {
                                array( 'class' => 'mw-input' )
                        )
                );
+
+               Hooks::run(
+                       'SpecialContributions::getForm::filters',
+                       array( $this, &$filters )
+               );
+
                $extraOptions = Html::rawElement(
                        'td',
                        array( 'colspan' => 2 ),
-                       $deletedOnlyCheck . $checkLabelTopOnly . $checkLabelNewOnly
+                       implode( '', $filters )
                );
 
                $dateSelectionAndSubmit = Xml::tags( 'td', array( 'colspan' => 2 ),
@@ -1122,7 +1128,8 @@ class ContribsPager extends ReverseChronologicalPager {
                        # Tags, if any.
                        list( $tagSummary, $newClasses ) = ChangeTags::formatSummaryRow(
                                $row->ts_tags,
-                               'contributions'
+                               'contributions',
+                               $this->getContext()
                        );
                        $classes = array_merge( $classes, $newClasses );
                        $ret .= " $tagSummary";
index 3ce9c76..f9b5050 100644 (file)
@@ -418,6 +418,8 @@ class SpecialExport extends SpecialPage {
        private function getPagesFromCategory( $title ) {
                global $wgContLang;
 
+               $maxPages = $this->getConfig()->get( 'ExportPagelistLimit' );
+
                $name = $title->getDBkey();
 
                $dbr = wfGetDB( DB_SLAVE );
@@ -426,7 +428,7 @@ class SpecialExport extends SpecialPage {
                        array( 'page_namespace', 'page_title' ),
                        array( 'cl_from=page_id', 'cl_to' => $name ),
                        __METHOD__,
-                       array( 'LIMIT' => '5000' )
+                       array( 'LIMIT' => $maxPages )
                );
 
                $pages = array();
@@ -451,13 +453,15 @@ class SpecialExport extends SpecialPage {
        private function getPagesFromNamespace( $nsindex ) {
                global $wgContLang;
 
+               $maxPages = $this->getConfig()->get( 'ExportPagelistLimit' );
+
                $dbr = wfGetDB( DB_SLAVE );
                $res = $dbr->select(
                        'page',
                        array( 'page_namespace', 'page_title' ),
                        array( 'page_namespace' => $nsindex ),
                        __METHOD__,
-                       array( 'LIMIT' => '5000' )
+                       array( 'LIMIT' => $maxPages )
                );
 
                $pages = array();
index 323903e..9970dfa 100644 (file)
@@ -246,9 +246,11 @@ class FileDuplicateSearchPage extends QueryPage {
                        // No prefix suggestion outside of file namespace
                        return array();
                }
+               $search = SearchEngine::create();
+               $search->setLimitOffset( $limit, $offset );
                // Autocomplete subpage the same as a normal search, but just for files
-               $prefixSearcher = new TitlePrefixSearch;
-               $result = $prefixSearcher->search( $search, $limit, array( NS_FILE ), $offset );
+               $search->setNamespaces( array( NS_FILE ) );
+               $result = $search->defaultPrefixSearch( $search );
 
                return array_map( function ( Title $t ) {
                        // Remove namespace in search suggestion
index 28ff1c7..a164c1e 100644 (file)
@@ -262,15 +262,15 @@ class SpecialLog extends SpecialPage {
                // Select: All, None, Invert
                $links = array();
                $links[] = Html::element(
-                       'a', array( 'href' => '#', 'id' => 'checkbox-all' ),
+                       'a', array( 'href' => '#', 'class' => 'mw-checkbox-all' ),
                        $this->msg( 'checkbox-all' )->text()
                );
                $links[] = Html::element(
-                       'a', array( 'href' => '#', 'id' => 'checkbox-none' ),
+                       'a', array( 'href' => '#', 'class' => 'mw-checkbox-none' ),
                        $this->msg( 'checkbox-none' )->text()
                );
                $links[] = Html::element(
-                       'a', array( 'href' => '#', 'id' => 'checkbox-invert' ),
+                       'a', array( 'href' => '#', 'class' => 'mw-checkbox-invert' ),
                        $this->msg( 'checkbox-invert' )->text()
                );
 
index 0a25180..0cefb38 100644 (file)
@@ -347,138 +347,17 @@ class SpecialMergeHistory extends SpecialPage {
                if ( $targetTitle->getArticleID() == $destTitle->getArticleID() ) {
                        return false;
                }
-               # Verify that this timestamp is valid
-               # Must be older than the destination page
-               $dbw = wfGetDB( DB_MASTER );
-               # Get timestamp into DB format
-               $this->mTimestamp = $this->mTimestamp ? $dbw->timestamp( $this->mTimestamp ) : '';
-               # Max timestamp should be min of destination page
-               $maxtimestamp = $dbw->selectField(
-                       'revision',
-                       'MIN(rev_timestamp)',
-                       array( 'rev_page' => $this->mDestID ),
-                       __METHOD__
-               );
-               # Destination page must exist with revisions
-               if ( !$maxtimestamp ) {
-                       $this->getOutput()->addWikiMsg( 'mergehistory-fail' );
 
-                       return false;
-               }
-               # Get the latest timestamp of the source
-               $lasttimestamp = $dbw->selectField(
-                       array( 'page', 'revision' ),
-                       'rev_timestamp',
-                       array( 'page_id' => $this->mTargetID, 'page_latest = rev_id' ),
-                       __METHOD__
-               );
-               # $this->mTimestamp must be older than $maxtimestamp
-               if ( $this->mTimestamp >= $maxtimestamp ) {
-                       $this->getOutput()->addWikiMsg( 'mergehistory-fail' );
+               // MergeHistory object
+               $mh = new MergeHistory( $targetTitle, $destTitle, $this->mTimestamp );
 
+               // Merge!
+               $mergeStatus = $mh->merge( $this->getUser(), $this->mComment );
+               if ( !$mergeStatus->isOK() ) {
+                       // Failed merge
+                       $this->getOutput()->addWikiMsg( $mergeStatus->getMessage() );
                        return false;
                }
-               # Get the timestamp pivot condition
-               if ( $this->mTimestamp ) {
-                       $timewhere = "rev_timestamp <= {$this->mTimestamp}";
-                       $timestampLimit = wfTimestamp( TS_MW, $this->mTimestamp );
-               } else {
-                       $timewhere = "rev_timestamp <= {$maxtimestamp}";
-                       $timestampLimit = wfTimestamp( TS_MW, $lasttimestamp );
-               }
-               # Check that there are not too many revisions to move
-               $limit = 5000; // avoid too much slave lag
-               $count = $dbw->selectRowCount( 'revision', '1',
-                       array( 'rev_page' => $this->mTargetID, $timewhere ),
-                       __METHOD__,
-                       array( 'LIMIT' => $limit + 1 )
-               );
-               if ( $count > $limit ) {
-                       $this->getOutput()->addWikiMsg( 'mergehistory-fail-toobig' );
-
-                       return false;
-               }
-               # Do the moving...
-               $dbw->update(
-                       'revision',
-                       array( 'rev_page' => $this->mDestID ),
-                       array( 'rev_page' => $this->mTargetID, $timewhere ),
-                       __METHOD__
-               );
-
-               $count = $dbw->affectedRows();
-               # Make the source page a redirect if no revisions are left
-               $haveRevisions = $dbw->selectField(
-                       'revision',
-                       'rev_timestamp',
-                       array( 'rev_page' => $this->mTargetID ),
-                       __METHOD__,
-                       array( 'FOR UPDATE' )
-               );
-               if ( !$haveRevisions ) {
-                       if ( $this->mComment ) {
-                               $comment = $this->msg(
-                                       'mergehistory-comment',
-                                       $targetTitle->getPrefixedText(),
-                                       $destTitle->getPrefixedText(),
-                                       $this->mComment
-                               )->inContentLanguage()->text();
-                       } else {
-                               $comment = $this->msg(
-                                       'mergehistory-autocomment',
-                                       $targetTitle->getPrefixedText(),
-                                       $destTitle->getPrefixedText()
-                               )->inContentLanguage()->text();
-                       }
-
-                       $contentHandler = ContentHandler::getForTitle( $targetTitle );
-                       $redirectContent = $contentHandler->makeRedirectContent( $destTitle );
-
-                       if ( $redirectContent ) {
-                               $redirectPage = WikiPage::factory( $targetTitle );
-                               $redirectRevision = new Revision( array(
-                                       'title' => $targetTitle,
-                                       'page' => $this->mTargetID,
-                                       'comment' => $comment,
-                                       'content' => $redirectContent ) );
-                               $redirectRevision->insertOn( $dbw );
-                               $redirectPage->updateRevisionOn( $dbw, $redirectRevision );
-
-                               # Now, we record the link from the redirect to the new title.
-                               # It should have no other outgoing links...
-                               $dbw->delete( 'pagelinks', array( 'pl_from' => $this->mDestID ), __METHOD__ );
-                               $dbw->insert( 'pagelinks',
-                                       array(
-                                               'pl_from' => $this->mDestID,
-                                               'pl_from_namespace' => $destTitle->getNamespace(),
-                                               'pl_namespace' => $destTitle->getNamespace(),
-                                               'pl_title' => $destTitle->getDBkey() ),
-                                       __METHOD__
-                               );
-                       } else {
-                               // would be nice to show a warning if we couldn't create a redirect
-                       }
-               } else {
-                       $targetTitle->invalidateCache(); // update histories
-               }
-               $destTitle->invalidateCache(); // update histories
-               # Check if this did anything
-               if ( !$count ) {
-                       $this->getOutput()->addWikiMsg( 'mergehistory-fail' );
-
-                       return false;
-               }
-               # Update our logs
-               $logEntry = new ManualLogEntry( 'merge', 'merge' );
-               $logEntry->setPerformer( $this->getUser() );
-               $logEntry->setComment( $this->mComment );
-               $logEntry->setTarget( $targetTitle );
-               $logEntry->setParameters( array(
-                       '4::dest' => $destTitle->getPrefixedText(),
-                       '5::mergepoint' => $timestampLimit
-               ) );
-               $logId = $logEntry->insert();
-               $logEntry->publish( $logId );
 
                $targetLink = Linker::link(
                        $targetTitle,
@@ -490,11 +369,9 @@ class SpecialMergeHistory extends SpecialPage {
                $this->getOutput()->addWikiMsg( $this->msg( 'mergehistory-done' )
                        ->rawParams( $targetLink )
                        ->params( $destTitle->getPrefixedText() )
-                       ->numParams( $count )
+                       ->numParams( $mh->getMergedRevisionCount() )
                );
 
-               Hooks::run( 'ArticleMergeComplete', array( $targetTitle, $destTitle ) );
-
                return true;
        }
 
index a7e5e02..339c1d9 100644 (file)
@@ -820,15 +820,7 @@ class MovePageForm extends UnlistedSpecialPage {
         * @return string[] Matching subpages
         */
        public function prefixSearchSubpages( $search, $limit, $offset ) {
-               $title = Title::newFromText( $search );
-               if ( !$title || !$title->canExist() ) {
-                       // No prefix suggestion in special and media namespace
-                       return array();
-               }
-               // Autocomplete subpage the same as a normal search
-               $prefixSearcher = new StringPrefixSearch;
-               $result = $prefixSearcher->search( $search, $limit, array(), $offset );
-               return $result;
+               return $this->prefixSearchString( $search, $limit, $offset );
        }
 
        protected function getGroupName() {
index 5d3700d..e24764a 100644 (file)
@@ -359,7 +359,8 @@ class SpecialNewpages extends IncludableSpecialPage {
                if ( isset( $result->ts_tags ) ) {
                        list( $tagDisplay, $newClasses ) = ChangeTags::formatSummaryRow(
                                $result->ts_tags,
-                               'newpages'
+                               'newpages',
+                               $this->getContext()
                        );
                        $classes = array_merge( $classes, $newClasses );
                } else {
index 69a9d48..20af655 100644 (file)
@@ -122,8 +122,7 @@ class SpecialPageLanguage extends FormSpecialPage {
                }
 
                // Get the default language for the wiki
-               // Returns the default since the page is not loaded from DB
-               $defLang = $title->getPageLanguage()->getCode();
+               $defLang = $this->getConfig()->get( 'LanguageCode' );
 
                $pageId = $title->getArticleID();
 
@@ -214,15 +213,7 @@ class SpecialPageLanguage extends FormSpecialPage {
         * @return string[] Matching subpages
         */
        public function prefixSearchSubpages( $search, $limit, $offset ) {
-               $title = Title::newFromText( $search );
-               if ( !$title || !$title->canExist() ) {
-                       // No prefix suggestion in special and media namespace
-                       return array();
-               }
-               // Autocomplete subpage the same as a normal search
-               $prefixSearcher = new StringPrefixSearch;
-               $result = $prefixSearcher->search( $search, $limit, array(), $offset );
-               return $result;
+               return $this->prefixSearchString( $search, $limit, $offset );
        }
 
        protected function getGroupName() {
index a6c0423..6401063 100644 (file)
@@ -303,15 +303,7 @@ class SpecialPrefixindex extends SpecialAllPages {
         * @return string[] Matching subpages
         */
        public function prefixSearchSubpages( $search, $limit, $offset ) {
-               $title = Title::newFromText( $search );
-               if ( !$title || !$title->canExist() ) {
-                       // No prefix suggestion in special and media namespace
-                       return array();
-               }
-               // Autocomplete subpage the same as a normal search
-               $prefixSearcher = new StringPrefixSearch;
-               $result = $prefixSearcher->search( $search, $limit, array(), $offset );
-               return $result;
+               return $this->prefixSearchString( $search, $limit, $offset );
        }
 
        protected function getGroupName() {
index 8db8f24..dc210db 100644 (file)
@@ -273,14 +273,6 @@ class SpecialRecentChangesLinked extends SpecialRecentChanges {
         * @return string[] Matching subpages
         */
        public function prefixSearchSubpages( $search, $limit, $offset ) {
-               $title = Title::newFromText( $search );
-               if ( !$title || !$title->canExist() ) {
-                       // No prefix suggestion in special and media namespace
-                       return array();
-               }
-               // Autocomplete subpage the same as a normal search
-               $prefixSearcher = new StringPrefixSearch;
-               $result = $prefixSearcher->search( $search, $limit, array(), $offset );
-               return $result;
+               return $this->prefixSearchString( $search, $limit, $offset );
        }
 }
index f99a52d..f488d3c 100644 (file)
@@ -675,13 +675,14 @@ class PageArchive {
  * @ingroup SpecialPage
  */
 class SpecialUndelete extends SpecialPage {
-       private $mAction;
+       private $mAction;
        private $mTarget;
        private $mTimestamp;
        private $mRestore;
+       private $mRevdel;
        private $mInvert;
        private $mFilename;
-       private $mTargetTimestamp;
+       private $mTargetTimestamp;
        private $mAllowed;
        private $mCanView;
        private $mComment;
@@ -719,6 +720,7 @@ class SpecialUndelete extends SpecialPage {
                $posted = $request->wasPosted() &&
                        $user->matchEditToken( $request->getVal( 'wpEditToken' ) );
                $this->mRestore = $request->getCheck( 'restore' ) && $posted;
+               $this->mRevdel = $request->getCheck( 'revdel' ) && $posted;
                $this->mInvert = $request->getCheck( 'invert' ) && $posted;
                $this->mPreview = $request->getCheck( 'preview' ) && $posted;
                $this->mDiff = $request->getCheck( 'diff' );
@@ -831,13 +833,42 @@ class SpecialUndelete extends SpecialPage {
                        } else {
                                $this->showFile( $this->mFilename );
                        }
-               } elseif ( $this->mRestore && $this->mAction == 'submit' ) {
-                       $this->undelete();
+               } elseif ( $this->mAction === "submit" ) {
+                       if ( $this->mRestore ) {
+                               $this->undelete();
+                       } elseif ( $this->mRevdel ) {
+                               $this->redirectToRevDel();
+                       }
+
                } else {
                        $this->showHistory();
                }
        }
 
+       /**
+        * Convert submitted form data to format expected by RevisionDelete and
+        * redirect the request
+        */
+       private function redirectToRevDel() {
+               $archive = new PageArchive( $this->mTargetObj );
+
+               $revisions = array();
+
+               foreach ( $this->getRequest()->getValues() as $key => $val ) {
+                       $matches = array();
+                       if ( preg_match( "/^ts(\d{14})$/", $key, $matches ) ) {
+                               $revisions[ $archive->getRevision( $matches[1] )->getId() ] = 1;
+                       }
+               }
+               $query = array(
+                       "type" => "revision",
+                       "ids" => $revisions,
+                       "target" => wfUrlencode( $this->mTargetObj->getPrefixedText() )
+               );
+               $url = SpecialPage::getTitleFor( "RevisionDelete" )->getFullURL( $query );
+               $this->getOutput()->redirect( $url );
+       }
+
        function showSearchForm() {
                $out = $this->getOutput();
                $out->setPageTitle( $this->msg( 'undelete-search-title' ) );
@@ -1142,7 +1173,7 @@ class SpecialUndelete extends SpecialPage {
                        array( 'ts_rev_id' => $rev->getId() ),
                        __METHOD__
                );
-               $tagSummary = ChangeTags::formatSummaryRow( $tags, 'deleteddiff' );
+               $tagSummary = ChangeTags::formatSummaryRow( $tags, 'deleteddiff', $this->getContext() );
 
                // FIXME This is reimplementing DifferenceEngine#getRevisionHeader
                // and partially #showDiffPage, but worse
@@ -1356,7 +1387,20 @@ class SpecialUndelete extends SpecialPage {
                $out->addHTML( Xml::element( 'h2', null, $this->msg( 'history' )->text() ) . "\n" );
 
                if ( $haveRevisions ) {
-                       # The page's stored (deleted) history:
+                       # Show the page's stored (deleted) history
+
+                       if ( $this->getUser()->isAllowed( 'deleterevision' ) ) {
+                               $out->addHTML( Html::element(
+                                       'button',
+                                       array(
+                                               'name' => 'revdel',
+                                               'type' => 'submit',
+                                               'class' => 'deleterevision-log-submit mw-log-deleterevision-button'
+                                       ),
+                                       $this->msg( 'showhideselectedversions' )->text()
+                               ) . "\n" );
+                       }
+
                        $out->addHTML( '<ul>' );
                        $remaining = $revisions->numRows();
                        $earliestLiveTime = $this->mTargetObj->getEarliestRevTime();
@@ -1461,18 +1505,18 @@ class SpecialUndelete extends SpecialPage {
 
                // Tags
                $attribs = array();
-               list( $tagSummary, $classes ) = ChangeTags::formatSummaryRow( $row->ts_tags, 'deletedhistory' );
+               list( $tagSummary, $classes ) = ChangeTags::formatSummaryRow(
+                       $row->ts_tags,
+                       'deletedhistory',
+                       $this->getContext()
+               );
                if ( $classes ) {
                        $attribs['class'] = implode( ' ', $classes );
                }
 
-               // Revision delete links
-               $revdlink = Linker::getRevDeleteLink( $user, $rev, $this->mTargetObj );
-
-               $revisionRow = $this->msg( 'undelete-revision-row' )
+               $revisionRow = $this->msg( 'undelete-revision-row2' )
                        ->rawParams(
                                $checkBox,
-                               $revdlink,
                                $last,
                                $pageLink,
                                $userLink,
@@ -1709,15 +1753,7 @@ class SpecialUndelete extends SpecialPage {
         * @return string[] Matching subpages
         */
        public function prefixSearchSubpages( $search, $limit, $offset ) {
-               $title = Title::newFromText( $search );
-               if ( !$title || !$title->canExist() ) {
-                       // No prefix suggestion in special and media namespace
-                       return array();
-               }
-               // Autocomplete subpage the same as a normal search
-               $prefixSearcher = new StringPrefixSearch;
-               $result = $prefixSearcher->search( $search, $limit, array(), $offset );
-               return $result;
+               return $this->prefixSearchString( $search, $limit, $offset );
        }
 
        protected function getGroupName() {
index 5b3c43e..3ccc797 100644 (file)
@@ -501,11 +501,29 @@ class SpecialUpload extends SpecialPage {
                        $pageText = false;
                }
 
+               $changeTags = $this->getRequest()->getVal( 'wpChangeTags' );
+               if ( is_null( $changeTags ) || $changeTags === '' ) {
+                       $changeTags = array();
+               } else {
+                       $changeTags = array_filter( array_map( 'trim', explode( ',', $changeTags ) ) );
+               }
+
+               if ( $changeTags ) {
+                       $changeTagsStatus = ChangeTags::canAddTagsAccompanyingChange(
+                               $changeTags, $this->getUser() );
+                       if ( !$changeTagsStatus->isOK() ) {
+                               $this->showUploadError( $this->getOutput()->parse( $changeTagsStatus->getWikiText() ) );
+
+                               return;
+                       }
+               }
+
                $status = $this->mUpload->performUpload(
                        $this->mComment,
                        $pageText,
                        $this->mWatchthis,
-                       $this->getUser()
+                       $this->getUser(),
+                       $changeTags
                );
 
                if ( !$status->isGood() ) {
index d9a5e4f..0273484 100644 (file)
@@ -649,7 +649,7 @@ class LoginForm extends SpecialPage {
                                "allowed account creation w/o throttle\n" );
                } else {
                        if ( ( $wgAccountCreationThrottle && $currentUser->isPingLimitable() ) ) {
-                               $key = wfMemcKey( 'acctcreate', 'ip', $ip );
+                               $key = wfGlobalCacheKey( 'acctcreate', 'ip', $ip );
                                $value = $cache->get( $key );
                                if ( !$value ) {
                                        $cache->set( $key, 0, $cache::TTL_DAY );
@@ -890,7 +890,7 @@ class LoginForm extends SpecialPage {
 
                $throttleCount = 0;
                if ( is_array( $wgPasswordAttemptThrottle ) ) {
-                       $throttleKey = wfMemcKey( 'password-throttle', $wgRequest->getIP(), md5( $username ) );
+                       $throttleKey = wfGlobalCacheKey( 'password-throttle', $wgRequest->getIP(), md5( $username ) );
                        $count = $wgPasswordAttemptThrottle['count'];
                        $period = $wgPasswordAttemptThrottle['seconds'];
 
@@ -917,7 +917,7 @@ class LoginForm extends SpecialPage {
                global $wgRequest;
                $username = trim( $username ); // sanity
 
-               $throttleKey = wfMemcKey( 'password-throttle', $wgRequest->getIP(), md5( $username ) );
+               $throttleKey = wfGlobalCacheKey( 'password-throttle', $wgRequest->getIP(), md5( $username ) );
                ObjectCache::getLocalClusterInstance()->delete( $throttleKey );
        }
 
@@ -1000,7 +1000,7 @@ class LoginForm extends SpecialPage {
 
                                // Reset the throttle
                                $request = $this->getRequest();
-                               $key = wfMemcKey( 'password-throttle', $request->getIP(), md5( $this->mUsername ) );
+                               $key = wfGlobalCacheKey( 'password-throttle', $request->getIP(), md5( $this->mUsername ) );
                                $cache->delete( $key );
 
                                if ( $this->hasSessionCookie() || $this->mSkipCookieCheck ) {
@@ -1758,4 +1758,7 @@ class LoginForm extends SpecialPage {
                return $expired;
        }
 
+       protected function getSubpagesForPrefixSearch() {
+               return array( 'signup' );
+       }
 }
index 7351c33..205b67e 100644 (file)
@@ -292,7 +292,7 @@ class UserrightsPage extends SpecialPage {
                $user->invalidateCache();
 
                // update groups in external authentication database
-               Hooks::run( 'UserGroupsChanged', array( $user, $add, $remove, $this->getUser() ) );
+               Hooks::run( 'UserGroupsChanged', array( $user, $add, $remove, $this->getUser(), $reason ) );
                $wgAuth->updateExternalDBGroups( $user, $add, $remove );
 
                wfDebug( 'oldGroups: ' . print_r( $oldGroups, true ) . "\n" );
index a628902..17442be 100644 (file)
@@ -126,7 +126,7 @@ class SpecialVersion extends SpecialPage {
                                break;
 
                        default:
-                               $out->addModules( 'mediawiki.special.version' );
+                               $out->addModuleStyles( 'mediawiki.special.version' );
                                $out->addWikiText(
                                        $this->getMediaWikiCredits() .
                                        $this->softwareInformation() .
index cc5c150..03e555f 100644 (file)
@@ -32,6 +32,10 @@ class SpecialWatchlist extends ChangesListSpecialPage {
                parent::__construct( $page, $restriction );
        }
 
+       public function doesWrites() {
+               return true;
+       }
+
        /**
         * Main execution point
         *
index 47fd972..45ef9a2 100644 (file)
@@ -548,15 +548,7 @@ class SpecialWhatLinksHere extends IncludableSpecialPage {
         * @return string[] Matching subpages
         */
        public function prefixSearchSubpages( $search, $limit, $offset ) {
-               $title = Title::newFromText( $search );
-               if ( !$title || !$title->canExist() ) {
-                       // No prefix suggestion in special and media namespace
-                       return array();
-               }
-               // Autocomplete subpage the same as a normal search
-               $prefixSearcher = new StringPrefixSearch;
-               $result = $prefixSearcher->search( $search, $limit, array(), $offset );
-               return $result;
+               return $this->prefixSearchString( $search, $limit, $offset );
        }
 
        protected function getGroupName() {
index 27574fa..07060b2 100644 (file)
@@ -62,12 +62,12 @@ class MediaWikiPageLinkRenderer implements PageLinkRenderer {
        /**
         * Returns the (partial) URL for the given page (including any section identifier).
         *
-        * @param TitleValue $page The link's target
+        * @param LinkTarget $page The link's target
         * @param array $params Any additional URL parameters.
         *
         * @return string
         */
-       public function getPageUrl( TitleValue $page, $params = array() ) {
+       public function getPageUrl( LinkTarget $page, $params = array() ) {
                // TODO: move the code from Linker::linkUrl here!
                // The below is just a rough estimation!
 
@@ -93,20 +93,24 @@ class MediaWikiPageLinkRenderer implements PageLinkRenderer {
        /**
         * Returns an HTML link to the given page, using the given surface text.
         *
-        * @param TitleValue $page The link's target
+        * @param LinkTarget $linkTarget The link's target
         * @param string $text The link's surface text (will be derived from $page if not given).
         *
         * @return string
         */
-       public function renderHtmlLink( TitleValue $page, $text = null ) {
+       public function renderHtmlLink( LinkTarget $linkTarget, $text = null ) {
                if ( $text === null ) {
-                       $text = $this->formatter->getFullText( $page );
+                       $text = $this->formatter->getFullText( $linkTarget );
                }
 
                // TODO: move the logic implemented by Linker here,
                // using $this->formatter and $this->baseUrl, and
                // re-implement Linker to use a HtmlPageLinkRenderer.
-               $title = Title::newFromTitleValue( $page );
+               if ( $linkTarget instanceof Title ) {
+                       $title = $linkTarget;
+               } else {
+                       $title = Title::newFromLinkTarget( $linkTarget );
+               }
                $link = Linker::link( $title, htmlspecialchars( $text ) );
 
                return $link;
@@ -115,12 +119,12 @@ class MediaWikiPageLinkRenderer implements PageLinkRenderer {
        /**
         * Returns a wikitext link to the given page, using the given surface text.
         *
-        * @param TitleValue $page The link's target
+        * @param LinkTarget $page The link's target
         * @param string $text The link's surface text (will be derived from $page if not given).
         *
         * @return string
         */
-       public function renderWikitextLink( TitleValue $page, $text = null ) {
+       public function renderWikitextLink( LinkTarget $page, $text = null ) {
                if ( $text === null ) {
                        $text = $this->formatter->getFullText( $page );
                }
index c497865..1de4247 100644 (file)
@@ -151,33 +151,33 @@ class MediaWikiTitleCodec implements TitleFormatter, TitleParser {
        /**
         * @see TitleFormatter::getText()
         *
-        * @param TitleValue $title
+        * @param LinkTarget $title
         *
         * @return string $title->getText()
         */
-       public function getText( TitleValue $title ) {
+       public function getText( LinkTarget $title ) {
                return $this->formatTitle( false, $title->getText(), '' );
        }
 
        /**
         * @see TitleFormatter::getText()
         *
-        * @param TitleValue $title
+        * @param LinkTarget $title
         *
         * @return string
         */
-       public function getPrefixedText( TitleValue $title ) {
+       public function getPrefixedText( LinkTarget $title ) {
                return $this->formatTitle( $title->getNamespace(), $title->getText(), '' );
        }
 
        /**
         * @see TitleFormatter::getText()
         *
-        * @param TitleValue $title
+        * @param LinkTarget $title
         *
         * @return string
         */
-       public function getFullText( TitleValue $title ) {
+       public function getFullText( LinkTarget $title ) {
                return $this->formatTitle( $title->getNamespace(), $title->getText(), $title->getFragment() );
        }
 
index ca91f58..2ca5707 100644 (file)
@@ -37,32 +37,32 @@ interface PageLinkRenderer {
         *
         * @todo expand this to cover the functionality of Linker::linkUrl
         *
-        * @param TitleValue $page The link's target
+        * @param LinkTarget $page The link's target
         * @param array $params Any additional URL parameters.
         *
         * @return string
         */
-       public function getPageUrl( TitleValue $page, $params = array() );
+       public function getPageUrl( LinkTarget $page, $params = array() );
 
        /**
         * Returns an HTML link to the given page, using the given surface text.
         *
         * @todo expand this to cover the functionality of Linker::link
         *
-        * @param TitleValue $page The link's target
+        * @param LinkTarget $page The link's target
         * @param string $text The link's surface text (will be derived from $page if not given).
         *
         * @return string
         */
-       public function renderHtmlLink( TitleValue $page, $text = null );
+       public function renderHtmlLink( LinkTarget $page, $text = null );
 
        /**
         * Returns a wikitext link to the given page, using the given surface text.
         *
-        * @param TitleValue $page The link's target
+        * @param LinkTarget $page The link's target
         * @param string $text The link's surface text (will be derived from $page if not given).
         *
         * @return string
         */
-       public function renderWikitextLink( TitleValue $page, $text = null );
+       public function renderWikitextLink( LinkTarget $page, $text = null );
 }
index aad8376..4edc5db 100644 (file)
@@ -51,29 +51,29 @@ interface TitleFormatter {
         *
         * @note Only minimal normalization is applied. Consider using TitleValue::getText() directly.
         *
-        * @param TitleValue $title The title to format
+        * @param LinkTarget $title The title to format
         *
         * @return string
         */
-       public function getText( TitleValue $title );
+       public function getText( LinkTarget $title );
 
        /**
         * Returns the title formatted for display, including the namespace name.
         *
-        * @param TitleValue $title The title to format
+        * @param LinkTarget $title The title to format
         *
         * @return string
         */
-       public function getPrefixedText( TitleValue $title );
+       public function getPrefixedText( LinkTarget $title );
 
        /**
         * Returns the title formatted for display, with namespace and fragment.
         *
-        * @param TitleValue $title The title to format
+        * @param LinkTarget $title The title to format
         *
         * @return string
         */
-       public function getFullText( TitleValue $title );
+       public function getFullText( LinkTarget $title );
 
        /**
         * Returns the name of the namespace for the given title.
index a0f3b6f..c8ebc2a 100644 (file)
@@ -35,7 +35,7 @@ use Wikimedia\Assert\Assert;
  * @see https://www.mediawiki.org/wiki/Requests_for_comment/TitleValue
  * @since 1.23
  */
-class TitleValue {
+class TitleValue implements LinkTarget {
        /**
         * @var int
         */
index 6f713f1..fca7775 100644 (file)
@@ -18,9 +18,6 @@
  * http://www.gnu.org/copyleft/gpl.html
  */
 
-use MediaWiki\Session\BotPasswordSessionProvider;
-use MediaWiki\Session\SessionInfo;
-
 /**
  * Utility class for bot passwords
  * @since 1.27
index b1f79ce..95e5ceb 100644 (file)
@@ -45,6 +45,11 @@ class User implements IDBAccessObject {
         */
        const TOKEN_LENGTH = 32;
 
+       /**
+        * @const string An invalid value for user_token
+        */
+       const INVALID_TOKEN = '*** INVALID ***';
+
        /**
         * Global constant made accessible as class constants so that autoloader
         * magic can be used.
@@ -309,6 +314,22 @@ class User implements IDBAccessObject {
                return $this->getName();
        }
 
+       /**
+        * Test if it's safe to load this User object. You should typically check this before using
+        * $wgUser or RequestContext::getUser in a method that might be called before the system has
+        * been fully initialized. If the object is unsafe, you should use an anonymous user:
+        * \code
+        * $user = $wgUser->isSafeToLoad() ? $wgUser : new User;
+        * \endcode
+        *
+        * @since 1.27
+        * @return bool
+        */
+       public function isSafeToLoad() {
+               global $wgFullyInitialised;
+               return $wgFullyInitialised || $this->mLoadedItems === true || $this->mFrom !== 'session';
+       }
+
        /**
         * Load the user table data for this object from the source given by mFrom.
         *
@@ -327,7 +348,7 @@ class User implements IDBAccessObject {
                $this->queryFlagsUsed = $flags;
 
                // If this is called too early, things are likely to break.
-               if ( $this->mFrom === 'session' && empty( $wgFullyInitialised ) ) {
+               if ( !$wgFullyInitialised && $this->mFrom === 'session' ) {
                        \MediaWiki\Logger\LoggerFactory::getInstance( 'session' )
                                ->warning( 'User::loadFromSession called before the end of Setup.php', array(
                                        'exception' => new Exception( 'User::loadFromSession called before the end of Setup.php' ),
@@ -641,7 +662,8 @@ class User implements IDBAccessObject {
                $user = self::newFromRow( $row );
 
                // A user is considered to exist as a non-system user if it has a
-               // password set, or a temporary password set, or an email set.
+               // password set, or a temporary password set, or an email set, or a
+               // non-invalid token.
                $passwordFactory = new PasswordFactory();
                $passwordFactory->init( RequestContext::getMain()->getConfig() );
                try {
@@ -657,7 +679,7 @@ class User implements IDBAccessObject {
                        $newpassword = PasswordFactory::newInvalidPassword();
                }
                if ( !$password instanceof InvalidPassword || !$newpassword instanceof InvalidPassword
-                       || $user->mEmail
+                       || $user->mEmail || $user->mToken !== self::INVALID_TOKEN
                ) {
                        // User exists. Steal it?
                        if ( !$options['steal'] ) {
@@ -677,11 +699,11 @@ class User implements IDBAccessObject {
                                __METHOD__
                        );
                        $user->invalidateEmail();
+                       $user->mToken = self::INVALID_TOKEN;
                        $user->saveSettings();
+                       SessionManager::singleton()->preventSessionsForUser( $user->getName() );
                }
 
-               SessionManager::singleton()->preventSessionsForUser( $user->getName() );
-
                return $user;
        }
 
@@ -1156,7 +1178,7 @@ class User implements IDBAccessObject {
                        // Other code expects these to be set in the session, so set them.
                        $session->set( 'wsUserID', $this->getId() );
                        $session->set( 'wsUserName', $this->getName() );
-                       $session->set( 'wsToken', $this->mToken );
+                       $session->set( 'wsToken', $this->getToken() );
                        return true;
                }
 
@@ -1365,7 +1387,7 @@ class User implements IDBAccessObject {
                        $this->addGroup( $group );
                }
                // update groups in external authentication database
-               Hooks::run( 'UserGroupsChanged', array( $this, $toPromote, array(), false ) );
+               Hooks::run( 'UserGroupsChanged', array( $this, $toPromote, array(), false, false ) );
                $wgAuth->updateExternalDBGroups( $this, $toPromote );
 
                $newGroups = array_merge( $oldGroups, $toPromote ); // all groups
@@ -1526,10 +1548,16 @@ class User implements IDBAccessObject {
                # We only need to worry about passing the IP address to the Block generator if the
                # user is not immune to autoblocks/hardblocks, and they are the current user so we
                # know which IP address they're actually coming from
-               if ( !$this->isAllowed( 'ipblock-exempt' ) && $this->equals( $wgUser ) ) {
-                       $ip = $this->getRequest()->getIP();
-               } else {
-                       $ip = null;
+               $ip = null;
+               if ( !$this->isAllowed( 'ipblock-exempt' ) ) {
+                       // $wgUser->getName() only works after the end of Setup.php. Until
+                       // then, assume it's a logged-out user.
+                       $globalUserName = $wgUser->isSafeToLoad()
+                               ? $wgUser->getName()
+                               : IP::sanitizeIP( $wgUser->getRequest()->getIP() );
+                       if ( $this->getName() === $globalUserName ) {
+                               $ip = $this->getRequest()->getIP();
+                       }
                }
 
                // User/IP blocking
@@ -1738,40 +1766,43 @@ class User implements IDBAccessObject {
                $keys = array();
                $id = $this->getId();
                $userLimit = false;
+               $isNewbie = $this->isNewbie();
 
-               if ( isset( $limits['anon'] ) && $id == 0 ) {
-                       $keys[wfMemcKey( 'limiter', $action, 'anon' )] = $limits['anon'];
-               }
-
-               if ( isset( $limits['user'] ) && $id != 0 ) {
-                       $userLimit = $limits['user'];
-               }
-               if ( $this->isNewbie() ) {
-                       if ( isset( $limits['newbie'] ) && $id != 0 ) {
+               if ( $id == 0 ) {
+                       // limits for anons
+                       if ( isset( $limits['anon'] ) ) {
+                               $keys[wfMemcKey( 'limiter', $action, 'anon' )] = $limits['anon'];
+                       }
+               } else {
+                       // limits for logged-in users
+                       if ( isset( $limits['user'] ) ) {
+                               $userLimit = $limits['user'];
+                       }
+                       // limits for newbie logged-in users
+                       if ( $isNewbie && isset( $limits['newbie'] ) ) {
                                $keys[wfMemcKey( 'limiter', $action, 'user', $id )] = $limits['newbie'];
                        }
+               }
+
+               // limits for anons and for newbie logged-in users
+               if ( $isNewbie ) {
+                       // ip-based limits
                        if ( isset( $limits['ip'] ) ) {
                                $ip = $this->getRequest()->getIP();
                                $keys["mediawiki:limiter:$action:ip:$ip"] = $limits['ip'];
                        }
+                       // subnet-based limits
                        if ( isset( $limits['subnet'] ) ) {
                                $ip = $this->getRequest()->getIP();
-                               $matches = array();
-                               $subnet = false;
-                               if ( IP::isIPv6( $ip ) ) {
-                                       $parts = IP::parseRange( "$ip/64" );
-                                       $subnet = $parts[0];
-                               } elseif ( preg_match( '/^(\d+\.\d+\.\d+)\.\d+$/', $ip, $matches ) ) {
-                                       // IPv4
-                                       $subnet = $matches[1];
-                               }
+                               $subnet = IP::getSubnet( $ip );
                                if ( $subnet !== false ) {
                                        $keys["mediawiki:limiter:$action:subnet:$subnet"] = $limits['subnet'];
                                }
                        }
                }
+
                // Check for group-specific permissions
-               // If more than one group applies, use the group with the highest limit
+               // If more than one group applies, use the group with the highest limit ratio (max/period)
                foreach ( $this->getGroups() as $group ) {
                        if ( isset( $limits[$group] ) ) {
                                if ( $userLimit === false
@@ -1781,6 +1812,7 @@ class User implements IDBAccessObject {
                                }
                        }
                }
+
                // Set the user limit key
                if ( $userLimit !== false ) {
                        list( $max, $period ) = $userLimit;
@@ -1788,6 +1820,30 @@ class User implements IDBAccessObject {
                        $keys[wfMemcKey( 'limiter', $action, 'user', $id )] = $userLimit;
                }
 
+               // ip-based limits for all ping-limitable users
+               if ( isset( $limits['ip-all'] ) ) {
+                       $ip = $this->getRequest()->getIP();
+                       // ignore if user limit is more permissive
+                       if ( $isNewbie || $userLimit === false
+                               || $limits['ip-all'][0] / $limits['ip-all'][1] > $userLimit[0] / $userLimit[1] ) {
+                               $keys["mediawiki:limiter:$action:ip-all:$ip"] = $limits['ip-all'];
+                       }
+               }
+
+               // subnet-based limits for all ping-limitable users
+               if ( isset( $limits['subnet-all'] ) ) {
+                       $ip = $this->getRequest()->getIP();
+                       $subnet = IP::getSubnet( $ip );
+                       if ( $subnet !== false ) {
+                               // ignore if user limit is more permissive
+                               if ( $isNewbie || $userLimit === false
+                                       || $limits['ip-all'][0] / $limits['ip-all'][1]
+                                       > $userLimit[0] / $userLimit[1] ) {
+                                       $keys["mediawiki:limiter:$action:subnet-all:$subnet"] = $limits['subnet-all'];
+                               }
+                       }
+               }
+
                $cache = ObjectCache::getLocalClusterInstance();
 
                $triggered = false;
@@ -2429,14 +2485,38 @@ class User implements IDBAccessObject {
         * Get the user's current token.
         * @param bool $forceCreation Force the generation of a new token if the
         *   user doesn't have one (default=true for backwards compatibility).
-        * @return string Token
+        * @return string|null Token
         */
        public function getToken( $forceCreation = true ) {
+               global $wgAuthenticationTokenVersion;
+
                $this->load();
                if ( !$this->mToken && $forceCreation ) {
                        $this->setToken();
                }
-               return $this->mToken;
+
+               if ( !$this->mToken ) {
+                       // The user doesn't have a token, return null to indicate that.
+                       return null;
+               } elseif ( $this->mToken === self::INVALID_TOKEN ) {
+                       // We return a random value here so existing token checks are very
+                       // likely to fail.
+                       return MWCryptRand::generateHex( self::TOKEN_LENGTH );
+               } elseif ( $wgAuthenticationTokenVersion === null ) {
+                       // $wgAuthenticationTokenVersion not in use, so return the raw secret
+                       return $this->mToken;
+               } else {
+                       // $wgAuthenticationTokenVersion in use, so hmac it.
+                       $ret = MWCryptHash::hmac( $wgAuthenticationTokenVersion, $this->mToken, false );
+
+                       // The raw hash can be overly long. Shorten it up.
+                       $len = max( 32, self::TOKEN_LENGTH );
+                       if ( strlen( $ret ) < $len ) {
+                               // Should never happen, even md5 is 128 bits
+                               throw new \UnexpectedValueException( 'Hmac returned less than 128 bits' );
+                       }
+                       return substr( $ret, -$len );
+               }
        }
 
        /**
@@ -2447,7 +2527,10 @@ class User implements IDBAccessObject {
         */
        public function setToken( $token = false ) {
                $this->load();
-               if ( !$token ) {
+               if ( $this->mToken === self::INVALID_TOKEN ) {
+                       \MediaWiki\Logger\LoggerFactory::getInstance( 'session' )
+                               ->debug( __METHOD__ . ": Ignoring attempt to set token for system user \"$this\"" );
+               } elseif ( !$token ) {
                        $this->mToken = MWCryptRand::generateHex( self::TOKEN_LENGTH );
                } else {
                        $this->mToken = $token;
@@ -3402,22 +3485,21 @@ class User implements IDBAccessObject {
                                return;
                        }
 
-                       $that = $this;
                        // Try to update the DB post-send and only if needed...
-                       DeferredUpdates::addCallableUpdate( function() use ( $that, $title, $oldid ) {
-                               if ( !$that->getNewtalk() ) {
+                       DeferredUpdates::addCallableUpdate( function() use ( $title, $oldid ) {
+                               if ( !$this->getNewtalk() ) {
                                        return; // no notifications to clear
                                }
 
                                // Delete the last notifications (they stack up)
-                               $that->setNewtalk( false );
+                               $this->setNewtalk( false );
 
                                // If there is a new, unseen, revision, use its timestamp
                                $nextid = $oldid
                                        ? $title->getNextRevisionID( $oldid, Title::GAID_FOR_UPDATE )
                                        : null;
                                if ( $nextid ) {
-                                       $that->setNewtalk( true, Revision::newFromId( $nextid ) );
+                                       $this->setNewtalk( true, Revision::newFromId( $nextid ) );
                                }
                        } );
                }
@@ -4788,9 +4870,8 @@ class User implements IDBAccessObject {
         * Deferred version of incEditCountImmediate()
         */
        public function incEditCount() {
-               $that = $this;
-               wfGetDB( DB_MASTER )->onTransactionPreCommitOrIdle( function() use ( $that ) {
-                       $that->incEditCountImmediate();
+               wfGetDB( DB_MASTER )->onTransactionPreCommitOrIdle( function() {
+                       $this->incEditCountImmediate();
                } );
        }
 
index 7d63156..69426d7 100644 (file)
@@ -310,6 +310,7 @@ class ClassCollector {
                case T_NAMESPACE:
                case T_CLASS:
                case T_INTERFACE:
+               case T_TRAIT:
                        $this->startToken = $token;
                }
        }
@@ -331,6 +332,7 @@ class ClassCollector {
 
                case T_CLASS:
                case T_INTERFACE:
+               case T_TRAIT:
                        $this->tokens[] = $token;
                        if ( is_array( $token ) && $token[0] === T_STRING ) {
                                $this->classes[] = $this->namespace . $this->implodeTokens();
index 8abca5b..b352ecc 100644 (file)
@@ -766,4 +766,23 @@ class IP {
        public static function clearCaches() {
                self::$proxyIpSet = null;
        }
+
+       /**
+        * Returns the subnet of a given IP
+        *
+        * @param string $ip
+        * @return string|false
+        */
+       public static function getSubnet( $ip ) {
+               $matches = array();
+               $subnet = false;
+               if ( IP::isIPv6( $ip ) ) {
+                       $parts = IP::parseRange( "$ip/64" );
+                       $subnet = $parts[0];
+               } elseif ( preg_match( '/^(\d+\.\d+\.\d+)\.\d+$/', $ip, $matches ) ) {
+                       // IPv4
+                       $subnet = $matches[1];
+               }
+               return $subnet;
+       }
 }
index 10606c1..c5112ff 100644 (file)
@@ -250,14 +250,7 @@ class MWCryptRand {
                }
 
                if ( strlen( $buffer ) < $bytes ) {
-                       // If available make use of openssl's random_pseudo_bytes method to
-                       // attempt to generate randomness. However don't do this on Windows
-                       // with PHP < 5.3.4 due to a bug:
-                       // http://stackoverflow.com/questions/1940168/openssl-random-pseudo-bytes-is-slow-php
-                       // http://git.php.net/?p=php-src.git;a=commitdiff;h=cd62a70863c261b07f6dadedad9464f7e213cad5
-                       if ( function_exists( 'openssl_random_pseudo_bytes' )
-                               && ( !wfIsWindows() || version_compare( PHP_VERSION, '5.3.4', '>=' ) )
-                       ) {
+                       if ( function_exists( 'openssl_random_pseudo_bytes' ) ) {
                                $rem = $bytes - strlen( $buffer );
                                $openssl_bytes = openssl_random_pseudo_bytes( $rem, $openssl_strong );
                                if ( $openssl_bytes === false ) {
index aac76c5..53300c5 100644 (file)
@@ -7,7 +7,7 @@
        "--builtin-classes": true,
        "--processes": "0",
        "--warnings-exit-nonzero": true,
-       "--external": "HTMLElement,HTMLDocument,Window,File,MouseEvent,KeyboardEvent,HTMLIframeElement,HTMLInputElement,XMLDocument",
+       "--external": "HTMLElement,HTMLDocument,Window,Blob,File,MouseEvent,KeyboardEvent,HTMLIframeElement,HTMLInputElement,XMLDocument",
        "--output": "docs/js",
        "--": [
                "maintenance/jsduck/external.js",
index 1b6e9d6..b953405 100644 (file)
@@ -114,4 +114,7 @@ class FakeConverter {
        function translate( $text, $variant ) {
                return $text;
        }
+
+       public function updateConversionTable( Title $title ) {
+       }
 }
index 42c5a57..c094215 100644 (file)
@@ -4231,6 +4231,16 @@ class Language {
                return $this->mConverter->getParsedTitle();
        }
 
+       /**
+        * Refresh the cache of conversion tables when
+        * MediaWiki:Conversiontable* is updated.
+        *
+        * @param Title $titleobj The Title of the page being updated
+        */
+       public function updateConversionTable( Title $title ) {
+               $this->mConverter->updateConversionTable( $title );
+       }
+
        /**
         * Prepare external link text for conversion. When the text is
         * a URL, it shouldn't be converted, and it'll be wrapped in
index b00aa34..4eeba64 100644 (file)
@@ -1053,24 +1053,12 @@ class LanguageConverter {
        }
 
        /**
-        * Hook to refresh the cache of conversion tables when
+        * Refresh the cache of conversion tables when
         * MediaWiki:Conversiontable* is updated.
-        * @private
         *
-        * @param WikiPage $page
-        * @param User $user User object for the current user
-        * @param Content $content New page content
-        * @param string $summary Edit summary of the edit
-        * @param bool $isMinor Was the edit marked as minor?
-        * @param null $isWatch Unused.
-        * @param null $section Unused.
-        * @param int $flags Bitfield
-        * @param Revision|null $revision New Revision object or null
-        * @return bool True
+        * @param Title $titleobj The Title of the page being updated
         */
-       function OnPageContentSaveComplete( $page, $user, $content, $summary, $isMinor,
-                       $isWatch, $section, $flags, $revision ) {
-               $titleobj = $page->getTitle();
+       public function updateConversionTable( Title $titleobj ) {
                if ( $titleobj->getNamespace() == NS_MEDIAWIKI ) {
                        $title = $titleobj->getDBkey();
                        $t = explode( '/', $title, 3 );
@@ -1081,7 +1069,6 @@ class LanguageConverter {
                                }
                        }
                }
-               return true;
        }
 
        /**
index 9dc3a86..7582f56 100644 (file)
@@ -78,7 +78,6 @@ class GanConverter extends LanguageConverter {
  */
 class LanguageGan extends LanguageZh {
        function __construct() {
-               global $wgHooks;
                parent::__construct();
 
                $variants = array( 'gan', 'gan-hans', 'gan-hant' );
@@ -95,8 +94,6 @@ class LanguageGan extends LanguageZh {
                        $variants, $variantfallbacks,
                        array(),
                        $ml );
-
-               $wgHooks['PageContentSaveComplete'][] = $this->mConverter;
        }
 
        /**
index af0431f..93cc2c8 100644 (file)
@@ -190,8 +190,6 @@ class IuConverter extends LanguageConverter {
  */
 class LanguageIu extends Language {
        function __construct() {
-               global $wgHooks;
-
                parent::__construct();
 
                $variants = array( 'iu', 'ike-cans', 'ike-latn' );
@@ -203,6 +201,5 @@ class LanguageIu extends Language {
 
                $flags = array();
                $this->mConverter = new IuConverter( $this, 'iu', $variants, $variantfallbacks, $flags );
-               $wgHooks['PageContentSaveComplete'][] = $this->mConverter;
        }
 }
index 0357730..bac493b 100644 (file)
@@ -392,7 +392,6 @@ class KkConverter extends LanguageConverter {
  */
 class LanguageKk extends LanguageKk_cyrl {
        function __construct() {
-               global $wgHooks;
                parent::__construct();
 
                $variants = array( 'kk', 'kk-cyrl', 'kk-latn', 'kk-arab', 'kk-kz', 'kk-tr', 'kk-cn' );
@@ -407,8 +406,6 @@ class LanguageKk extends LanguageKk_cyrl {
                );
 
                $this->mConverter = new KkConverter( $this, 'kk', $variants, $variantfallbacks );
-
-               $wgHooks['PageContentSaveComplete'][] = $this->mConverter;
        }
 
        /**
index c14f468..ee54b8b 100644 (file)
@@ -230,7 +230,6 @@ class KuConverter extends LanguageConverter {
 class LanguageKu extends LanguageKu_ku {
 
        function __construct() {
-               global $wgHooks;
                parent::__construct();
 
                $variants = array( 'ku', 'ku-arab', 'ku-latn' );
@@ -241,6 +240,5 @@ class LanguageKu extends LanguageKu_ku {
                );
 
                $this->mConverter = new KuConverter( $this, 'ku', $variants, $variantfallbacks );
-               $wgHooks['PageContentSaveComplete'][] = $this->mConverter;
        }
 }
index afd7283..2787ca3 100644 (file)
@@ -166,8 +166,6 @@ class ShiConverter extends LanguageConverter {
  */
 class LanguageShi extends Language {
        function __construct() {
-               global $wgHooks;
-
                parent::__construct();
 
                $variants = array( 'shi', 'shi-tfng', 'shi-latn' );
@@ -179,6 +177,5 @@ class LanguageShi extends Language {
 
                $flags = array();
                $this->mConverter = new ShiConverter( $this, 'shi', $variants, $variantfallbacks, $flags );
-               $wgHooks['PageContentSaveComplete'][] = $this->mConverter;
        }
 }
index ece50e8..1ffb321 100644 (file)
@@ -198,8 +198,6 @@ class SrConverter extends LanguageConverter {
  */
 class LanguageSr extends Language {
        function __construct() {
-               global $wgHooks;
-
                parent::__construct();
 
                $variants = array( 'sr', 'sr-ec', 'sr-el' );
@@ -214,6 +212,5 @@ class LanguageSr extends Language {
                        'W' => 'W', 'реч' => 'W', 'reč' => 'W', 'ријеч' => 'W', 'riječ' => 'W'
                );
                $this->mConverter = new SrConverter( $this, 'sr', $variants, $variantfallbacks, $flags );
-               $wgHooks['PageContentSaveComplete'][] = $this->mConverter;
        }
 }
index 6910d9c..d37a207 100644 (file)
@@ -132,7 +132,6 @@ class UzConverter extends LanguageConverter {
  */
 class LanguageUz extends Language {
        function __construct() {
-               global $wgHooks;
                parent::__construct();
 
                $variants = array( 'uz', 'uz-latn', 'uz-cyrl' );
@@ -143,6 +142,5 @@ class LanguageUz extends Language {
                );
 
                $this->mConverter = new UzConverter( $this, 'uz', $variants, $variantfallbacks );
-               $wgHooks['PageContentSaveComplete'][] = $this->mConverter;
        }
 }
index 51b09f6..36345c9 100644 (file)
@@ -111,8 +111,6 @@ class ZhConverter extends LanguageConverter {
  */
 class LanguageZh extends LanguageZh_hans {
        function __construct() {
-               global $wgHooks;
-
                parent::__construct();
 
                $variants = array(
@@ -148,8 +146,6 @@ class LanguageZh extends LanguageZh_hans {
                                                                $variants, $variantfallbacks,
                                                                array(),
                                                                $ml );
-
-               $wgHooks['PageContentSaveComplete'][] = $this->mConverter;
        }
 
        /**
index 7711d8f..c2d3ee1 100644 (file)
@@ -170,9 +170,9 @@ class Names {
                'gl' => 'galego',               # Galician
                'glk' => 'گیلکی',  # Gilaki
                'gn' => 'Avañe\'ẽ',  # Guaraní, Paraguayan
-               'gom' => 'à¤\97à¥\8bवा à¤\95à¥\8bà¤\82à¤\95णà¥\80 / Gova Konknni',      # Goan Konkani
-               'gom-deva' => 'à¤\97à¥\8bवा à¤\95à¥\8bà¤\82à¤\95णà¥\80',        # Goan Konkani (Devanagari script)
-               'gom-latn' => 'Gova Konknni',   # Goan Konkani (Latin script)
+               'gom' => 'à¤\97à¥\8bà¤\82यà¤\9aà¥\80 à¤\95à¥\8bà¤\82à¤\95णà¥\80 / Gõychi Konknni',     # Goan Konkani
+               'gom-deva' => 'à¤\97à¥\8bà¤\82यà¤\9aà¥\80 à¤\95à¥\8bà¤\82à¤\95णà¥\80',  # Goan Konkani (Devanagari script)
+               'gom-latn' => 'Gõychi Konknni',        # Goan Konkani (Latin script)
                'got' => '𐌲𐌿𐍄𐌹𐍃𐌺',    # Gothic
                'grc' => 'Ἀρχαία ἑλληνικὴ', # Ancient Greek
                'gsw' => 'Alemannisch', # Alemannic
@@ -260,6 +260,7 @@ class Names {
                'li' => 'Limburgs',     # Limburgian
                'lij' => 'Ligure',      # Ligurian
                'liv' => 'Līvõ kēļ',        # Livonian
+               'lki' => 'لەکی‎', # Laki
                'lmo' => 'lumbaart',    # Lombard
                'ln' => 'lingála',             # Lingala
                'lo' => 'ລາວ',    # Laotian
index 983b3ce..7036989 100644 (file)
        "changepassword-throttled": "لديك محاولات تسجيل دخول كثيرة حديثة. من فضلك انتظر $1 قبل المحاولة ثانية.",
        "botpasswords-label-appid": "اسم البوت:",
        "botpasswords-label-create": "أنشأ",
+       "botpasswords-label-update": "حدث",
        "botpasswords-label-cancel": "ألغ",
        "botpasswords-label-delete": "احذف",
        "botpasswords-label-resetpassword": "أعد ضبط كلمة السر",
+       "botpasswords-label-restrictions": "قيود الاستخدام:",
        "resetpass_forbidden": "كلمات السر لا يمكن تغييرها",
        "resetpass-no-info": "يجب أن تكون مسجل الدخول للوصول إلى هذه الصفحة مباشرة.",
        "resetpass-submit-loggedin": "تغيير كلمة السر",
        "userrights": "صلاحيات المستخدم",
        "userrights-lookup-user": "أدِر مجموعات المستخدم",
        "userrights-user-editname": "أدخل اسم مستخدم:",
-       "editusergroup": "عدل مجموعات المستخدم",
+       "editusergroup": "عدل مجموعات {{GENDER:$1|المستخدم|المستخدمة}}",
        "editinguser": "تغيير صلاحيات {{GENDER:$1|المستخدم|المستخدمة}} <strong>[[User:$1|$1]]</strong> $2",
        "userrights-editusergroup": "تعديل مجموعات المستخدم",
-       "saveusergroups": "احفظ مجموعات المستخدم",
+       "saveusergroups": "احفظ مجموعات {{GENDER:$1|المستخدم|المستخدمة}}",
        "userrights-groupsmember": "عضو في:",
        "userrights-groupsmember-auto": "عضو ضمني في:",
        "userrights-groups-help": "يمكنك تغيير المجموعات التي ينتمي هذا المستخدم إليها:\n* يعني الصندوق المعلم أن المستخدم في هذه المجموعة.\n* يعني الصندوق غير المعلم أن المستخدم ليس في هذه المجموعة.\n* تعني علامة * عدم إمكانية إزالة المجموعة متى ما أضفتها، أو العكس.",
        "right-managechangetags": "إنشاء وحذف [[Special:Tags|الوسوم]] من قاعدة البيانات",
        "right-applychangetags": "تطبيق [[Special:Tags|الوسوم]]  مع التغييرات التي أجريتها.",
        "grant-generic": "\"$1\" حزمة الصلاحيات",
+       "grant-uploadfile": "ارفع ملفات جديدة",
        "newuserlogpage": "سجل إنشاء المستخدمين",
        "newuserlogpagetext": "هذا سجل بعمليات إنشاء المستخدمين.",
        "rightslog": "سجل صلاحيات المستخدمين",
        "rcshowhidemine": "$1 تعديلاتي",
        "rcshowhidemine-show": "أظهر",
        "rcshowhidemine-hide": "أخف",
+       "rcshowhidecategorization": "$1 تصنيف الصفحات",
        "rcshowhidecategorization-show": "أظهر",
        "rcshowhidecategorization-hide": "أخف",
        "rclinks": "أظهر آخر $1 تعديل في آخر $2 يوم<br />$3",
        "recentchangeslinked-page": "اسم الصفحة:",
        "recentchangeslinked-to": "أظهر التغييرات للصفحات الموصولة للصفحة المعطاة عوضا عن ذلك",
        "recentchanges-page-added-to-category": "[[:$1]] أضيفت إلى التصنيف",
+       "recentchanges-page-added-to-category-bundled": "أضيفت [[:$1]] و{{PLURAL:$2|صفحة واحدة|صفحتان|$2 صفحات}} إلى التصنيف",
+       "recentchanges-page-removed-from-category-bundled": "أزيلت [[:$1]] و{{PLURAL:$2|صفحة واحدة|صفحتان|$2 صفحات}} من التصنيف",
        "upload": "ارفع ملفا",
        "uploadbtn": "ارفع الملف",
        "reuploaddesc": "إلغاء الرفع والرجوع إلى استمارة الرفع",
        "upload-dialog-button-done": "تم",
        "upload-dialog-button-save": "احفظ",
        "upload-dialog-button-upload": "رفع",
-       "upload-form-label-select-file": "اختر ملفا",
        "upload-form-label-infoform-title": "التفاصيل",
        "upload-form-label-infoform-name": "الاسم",
        "upload-form-label-infoform-description": "الوصف",
        "foreign-structured-upload-form-label-own-work": "هذا عملي الخاص",
        "foreign-structured-upload-form-label-infoform-categories": "تصنيفات",
        "foreign-structured-upload-form-label-infoform-date": "التاريخ",
-       "foreign-structured-upload-form-3-label-yes": "نعم",
-       "foreign-structured-upload-form-3-label-no": "لا",
        "backend-fail-stream": "لا يمكن عرض الملف $1.",
        "backend-fail-backup": "لا يمكن صنع نسخة أحتياطية للملف $1.",
        "backend-fail-notexists": "الملف $1 غير موجود.",
        "querypage-disabled": "تم تعطيل هذه الصفحة الخاصة لأسباب تتعلق بالأداء.",
        "apihelp": "مساعدة API",
        "apihelp-no-such-module": "الوحدة \"$1\" غير موجودة.",
+       "apisandbox": "ملعب API",
+       "apisandbox-fullscreen": "وسع اللوحة",
+       "apisandbox-unfullscreen": "أظهر الصفحة",
+       "apisandbox-submit": "عمل الطلب",
+       "apisandbox-reset": "إفراغ",
+       "apisandbox-retry": "أعد المحاولة",
+       "apisandbox-examples": "أمثلة",
+       "apisandbox-results": "النتائج",
+       "apisandbox-request-url-label": "مسار الطلب:",
+       "apisandbox-request-time": "وقت الطلب: $1",
        "booksources": "مصادر كتاب",
        "booksources-search-legend": "البحث عن مصادر الكتب",
        "booksources-isbn": "ردمك:",
        "wlshowhideliu": "المسجلين",
        "wlshowhideanons": "المجهولين",
        "wlshowhidemine": "تعديلاتي",
+       "wlshowhidecategorization": "تصنيف الصفحات",
        "watchlist-options": "خيارات قائمة المراقبة",
        "watching": "يراقب...",
        "unwatching": "إزالة المراقبة...",
        "ipb-confirm": "أكّد المنع",
        "badipaddress": "عنوان أيبي غير صحيح",
        "blockipsuccesssub": "تم المنع بنجاح",
-       "blockipsuccesstext": "[[Special:Contributions/$1|$1]] ØªÙ\85 {{GENDER:$1|Ù\85Ù\86عÙ\87\85Ù\86عÙ\87ا}}.<br />\nØ£Ù\86ظر [[Special:BlockList|قائمة منع الآيبي]] لمراجعة حالات المنع.",
+       "blockipsuccesstext": "[[Special:Contributions/$1|$1]] ØªÙ\85 {{GENDER:$1|Ù\85Ù\86عÙ\87\85Ù\86عÙ\87ا}}.<br />\nطاÙ\84ع [[Special:BlockList|قائمة منع الآيبي]] لمراجعة حالات المنع.",
        "ipb-blockingself": "أنت على وشك منع نفسك! أمتأكد من رغبتك في القيام بذلك؟",
        "ipb-confirmhideuser": "أنت على وشك منع مستخدم مع تفعيل خيار \"أخف المستخدم\". سوف يخفي هذا الخيار اسم المستخدم من جميل القوائم ومدخلات السجلات. أمتأكد من رغبتك في القيام بذلك؟",
        "ipb-confirmaction": "إن كنت متأكدًا أنك تريد القيام بذلك حقًا، فالرجاء التحقق من حقل \"{{int:ipb-confirm}}\" في الأسفل.",
        "newimages-legend": "المرشح",
        "newimages-label": "اسم الملف (أو جزء منه):",
        "newimages-showbots": "أظهر التحميلات بواسطة البوتات",
+       "newimages-hidepatrolled": "أخف المرفوعات المنظورة",
        "noimages": "لا شيء للعرض.",
        "ilsubmit": "بحث",
        "bydate": "حسب التاريخ",
        "pagelang-language": "اللغة",
        "pagelang-use-default": "استخدام اللغة الافتراضية",
        "pagelang-select-lang": "اختر اللغة",
+       "pagelang-submit": "إرسال",
        "right-pagelang": "تغيير لغة الصفحة",
        "action-pagelang": "تغيير لغة الصفحة",
        "log-name-pagelang": "سجل تغيير اللغة",
        "mw-widgets-titleinput-description-new-page": "الصفحة غير موجودة بعد",
        "mw-widgets-titleinput-description-redirect": "تحويل إلى $1",
        "api-error-blacklisted": "اختر عنوانا مختلفا ومفهوما.",
+       "sessionprovider-generic": "جلسات $1",
        "randomrootpage": "صفحة جذر عشوائية"
 }
index 3e7650d..72c1e12 100644 (file)
        "unwatch": "ما تزيدش تعس",
        "watchlist-details": "{{PLURAL:$1||باجه وحده|باجتين|$1 باجات|$1 باجه}} في ليستت مراقبتك، من غير اعتبار باجات النقاش هي باجات منفصله.",
        "wlshowlast": "بين آخر $1 سوايع $2 يامات",
-       "watchlistall2": "لكل",
        "watchlist-options": "ابسيون ليستت المراقبه",
        "actioncomplete": "العمليه اندارت",
        "actionfailed": "العمليه فشلت",
        "allmessagesdefault": "الكتبه الافتراضيه",
        "thumbnail-more": "كبر",
        "thumbnail_error": "غلطه في خدمت صورة مصغرةالمينياتير: $1",
-       "tooltip-pt-userpage": "باجتÙ\83 Ù\86تع Ù\85ستعÙ\85Ù\84",
-       "tooltip-pt-mytalk": "باجÙ\87 Ù\86تع Ù\86Ù\82اشاتÙ\83",
-       "tooltip-pt-preferences": "وش خيرت",
+       "tooltip-pt-userpage": "اÙ\84باجة ØªØ§Ø¹ Ø§Ù\84Ù\85ستعÙ\85Ù\84Ù\8a {{GENDER:|تاعÙ\83}}",
+       "tooltip-pt-mytalk": "اÙ\84باجة ØªØ§Ø¹ Ø§Ù\84Ù\85Ù\87ادرة {{GENDER:|تاعÙ\83}}",
+       "tooltip-pt-preferences": "الختيّارات {{GENDER:|تاعك}}",
        "tooltip-pt-watchlist": "ليستت الباجات الي راك أتبع تبديلاتهم",
-       "tooltip-pt-mycontris": "ليسته نتع مساهماتك",
+       "tooltip-pt-mycontris": "الليستة تاع المساهمات {{GENDER:|تاعك}}",
        "tooltip-pt-login": "مادابيك تسجل دخلتك ، لكن ماشي لازم",
        "tooltip-pt-logout": "سجل خروج",
        "tooltip-pt-createaccount": "ننصح باش تصنع حساب و تسجل دخلتك ; على كل حال ماهوش ضروري",
        "tooltip-t-whatlinkshere": "ليستة تاع كامل باجات المحتاوا الواصله هنا",
        "tooltip-t-recentchangeslinked": "ليستة تاع التبديلات التوالا تاع الباجات الّي عندهم رباط معا هادي",
        "tooltip-feed-atom": "سيلان آتوم تاع هاد الباجة",
-       "tooltip-t-contributions": "شوفان ليسته مساهمات هاذا المستخدم",
+       "tooltip-t-contributions": "شوف ليستة تاع المساهمات تاع {{GENDER:$1|هاد المستعملي|هاد المستعمليّة}}",
        "tooltip-t-emailuser": "أرسل بريه لهاذ المستخدم",
        "tooltip-t-upload": "أرسل تصويرة و إلا أي ملف ميديا للسرفر",
        "tooltip-t-specialpages": "ليستة تاع كامل الباجات الخصوصيّة",
index 370ea04..1458449 100644 (file)
        "uploaded-script-svg": "Alcontróse l'elementu «$1» que puede recibir scripts nel ficheru SVG xubíu.",
        "uploaded-hostile-svg": "Alcontróse CSS inseguru nel elementu d'estilu del ficheru SVG xubíu.",
        "uploaded-event-handler-on-svg": "Nun se permite configurar los atributos de controladores de socesos <code>$1=\"$2\"</code> nos ficheros SVG.",
-       "uploaded-href-unsafe-target-svg": "Alcontróse un \"href\" a un destín inseguru <code>&lt;$1 $2=\"$3\"&gt;</code> nel ficheru SVG xubíu.",
+       "uploaded-href-attribute-svg": "Los atributos href de los ficheros SVG are sólo pueden enllazar a destinos http:// o https://, alcontróse <code>&lt;$1 $2=\"$3\"&gt;</code>.",
+       "uploaded-href-unsafe-target-svg": "Alcontróse un href a datos inseguros: la URI de destín <code>&lt;$1 $2=\"$3\"&gt;</code> nel ficheru SVG xubíu.",
        "uploaded-animate-svg": "Alcontróse la etiqueta \"animate\" que puede cambiar href, usando l'atributu \"from\" <code>&lt;$1 $2=\"$3\"&gt;</code> nel ficheru SVG xubíu.",
        "uploaded-setting-event-handler-svg": "Ta torgada la configuración d'atributos del xestor de socesos. Alcontróse <code>&lt;$1 $2=\"$3\"&gt;</code> nel ficheru SVG xubíu.",
        "uploaded-setting-href-svg": "Usar la etiqueta «set» p'amestar l'atributu «href» al elementu padre ta torgao.",
        "foreign-structured-upload-form-label-own-work-message-shared": "Certifico que tengo los drechos d'autor d'esti ficheru, y aceuto irrevocablemente lliberalu a Wikimedia Commons baxo la llicencia [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0], y aceuto les [https://wikimediafoundation.org/wiki/Terms_of_Use Condiciones d'usu].",
        "foreign-structured-upload-form-label-not-own-work-message-shared": "Si nun tienes los drechos d'autor d'esti ficheru, o quieres lliberalu baxo una llicencia diferente, considera usar el [https://commons.wikimedia.org/wiki/Special:UploadWizard Asistente de carga en Commons Upload].",
        "foreign-structured-upload-form-label-not-own-work-local-shared": "Tamién pué interesate usar [[Special:Upload|la páxina de carga de {{SITENAME}}]] si esti ficheru pué xubise allí baxo les sos polítiques.",
-       "foreign-structured-upload-form-2-label-intro": "Gracies por donar una imaxe pa usar en {{SITENAME}}. Sólo tendríes de siguir si la mesma cumple delles condiciones:",
-       "foreign-structured-upload-form-2-label-ownwork": "Tien que ser completamente <strong>la to propia creación</strong>, non alcontrada n'Internet",
-       "foreign-structured-upload-form-2-label-noderiv": "Nun tien de contener <strong>nengún trabayu d'otra persona</strong>, o tar inspiráu por otros",
-       "foreign-structured-upload-form-2-label-useful": "Tien de ser <strong>educativu y útil</strong> pa enseñar a otros",
-       "foreign-structured-upload-form-2-label-ccbysa": "Tien d'<strong>aceutar espublizase pa siempre</strong> n'Internet baxo la llicencia [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Reconocimientu-CompartirIgual 4.0]",
-       "foreign-structured-upload-form-2-label-alternative": "Si nun ye verdadero tolo anterior, inda podríes ser capaz de xubir esti ficheru usando l'[https://commons.wikimedia.org/wiki/Special:UploadWizard Asistente de xubíes de Commons], mentanto tea disponible baxo una llicencia llibre.",
-       "foreign-structured-upload-form-2-label-termsofuse": "Al xubir el ficheru, certifiques que tienes los drechos d'autor d'esti ficheru, y aceutes definitivamente espublizar esti ficheru en Wikimedia Commons baxo la llicencia Creative Commons Reconocimientu-CompartirIgual 4.0, y aceutes les [https://wikimediafoundation.org/wiki/Terms_of_Use Condiciones d'usu].",
-       "foreign-structured-upload-form-3-label-question-website": "¿Descargasti esta imaxe d'un sitiu web, o la alcontrasti con una gueta d'imáxenes?",
-       "foreign-structured-upload-form-3-label-question-ownwork": "¿Creasti esta imaxe (sacasti la semeya, ficisti'l dibuxu, etc.) tu mesmu?",
-       "foreign-structured-upload-form-3-label-question-noderiv": "¿Contien, o ta inspirada por, una obra propiedá d'otra persona, como un logo?",
-       "foreign-structured-upload-form-3-label-yes": "Sí",
-       "foreign-structured-upload-form-3-label-no": "Non",
-       "foreign-structured-upload-form-3-label-alternative": "Por desgracia, nesti casu, esta ferramienta nun tien encontu pa xubir esti ficheru. Índa podríes xubilu usando l'[https://commons.wikimedia.org/wiki/Special:UploadWizard Asistente de xubíes de Commons], mentanto tea disponible baxo una llicencia llibre.",
-       "foreign-structured-upload-form-4-label-good": "Usando esta ferramienta, puedes xubir gráficos educativos creaos por ti y fotografíes que sacaras, que nun contengan obres d'otres persones.",
-       "foreign-structured-upload-form-4-label-bad": "Nun puedes xubir imáxenes atopaes nun motor de gueta o descargaes d'otros sitios web.",
        "backend-fail-stream": "Nun se pudo tresmitir el ficheru $1.",
        "backend-fail-backup": "Nun se pudo facer copia de seguridá del ficheru $1.",
        "backend-fail-notexists": "El ficheru $1 nun esiste.",
        "querypage-disabled": "Esta páxina especial ta desactivada por razones de rindimientu.",
        "apihelp": "Ayuda de la API",
        "apihelp-no-such-module": "Nun s'alcuentra'l módulu «$1».",
+       "apisandbox": "Zona de pruebes API",
+       "apisandbox-api-disabled": "La API ta desactivada nesti sitiu.",
+       "apisandbox-intro": "Usa esta páxina pa esperimentar cola <strong>API de serviciu web de MediaWiki</strong>.\nConsulta [[mw:API:Main page|la documentación de la API]] pa más detalles tocante al so usu. Exemplu: [//www.mediawiki.org/wiki/API#A_simple_example llamar al conteníu d'una Páxina principal]. Seleiciona una aición pa ver más exemplos.\n\nTen presente que, anque esto ye una zona de pruebes, les aiciones que faigas nesta páxina puen camudar la wiki.",
+       "apisandbox-fullscreen": "Espander el panel",
+       "apisandbox-submit": "Facer solicitú",
+       "apisandbox-reset": "Llimpiar",
+       "apisandbox-examples": "Exemplos",
+       "apisandbox-results": "Resultaos",
+       "apisandbox-request-url-label": "URL de la solicitú:",
+       "apisandbox-request-time": "Duración de la solicitú: {{PLURAL:$1|$1 ms}}",
        "booksources": "Fontes de llibros",
        "booksources-search-legend": "Busca de fontes de llibros",
        "booksources-search": "Buscar",
index a879fbc..b530c19 100644 (file)
        "watchthisupload": "Bu faylı izlə",
        "filename-bad-prefix": "Yüklədiyiniz faylın adı, çox güman ki, rəqəmsal kameralar tərəfindən avtomatik olaraq əlavə edilən və heç bir açıqlaması olmayan '''\"$1\"''' ilə başlayır.\nXahiş edirik faylın adını daha düzgün seçin.",
        "filename-prefix-blacklist": " #<!-- Bu sətrə toxunmayın --> <pre>\n# Sintaksis aşağıdakı kimi görünür:\n#   * \"#\" simvolundan sətrin sonuna kimi yazılar şərhdir\n#   * Tipik fayl adları üçün olan prefiksdəki hər bir boş olmayan sətir rəqəmli kamera trəfindən avtomatik qeydə alınır\nCIMG # Casio\nDSC_ # Nikon\nDSCF # Fuji\nDSCN # Nikon\nDUW # digər mobil telefonlar\nIMG # generic\nJD # Jenoptik\nMGP # Pentax\nPICT # misc.\n #</pre> <!-- Bu sətrə toxunmayın -->",
-       "upload-success-subj": "Yükləmə tamamlandı",
-       "upload-failure-subj": "Yükləmə problemi",
-       "upload-failure-msg": "Yüklədiyiniz [$2] forması ilə bağlı problem yaranıb:\n\n$1",
-       "upload-warning-subj": "Yükləmə xəbərdarlığı",
        "upload-proto-error": "Yanlış protokol",
        "upload-file-error": "Daxili xəta",
        "upload-misc-error": "Naməlum yükləmə xətası",
        "pager-newer-n": "{{PLURAL:$1|1 daha yeni|$1 daha yeni}}",
        "pager-older-n": "{{PLURAL:$1|1 daha köhnə|$1 daha köhnə}}",
        "suppress": "Gizlət",
+       "apisandbox-results": "Nəticə",
        "booksources": "Kitab mənbələri",
        "booksources-search-legend": "Kitab mənbələri axtar",
        "booksources-isbn": "ISBN:",
        "wlheader-showupdated": "Son ziyarətinizdən sonra edilən dəyişikliklər '''qalın şriftlərlə''' göstərilmişdir.",
        "wlnote": "Aşağıdakı {{PLURAL:$1|'''$1''' dəyişiklik|'''$1''' dəyişiklik}} son {{PLURAL:$2|saatda|'''$2''' saatda}} edilmişdir.",
        "wlshowlast": "Son $1 saatı $2 günü göstər",
-       "watchlistall2": "hamısı",
        "wlshowhidemine": "mənimn redaktələrim",
        "watchlist-options": "İzləmə siyahısının nizamlamaları",
        "watching": "İzlənilir...",
        "modifiedarticleprotection": "\"[[$1]]\" səhifəsi üçün mühafizə səviyyəsi dəyişildi",
        "unprotectedarticle": "mühafizə kənarlaşdırıldı \"[[$1]]\"",
        "protect-title": "\"$1\" üçün mühafizə səviyyəsinin dəyişdirilməsi",
-       "prot_1movedto2": "[[$1]] adı dəyişildi. Yeni adı: [[$2]]",
+       "prot_1movedto2": "[[$1]] səhifəsinin adı dəyişilib. Yeni adı: [[$2]]",
        "protect-legend": "Qorumayı təsdiq et",
        "protectcomment": "Səbəb:",
        "protectexpiry": "Vaxtı bitib",
index 122ef3d..dab924e 100644 (file)
        "sig_tip": "سیزین ایمضانیز واخت ایله",
        "hr_tip": "دوزئی خط (آز ایشلدین)",
        "summary": "قیساسی:",
-       "subject": "Ù\82Ù\88Ù\86Ù\88:",
+       "subject": "Ù\85Ù\88ضÙ\88ع:",
        "minoredit": "بو بیر کیچیک دَییشدیرمه‌دیر",
        "watchthis": "بو صفحه‌نی ایزله",
-       "savearticle": "صÙ\81Ø­Ù\87â\80\8cÙ\86Û\8c Ø³Ø§Ø®Ù\84ا",
+       "savearticle": "صÙ\81Ø­Ù\87â\80\8cÙ\86Û\8c Ø°Ø®Û\8cرÙ\87 Ø§Ø¦Øª",
        "preview": "اؤن‌گؤستریش",
        "showpreview": "سیناق گؤستریش",
        "showdiff": "دَییشیکلیکلری گؤستر",
        "upload-dialog-button-done": "اولدو",
        "upload-dialog-button-save": "ذخیره ائت",
        "upload-dialog-button-upload": "یوکله",
-       "upload-form-label-select-file": "فایل سئچ",
        "upload-form-label-infoform-title": "جوزئیات",
        "upload-form-label-infoform-name": "آد",
        "upload-form-label-infoform-description": "آچیقلاما",
        "double-redirect-fixed-move": "[[$1]] آپاریلمیش‌دیر.\nاوْتوماتیک اوْلاراق گۆنجل‌له‌نیبدیر و ایندی [[$2]]-ه یوْل‌لاندیریر.",
        "double-redirect-fixed-maintenance": "[[$1]]-دن [[$2]]-ه ایکی‌قات یوْل‌لاندیرما، بیر ساخلاما ایشین‌ده، اوْتوماتیک اوْلاراق دۆزلدیلیر.",
        "double-redirect-fixer": "يؤنلندیرمه تعمیرجیسی",
-       "brokenredirects": "خطالی ایستیقامتلندیرمه",
+       "brokenredirects": "خطالی یوْللاندیرمالار",
        "brokenredirectstext": "آشاغی‌داکی ایستیقامتلندیرمه‌لر مؤوجود اولمایان صحیفه‌لره کئچید وئریر:",
        "brokenredirects-edit": "دَییشدیر",
        "brokenredirects-delete": "سیل",
        "withoutinterwiki-summary": "آشاغیداکی صحیفه‌لر، باشقا دیل‌لره باغلانتیلاری یوخدور.",
        "withoutinterwiki-legend": "اؤن‌اَک",
        "withoutinterwiki-submit": "گؤستر",
-       "fewestrevisions": "ان آز دَییشدیریلن صحیفه‌لر",
+       "fewestrevisions": "ان آز دَییشدیریلن صفحه‌لر",
        "nbytes": "{{PLURAL:$1|بیر|$1}} بایت",
        "ncategories": "{{PLURAL:$1|بیر|$1}} بؤلمه",
        "ninterwikis": "{{PLURAL:$1|بیر|$1}} ویکی‌آراسی",
        "specialpage-empty": "بو صحیفه بوشدور",
        "lonelypages": "يئتیم صحیفه‌‌لر",
        "lonelypagestext": "آشاغی‌داکی صحیفه‌لره {{SITENAME}} سایتین‌داکی دیگر صحیفه‌لردن علاقه وئریلممیش یا دا چارپاز داخیل ائدیلممیش.",
-       "uncategorizedpages": "بؤلمه‌سیز صحیفه‌لر",
+       "uncategorizedpages": "بؤلمه‌سیز صفحه‌لر",
        "uncategorizedcategories": "بؤلمه‌سیز بؤلمه‌لر",
-       "uncategorizedimages": "بؤلمه‌سیز شکیل‌لر",
+       "uncategorizedimages": "بؤلمه‌سیز فایللار",
        "uncategorizedtemplates": "بؤلمه‌سیز شابلونلار",
        "unusedcategories": "ایستیفاده ائدیلمه‌میش بؤلمه‌لر",
        "unusedimages": "ایشلنمه‌میش فایل‌لار",
        "prefixindex-namespace": "بوتون صفحه لر (آد فضاسی$1) قاباق دان یاپیشیقی وار",
        "prefixindex-strip": "لیست‌ده، اؤن‌اَکی قوْپارت",
        "shortpages": "قیسا صحیفه‌‌لر",
-       "longpages": "اوزون صحیفه‌‌لر",
+       "longpages": "اۇزون صفحه‌‌لر",
        "deadendpages": "کئچید وئرمه‌ين صحیفه‌‌لر",
        "deadendpagestext": "آشاغیداکی صحیفه‌‌لردن بو ویکیپئدیياداکی دیگر صحیفه‌‌لره هئچ بیر کئچید يوخدور.",
        "protectedpages": "محافظه‌‌لی صحیفه‌‌لر",
        "protectedtitles-summary": "بۇ صحیفه، ایندیکی یارانماق‌دان قوْرونان باشلیقلاری لیست ائدیر. ایندیکی قوْرونان موْجود اوْلان صحیفه‌لرین لیستینی گؤرمک اۆچون، [[{{#special:ProtectedPages}}|{{int:protectedpages}}]]-ه باخین.",
        "protectedtitlesempty": "حال-حاضردا، بو پارامئترلری قورونان هئچ بیر موضوع یوخ‌دور.",
        "listusers": "ایشلدن لیستی",
-       "listusers-editsonly": "يالنیز دَییشدیرمه ائدن ایستیفاده‌چیلری گؤستر",
+       "listusers-editsonly": "تکجه دَییشدیرمه ائدن ایشلدنلری گؤستر",
        "listusers-creationsort": "يارانما تاریخینه گؤره سیرالا",
        "listusers-desc": "آزالان سیرادا سیرالا",
        "usereditcount": "$1 {{PLURAL:$1|دَییشدیرمه}}",
        "usercreated": "$1 تاریخینده، ساعات $2-ده {{GENDER:$3|یارانیب‌دیر}}",
        "newpages": "يئنی صفحه‌لر",
        "newpages-username": "ایشلدن آدی:",
-       "ancientpages": "ان اسکی صحیفه‌لر",
+       "ancientpages": "ان اسکی صفحه‌لر",
        "move": "آدینی دَییشدیر",
        "movethispage": "بو صحیفه‌‌نین آدینی ديَیشدیر",
        "unusedimagestext": "آشاغی‌داکی فایل‌لار وار آنجاق هر هانسی بیر صحیفه‌ده باسدیریلمیش دئییل.\nخاهیش ائدیریک اونوتمایین کی، دیگر web سایت‌لاری بیر فایلا بیرباشا بیر اورل ایله علاقه وئره بیلر، و بونا گؤره ائففئکتیو ایستیفاده‌ده اولماسا بئله هله بورادا لیستنبیلیر.",
        "querypage-disabled": "پِرفورمانس اوچون بو اؤزل صحیفه باغلانیب‌دیر.",
        "apihelp": "API یاردیمی",
        "apihelp-no-such-module": "«$1» ماژول تاپیلمادی.",
+       "apisandbox-reset": "تمیزله",
+       "apisandbox-examples": "میثال",
+       "apisandbox-results": "نتیجه",
+       "apisandbox-request-time": "زامان ایستمک:$1",
        "booksources": "کیتاب قایناقلاری",
        "booksources-search-legend": "کیتاب قایناقلارین آختار",
        "booksources-search": "آختار",
        "email-legend": "باشقا {{SITENAME}} ایستیفاده‌چیسینه ایمیل گؤندر",
        "emailfrom": "کیم‌دن:",
        "emailto": "کیمه:",
-       "emailsubject": "Ù\82Ù\88Ù\86Ù\88:",
+       "emailsubject": "Ù\85Ù\88ضÙ\88ع:",
        "emailmessage": "مئساژ",
        "emailsend": "گؤندر",
        "emailccme": "مئساژیمین بیر کوپیسینی ده منه ایمیل ائت.",
        "deleting-backlinks-warning": "'''اخطار:''' بو سیلمگه قصدینیز اولان صفحه‌یه، [[Special:WhatLinksHere/{{FULLPAGENAME}}|باشقا صفحه‌لر]] باغلانتی وئریب یا اونو اؤزلرین‌ده ایشلدیب‌لر.",
        "rollback": "اوولکی نوسخه لر",
        "rollbacklink": "قایتار",
-       "rollbacklinkcount": "گیتیرلمه $1  {{PLURAL:$1|دییشمک |دییشمک}} دییشدیرمه",
-       "rollbacklinkcount-morethan": "گیتیرلمه آرتیق $1 {{PLURAL:$1|دییشمک |دییشمک}} دییشدیرمه",
+       "rollbacklinkcount": "$1 دییشدیرمه‌نی قایتار",
+       "rollbacklinkcount-morethan": "$1-دن چوْخ دییشدیرمه‌نی قایتار",
        "rollbackfailed": "گئری قایتارما اوغورسوزدور",
        "cantrollback": "دییشدیر گئری قایتاریلا بیلمز؛ آخیرینجی دییشدیر صحیفه‌ده اولان یئگانه فالیت‌دیر.",
        "alreadyrolled": "[[User:$2|$2]] ([[User talk:$2|موزاکیره]] {{int:pipe-separator}} [[Special:Contributions/$2| {{int:contribslink}}]]) طرفین‌دن [[:$1]] صحیفه‌سینده ائدیلمیش سون دییشیک‌لیک گئرییه آلینا بیلمیر؛\nباشقا بیری صحیفه‌ده دییشیک‌لیک ائتدی یا دا صحیفنی گئرییه آلدی.\n\nسون دییشیک‌لیگی ائدن: [[User:$3|$3]] ([[User talk:$3|تالک]] {{int:pipe-separator}} [[Special:Contributions/$3| {{int:contribslink}}]] ).",
        "protect-level-autoconfirmed": "تکجه اوْتوماتیک تأیید اوْلموش ایشلدن‌لره ایجازه وئر",
        "protect-level-sysop": "یالنیز ایداره‌چیلره ایجازه وئر",
        "protect-summary-cascade": "پیلله‌لی",
-       "protect-expiring": "$1 (UTC)- تاریخینده واختی بیتیر",
-       "protect-expiring-local": "$1-ده بیتیر",
+       "protect-expiring": "$1 (UTC)- تاریخینده وقتی قۇرتولور",
+       "protect-expiring-local": "$1-ده قۇرتولور",
        "protect-expiry-indefinite": "سوْن‌سۇز",
        "protect-cascade": "بو صحیفه‌ده ایستیفاده ائدیلن بوتون صحیفه‌لری قوروماغا آل (پیلله‌لی قوروماق)",
        "protect-cantedit": "بو صحیفه‌نین محافظه درجه‌سینی دییش‌دیره بیلمزسینیز، چونکی بو دییشیک‌لیک اوچون حقوقونوز یوخ‌دور.",
        "sp-contributions-toponly": "تکجه سون نوسخه اولان دییشیکلری گؤستر",
        "sp-contributions-newonly": "تکجه صفحه یاراتماق دَییشیکلیکلرینی گؤستر",
        "sp-contributions-submit": "آختار",
-       "whatlinkshere": "بو صفحه‌یه باغلانتیلار",
+       "whatlinkshere": "بۇ صفحه‌‌يه باغلانتیلار",
        "whatlinkshere-title": "«$1»-ه باغلانان صحیفه‌لر",
        "whatlinkshere-page": "صفحه:",
        "linkshere": "آشاغیداکی صفحه‌لر '''[[:$1]]'''-ه باغلانیب:",
        "reblock-logentry": "[[$1]] اوچون سون تاریخی $2 $3 اولماق اوزره بلوک پارامئترلری دییشدیریلدی",
        "blocklogtext": "ایستیفاده‌چی‌لرین باغلانماسی و باغلانماقین گؤتورولمه‌سی سیاهی‌سی.\nآوتوماتیک باغلانمیش ای پی-عنوان‌لار بورادا گؤستریلمیر.\nحال-هازیرکی [[Special:BlockList|قاداغا‌لارین و بلوکلاما‌لارین سیاهی‌سی]]نا باخ.",
        "unblocklogentry": "$1 اوزرین‌دکی آچیلدی",
-       "block-log-flags-anononly": "يالنیز قئيدیاتسیز ایستیفاده‌چیلر",
+       "block-log-flags-anononly": "\nتکجه تایید اوْلونمامیش ایشلدنلر",
        "block-log-flags-nocreate": "حساب یاراتماق اولماز",
        "block-log-flags-noautoblock": "آوتوبلوکلاما غيری مومکوندور",
        "block-log-flags-noemail": "ائ-مایل بلوکلانیب",
        "import-rootpage-invalid": "وئریلن کؤک صحیفه‌‌سی اعتبارسیز آددیر.",
        "import-rootpage-nosubpage": "آد فضا سی  \"$1\" آنا باسئ ٔآلت صحیفه اوچون اجازه وئرمیر.",
        "importlogpage": "چیخاریلما گونده‌لیگی",
-       "importlogpagetext": "باشÙ\82ا Ù\88Û\8cÚ©Û\8cÙ\84ردÙ\86Ø\8c Ø¯Ù\8eÛ\8cÛ\8cØ´Û\8cÚ©Ù\84Û\8cÚ© Ú¯Ø¦Ú\86Ù\85Û\8cØ´Ù\84رÛ\8cÙ\84Ù\87 Ø¨Û\8cرÙ\84Û\8cÚ©â\80\8cدÙ\87 Ú¯ØªÛ\8cرÛ\8cÙ\84Ù\85Û\8cØ´ ØµØ­Û\8cÙ\81ه‌لر.",
+       "importlogpagetext": "Ø¢Û\8cرÛ\8c Ù\88Û\8cÚ©Û\8cÙ\84ردÙ\86Ø\8c Ø¯Ù\8eÛ\8cÛ\8cØ´Û\8cÚ©Ù\84Û\8cÚ© Ú¯Ø¦Ú\86Ù\85Û\8cØ´Ù\84رÛ\8cÙ\84Ù\87 Ø¨Û\8cرÙ\84Û\8cÚ©â\80\8cدÙ\87 Ú¯ØªÛ\8cرÛ\8cÙ\84Ù\85Û\8cØ´ ØµÙ\81Ø­ه‌لر.",
        "import-logentry-upload-detail": "{{PLURAL:$1|بیر|$1}} نوسخه ایچری گتیریلدی",
        "import-logentry-interwiki-detail": "$2-دن {{PLURAL:$1|بیر|$1}} نوسخه ایچری گتیریلدی",
        "javascripttest": "جاوااسکریپت تِستی",
        "logentry-delete-delete": "$1، $3 صفحه‌سینی {{GENDER:$2|سیلدی}}",
        "logentry-delete-restore": "$1، $3 صفحه‌سینی {{GENDER:$2|قایتاردی}}",
        "logentry-delete-event": "$1، $3-ده $5 سیاهی اولایینین {{PLURAL:$5|گؤرونوشونو|گؤرونوشلرینی}} {{GENDER:$2|دَییشدیردی}}: $4",
-       "logentry-delete-revision": "$1، $3 صحیفه‌سینده $5 نوسخه‌نین {{PLURAL:گؤرونوشونو|گؤرونوشلرینی}} {{GENDER:$2|دَییشدیردی}}: $4",
+       "logentry-delete-revision": "$1، $3 صفحه‌سینده $5 نوسخه‌نین {{PLURAL:گؤرونوشونو|گؤرونوشلرینی}} {{GENDER:$2|دَییشدیردی}}: $4",
        "logentry-delete-event-legacy": "$1، $3-ده سیاهی اولایلارینین گؤرونوشلرینی {{GENDER:$2|دَییشدیردی}}",
        "logentry-delete-revision-legacy": "$1، $3 صحیفه‌سینده نوسخه‌لرین گؤرونوشلرینی {{GENDER:$2|دَییشدیردی}}",
        "logentry-suppress-delete": "$1، $3 صفحه‌سینی {{GENDER:$2|یاتیردی}}",
        "logentry-suppress-event": "$1، $3-ده $5 سیاهی اولایینین {{PLURAL:$5|گؤرونوشونو|گؤرونوشلرینی}} گیزلینجه {{GENDER:$2|دَییشدیردی}}: $4",
-       "logentry-suppress-revision": "$1، $3 صحیفه‌سینده $5 نوسخه‌نین {{PLURAL:گؤرونوشونو|گؤرونوشلرینی}} گیزلینجه {{GENDER:$2|دَییشدیردی}}: $4",
+       "logentry-suppress-revision": "$1، $3 صفحه‌سینده $5 نوسخه‌نین {{PLURAL:گؤرونوشونو|گؤرونوشلرینی}} گیزلینجه {{GENDER:$2|دَییشدیردی}}: $4",
        "logentry-suppress-event-legacy": "$1، $3-ده سیاهی اولایلارینین گؤرونوشلرینی گیزلینجه {{GENDER:$2|دَییشدیردی}}",
        "logentry-suppress-revision-legacy": "$1، $3 صحیفه‌سینده نوسخه‌لرین گؤرونوشلرینی گیزلینجه {{GENDER:$2|دَییشدیردی}}",
        "revdelete-content-hid": "ایچینده‌کیلر گیزلی‌دیر",
        "revdelete-uname-unhid": "ایستیفاده‌چی آدی گیزلیلیک‌دن چیخدی",
        "revdelete-restricted": "ایداره‌چیلره محدودیت قویدو",
        "revdelete-unrestricted": "ایداره‌چیلرین محدودیتلرینی گؤتوردو",
-       "logentry-block-block": "$1 {{GENDER:$4|$3}}-نی {{GENDER:$2|بلوکلادی}}. قورتارماق تاریخی: $5 $6",
+       "logentry-block-block": "$1 {{GENDER:$4|$3}}-نی {{GENDER:$2|باغلادی}}. قۇرتارماق تاریخی: $5 $6",
        "logentry-block-unblock": "$1 {{GENDER:$4|$3}}-نین {{GENDER:$2|بلوکلاماغینی قالدیردی}}",
+       "logentry-import-upload": "$1 $3-نی فایل یوکله‌مه یولو ایله {{GENDER:$2|ایچری گتیردی}}",
+       "logentry-import-upload-details": "$1 $3-نی فایل یوکله‌مه یولو ایله {{GENDER:$2|ایچری گتیردی}} ($4 {{PLURAL:$4|نوسخه}})",
+       "logentry-import-interwiki-details": "$1 $3-نی $5-دن {{GENDER:$2|ایچری گتیردی}} ($4 {{PLURAL:$4|نوسخه}})",
        "logentry-move-move": "$1، $3 صفحه‌سینی $4-ه {{GENDER:$2|آپاردی}}",
        "logentry-move-move-noredirect": "$1، $3 صفحه‌سینی، یوْل‌لاندیرما قوْیماماق‌لا، $4-ه {{GENDER:$2|آپاردی}}",
        "logentry-move-move_redir": "$1، $3 صفحه‌سینی، $4-ده یوْل‌لاندیرما اۆستونه {{GENDER:$2|آپاردی}}",
        "logentry-move-move_redir-noredirect": "$1، $3 صفحه‌سینی، یوْل‌لاندیرما قوْیماماق‌لا، یوْل‌لاندیرما اوْلان $4 اۆستونه {{GENDER:$2|آپاردی}}",
        "logentry-patrol-patrol": "$1، $3 صحیفه‌سینین $4 نوسخه‌سینی، نظارتلنمیش {{GENDER:$2|نیشانلادی}}",
-       "logentry-patrol-patrol-auto": "$1، $3 صحیفه‌سینین $4 نوسخه‌سینی، اوتوماتیک اولاراق نظارتلنمیش {{GENDER:$2|نیشانلادی}}",
+       "logentry-patrol-patrol-auto": "$1، $3 صفحه‌سینین $4 نوسخه‌سینی، اوْتوماتیک یوْخلانمیش {{GENDER:$2|علامتلدی}}",
        "logentry-newusers-newusers": " بیر ایستیفاده‌چی حسابی $1 {{GENDER:$2|یاراتدی}}",
        "logentry-newusers-create": "$1 ایشلدن حسابی {{GENDER:$2|یارادیلدی}}",
        "logentry-newusers-create2": "$1 ایستیفاده‌چی، $3 حسابی {{GENDER:$2|یاراتدی}}",
        "feedback-error2": "خطا: دَییشدیرمه باشاری‌سیز اولدو",
        "feedback-error3": "خطا: API-دان جاواب گلمه‌دی",
        "feedback-message": "مئساژ:",
-       "feedback-subject": "Ù\82Ù\88Ù\86Ù\88:",
+       "feedback-subject": "Ù\85Ù\88ضÙ\88ع:",
        "feedback-submit": "گؤندر",
        "feedback-thanks": "تشکورلر! سیزین گئری-بیلدیریمینیز «[$2 $1]» صحیفه‌سینه گؤندریلدی.",
        "feedback-thanks-title": "تشکورلر!",
index 8cb4b42..637418e 100644 (file)
        "october-date": "Октябрь $1",
        "november-date": "Ноябрь $1",
        "december-date": "Сентябрь $1",
+       "period-am": "ТК",
+       "period-pm": "ТС",
        "pagecategories": "{{PLURAL:$1|1=Категория|Категориялар}}",
        "category_header": "«$1» категорияһындағы биттәр",
        "subcategories": "Эске категориялар",
        "pool-timeout": "Блоклауҙы көтөү ваҡыты үтте",
        "pool-queuefull": "Һорауҙар сираты тулы",
        "pool-errorunknown": "Билдәһеҙ хата",
+       "pool-servererror": "Пул иҫәпләү хеҙмәте эшләмәй ($1).",
        "poolcounter-usage-error": "$1: ҡулланыу хатаһы",
        "aboutsite": "{{SITENAME}} тураһында",
        "aboutpage": "Project:Тасуирлама",
        "pagetitle": "{{SITENAME}} проектынан",
        "retrievedfrom": "Сығанағы — «$1»",
        "youhavenewmessages": "Яңы $1 бар ($2).",
-       "youhavenewmessagesfromusers": "Һеҙгә {{PLURAL:$3|1=башҡа ҡатнашыусынан|$3 ҡатнашыусынан}} $1 бар ($2).",
+       "youhavenewmessagesfromusers": "{{PLURAL:$4|Һеҙгә}} {{PLURAL:$3|$3 ҡатнашыусыһана}} $1 килде ($2).",
        "youhavenewmessagesmanyusers": "Һеҙгә күп ҡатнашыусынан $1 бар ($2).",
        "newmessageslinkplural": "{{PLURAL:$1|1=яңы хәбәр|яңы хәбәр}}",
-       "newmessagesdifflinkplural": "һуңғы {{PLURAL:$1|1=үҙгәртеү|үҙгәртеү}}",
+       "newmessagesdifflinkplural": "һуңғы {{PLURAL:$1|үҙгәртеү|999=үҙгәртеүҙәр}}",
        "youhavenewmessagesmulti": "Һеҙгә яңы хәбәрҙәр бар: $1",
        "editsection": "үҙгәртергә",
        "editold": "төҙәтеү",
        "readonly_lag": "Өҫтәмә сервер төп сервер менән синхронлашҡанға тиклем мәғлүмәттәр базаһы автоматик рәүештә үҙгәрештәргә ҡаршы ябылған.",
        "internalerror": "Эске хата",
        "internalerror_info": "Эске хата: $1",
+       "internalerror-fatal-exception": "$1 тибындағы төҙәтә алмаҫлыҡ ҡағиҙәнән ситкә тайпылыш",
        "filecopyerror": "«$2» файлына «$1» файлының күсермәһен яһап булмай.",
        "filerenameerror": "«$1» файлының исемен «$2» исеменә алмаштырып булмай.",
        "filedeleteerror": "«$1» файлын юйып булмай.",
        "badtitle": "Ярамаған исем",
        "badtitletext": "Биттең һоратылған исеме дөрөҫ түгел, буш йәки телдәр араһы йәки интервики исеме яңылыш күрһәтелгән. Исемдә тыйылған символдар булыуы ла мөмкин.",
        "title-invalid-empty": "Һоратылған бит башлығы буш йәки исемдәр арауығы була.",
+       "title-invalid-utf8": "Һеҙ эҙләгән биттә  UTF-8 дөрөҫ булмаған символдар теҙмәһе бар.",
        "perfcached": "Был мәғлүмәттәр кэштан алынған, уларҙа һуңғы үҙгәртеүҙәр булмаҫҡа мөмкин. Кэшта иң күбе {{PLURAL:$1|язма}} һаҡлана.",
        "perfcachedts": "Был мәғлүмәттәр кэштан алынған, ул һуңғы тапҡыр $1 яңыртылды.  Кэшта иң күбе  {{PLURAL:$4|язма}} һаҡлана",
        "querypage-no-updates": "Был битте яңыртыу хәҙер тыйылған.\nБында күрһәтелгән мәғлүмәттәр яңыртылмаясаҡ.",
        "querypage-disabled": "Был махсус бит һөҙөмтәлелекте арттырыу өсөн ябылған.",
        "apihelp": "API белешмәһе",
        "apihelp-no-such-module": "«$1» модуле табылмаған.",
+       "apisandbox": "API һынау урыны",
+       "apisandbox-api-disabled": "Был сайтта API һүндерелгән.",
+       "apisandbox-intro": "''MediaWiki API''' өйрәнеү бите.  API ҡулланыу тураһында белешмә алыу өсөн [//www.mediawiki.org/wiki/API:Main_page API документацияһы]на мөрәжәғәт итегеҙ. Мәҫәләң, [//www.mediawiki.org/wiki/API#A_simple_example Башбит эстәлеген нисек алырға]. Башҡа миҫалдарҙы күреү өсөн ғәмәлде ҡулланығыҙ.",
+       "apisandbox-submit": "Һоратыу яһарға",
+       "apisandbox-reset": "Таҙарт",
+       "apisandbox-examples": "Миҫал",
+       "apisandbox-results": "Һөҙөмтә",
+       "apisandbox-request-url-label": "Һоратыуҙың URL-адресы:",
+       "apisandbox-request-time": "Мөрәжәғәт ваҡыты:$1",
        "booksources": "Китап сығанаҡтары",
        "booksources-search-legend": "Китап сығанаҡтарын эҙлә",
        "booksources-search": "Эҙләү",
        "contributions": "{{GENDER:$1|Ҡатнашыусы}} башҡарған эш",
        "contributions-title": "$1 исемле ҡатнашыусы башҡарған эш",
        "mycontris": "Башҡарған эштәр",
+       "anoncontribs": "башҡарған эштәр",
        "contribsub2": "{{GENDER:$3|$1}} башҡарған эше ($2)",
        "nocontribs": "Күрһәтелгән шарттарға яуап биргән үҙгәртеүҙәр табылманы.",
        "uctop": "(ағымдағы)",
index 3a2f425..5065d6b 100644 (file)
        "botpasswords-bad-appid": "Назва робата «$1» зьяўляецца няслушнай.",
        "botpasswords-insert-failed": "Не атрымалася дадаць робата зь імем «$1». Магчыма, ён ужо быў дададзены?",
        "botpasswords-update-failed": "Не атрымалася абнавіць робата зь імем «$1». Магчыма, ён быў выдалены?",
+       "botpasswords-created-title": "Пароль робата створаны",
+       "botpasswords-created-body": "Пароль робата «$1» быў пасьпяхова створаны.",
+       "botpasswords-updated-title": "Пароль робата абноўлены",
+       "botpasswords-updated-body": "Пароль робата «$1» быў пасьпяхова абноўлены.",
+       "botpasswords-deleted-title": "Пароль робата выдалены",
+       "botpasswords-deleted-body": "Пароль робата «$1» быў выдалены.",
+       "botpasswords-newpassword": "Новы пароль для ўваходу пад <strong>$1</strong> — <strong>$2</strong>. <em>Калі ласка, запішыце яго для далейшага выкарыстаньня.</em>",
+       "botpasswords-no-provider": "BotPasswordsSessionProvider недаступны.",
+       "botpasswords-restriction-failed": "Уваход ня выкананы праз абмежаваньні на пароль робата",
+       "botpasswords-invalid-name": "Пададзенае імя ўдзельніка ня ўтрымлівае падзяляльнік для паролю робата («$1»).",
+       "botpasswords-not-exist": "Удзельнік «$1» ня мае паролю для робата з назвай «$2».",
        "resetpass_forbidden": "Пароль ня можа быць зьменены",
        "resetpass-no-info": "Для непасрэднага доступу да гэтай старонкі Вам неабходна ўвайсьці ў сыстэму.",
        "resetpass-submit-loggedin": "Зьмяніць пароль",
        "previewnote": "'''Гэта толькі папярэдні прагляд.'''\nВашыя зьмены яшчэ не былі захаваныя!",
        "continue-editing": "Перайсьці да рэдагаваньня",
        "previewconflict": "Гэта папярэдні прагляд тэксту зь верхняга вакна рэдагаваньня, так ён будзе выглядаць, калі Вы вырашыце яго захаваць.",
-       "session_fail_preview": "'''Не атрымалася захаваць Вашую праўку праз тое, што былі страчаныя зьвесткі пра сэсію.\nКалі ласка, паспрабуйце яшчэ раз. Калі памылка ня зьнікне, паспрабуйце [[Special:UserLogout|выйсьці з сыстэмы]] і ўвайсьці ізноў.'''",
-       "session_fail_preview_html": "'''Не атрымалася захаваць Вашую праўку праз тое, што былі страчаныя зьвесткі пра сэсію.'''\n\n''Таму што ў {{GRAMMAR:месны|{{SITENAME}}}} дазволена выкарыстоўваць чысты HTML, папярэдні прагляд быў адключаны для засьцярогі ад атакаў праз JavaScript.''\n\n'''Калі гэта сапраўдная спроба рэдагаваньня, калі ласка, паспрабуйце яшчэ раз. Калі гэта не дапамагае, паспрабуйце [[Special:UserLogout|выйсьці з сыстэмы]] і ўвайсьці ізноў.'''",
+       "session_fail_preview": "Выбачайце! Мы не змаглі апрацаваць вашую праўку праз страту зьвестак сэсіі.\n\nМагчыма, вы выйшлі з сыстэмы. <strong>Калі ласка, праверце, што вы знаходзіцеся ў сыстэме і паспрабуйце яшчэ раз<strong>. Калі не спрацуе, паспрабуйце [[Special:UserLogout|выйсьці]] і ўвайсьці яшчэ раз, а таксама праверце, што ваш браўзэр дазваляе файлы-кукі з гэтага сайту.",
+       "session_fail_preview_html": "Выбачайце! Мы не змаглі апрацаваць вашую праўку праз страту зьвестак сэсіі.\n\n<em>Таму што ў {{GRAMMAR:месны|{{SITENAME}}}} дазволена выкарыстоўваць чысты HTML, папярэдні прагляд быў адключаны для засьцярогі ад атакаў праз JavaScript.</em>\n\n<strong>Калі гэта сапраўдная спроба рэдагаваньня, калі ласка, паспрабуйце яшчэ раз.</strong>\nКалі гэта не дапамагае, паспрабуйце [[Special:UserLogout|выйсьці з сыстэмы]] і ўвайсьці ізноў, а таксама праверце, што ваш браўзер дазваляе кукі-файлы з гэтага сайту.",
        "token_suffix_mismatch": "'''Вашае рэдагаваньне было адхіленае, таму што Ваш кліент ня можа апрацоўваць знакі пунктуацыі ў акне рэдагаваньня.\nРэдагаваньне было скасаванае для таго, каб пазьбегнуць зьнішчэньня тэксту старонкі.\nТакія памылкі здараюцца, калі Вы выкарыстоўваеце ананімны проксі-сэрвэр, які ўтрымлівае памылкі.'''",
        "edit_form_incomplete": "'''Некаторыя часткі формы рэдагаваньня не дасягнулі сэрвэра. Упэўніцеся, што Вашыя рэдагаваньні не пашкоджаныя і паспрабуйце зноў.'''",
        "editing": "Рэдагаваньне: $1",
        "mergehistory-empty": "Няма гісторыі рэдагаваньняў, якую магчыма аб'яднаць.",
        "mergehistory-done": "$3 {{PLURAL:$3|вэрсія|вэрсіі|вэрсіяў}} з $1 пасьпяхова аб’яднаныя ў [[:$2]].",
        "mergehistory-fail": "Не атрымалася аб'яднаць гісторыі старонак. Калі ласка, праверце парамэтры старонкі і часу.",
+       "mergehistory-fail-bad-timestamp": "Няслушная метка часу.",
+       "mergehistory-fail-invalid-source": "Няслушная старонка-крыніца.",
+       "mergehistory-fail-invalid-dest": "Няслушная мэтавая старонка.",
        "mergehistory-fail-toobig": "Немагчыма аб’яднаць гісторыю, бо будзе перавышаны ліміт у $1 {{PLURAL:$1|1=вэрсію|вэрсіі|вэрсіяў}}, якія будуць перанесеныя.",
        "mergehistory-no-source": "Не існуе крынічнай старонкі $1.",
        "mergehistory-no-destination": "Не існуе мэтавай старонкі $1.",
        "right-managechangetags": "ствараць і выдаляць [[Special:Tags|меткі]] з базы зьвестак",
        "right-applychangetags": "дадаваць [[Special:Tags|меткі]] пры рэдагаваньні",
        "right-changetags": "дадаваць і выдаляць адвольныя [[Special:Tags|меткі]] да асобных вэрсіяў і запісаў у журнале падзеяў",
+       "grant-generic": "Набор правоў «$1»",
+       "grant-group-page-interaction": "Узаемадзеньне з старонкамі",
        "grant-createaccount": "Стварыць рахункі",
        "grant-createeditmovepage": "Ствараць, рэдагаваць і пераносіць старонкі",
        "grant-delete": "Выдаляць старонкі, вэрсіі і запісы журналу",
        "uploaded-script-svg": "У загружаным SVG-файле знойдзены небясьпечны элемэнт з падтрымкай сцэнароў «$1».",
        "uploaded-hostile-svg": "Знойдзены небясьпечны CSS у элемэнце стылю загружанага SVG-файла.",
        "uploaded-event-handler-on-svg": "Усталёўваць атрыбуты апрацоўніка падзеяў <code>$1=\"$2\"</code> не дазволена для SVG-файлаў.",
-       "uploaded-href-unsafe-target-svg": "У Ð·Ð°Ð³Ñ\80Ñ\83жанÑ\8bм SVG-Ñ\84айле Ð·Ð½Ð¾Ð¹Ð´Ð·ÐµÐ½Ð°Ñ\8f Ñ\81паÑ\81Ñ\8bлка Ð½Ð° Ð½ÐµÐ±Ñ\8fÑ\81Ñ\8cпеÑ\87нÑ\83Ñ\8e Ð¼Ñ\8dÑ\82Ñ\83 <code>&lt;$1 $2=\"$3\"&gt;</code>.",
+       "uploaded-href-unsafe-target-svg": "У Ð·Ð°Ð³Ñ\80Ñ\83жанÑ\8bм SVG-Ñ\84айле Ð·Ð½Ð¾Ð¹Ð´Ð·ÐµÐ½Ð°Ñ\8f Ñ\81паÑ\81Ñ\8bлка Ð½Ð° Ð½ÐµÐ±Ñ\8fÑ\81Ñ\8cпеÑ\87нÑ\8bÑ\8f Ð·Ñ\8cвеÑ\81Ñ\82кÑ\96: URI-мÑ\8dÑ\82Ñ\8b <code>&lt;$1 $2=\"$3\"&gt;</code>.",
        "uploaded-animate-svg": "У загружаным SVG-файле знойдзены тэг «animate», які можа зьмяняць спасылку з дапамогай атрыбуту «from» <code>&lt;$1 $2=\"$3\"&gt;</code>.",
        "uploaded-setting-event-handler-svg": "Усталёўка атрыбутаў апрацоўкі падзеяў заблякаваная, у загружаным SVG-файле знойдзены код <code>&lt;$1 $2=\"$3\"&gt;</code>.",
        "uploaded-setting-href-svg": "Выкарыстаньне тэгу «set» для дадаваньня атрыбуту «href» у бацькоўскі элемэнт заблякаванае.",
        "upload-dialog-button-done": "Зроблена",
        "upload-dialog-button-save": "Захаваць",
        "upload-dialog-button-upload": "Загрузіць",
-       "upload-form-label-select-file": "Абраць файл",
        "upload-form-label-infoform-title": "Падрабязнасьці",
        "upload-form-label-infoform-name": "Назва",
        "upload-form-label-infoform-name-tooltip": "Унікальнае апісаньне файлу, якое будзе выкарыстоўвацца як яго назва. Вы можаце карыстацца звычайнай мовай з прабеламі. Не дадавайце пашырэньне файлу.",
        "foreign-structured-upload-form-label-own-work-message-shared": "Я пацьвярджаю, што зьяўляюся ўласьнікам аўтарскіх правоў на гэты файл, і згодны незваротна перадаць гэты файл ў Вікісховішча на ўмовах ліцэнзіі [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0], а таксама згодны з [https://wikimediafoundation.org/wiki/Terms_of_Use умовамі выкарыстаньня].",
        "foreign-structured-upload-form-label-not-own-work-message-shared": "Калі вы не зьяўляецеся ўласьнікам аўтарскіх правоў на гэты файл, або вы жадаеце распаўсюджваць яго пад іншай ліцэнзіяй, можаце скарыстацца [https://commons.wikimedia.org/wiki/Special:UploadWizard Майстарам загрузкі ў Вікісховішча].",
        "foreign-structured-upload-form-label-not-own-work-local-shared": "Вы таксама можаце скарыстацца [[Special:Upload|старонкай загрузкі {{GRAMMAR:родны|{{SITENAME}}}}]], калі правілы сайту дазваляюць загрузку такога файлу.",
-       "foreign-structured-upload-form-2-label-intro": "Дзякуем за ахвяраваньне выявы, якая будзе выкарыстаная ў {{GRAMMAR:месны|{{SITENAME}}}}. Вам варта працягваць, калі яна адпавядае наступным умовам:",
-       "foreign-structured-upload-form-2-label-ownwork": "Гэта мусіць быць цалкам <strong>вашая ўласная праца</strong>, а ня проста выява з Інтэрнэту",
-       "foreign-structured-upload-form-2-label-noderiv": "Яна <strong>не павінна ўтрымліваць чужой працы</strong> або быць натхнёнай ёю",
-       "foreign-structured-upload-form-2-label-useful": "Яна павінна быць <strong>адукацыйнай і карыснай</strong> для навучаньня іншых",
-       "foreign-structured-upload-form-2-label-ccbysa": "Вы мусіце быць <strong>згодныя апублікаваць яе ў Інтэрнэце назаўсёды</strong> паводле ліцэнзіі [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0]",
-       "foreign-structured-upload-form-2-label-alternative": "Калі ня ўсё з вышэйпералічанага праўда, вы ўсё яшчэ можаце загрузіць гэты файл з дапамогай [https://commons.wikimedia.org/wiki/Special:UploadWizard майстару загрузкі Вікісховішча], калі файл даступны паводле свабоднай ліцэнзіі.",
-       "foreign-structured-upload-form-2-label-termsofuse": "Калі вы загружаеце файл, вы пацьвярджаеце, што валодаеце аўтарскімі правамі на яго, і згодныя незваротна перадаць гэты файл у Вікісховішча паводле ліцэнзіі Creative Commons Attribution-ShareAlike 4.0, а таксама, што вы згодныя з [https://wikimediafoundation.org/wiki/Terms_of_Use умовамі выкарыстаньня].",
-       "foreign-structured-upload-form-3-label-question-website": "Вы спампавалі гэтую выяву зь нейкага сайту або знайшлі яе праз пошук выяваў?",
-       "foreign-structured-upload-form-3-label-question-ownwork": "Вы стварылі гэтую выяву (зрабілі фота, накід малюнку і г. д.) самі?",
-       "foreign-structured-upload-form-3-label-question-noderiv": "Ці ўтрымлівае яна або яна натхнёная працай, якой валодае нехта іншы, як прыклад, лягатып?",
-       "foreign-structured-upload-form-3-label-yes": "Так",
-       "foreign-structured-upload-form-3-label-no": "Не",
-       "foreign-structured-upload-form-3-label-alternative": "На жаль, у гэтым выпадку інструмэнт не падтрымлівае загрузку такога файлу. Вы ўсё яшчэ можаце загрузіць яго з дапамогай [https://commons.wikimedia.org/wiki/Special:UploadWizard майстару загрузкі Вікісховішча], пры ўмове, што файл даступны паводле вольнай ліцэнзіі.",
-       "foreign-structured-upload-form-4-label-good": "З дапамогай гэтага інструмэнту вы можаце загрузіць адукацыйную графіку, створаную вамі, а таксама зробленыя вамі фотаздымкі, якія ня ўтрымліваюць працы, што належаць некаму іншаму.",
-       "foreign-structured-upload-form-4-label-bad": "Вы ня можаце загружаць выявы, знойдзеныя ў пошукавых сыстэмах або спампаваныя зь іншых сайтаў.",
        "backend-fail-stream": "Немагчыма накіраваць файл $1.",
        "backend-fail-backup": "Немагчыма зрабіць рэзэрвовую копію файла $1.",
        "backend-fail-notexists": "Файл $1 не існуе.",
        "querypage-disabled": "Гэта спэцыяльная старонка адключаная для падвышэньня прадукцыйнасьці",
        "apihelp": "Даведка API",
        "apihelp-no-such-module": "Модуль «$1» ня знойдзены.",
+       "apisandbox": "Пясочніца API",
+       "apisandbox-api-disabled": "API забаронены на гэтым сайце.",
+       "apisandbox-intro": "Выкарыстоўвайце гэтую старонку для экспэрымэнтаў з <strong>API вэб-сэрвісу MediaWiki</strong>.\nЗьвяртайцеся да [[mw:API:Main page|дакумэнтацыі API]] для дадатковай інфармацыі па выкарыстаньні API. Напрыклад, [//www.mediawiki.org/wiki/API#A_simple_example як атрымаць зьмест галоўнай старонкі]. Абярыце дзеяньне, каб пабачыць болей узораў.\n\nЗьвярніце ўвагу, што нягледзячы на тое, што гэта пясочніца, вашыя дзеяньні могуць унесьці зьмены ў вікі.",
+       "apisandbox-submit": "Зрабіць запыт",
+       "apisandbox-reset": "Ачысьціць",
+       "apisandbox-examples": "Прыклады",
+       "apisandbox-results": "Вынікі",
+       "apisandbox-request-url-label": "URL-адрас запыту:",
+       "apisandbox-request-time": "Час запыту: {{PLURAL:$1|$1 мс}}",
        "booksources": "Крыніцы кніг",
        "booksources-search-legend": "Пошук кніг",
        "booksources-isbn": "ISBN:",
        "file-info-size": "$1 × $2 {{PLURAL:$2|піксэль|піксэлі|піксэляў}}, памер файла: $3, тып MIME: $4",
        "file-info-size-pages": "$1 × $2 піксэлаў, памер файла: $3, MIME-тып: $4, $5 {{PLURAL:$5|старонка|старонкі|старонак}}",
        "file-nohires": "Няма вэрсіі зь лепшым разрозьненьнем.",
-       "svg-long-desc": "SVG-файл, намінальна $1 × $2 {{PLURAL:$2|піксэл|піксэлы|піксэлаў}}, памер файла: $3.",
+       "svg-long-desc": "SVG-файл, намінальна $1 × $2 {{PLURAL:$2|піксэл|піксэлы|піксэлаў}}, памер файла: $3",
        "svg-long-desc-animated": "Анімаваны SVG-файл, намінальна $1 × $2 {{PLURAL:$2|піксэл|піксэлы|піксэлаў}}, памер файла: $3",
        "svg-long-error": "Няслушны SVG-файл: $1",
        "show-big-image": "Арыгінальны файл",
index fb49e5c..3ee9c90 100644 (file)
        "revdelete-no-file": "Посоченият файл не съществува.",
        "revdelete-show-file-confirm": "Необходимо е потвърждение, че желаете да прегледате изтритата версия на файла „<nowiki>$1</nowiki>“ от $2 $3.",
        "revdelete-show-file-submit": "Да",
+       "revdelete-selected-text": "{{PLURAL:$1|Избрана версия|Избрани версии}} от [[:$2]]:",
        "logdelete-selected": "{{PLURAL:$1|Избрано събитие|Избрани събития}}:",
        "revdelete-confirm": "Необходимо е да потвърдите, че желаете да извършите действието, разбирате последствията и го правите според [[{{MediaWiki:Policy-url}}|политиката]].",
        "revdelete-suppress-text": "Премахването трябва да се използва '''само''' при следните случаи:\n* Потенциално уязвима в правно отношение информация\n* Неподходяща лична информация\n*: ''домашни адреси и телефонни номера, номера за социално осигуряване и др.''",
        "right-override-export-depth": "Изнасяне на страници, включително свързаните с тях в дълбочина до пето ниво",
        "right-sendemail": "Изпращане на е-писма до другите потребители",
        "right-passwordreset": "Преглеждане на е-писма за възстановяване на парола",
+       "grant-group-email": "Изпращане на е-писмо",
        "grant-delete": "Изтриване на страници, редакции и записи в дневника",
        "grant-editmyoptions": "Редактиране на вашите потребителски настройки",
        "grant-editmywatchlist": "редактиране на списъка ви за наблюдение",
        "upload-dialog-button-done": "Готово",
        "upload-dialog-button-save": "Съхраняване",
        "upload-dialog-button-upload": "Качване",
-       "upload-form-label-select-file": "Избиране на файл",
        "upload-form-label-infoform-title": "Подробности",
        "upload-form-label-infoform-name": "Име",
        "upload-form-label-infoform-name-tooltip": "Уникално описателно заглавие на файла, което ще бъде записано като име на файла. Можете да използвате обикновен текст с разстояние. Не включвайте файловото разширение.",
        "foreign-structured-upload-form-label-infoform-date": "Дата",
        "foreign-structured-upload-form-label-own-work-message-local": "Потвърждавам, че качвам този файл в съответствие с правилата и лицензионната политика на сайта {{SITENAME}}.",
        "foreign-structured-upload-form-label-not-own-work-message-local": "Ако не можете да заредите този файл в съответствие с правилата на сайта {{SITENAME}}, моля, затворете този прозорец и опитайте друг метод.",
-       "foreign-structured-upload-form-3-label-yes": "Да",
-       "foreign-structured-upload-form-3-label-no": "Не",
        "backend-fail-notexists": "Файлът $1 не съществува.",
        "backend-fail-delete": "Файлът $1 не може да бъде изтрит.",
        "backend-fail-alreadyexists": "Файлът $1 вече съществува.",
        "suppress": "Премахване от публичния архив",
        "querypage-disabled": "Тази специална страница е изключена, защото затруднява производителността на уикито.",
        "apihelp-no-such-module": "Модул \"$1\" не беше намерен.",
+       "apisandbox-reset": "Изчистване",
+       "apisandbox-examples": "Пример",
+       "apisandbox-results": "Резултат",
        "booksources": "Източници на книги",
        "booksources-search-legend": "Търсене на информация за книга",
        "booksources-search": "Търсене",
        "logempty": "Дневникът не съдържа записи, отговарящи на избрания критерий.",
        "log-title-wildcard": "Търсене на заглавия, започващи със",
        "showhideselectedlogentries": "Промяна на видимостта на избраните записи",
+       "checkbox-all": "Всички",
+       "checkbox-none": "Никои",
        "allpages": "Всички страници",
        "nextpage": "Следваща страница ($1)",
        "prevpage": "Предходна страница ($1)",
        "listgrouprights-removegroup-self-all": "Може да премахва всички групи от собствената сметка",
        "listgrouprights-namespaceprotection-header": "Ограничения на именните пространства",
        "listgrouprights-namespaceprotection-namespace": "Именно пространство",
+       "listgrants-rights": "Права",
        "trackingcategories": "Категории за проследяване",
        "trackingcategories-summary": "Тази страница съдържа списък на категории за проследяване, които се попълват автоматично от софтуера на МедияУики. Имената им могат да се променят чрез съответните системни съобщения в именното пространство {{ns:8}}.",
        "trackingcategories-msg": "Категория за проследяване",
index 1ebe1b4..f1083c6 100644 (file)
        "laggedslavemode": "সতর্কীকরণ: পাতাটি সম্ভবত সম্প্রতি হালনাগাদকৃত নয়।",
        "readonly": "ডেটাবেজের ব্যবহার সীমাবদ্ধ",
        "enterlockreason": "তালাবদ্ধ করার কারণ কি তা বলুন, সাথে কখন তালা খুলবেন তার আনুমানিক সময় উল্লখ্য করুন",
-       "readonlytext": "নতà§\81ন à¦­à§\81à¦\95à§\8dতি à¦\8fবà¦\82 à¦\85নà§\8dযানà§\8dয à¦¸à¦®à§\8dপাদনার à¦\9cনà§\8dয à¦¡à¦¾à¦\9fাবà§\87à¦\9c à¦¬à¦°à§\8dতমানà§\87 à¦¬à¦¨à§\8dধ à¦\95রা à¦\86à¦\9bà§\87। à¦¸à¦®à§\8dভবত à¦¡à¦¾à¦\9fাবà§\87à¦\9c à¦°à¦\95à§\8dষণাবà§\87à¦\95à§\8dষণà§\87র à¦¨à¦¿à¦¯à¦¼à¦®à¦¿à¦¤ à¦\95াà¦\9c à¦\9aলà¦\9bà§\87। à¦\95িà¦\9bà§\81à¦\95à§\8dষণ à¦ªà¦°à§\87 à¦\8fà¦\9fি à¦¸à§\8dবাভাবিà¦\95 à¦\85বসà§\8dথায় à¦«à¦¿à¦°à§\87 à¦\86সবà§\87।\n\nপà§\8dরশাসà¦\95 এই ব্যাখ্যা দিয়েছেন: $1",
+       "readonlytext": "নতà§\81ন à¦­à§\81à¦\95à§\8dতি à¦\8fবà¦\82 à¦\85নà§\8dযানà§\8dয à¦¸à¦®à§\8dপাদনার à¦\9cনà§\8dয à¦¡à¦¾à¦\9fাবà§\87à¦\9c à¦¬à¦°à§\8dতমানà§\87 à¦¬à¦¨à§\8dধ à¦\95রা à¦\86à¦\9bà§\87। à¦¸à¦®à§\8dভবত à¦¡à¦¾à¦\9fাবà§\87à¦\9c à¦°à¦\95à§\8dষণাবà§\87à¦\95à§\8dষণà§\87র à¦¨à¦¿à¦¯à¦¼à¦®à¦¿à¦¤ à¦\95াà¦\9c à¦\9aলà¦\9bà§\87। à¦\95িà¦\9bà§\81à¦\95à§\8dষণ à¦ªà¦°à§\87 à¦\8fà¦\9fি à¦¸à§\8dবাভাবিà¦\95 à¦\85বসà§\8dথায় à¦«à¦¿à¦°à§\87 à¦\86সবà§\87।\n\nসিসà§\8dà¦\9fà§\87ম à¦ªà§\8dরশাসà¦\95 à¦¯à¦¿à¦¨à¦¿ à¦\8fà¦\9fি à¦¬à¦¨à§\8dধ à¦\95রà§\87à¦\9bà§\87ন à¦¤à¦¿à¦¨à¦¿ এই ব্যাখ্যা দিয়েছেন: $1",
        "missing-article": "\"$1\" $2 লেখাটি ডাটাবেসের কোন পাতায় খুজে পাওয়া যায়নি।\n\nমুছে ফেলা কোন পাতায় সংযোগ থাকার কারনেই সাধারণত এমনটি ঘটে।\n\nযদি এমনটি না হয়, তাহলে আপনি সফটওয়্যারে কোন ত্রুটি খুজে পেয়েছেন।\nদয়াকরে এ ব্যাপার সম্পর্কে ইউআরএল সহ কোন [[Special:ListUsers/sysop|প্রশাসককে]] জানান।",
        "missingarticle-rev": "(সংস্করণ#: $1)",
        "missingarticle-diff": "(পার্থক্য: $1, $2)",
        "mypreferencesprotected": "আপনার পছন্দসমূহ সম্পাদনা করতে আপনার অনুমতি নেই",
        "ns-specialprotected": "বিশেষ পাতাসমূহ সম্পাদনা করা যাবে না।",
        "titleprotected": "[[User:$1|$1]] কর্তৃক এই শিরোনামটি সৃষ্টি করা থেকে সুরক্ষিত করা হয়েছে। কারণ: \"<em>$2</em>\"।",
-       "filereadonlyerror": "\"$1\" à¦«à¦¾à¦\87লà¦\9fিà¦\95à§\87 à¦ªà¦°à¦¿à¦¬à¦°à§\8dতন à¦\95রা à¦¸à¦®à§\8dভব à¦¹à¦\9aà§\8dà¦\9bà§\87 à¦¨à¦¾ à¦\95ারন \"$2\" à¦«à¦¾à¦\87ল à¦°à¦¿à¦ªà§\8bসিà¦\9fà§\8bরি à¦°à¦¿à¦¡-à¦\85নলি-মà§\8bডà§\87 à¦\86à¦\9bà§\87।\n\nà¦\8fà¦\95à¦\9cন à¦ªà§\8dরশাসà¦\95 à¦¯à¦¿à¦¨à¦¿ à¦\8fà¦\9fাà¦\95à§\87 à¦²à¦\95ড à¦\95রà§\87à¦\9bà§\87ন à¦¤à¦¾à¦° à¦¯à§\8cà¦\95à§\8dতিà¦\95তা à¦¦à§\87à¦\93য়া à¦¹à¦²: \"$3\"",
+       "filereadonlyerror": "\"$1\" à¦«à¦¾à¦\87লà¦\9fিà¦\95à§\87 à¦ªà¦°à¦¿à¦¬à¦°à§\8dতন à¦\95রা à¦¸à¦®à§\8dভব à¦¹à¦\9aà§\8dà¦\9bà§\87 à¦¨à¦¾ à¦\95ারণ \"$2\" à¦«à¦¾à¦\87ল à¦¸à¦\82à¦\97à§\8dরহসà§\8dথল à¦¶à§\81ধà§\81মাতà§\8dর-পঠন à¦®à§\8bডà§\87 à¦\86à¦\9bà§\87।\n\nসিসà§\8dà¦\9fà§\87ম à¦ªà§\8dরশাসà¦\95 à¦¯à¦¿à¦¨à¦¿ à¦\8fà¦\9fি à¦¬à¦¨à§\8dধ à¦\95রà§\87à¦\9bà§\87ন à¦¤à¦¿à¦¨à¦¿ à¦\8fà¦\87 à¦¬à§\8dযাà¦\96à§\8dযা à¦¦à¦¿à¦¯à¦¼à§\87à¦\9bà§\87ন: \"$3\"।",
        "invalidtitle-knownnamespace": "অবৈধ শিরোনাম, যেখানে নামস্থান \"$2\" এবং লেখা হয়েছে \"$3\"",
        "invalidtitle-unknownnamespace": "অবৈধ শিরোনাম, যেখানে ব্যবহৃত হয়েছে অপরিচিত নামস্থান সংখ্যা $1 এবং লেখা হয়েছে \"$2\"",
        "exception-nologin": "লগইন করা হয়নি",
        "botpasswords-label-delete": "অপসারণ",
        "botpasswords-label-resetpassword": "পাসওয়ার্ড পুনঃস্থাপন",
        "botpasswords-label-grants": "প্রয়োগযোগ্য মঞ্জুরি:",
-       "botpasswords-label-grants-column": "অনুমদিত",
+       "botpasswords-label-grants-column": "à¦\85নà§\81মà§\8bদিত",
        "botpasswords-bad-appid": "\"$1\" বট নামটি সঠিক নয়।",
        "botpasswords-insert-failed": "\"$1\" নামের বট যুক্ত করা যায়নি। আগে থেকেই তালিকায় রয়েছে?",
        "botpasswords-update-failed": "\"$1\" নামের বট যুক্ত করা যায়নি। আগে অপসারণ করা হয়েছিল?",
        "botpasswords-updated-title": "বট পাসওয়ার্ড আপডেট করা হয়েছে",
        "botpasswords-updated-body": "\"$1\" বট পাসওয়ার্ডটি সফলভাবে হালনাগাদ করা হয়েছে।",
        "botpasswords-deleted-title": "বট পাসওয়ার্ড অপসারণ করা হয়েছে",
+       "botpasswords-no-provider": "BotPasswordsSessionProvider উপলব্ধ নয়।",
        "resetpass_forbidden": "পাসওয়ার্ড পরিবর্তন করা সম্ভব নয়",
        "resetpass-no-info": "এই পাতাটিতে সরাসরি প্রবেশাধিকার পেতে আপনাকে অবশ্যই লগইন করতে হবে।",
        "resetpass-submit-loggedin": "পাসওয়ার্ড পরিবর্তন",
        "passwordreset-emailtext-ip": "কেউ একজন (সম্ভবত আপনি, $1 আইপি ঠিকানা থেকে) {{SITENAME}} ($4) সাইটের জন্য আপনার\nপাসওয়ার্ড বদলের জন্য অনুরোধ করেছে। নিচের ব্যবহারকারী {{PLURAL:$3|অ্যাকাউন্টটি|অ্যাকাউন্টগুলো}}\nএই ই-মেইল ঠিকানার সাথে সংযুক্ত:\n\n$2\n\n{{PLURAL:$3|এই অস্থায়ী পাসওয়ার্ডটি|এই অস্থায়ী পাসওয়ার্ডগুলো}} আগামী {{PLURAL:$5|এক দিন|$5 দিন}} পর মেয়াদোত্তীর্ণ হয়ে যাবে।\nআপনার অবশ্যই লগ-ইন করে একটি নতুন পাসওয়ার্ড পছন্দ করা উচিত। যদি অন্য কেউ এই অনুরোধ করে থাকে,\nঅথবা আপনি যদি পুরোনো পাসওয়ার্ড মনে করতে পারেন, এবং আপনার সেটি পরিবর্তন করার কোনো ইচ্ছা না থাকে, তবে\nআপনি এই বার্তাটি উপেক্ষা করতে পারে, এবং আপনার পুরোনো পাসওয়ার্ড ব্যবহার করা চালিয়ে যেতে পারেন।",
        "passwordreset-emailtext-user": "ব্যবহারকারী $1 {{SITENAME}} ($4) সাইটের জন্য আপনার পাসওয়ার্ড বদলের জন্য অনুরোধ করেছে। নিচের ব্যবহারকারী {{PLURAL:$3|অ্যাকাউন্টটি|অ্যাকাউন্টগুলো}}\nএই ই-মেইল ঠিকানার সাথে সংযুক্ত:\n\n$2\n\n{{PLURAL:$3|এই অস্থায়ী পাসওয়ার্ডটি|এই অস্থায়ী পাসওয়ার্ডগুলো}} আগামী {{PLURAL:$5|এক দিন|$5 দিন}} পর মেয়াদোত্তীর্ণ হয়ে যাবে।\nআপনার অবশ্যই লগ-ইন করে একটি নতুন পাসওয়ার্ড পছন্দ করা উচিত। যদি অন্য কেউ এই অনুরোধ করে থাকে,\nঅথবা আপনি যদি পুরোনো পাসওয়ার্ড মনে করতে পারেন, এবং আপনার সেটি পরিবর্তন করার কোনো ইচ্ছা না থাকে, তবে\nআপনি এই বার্তাটি উপেক্ষা করতে পারে, এবং আপনার পুরোনো পাসওয়ার্ড ব্যবহার করা চালিয়ে যেতে পারেন।",
        "passwordreset-emailelement": "ব্যবহারকারী নাম: \n$1\n\nঅস্থায়ী পাসওয়ার্ড: \n$2",
-       "passwordreset-emailsentemail": "যদি আপনার অ্যাকাউন্টের জন্য এটি একটি নিবন্ধিত ইমেল ঠিকানা হয়, তাহলে একটি পাসওয়ার্ড বদলের ইমেইল পাঠানো হবে।",
+       "passwordreset-emailsentemail": "যদি এই ই-মেইল ঠিকানা আপনার অ্যাকাউন্টের সাথে সংযুক্ত করা থাকে, তাহলে একটি পাসওয়ার্ড বদলের ইমেইল পাঠানো হবে।",
+       "passwordreset-emailsentusername": "যদি এই ব্যবহারকারী নামের সাথে ই-মেইল ঠিকানা সংযুক্ত করা থাকে, তাহলে একটি পাসওয়ার্ড বদলের ইমেইল পাঠানো হবে।",
        "passwordreset-emailsent-capture": "স্মরণ করিয়ে দেয়ার জন্য একটি ইমেইল করা হয়েছে, যা নিচে দেখানো হচ্ছে।",
        "passwordreset-emailerror-capture": "স্মরণ করিয়ে দেয়ার জন্য একটি ইমেইল তৈরী করা হয়েছিল, যা নিচে দেখানো হচ্ছে, তবে $1 {{GENDER:$2|ব্যবহারকারীকে}} এটি পাঠানো যায়নি!",
        "changeemail": "ই-মেইল ঠিকানা পরিবর্তন বা বাতিল",
        "copyrightwarning2": "অনুগ্রহ করে লক্ষ করুন: {{SITENAME}}-এর এই ভুক্তিতে আপনার লেখা বা অবদান অন্যান্য ব্যবহারকারীরা পরিবর্তন বা পরিবর্ধন করতে, এমনকি মুছে ফেলতে পারবেন। {{SITENAME}} এ আপনার সকল লেখালেখি/অবদান গনু ফ্রি ডকুমেন্টেশনের ($1) আওতায় বিনামূল্যে প্রাপ্য ও হস্তান্তরযোগ্য। আপনার জমা দেয়া লেখা যে কেউ হৃদয়হীনভাবে সম্পাদনা করতে এবং যথেচ্ছভাবে ব্যবহার করতে পারেন। আপনি যদি এ ব্যাপারে একমত না হন, তাহলে এখানে আপনার লেখা জমা দেবেন না। আপনি আরো প্রতিজ্ঞা করছেন যে, এই লেখাগুলো আপনি নিজে লিখেছেন (তবে কোন মৌলিক গবেষণা নয়) বা সাধারণের ব্যবহারের জন্য উন্মুক্ত কোন উৎস থেকে সংগ্রহ করেছেন। '''স্বত্ব সংরক্ষিত কোন লেখা স্বত্বাধিকারীর অনুমতি ছাড়া এখানে জমা দেবেন না।'''",
        "editpage-cannot-use-custom-model": "এই পাতার বিষয়বস্তুর মডেল পরিবর্তন করা যাবে না।",
        "longpageerror": "'''ত্রুটি:  আপনার জমা দেয়া টেক্সটের পরিমাণ {{PLURAL:$1|এক কিলোবাইট|$1 কিলোবাইট}}, যা সর্বোচ্চ সীমা {{PLURAL:$2|এক কিলোবাইটের|$2 কিলোবাইটের}} চেয়ে বেশি।'''\nএটি সংরক্ষণ করা সম্ভব নয়।",
-       "readonlywarning": "'''সতর্কীকরণ: রক্ষণাবেক্ষণের জন্য ডাটাবেজ অবরুদ্ধ রাখা হয়েছে, তাই এই মুহূর্তে আপনার সম্পাদনা সংরক্ষণ করতে পারবেন না।'''\nআপনি চাইলে লেখাটি কাট এবং পেষ্ট করে ভবিষ্যতের জন্য কোন টেক্সট ফাইলে সংরক্ষণ করতে পারেন।\n\nযে প্রশাসক এই ডাটাবেজটি অবরুদ্ধ করেছেন তিনি যা ব্যাখ্যা দিয়েছেন: $1",
+       "readonlywarning": "<strong>সতর্কীকরণ: রক্ষণাবেক্ষণের জন্য ডাটাবেজ অবরুদ্ধ রাখা হয়েছে, তাই এই মুহূর্তে আপনি আপনার সম্পাদনা সংরক্ষণ করতে পারবেন না।</strong>\nআপনি চাইলে লেখাটি অনুলিপি করে ও কোন টেক্সট ফাইলে প্রতিলেপন করার দ্বারা ভবিষ্যতের জন্য সংরক্ষণ করতে পারেন।\n\nসিস্টেম প্রশাসক যিনি এটি বন্ধ করেছেন তিনি এই ব্যাখ্যা দিয়েছেন: $1",
        "protectedpagewarning": "'''সতর্কীকরণ: এই পাতাটি বন্ধ করা হয়েছে; কেবলমাত্র প্রশাসক মর্যাদার ব্যবহারকারীরাই এটি সম্পাদনা করতে পারবেন।'''\nআপনার সুবিধার্থে পাতাটির সাম্প্রতিক সংরক্ষণ লগের বিবরণ নিচে দেওয়া হলো।",
        "semiprotectedpagewarning": "'''নোট:''' এই পাতাটির ব্যবহার নিয়ন্ত্রণ করা হয়েছে তাই নিবন্ধনকৃত ব্যবহারকারী এটি সম্পাদনা করতে পারবেন।\nআপনার সুবিধার্থে পাতাটির সাম্প্রতিক সংরক্ষণ লগের বিবরণ নিচে দেওয়া হলো।",
        "cascadeprotectedwarning": "<strong>সতর্কীকরণ:</strong> এই পাতাটি সুরক্ষিত, ফলে এটি শুধুমাত্র প্রশাসক অধিকারপ্রাপ্ত ব্যবহারকারীগণ সম্পাদনা করতে পারেন, কারণ এটি নিচের প্রপাতাকার-সুরক্ষিত {{PLURAL:$1|পাতায়|পাতাসমূহে}} অন্তর্ভুক্ত আছে:",
        "mergehistory-empty": "কোন সংশোধন একত্র করা যাবে না.",
        "mergehistory-done": "$1-এর $3{{PLURAL:$3|টি সংশোধন}} [[:$2]]-এর সাথে একত্রিত করা হয়েছে।",
        "mergehistory-fail": "ইতিহাস একত্র করা গেল না। অনুগ্রহ করে পাতাটি ও সময়ের প্যারামিটারগুলি আবার পরীক্ষা করে দেখুন।",
+       "mergehistory-fail-bad-timestamp": "সময়তারিখ অবৈধ।",
+       "mergehistory-fail-invalid-source": "উত্স পাতা অবৈধ।",
+       "mergehistory-fail-invalid-dest": "গন্তব্য পাতা অবৈধ।",
        "mergehistory-fail-toobig": "ইতিহাস থেকে আগের পাতাগুলো একীকরণ সম্ভব নয়, কারণ এর ফলে সর্বোচ্চ $1 টি {{PLURAL:$1|সংস্করণ}} স্থানান্তরের সীমানা অতিক্রম করবে।",
        "mergehistory-no-source": "$1 বলে কোন উৎস পাতার অস্তিত্ব নেই।",
        "mergehistory-no-destination": "$1 বলে কোন গন্তব্য পাতার অস্তিত্ব নেই।",
        "prefs-watchlist-days": "যত দিনের নজরতালিকা দেখানো হবে:",
        "prefs-watchlist-days-max": "সর্বোচ্চ $1 {{PLURAL:$1|দিন|দিন}}",
        "prefs-watchlist-edits": "সম্প্রসারিত নজর তালিকায় সর্বোচ্চ সংখ্যার পরিবর্তন দেখানোর জন্য:",
-       "prefs-watchlist-edits-max": "সরà§\8dবà§\8bà¦\9aà§\8dà¦\9a à¦¨à¦¾à¦®à§\8dবার: ১০০০",
+       "prefs-watchlist-edits-max": "সরà§\8dবà§\8bà¦\9aà§\8dà¦\9a à¦¨à¦®à§\8dবর: ১০০০",
        "prefs-watchlist-token": "নজরতালিকা টোকেন:",
        "prefs-misc": "বিবিধ",
        "prefs-resetpass": "পাসওয়ার্ড পরিবর্তন",
        "upload-dialog-button-done": "সম্পন্ন",
        "upload-dialog-button-save": "সংরক্ষণ",
        "upload-dialog-button-upload": "আপলোড",
-       "upload-form-label-select-file": "ফাইল নির্বাচন করুন",
        "upload-form-label-infoform-title": "বিস্তারিত",
        "upload-form-label-infoform-name": "নাম",
        "upload-form-label-infoform-description": "বিবরণ",
        "foreign-structured-upload-form-label-infoform-categories": "বিষয়শ্রেণীসমূহ",
        "foreign-structured-upload-form-label-infoform-date": "তারিখ",
        "foreign-structured-upload-form-label-not-own-work-local-local": "এছাড়াও আপনি [[Special:Upload|ডিফল্ট আপলোডের পাতা]] চেষ্টা করতে পারেন।",
-       "foreign-structured-upload-form-2-label-ccbysa": "[https://creativecommons.org/licenses/by-sa/4.0/deed.bn ক্রিয়েটিভ কমন্স অ্যাট্রিবিউশন-শেয়ার অ্যালাইক ৪.০] লাইসেন্সের আওতায় এটি ইন্টারনেটে <strong>চিরতরে প্রকাশ করা ঠিক হবে</strong>",
-       "foreign-structured-upload-form-3-label-yes": "হ্যাঁ",
-       "foreign-structured-upload-form-3-label-no": "না",
+       "foreign-structured-upload-form-label-not-own-work-local-default": "এছাড়াও আপনি [[Special:Upload|{{SITENAME}}-এর আপলোডের পাতা]] ব্যবহার করার চেষ্টা করতে পারেন, যদি এই ফাইলটি তাদের নীতিমালা অধীনে সেখানে আপলোড করা যায়।",
+       "foreign-structured-upload-form-label-own-work-message-shared": "আমি প্রত্যয়ন করছি যে আমি এই ফাইলের স্বত্তাধিকারী, এবং [https://creativecommons.org/licenses/by-sa/4.0/deed.bn ক্রিয়েটিভ কমন্স অ্যাট্রিবিউশন-শেয়ার অ্যালাইক ৪.০] লাইসেন্সের অধীনে এই ফাইলটি উইকিমিডিয়া কমন্সে অপরিবর্তনীয় প্রকাশে সম্মত হচ্ছি, এবং আমি [https://wikimediafoundation.org/wiki/Terms_of_Use ব্যবহারের শর্তাবলীর] সাথে সম্মত।",
+       "foreign-structured-upload-form-label-not-own-work-message-shared": "যদি আপনি এই ফাইলের স্বত্তাধিকারী না হন, বা আপনি একটি ভিন্ন লাইসেন্সের আওতায় প্রকাশ করতে ইচ্ছুক থাকেন, তাহলে [https://commons.wikimedia.org/wiki/Special:UploadWizard?uselang=bn কমন্স আপলোড উইজার্ড] ব্যবহার করতে বিবেচনা করুন।",
+       "foreign-structured-upload-form-label-not-own-work-local-shared": "এছাড়াও আপনি [[Special:Upload|{{SITENAME}}-এর আপলোডের পাতা]] ব্যবহার করার চেষ্টা করতে পারেন, যদি সাইটটি তাদের নীতিমালার অধীনে এই ফাইল আপলোড করার অনুমতি দেয়।",
        "backend-fail-stream": "\"$1\" ফাইলের স্ট্রিম দেখানো যাচ্ছে না।",
        "backend-fail-backup": "\"$1\" ফাইলের ব্যাকআপ তৈরী সম্ভব নয়।",
        "backend-fail-notexists": "\"$1\" নামের কোনো ফাইল নেই।",
        "querypage-disabled": "কারিগরি কারণে এই বিশেষ পাতাটি আপাতত বন্ধ রয়েছে।",
        "apihelp": "এপিআই সাহায্য",
        "apihelp-no-such-module": "মডিউল \"$1\" পাওয়া যায়নি।",
+       "apisandbox": "এপিআই খেলাঘর",
+       "apisandbox-api-disabled": "এপিআই এই সাইটে নিষ্ক্রিয় করা আছে।",
+       "apisandbox-fullscreen": "প্যানেল সম্প্রসারণ করুন",
+       "apisandbox-unfullscreen": "পাতা দেখাও",
+       "apisandbox-submit": "অনুরোধ রাখুন",
+       "apisandbox-reset": "পরিস্কার",
+       "apisandbox-retry": "পুনঃচেষ্টা করুন",
+       "apisandbox-examples": "উদাহরণ",
+       "apisandbox-dynamic-parameters-add-label": "প্যারামিটার যোগ করুন:",
+       "apisandbox-dynamic-parameters-add-placeholder": "প্যারামিটারের নাম",
+       "apisandbox-results": "ফলাফল",
+       "apisandbox-request-url-label": "অনুরোধের URL:",
+       "apisandbox-request-time": "অনুরোধের সময়: {{PLURAL:$1|$1 মি.সে.}}",
        "booksources": "বইয়ের উৎস",
        "booksources-search-legend": "বইয়ের উৎসের জন্য অনুসন্ধান করা হোক",
        "booksources-isbn": "আইএসবিএন:",
        "block-log-flags-hiddenname": "ব্যবহারকারীনাম লুকায়িত",
        "range_block_disabled": "প্রশাসকের পক্ষে আইপি ঠিকানার শ্রেণী বাধাদানের ক্ষমতা নিষ্ক্রিয় আছে।",
        "ipb_expiry_invalid": "মেয়াদোত্তীর্ণকাল অবৈধ।",
+       "ipb_expiry_old": "মেয়াদোত্তীর্ণের সময় অতীত হয়েছে।",
        "ipb_expiry_temp": "লুকানো ব্যবহারকারীনাম বাধা চিরস্থায়ী হতে হবে।",
        "ipb_hide_invalid": "এই অ্যাকাউন্ট বাধা দেয়া সম্ভব নয়; এটি {{PLURAL:$1|একের অধিক|$1টি}} সম্পাদনা করেছে।",
        "ipb_already_blocked": "\"$1\" ইতিমধ্যে ব্লক",
        "lockedbyandtime": "({{GENDER:$1|$1}} $2 এর $3 সময়ে)",
        "move-page": "$1 স্থানান্তর",
        "move-page-legend": "পাতা স্থানান্তর",
-       "movepagetext": "নিচের ফর্মটি ব্যবহার করে একটি পাতার শিরোনাম পরিবর্তন করা যাবে, এবং সেই সাথে নতুন শিরোনামে এর সমগ্র ইতিহাস স্থানান্তর করা যাবে।\nপুরনো শিরোনামটি নতুন শিরোনামটির প্রতি একটি পুনর্নির্দেশনা ধারণ করবে।\nযেসমস্ত পুনর্নির্দেশনা পুরনো শিরোনামটির দিকে নির্দেশ করছিল, সেগুলি স্বয়ংক্রিয়ভাবে হালনাগাদ করতে পারবেন।\nযদি তা না চান, তবে [[Special:DoubleRedirects|দ্বি-পুনর্নির্দেশনা]] বা [[Special:BrokenRedirects|অচল পুনর্নির্দেশনাগুলি]] পরীক্ষা করে দেখতে ভুলবেন না।\nসংযোগগুলি যাতে তাদের লক্ষ্যে পৌঁছায়, তা নিশ্চিত করার দায়িত্ব আপনার।\n\nলক্ষ্য করুন যে যদি নতুন শিরোনামে ইতিমধ্যেই একটি পাতা থেকে থাকে, তবে উৎস পাতাটি সেই শিরোনামে স্থানান্তর করা হবে '''না''', যদি না নতুন শিরোনামের পাতাটি খালি থাকে বা একটি পুননির্দেশনা হয় এবং এর কোন অতীত সম্পাদনা ইতিহাস না থাকে।\nঅর্থাৎ আপনি ভুল করে নাম পরিবর্তন করলে সহজেই পুরনো নামে ফেরত যেতে পারবেন, কিন্তু ইতিমধ্যে বিদ্যমান কোন পাতার উপরে লিখতে পারবেন না।\n\n'''সতর্কীকরণ!'''\nকোন জনপ্রিয় পাতার ক্ষেত্রে এই পরিবর্তনটি খুবই আকস্মিক হতে পারে; অগ্রসর হবার আগে এই কাজটির ফলাফল কী হতে পারে, সে ব্যাপারে অনুগ্রহ করে নিশ্চিত হোন।",
-       "movepagetext-noredirectfixer": "নিচের ফর্মটি ব্যবহার করে একটি পাতার শিরোনাম পরিবর্তন করা যাবে, এবং সেই সাথে নতুন শিরোনামে এর সমগ্র ইতিহাস স্থানান্তর করা যাবে।\nপুরনো শিরোনামটি নতুন শিরোনামটির প্রতি একটি পুনর্নির্দেশনা ধারণ করবে।\n[[Special:DoubleRedirects|দ্বি-পুনর্নির্দেশনা]] বা [[Special:BrokenRedirects|অচল পুনর্নির্দেশনাগুলি]] পরীক্ষা করে দেখতে ভুলবেন না।\nসংযোগগুলি যাতে তাদের লক্ষ্যে পৌঁছায়, তা নিশ্চিত করার দায়িত্ব আপনার।\n\nলক্ষ্য করুন যে যদি নতুন শিরোনামে ইতিমধ্যেই একটি পাতা থেকে থাকে, তবে উৎস পাতাটি সেই শিরোনামে স্থানান্তর করা হবে '''না''', যদি না নতুন শিরোনামের পাতাটি খালি থাকে বা একটি পুননির্দেশনা হয় এবং এর কোন অতীত সম্পাদনা ইতিহাস না থাকে। \nঅর্থাৎ আপনি ভুল করে নাম পরিবর্তন করলে সহজেই পুরনো নামে ফেরত যেতে পারবেন, কিন্তু ইতিমধ্যে বিদ্যমান কোন পাতার উপরে লিখতে পারবেন না।\n\n'''সতর্কীকরণ!'''\nকোন জনপ্রিয় পাতার ক্ষেত্রে এই পরিবর্তনটি খুবই আকস্মিক হতে পারে;\nঅগ্রসর হবার আগে এই কাজটির ফলাফল কী হতে পারে, সে ব্যাপারে অনুগ্রহ করে নিশ্চিত হোন।",
+       "movepagetext": "নিচের ফর্মটি ব্যবহার করে একটি পাতার শিরোনাম পরিবর্তন করা যাবে, এবং সেই সাথে নতুন শিরোনামে এর সমগ্র ইতিহাস স্থানান্তর করা যাবে।\nপুরনো শিরোনামটি নতুন শিরোনামটির প্রতি একটি পুনর্নির্দেশনা ধারণ করবে।\nযেসমস্ত পুনর্নির্দেশনা পুরনো শিরোনামটির দিকে নির্দেশ করছিল, সেগুলি স্বয়ংক্রিয়ভাবে হালনাগাদ করতে পারবেন।\nযদি তা না চান, তবে [[Special:DoubleRedirects|দ্বি-পুনর্নির্দেশনা]] বা [[Special:BrokenRedirects|অচল পুনর্নির্দেশনাগুলি]] পরীক্ষা করে দেখতে ভুলবেন না।\nসংযোগগুলি যাতে তাদের লক্ষ্যে পৌঁছায়, তা নিশ্চিত করার দায়িত্ব আপনার।\n\nলক্ষ্য করুন যে যদি নতুন শিরোনামে ইতিমধ্যেই একটি পাতা থেকে থাকে, তবে উৎস পাতাটি সেই শিরোনামে স্থানান্তর করা হবে <strong>না</strong>, যদি না নতুন শিরোনামের পাতাটি খালি থাকে বা একটি পুননির্দেশনা হয় এবং এর কোন অতীত সম্পাদনা ইতিহাস না থাকে।\nঅর্থাৎ আপনি ভুল করে নাম পরিবর্তন করলে সহজেই পুরনো নামে ফেরত যেতে পারবেন, কিন্তু ইতিমধ্যে বিদ্যমান কোন পাতার উপরে লিখতে পারবেন না।\n\n<strong>টীকা:</strong>\nকোন জনপ্রিয় পাতার ক্ষেত্রে এই পরিবর্তনটি খুবই আকস্মিক হতে পারে; অগ্রসর হবার আগে এই কাজটির ফলাফল কী হতে পারে, সে ব্যাপারে অনুগ্রহ করে নিশ্চিত হোন।",
+       "movepagetext-noredirectfixer": "নিচের ফর্মটি ব্যবহার করে একটি পাতার শিরোনাম পরিবর্তন করা যাবে, এবং সেই সাথে নতুন শিরোনামে এর সমগ্র ইতিহাস স্থানান্তর করা যাবে।\nপুরনো শিরোনামটি নতুন শিরোনামটির প্রতি একটি পুনর্নির্দেশনা ধারণ করবে।\n[[Special:DoubleRedirects|দ্বি-পুনর্নির্দেশনা]] বা [[Special:BrokenRedirects|অচল পুনর্নির্দেশনাগুলি]] পরীক্ষা করে দেখতে ভুলবেন না।\nসংযোগগুলি যাতে তাদের লক্ষ্যে পৌঁছায়, তা নিশ্চিত করার দায়িত্ব আপনার।\n\nলক্ষ্য করুন যে যদি নতুন শিরোনামে ইতিমধ্যেই একটি পাতা থেকে থাকে, তবে উৎস পাতাটি সেই শিরোনামে স্থানান্তর করা হবে <strong>না</strong>, যদি না নতুন শিরোনামের পাতাটি খালি থাকে বা একটি পুননির্দেশনা হয় এবং এর কোন অতীত সম্পাদনা ইতিহাস না থাকে। \nঅর্থাৎ আপনি ভুল করে নাম পরিবর্তন করলে সহজেই পুরনো নামে ফেরত যেতে পারবেন, কিন্তু ইতিমধ্যে বিদ্যমান কোন পাতার উপরে লিখতে পারবেন না।\n\n<strong>টীকা:</strong>\nকোন জনপ্রিয় পাতার ক্ষেত্রে এই পরিবর্তনটি খুবই আকস্মিক হতে পারে;\nঅগ্রসর হবার আগে এই কাজটির ফলাফল কী হতে পারে, সে ব্যাপারে অনুগ্রহ করে নিশ্চিত হোন।",
        "movepagetalktext": "পাতাটির সাথে সাথে সংশ্লিষ্ট আলোচনা পাতাটিও স্বয়ংক্রিয়ভাবে সরানো হবে '''যদি না:'''\n*খালি নয় এমন একটি আলাপ পাতা নতুন শিরোনামটির অধীনে ইতিমধ্যেই বিদ্যমান থাকে, অথবা\n*আপনি নিচের বাক্সটি থেকে টিক সরিয়ে নিতে পারেন।\n\nএসব ক্ষেত্রে আপনি চাইলে নিজের হাতে পাতাটিকে সরাতে বা একত্রীকরণ করতে পারেন।",
        "moveuserpage-warning": "'''সতর্কতা:''' আপনি একটি ব্যবহারকারী পাতা স্থানান্তর করছেন। অনুগ্রহ করে লক্ষ্য করুন যে এর মাধ্যমে কেবলমাত্র পাতাটি স্থানান্তর হবে, কিন্তু পাতার নাম পরিবর্তন হবে ''না''।",
        "movecategorypage-warning": "<strong>সতর্কীকরণ:</strong> আপনি একটি বিষয়শ্রেণীর পাতা স্থানান্তর করতে চলেছেন। দয়া করে মনে রাখবেন যে এতে শুধুমাত্র পাতাটি স্থানান্তরিত হবে এবং পুরাতন বিষয়শ্রেণীতে থাকা কোন পাতা নতুনটিতে পুনঃশ্রেণীকরণ করা হবে <em>না</em>।",
        "movenosubpage": "এই পাতাটির কোনো উপপাতা নেই।",
        "movereason": "কারণ:",
        "revertmove": "পূর্বাবস্থায় ফেরত নেওয়া হোক",
-       "delete_and_move_text": "==মুছে ফেলা আবশ্যক==\n\n\"[[:$1]]\" শিরোনামের গন্তব্য পাতাটি ইতিমধ্যেই বিদ্যমান। আপনি কি স্থানান্তর সফল করার জন্য পাতাটি মুছে দিতে চান?",
+       "delete_and_move_text": "\"[[:$1]]\" শিরোনামের গন্তব্য পাতাটি ইতিমধ্যেই বিদ্যমান। আপনি কি স্থানান্তর সফল করার জন্য পাতাটি মুছে দিতে চান?",
        "delete_and_move_confirm": "হ্যাঁ, পাতাটি মুছে ফেলা হোক",
        "delete_and_move_reason": "\"[[$1]]\" থেকে স্থানান্তরের স্বার্থে মুছে ফেলা হয়েছে",
        "selfmove": "উৎস ও গন্তব্য পাতা একই শিরোনামের; কোন পাতা একই শিরোনামের আরেক পাতায় সরানো যাবে না।",
        "move-leave-redirect": "পুনর্নির্দেশ রেখে দিন",
        "protectedpagemovewarning": "'''সতর্কীকরণ:''' এই পাতাটি বন্ধ করা হয়েছে; কেবলমাত্র প্রশাসক মর্যাদার ব্যবহারকারীরাই এটি স্থানান্তর করতে পারবেন।\nআপনার সুবিধার্থে পাতাটির সাম্প্রতিক সংরক্ষণ লগের বিবরণ নিচে দেওয়া হলো।",
        "semiprotectedpagemovewarning": "'''নোট:''' এই পাতাটির ব্যবহার নিয়ন্ত্রণ করা হয়েছে তাই নিবন্ধনকৃত ব্যবহারকারী এটি স্থানান্তর করতে পারবেন।\nআপনার সুবিধার্থে পাতাটির সাম্প্রতিক সংরক্ষণ লগের বিবরণ নিচে দেওয়া হলো:",
-       "move-over-sharedrepo": "== এই নামের ফাইল রয়েছে ==\n[[:$1]] নামের ফাইলটি শেয়ার্ড রিপোজিটরীতে রয়েছে। একই নামের একটি ফাইল এখানে স্থানান্তর করা হলে পূর্বের ফাইলটি প্রতিস্থাপিত হবে।",
+       "move-over-sharedrepo": "[[:$1]] নামের ফাইলটি শেয়ার্ড সংগ্রহস্থলে রয়েছে। একই নামের একটি ফাইল এখানে স্থানান্তর করা হলে পূর্বের ফাইলটি প্রতিস্থাপিত হবে।",
        "file-exists-sharedrepo": "নির্ধিত নামের ফাইলটি পূর্বেই শেয়ার্ড রিপোজিরটীতে রয়েছে। \nঅনুগ্রহ করে অন্য কোনো নাম নির্বাচন করুন।",
        "export": "পাতা রপ্তানি",
        "exporttext": "আপনি কোন একটি নির্দিষ্ট পাতার বা অনেকগুলি পাতার একটি সেটের বিষয়বস্তু এবং সম্পাদনা ইতিহাস XML-এ আবৃত করে রপ্তানি করতে পারেন। এটি মিডিয়াউইকি সফটওয়্যার ব্যবহারকারী অন্য একটি উইকিতে [[Special:Import|আমদানি পাতার]] মাধ্যমে আমদানি করা সম্ভব।\n\nপাতা রপ্তানি করতে চাইলে নিচের টেক্সট বাক্সে শিরোনামগুলি প্রবেশ করান, প্রতি লাইনে একটি শিরোনাম দিয়ে, এবং নির্বাচন করুন আপনি বর্তমান সংস্করণসহ সবগুলি পুরনো সংস্করণ পাতার ইতিহাসের লাইনসহ রপ্তানি করতে চান, নাকি কেবল সর্বশেষ সম্পাদনাটির তথ্যসহ বর্তমান সংস্করণটি রপ্তানি করতে চান।\n\nদ্বিতীয় ক্ষেত্রটিতে আপনি একটি সংযোগও ব্যবহার করতে পারেন, যেমন \"[[{{MediaWiki:Mainpage}}]]\" পাতাটির জন্য [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]]।",
        "tooltip-t-recentchangeslinked": "এই পাতা থেকে সংযোগ আছে, এমন পাতাগুলিতে সাম্প্রতিক পরিবর্তন",
        "tooltip-feed-rss": "এই পাতার জন্য আরএসএস ফিড",
        "tooltip-feed-atom": "এই পাতার জন্য অ্যাটম ফিড",
-       "tooltip-t-contributions": "{{GENDER:|এই ব্যবহারকারীর}} অবদানগুলির একটি তালিকা",
-       "tooltip-t-emailuser": "{{GENDER:|এই ব্যবহারকারীকে}} একটি ই-মেইল পাঠান",
+       "tooltip-t-contributions": "{{GENDER:$1|এই ব্যবহারকারীর}} অবদানগুলির একটি তালিকা",
+       "tooltip-t-emailuser": "{{GENDER:$1|এই ব্যবহারকারীকে}} একটি ইমেইল পাঠান",
        "tooltip-t-info": "এই পাতা সম্পর্কে আরো তথ্য",
        "tooltip-t-upload": "ফাইল আপলোড করুন",
        "tooltip-t-specialpages": "সব বিশেষ পাতার তালিকা",
        "anonymous": "{{SITENAME}} এর বেনামী {{PLURAL:$1|ব্যবহারকারী|ব্যবহারকারীবৃন্দ}}",
        "siteuser": "{{SITENAME}} ব্যবহারকারী $1",
        "anonuser": "{{SITENAME}} বেনামী ব্যবহারকারী $1",
-       "lastmodifiedatby": "এই পাতাটিতে শেষ পরিবর্তন হয়েছিল $2, $1 by $3।",
+       "lastmodifiedatby": "$3 কর্তৃক $2, $1 তারিখে এই পাতাটিতে শেষ পরিবর্তন করা হয়েছিল।",
        "othercontribs": "$1-এর কাজের উপর ভিত্তি করে।",
        "others": "অন্যান্য",
-       "siteusers": "{{SITENAME}} {{PLURAL:$2|ব্যবহারকারী|ব্যবহারকারী}} $1",
+       "siteusers": "{{SITENAME}} {{PLURAL:$2|{{GENDER:$1|ব্যবহারকারী}}|ব্যবহারকারী}} $1",
        "anonusers": "{{SITENAME}} বেনামী {{PLURAL:$2|ব্যবহারকারী|ব্যবহারকারীগণ}} $1",
        "creditspage": "পাতার স্বীকৃতি",
        "nocredits": "এই পাতাটির জন্য কোন কৃতিত্ব-সম্পর্কিত তথ্য নেই।",
        "newimages-legend": "ছাকনী",
        "newimages-label": "ফাইলের নাম (অথবা এর কোন অংশ):",
        "newimages-showbots": "বটের আপলোড গুলো দেখাও।",
+       "newimages-hidepatrolled": "টহলকৃত আপলোড আড়াল করো",
        "noimages": "দেখার মত কিছু নেই।",
        "ilsubmit": "অনুসন্ধান",
        "bydate": "তারিখ অনুযায়ী",
        "metadata-help": "এই ফাইলে অতিরিক্ত কিছু তথ্য আছে। সম্ভবত যে ডিজিটাল ক্যামেরা বা স্ক্যানারের মাধ্যমে এটি তৈরি বা ডিজিটায়িত করা হয়েছিল, সেটি কর্তৃক তথ্যগুলি যুক্ত হয়েছে। যদি ফাইলটি তার আদি অবস্থা থেকে পরিবর্তিত হয়ে থাকে, কিছু কিছু বিবরণ পরিবর্তিত ফাইলটির জন্য প্রযোজ্য না-ও হতে পারে।",
        "metadata-expand": "সম্প্রসারিত সবিস্তারে দেখাও",
        "metadata-collapse": "সম্প্রসারিত সবিস্তারে দেখিও না",
-       "metadata-fields": "à¦\8fà¦\87 à¦¬à¦¾à¦°à§\8dতায় à¦¤à¦¾à¦²à¦¿à¦\95াভà§\81à¦\95à§\8dত à¦\9aিতà§\8dর à¦®à§\87à¦\9fাডাà¦\9fা à¦\95à§\8dষà§\87তà§\8dরà¦\97à§\81লি à¦\9bবির à¦ªà¦¾à¦¤à¦¾à¦¯à¦¼ à¦ªà§\8dরদরà§\8dশন à¦\95রা à¦¹à¦¬à§\87, à¦¯à¦\96ন à¦\85ধি-à¦\89পাতà§\8dত à¦¸à¦¾à¦°à¦£à¦¿à¦\9fি à¦¸à¦\82à¦\95à§\81à¦\9aিত à¦\95রা à¦¹à¦¬à§\87। à¦\85নà§\8dয à¦\95à§\8dষà§\87তà§\8dরà¦\97à§\81লি à¦¸à§\8dবাভাবিà¦\95 à¦\85বসà§\8dথায় à¦²à§\81à¦\95à§\8dà¦\95ায়িত à¦¥à¦¾à¦\95বà§\87।\n* make\n* model\n* datetimeoriginal\n* exposuretime\n* fnumber\n* isospeedratings\n* focallength\n* artist\n* copyright\n* imagedescription\n* gpslatitude\n* gpslongitude\n* gpsaltitude",
+       "metadata-fields": "এই বার্তায় তালিকাভুক্ত চিত্র মেটাডাটা ক্ষেত্রগুলি ছবির পাতায় প্রদর্শন করা হবে, যখন অধি-উপাত্ত সারণিটি সংকুচিত করা হবে। অন্য ক্ষেত্রগুলি স্বাভাবিক অবস্থায় লুকায়িত থাকবে।\n* make\n* model\n* datetimeoriginal\n* exposuretime\n* fnumber\n* isospeedratings\n* focallength\n* artist\n* copyright\n* imagedescription\n* gpslatitude\n* gpslongitude\n* gpsaltitude",
        "exif-imagewidth": "চওড়া",
        "exif-imagelength": "লম্বা",
        "exif-bitspersample": "উপাদানপ্রতি বিট",
        "exif-originaltransmissionref": "মূল ট্রান্সমিশনকৃত স্থানের কোড",
        "exif-identifier": "আইডেন্টিফায়ার",
        "exif-lens": "ব্যবহৃত লেন্স",
-       "exif-serialnumber": "à¦\95à§\8dযামà§\87রার à¦¸à¦¿à¦°à¦¿à¦¯à¦¼à¦¾à¦² à¦¨à¦¾à¦®à§\8dবার",
+       "exif-serialnumber": "à¦\95à§\8dযামà§\87রার à¦\95à§\8dরমিà¦\95 à¦¨à¦®à§\8dবর",
        "exif-cameraownername": "ক্যামেরার স্বত্ত্বাধিকারী",
        "exif-label": "লেবেল",
        "exif-datetimemetadata": "মেটাডেটার তারিখ সর্বশেষ পরিবর্তিত হয়েছিলো",
        "version-libraries-license": "লাইসেন্স",
        "version-libraries-description": "বিবরণ",
        "version-libraries-authors": "লেখক",
-       "redirect": "পাতা, à¦«à¦¾à¦\87ল, à¦¬à§\8dযবহারà¦\95রà§\80, à¦\85থবা à¦¸à¦\82শà§\8bধন আইডি দ্বারা পুনঃনির্দেশ করা হয়েছে",
+       "redirect": "পাতা, à¦«à¦¾à¦\87ল, à¦¬à§\8dযবহারà¦\95রà§\80, à¦¸à¦\82শà§\8bধন à¦¬à¦¾ à¦²à¦\97 আইডি দ্বারা পুনঃনির্দেশ করা হয়েছে",
        "redirect-legend": "একটি ফাইল অথবা পাতায় পুনঃনির্দেশ করা হয়েছে",
-       "redirect-summary": "à¦\8fà¦\87 à¦¬à¦¿à¦¶à§\87ষ à¦ªà¦¾à¦¤à¦¾à¦\9fি à¦\8fà¦\95à¦\9fি à¦«à¦¾à¦\87লà§\87 (ফাà¦\87লà§\87র à¦¨à¦¾à¦®), à¦\8fà¦\95à¦\9fি à¦ªà¦¾à¦¤à¦¾à¦¯à¦¼ (সà¦\82সà§\8dà¦\95রণ à¦\86à¦\87ডি à¦¬à¦¾ à¦ªà¦¾à¦¤à¦¾ à¦\86à¦\87ডি), à¦\85থবা à¦\8fà¦\95à¦\9fি à¦¬à§\8dযবহারà¦\95রà§\80 à¦ªà¦¾à¦¤à¦¾à¦¯à¦¼ (সà¦\82à¦\96à§\8dযায় à¦²à§\87à¦\96া à¦¬à§\8dযবহারà¦\95ারà§\80 à¦\86à¦\87ডি) à¦ªà§\81নà¦\83নিরà§\8dদà§\87শিত à¦¹à¦¯à¦¼à§\87à¦\9bà§\87। à¦¬à§\8dযবহার:  [[{{#Special:Redirect}}/file/à¦\89দাহরণ.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], à¦\85থবা [[{{#Special:Redirect}}/user/101]]।",
+       "redirect-summary": "à¦\8fà¦\87 à¦¬à¦¿à¦¶à§\87ষ à¦ªà¦¾à¦¤à¦¾à¦\9fি à¦\8fà¦\95à¦\9fি à¦«à¦¾à¦\87লà§\87 (পà§\8dরদতà§\8dত à¦«à¦¾à¦\87লà§\87র à¦¨à¦¾à¦®), à¦\8fà¦\95à¦\9fি à¦ªà¦¾à¦¤à¦¾à¦¯à¦¼ (পà§\8dরদতà§\8dত à¦¸à¦\82সà§\8dà¦\95রণ à¦\86à¦\87ডি à¦¬à¦¾ à¦ªà¦¾à¦¤à¦¾ à¦\86à¦\87ডি), à¦\8fà¦\95à¦\9fি à¦¬à§\8dযবহারà¦\95রà§\80 à¦ªà¦¾à¦¤à¦¾à¦¯à¦¼ (পà§\8dরদতà§\8dত à¦¸à¦\82à¦\96à§\8dযায় à¦²à§\87à¦\96া à¦¬à§\8dযবহারà¦\95ারà§\80 à¦\86à¦\87ডি) à¦¬à¦¾ à¦\8fà¦\95à¦\9fি à¦²à¦\97 à¦­à§\81à¦\95à§\8dতিতà§\87 (পà§\8dরদতà§\8dত à¦²à¦\97 à¦­à§\81à¦\95à§\8dতি) à¦ªà§\81নà¦\83নিরà§\8dদà§\87শিত à¦¹à¦¯à¦¼à§\87à¦\9bà§\87। à¦¬à§\8dযবহার:  [[{{#Special:Redirect}}/file/à¦\89দাহরণ.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], [[{{#Special:Redirect}}/user/101]], à¦¬à¦¾ [[{{#Special:Redirect}}/logid/186]]।",
        "redirect-submit": "যাও",
        "redirect-lookup": "দেখুন:",
        "redirect-value": "মান:",
        "redirect-page": "পাতার আইডি",
        "redirect-revision": "পাতা সংস্করণ",
        "redirect-file": "ফাইলের নাম",
+       "redirect-logid": "লগ আইডি",
        "redirect-not-exists": "মান পাওয়া যায়নি",
        "fileduplicatesearch": "সদৃশ ফাইলের জন্য অনুসন্ধান",
        "fileduplicatesearch-summary": "হ্যাশ ভ্যালুর ওর ভিত্তি করে একই ছবিগুলো খুঁজুন।",
        "action-pagelang": "পাতার ভাষা পরিবর্তন করুন",
        "log-name-pagelang": "ভাষা পরিবর্তন লগ",
        "log-description-pagelang": "এটি পাতার ভাষা পরিবর্তনের লগ।",
-       "logentry-pagelang-pagelang": "$1 পাতার ভাষা $3 এর জন্য $4 থেকে $5 এ {{GENDER:$2|পরিবর্তন}} করেছেন।",
+       "logentry-pagelang-pagelang": "$1 $3-এর ভাষা $4 থেকে $5-এ {{GENDER:$2|পরিবর্তন}} করেছেন",
        "default-skin-not-found-row-enabled": "* <code>$1</code> / $2 (সক্রিয় করা)",
        "default-skin-not-found-row-disabled": "* <code>$1</code> / $2 ('''নিষ্ক্রিয় করা''')",
        "mediastatistics": "মিডিয়া পরিসংখ্যান",
index c2d5f48..40204f2 100644 (file)
        "changepassword-success": "S'ha canviat la vostra contrasenya amb èxit!",
        "changepassword-throttled": "Heu realitzat massa intents d'inici de sessió.\nEspereu $1 abans de tornar-ho a provar.",
        "botpasswords": "Contrasenyes de bot",
+       "botpasswords-disabled": "S'han inhabilitat les contrasenyes dels bots",
+       "botpasswords-no-central-id": "Per a utilitzar contrasenyes de bots heu d'haver iniciat una sessió en un compte centralitzat.",
        "botpasswords-label-appid": "Nom del bot:",
        "botpasswords-label-create": "Crea",
        "botpasswords-label-update": "Actualitza",
        "upload-dialog-button-done": "Fet",
        "upload-dialog-button-save": "Desa",
        "upload-dialog-button-upload": "Carrega",
-       "upload-form-label-select-file": "Seleccioneu fitxer",
        "upload-form-label-infoform-title": "Detalls",
        "upload-form-label-infoform-name": "Nom",
        "upload-form-label-infoform-description": "Descripció",
        "foreign-structured-upload-form-label-infoform-date": "Data",
        "foreign-structured-upload-form-label-not-own-work-local-local": "També podeu provar [[Special:Upload|la pàgina de càrrega per defecte]].",
        "foreign-structured-upload-form-label-own-work-message-default": "Entenc que esteu carregant el fitxer en un repositori compartit. Confirmo que ho estic fent seguint les condicions d'ús i les polítiques de llicenciament que s'hi apliquen.",
-       "foreign-structured-upload-form-3-label-yes": "Sí",
-       "foreign-structured-upload-form-3-label-no": "No",
        "backend-fail-stream": "No s'ha pogut transmetre el fitxer $1.",
        "backend-fail-backup": "No s'ha pogut fer una còpia de seguretat del fitxer $1.",
        "backend-fail-notexists": "El fitxer $1 no existeix.",
        "querypage-disabled": "Aquesta pàgina especial està desactivada per a no perjudicar el rendiment.",
        "apihelp": "Ajuda de l'API",
        "apihelp-no-such-module": "No s'ha trobat el mòdul \"$1\".",
+       "apisandbox": "Pàgina de proves de l'API",
+       "apisandbox-api-disabled": "L'API està desactivada en aquest lloc.",
+       "apisandbox-intro": "Utilitzeu aquesta pàgina per experimentar amb l'<nowiki />'''API de web service de MediaWiki'''.\nVisiteu [//www.mediawiki.org/wiki/API:Main_page la documentació de l'API] per a més informació sobre l'ús de l'API. Exemple: [//www.mediawiki.org/wiki/API#A_simple_example recuperar el contingut d'una Pàgina Principal]. Seleccioneu una acció per veure més exemples.\n\nTingueu en compte que, encara que això és una pàgina de proves, les accions que feu en aquesta pàgina poden modificar la wiki.",
+       "apisandbox-submit": "Fes sol·licitud",
+       "apisandbox-reset": "Neteja",
+       "apisandbox-examples": "Exemple",
+       "apisandbox-results": "Resultat",
+       "apisandbox-request-url-label": "Sol·licita URL:",
+       "apisandbox-request-time": "Temps de sol·licitud: $1",
        "booksources": "Obres de referència",
        "booksources-search-legend": "Cerca fonts de llibres",
        "booksources-isbn": "ISBN:",
        "javascripttest-pagetext-frameworks": "Trieu un dels següents entorns de prova: $1",
        "javascripttest-pagetext-skins": "Trieu un tema per a executar-hi els tests:",
        "javascripttest-qunit-intro": "Consulteu la [documentació de tests de $1] a mediawiki.org.",
-       "tooltip-pt-userpage": "La vostra pàgina d'usuari",
+       "tooltip-pt-userpage": "{{GENDER:|La vostra}} pàgina d'usuari",
        "tooltip-pt-anonuserpage": "La pàgina d'usuari per la ip que utilitzeu",
-       "tooltip-pt-mytalk": "La vostra pàgina de discussió.",
+       "tooltip-pt-mytalk": "{{GENDER:|La vostra}} pàgina de discussió",
        "tooltip-pt-anontalk": "Discussió sobre les edicions per aquesta adreça ip.",
-       "tooltip-pt-preferences": "Les vostres preferències.",
+       "tooltip-pt-preferences": "{{GENDER:|Les vostres}} preferències",
        "tooltip-pt-watchlist": "La llista de pàgines de les quals vigileu els canvis.",
-       "tooltip-pt-mycontris": "Llista de les vostres contribucions.",
+       "tooltip-pt-mycontris": "Llista de {{GENDER:|les vostres}} contribucions",
        "tooltip-pt-login": "Us animem a registrar-vos, però no és obligatori",
        "tooltip-pt-logout": "Finalitza la sessió d'usuari",
        "tooltip-pt-createaccount": "Us animem a què creeu un compte i inicieu sessió, encara que no és obligatori",
        "tooltip-t-recentchangeslinked": "Canvis recents a pàgines enllaçades des d'aquesta pàgina",
        "tooltip-feed-rss": "Canal RSS d'aquesta pàgina",
        "tooltip-feed-atom": "Canal Atom d'aquesta pàgina",
-       "tooltip-t-contributions": "Llista de contribucions d'aquest usuari",
+       "tooltip-t-contributions": "Llista de les contribucions d'{{GENDER:$1|aquest usuari|aquesta usuària}}",
        "tooltip-t-emailuser": "Envia un correu en aquest usuari.",
        "tooltip-t-info": "Més informació sobre aquesta pàgina",
        "tooltip-t-upload": "Carregueu fitxers",
        "mediastatistics-header-text": "Textual",
        "mediastatistics-header-executable": "Executables",
        "mediastatistics-header-archive": "Formats comprimits",
+       "mediastatistics-header-total": "Tots els fitxers",
        "json-warn-trailing-comma": "$1 al final {{PLURAL:$1|coma ha estat eliminada|comes han estat eliminades}} de JSON",
        "json-error-unknown": "S'ha produït un error amb el JSON. Error: $1",
        "json-error-depth": "S'ha superat la profunditat màxima de pila",
        "mw-widgets-dateinput-placeholder-month": "AAAA-MM",
        "mw-widgets-titleinput-description-new-page": "la pàgina no existeix encara",
        "mw-widgets-titleinput-description-redirect": "redirigeix a $1",
-       "api-error-blacklisted": "Trieu un títol diferent, més descriptiu."
+       "api-error-blacklisted": "Trieu un títol diferent, més descriptiu.",
+       "sessionprovider-generic": "$1 sessions",
+       "sessionprovider-mediawiki-session-cookiesessionprovider": "sessions basades en galetes",
+       "sessionprovider-nocookies": "Pot ser que les galetes estiguin inhabilitades. Assegureu-vos que teniu les galetes habilitades i inicieu de nou."
 }
index d3b4ae5..29af2c2 100644 (file)
        "copyrightpage": "{{ns:project}}:Авторан бакъонаш",
        "currentevents": "ХӀинцалера хилларш",
        "currentevents-url": "Project:ХӀинцалера хилларш",
-       "disclaimers": "Ð\91еÑ\85к тӀецалацар",
-       "disclaimerpage": "Project:Ð\91еÑ\85к тӀецалацар",
+       "disclaimers": "Ð\96оÑ\8cпалла тӀецалацар",
+       "disclaimerpage": "Project:Ð\96оÑ\8cпалла тӀецалацар",
        "edithelp": "ГӀо оцу тадарна",
        "helppage-top-gethelp": "ГӀо",
        "mainpage": "Коьрта агӀо",
        "emailconfirmlink": "Бакъде хьай электронан поштан адрес",
        "invalidemailaddress": "Электронан поштан адрес тӀелаца йиш яц, цуна формат нийса цахилар бахьнехь.\nДехар до, язъе нийса электронан адрес я и меттиг есса йита.",
        "cannotchangeemail": "ХӀокху декъашхочун дӀаяздарца долу электронан поштан адресаш хуьйцийла дац хӀокху вики чохь",
-       "emaildisabled": "Ð¥Ó\80окÑ\85Ñ\83 Ñ\81айÑ\82ан Ñ\82аÑ\80о Ñ\8fÑ\86 Ñ\8dлекÑ\82Ñ\80онан Ð¿Ð¾Ñ\87те хаамаш бахьийта.",
+       "emaildisabled": "Ð¥Ó\80окÑ\85Ñ\83 Ñ\81айÑ\82ан Ñ\82аÑ\80о Ñ\8fÑ\86 Ñ\8dлекÑ\82Ñ\80онан Ð¿Ð¾Ñ\88те хаамаш бахьийта.",
        "accountcreated": "Декъашхочун дӀаяздар кхоьллина",
        "accountcreatedtext": "Кхоьллина декъашхочун [[{{ns:User}}:$1|$1]] ([[{{ns:User talk}}:$1|дийцаре.]]) дӀаяздар.",
        "createaccount-title": "{{SITENAME}}: декъашхочун дӀаяздар кхоллар",
        "resetpass-submit-cancel": "Цаоьшу",
        "resetpass-wrong-oldpass": "Нийса йоцу я хана йолу карара пароль. Ахьа кхиамца пароль хийцина я керла хана йолу пароль ехна хила там бу.",
        "resetpass-recycled": "Дехар до, хӀинца йолччул башха пароль хӀотта йе.",
-       "resetpass-temp-emailed": "Ð\90Ñ\85Ñ\8cа Ñ\87Ñ\83гÓ\80оÑ\88 Ñ\8fзйина Ñ\86кÑ\8aаÑ\87Ñ\83нна Ñ\8dлекÑ\82Ñ\80онан Ð¿Ð¾Ñ\87те яийтина пароль. Чудахар чекхдалийта язъян еза керла пароль.",
+       "resetpass-temp-emailed": "Ð\90Ñ\85Ñ\8cа Ñ\87Ñ\83гÓ\80оÑ\88 Ñ\8fзйина Ñ\86кÑ\8aаÑ\87Ñ\83нна Ñ\8dлекÑ\82Ñ\80онан Ð¿Ð¾Ñ\88те яийтина пароль. Чудахар чекхдалийта язъян еза керла пароль.",
        "resetpass-temp-password": "Цхьан хана пароль:",
        "resetpass-abort-generic": "Пароль хийцар дӀахедар",
        "resetpass-expired": "Хьан паролан хан чекхелла. Дехар до керла пароль хӀоттаяр.",
        "rcshowhidemine": "$1 айхьа нисдинарш",
        "rcshowhidemine-show": "Гайта",
        "rcshowhidemine-hide": "Къайладаха",
-       "rcshowhidecategorization": "$1 Ð°Ð³Ó\80онийн ÐºÐ°Ñ\82егоÑ\80еÑ\88",
+       "rcshowhidecategorization": "$1 ÐºÐ°Ñ\82егоÑ\80еÑ\88 Ñ\82Ó\80еÑ\82оÑ\85аÑ\80",
        "rcshowhidecategorization-show": "Гайта",
        "rcshowhidecategorization-hide": "Къайлаяккха",
        "rclinks": "Гайта тӀаьххьарлерачу $2 дийнахь бина болу $1 хийцамаш\n<br />$3",
        "watchthisupload": "Латайе хӀара файл тергаме могӀам юкъахь",
        "filewasdeleted": "Иштта цӀе йолуш файл хьалха чуяьккхина хила, амма дӀаяьккхина. Дехар до, юху чуяккхале $1 хьажа.",
        "filename-bad-prefix": "Чуйоккхучу файлан цӀе йолалуш ю «'''$1'''» и сурт доккхучу хӀумнан кепан цӀе хилла мега. Дехар до, хьаржа файлан йогӀуш йолу цӀе.",
-       "filename-prefix-blacklist": "#<!-- битийша хlара могlа ша мабарра --> <pre>\n# Бухасиз шолгlа:\n#  * Массо, саболчунтlийра йуьлалуш ерш «#» хуьлу хетаме дерг (могlа чекх балац)\n#  * Хlора баьсса боцу могlа — хlман цlе лораш йу, терахьца сурт доккхучо луш ма хиллар\nCIMG # Casio\nDSC_ # Nikon\nDSCF # Fuji\nDSCN # Nikon\nDUW # ишта цхьаболу лера гlирс а\nIMG # еригге\nJD # Jenoptik\nMGP # Pentax\nPICT # тайп тайпан\n #</pre> <!-- битийша хlара могlа ша мабарра -->",
+       "filename-prefix-blacklist": "#<!-- битийша хӀара могӀа ша мабарра --> <pre>\n# Бухасиз шолгӀа:\n#  * Массо, саболчунтӀийра йуьлалуш ерш «#» хуьлу хетаме дерг (могӀа чекх балац)\n#  * ХӀора баьсса боцу могӀа — хьан цӀе лораш ю, терахьца сурт доккхучо луш ма хиллар\nCIMG # Casio\nDSC_ # Nikon\nDSCF # Fuji\nDSCN # Nikon\nDUW # ишта цхьаболу лера гӀирс а\nIMG # ерриге\nJD # Jenoptik\nMGP # Pentax\nPICT # тайп тайпан\n #</pre> <!-- битийша хӀара могӀа ша мабарра -->",
        "upload-proto-error": "Нийса йоцу протокол",
        "upload-file-error": "Чоьхьара гӀалат",
        "upload-misc-error": "Чуяккхаран цадевза гӀалат",
        "upload-dialog-button-done": "Кийчча ю",
        "upload-dialog-button-save": "Ӏалашъян",
        "upload-dialog-button-upload": "Чуяккха",
-       "upload-form-label-select-file": "Харжа файл",
        "upload-form-label-infoform-title": "Мадарра",
        "upload-form-label-infoform-name": "ЦӀе",
        "upload-form-label-infoform-description": "Цуьнах лаьцна",
        "foreign-structured-upload-form-label-own-work-message-shared": "Аса со хӀокху файлан авторан бакъонаш долахь ерг хилар бакъдо, цундела хӀара файл [https://creativecommons.org/licenses/by-sa/4.0/deed.ru Creative Commons Attribution-ShareAlike 4.0] лицензица Викигуламан чуяккха бакъо ю, цул совнах [https://wikimediafoundation.org/wiki/ хӀокху хьолаца лело] а мега.",
        "foreign-structured-upload-form-label-not-own-work-message-shared": "ХӀокху файлан авторан бакъонаш хьай яцахь, я хьайна кхечу лицензица яржо лууш делахь хьажа [https://commons.wikimedia.org/wiki/Special:UploadWizard Викигуламера чуяхаран говзанча] лелон тароне.",
        "foreign-structured-upload-form-label-not-own-work-local-shared": "ХӀара файл {{SITENAME}} сайтан бакъонашца чуйоккхила делахь, хьайн таро ю [[Special:Upload|хӀара агӀо]] лелаян.",
-       "foreign-structured-upload-form-3-label-yes": "ХӀаъ",
-       "foreign-structured-upload-form-3-label-no": "ХӀахӀа",
        "backend-fail-stream": "ДӀаяккха цатарло файл «$1».",
        "backend-fail-backup": "Таро яц файлан $1 тӀаьхьалонан копиян.",
        "backend-fail-notexists": "Файл $1 яц.",
        "suppress": "Хьулдар",
        "apihelp": "API гӀо",
        "apihelp-no-such-module": "Модуль «$1» цакарий.",
+       "apisandbox": "Ловзаран майда API",
+       "apisandbox-intro": "Лела йе хӀара агӀо '''MediaWiki API''' зуьйш.\nAPI кхин муха лела йо хьажа [//www.mediawiki.org/wiki/API:Main_page кхузахь]. Масала, [//www.mediawiki.org/wiki/API#A_simple_example Коьрта агӀона чулацам схьаэца]. Кхин масалаш ган харжа дийриг.",
+       "apisandbox-submit": "Дехар далар",
+       "apisandbox-reset": "ЦӀанъян",
+       "apisandbox-examples": "Масала",
+       "apisandbox-results": "Хилам",
+       "apisandbox-request-url-label": "Дехаран URL-адрес:",
+       "apisandbox-request-time": "Дехар дина хан: $1",
        "booksources": "Жайнан хьосташ",
        "booksources-search-legend": "Жайнех лаьцна хаам лахар",
        "booksources-search": "Лахар",
        "emailuser": "Декъашхочун хааман кехат",
        "emailuser-title-target": "{{GENDER:$1|декъашхочунга}} электронан хаам базбар",
        "emailuser-title-notarget": "Декъашхочунга кехат яздар",
-       "emailpagetext": "Ð¥Ó\80окÑ\85Ñ\83 Ð°Ð³Ó\80она Ð³Ó\80оÑ\8cнÑ\86а Ð¹Ð¸Ñ\88 Ñ\8e {{GENDER:$1|декÑ\8aаÑ\88Ñ\85оÑ\87Ñ\83н}} Ñ\8dлекÑ\82Ñ\80онан Ð¿Ð¾Ñ\87те хаам бахьийта.\nХьоьга жоп лур ду ахьа [[Special:Preferences|хьайн гӀирса чу]] дӀаяздина долу адрес тӀе.",
+       "emailpagetext": "Ð¥Ó\80окÑ\85Ñ\83 Ð°Ð³Ó\80она Ð³Ó\80оÑ\8cнÑ\86а Ð¹Ð¸Ñ\88 Ñ\8e {{GENDER:$1|декÑ\8aаÑ\88Ñ\85оÑ\87Ñ\83н}} Ñ\8dлекÑ\82Ñ\80онан Ð¿Ð¾Ñ\88те хаам бахьийта.\nХьоьга жоп лур ду ахьа [[Special:Preferences|хьайн гӀирса чу]] дӀаяздина долу адрес тӀе.",
        "defemailsubject": "Хаам {{grammar:genitive|{{SITENAME}}}} чура бу",
        "usermaildisabled": "Декъашхочун электронан пошт дӀаяйина ю",
        "noemailtitle": "Электронан поштан адрес дац",
        "notanarticle": "Яззам бац",
        "notvisiblerev": "Верси дӀаяьккхина хила",
        "watchlist-details": "Хьан тергаме могӀанца $1 {{PLURAL:$1|агӀо}} ю, дийцаре агӀонаш йоцуш.",
-       "wlheader-enotif": "ЭлекÑ\82Ñ\80онан Ð¿Ð¾Ñ\87те хаамаш байтар латина ду.",
+       "wlheader-enotif": "ЭлекÑ\82Ñ\80онан Ð¿Ð¾Ñ\88те хаамаш байтар латина ду.",
        "wlheader-showupdated": "Хийцам бина агӀонаш '''Ӏаьржа''' шрифтцан билгальяха ю.",
        "wlnote": "Гойту <strong>$2</strong> {{plural:$2|сахьтчохь}} бина {{PLURAL:$1|тӀеххьара '''$1''' хийцам}}, хан $3 $4",
        "wlshowlast": "Гайта тӀаьххьара $1 сахьт $2 де",
        "wlshowhideanons": "ЦӀе хьулйина декъашхой",
        "wlshowhidepatr": "хьажжина нисдарш",
        "wlshowhidemine": "Сан нисдарш",
+       "wlshowhidecategorization": "категореш тӀетохар",
        "watchlist-options": "Тергаме могlаман гlирс нисбар",
        "watching": "Тергаме мlогаман юкъаяккха…",
        "unwatching": "Тергаме могӀанан чура дӀаяккхар…",
        "protect-fallback": "Бакъо оьшу «$1»",
        "protect-level-autoconfirmed": "Магийна авто-тӀелаьцна болу декъашхошна",
        "protect-level-sysop": "Магийна куьйгалхошна",
-       "protect-summary-cascade": "чахчареца",
+       "protect-summary-cascade": "каскадан",
        "protect-expiring": "чакхйолу $1 (UTC)",
        "protect-expiring-local": "чекхйолу $1",
        "protect-expiry-indefinite": "хан чаккхе йоцуш",
        "exif-gpsdatestamp": "Терахь",
        "exif-jpegfilecomment": "JPEG-файлан билгалдаккхар",
        "exif-keywords": "Коьрта дешнаш",
+       "exif-countrycreated": "Мохк, сурт дин хилла болу",
+       "exif-citycreated": "ГӀала, сурт дина йолу",
        "exif-objectname": "Йоцца цӀе",
        "exif-specialinstructions": "Къаьсттина тӀехьажор",
        "exif-headline": "Корта",
        "exif-licenseurl": "Авторийн лицензин URL",
        "exif-morepermissionsurl": "Альтернативан лицензин хаам",
        "exif-pngfilecomment": "PNG-файлан билгалдаккхар",
-       "exif-disclaimer": "Ð\91еÑ\85к тӀецалацар",
+       "exif-disclaimer": "Ð\96оÑ\8cпалла тӀецалацар",
        "exif-contentwarning": "Чулацамах лаьцна дӀахьедар",
        "exif-giffilecomment": "GIF-файлан билгалдаккхар",
        "exif-intellectualgenre": "ХӀуман тайп",
        "logentry-newusers-newusers": "{{GENDER:$2|ДӀавазвелла|ДӀаязелла}} керла декъашхо $1",
        "logentry-newusers-create": "{{GENDER:$2|ДӀавазвелла|ДӀаязелла}} керла декъашхо $1",
        "logentry-newusers-create2": "$1 {{GENDER:$2|кхоьллина}} декъашхочун дӀаяздапр $3",
-       "logentry-newusers-byemail": "$1 {{GENDER:$2|кÑ\85оÑ\8cллина}} Ð´ÐµÐºÑ\8aаÑ\88Ñ\85оÑ\87Ñ\83н Ð´Ó\80аÑ\8fздаÑ\80 $3 Ð¿Ð°Ñ\80олÑ\8c Ñ\8dлекÑ\82Ñ\80онан Ð¿Ð¾Ñ\87те яхьийтина",
+       "logentry-newusers-byemail": "$1 {{GENDER:$2|кÑ\85оÑ\8cллина}} Ð´ÐµÐºÑ\8aаÑ\88Ñ\85оÑ\87Ñ\83н Ð´Ó\80аÑ\8fздаÑ\80 $3 Ð¿Ð°Ñ\80олÑ\8c Ñ\8dлекÑ\82Ñ\80онан Ð¿Ð¾Ñ\88те яхьийтина",
        "logentry-newusers-autocreate": "Автоматически кхоьллина {{GENDER:$2|декъашхочун}} $1 дӀаяздар",
+       "logentry-protect-modify": "$1 {{GENDER:$2|хийцина}} ларяран барам $3 $4",
+       "logentry-protect-modify-cascade": "$1 {{GENDER:$2|хийцина}} ларяран барам $3 $4 [каскадан]",
        "logentry-rights-rights": "$1 {{GENDER:$2|хийцина}} $3 бакъо $4 → $5",
        "logentry-rights-rights-legacy": "$1 {{GENDER:$2|хийцина}} хӏокхуна $3 бакъо",
        "logentry-rights-autopromote": "$1 {{GENDER:$2|вара|яра}} автоматически {{GENDER:$2|сихьа ваьлла|сихьа яьлла}} $4 $5 чу",
index e07376f..590bcfa 100644 (file)
        "cancel": "ھەڵیوەشێنەوە",
        "moredotdotdot": "زیاتر",
        "morenotlisted": "ئەم لیستەیە تەواو نییە",
-       "mypage": "پەڕه‌",
+       "mypage": "پەڕە",
        "mytalk": "لێدوان",
        "anontalk": "لێدوان بۆ ئەم ئایپییە",
        "navigation": "ڕێدۆزی",
        "and": "&#32;و",
        "qbfind": "بدۆزەرەوە",
-       "qbbrowse": "بگه‌ڕێ",
+       "qbbrowse": "بگەڕێ",
        "qbedit": "دەستکاری",
        "qbpageoptions": "ئەم پەڕەیە",
        "qbmyoptions": "پەڕەکانم",
        "create": "دروستکردن",
        "create-local": "وەسفی ناوچەیی زۆر بکە",
        "editthispage": "دەستکاری ئەم پەڕەیە بکە‌",
-       "create-this-page": "ئەم پەڕە دروست بکە",
+       "create-this-page": "ئەم پەڕەیە دروست بکە",
        "delete": "سڕینەوە",
-       "deletethispage": "سڕینه‌وه‌ی ئه‌م په‌ڕه‌یه‌",
+       "deletethispage": "سڕینەوه‌ی ئەم پەڕەیە",
        "undeletethispage": "ئەم پەڕەیە بھێنەوە",
        "undelete_short": "{{PLURAL:$1|یەک گۆڕانکاریی|$1 گۆڕانکاریی}} سڕاوە بەجێبھێنەرەوە",
        "viewdeleted_short": "{{PLURAL:$1|یەک گۆڕانکاریی سڕاو|$1 گۆڕانکاریی سڕاو}} ببینە",
        "protect": "پاراستن",
        "protect_change": "گۆڕین",
-       "protectthispage": "ئه‌م په‌ڕه‌یه‌ بپارێزه‌",
+       "protectthispage": "ئەم پەڕەیە بپارێزە",
        "unprotect": "پاراستنی بگۆڕە",
        "unprotectthispage": "پاراستنی ئەم پەڕەیە بگۆڕە",
        "newpage": "پەڕەی نوێ",
        "talkpage": "باس لەسەر ئەم پەڕە بکە‌",
        "talkpagelinktext": "لێدوان",
-       "specialpage": "په‌ڕه‌ی تایبه‌ت",
+       "specialpage": "پەڕەی تایبەت",
        "personaltools": "ئامڕازە تاکەکەسییەکان",
        "articlepage": "پەڕەی ناوەرۆک ببینە",
        "talk": "وتووێژ",
        "projectpage": "پەڕەی پرۆژە نیشان بدە",
        "imagepage": "پەڕەی پەڕگە نیشان بدە",
        "mediawikipage": "پەڕەی پەیام نیشان بدە",
-       "templatepage": "په‌ڕه‌ی داڕێژە ببینە‌",
-       "viewhelppage": "په‌ڕه‌ی یارمه‌تی نیشانبده‌",
-       "categorypage": "په‌ڕه‌ی هاوپۆل نیشانبده‌",
+       "templatepage": "پەڕەی داڕێژە ببینە",
+       "viewhelppage": "پەڕەی یارمەتی ببینە",
+       "categorypage": "پەڕەی پۆل ببینە",
        "viewtalkpage": "بینینی لێدوان",
        "otherlanguages": "بە زمانەکانی تر",
        "redirectedfrom": "(ڕەوانەکراوە لە $1ەوە)",
        "confirmable-yes": "بەڵێ",
        "confirmable-no": "نا",
        "thisisdeleted": "$1 نیشان بدە یا بھێنەوە؟",
-       "viewdeleted": "$1 نیشان بده‌؟",
+       "viewdeleted": "بینینی $1؟",
        "restorelink": "{{PLURAL:$1|یەک گۆڕانکاریی سڕاو|$1 گۆڕانکاریی سڕاو}}",
        "feedlinks": "خۆراک:",
        "feed-invalid": "ئەندام بوونی ئەو جۆرە خۆراکە نەناسراوە.",
        "nosuchactiontext": "ئەو چالاکییەی لە لایەن بەستەرەوە دیاریکراوە ناتەواوە.\nلەوانەیە بە هەڵە بەستەرەکەت نووسیبێت، یان بەستەرێکی هەڵەی بە دواوە بێت.\nلەوانەیە ئەمە نیشانەی هەڵەیەک بێت لەو نەرمەکاڵایەی کە بەکاردێت لە لایەن {{SITENAME}}.",
        "nosuchspecialpage": "پەڕەی تایبەتی ئاوا بوونی نییە",
        "nospecialpagetext": "<strong>پەڕەیەکی تایبەت دەخوازیت کە بوونی نیە.</strong>\n\nلیستێکی پەڕە تایبەتە دروستەکان لە [[Special:SpecialPages|{{int:specialpages}}]] لە بەردەست‌دایە.",
-       "error": "هه‌ڵه‌",
+       "error": "ھەڵە",
        "databaseerror": "ھەڵەی بنکەدراوه",
        "databaseerror-function": "کردە: $1",
        "databaseerror-error": "هەڵە: $1",
        "missingarticle-diff": "(جیاوازی: $1، $2)",
        "readonly_lag": "بنكه‌دراوه‌كه‌ به‌شێوه‌ی خۆكار به‌ندكراوه‌، له‌كاتێكدا بنكه‌دراوه‌ی ڕاژه‌كاره‌كه‌ ڕۆڵی له‌خۆگرتن ده‌گێڕێت",
        "internalerror": "ھەڵەی ناوخۆیی",
-       "internalerror_info": "هه‌ڵه‌ی ناوخۆیی: $1",
+       "internalerror_info": "ھەڵەی ناوخۆیی: $1",
        "filecopyerror": "نەکرا پەڕگەی «$1» کۆپی بکرێت بۆ «$2».",
-       "filerenameerror": "ناوی په‌ڕگه‌ی \"$1\" نه‌گۆڕدرا بۆ \"$2\".",
+       "filerenameerror": "ناکرێت ناوی پەڕگەی «$1» بگۆڕرێت بۆ «$2».",
        "filedeleteerror": "نەکرا پەڕگەی «$1» بسڕدرێتەوە.",
        "directorycreateerror": "نەتوانرا بوخچەی \"$1\"دروست بکرێت.",
-       "filenotfound": "په‌ڕگه‌ی \"$1\" نه‌دۆزرایه‌وه‌",
+       "filenotfound": "پەڕگەی «$1» نەدۆزرایەوە",
        "unexpected": "نرخی چاوەڕوان نەکراو: \"$1\"=\"$2\" .",
        "formerror": "هەڵە: فورمەکە نانێردرێت.",
        "badarticleerror": "ئەو ئاماژە لەم لاپەڕەدا پێک‌نایە.",
        "viewsourcetext": "دەتوانی سەرچاوەی ئەم پەڕە ببینی و کۆپیی بکەی:",
        "viewyourtext": "دەتوانی ژێدەری '''دەستکارییەکەت''' لەم پەڕەیەدا ببینی و کۆپی بکەی:",
        "protectedinterface": "ئەم پەڕەیە دەقی ڕواڵەتی نەرمامێری ئەم ویکییە نیشان دەدات و بۆ بەرگری لە خراپکاری پارێزراوە.\nبۆ زیادکردن یان گۆڕینی وەرگێڕانەکان بۆ ھەموو ویکییەکان، تکایە لە [//translatewiki.net/ translatewiki.net]، پرۆژەی ناوچەیی کردنی میدیاویکی کەڵک وەربگرە.",
-       "editinginterface": "<strong>ھۆشیار بە:</strong> خەریکی دەستکاریی پەڕەیەک دەکەیت کە بۆ دابینکردنی دەقی ڕووکاری نەرمامێر بەکاردێت.\nگۆڕانکارییەکانی لەم پەڕەیەدا کاریگەر دەبێت لە سەر ڕواڵەتی پەڕەکانی بەکارھێنەرانی تر لەم ویکییەدا.",
+       "editinginterface": "<strong>ھۆشیار بە:</strong> خەریکی دەستکاریی پەڕەیەک دەکەیت کە بۆ دابین کردنی دەقی ڕووکاری نەرمامێر بەکاردێت.\nگۆڕانکارییەکان لەم پەڕەیەدا لە سەر ڕواڵەتی پەڕەکان بۆ بەکارھێنەرانی تر لەم ویکییەدا کاریگەر دەبێت.",
        "cascadeprotected": "ئەم لاپەڕە پارێزراوە لە دەستکاریی، چونکا خراوەتە سەر ڕیزی ئەم {{PLURAL:$1|لاپەڕانه‌، کە}} که‌ به‌ هه‌ڵکردنی بژارده‌ی داڕژان هه‌ڵکراوه‌:\n$2",
        "namespaceprotected": "تۆ ناتوانی لاپەڕەکانی ناو نەیمسپەیسی '''$1''' بگۆڕی.",
        "customcssprotected": "دەسەڵاتی دەستکارییکردنی ئەم پەڕەی CSS ـەت نییە چوونکە ڕێکخستنەکانی کەسێکی تر لەخۆ دەگرێت.",
        "missingcommentheader": "'''بیرهێنانەوە:''' بۆ ئەم بۆچوونەت سەردێڕ\\بابەت ڕاچاو نەکردووە.\nئەگەر دیسان «{{int:savearticle}}» لێبدەی، دەستکاریەکەت بێ سەردێڕ یان بابەت پاشەکەوت دەبێ.",
        "summary-preview": "پێشبینینی کورتە:",
        "subject-preview": "پێشبینینی بابەت/سەردێڕ:",
-       "blockedtitle": "به‌کار هینه‌ر له‌کار خراوه",
+       "blockedtitle": "بەکارھێنەر بەربەست کراوە",
        "blockedtext": "'''ناوی بەکارهێنەری یان ئای‌پی ئەدرەسی تۆ بەربەست‌ کراوە.'''\n\nبەربەست لە لایەن $1 کراوە.\nهۆکاری بەربەست کردن ''$2''ە.\n\n* دەستپێکی بەربەست‌کران: $8\n* کۆتایی هاتنی بەربەست‌کران: $6\n* بابەتی بەربەست: $7\n\nبۆ وتووێژ سەبارەت بە بەربەست‌کرانەکە دەبێ پەیوەندی بکەی بە $1 یان یەکێ دی لە [[{{MediaWiki:Grouppage-sysop}}|بەڕێوبەران]].\nلە بیرت بێ تاکوو ئیمەیل ئەدرەسێکی بڕوا پێ‌کراو لە [[Special:Preferences|ھەڵبژاردەکانی بەکارھێنەر]] ڕاچاو نەکەی، نابێت لە هەلی «ئیمەیل ناردن بۆ ئەم بەکارهێنەرە» کەڵک وەر بگری؛ کەڵک وەرگرتن لەوە بەربەست نەکراوە بۆت.\n\nئای‌پی ئەدرەسی ئێستای تۆ $3 و پێناسەی بەربەست‌کراو #$5.\nتکایە لە هەر پرس و داواکاریەکت‌دا هەموو وردەکاریەکانی سەرەوە بگونجێنە.",
        "autoblockedtext": "ناونیشانی IPی تۆ بە شێوەی خۆکارانە بەرگیری لێ کراوە چوونکە بەکارھێنەرێکی دیکە بە خراپی بە کاری ھێناوە و بە دەستی $1 بەرگیری لێ کراوە.\nبەم ھۆکارەوە:\n\n:<em>$2</em>\n\n* دەست پێ کردنی بەرگیری: $8\n* بە سەر چوونی بەرگیری: $6\n* Intended blockee: $7\n\nدەتوانیت پەیوەندی بکەیت بە $1 یان یەکێکی دیکە لە [[{{MediaWiki:Grouppage-sysop}}|بەڕێوەبەران]] بۆ وتووێژ لە سەر بەرگیرییەکە.\n\nتێ بگە کە ناتوانیت ئامرازی «ئیمێل بنێرە بۆ ئەم بەکارھێنەرە» بە کار بھێنیت مەگەر ئەوەی کە پێشتر لە [[Special:Preferences|ھەڵبژاردەکانی بەکارھێنەر]]تدا ناونیشانێکی گونجاوی ئیمێلت تۆمار کردبێت و بەرگیریت لێ نەکرابێت لە بەکارھێنانی ئەو ئامرازەش.\n\nناونیشانی IPی ئێستای تۆ $3ـە و پێناسەی یەرگیرییەکە #$5ـە.\nتکایە ھەموو وردەکارییەکانی سەرەوە ھەبێت لە ھەر پرس و داوایک کە دەیکەیت.",
        "blockednoreason": "هیچ هۆکارێک نەدراوە",
        "clearyourcache": "تێبینی:''' لە دوای پاشەکەوت کردن، لەوانەیە  بۆ بینینی گۆڕانکارییەکان پێویست بێ cacheی وێبگەڕەکەت پاکبکەیتەوە.\n* '''Firefox / Safari:''' دوگمەی ''Shift'' بگرە کاتێک لەسەر ''Reload''دا کرتە دەکەی، یان ھەرکام لە ''Ctrl-F5'' یان ''Ctrl-R'' لێبدە (''⌘-R'' لەسەر Mac دا)\n* '''Google Chrome:''' دوگمەکانی ''Ctrl-Shift-R'' لێبدە  (''⌘-Shift-R'' لەسەر Mac دا)\n* '''Internet Explorer:''' دوگمەی ''Ctrl'' بگرە کاتێک لەسەر  ''Refresh''دا کرتە دەکەی، یان ''Ctrl-F5'' لێبدە\n* '''Opera:''' لە ڕێگەی ''Tools → Preferences'' ەوە cacheەکە بسڕەوە.",
        "usercssyoucanpreview": "'''سەرچەشن:''' «{{int:showpreview}}» بەکاربێنە بۆ تاقی‌کردنەوەی CSS نوێ‌کەت، پێش پاشەکەوت‌کردن.",
        "userjsyoucanpreview": "'''سەرچەشن:''' «{{int:showpreview}}» بەکاربێنە بۆ تاقی‌کردنەوەی جاڤاسکریپتە نوێ‌کەت، پێش پاشەکەوت‌کردن.",
-       "usercsspreview": "'''له‌یادت بێ که‌ ئێسته‌ ته‌نها پێشبینینی CSS به‌کارهێنه‌ریه‌که‌ت ده‌که‌ی.'''\n'''هێشتا پاشه‌که‌وت نه‌بووه !'''",
+       "usercsspreview": "<strong>لە بیرت ببێت کە تەنھا خەریکی پێشبینینی CSSـەکەت دەبینیت.\nھێشتا پاشەکەوەت نەکراوە!</strong>",
        "userjspreview": "'''لەیادت بێ کە ئێستە تەنها پێشبینین\\تاقی‌کردنەوەی جاڤاسکریپتی بەکارهێنەریەکەت دەکەی.'''\n'''هێشتا پاشەکەوت نەبووه !'''",
-       "sitecsspreview": "'''له‌یادت بێ که‌ ئێسته‌ ته‌نها پێشبینینی ئەم CSS ده‌که‌ی.'''\n'''هێشتا پاشه‌که‌وت نه‌کراوە !'''",
+       "sitecsspreview": "<strong>لە بیرت ببێت کە تەنھا خەریکی پێشبینینی ئەم CSSـە دەبینیت.\nھێشتا پاشەکەوەت نەکراوە!</strong>",
        "sitejspreview": "'''لە بیرت نەچێت ئەمە تەنیا پێشبینینی ئەم کۆدەی جاڤاسکریپتە.'''\n'''گۆڕانکارییەکانت ھێشتا پاشەکەوت نەکراون!'''",
        "userinvalidcssjstitle": "'''ئاگادارکردنەوە:''' پێست نیە بۆ \"$1\".\nلەیادت بێ کە لاپەڕەکانی‌ .css و .js لە بابەت بە پیتی بچووک کەڵک وەر ئەگرن. وەک {{ns:user}}:Foo/vector.css نە وەک {{ns:user}}:Foo/Vector.css .",
        "updated": "(نوێ‌کراوە)",
        "semiprotectedpagewarning": "'''ئاگاداری:''' ئەم پەڕە داخراوە بۆ ئەوی تەنھا بەکارھێنەرە تۆمارکراوەکان بتوانن دەستکاریی بکەن.\nدوایین لۆگ بۆ ژێدەر لە خوارەوەدا ھاتووە:",
        "cascadeprotectedwarning": "'''ئاگاداری:''' ئەم لاپەڕە داخراوە بۆیە تەنها ئەو کەسانەی مافی بەڕێوبەرایەتی‌یان هەیە ئەتوانن دەستکاری بکەن، چۆنکا ئەمە {{PLURAL:$1|لاپه‌ڕه‌|لاپه‌ڕانه‌}} لە زنجیرەی پارێزراوەکانی لە خۆ گرتووە‌:",
        "titleprotectedwarning": "'''ئاگاداری: ئەم پەڕە داخراوە، بۆئەوەی بۆ درووست‌کردنی [[Special:ListGroupRights|مافە تایبەتەکانت]] پێویستن.'''\nبۆ چاوانە دوایین لۆگ لە خوارەوەدا ھاتووە:",
-       "templatesused": "ئەو {{PLURAL:$1|داڕێژە کە لەم پەڕەیەدا بە کارھێنراوە|داڕێژانە کە لەم پەڕەیەدا بە کارھێنراون}}:",
-       "templatesusedpreview": "ئەو {{PLURAL:$1|داڕێژە کە لەم پێشبینینەدا بە کارھێنراوە|داڕێژانە کە لەم پێشبینینەدا بە کارھێنراون}}:",
+       "templatesused": "ئەو {{PLURAL:$1|داڕێژەیە کە لەم پەڕەیەدا بە کارھێنراوە|داڕێژانە کە لەم پەڕەیەدا بە کارھێنراون}}:",
+       "templatesusedpreview": "ئەو {{PLURAL:$1|داڕێژەیە کە لەم پێشبینینەدا بە کارھێنراوە|داڕێژانە کە لەم پێشبینینەدا بە کارھێنراون}}:",
        "templatesusedsection": "ئەو {{PLURAL:$1|داڕێژە|داڕێژانە}} کە لەم بەشەدا بە کارھێنراون:",
        "template-protected": "(پارێزراو)",
        "template-semiprotected": "(نیوەپارێزراو)",
        "prefs-reset-intro": "دەتوانی لەم لاپەڕە بۆ گەڕانەوەی هەڵبژاردەکانت بۆ بنچینەیی ماڵپەر کەڵک وەرگریت.\nگەر ئەوە بکەی ئیتر گۆڕانەکەت ناگەڕێتەوە.",
        "prefs-emailconfirm-label": "پشتڕاستکردنەوەی ئیمەیل:",
        "youremail": "ئیمەیل:",
-       "username": "{{GENDER:$1|ناوی به‌کارھێنەر}}:",
+       "username": "{{GENDER:$1|ناوی بەکارھێنەر}}:",
        "prefs-memberingroups": "{{GENDER:$2|ئەندامی}} {{PLURAL:$1|گرووپی|گرووپەکانی}}:",
        "prefs-registration": "کاتی خۆتۆمارکردن:",
        "yourrealname": "ناوی ڕاستی:",
        "yourvariant": "شێوەزاری زمانی ناوەرۆک:",
        "yournick": "واژووی نوێ:",
        "prefs-help-signature": "بۆچوونەکان لە لاپەڕەکانی وتووێژدا دەبێ بە \"<nowiki>~~~~</nowiki>\" دیاری بکرێن، کە دواتر خۆکار دەگۆڕێ بە واژۆکەت و مۆری کاتی.",
-       "badsig": "ئیمزاكه‌ هه‌ڵه‌یه‌، ته‌ماشای كۆدی HTML بكه‌‌",
+       "badsig": "کۆدی ئیمزای ھەڵە.\nبە تاگە HTMLکاندا بچۆرەوە.",
        "badsiglength": "واژووەکەت زۆر درێژە.\nواژوو نابێ لە $1 {{PLURAL:$1|نووسە}} درێژتر بێت.",
        "yourgender": "پێت خۆشە چۆن وەسف بکرێیت؟",
        "gender-unknown": "پێم خۆشە باسی نەکەم",
        "newuserlogpagetext": "ئەمە لۆگێکی دروستکردنی بەکارھێنەرە.",
        "rightslog": "لۆگی مافەکانی بەکارھێنەر",
        "rightslogtext": "ئەمە لۆگی دەستکاری مافەکانی بەکار‌هێنەرە.",
-       "action-read": "خوێندنەوەی ئەم پەڕە",
+       "action-read": "خوێندنەوەی ئەم پەڕەیە",
        "action-edit": "دەستکاریی ئەم پەڕەیە",
        "action-createpage": "دروستکردنی پەڕەکان",
        "action-createtalk": "دروستکردنی پەڕەکانی وتووێژ",
        "action-createaccount": "دروست کردنی ئەم ھەژماری بەکارھێنەرییە",
-       "action-history": "مێژووی ئەم پەڕەیە ببینە",
+       "action-history": "بینینی مێژووی ئەم پەڕەیە",
        "action-minoredit": "نیشان‌کردنی ئەم دەستکاریە وەک بچووک",
        "action-move": "گواستنەوەی ئەم پەڕەیە",
        "action-move-subpages": "گواستنەوەی ئەم پەڕەیە و ژێرپەڕەکانی",
        "listgrouprights-addgroup-self": "زیادکردنی {{PLURAL:$2|گرووپ|گرووپەکان}} بۆ سەر ھەژماری خۆی: $1",
        "listgrouprights-removegroup-self": "لابردنی {{PLURAL:$2|گرووپ|گرووپەکان}} لە سەر ھەژماری خۆی: $1",
        "listgrouprights-addgroup-self-all": "زیادکردنی ھەموو گرووپەکان بۆ سەر ھەژماری خۆی",
-       "listgrouprights-removegroup-self-all": "لابردنی هەموو گرووپەکان له‌ سه‌ر هه‌ژماری خۆ",
+       "listgrouprights-removegroup-self-all": "لابردنی ھەموو گرووپەکان لە سەر ھەژماری خۆی",
        "listgrouprights-namespaceprotection-header": "سنوورداریی بۆشایی ناو",
        "listgrouprights-namespaceprotection-namespace": "بۆشایی ناو",
        "listgrouprights-namespaceprotection-restrictedto": "مافی رێ‌پێدراوی بەکارھێنەر بۆ دەستکاری",
        "trackingcategories": "پۆلەکانی شوێنکەوتن",
        "trackingcategories-name": "ناوی پەیام",
-       "mailnologin": "ناونیشان بۆ ناردن نییه‌",
+       "mailnologin": "ناونیشان بۆ ناردن نییە",
        "mailnologintext": "ده‌بێ له‌ [[Special:UserLogin|ژووره‌وه‌]] بیت و ناونیشانێکی بڕواپێ‌کراوی ئی‌مه‌یلت له‌ ناو [[Special:Preferences|هه‌ڵبژارده‌کان]] دیاری کردبێت تا بتوانی ئی‌مه‌یل بنێریت بۆ به‌کارهێنه‌رانی دیکه‌.",
        "emailuser": "ئیمەیل بنێرە بۆ ئەم بەکارھێنەرە",
        "emailuser-title-target": "ئیمەیلی ئەم {{GENDER:$1|بەکارھێنەر}}ە",
        "protect-expiring-local": "بەسەردەچێ لە $1",
        "protect-expiry-indefinite": "بێسنوور",
        "protect-cascade": "پەڕەکانی نێو ئەم پەڕە بپارێزە (پاراستنی تاڤگەیی)",
-       "protect-cantedit": "ناتوانی ئاستی پاراستنی ئەم پەڕە بگۆڕی، چونکوو تۆ ئیجازەی ئەم کارەت نیە.",
+       "protect-cantedit": "ناتوانیت ئاستەکانی پاراستنی ئەم پەڕەیە بگۆڕیت، چونکە بۆت نییە دەستکاریی بکەیت.",
        "protect-othertime": "کاتی تر:",
        "protect-othertime-op": "کاتی تر",
        "protect-existing-expiry": "ئەم کاتی بەسەرچوونی ماوە کە هەیە: $3، $2",
        "tooltip-ca-undelete": "هێنانەوەی دەستکاریەکانی پیش سڕینەوە وا لەسەر ئەم لاپەڕە ڕووی‌داوە",
        "tooltip-ca-move": "ئەم پەڕەیە بگوازەوە",
        "tooltip-ca-watch": "ئەم پەڕە بخە سەر لیستی چاودێریت",
-       "tooltip-ca-unwatch": "ئەم پەڕە لە لیستی چاودێریت لابە",
+       "tooltip-ca-unwatch": "ئەم پەڕەیە لە لیستی چاودێریت لاببە",
        "tooltip-search": "لە {{SITENAME}} بگەڕێ",
        "tooltip-search-go": "بڕۆ بۆ پەڕەیەک کە بە تەواوی ئەم ناوەی ھەیە ئەگەر بببێت",
        "tooltip-search-fulltext": "لە پەڕەکاندا بگەڕێ بۆ ئەم دەقە",
        "tooltip-save": "گۆڕانکارییەکانی خۆت پاشکەوت بکە",
        "tooltip-preview": "پێش بینینی گۆڕانکارییەکان، تکایە پێش پاشکەوت کردن ئەمە بەکار بھێنە",
        "tooltip-diff": "نیشان دانی گۆڕانکارییەکانت لە دەقەکەدا",
-       "tooltip-compareselectedversions": "جیاوازییەکانی دوو وەشانە دیاریکراوەی ئەم پەڕە ببینە.",
+       "tooltip-compareselectedversions": "جیاوازییەکانی دوو وەشانە دیاریکراوەی ئەم پەڕەیە ببینە.",
        "tooltip-watch": "ئەم پەڕە بخە سەر لیستی چاودێریت",
        "tooltip-watchlistedit-normal-submit": "ناونیشانەکان لاببە",
        "tooltip-watchlistedit-raw-submit": "نوێکردنەوەی لیستی چاودێری",
index d098b02..adc0dbc 100644 (file)
@@ -6,7 +6,8 @@
                        "Don Alessandro",
                        "Urhixidur",
                        "아라",
-                       "Исмаил Садуев"
+                       "Исмаил Садуев",
+                       "Умар"
                ]
        },
        "tog-underline": "Багълантыларнынъ тюбюни сызув:",
        "mytalk": "Музакере",
        "anontalk": "Бу IP-нинъ музакереси",
        "navigation": "Сайтта ёл тапув",
-       "and": "&#32;а",
+       "and": "&#32;ве",
        "qbfind": "Тап",
        "qbbrowse": "Бакъып чыкъ",
        "qbedit": "Денъиштир",
        "watchthisupload": "Бу файлны козет",
        "filewasdeleted": "Бу исимде бир файл бар эди, амма ёкъ этильген эди. Лютфен, текрар юклемеден эвель $1 тешкеринъиз.",
        "filename-bad-prefix": "Сиз юклеген файлнынъ ады '''\"$1\"'''-нен башлана. Бу, адетиндже, ракъамлы фотоаппаратлардан файл адына язылгъан манасыз ишаретлердир. Лютфен, бу файл ичюн анълыджа бир ад сайлап язынъыз.",
-       "upload-success-subj": "Юкленюв беджерильди",
        "upload-proto-error": "Янълыш протокол",
        "upload-proto-error-text": "Интернеттен бир ресим файлы юклемеге истесенъиз адрес <code>http://</code> я да <code>ftp://</code>нен башламалы.",
        "upload-file-error": "Ички хата",
        "wlheader-showupdated": "Сонъки зияретинъизден сонъ денъиштирильген саифелер '''къалын арифлернен''' косьтерильди.",
        "wlnote": "Ашагъыда саат $3, $4 ичюн сонъки {{PLURAL:$2|1=саат|'''$2''' саат}} ичинде япылгъан сонъки {{PLURAL:$1|1=денъиштирме|'''$1''' денъиштирме}} косьтериле.",
        "wlshowlast": "Сонъки $1 саат ичюн, $2 кунь ичюн я да  косьтер",
-       "watchlistall2": "эписини",
        "watchlist-options": "Козетюв джедвели сазламалары",
        "watching": "Козетюв джедвелине кирсетильмекте...",
        "unwatching": "Козетюв джедвелинден ёкъ этильмекте...",
index eae4dfd..ac313b7 100644 (file)
        "uploaded-script-svg": "V načteném SVG souboru byl nalezen skriptovatelný element „$1“.",
        "uploaded-hostile-svg": "V načteném SVG souboru bylo v elementu se styly nalezeno nebezpečné CSS.",
        "uploaded-event-handler-on-svg": "Nastavování atributů pro obsluhu událostí <code>$1=\"$2\"</code> není v SVG souborech dovoleno.",
-       "uploaded-href-unsafe-target-svg": "V načteném SVG souboru byl nalezen href s nebezpečným cílem <code>&lt;$1 $2=\"$3\"&gt;</code>.",
+       "uploaded-href-attribute-svg": "Atributy href v souborech SVG smějí odkazovat jen na cíle využívající http:// nebo https://, nalezeno <code>&lt;$1 $2=\"$3\"&gt;</code>.",
+       "uploaded-href-unsafe-target-svg": "V načteném SVG souboru byl nalezen href odkazující na nebezpečný cíl s datovým URI <code>&lt;$1 $2=\"$3\"&gt;</code>.",
        "uploaded-animate-svg": "V načteném SVG souboru byla nalezena značka „animate“, která by mohla měnit href, s atributem „from“ <code>&lt;$1 $2=\"$3\"&gt;</code>.",
        "uploaded-setting-event-handler-svg": "Nastavování atributů pro obsluhu událostí je zablokováno, v načteném SVG souboru bylo nalezeno <code>&lt;$1 $2=\"$3\"&gt;</code>.",
        "uploaded-setting-href-svg": "Použití značky „set“ pro přidání atributu „href“ rodičovskému elementu je zablokováno.",
        "querypage-disabled": "Tato speciální stránka je z výkonnostních důvodů vypnuta.",
        "apihelp": "Nápověda k API",
        "apihelp-no-such-module": "Modul „$1“ nebyl nalezen.",
+       "apisandbox": "API pískoviště",
+       "apisandbox-jsonly": "Pro použití API pískoviště je nutný JavaScript.",
+       "apisandbox-api-disabled": "API je na tomto webu vypnuto.",
+       "apisandbox-intro": "Pomocí této stránky můžete experimentovat s <strong>webovými službami MediaWiki API</strong>.\nPodrobnosti využití API najdete v [[mw:API:Main page|jeho dokumentaci]]. Příklad: [//www.mediawiki.org/wiki/API#A_simple_example získání obsahu Hlavní stránky]. Další příklady uvidíte vybráním parametru action.\n\nUvědomte si, že přestože jste na pískovišti, mohou akce provedené na této stránce wiki změnit.",
+       "apisandbox-fullscreen": "Rozbalit panel",
+       "apisandbox-fullscreen-tooltip": "Rozbalí panel pískoviště, aby vyplnil okno prohlížeče.",
+       "apisandbox-unfullscreen": "Zobrazit stránku",
+       "apisandbox-unfullscreen-tooltip": "Zmenší panel pískoviště, aby byly dostupné navigační odkazy MediaWiki.",
+       "apisandbox-submit": "Odeslat požadavek",
+       "apisandbox-reset": "Vyčistit",
+       "apisandbox-retry": "Zkusit znovu",
+       "apisandbox-loading": "Načítají se informace o API modulu „$1“…",
+       "apisandbox-load-error": "Při načítání informací k API modulu „$1“ došlo k chybě: $2",
+       "apisandbox-no-parameters": "Tento API modul nemá žádné parametry.",
+       "apisandbox-helpurls": "Odkazy na nápovědu",
+       "apisandbox-examples": "Příklady",
+       "apisandbox-dynamic-parameters": "Doplňkové parametry",
+       "apisandbox-dynamic-parameters-add-label": "Přidat parametr:",
+       "apisandbox-dynamic-parameters-add-placeholder": "Jméno parametru",
+       "apisandbox-dynamic-error-exists": "Parametr s názvem „$1“ již existuje.",
+       "apisandbox-deprecated-parameters": "Zavržené parametry",
+       "apisandbox-fetch-token": "Automaticky naplnit token",
+       "apisandbox-submit-invalid-fields-title": "Některá pole jsou neplatná",
+       "apisandbox-submit-invalid-fields-message": "Opravte označená pole a zkuste to znovu.",
+       "apisandbox-results": "Výsledky",
+       "apisandbox-sending-request": "Odesílá se API požadavek…",
+       "apisandbox-loading-results": "Přijímají se API výsledky…",
+       "apisandbox-results-error": "Došlo k chybě při načítání odpovědi na API dotaz: $1.",
+       "apisandbox-request-url-label": "URL požadavku:",
+       "apisandbox-request-time": "Trvání požadavku: {{PLURAL:$1|$1 ms}}",
+       "apisandbox-results-fixtoken": "Opravit token a znovu odeslat",
+       "apisandbox-results-fixtoken-fail": "Nepodařilo se načíst token „$1“.",
+       "apisandbox-alert-page": "Pole na této stránce nejsou platná.",
+       "apisandbox-alert-field": "Hodnota tohoto pole není platná.",
        "booksources": "Zdroje knih",
        "booksources-search-legend": "Vyhledat knižní zdroje",
        "booksources-search": "Hledat",
        "group-bot.js": "/* Zde uvedený JavaScript bude použit pouze pro boty */",
        "group-sysop.js": "/* Zde uvedený JavaScript bude použit pouze pro správce */",
        "group-bureaucrat.js": "/* Zde uvedený JavaScript bude použit pouze pro byrokraty */",
-       "anonymous": "anonymní {{PLURAL:$1|uživatel|uživatelé|uživatelé}} {{GRAMMAR:2sg|{{SITENAME}}}}",
+       "anonymous": "{{PLURAL:$1|anonymního uživatele|anonymních uživatelů}} {{GRAMMAR:2sg|{{SITENAME}}}}",
        "siteuser": "uživatel {{grammar:2sg|{{SITENAME}}}} $1",
        "anonuser": "anonymní uživatel {{grammar:2sg|{{SITENAME}}}} $1",
        "lastmodifiedatby": "Tuto stránku naposledy {{GENDER:$4|změnil|změnila|změnil}} $3 v $2, $1.",
-       "othercontribs": "Do textu {{PLURAL:$2|přispěl|přispěli}} $1.",
+       "othercontribs": "Založeno na práci $1.",
        "others": "další",
-       "siteusers": "{{PLURAL:$2|uživatel|uživatelé|uživatelé}} {{grammar:2sg|{{SITENAME}}}} $1",
+       "siteusers": "{{PLURAL:$2|{{GENDER:$1|uživatele|uživatelky}}|uživatelů}} {{grammar:2sg|{{SITENAME}}}} $1",
        "anonusers": "anonymní {{PLURAL:$2|uživatel|uživatelé}} {{grammar:2sg|{{SITENAME}}}} $1",
        "creditspage": "Zásluhy za stránku",
        "nocredits": "K této stránce neexistuje informace o zásluhách.",
        "expand_templates_preview_fail_html": "<em>Protože {{SITENAME}} má povolené syrové HTML a došlo ke ztrátě dat relace, je náhled skryt kvůli ochraně před JavaScriptovými útoky.</em>\n\n<strong>Pokud to byl legitimní pokus o náhled, zkuste to znovu.</strong>\nPokud to stále nebude fungovat, zkuste se [[Special:UserLogout|odhlásit]] a znovu přihlásit.",
        "expand_templates_preview_fail_html_anon": "<em>Protože {{SITENAME}} má povolené syrové HTML a vy nejste přihlášeni, je náhled skryt kvůli ochraně před JavaScriptovými útoky.</em>\n\n<strong>Pokud to byl legitimní pokus o náhled, [[Special:UserLogin|přihlaste se]] a zkuste to znovu.</strong>",
        "expand_templates_input_missing": "Musíte zadat alespoň nějaký vstupní text.",
-       "pagelanguage": "Volba jazyka stránky",
+       "pagelanguage": "Změnit jazyk stránky",
        "pagelang-name": "Stránka",
        "pagelang-language": "Jazyk",
        "pagelang-use-default": "Použít implicitní jazyk",
index 145a9bc..70fbb4b 100644 (file)
        "october-date": "октѡврїꙗ $1 числа",
        "november-date": "ноємврїꙗ $1 числа",
        "december-date": "дєкємврїꙗ $1 числа",
+       "period-am": "до полоудьни",
+       "period-pm": "по полоудьни",
        "pagecategories": "{{PLURAL:$1|Катигорїꙗ|Катигорїи|Катигорїѩ|Катигорїѩ}}",
        "category_header": "катигорїѩ ⁖ $1 ⁖ страницѧ",
        "subcategories": "подъкатигорїѩ",
        "category-media-header": "катигорїѩ ⁖ $1 ⁖ дѣла",
-       "category-empty": "''си катигорїи нꙑнѣ страницѧ и дѣлъ нѣстъ''",
+       "category-empty": "''сѥи катигорїи нꙑнѣ страницѧ и дѣлъ нѣстъ''",
        "hidden-categories": "{{PLURAL:$1|съкрꙑта катигорїꙗ|съкрꙑти катигорїи|съкрꙑтꙑ катигорїѩ}}",
        "hidden-category-category": "съкрꙑтꙑ катигорїѩ",
-       "category-subcat-count": "{{PLURAL:$2|Сѥи катигорїи тъкъмо сꙗ подъкатигорїꙗ ѥстъ|Сѥи катигорїи {{PLURAL:$1|ѥдина подъкатигорїꙗ ѥстъ|2 подъкатигорїи ѥстє|$1 подъкатигорїѩ сѫтъ}} · а вьсѩ жє подъкатигорїѩ число $2 ѥстъ}}",
+       "category-subcat-count": "{{PLURAL:$2|1=Сѥи катигорїи тъкъмо сꙗ подъкатигорїꙗ ѥстъ|Сѥи катигорїи {{PLURAL:$1|1=ѥдина подъкатигорїꙗ ѥстъ|2 подъкатигорїи ѥстє|$1 подъкатигорїѩ сѫтъ}} · а вьсѩ жє подъкатигорїѩ число $2 ѥстъ}}",
        "listingcontinuesabbrev": "· вѧщє",
        "about": "опьсаниѥ",
        "article": "члѣнъ",
        "anontalk": "бєсѣда",
        "navigation": "плаваниѥ",
        "and": "&#32;и",
-       "qbedit": "иÑ\81пÑ\80ави",
+       "qbedit": "иÑ\81пÑ\80авлѥниѥ",
        "qbpageoptions": "сꙗ страница",
        "qbmyoptions": "моꙗ страницѧ",
        "faq": "чѧстꙑ въпроси",
        "faqpage": "Project:Чѧстꙑ въпроси",
        "actions": "дѣиства",
        "namespaces": "имєнъ просторꙑ",
+       "variants": "обраꙁи",
        "navigation-heading": "плаваниѥ",
        "errorpagetitle": "блаꙁна",
        "returnto": "къ страници ⁖ $1 ⁖ въꙁвращєниѥ ⁙",
        "permalink": "въиньна съвѧꙁь",
        "print": "пєчатаниѥ",
        "view": "поꙁьрѣниѥ",
-       "edit": "иÑ\81пÑ\80ави",
+       "edit": "иÑ\81пÑ\80авлѥниѥ",
        "create": "сътворѥниѥ",
-       "editthispage": "си страницѧ исправлѥниѥ",
-       "create-this-page": "си страницѧ сътворѥниѥ",
+       "editthispage": "сѥѩ страницѧ исправлѥниѥ",
+       "create-this-page": "сѥѩ страницѧ сътворѥниѥ",
        "delete": "поничьжєниѥ",
-       "deletethispage": "си страницѧ поничьжєниѥ",
+       "deletethispage": "сѥѩ страницѧ поничьжєниѥ",
+       "undeletethispage": "сѥѩ страницѧ въстаниѥ иꙁ поничьжєниꙗ",
+       "undelete_short": "въстаниѥ {{PLURAL:$1|ѥдьнꙑ мѣнꙑ|$1 мѣноу|$1 мѣнъ}} иꙁ поничьжєниꙗ",
+       "viewdeleted_short": "{{PLURAL:$1|ѥдьнꙑ поничьжєнꙑ мѣнꙑ|$1 поничьжєноу мѣноу|$1 поничьжєнъ мѣнъ}} поꙁьрѣниѥ",
        "protect": "ꙁабранѥниѥ",
        "protect_change": "иꙁмѣнѥниѥ",
-       "protectthispage": "си страницѧ ꙁабранєниѥ",
+       "protectthispage": "сѥѩ страницѧ ꙁабранєниѥ",
        "unprotect": "ꙁабранѥниꙗ обраꙁа иꙁмѣнѥниѥ",
+       "unprotectthispage": "ꙁабранѥниꙗ сѥѩ страницѧ обраꙁа иꙁмѣнѥниѥ",
        "newpage": "нова страница",
-       "talkpage": "си страницѧ бєсѣда",
+       "talkpage": "сѥѩ страницѧ бєсѣда",
        "talkpagelinktext": "бєсѣда",
        "specialpage": "нарочьна страница",
        "personaltools": "моꙗ орѫдиꙗ",
+       "articlepage": "члѣна поꙁьрѣниѥ",
        "talk": "бєсѣда",
+       "views": "поꙁьрѣниꙗ",
        "toolbox": "орѫдиꙗ",
+       "userpage": "польꙃєватєлꙗ страницѧ поꙁьрѣниѥ",
+       "imagepage": "дѣла страницѧ поꙁьрѣниѥ",
+       "templatepage": "обраꙁьца страницѧ поꙁьрѣниѥ",
+       "viewhelppage": "помощи страницѧ поꙁьрѣниѥ",
+       "categorypage": "катигорїѩ страницѧ поꙁьрѣниѥ",
+       "viewtalkpage": "бєсѣдꙑ поꙁьрѣниѥ",
        "otherlanguages": "дроугꙑ ѩꙁꙑкꙑ",
        "redirectedfrom": "(прѣнаправлѥниѥ отъ ⁖ $1 ⁖)",
        "redirectpagesub": "прѣнаправлѥниѥ",
        "redirectto": "прѣнаправлѥниѥ къ :",
        "lastmodifiedat": "страницѧ послѣдьнꙗ мѣна сътворѥна $2 · $1 бѣ ⁙",
+       "protectedpage": "сꙗ страница ꙁабранѥна ѥстъ",
        "jumpto": "прѣиди къ :",
        "jumptonavigation": "плаваниѥ",
        "jumptosearch": "исканиѥ",
        "policy-url": "Project:Полїтїка",
        "portal": "обьщєниꙗ съвѣтъ",
        "portal-url": "Project:Обьщєниꙗ съвѣтъ",
+       "privacy": "личьнъ вѣстии полїтїка",
+       "privacypage": "Project:Личьнъ вѣстии полїтїка",
        "pagetitle": "$1 · {{SITENAME}}",
        "retrievedfrom": "поѩто иꙁ ⁖ $1 ⁖",
        "youhavenewmessages": "$1 тєбѣ напьсанꙑ сѫтъ ($2)",
        "newmessageslinkplural": "{{PLURAL:$1|ново напьсаниѥ|нова напьсании|999=новꙑ напьсаниꙗ}}",
        "newmessagesdifflinkplural": "{{PLURAL:$1|послѣдьнꙗ мѣна|послѣдьни мѣни|999=послѣдьн҄ь мѣнъ}}",
-       "editsection": "иÑ\81пÑ\80ави",
-       "editold": "иÑ\81пÑ\80ави",
+       "editsection": "иÑ\81пÑ\80авлѥниѥ",
+       "editold": "иÑ\81пÑ\80авлѥниѥ",
        "viewsourceold": "страницѧ источьнъ обраꙁъ",
-       "editlink": "иÑ\81пÑ\80ави",
+       "editlink": "иÑ\81пÑ\80авлѥниѥ",
        "viewsourcelink": "страницѧ источьнъ обраꙁъ",
        "editsectionhint": "исправлѥниѥ чѧсти : $1",
        "toc": "каталогъ",
        "showtoc": "виждь",
        "hidetoc": "съкрꙑи",
+       "confirmable-yes": "да",
+       "confirmable-no": "нѣтъ",
        "viewdeleted": "$1 видєти хощєши ;",
-       "red-link-title": "$1 (си страницѧ нѣстъ)",
+       "restorelink": "{{PLURAL:$1|ѥдьна поничьжєна мѣна|$1 поничьжєноу мѣноу|$1 поничьжєнъ мѣнъ}}",
+       "feedlinks": "потокъ :",
+       "red-link-title": "$1 (сѥѩ страницѧ нѣстъ)",
        "nstab-main": "члѣнъ",
        "nstab-user": "польꙃєватєл҄ь",
        "nstab-media": "срѣдьства",
        "nstab-help": "страница помощи",
        "nstab-category": "катигорїꙗ",
        "mainpage-nstab": "главьна страница",
-       "nosuchspecialpage": "си нарочнꙑ страницѧ нѣстъ",
+       "nosuchspecialpage": "сѥѩ нарочнꙑ страницѧ нѣстъ",
        "error": "блаꙁна",
+       "databaseerror-error": "блаꙁна : $1",
        "internalerror": "вънѫтрѣнꙗ блаꙁна",
        "badtitle": "ꙁъло имѧ",
        "viewsource": "страницѧ источьнъ обраꙁъ",
        "viewsource-title": "вижьдь страницѧ ⁖ $1 ⁖ источьнъ обраꙁъ",
+       "exception-nologin": "тꙑ нє въшьлъ ѥси",
        "welcomeuser": "Добрѣ прити · $1!",
        "welcomecreation-msg": "твоѥ польꙃєватєльско мѣсто сътворєно ѥстъ ⁙\nнꙑнѣ иꙁмѣнити [[Special:Preferences|{{GRAMMAR:genitive|{{SITENAME}}}} строи]] можєши",
        "yourname": "твоѥ имѧ",
        "userlogin-yourname": "польꙃєватєлꙗ имѧ",
        "userlogin-yourname-ph": "твоѥ польꙃєватєлꙗ имѧ напьши",
+       "createacct-another-username-ph": "польꙃєватєлꙗ имѧ напьши",
        "yourpassword": "таино слово напиши",
        "userlogin-yourpassword": "таино слово",
        "userlogin-yourpassword-ph": "твоѥ таино слово напьши",
        "userlogout": "ис̾ходъ",
        "notloggedin": "тꙑ нє въшьлъ ѥси",
        "userlogin-noaccount": "мѣсто ти нѣстъ ли ?",
+       "userlogin-joinproject": "въ {{grammar:locative|{{SITENAME}}}} чѧсть прими",
        "nologin": "мѣсто ти нѣстъ ли ? $1",
        "nologinlink": "съꙁижди си мѣсто",
        "createaccount": "съꙁижди си мѣсто",
        "gotaccount": "мѣсто ти ѥстъ ли? $1",
        "gotaccountlink": "въниди",
        "userlogin-resetpassword-link": "таино слово ꙁабꙑлъ ли ;",
+       "userlogin-helplink2": "помощь въниждєниꙗ дѣлꙗ",
        "createaccountreason": "какъ съмꙑслъ :",
        "createacct-reason": "какъ съмꙑслъ",
+       "createacct-reason-ph": "чєсо дѣлꙗ ино польꙃєватєльско мѣсто сътворити хощєши ;",
        "createacct-submit": "съꙁижди си мѣсто",
+       "createacct-another-submit": "съꙁижди мѣсто",
        "createacct-benefit-heading": "{{SITENAME}} съꙁьдаѥтъ сѧ чьловѣкꙑ · ижє ꙗко тꙑ сѫтъ",
        "createacct-benefit-body1": "{{PLURAL:$1|мѣна|мѣнꙑ|мѣнъ}}",
        "createacct-benefit-body2": "{{PLURAL:$1|страница|страници|страницѧ}}",
        "loginerror": "въхода блаꙁна",
        "createacct-error": "мѣста сътворѥниꙗ блаꙁна",
        "loginsuccess": "'''нꙑнѣ тꙑ {{GENDER|въшьлъ|въшьла}} въ {{grammar:locative|{{SITENAME}}}} подь имьньмъ ⁖ $1 ⁖.'''",
-       "mailmypassword": "поÑ\81Ñ\8aли Ð½Ð¾Ð²Ð¾ Ñ\82аино Ñ\81лово",
+       "mailmypassword": "нова Ñ\82аина Ñ\81лова Ð¾Ñ\83Ñ\81Ñ\82авлѥниѥ",
        "accountcreated": "мѣсто сътворєно ѥстъ",
        "accountcreatedtext": "польꙃєватєльско мѣсто [[{{ns:User}}:$1|$1]] ([[{{ns:User talk}}:$1|бєсѣда]]) сътворєно бѣ",
        "loginlanguagelabel": "ѩꙁꙑкъ : $1",
        "botpasswords-label-cancel": "отъмѣтаниѥ",
        "resetpass-submit-loggedin": "таина словєсє иꙁмѣнѥниѥ",
        "resetpass-submit-cancel": "отъмѣтаниѥ",
+       "passwordreset": "нова таина слова оуставлѥниѥ",
        "passwordreset-username": "польꙃєватєлꙗ имѧ :",
        "changeemail-none": "(нѣстъ)",
        "link_sample": "съвѧꙁи имѧ",
        "summary": "опьсаниѥ :",
        "subject": "ѳєма :",
        "minoredit": "малаꙗ мѣна",
-       "watchthis": "си страницѧ блюдєниѥ",
+       "watchthis": "сѥѩ страницѧ блюдєниѥ",
        "savearticle": "съхранѥниѥ",
        "showpreview": "мѣнꙑ поꙁьрѣниѥ (бєꙁ съхранѥниꙗ)",
        "blockedtitle": "польꙃєватєл҄ь ꙁаграждєнъ ѥстъ",
        "loginreqlink": "въниди",
        "newarticle": "(новъ)",
-       "noarticletext": "нꙑнѣ с̑ьдє ничєсожє нє напьсано ѥстъ ⁙\n[[Special:Search/{{PAGENAME}}|си страницѧ имѧ искати]] дроугꙑ страницѧ ·\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} съвѧꙁанꙑ їсторїѩ видѣти] ·\nили [{{fullurl:{{FULLPAGENAME}}|action=edit}} ѭжє исправити]</span> можєши",
-       "noarticletext-nopermission": "нꙑнѣ с̑ьдє ничєсожє нє напьсано ѥстъ ⁙\n[[Special:Search/{{PAGENAME}}|си страницѧ имѧ искати]] дроугꙑ страницѧ или\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} съвѧꙁанꙑ їсторїѩ видѣти]</span> можєши ⁙ сътворити жє си страницѧ нє можєши",
-       "userpage-userdoesnotexist": "польꙃєватєльска мѣста ⁖ $1 ⁖ нꙑнѣ нѣстъ ⁙\nпрѣдъ сътворѥниѥмь или исправлѥниѥмь си страницѧ помꙑсли жє ащє исто тъ дѣиство ноуждьно ли",
+       "noarticletext": "нꙑнѣ с̑ьдє ничєсожє нє напьсано ѥстъ ⁙\n[[Special:Search/{{PAGENAME}}|сѥѩ страницѧ имѧ искати]] дроугꙑ страницѧ ·\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} съвѧꙁанꙑ їсторїѩ видѣти] ·\nили [{{fullurl:{{FULLPAGENAME}}|action=edit}} ѭжє исправити]</span> можєши",
+       "noarticletext-nopermission": "нꙑнѣ с̑ьдє ничєсожє нє напьсано ѥстъ ⁙\n[[Special:Search/{{PAGENAME}}|сѥѩ страницѧ имѧ искати]] дроугꙑ страницѧ или\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} съвѧꙁанꙑ їсторїѩ видѣти]</span> можєши ⁙ сътворити жє сѭ страницѫ нє можєши",
+       "userpage-userdoesnotexist": "польꙃєватєльска мѣста ⁖ $1 ⁖ нꙑнѣ нѣстъ ⁙\nпрѣдъ сътворѥниѥмь или исправлѥниѥмь сѥѩ страницѧ помꙑсли жє ащє исто тъ дѣиство ноуждьно ли",
        "userpage-userdoesnotexist-view": "польꙃєватєльско мѣсто ⁖ $1 ⁖ сътворєно нѣстъ",
        "clearyourcache": "'''НАРОЧИТО''': По съхранѥнии можєши обити своѥго съмотрила съхранъ да видѣлъ би мѣнꙑ\n* '''Mozilla ли Firefox ли Safari''' ли жьмꙑи ''Shift'' а мꙑшиѭ жьми ''Reload'' или жьми ''Ctrl-F5'' ꙗко жє ''Ctrl-R'' (⌘-R вън Apple Mac)\n* '''Google Chrome:''' ли жьмꙑи ''Ctrl-Shift-R'' (⌘-Shift-R въ Mac)\n* '''Internet Explorer''' ли жьмꙑи ''Ctrl'' а мꙑшиѭ жьми ''Refresh'' или жьми ''Ctrl-F5'' \n* '''Опєрꙑ''' польꙃєватєльмъ можєть бꙑти ноужда пльнѣ поничьжити ихъ съмотрила съхранъ въ ''Tools → Preferences'' ⁙",
        "updated": "(оновлѥно ѥстъ)",
        "creating": "сътворѥниѥ ⁖ $1 ⁖",
        "editingsection": "исправлѥниѥ ⁖ $1 ⁖ (чѧсть)",
        "editingcomment": "исправлѥниѥ ⁖ $1 ⁖ (нова чѧсть)",
+       "explainconflict": "нѣкъто сѭ страницѫ иꙁмѣнилъ въ врѣмѧ ѥгда тꙑ ѥѩжє исправлꙗти почѧашє ⁙\nврьхоу нꙑнѣщьн҄ь страницѧ обраꙁъ авлѥнъ ѥстъ ⁙\nниꙁоу жє твоꙗ мѣна авлѥна ѥстъ ⁙\nсъѥдинити твоѭ мѣноу съ новомь обраꙁомь страницѧ длъжєнъ ѥси ⁙\nащє жє ⁖ {{int:savearticle}} ⁖ жьмєши · <strong>тъкъмо</strong> напьсаниѥ ижє врьхоу ѥстъ съхранѥно бѫдєтъ",
        "yourtext": "твоѥ напьсаниѥ",
        "templatesused": "сѥѩ страницѧ {{PLURAL:$1|сь обраꙁьць польꙃоуѥтъ сѧ ѥстъ|с҄и обраꙁьца польꙃоуѭтъ сѧ ѥстє|с҄и обраꙁьци польꙃоуѭтъ сѧ сѫтъ}} :",
        "template-protected": "(ꙁабранєно ѥстъ)",
        "template-semiprotected": "(чѧстьно ꙁабранѥно)",
        "hiddencategories": "сꙗ страница въ {{PLURAL:$1|1 съкрꙑтѣи катигорїи|$1 съкрꙑтѣхъ катигорїѩ}} сѧ авлꙗѥтъ :",
-       "moveddeleted-notice": "сꙗ страница поничьжєна ѥстъ ⁙\nпоничьжєниꙗ и прѣимєнованиꙗ їстории си страницѧ нижѣ видѣти можєши",
+       "moveddeleted-notice": "сꙗ страница поничьжєна ѥстъ ⁙\nпоничьжєниꙗ и прѣимєнованиꙗ їстории сѥѩ страницѧ нижѣ видѣти можєши",
+       "postedit-confirmation-created": "страница сътворѥна ѥстъ",
        "postedit-confirmation-saved": "твоꙗ мѣна съхранѥна ѥстъ",
-       "viewpagelogs": "си страницѧ їсторїѩ",
+       "viewpagelogs": "сѥѩ страницѧ їсторїѩ",
        "cur": "нꙑ҃н",
        "last": "пс҃лд",
        "page_first": "прьва страница",
        "deletedhist": "поничьжєна їсторїꙗ",
        "revdelete-otherreason": "инъ или допльнитєл҄ьнъ съмꙑслъ :",
        "revdelete-reasonotherlist": "инъ съмꙑслъ",
+       "revdelete-edit-reasonlist": "поничьжєниꙗ съмꙑслъ исправлѥниѥ",
        "mergehistory-reason": "какъ съмꙑслъ :",
+       "history-title": "иꙁмѣнѥнии їсторїꙗ страницѧ ⁖ $1 ⁖",
        "editundo": "отъмѣтаниѥ",
        "searchresults": "исканиꙗ слѣдьствиѥ",
        "searchresults-title": "исканиꙗ ⁖ $1 ⁖ слѣдьствиѥ",
        "right-delete": "страницѧ поничьжєниѥ",
        "newuserlogpage": "новъ мѣстъ сътворѥниꙗ їсторїꙗ",
        "rightslog": "чинодатєльства їсторїꙗ",
-       "action-edit": "си страницѧ исправлєниѥ",
+       "action-edit": "сѥѩ страницѧ исправлѥниѥ",
        "nchanges": "$1 {{PLURAL:$1|мѣна|мѣнꙑ|мѣнъ}}",
        "enhancedrc-history": "їсторїꙗ",
        "recentchanges": "послѣдьнѩ мѣнꙑ",
        "recentchanges-label-newpage": "по сѥи мѣнꙑ нова страница сътворѥна ѥстъ",
        "recentchanges-label-minor": "малаꙗ мѣна",
        "recentchanges-label-bot": "сѭ мѣноу аѵтоматъ сътворилъ",
+       "recentchanges-label-plusminus": "страницѧ мѣра на сѥ баитъ число иꙁмѣнѥна ѥстъ",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (таждє ꙁьри [[Special:NewPages|новъ страницѧ каталогъ]])",
        "rclistfrom": "новъ мѣнъ каꙁаниѥ отъ $2 · $3",
-       "rcshowhideminor": "$1 малꙑ мѣнꙑ",
+       "rcshowhideminor": "$1 малъ мѣнъ",
        "rcshowhideminor-show": "каꙁаниѥ",
        "rcshowhideminor-hide": "съкрꙑтиѥ",
        "rcshowhidebots": "$1 аѵтоматъ",
        "rcshowhidebots-show": "каꙁаниѥ",
        "rcshowhidebots-hide": "съкрꙑтиѥ",
-       "rcshowhideliu": "$1 польꙃєватєлъ · ѩжє съꙁижьдє сѥ мѣсто · мѣн",
+       "rcshowhideliu": "$1 польꙃєватєлъ · ѩжє съꙁижьдє сѥ мѣсто · мѣнъ",
        "rcshowhideliu-hide": "съкрꙑтиѥ",
-       "rcshowhideanons": "$1 анѡнѷмьнъ польꙃєватєлъ мѣн",
+       "rcshowhideanons": "$1 анѡнѷмьнъ польꙃєватєлъ мѣнъ",
        "rcshowhideanons-show": "каꙁаниѥ",
        "rcshowhideanons-hide": "съкрꙑтиѥ",
-       "rcshowhidemine": "$1 моꙗ мѣнꙑ",
+       "rcshowhidemine": "$1 моѩ мѣнъ",
        "rcshowhidemine-show": "каꙁаниѥ",
        "rcshowhidemine-hide": "съкрꙑтиѥ",
        "rclinks": "$1 послѣдьн҄ь  мѣнъ · ѩжє $2 послѣдьни дьни створѥнꙑ сѫтъ · каꙁаниѥ<br />$3",
        "statistics-pages-desc": "вьсѩ страницѧ въкоупомь съ бѣсєдꙑ · прѣнаправлѥниꙗ и инꙑ",
        "statistics-files": "положєнꙑ дѣла",
        "statistics-users-active": "дѣꙗтєльнꙑ польꙃєватєлє",
-       "brokenredirects-edit": "иÑ\81пÑ\80ави",
+       "brokenredirects-edit": "иÑ\81пÑ\80авлѥниѥ",
        "brokenredirects-delete": "поничьжєниѥ",
        "nbytes": "$1 {{PLURAL:$1|баитъ|баита|баитъ}}",
        "ncategories": "$1 {{PLURAL:$1|катигорїꙗ|катигорїи|катигорїѩ}}",
        "nlinks": "$1 {{PLURAL:$1|съвѧꙁь|съвѧꙁи|съвѧꙁии}}",
        "nmembers": "$1 {{PLURAL:$1|члѣнъ|члѣна|члѣни|члѣнъ}}",
+       "uncategorizedpages": "бєскатигорїинꙑ страницѧ",
+       "uncategorizedcategories": "бєскатигорїинꙑ катигорїѩ",
+       "uncategorizedimages": "бєскатигорїинꙑ дѣла",
+       "uncategorizedtemplates": "бєскатигорїинꙑ обраꙁьци",
+       "wantedcategories": "ноуждьни катигорїѩ",
+       "wantedpages": "ноуждьни страницѧ",
+       "wantedfiles": "ноуждьни дѣла",
+       "wantedtemplates": "ноуждьни обраꙁьци",
        "shortpages": "кратъкꙑ страницѧ",
+       "longpages": "дльгꙑ страницѧ",
+       "protectedpages": "ꙁабранѥнꙑ страницѧ",
        "protectedpages-reason": "какъ съмꙑслъ",
+       "protectedtitles": "ꙁабранѥнꙑ имєна",
        "listusers": "польꙃєватєлъ каталогъ",
        "usereditcount": "$1 {{PLURAL:$1|мѣна|мѣнꙑ|мѣнъ}}",
        "usercreated": "{{GENDER:$3|сътворилъ|сътворила}} мѣсто $1 въ $2",
        "newpages-username": "польꙃєватєлꙗ имѧ :",
        "ancientpages": "давьни страницѧ",
        "move": "прѣимєнованиѥ",
-       "movethispage": "си страницѧ прѣимєнованиѥ",
+       "movethispage": "сѥѩ страницѧ прѣимєнованиѥ",
        "pager-newer-n": "{{PLURAL:$1|нова 1|новꙑ $1|новъ $1}}",
        "pager-older-n": "{{PLURAL:$1|давьнꙗ 1|давьни $1|давьн҄ь $1}}",
+       "booksources": "кънигъ кладѧꙃи",
+       "booksources-search-legend": "кънигъ кладѧꙃь исканиѥ",
        "booksources-search": "исканиѥ",
        "specialloguserlabel": "испльнитєл҄ь :",
        "speciallogtitlelabel": "страницѧ или польꙃєватєлꙗ имѧ :",
        "linksearch-ns": "имєнъ просторъ :",
        "linksearch-ok": "ищи",
        "listusers-submit": "виждь",
+       "listusers-blocked": "({{GENDER:$1|ꙁаграждєнъ|ꙁаграждєна}} ѥстъ)",
        "listgrouprights-members": "(польꙃєватєлъ каталогъ)",
        "emailuser": "посъли єпїстолѫ",
        "emailusername": "польꙃєватєлꙗ имѧ :",
        "addedwatchtext": "страница ⁖ [[:$1]] ⁖ нꙑнѣ подъ твоимь [[Special:Watchlist|блюдєниѥмь]] ѥстъ ⁙\nвсꙗ ѥѩ и ѥѩжє бєсѣдꙑ страницѧ мѣнꙑ твоꙗ блюдєнии каталоꙃѣ покаꙁанꙑ бѫдѫтъ",
        "removedwatchtext": "страница ⁖ [[:$1]] ⁖ нꙑнѣ твоѥго [[Special:Watchlist|блюдєниꙗ]] иꙁнєсєна ѥстъ",
        "watch": "блюдєниѥ",
-       "watchthispage": "си страницѧ блюдєниѥ",
+       "watchthispage": "сѥѩ страницѧ блюдєниѥ",
        "unwatch": "остави блюдєниѥ",
        "watchlist-options": "блюдєниѩ строи",
        "watching": "блюдєниѥ ...",
        "prot_1movedto2": "⁖ [[$1]] ⁖ нарєчєнъ ⁖ [[$2]] ⁖ ѥстъ",
        "protectcomment": "какъ съмꙑслъ :",
        "protect-cascadeon": "сꙗ страница отъ исправлєниꙗ ꙁабранѥна ѥстъ бо въ съставѣ {{PLURAL:$1|страницѧ ижє съвѧꙁьно ꙁабранѥниѥ иматъ|страницоу ижє съвѧꙁьно ꙁабранѥниѥ иматє|страниць ижє съвѧꙁьно ꙁабранѥниѥ имѫтъ}} ⁙\nсѥѩ страницѧ ꙁабранѥниꙗ обраꙁа иꙁмѣнѥниѥ ничєсо жє въ съвѧꙁьнѣ ꙁабранѥнии иꙁмѣнити нє можєтъ",
-       "protect-level-sysop": "толико съмотритєлє",
+       "protect-level-sysop": "тъкъмо съмотритєлє",
        "protect-othertime": "ино врѣмѧ :",
        "protect-othertime-op": "ино врѣмѧ",
        "protect-otherreason-op": "инъ съмꙑслъ",
        "protect-expiry-options": "1 часъ:1 hour,1 дьнь:1 day,1 сєдмица:1 week,2 сєдмици:2 weeks,1 мѣсѧць:1 month,3 мѣсѧць:3 months,6 мѣсѧць:6 months,1 лѣто:1 year,вѣчьно:infinite",
        "pagesize": "(баитъ)",
-       "restriction-edit": "иÑ\81пÑ\80ави",
+       "restriction-edit": "иÑ\81пÑ\80авлѥниѥ",
        "restriction-move": "прѣимєнованиѥ",
        "restriction-upload": "положєниѥ",
        "undeletecomment": "какъ съмꙑслъ :",
        "ipbreason": "какъ съмꙑслъ :",
        "ipbother": "ино врѣмѧ :",
        "ipboptions": "2 часа:2 hours,1 дьнь:1 day,3 дьни:3 days,1 сєдмица:1 week,2 сєдмици:2 weeks,1 мѣсѧць:1 month,3 мѣсѧць:3 months,6 мѣсѧць:6 months,1 лѣто:1 year,вѣчьно:infinite",
+       "blocklist": "ꙁаграждєнꙑ польꙃєватєлє",
        "ipblocklist": "ꙁаграждєнꙑ польꙃєватєлє",
        "blocklist-reason": "какъ съмꙑслъ",
        "ipblocklist-submit": "исканиѥ",
        "blocklogentry": "ꙁаградилъ [[$1]] на врѣмѧ $2 $3",
        "block-log-flags-anononly": "тъкъмо анѡнѷмьнꙑ польꙃєватєлє",
        "block-log-flags-nocreate": "сътворѥниѥ мѣстъ ꙁабранєно ѥстъ",
+       "ipb_already_blocked": "⁖ $1 ⁖ ю ꙁаграждєнъ ѥстъ",
        "move-page": "прѣимєнованиѥ ⁖ $1 ⁖",
        "move-page-legend": "страницѧ прѣимєнованиѥ",
        "newtitle": "ново имѧ :",
-       "move-watch": "си страницѧ блюдєниѥ",
+       "move-watch": "сѥѩ страницѧ блюдєниѥ",
        "movepagebtn": "прѣимєнованиѥ",
        "pagemovedsub": "прѣимєнованиѥ сътворѥно ѥстъ",
        "movepage-moved": "'''⁖ $1 ⁖ нарєчєнъ ⁖ $2⁖ ѥстъ'''",
        "movepage-moved-redirect": "прѣнаправлѥниѥ сътворѥно бѣ",
-       "movetalk": "си страницѧ бєсѣдꙑ прѣимєнованиѥ",
+       "movetalk": "сѥѩ страницѧ бєсѣдꙑ прѣимєнованиѥ",
        "movelogpage": "прѣимєнованиꙗ їсторїꙗ",
        "movereason": "какъ съмꙑслъ :",
        "move-leave-redirect": "прѣнаправлѥниꙗ сътворѥниѥ",
        "tooltip-pt-watchlist": "страницѧ ижє ихъжє иꙁмѣнѥниꙗ подъ твоимь блюдєниѥмь сѫтъ",
        "tooltip-pt-mycontris": "{{GENDER:|твоѩ}} добродѣꙗнии каталогъ",
        "tooltip-pt-logout": "ис̾ходъ",
-       "tooltip-ca-talk": "си страницѧ бєсѣда",
-       "tooltip-ca-edit": "си страницѧ исправлѥниѥ",
+       "tooltip-ca-talk": "сѥѩ страницѧ бєсѣда",
+       "tooltip-ca-edit": "сѥѩ страницѧ исправлѥниѥ",
        "tooltip-ca-viewsource": "си страница ꙁабранєна ѥстъ ⁙\nѥѩ источьнъ обраꙁъ видєти можєши",
-       "tooltip-ca-protect": "си страницѧ ꙁабранєниѥ",
-       "tooltip-ca-delete": "си страницѧ поничьжєниѥ",
-       "tooltip-ca-move": "си страницѧ прѣимєнованиѥ",
-       "tooltip-ca-watch": "си страницѧ блюдєниѥ",
+       "tooltip-ca-protect": "сѥѩ страницѧ ꙁабранєниѥ",
+       "tooltip-ca-delete": "сѥѩ страницѧ поничьжєниѥ",
+       "tooltip-ca-move": "сѥѩ страницѧ прѣимєнованиѥ",
+       "tooltip-ca-watch": "сѥѩ страницѧ блюдєниѥ",
        "tooltip-search": "ищи {{{grammar:genitive|{{SITENAME}}}}} страницѧ",
+       "tooltip-search-go": "прѣиди къ страницѧ съ симь имєньмь ащє жє та страница ѥстъ",
        "tooltip-search-fulltext": "исканиѥ страницѧ ижє сѥ напьсаниѥ дрьжатъ",
        "tooltip-p-logo": "главьна страница",
        "tooltip-n-mainpage": "виждь главьноу страницѫ",
        "tooltip-n-mainpage-description": "виждь главьноу страницѫ",
        "tooltip-n-recentchanges": "послѣдьн҄ь мѣнъ каталогъ",
+       "tooltip-t-whatlinkshere": "страницѧ ижє съвѧꙁи дос҄ьдє имѫтъ",
        "tooltip-t-contributions": "{{GENDER:$1|польꙃєватєлꙗ|польꙃєватєлицѧ}} добродѣꙗнии каталогъ",
        "tooltip-t-upload": "положєниѥ дѣлъ",
        "tooltip-t-specialpages": "вьсѣѩ нарочьнъ страницѧ каталогъ",
        "tooltip-ca-nstab-user": "виждь польꙃєватєлꙗ страницѫ",
        "tooltip-ca-nstab-special": "сѥ нарочьна страница ѥстъ · ѥѩжє иꙁмѣнꙗти нє можєши",
        "tooltip-ca-nstab-image": "виждь дѣла страницѫ",
+       "tooltip-ca-nstab-template": "обраꙁьца поꙁьрѣниѥ",
        "tooltip-ca-nstab-category": "виждь катигорїѩ страницѫ",
        "tooltip-minoredit": "оꙁначи ꙗко малоу мѣноу",
        "tooltip-save": "твоѩ мѣнъ съхранѥниѥ",
-       "tooltip-watch": "си страницѧ блюдєниѥ",
+       "tooltip-watch": "сѥѩ страницѧ блюдєниѥ",
        "tooltip-summary": "кратъко опьсаниѥ напьши",
        "pageinfo-header-edits": "мѣнъ їсторїꙗ",
        "pageinfo-header-restrictions": "страницѧ ꙁабранѥниѥ",
        "fileduplicatesearch-filename": "дѣла имѧ :",
        "fileduplicatesearch-submit": "ищи",
        "specialpages": "нарочьнꙑ страницѧ",
+       "specialpages-group-other": "инꙑ нарочьнꙑ страницѧ",
+       "specialpages-group-login": "въниждєниѥ и сътворѥниѥ мѣстъ",
        "tag-filter": "[[Special:Tags|мѣтъць]] сито :",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|мѣтъка|мѣтъцѣ|мѣтъци}}]]: $2)",
        "tags-active-yes": "да",
        "tags-active-no": "нѣтъ",
-       "tags-edit": "иÑ\81пÑ\80ави",
+       "tags-edit": "иÑ\81пÑ\80авлѥниѥ",
        "tags-hitcount": "$1 {{PLURAL:$1|мѣна|мѣноу|мѣнъ}}",
        "tags-create-reason": "какъ съмꙑслъ :",
        "tags-create-submit": "сътворѥниѥ",
        "htmlform-yes": "да",
        "logentry-delete-delete": "$1 {{GENDER:$2|поничьжилъ|поничьжила}} страницѫ ⁖ $3 ⁖",
        "logentry-block-block": "$1 {{GENDER:$2|ꙁаградилъ|ꙁаградила}} {{GENDER:$4|$3}} на врѣмѧ $5 $6",
+       "logentry-suppress-block": "$1 {{GENDER:$2|ꙁаграждєнъ|ꙁаграждєна}} ѥстъ {{GENDER:$4|$3}} врѣмєньмь $5 $6",
        "logentry-move-move": "$1 {{GENDER:$2|нарєчє}} страницѫ ⁖ $3 ⁖ имєньмь ⁖ $4 ⁖",
        "logentry-move-move-noredirect": "$1 {{GENDER:$2|нарєчє}} страницѫ ⁖ $3 ⁖ имєньмь ⁖ $4 ⁖ бєꙁ прѣнаправлєниꙗ сътворѥниꙗ",
        "logentry-move-move_redir": "$1 {{GENDER:$2|нарєчє}} страницѧ ⁖ $3 ⁖ имєньмь ⁖ $4 ⁖ врьхоу прѣнаправлѥниꙗ",
        "searchsuggest-search": "исканиѥ",
        "searchsuggest-containing": "сѥ дрьжащи···",
        "api-error-unknownerror": "нєвѣдома блаꙁна : ⁖ $1 ⁖",
+       "duration-seconds": "$1 {{PLURAL:$1|дєѵтєролєпто|дєѵтєролєпта|дєѵтєролєптъ}}",
+       "duration-minutes": "$1 {{PLURAL:$1|лєпто|лєпта|лєптъ}}",
+       "duration-hours": "$1 {{PLURAL:$1|чѧсъ|чѧса|чѧсъ}}",
+       "duration-days": "$1 {{PLURAL:$1|дьнь|дьнꙗ|дьнь}}",
+       "duration-weeks": "$1 {{PLURAL:$1|сєдмица|сєдмици|сєдмицѧ}}",
+       "duration-years": "$1 {{PLURAL:$1|лѣто|лѣта|лѣтъ}}",
+       "duration-decades": "$1 {{PLURAL:$1|дєсѧтилѣтиѥ|дєсѧтилѣтиꙗ|дєсѧтилѣтии}}",
+       "duration-centuries": "$1 {{PLURAL:$1|вѣкъ|вѣка|вѣкъ}}",
+       "duration-millennia": "$1 {{PLURAL:$1|тꙑсѫщєлѣтиѥ|тꙑсѫщєлѣтиꙗ|тꙑсѫщєлѣтии}}",
+       "limitreport-cputime-value": "$1 {{PLURAL:$1|дєѵтєролєпто|дєѵтєролєпта|дєѵтєролєптъ}}",
+       "limitreport-walltime-value": "$1 {{PLURAL:$1|дєѵтєролєпто|дєѵтєролєпта|дєѵтєролєптъ}}",
+       "pagelanguage": "страницѧ ѩꙁꙑка иꙁмѣнѥниѥ",
+       "pagelang-name": "страница",
+       "pagelang-language": "ѩꙁꙑкъ",
+       "mediastatistics-nbytes": "{{PLURAL:$1|$1 баитъ|$1 баита|$1 баитъ}} ($2 · $3%)",
+       "mediastatistics-header-unknown": "нєвѣдомꙑ",
+       "mediastatistics-header-total": "вьсѥ дѣла",
+       "json-error-syntax": "сѷнтаѯьна блаꙁна",
        "special-characters-group-latin": "латиньска аꙁъбоукꙑ",
        "special-characters-group-latinextended": "латиньскꙑ аꙁъбоукьвє доложєниѥ",
        "special-characters-group-ipa": "М҃ФА",
        "special-characters-group-greek": "грьчьска аꙁъбоукꙑ",
        "special-characters-group-cyrillic": "климєнтовица / гражданьска аꙁъбоукꙑ",
        "special-characters-group-arabic": "аравьска аꙁъбоукꙑ",
+       "special-characters-group-arabicextended": "аравьскꙑ аꙁъбоукъвє доложєниѥ",
+       "special-characters-group-persian": "пєрсьска аꙁъбоукꙑ",
        "special-characters-group-hebrew": "єврєиска аꙁъбоукꙑ",
        "special-characters-group-bangla": "бангальска аꙁъбоукꙑ",
+       "special-characters-group-tamil": "тамильска аꙁъбоукꙑ",
        "special-characters-group-telugu": "тєлоужьска аꙁъбоукꙑ",
-       "special-characters-group-sinhala": "синхальска аꙁъбоукꙑ"
+       "special-characters-group-sinhala": "синхальска аꙁъбоукꙑ",
+       "special-characters-group-gujarati": "гоуджаратьска аꙁъбоукꙑ",
+       "special-characters-group-devanagari": "дєванагари",
+       "special-characters-group-thai": "таиска аꙁъбоукꙑ",
+       "special-characters-group-lao": "лаосьска аꙁъбоукꙑ",
+       "special-characters-group-khmer": "къмєрьска аꙁъбоукꙑ",
+       "mw-widgets-titleinput-description-new-page": "страницѧ ю нѣстъ",
+       "mw-widgets-titleinput-description-redirect": "прѣнаправлѥниѥ къ ⁖ $1 ⁖"
 }
index 4f3b094..f88c082 100644 (file)
        "mostcategories": "Чи нумай категорине кĕртнĕ страницăсем",
        "mostimages": "Чи анлă усă куракан ӳкерчĕксем",
        "mostrevisions": "Чи нумай тӳрлетнĕ страницăсем",
-       "prefixindex": "СÄ\83маÑ\85 Ð¿Ñ\83çламÄ\83Ñ\88Ä\95Ñ\81ен ÐºÄ\83Ñ\82аÑ\80Ñ\82мÄ\83Ñ\88Ä\95",
+       "prefixindex": "СÄ\83маÑ\85 Ð¿Ñ\83çламÄ\83Ñ\88Ä\95Ñ\81ен ÐºÄ\83Ñ\82аÑ\80Ñ\82аканни",
        "prefixindex-submit": "Кăтарт",
        "shortpages": "Кĕске статьясем",
        "longpages": "Вăрăм страницăсем",
index ade3f30..17aa7bd 100644 (file)
        "passwordreset-emailtext-ip": "Nogen (sandsynligvis dig, fra IP-adressen $1) har anmodet om at få nulstillet din adgangskode til {{SITENAME}} ($4). {{PLURAL:$3|Den følgende brugerkonto er associeret|De følgende brugerkonti er associerede}} med denne e-mailadresse:\n\n$2\n\n{{PLURAL:$3|Denne midlertidige adgangskode|Disse midlertidige adgangskoder}} vil udløbe om {{PLURAL:$5|en dag|$5 dage}}.\nDu bør logge på og vælge en ny adgangskode nu. Hvis en anden end dig har lavet denne anmodning, eller hvis du er kommet i tanke om din oprindelig adgangskode og ikke længere ønsker at ændre den, kan du ignorere denne meddelelse og fortsætte med at bruge din gamle adgangskode.",
        "passwordreset-emailtext-user": "Brugeren $1 på {{SITENAME}} har anmodet om at få nulstillet din adgangskode til {{SITENAME}} ($4). {{PLURAL:$3|Den følgende brugerkonto er associeret|De følgende brugerkonti er associerede}} med denne e-mailadresse:\n\n$2\n\n{{PLURAL:$3|Denne midlertidige adgangskode|Disse midlertidige adgangskoder}} vil udløbe om {{PLURAL:$5|en dag|$5 dage}}.\nDu bør logge på og vælge en ny adgangskode nu. Hvis en anden end dig har lavet denne anmodning, eller hvis du er kommet i tanke om din oprindelig adgangskode og ikke længere ønsker at ændre den, kan du ignorere denne meddelelse og fortsætte med at bruge din gamle adgangskode.",
        "passwordreset-emailelement": "Brugernavn: \n$1\n\nMidlertidig adgangskode: \n$2",
-       "passwordreset-emailsentemail": "Hvis dettte er en registreret e-mail-adresse til din konto, så vil en e-mail om nulstilling af adgangskoden blive sendt.",
+       "passwordreset-emailsentemail": "Hvis denne e-mailadresse er knyttet til din konto, så vil en e-mail om nulstilling af adgangskoden blive sendt.",
+       "passwordreset-emailsentusername": "Hvis der er en e-mailadresse forbundet med dette brugernavn, så vil en e-mail om nulstilling af adgangskoden blive sendt.",
        "passwordreset-emailsent-capture": "En e-mail om nulstilling af adgangskode, som vist nedenfor, er blevet sendt.",
        "passwordreset-emailerror-capture": "En mail om nulstilling af adgangskode, som vist nedenfor, blev genereret, men det lykkedes ikke at sende den til {{GENDER:$2|bruger}}: $1",
        "changeemail": "Ændr eller fjern e-mailadresse",
        "rcshowhidemine": "$1 egne bidrag",
        "rcshowhidemine-show": "Vis",
        "rcshowhidemine-hide": "Skjul",
+       "rcshowhidecategorization": "$1 kategorisering af sider",
        "rcshowhidecategorization-show": "Vis",
        "rcshowhidecategorization-hide": "Skjul",
        "rclinks": "Vis seneste $1 ændringer i de sidste $2 dage<br />$3",
        "recentchangeslinked-summary": "Dette er en liste over de seneste ændringer af sider, der linkes til fra en bestemt side (eller medlemmer af en bestemt kategori).\nSider på [[Special:Watchlist|din overvågningsliste]] er vist med '''fed''' skrift.",
        "recentchangeslinked-page": "Sidenavn:",
        "recentchangeslinked-to": "Vis ændringer i sider der henviser til den angivne side i stedet",
+       "recentchanges-page-added-to-category": "[[:$1]] tilføjet til kategori",
+       "recentchanges-page-added-to-category-bundled": "[[:$1]] og {{PLURAL:$2|én side|$2 sider}} tilføjet til kategori",
        "upload": "Læg en fil op",
        "uploadbtn": "Læg en fil op",
        "reuploaddesc": "Tilbage til formularen til at lægge filer op.",
        "querypage-disabled": "Denne specialside er deaktiveret af hensyn til ydeevnen.",
        "apihelp": "API-hjælp",
        "apihelp-no-such-module": "Modul \"$1\" ikke fundet.",
+       "apisandbox": "API-sandkassen",
+       "apisandbox-api-disabled": "API er deaktiveret på dette websted.",
+       "apisandbox-intro": "Brug denne side til at eksperimentere med '''MediaWiki web service API'''.\nVi henviser til [//www.mediawiki.org/wiki/API:Main_page dokumentationen af API] for yderligere oplysninger om brug af API.  Eksempel: [//www.mediawiki.org/wiki/API#A_simple_example få indholdet af en forside]. Vælg en handling at se flere eksempler.\n\nBemærk, at selv om dette er en sandkasse, vil handlinger du udfører på denne side redigere wikien.",
+       "apisandbox-submit": "Lav forespørgsel",
+       "apisandbox-reset": "Ryd",
+       "apisandbox-examples": "Eksempel",
+       "apisandbox-results": "Resultat",
+       "apisandbox-request-url-label": "Forespurgt URL:",
+       "apisandbox-request-time": "Forespørgselstid: $1",
        "booksources": "Bogkilder",
        "booksources-search-legend": "Søgning efter bøger",
        "booksources-search": "Søg",
        "wlshowhideanons": "anonyme brugere",
        "wlshowhidepatr": "patruljerede redigeringer",
        "wlshowhidemine": "mine redigeringer",
+       "wlshowhidecategorization": "kategorisering af sider",
        "watchlist-options": "Indstillinger for overvågningslisten",
        "watching": "Tilføjer overvågning …",
        "unwatching": "Fjerner overvågning …",
index aaa9eed..1f669ee 100644 (file)
        "search": "Suche",
        "searchbutton": "Suchen",
        "go": "Ausführen",
-       "searcharticle": "Suchen",
+       "searcharticle": "Seite",
        "history": "Versionen",
        "history_short": "Versionsgeschichte",
        "updatedmarker": "Änderung seit deinem letzten Besuch",
        "changepassword-success": "Dein Passwort wurde erfolgreich geändert!",
        "changepassword-throttled": "Du hast kürzlich zu viele Anmeldeversuche unternommen.\nBitte warte $1, bevor du es erneut versuchst.",
        "botpasswords": "Botpasswörter",
-       "botpasswords-summary": "<em>Botpasswörter</em> erlauben Zugriff auf ein Benutzerkonto über die API, ohne die Hauptanmeldeinformationen des Benutzerkontos zu verwenden. Die verfügbaren Benutzerrechte bei der Anmeldung mit einem Botpasswort können beschränkt sein.\n\nWenn du nicht weist, warum du dies tun möchtest, solltest du dies wahrscheinlich nicht tun. Niemand soll dich jemals bitten, ein Passwort zu erzeugen und es an ihn zu übergeben.",
+       "botpasswords-summary": "<em>Botpasswörter</em> erlauben Zugriff auf ein Benutzerkonto über die API, ohne die Hauptanmeldeinformationen des Benutzerkontos zu verwenden. Die verfügbaren Benutzerrechte bei der Anmeldung mit einem Botpasswort können beschränkt sein.\n\nWenn du nicht weist, warum du ein Passwort erstellen sollst, tu es nicht. Du solltest ein Passwort nicht für einen anderen erzeugen und es an ihn übergeben.",
        "botpasswords-disabled": "Botpasswörter sind deaktiviert.",
        "botpasswords-no-central-id": "Um Botpasswörter zu verwenden, musst du bei einem zentralisierten Benutzerkonto angemeldet sein.",
        "botpasswords-existing": "Vorhandene Botpasswörter",
        "previewnote": "'''Dies ist nur eine Vorschau.'''\nDie Seite wurde noch nicht gespeichert!",
        "continue-editing": "Zum Bearbeitungsfeld gehen",
        "previewconflict": "Diese Vorschau gibt den Inhalt des oberen Textfeldes wieder. So wird die Seite aussehen, wenn du jetzt speicherst.",
-       "session_fail_preview": "'''Deine Bearbeitung konnte nicht gespeichert werden, da Sitzungsdaten verloren gegangen sind.\nBitte versuche es erneut, indem du unter der folgenden Textvorschau nochmals auf „Seite speichern“ klickst.\nSollte das Problem bestehen bleiben, [[Special:UserLogout|melde dich ab]] und danach wieder an.'''",
-       "session_fail_preview_html": "'''Deine Bearbeitung konnte nicht gespeichert werden, da Sitzungsdaten verloren gegangen sind.'''\n\n''Da in {{SITENAME}} das Speichern von reinem HTML aktiviert ist, wurde die Vorschau ausgeblendet, um JavaScript-Attacken vorzubeugen.''\n\n'''Bitte versuche es erneut, indem du unter der folgenden Textvorschau nochmals auf „Seite speichern“ klickst.\nSollte das Problem bestehen bleiben, [[Special:UserLogout|melde dich ab]] und danach wieder an.'''",
+       "session_fail_preview": "Entschuldigung! Wir konnten deine Bearbeitung nicht verarbeiten, da Sitzungsdaten verloren gegangen sind.\n\nDu wurdest eventuell abgemeldet. <strong>Bitte verifiziere, dass du noch angemeldet bist und versuche es erneut</strong>.\nFalls dies nicht funktioniert, versuche dich [[Special:UserLogout|abzumelden]] und anschließend wieder anzumelden und überprüfe, ob dein Browser Cookies von dieser Website akzeptiert.",
+       "session_fail_preview_html": "Deine Bearbeitung konnte nicht gespeichert werden, da Sitzungsdaten verloren gegangen sind.\n\n<em>Da in {{SITENAME}} das Speichern von reinem HTML aktiviert ist, wurde die Vorschau ausgeblendet, um JavaScript-Attacken vorzubeugen.</em>\n\n<strong>Bitte versuche es erneut, indem du unter der folgenden Textvorschau nochmals auf „Seite speichern“ klickst.</strong>\nSollte das Problem bestehen bleiben, [[Special:UserLogout|melde dich ab]] und danach wieder an. Überprüfe, ob dein Browser Cookies von dieser Website akzeptiert.",
        "token_suffix_mismatch": "'''Deine Bearbeitung wurde zurückgewiesen, da dein Browser Zeichen im Bearbeiten-Token verstümmelt hat.\nEine Speicherung kann den Seiteninhalt zerstören. Dies geschieht bisweilen durch die Benutzung eines anonymen Proxy-Dienstes, der fehlerhaft arbeitet.'''",
        "edit_form_incomplete": "'''Der Inhalt des Bearbeitungsformulars hat den Server nicht vollständig erreicht. Bitte prüfe deine Bearbeitungen auf Vollständigkeit und versuche es erneut.'''",
        "editing": "Bearbeiten von „$1“",
        "mergehistory-empty": "Es können keine Versionen vereinigt werden.",
        "mergehistory-done": "{{PLURAL:$3|Eine Version wurde|$3 Versionen wurden}} von „$1“ nach „[[:$2]]“ vereinigt.",
        "mergehistory-fail": "Versionsvereinigung nicht möglich, bitte prüfe die Seite und die Zeitangaben.",
+       "mergehistory-fail-bad-timestamp": "Ungültiger Zeitstempel.",
+       "mergehistory-fail-invalid-source": "Ungültige Quellseite.",
+       "mergehistory-fail-invalid-dest": "Ungültige Zielseite.",
+       "mergehistory-fail-no-change": "Keine Versionen zusammengeführt. Kontrolliere bitte die Seiten- und Zeitparameter erneut.",
+       "mergehistory-fail-permission": "Keine ausreichenden Berechtigungen, um die Versionsgeschichte zusammenzuführen.",
+       "mergehistory-fail-self-merge": "Quell- und Zielseiten sind identisch.",
+       "mergehistory-fail-timestamps-overlap": "Die Quellversionen überlappen sich oder kommen nach den Ziel-Versionen.",
        "mergehistory-fail-toobig": "Die Versionsgeschichtenzusammenführung konnte nicht ausgeführt werden, da sonst mehr als {{PLURAL:$1|eine Version|$1 Versionen}} verschoben werden {{PLURAL:$1|würde|würden}}.",
        "mergehistory-no-source": "Ursprungsseite „$1“ ist nicht vorhanden.",
        "mergehistory-no-destination": "Zielseite „$1“ ist nicht vorhanden.",
        "upload-dialog-button-done": "Schließen",
        "upload-dialog-button-save": "Speichern",
        "upload-dialog-button-upload": "Hochladen",
-       "upload-form-label-select-file": "Datei auswählen",
        "upload-form-label-infoform-title": "Einzelheiten",
        "upload-form-label-infoform-name": "Name",
        "upload-form-label-infoform-name-tooltip": "Ein eindeutiger erklärender Titel für die Datei, die als Dateiname angeboten wird. Du musst reine Sprache mit Leerzeichen verwenden. Nicht die Dateierweiterung einschließen.",
        "foreign-structured-upload-form-label-own-work-message-shared": "Ich bestätige, dass ich das Urheberrecht für diese Datei besitze und stimme unwiderruflich der Veröffentlichung dieser Datei auf Wikimedia Commons unter der Lizenz [https://creativecommons.org/licenses/by-sa/4.0/deed.de „Creative Commons Namensnennung – Weitergabe unter gleichen Bedingungen 4.0 International“] sowie den [https://wikimediafoundation.org/wiki/Terms_of_Use/de Nutzungsbedingungen] zu.",
        "foreign-structured-upload-form-label-not-own-work-message-shared": "Falls du nicht das Urheberrecht für diese Datei besitzt oder du diese Datei unter einer anderen Lizenz veröffentlichen möchtest, ziehe [https://commons.wikimedia.org/wiki/Special:UploadWizard den Hochladeassistenten auf Wikimedia Commons] in Erwägung.",
        "foreign-structured-upload-form-label-not-own-work-local-shared": "Du kannst auch [[Special:Upload|die Hochladeseite auf {{SITENAME}}]] ausprobieren, falls die Website das Hochladen dieser Datei unter ihren Richtlinien erlaubt.",
-       "foreign-structured-upload-form-2-label-intro": "Vielen Dank für das Spenden eines Bildes zur Verwendung auf {{SITENAME}}. Du solltest nur fortfahren, wenn es mehrere Bedingungen erfüllt:",
-       "foreign-structured-upload-form-2-label-ownwork": "Es muss vollständig <strong>deine eigene Schöpfung</strong> sein, nicht nur aus dem Internet genommen",
-       "foreign-structured-upload-form-2-label-noderiv": "Es darf <strong>kein Werk eines anderen</strong> enthalten oder von diesem inspiriert sein",
-       "foreign-structured-upload-form-2-label-useful": "Es soll <strong>lehrreich und nützlich</strong> für andere sein",
-       "foreign-structured-upload-form-2-label-ccbysa": "Es muss <strong>in Ordnung sein, um es für immer</strong> im Internet unter der Lizenz [https://creativecommons.org/licenses/by-sa/4.0/deed.de „Creative Commons – Namensnennung – Weitergabe unter gleichen Bedingungen 4.0“] zu veröffentlichen",
-       "foreign-structured-upload-form-2-label-alternative": "Falls nicht alle der obigen Antworten wahr sind, kannst du diese Datei mithilfe des [https://commons.wikimedia.org/wiki/Special:UploadWizard Hochladeassistenten auf Commons] hochladen, solange sie unter einer freien Lizenz verfügbar ist.",
-       "foreign-structured-upload-form-2-label-termsofuse": "Durch das Hochladen der Datei bestätigst du, dass du das Urheberrecht an dieser Datei besitzt und stimmst der unwiderruflichen Veröffentlichung der Datei auf Wikimedia Commons unter der Lizenz „Creative Commons – Namensnennung – Weitergabe unter gleichen Bedingungen 4.0“ und den [https://wikimediafoundation.org/wiki/Terms_of_Use/de Nutzungsbedingungen] zu.",
-       "foreign-structured-upload-form-3-label-question-website": "Hast du dieses Bild von einer Website heruntergeladen oder es auf einer Bildersuche gefunden?",
-       "foreign-structured-upload-form-3-label-question-ownwork": "Hast du dieses Bild (das Foto, die Zeichnung etc.) selbst erstellt?",
-       "foreign-structured-upload-form-3-label-question-noderiv": "Enthält es oder wurde es inspiriert von einem Werk eines anderen, wie ein Logo?",
-       "foreign-structured-upload-form-3-label-yes": "Ja",
-       "foreign-structured-upload-form-3-label-no": "Nein",
-       "foreign-structured-upload-form-3-label-alternative": "Leider unterstützt dieses Werkzeug in diesem Fall nicht das Hochladen dieser Datei. Du kannst sie mithilfe des [https://commons.wikimedia.org/wiki/Special:UploadWizard Hochladeassistenten auf Commons] hochladen, solange sie unter einer freien Lizenz verfügbar ist.",
-       "foreign-structured-upload-form-4-label-good": "Mit diesem Werkzeug kannst du lehrreiche Grafiken hochladen, die du erstellt hast und Fotos, die du aufgenommen hast, die keine Werke von anderen enthalten.",
-       "foreign-structured-upload-form-4-label-bad": "Du kannst keine Bilder hochladen, die du auf einer Suchmaschine gefunden oder von anderen Websites heruntergeladen hast",
        "backend-fail-stream": "Die Datei $1 konnte nicht übertragen werden.",
        "backend-fail-backup": "Die Datei $1 konnte nicht gesichert werden.",
        "backend-fail-notexists": "Die Datei $1 ist nicht vorhanden.",
        "querypage-disabled": "Diese Spezialseite wurde aus Gründen der Leistungserhaltung deaktiviert.",
        "apihelp": "API-Hilfe",
        "apihelp-no-such-module": "Modul „$1“ nicht gefunden.",
+       "apisandbox": "API-Spielwiese",
+       "apisandbox-jsonly": "Zur Nutzung der API-Spielwiese ist JavaScript erforderlich.",
+       "apisandbox-api-disabled": "Die API wurde auf diesem Wiki deaktiviert.",
+       "apisandbox-intro": "Diese Seite kannst du für Versuche mit der <strong>MediaWiki-API</strong> verwenden.\nDie [[mw:API:Main page|Dokumentation zur API]] enthält weitere Hinweise zu ihrer Nutzung. Beispiel: [//www.mediawiki.org/wiki/API:Main_page/de#Ein_einfaches_Beispiel Den Inhalt der Hauptseite abrufen]. Für weitere Beispiele eine der verfügbaren Aktionen auswählen.\n\nObwohl dies eine Spielwiese ist, bedenke, dass Aktionen, die du auf dieser Seite durchführst, das Wiki verändern.",
+       "apisandbox-fullscreen": "Panel expandieren",
+       "apisandbox-fullscreen-tooltip": "Expandiert das Spielwiesen-Panel, um das Browserfenster auszufüllen.",
+       "apisandbox-unfullscreen": "Seite anzeigen",
+       "apisandbox-unfullscreen-tooltip": "Reduziert das Spielwiesen-Panel, so dass MediaWiki-Navigationslinks verfügbar sind.",
+       "apisandbox-submit": "Anfrage ausführen",
+       "apisandbox-reset": "Leeren",
+       "apisandbox-retry": "Erneut versuchen",
+       "apisandbox-loading": "Lade Informationen für das API-Modul „$1“ …",
+       "apisandbox-load-error": "Beim Laden von Informationen für das API-Modul „$1“ ist ein Fehler aufgetreten: $2",
+       "apisandbox-no-parameters": "Dieses API-Modul hat keine Parameter.",
+       "apisandbox-helpurls": "Hilfe-Links",
+       "apisandbox-examples": "Beispiele",
+       "apisandbox-dynamic-parameters": "Zusätzliche Parameter",
+       "apisandbox-dynamic-parameters-add-label": "Parameter hinzufügen:",
+       "apisandbox-dynamic-parameters-add-placeholder": "Name des Parameters",
+       "apisandbox-dynamic-error-exists": "Ein Parameter mit dem Namen „$1“ ist bereits vorhanden.",
+       "apisandbox-deprecated-parameters": "Veraltete Parameter",
+       "apisandbox-fetch-token": "Den Token automatisch ausfüllen",
+       "apisandbox-submit-invalid-fields-title": "Einige Felder sind ungültig",
+       "apisandbox-submit-invalid-fields-message": "Korrigiere bitte die markierten Felder und versuche es erneut.",
+       "apisandbox-results": "Ergebnisse",
+       "apisandbox-sending-request": "Sende API-Anfrage …",
+       "apisandbox-loading-results": "Rufe API-Ergebnisse ab …",
+       "apisandbox-results-error": "Beim Laden der API-Anfragenantwort ist ein Fehler aufgetreten: $1.",
+       "apisandbox-request-url-label": "Anforderungs-URL:",
+       "apisandbox-request-time": "Dauer der Anfrage: {{PLURAL:$1|Eine Millisekunde|$1 Millisekunden}}",
+       "apisandbox-results-fixtoken": "Token korrigieren und erneut übertragen",
+       "apisandbox-results-fixtoken-fail": "Der „$1“-Token konnte nicht abgerufen werden.",
+       "apisandbox-alert-page": "Felder auf dieser Seite sind nicht gültig.",
+       "apisandbox-alert-field": "Der Wert dieses Feldes ist nicht gültig.",
        "booksources": "ISBN-Suche",
        "booksources-search-legend": "Suche nach Bezugsquellen für Bücher",
        "booksources-search": "Suchen",
        "import-nonewrevisions": "Es wurden keine Versionen importiert. Entweder waren alle bereits vorhanden oder wurden aufgrund von Fehlern übersprungen.",
        "xml-error-string": "$1 Zeile $2, Spalte $3, (Byte $4): $5",
        "import-upload": "XML-Dateien importieren",
-       "import-token-mismatch": "Verlust der Sessiondaten. Bitte versuche es erneut.",
+       "import-token-mismatch": "Die Sitzungsdaten sind verloren gegangen.\n\nDu wurdest eventuell abgemeldet. <strong>Bitte verifiziere, dass du noch angemeldet bist und versuche es erneut</strong>.\nFalls dies nicht funktioniert, versuche dich [[Special:UserLogout|abzumelden]] und anschließend wieder anzumelden und überprüfe, ob dein Browser Cookies von dieser Website akzeptiert.",
        "import-invalid-interwiki": "Aus dem angegebenen Wiki ist kein Import möglich.",
        "import-error-edit": "Die Seite „$1“ wurde nicht importiert, da du nicht berechtigt bist, sie zu bearbeiten.",
        "import-error-create": "Die Seite „$1“ wurde nicht importiert, da du nicht berechtigt bist, sie zu erstellen.",
        "pageinfo-robot-index": "Erlaubt",
        "pageinfo-robot-noindex": "Nicht erlaubt",
        "pageinfo-watchers": "Anzahl der Beobachter dieser Seite",
-       "pageinfo-visiting-watchers": "Anzahl der Seitenbeobachter, die die letzten Bearbeitungen besucht haben",
+       "pageinfo-visiting-watchers": "Anzahl der Beobachter dieser Seite, die die letzten Bearbeitungen besucht haben",
        "pageinfo-few-watchers": "Weniger als {{PLURAL:$1|ein|$1}} Beobachter",
        "pageinfo-few-visiting-watchers": "Es könnte einen beobachtenden Benutzer geben oder nicht, der die letzten Bearbeitungen besucht hat",
        "pageinfo-redirects-name": "Anzahl der Weiterleitungen zu dieser Seite",
        "expand_templates_generate_xml": "XML-Parser-Baum zeigen",
        "expand_templates_generate_rawhtml": "Rohes HTML anzeigen",
        "expand_templates_preview": "Vorschau",
-       "expand_templates_preview_fail_html": "<em>Da {{SITENAME}} rohes HTML aktiviert hat und es einen Verlust deiner Sitzungsdaten gab, ist die Vorschau als Vorsichtsmaßnahme gegen JavaScript-Angriffe versteckt.</em>\n\n<strong>Falls dies ein zulässiger Vorschauversuch ist, versuche es bitte erneut.</strong>\nFalls dieses Problem weiterhin bestehen bleibt, versuche dich [[Special:UserLogout|abzumelden]] und erneut anzumelden.",
+       "expand_templates_preview_fail_html": "<em>Da {{SITENAME}} rohes HTML aktiviert hat und es einen Verlust deiner Sitzungsdaten gab, ist die Vorschau als Vorsichtsmaßnahme gegen JavaScript-Angriffe versteckt.</em>\n\n<strong>Falls dies ein zulässiger Vorschauversuch ist, versuche es bitte erneut.</strong>\nFalls dieses Problem weiterhin bestehen bleibt, versuche dich [[Special:UserLogout|abzumelden]] und erneut anzumelden und überprüfe, ob dein Browser Cookies von dieser Website akzeptiert.",
        "expand_templates_preview_fail_html_anon": "<em>Da {{SITENAME}} rohes HTML aktiviert hat und du nicht angemeldet bist, ist die Vorschau als Vorsichtsmaßnahme gegen JavaScript-Angriffe versteckt.</em>\n\n<strong>Falls dies ein zulässiger Vorschauversuch ist, [[Special:UserLogin|melde dich bitte an]] und versuche es erneut.</strong>",
        "expand_templates_input_missing": "Du musst mindestens einen Eingabetext angeben.",
        "pagelanguage": "Seitensprache ändern",
index 8c09ed9..b3e71d0 100644 (file)
        "querypage-disabled": "Na pelaya xısusi,sebeb de performansi ra qefılneyê.",
        "apihelp": "Peştiya APIyi",
        "apihelp-no-such-module": "Modulê \"$1\" çıniyo.",
+       "apisandbox": "API qumdor",
+       "apisandbox-submit": "Bıwazê",
+       "apisandbox-reset": "Bestere",
+       "apisandbox-examples": "Misal",
+       "apisandbox-results": "Netice",
+       "apisandbox-request-url-label": "URL waştış:",
+       "apisandbox-request-time": "Demê waştışi: $1",
        "booksources": "Çımeyê kıtaban",
        "booksources-search-legend": "Seba çımeyanê kıtaban cı geyre",
        "booksources-isbn": "ISBN:",
index 5564b18..5c75776 100644 (file)
        "upload-dialog-button-done": "Ολοκληρώθηκε",
        "upload-dialog-button-save": "Αποθήκευση",
        "upload-dialog-button-upload": "Ανέβασμα",
-       "upload-form-label-select-file": "Επιλογή αρχείου",
        "upload-form-label-infoform-title": "Λεπτομέρειες",
        "upload-form-label-infoform-name": "Όνομα",
        "upload-form-label-infoform-description": "Περιγραφή",
        "foreign-structured-upload-form-label-own-work-message-shared": "Δηλώνω ότι κατέχω τα πνευματικά δικαιώματα για αυτό το αρχείο, και συμφωνώ αμετάκλητα στην απελευθέρωση  αυτού του  αρχείου στο Wikimedia Commons με άδεια  [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0], και συμφωνώ με την [https://wikimediafoundation.org/wiki/Terms_of_Use Όρους Χρήσης].",
        "foreign-structured-upload-form-label-not-own-work-message-shared": "Αν δεν κατέχει τα πνευματικά δικαιώματα για αυτό το αρχείο, ή επιθυμείτε να το δημοσιεύσετε υπό μια διαφορετική άδεια χρήσης, μπορείτε να χρησιμοποιήσετε τον [https://commons.wikimedia.org/wiki/Special:UploadWizard Οδηγό Ανεβάσματος των Wikimedia Commons].",
        "foreign-structured-upload-form-label-not-own-work-local-shared": "Επίσης, μπορεί να θέλετε να δοκιμάσετε να χρησιμοποιήσετε  το [[Special:Upload|τη σελίδα ανεβάσματος για το {{SITENAME}}]], αν αυτό το αρχείο μπορεί να φορτωθεί σύμφωνα με  τις πολιτικές τους.",
-       "foreign-structured-upload-form-2-label-intro": "Σας ευχαριστούμε για τη δωρεά μιας εικόνας που θα χρησιμοποιηθεί στο {{SITENAME}}. Θα πρέπει να συνεχίσετε  μόνο εφόσον πληροί μια σειρά  προϋποθέσεων:",
-       "foreign-structured-upload-form-2-label-ownwork": "Πρέπει να είναι εξ ολοκλήρου <strong>δική σας δημιουργία</strong>, όχι απλά παρμένο από το Internet",
-       "foreign-structured-upload-form-2-label-noderiv": "Δεν πρέπει να  περιέχει <strong>κανένα έργο από οποιονδήποτε άλλον</strong>, ή με έμπνευση από αλλού",
-       "foreign-structured-upload-form-2-label-useful": "Θα πρέπει να είναι <strong>εκπαιδευτικό και χρήσιμο</strong> για διδασκαλία άλλων",
-       "foreign-structured-upload-form-2-label-ccbysa": "Πρέπει να είναι <strong>ΕΝΤΑΞΕΙ για δημοσίευση για πάντα</strong> στο Διαδίκτυο υπό τους όρους της [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0] άδειας",
-       "foreign-structured-upload-form-2-label-alternative": "Εάν όλα τα παραπάνω δεν είναι αλήθεια, μπορείτε ακόμα να είστε σε θέση να ανεβάσετε αυτό το αρχείο χρησιμοποιώντας τον [https://commons.wikimedia.org/wiki/Special:UploadWizard Οδηγό Ανεβάσματος] στα Wikimedia Commons, αρκεί να είναι διαθέσιμο υπό μια ελεύθερη άδεια χρήσης.",
-       "foreign-structured-upload-form-2-label-termsofuse": "Με το ανέβασμα του αρχείου, επιβεβαιώνετε ότι έχετε τα πνευματικά δικαιώματα για αυτό το αρχείο, και συμφωνείτε αμετάκλητα για την δημοσίευση αυτού του αρχείου στα Wikimedia Commons υπό την άδεια Creative Commons Attribution-ShareAlike 4.0, και συμφωνείτε με τους [https://wikimediafoundation.org/wiki/Terms_of_Use Όρους Χρήσης].",
-       "foreign-structured-upload-form-3-label-question-website": "Μήπως κατεβάσατε αυτή την εικόνα από μια ιστοσελίδα, ή την πήραμε μετά από αναζήτηση εικόνων;",
-       "foreign-structured-upload-form-3-label-question-ownwork": "Δημιουργήσατε αυτή την εικόνα (τραβήξατε φωτογραφία, κάνατε ένα σκίτσο κ.τ.λ.) μόνος σας;",
-       "foreign-structured-upload-form-3-label-question-noderiv": "Περιέχει, ή είναι εμπνευσμένο από έργο που ανήκει σε κάποιον άλλο, όπως ένα λογότυπο;",
-       "foreign-structured-upload-form-3-label-yes": "Ναι",
-       "foreign-structured-upload-form-3-label-no": "Όχι",
-       "foreign-structured-upload-form-3-label-alternative": "Δυστυχώς, σε αυτή την περίπτωση, αυτό το εργαλείο δεν υποστηρίζει το ανέβασμα αυτού του αρχείου. Μπορείτε ακόμα να είστε  σε θέση να το ανεβάσετε χρησιμοποιώντας τον [https://commons.wikimedia.org/wiki/Special:UploadWizard Οδηγός Ανεβάσματος] στα Wikimedia Commons, αρκεί να είναι διαθέσιμο υπό μια ελεύθερη άδεια χρήσης.",
-       "foreign-structured-upload-form-4-label-good": "Χρησιμοποιώντας αυτό το εργαλείο, μπορείτε να ανεβάσετε εκπαιδευτικά διαγράμματα που έχετε δημιουργήσει και φωτογραφίες, που δεν περιέχουν έργο που ανήκει σε κάποιον άλλο.",
-       "foreign-structured-upload-form-4-label-bad": "Δεν μπορείτε να ανεβάσετε εικόνες που βρέθηκαν σε μια μηχανή αναζήτησης ή που έχετε κατεβάσει από άλλες ιστοσελίδες.",
        "backend-fail-stream": "Αδύνατη η μετάδοση του αρχείου $1.",
        "backend-fail-backup": "Αδύνατη η δημιουργία αντίγραφου ασφαλείας του αρχείου $1.",
        "backend-fail-notexists": "Το αρχείο $1 δεν υπάρχει.",
        "lockmanager-fail-svr-release": "Δεν ήταν δυνατή η απόκτηση κλειδωμάτων στο διακομιστή $1.",
        "zip-file-open-error": "Παρουσιάστηκε σφάλμα κατά το άνοιγμα του αρχείου για ZIP ελέγχους.",
        "zip-wrong-format": "Το καθορισμένο αρχείο δεν ήταν  αρχείο ZIP.",
-       "zip-bad": "Το Î±Ï\81Ï\87είο ÎµÎ¯Î½Î±Î¹ ÎºÎ±Ï\84εÏ\83Ï\84Ï\81αμμένο Î® Î¼Îµ Î¬Î»Î»Î¿ Ï\84Ï\81Ï\8cÏ\80ο Î¼Î· Î±Î½Î±Î³Î½Ï\8eÏ\83ιμο Î±Ï\81Ï\87είο ZIP.! N! Î\94εν Î¼Ï\80οÏ\81εί Î½Î± ÎµÎ»ÎµÎ³Ï\87θεί  δεόντως ως προς την ασφάλεια.",
+       "zip-bad": "Το Î±Ï\81Ï\87είο ÎµÎ¯Î½Î±Î¹ ÎµÎ¯Ï\84ε ÎºÎ±Ï\84εÏ\83Ï\84Ï\81αμμένο ÎµÎ¯Ï\84ε Î¼Î· Î±Î½Î±Î³Î½Ï\8eÏ\83ιμο Î±Ï\81Ï\87είο ZIP.\nÎ\94εν Î¼Ï\80οÏ\81εί Î½Î± ÎµÎ»ÎµÎ³Ï\87θεί δεόντως ως προς την ασφάλεια.",
        "zip-unsupported": "Το αρχείο είναι ένα αρχείο ZIP που χρησιμοποιεί δυνατότητες ZIP που δεν υποστηρίζονται από το MediaWiki.\nΔεν μπορεί να ελέγχθεί δεόντως για την ασφάλεια.",
        "uploadstash": "Επιφορτώστε το απόθεμα",
        "uploadstash-summary": "Η σελίδα παρέχει πρόσβαση σε αρχεία που είναι  επιφορτωμένα  (ή στη διαδικασία της επιφόρτωσης) αλλά δεν έχει ακόμη δημοσιευθεί για το wiki. Αυτά τα αρχεία δεν είναι ορατά σε  οποιονδήποτε, αλλά στο χρήστη που τα επιφόρτωσε.",
        "querypage-disabled": "Αυτή η ειδική σελίδα είναι απενεργοποιημένη για λόγους απόδοσης.",
        "apihelp": "Βοήθεια API",
        "apihelp-no-such-module": "Το Module \"$1\" δεν βρέθηκε.",
+       "apisandbox": "Αμμοδοχείο API",
+       "apisandbox-api-disabled": "Η Διεπαφή Προγραμματισμού Εφαρμογών (API) είναι απενεργοποιημένη σε αυτήν την τοποθεσία.",
+       "apisandbox-intro": "Χρησιμοποιήστε αυτήν τη σελίδα για να πειραματιστείτε με το '''API της υπηρεσίας ιστού του MediaWiki'''.\nΑνατρέξτε στην [//www.mediawiki.org/wiki/API:Main_page τεκμηρίωση του API] για περισσότερες πληροφορίες πάνω στη χρήση του API. Παράδειγμα: [//www.mediawiki.org/wiki/API#A_simple_example λήψη του περιεχομένου της Αρχικής Σελίδας]. Επιλέξτε μια ενέργεια για να δείτε περισσότερα παραδείγματα.\n\nΝα σημειωθεί ότι, παρόλο που αυτό εδώ είναι αμμοδοχείο, οι ενέργειες που εκτελείτε σε αυτήν τη σελίδα μπορούν να τροποποιήσουν το wiki.",
+       "apisandbox-submit": "Υποβολή του αιτήματος",
+       "apisandbox-reset": "Εκκαθάριση",
+       "apisandbox-examples": "Παράδειγμα",
+       "apisandbox-results": "Αποτέλεσμα",
+       "apisandbox-request-url-label": "Αίτηση URL:",
+       "apisandbox-request-time": "Χρόνος αιτήματος: $1",
        "booksources": "Πηγές βιβλίων",
        "booksources-search-legend": "Αναζήτηση για πηγές βιβλίων",
        "booksources-isbn": "ISBN:",
index d0dbc2b..5e9b186 100644 (file)
        "previewnote": "<strong>Remember that this is only a preview.</strong>\nYour changes have not yet been saved!",
        "continue-editing": "Go to editing area",
        "previewconflict": "This preview reflects the text in the upper text editing area as it will appear if you choose to save.",
-       "session_fail_preview": "<strong>Sorry! We could not process your edit due to a loss of session data.</strong>\nPlease try again.\nIf it still does not work, try [[Special:UserLogout|logging out]] and logging back in.",
-       "session_fail_preview_html": "<strong>Sorry! We could not process your edit due to a loss of session data.</strong>\n\n<em>Because {{SITENAME}} has raw HTML enabled, the preview is hidden as a precaution against JavaScript attacks.</em>\n\n<strong>If this is a legitimate edit attempt, please try again.</strong>\nIf it still does not work, try [[Special:UserLogout|logging out]] and logging back in.",
+       "session_fail_preview": "Sorry! We could not process your edit due to a loss of session data.\n\nYou might have been logged out. <strong>Please verify that you're still logged in and try again</strong>.\nIf it still does not work, try [[Special:UserLogout|logging out]] and logging back in, and check that your browser allows cookies from this site.",
+       "session_fail_preview_html": "Sorry! We could not process your edit due to a loss of session data.\n\n<em>Because {{SITENAME}} has raw HTML enabled, the preview is hidden as a precaution against JavaScript attacks.</em>\n\n<strong>If this is a legitimate edit attempt, please try again.</strong>\nIf it still does not work, try [[Special:UserLogout|logging out]] and logging back in, and check that your browser allows cookies from this site.",
        "token_suffix_mismatch": "<strong>Your edit has been rejected because your client mangled the punctuation characters in the edit token.</strong>\nThe edit has been rejected to prevent corruption of the page text.\nThis sometimes happens when you are using a buggy web-based anonymous proxy service.",
        "edit_form_incomplete": "<strong>Some parts of the edit form did not reach the server; double-check that your edits are intact and try again.</strong>",
        "editing": "Editing $1",
        "mergehistory-empty": "No revisions can be merged.",
        "mergehistory-done": "$3 {{PLURAL:$3|revision|revisions}} of $1 {{PLURAL:$3|was|were}} merged into [[:$2]].",
        "mergehistory-fail": "Unable to perform history merge, please recheck the page and time parameters.",
-       "mergehistory-fail-toobig" : "Unable to perform history merge as more than the limit of $1 {{PLURAL:$1|revision|revisions}} would be moved.",
+       "mergehistory-fail-bad-timestamp": "Timestamp is invalid.",
+       "mergehistory-fail-invalid-source": "Source page is invalid.",
+       "mergehistory-fail-invalid-dest": "Destination page is invalid.",
+       "mergehistory-fail-no-change": "History merge did not merge any revisions. Please recheck the page and time parameters.",
+       "mergehistory-fail-permission": "Insufficient permissions to merge history.",
+       "mergehistory-fail-self-merge": "Source and destination pages are the same.",
+       "mergehistory-fail-timestamps-overlap": "Source revisions overlap or come after destination revisions.",
+       "mergehistory-fail-toobig": "Unable to perform history merge as more than the limit of $1 {{PLURAL:$1|revision|revisions}} would be moved.",
+       "mergehistory-warning-redirect-not-created": "",
        "mergehistory-no-source": "Source page $1 does not exist.",
        "mergehistory-no-destination": "Destination page $1 does not exist.",
        "mergehistory-invalid-source": "Source page must be a valid title.",
        "mergehistory-same-destination": "Source and destination pages cannot be the same",
        "mergehistory-reason": "Reason:",
        "mergehistory-revisionrow": "$1 ($2) $3 . . $4 $5 $6",
+       "mergehistory-redirect-text": "",
        "mergelog": "Merge log",
        "pagemerge-logentry": "merged [[$1]] into [[$2]] (revisions up to $3)",
        "revertmerge": "Unmerge",
        "upload-dialog-button-done": "Done",
        "upload-dialog-button-save": "Save",
        "upload-dialog-button-upload": "Upload",
-       "upload-form-label-select-file": "Select file",
        "upload-form-label-infoform-title": "Details",
        "upload-form-label-infoform-name": "Name",
        "upload-form-label-infoform-name-tooltip": "A unique descriptive title for the file, which will serve as a filename. You may use plain language with spaces. Do not include the file extension.",
        "foreign-structured-upload-form-label-own-work-message-shared": "I attest that I own the copyright on this file, and agree to irrevocably release this file to Wikimedia Commons under the [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0] license, and I agree to the [https://wikimediafoundation.org/wiki/Terms_of_Use Terms of Use].",
        "foreign-structured-upload-form-label-not-own-work-message-shared": "If you do not own the copyright on this file, or you wish to release it under a different license, consider using the [https://commons.wikimedia.org/wiki/Special:UploadWizard Commons Upload Wizard].",
        "foreign-structured-upload-form-label-not-own-work-local-shared": "You may also want to try using [[Special:Upload|the upload page on {{SITENAME}}]], if the site allows the upload of this file under their policies.",
-       "foreign-structured-upload-form-2-label-intro": "Thank you for donating an image to be used on {{SITENAME}}. You should only continue if it meets several conditions:",
-       "foreign-structured-upload-form-2-label-ownwork": "It must be entirely <strong>your own creation</strong>, not just taken from the Internet",
-       "foreign-structured-upload-form-2-label-noderiv": "It has to contain <strong>no work by anyone else</strong>, or inspired by them",
-       "foreign-structured-upload-form-2-label-useful": "It should be <strong>educational and useful</strong> for teaching others",
-       "foreign-structured-upload-form-2-label-ccbysa": "It must be <strong>OK to publish forever</strong> on the Internet under the [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0] license",
-       "foreign-structured-upload-form-2-label-alternative": "If not all of the above are true, you may still be able to upload this file using the [https://commons.wikimedia.org/wiki/Special:UploadWizard Commons Upload Wizard], as long as it's available under a free license.",
-       "foreign-structured-upload-form-2-label-termsofuse": "By uploading the file, you attest that you own the copyright on this file, and agree to irrevocably release this file to Wikimedia Commons under the Creative Commons Attribution-ShareAlike 4.0 license, and you agree to the [https://wikimediafoundation.org/wiki/Terms_of_Use Terms of Use].",
-       "foreign-structured-upload-form-3-label-question-website": "Did you download this image from a website, or get it from an image search?",
-       "foreign-structured-upload-form-3-label-question-ownwork": "Did you create this image (take the photo, sketch the drawing, etc.) yourself?",
-       "foreign-structured-upload-form-3-label-question-noderiv": "Does it contain, or is it inspired by, work owned by anyone else, like a logo?",
-       "foreign-structured-upload-form-3-label-yes": "Yes",
-       "foreign-structured-upload-form-3-label-no": "No",
-       "foreign-structured-upload-form-3-label-alternative": "Unfortunately, in this case, this tool does not support uploading this file. You may still be able to upload it using the [https://commons.wikimedia.org/wiki/Special:UploadWizard Commons Upload Wizard], as long as it's available under a free license.",
-       "foreign-structured-upload-form-4-label-good": "Using this tool, you can upload educational graphics you've created and photographs you've taken, that don't contain work owned by someone else.",
-       "foreign-structured-upload-form-4-label-bad": "You can not upload images found on a search engine or downloaded from other websites.",
        "backend-fail-stream": "Could not stream file \"$1\".",
        "backend-fail-backup": "Could not backup file \"$1\".",
        "backend-fail-notexists": "The file $1 does not exist.",
        "apihelp-summary": "",
        "apihelp-no-such-module": "Module \"$1\" not found.",
        "apihelp-link": "[[Special:ApiHelp/$1|$2]]",
+       "apisandbox": "API sandbox",
+       "apisandbox-summary": "",
+       "apisandbox-jsonly": "JavaScript is required to use the API sandbox.",
+       "apisandbox-api-disabled": "The API is disabled on this site.",
+       "apisandbox-intro": "Use this page to experiment with the <strong>MediaWiki web service API</strong>.\nRefer to [[mw:API:Main page|the API documentation]] for further details of API usage. Example: [//www.mediawiki.org/wiki/API#A_simple_example get the content of a Main Page]. Select an action to see more examples.\n\nNote that, although this is a sandbox, actions you carry out on this page may modify the wiki.",
+       "apisandbox-fullscreen": "Expand panel",
+       "apisandbox-fullscreen-tooltip": "Expand the sandbox panel to fill the browser window.",
+       "apisandbox-unfullscreen": "Show page",
+       "apisandbox-unfullscreen-tooltip": "Reduce the sandbox panel, so MediaWiki navigation links are available.",
+       "apisandbox-submit": "Make request",
+       "apisandbox-reset": "Clear",
+       "apisandbox-retry": "Retry",
+       "apisandbox-loading": "Loading information for API module \"$1\"...",
+       "apisandbox-load-error": "An error occurred while loading information for API module \"$1\": $2",
+       "apisandbox-no-parameters": "This API module has no parameters.",
+       "apisandbox-helpurls": "Help links",
+       "apisandbox-examples": "Examples",
+       "apisandbox-dynamic-parameters": "Additional parameters",
+       "apisandbox-dynamic-parameters-add-label": "Add parameter:",
+       "apisandbox-dynamic-parameters-add-placeholder": "Parameter name",
+       "apisandbox-dynamic-error-exists": "A parameter named \"$1\" already exists.",
+       "apisandbox-deprecated-parameters": "Deprecated parameters",
+       "apisandbox-fetch-token": "Auto-fill the token",
+       "apisandbox-submit-invalid-fields-title": "Some fields are invalid",
+       "apisandbox-submit-invalid-fields-message": "Please correct the marked fields and try again.",
+       "apisandbox-results": "Results",
+       "apisandbox-sending-request": "Sending API request...",
+       "apisandbox-loading-results": "Receiving API results...",
+       "apisandbox-results-error": "An error occurred while loading the API query response: $1.",
+       "apisandbox-request-url-label": "Request URL:",
+       "apisandbox-request-time": "Request time: {{PLURAL:$1|$1 ms}}",
+       "apisandbox-results-fixtoken": "Correct token and resubmit",
+       "apisandbox-results-fixtoken-fail": "Failed to fetch \"$1\" token.",
+       "apisandbox-alert-page": "Fields on this page are not valid.",
+       "apisandbox-alert-field": "The value of this field is not valid.",
        "booksources": "Book sources",
        "booksources-summary": "",
        "booksources-search-legend": "Search for book sources",
        "undelete-error-long": "Errors were encountered while undeleting the file:\n\n$1",
        "undelete-show-file-confirm": "Are you sure you want to view the deleted revision of the file \"<nowiki>$1</nowiki>\" from $2 at $3?",
        "undelete-show-file-submit": "Yes",
-       "undelete-revision-row": "$1 $2 ($3) $4 . . $5 $6 $7 $8 $9",
+       "undelete-revision-row2": "$1 ($2) $3 . . $4 $5 $6 $7 $8",
        "namespace": "Namespace:",
        "invert": "Invert selection",
        "tooltip-invert": "Check this box to hide changes to pages within the selected namespace (and the associated namespace if checked)",
        "import-nonewrevisions": "No revisions imported (all were either already present, or skipped due to errors).",
        "xml-error-string": "$1 at line $2, col $3 (byte $4): $5",
        "import-upload": "Upload XML data",
-       "import-token-mismatch": "Loss of session data.\nPlease try again.",
+       "import-token-mismatch": "Loss of session data.\n\nYou might have been logged out. <strong>Please verify that you're still logged in and try again</strong>.\nIf it still does not work, try [[Special:UserLogout|logging out]] and logging back in, and check that your browser allows cookies from this site.",
        "import-invalid-interwiki": "Cannot import from the specified wiki.",
        "import-error-edit": "Page \"$1\" was not imported because you are not allowed to edit it.",
        "import-error-create": "Page \"$1\" was not imported because you are not allowed to create it.",
        "expand_templates_generate_xml": "Show XML parse tree",
        "expand_templates_generate_rawhtml": "Show raw HTML",
        "expand_templates_preview": "Preview",
-       "expand_templates_preview_fail_html": "<em>Because {{SITENAME}} has raw HTML enabled and there was a loss of session data, the preview is hidden as a precaution against JavaScript attacks.</em>\n\n<strong>If this is a legitimate preview attempt, please try again.</strong>\nIf it still does not work, try [[Special:UserLogout|logging out]] and logging back in.",
+       "expand_templates_preview_fail_html": "<em>Because {{SITENAME}} has raw HTML enabled and there was a loss of session data, the preview is hidden as a precaution against JavaScript attacks.</em>\n\n<strong>If this is a legitimate preview attempt, please try again.</strong>\nIf it still does not work, try [[Special:UserLogout|logging out]] and logging back in, and check that your browser allows cookies from this site.",
        "expand_templates_preview_fail_html_anon": "<em>Because {{SITENAME}} has raw HTML enabled and you are not logged in, the preview is hidden as a precaution against JavaScript attacks.</em>\n\n<strong>If this is a legitimate preview attempt, please [[Special:UserLogin|log in]] and try again.</strong>",
        "expand_templates_input_missing": "You need to provide at least some input text.",
        "pagelanguage": "Change page language",
index 5dc6ea6..d257f96 100644 (file)
        "resetpass_submit": "Fari pasvorton kaj ensaluti",
        "changepassword-success": "Via pasvorto estis sukcese ŝanĝita!",
        "changepassword-throttled": "Vi tro ofte provis ensaluti al ĉi tiu konto.\nBonvolu atendi $1 antaŭ ol reprovi.",
+       "botpasswords-label-create": "Krei",
+       "botpasswords-label-update": "Ĝisdatigi",
+       "botpasswords-label-cancel": "Nuligi",
+       "botpasswords-label-delete": "Forigi",
+       "botpasswords-label-resetpassword": "Rekomencigi la pasvorton",
        "resetpass_forbidden": "Pasvortoj ne estas ŝanĝeblaj",
        "resetpass-no-info": "Vi devas ensaluti por atingi ĉi tiun paĝon rekte.",
        "resetpass-submit-loggedin": "Ŝanĝi pasvorton",
        "upload-dialog-button-done": "Farite",
        "upload-dialog-button-save": "Konservi",
        "upload-dialog-button-upload": "Alŝuti",
-       "upload-form-label-select-file": "Elekti dosieron",
        "upload-form-label-infoform-title": "Detaloj",
        "upload-form-label-infoform-name": "Nomo",
        "upload-form-label-infoform-description": "Priskribo",
        "foreign-structured-upload-form-label-own-work": "Tio estas mia propra laboro",
        "foreign-structured-upload-form-label-infoform-categories": "Kategorioj",
        "foreign-structured-upload-form-label-infoform-date": "Dato",
-       "foreign-structured-upload-form-3-label-yes": "Jes",
-       "foreign-structured-upload-form-3-label-no": "Ne",
        "backend-fail-stream": "Ne povis fluigi dosieron $1.",
        "backend-fail-backup": "Ne povis enarkivigi dosieron $1.",
        "backend-fail-notexists": "La dosiero $1 ne ekzistas.",
        "querypage-disabled": "Tiu ĉi speciala paĝo estas malfunkciigita pro rendimentaj kialoj.",
        "apihelp": "Helpo pri API",
        "apihelp-no-such-module": "Modulo \"$1\" ne estis trovita.",
+       "apisandbox": "API testejo",
+       "apisandbox-api-disabled": "API estas malŝalta en ĉi tiu retejo.",
+       "apisandbox-intro": "Uzu tiun ĉi paĝon por eksperimenti kun '''MediaWiki API'''.\nVidu [//www.mediawiki.org/wiki/API:Main_page la API-dokumentadon] por pli da detaloj pri la uzo de API. Ekz-e: [//www.mediawiki.org/wiki/API#A_simple_example atingi la enhavon de la Ĉefpaĝo]. Elektu agon por vidi pliajn ekzemplojn.\n\nNotu ke, kvankam ĉi tiu estas provejo, agoj kiun vi faros en ĉi tiu paĝo povas modifi la vikion.",
+       "apisandbox-submit": "Fari mendon",
+       "apisandbox-reset": "Nuligi",
+       "apisandbox-examples": "Ekzemplo",
+       "apisandbox-results": "Rezulto",
+       "apisandbox-request-url-label": "Mendi URL-on.",
+       "apisandbox-request-time": "Tempo de peto: $1",
        "booksources": "Libroservoj",
        "booksources-search-legend": "Serĉi librofontojn",
        "booksources-search": "Serĉi",
index 3487702..a8f7c5d 100644 (file)
                        "Syum90",
                        "Cindie.Capel",
                        "ElGatoSaez",
-                       "Joaquin1001"
+                       "Joaquin1001",
+                       "YoViajo",
+                       "Asierog"
                ]
        },
        "tog-underline": "Subrayar los enlaces:",
        "mergehistory-empty": "No hay revisiones fusionables.",
        "mergehistory-done": "Se {{PLURAL:$3|fusionó una revisión|fusionaron $3 revisiones}} de $1 en [[:$2]].",
        "mergehistory-fail": "No se puede realizar la fusión de historiales, por favor revisa la página y los parámetros de tiempo.",
+       "mergehistory-fail-invalid-source": "La página de origen no es válida.",
+       "mergehistory-fail-invalid-dest": "La página de destino no es válida.",
+       "mergehistory-fail-permission": "Permisos insuficientes para fusionar el historial.",
+       "mergehistory-fail-self-merge": "Las páginas de origen y destino son la misma.",
        "mergehistory-fail-toobig": "No se puede fusionar el historial ya que más del límite de $1 {{PLURAL:$1|revisión|revisiones}} se moverían.",
        "mergehistory-no-source": "La página origen $1 no existe.",
        "mergehistory-no-destination": "La página destino $1 no existe.",
        "rcshowhidemine": "$1 mis ediciones",
        "rcshowhidemine-show": "Mostrar",
        "rcshowhidemine-hide": "Ocultar",
-       "rcshowhidecategorization": "$1 categorización de página",
+       "rcshowhidecategorization": "$1 categorización de páginas",
        "rcshowhidecategorization-show": "Mostrar",
        "rcshowhidecategorization-hide": "Ocultar",
        "rclinks": "Ver los últimos $1 cambios en los últimos $2 días.<br />$3",
        "upload-dialog-button-done": "Hecho",
        "upload-dialog-button-save": "Guardar",
        "upload-dialog-button-upload": "Subir",
-       "upload-form-label-select-file": "Seleccionar archivo",
        "upload-form-label-infoform-title": "Detalles",
        "upload-form-label-infoform-name": "Nombre",
        "upload-form-label-infoform-name-tooltip": "Un título único descriptivo para el archivo, que servirá como un nombre de archivo. Puedes usar un lenguaje claro con espacios. No incluyas la extensión del archivo.",
        "foreign-structured-upload-form-label-own-work-message-shared": "Doy fe que soy dueño de los derechos de autor de este archivo, y acepto irrevocablemente liberar este archivo a Wikimedia Commons bajo la licencia [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0], y acepto los [https://wikimediafoundation.org/wiki/Terms_of_Use Términos de Uso].",
        "foreign-structured-upload-form-label-not-own-work-message-shared": "Si no es dueño de los derechos de autor de este archivo, o desea publicarlo bajo una licencia diferentes, considere usar el [https://commons.wikimedia.org/wiki/Special:UploadWizard Asistente de Carga de Commons].",
        "foreign-structured-upload-form-label-not-own-work-local-shared": "Puede que también quiera usar  [[Special:Upload|la página de carga {{SITENAME}}]], si el sitio permite la subida de este archivo bajo sus políticas.",
-       "foreign-structured-upload-form-2-label-intro": "Gracias por donar una imagen para ser usada en {{SITENAME}}. Sólo debería continuar si se cumpñen varias condiciones:",
-       "foreign-structured-upload-form-2-label-ownwork": "Debe ser completamente <strong>su creación propia</strong>, no solamente tomada de Internet",
-       "foreign-structured-upload-form-2-label-noderiv": "No tiene que contener <strong>trabajo de alguien más</strong>, o inspirado por otros",
-       "foreign-structured-upload-form-2-label-useful": "Debe ser <strong>educativo y útil</strong> para enseñarle a otros",
-       "foreign-structured-upload-form-2-label-ccbysa": "Debe estar <strong>aceptado para publicarse por siempre</strong> en Internet bajo la licencia [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0]",
-       "foreign-structured-upload-form-2-label-alternative": "Si no todos los criterios de arriba son ciertos, aún puede ser capaz de subir este archivo usando el[https://commons.wikimedia.org/wiki/Special:UploadWizard Asistente de Carga de Commons], mientras esté disponible bajo una licencia libre.",
-       "foreign-structured-upload-form-2-label-termsofuse": "Al subir el archivo, das fe de que eres dueño de los derechos de autor en este archivo, y aceptas irrevocablemente liberar este archivo a Wikimedia Commons bajo la licencia Creative Commons Attribution-ShareAlike 4.0, y aceptas los [https://wikimediafoundation.org/wiki/Terms_of_Use Términos de uso].",
-       "foreign-structured-upload-form-3-label-question-website": "¿Has descargado esta imagen de un sitio web, o la has obtenido de una búsqueda de imágenes?",
-       "foreign-structured-upload-form-3-label-question-ownwork": "¿Creó esta imagen (tomó la foto, hizo el dibujo, etc) usted mismo?",
-       "foreign-structured-upload-form-3-label-question-noderiv": "¿Contiene, o está inspirada por, trabajo de propiedad de cualquier otra persona, como un logotipo?",
-       "foreign-structured-upload-form-3-label-yes": "Sí",
-       "foreign-structured-upload-form-3-label-no": "No",
-       "foreign-structured-upload-form-3-label-alternative": "Desafortunadamente, en este caso, esta herramienta no admite la subida de este archivo. Pero lo puedes hacer usando el [https://commons.wikimedia.org/wiki/Special:UploadWizard Asistente de subidas de Commons], mientras esté disponible bajo una licencia libre.",
-       "foreign-structured-upload-form-4-label-good": "Usando esta herramienta, puedes subir gráficos educativos que hayas creado y fotografías que hayas tomado, que no contengan trabajo de alguien más.",
-       "foreign-structured-upload-form-4-label-bad": "No puedes subir imágenes encontradas en un motor de búsqueda o descargadas de otros sitios web.",
        "backend-fail-stream": "No se pudo transmitir el archivo «$1».",
        "backend-fail-backup": "No se pudo hacer copia de seguridad del archivo «$1».",
        "backend-fail-notexists": "El archivo  $1  no existe.",
        "querypage-disabled": "Esta página especial está deshabilitada por motivos de rendimiento.",
        "apihelp": "Ayuda de la API",
        "apihelp-no-such-module": "No se encontró el módulo \"$1\".",
+       "apisandbox": "Zona de pruebas API",
+       "apisandbox-api-disabled": "La API está desactivada en este sitio.",
+       "apisandbox-intro": "Usa esta página para experimentar con la '''API de servicio web de MediaWiki'''.\nPara más detalles sobre el uso de la API, visita [//www.mediawiki.org/wiki/API:Main_page su documentación]. Ejemplo: [//www.mediawiki.org/wiki/API#A_simple_example obtener el contenido de una Página principal]. Selecciona una acción para ver más ejemplos.\n\nObserva que, aunque sea una página de pruebas, las acciones que realices en esta página pueden modificar el wiki.",
+       "apisandbox-fullscreen": "Expandir panel",
+       "apisandbox-unfullscreen": "Mostrar página",
+       "apisandbox-submit": "Realizar solicitud",
+       "apisandbox-reset": "Limpiar",
+       "apisandbox-retry": "Reintentar",
+       "apisandbox-no-parameters": "Este módulo API no tiene parámetros.",
+       "apisandbox-helpurls": "Enlaces de ayuda",
+       "apisandbox-examples": "Ejemplos",
+       "apisandbox-dynamic-parameters": "Parámetros adicionales",
+       "apisandbox-dynamic-parameters-add-label": "Añadir parámetro:",
+       "apisandbox-dynamic-parameters-add-placeholder": "Nombre del parámetro",
+       "apisandbox-deprecated-parameters": "Parámetros desaconsejados",
+       "apisandbox-submit-invalid-fields-title": "Algunos campos no son válidos",
+       "apisandbox-results": "Resultados",
+       "apisandbox-sending-request": "Enviando pedido a la API...",
+       "apisandbox-loading-results": "Recibiendo resultados de la API...",
+       "apisandbox-request-url-label": "URL solicitante:",
+       "apisandbox-request-time": "Tiempo de solicitud: {{PLURAL:$1|$1 ms}}",
        "booksources": "Fuentes de libros",
        "booksources-search-legend": "Buscar fuentes de libros",
        "booksources-search": "Buscar",
        "listgrouprights-namespaceprotection-header": "Restricciones del espacio de nombres",
        "listgrouprights-namespaceprotection-namespace": "Espacio de nombres",
        "listgrouprights-namespaceprotection-restrictedto": "Derechos de usuario para editar",
+       "listgrants": "Subvenciones",
        "listgrants-grant": "Conceder",
-       "listgrants-rights": "Conceder",
+       "listgrants-rights": "Derechos",
        "trackingcategories": "Categorías de seguimiento",
        "trackingcategories-summary": "Esta página lista categorías de seguimiento que han sido generadas automáticamente por el software MediaWiki. Sus nombres pueden cambiarse editando su mensaje correspondiente en el espacio de nombres {{ns:8}}.",
        "trackingcategories-msg": "Categoría de seguimiento",
        "hidden-category-category-desc": "La categoría contiene <code><nowiki>__HIDDENCAT__</nowiki></code> en su página de contenido, lo que evita que aparezca en el cuadro de enlaces de categorías en las páginas, de forma predeterminada.",
        "trackingcategories-nodesc": "No hay descripción disponible.",
        "trackingcategories-disabled": "La categoría está desactivada",
-       "mailnologin": "Ninguna dirección de envio",
+       "mailnologin": "Ninguna dirección de envío",
        "mailnologintext": "Debes [[Special:UserLogin|iniciar sesión]] y tener una dirección electrónica válida en tus [[Special:Preferences|preferencias]] para enviar un correo electrónico a otros usuarios.",
        "emailuser": "Enviar un mensaje de correo a {{GENDER:{{BASEPAGENAME}}|este usuario|esta usuaria}}",
        "emailuser-title-target": "Enviar un mensaje a {{GENDER:$1|este usuario|esta usuaria}}",
        "wlshowhideanons": "usuarios anónimos",
        "wlshowhidepatr": "ediciones verificadas",
        "wlshowhidemine": "mis ediciones",
-       "wlshowhidecategorization": "categorización de página",
+       "wlshowhidecategorization": "categorización de páginas",
        "watchlist-options": "Opciones de la lista de seguimiento",
        "watching": "Vigilando...",
        "unwatching": "Eliminando de la lista de seguimiento...",
index 720c350..30f40b1 100644 (file)
@@ -26,7 +26,8 @@
                        "Roland",
                        "Postituvi",
                        "Purodha",
-                       "Macofe"
+                       "Macofe",
+                       "Adeliine"
                ]
        },
        "tog-underline": "Linkide allakriipsutus:",
        "upload-dialog-button-done": "Valmis",
        "upload-dialog-button-save": "Salvesta",
        "upload-dialog-button-upload": "Laadi üles",
-       "upload-form-label-select-file": "Faili valimine",
        "upload-form-label-infoform-title": "Üksikasjad",
        "upload-form-label-infoform-name": "Pealkiri",
        "upload-form-label-infoform-description": "Kirjeldus",
        "querypage-disabled": "See erilehekülg on keelatud, et jõudlust hoida.",
        "apihelp": "API abi",
        "apihelp-no-such-module": "Moodulit \"$1\" ei leitud.",
+       "apisandbox": "API liivakast",
+       "apisandbox-api-disabled": "API on selles võrgukohas keelatud.",
+       "apisandbox-intro": "Kasuta seda lehekülge '''MediaWiki API''' katsetamiseks.\nÜksikasjad API kasutamise kohta leiad [//www.mediawiki.org/wiki/API:Main_page API dokumentatsioonist]. Näide: [//www.mediawiki.org/wiki/API#A_simple_example esilehe sisu hankimine]. Vali toiming, et näha veel näiteid.\n\nPane tähele, et kuigi siin on liivakast, võivad siin leheküljel tehtud toimingud vikit muuta.",
+       "apisandbox-submit": "Tee päring",
+       "apisandbox-reset": "Puhasta",
+       "apisandbox-examples": "Näide",
+       "apisandbox-results": "Tulemus",
+       "apisandbox-request-url-label": "Päringu URL:",
+       "apisandbox-request-time": "Päringuaeg: $1",
        "booksources": "Raamatuotsimine",
        "booksources-search-legend": "Raamatuotsimine",
        "booksources-search": "Otsi",
        "unwatchthispage": "Ära jälgi",
        "notanarticle": "Pole sisulehekülg",
        "notvisiblerev": "Viimane teise kasutaja redaktsioon on kustutatud",
-       "watchlist-details": "Jälgimisloendis on {{PLURAL:$1|üks lehekülg|$1 lehekülge}}. Arutelulehekülgi pole eraldi välja toodud.",
+       "watchlist-details": "Jälgimisloendis on {{PLURAL:$1|üks lehekülg|$1 lehekülge}} (lisaks vastavad arutelulehed).",
        "wlheader-enotif": "E-posti teel teavitamine on lubatud.",
        "wlheader-showupdated": "Leheküljed, mida on muudetud peale sinu viimast külastust, on '''rasvases kirjas'''.",
        "wlnote": "Allpool on {{PLURAL:$1|viimane muudatus|viimased <strong>$1</strong> muudatust}} viimase {{PLURAL:$2|tunni|<strong>$2</strong> tunni}} jooksul seisuga $3, kell $4.",
index 0071de3..7c33712 100644 (file)
@@ -23,7 +23,8 @@
                        "Arkaitz Barnetik",
                        "Sator",
                        "Macofe",
-                       "Xð"
+                       "Xð",
+                       "Asierog"
                ]
        },
        "tog-underline": "Azpimarratu loturak:",
        "resetpass_submit": "Pasahitza definitu eta saioa hasi",
        "changepassword-success": "Zure pasahitza ondo aldatu da!",
        "changepassword-throttled": "Saioa hasteko saiakera gehiegi egin berri dituzu.\nBerriro saiatu aurretik $1 itxoin, mesedez.",
+       "botpasswords-label-appid": "Bot izena:",
        "botpasswords-label-create": "Sortu",
        "botpasswords-label-update": "Eguneratu",
+       "botpasswords-label-cancel": "Utzi",
        "botpasswords-label-delete": "Ezabatu",
        "resetpass_forbidden": "Ezin dira pasahitzak aldatu",
        "resetpass-no-info": "Orrialde honetara zuzenean sartzeko izena eman behar duzu.",
        "userrights": "Erabiltzaile baimenen kudeaketa",
        "userrights-lookup-user": "Erabiltzaile taldeak kudeatu",
        "userrights-user-editname": "Erabiltzaile izena idatzi:",
-       "editusergroup": "Erabiltzaile taldeak editatu",
+       "editusergroup": "{{GENDER:$1|Erabiltzaile}} taldeak editatu",
        "editinguser": "'''[[User:$1|$1]]''' $2 lankidearen erabiltzaile-eskubideak aldatzen",
        "userrights-editusergroup": "Erabiltzaile taldeak editatu",
        "saveusergroups": "Erabiltzaile taldeak gorde",
        "right-override-export-depth": "5eko sakonerararteko loturiko orrialdeak barne esportatu",
        "right-sendemail": "Beste erabiltzaileei e-posta bidali",
        "right-passwordreset": "Ikusi pasahitza berrezartze e-postak",
+       "grant-createaccount": "Kontuak sortu",
+       "grant-editmycssjs": "Zure CSS/JavaScript aldatu",
+       "grant-editmyoptions": "Aldatu zure hobespenak",
+       "grant-editmywatchlist": "Zure jarraipen zerrenda aldatu",
+       "grant-editprotected": "Babestutako orriak aldatu",
+       "grant-patrol": "Orrietako aldaketa patruilatu",
+       "grant-protect": "Orriak babestu eta babesgabetu",
        "grant-uploadfile": "Igotako fitxategi berriak",
+       "grant-basic": "Oinarrizko baimenak",
+       "grant-viewdeleted": "Ikusi ezabatutako fitxategiak eta orriak",
+       "grant-viewmywatchlist": "Zure jarraipen zerrenda ikusi",
        "newuserlogpage": "Erabiltzaile erregistroa",
        "newuserlogpagetext": "Hau azken erabiltzaileen sorreren erregistroa da.",
        "rightslog": "Erabiltzaile eskubideen erregistroa",
        "upload-dialog-button-done": "Egina",
        "upload-dialog-button-save": "Gorde",
        "upload-dialog-button-upload": "Igo",
-       "upload-form-label-select-file": "Fitxategia Aukeratu",
        "upload-form-label-infoform-title": "Xehetasunak",
        "upload-form-label-infoform-name": "Izena",
        "upload-form-label-infoform-description": "Deskribapena",
        "foreign-structured-upload-form-label-own-work": "Hau neure lana da",
        "foreign-structured-upload-form-label-infoform-categories": "Kategoriak",
        "foreign-structured-upload-form-label-infoform-date": "Data",
-       "foreign-structured-upload-form-3-label-yes": "Bai",
-       "foreign-structured-upload-form-3-label-no": "Ez",
        "backend-fail-stream": "Ezin izan da \"$1\" fitxategiaren stream egin.",
        "backend-fail-backup": "Ezin izan da \"$1\" fitxategiaren backup egin.",
        "backend-fail-notexists": "$1 fitxategia ez da existitzen.",
        "protectedpages-unknown-performer": "Erabiltzaile ezezaguna",
        "protectedtitles": "Babestutako tituluak",
        "protectedtitlesempty": "Ez dago parametro horiek dituen babesturiko izenbururik oraintxe.",
+       "protectedtitles-submit": "Izenburuak erakutsi",
        "listusers": "Erabiltzaileen zerrenda",
        "listusers-editsonly": "Aldaketak egin dituzten erabiltzaileak soilik erakutsi",
        "listusers-creationsort": "Sorrera dataren arabera sailkatu",
        "querypage-disabled": "Orrialde berezi hau desgaituta dago funtzionamendu arrazoiengatik.",
        "apihelp": "API laguntza",
        "apihelp-no-such-module": "Ez da \"$1\" modulua aurkitu.",
+       "apisandbox": "API proba orria",
+       "apisandbox-unfullscreen": "Erakutsi orria",
+       "apisandbox-submit": "Egin eskaera",
+       "apisandbox-reset": "Garbitu",
+       "apisandbox-retry": "Saiatu berriro",
+       "apisandbox-helpurls": "Laguntza estekak",
+       "apisandbox-examples": "Adibideak",
+       "apisandbox-dynamic-parameters": "Parametro gehigarriak",
+       "apisandbox-results": "Emaitzak",
        "booksources": "Iturri liburuak",
        "booksources-search-legend": "Liburuen bilaketa",
        "booksources-search": "Bilatu",
        "logempty": "Ez dago emaitzarik erregistroan.",
        "log-title-wildcard": "Testu honekin hasten diren izenburuak bilatu",
        "showhideselectedlogentries": "Erakutsi/ezkutatu aukeratutako log sarrerak",
+       "checkbox-all": "Denak",
+       "checkbox-none": "Bat ere ez",
        "allpages": "Orri guztiak",
        "nextpage": "Hurrengo orrialdea ($1)",
        "prevpage": "Aurreko orrialdea ($1)",
        "activeusers-noresult": "Ez da lankiderik aurkitu.",
        "listgrouprights": "Erabiltzaile talde eskumenak",
        "listgrouprights-summary": "Ondorengo zerrendak wikian dauden lankide taldeak agertzen dira, beraien eskubideekin.\nBadago [[{{MediaWiki:Listgrouprights-helppage}}|informazio osagarria]] banakako eskubideei buruz.",
-       "listgrouprights-key": "* <span class=\"listgrouprights-granted\">Eskubidea emanda</span>\n* <span class=\"listgrouprights-revoked\">Eskubidea kenduta</span>",
+       "listgrouprights-key": "Legenda\n* <span class=\"listgrouprights-granted\">Eskubidea emanda</span>\n* <span class=\"listgrouprights-revoked\">Eskubidea kenduta</span>",
        "listgrouprights-group": "Taldea",
        "listgrouprights-rights": "Eskumenak",
        "listgrouprights-helppage": "Help:Talde eskumenak",
        "listgrouprights-addgroup-self-all": "Talde guztiak norbere kontura gehitu",
        "listgrouprights-removegroup-self-all": "Talde guztiak norbere kontutik ezabatu",
        "listgrouprights-namespaceprotection-namespace": "Izen-tartea",
+       "listgrants": "Diru-laguntzak",
+       "listgrants-rights": "Eskubideak",
        "trackingcategories-name": "Mezuaren izena",
        "trackingcategories-nodesc": "Ez dago deskribapenik eskuragarri.",
        "trackingcategories-disabled": "Kategoria desgaitua dago",
        "wlshowlast": "Erakutsi azken $1 orduak, azken $2 egunak",
        "watchlist-hide": "Ezkutatu",
        "watchlist-submit": "Erakutsi",
-       "wlshowtime": "Erakutsi azkenak:",
+       "wlshowtime": "Erakusteko denboraldia:",
        "wlshowhideminor": "aldaketa txikiak",
        "wlshowhidebots": "bot-ak",
        "wlshowhideliu": "Erregistratutako erabiltzaileak",
        "whatlinkshere-hidelinks": "$1 loturak",
        "whatlinkshere-hideimages": "$1 irudi loturak",
        "whatlinkshere-filters": "Iragazleak",
+       "whatlinkshere-submit": "Joan",
        "autoblockid": "Blokeo automatikoa #$1",
        "block": "Blokeatu erabiltzailea",
        "unblock": "Erabiltzailea desblokeatu",
        "javascripttest-pagetext-frameworks": "Mesedez, aukera ezazu froga eremu hauetako bat: $1",
        "javascripttest-pagetext-skins": "Aukeratu frogak egiteko itxura bat:",
        "javascripttest-qunit-intro": "Ikusi [$1 frogen dokumentazioa] mediawiki.org orrialdean.",
-       "tooltip-pt-userpage": "Nire lankide orria",
+       "tooltip-pt-userpage": "{{GENDER:|Zure lankide}} orria",
        "tooltip-pt-anonuserpage": "Zure IParen lankide orrialdea",
-       "tooltip-pt-mytalk": "Nire eztabaida orria",
+       "tooltip-pt-mytalk": "{{GENDER:|Zure}} eztabaida orria",
        "tooltip-pt-anontalk": "Zure IParen eztabaida",
-       "tooltip-pt-preferences": "Nire hobespenak",
+       "tooltip-pt-preferences": "{{GENDER:|Zure}} hobespenak",
        "tooltip-pt-watchlist": "Jarraitzen dituzun orrialdeen zerrenda.",
        "tooltip-pt-mycontris": "Nire ekarpenen zerrenda",
        "tooltip-pt-anoncontribs": "IP helbide honetatik egindako aldaketen zerrenda",
        "watchlisttools-edit": "Zerrenda ikusi eta aldatu",
        "watchlisttools-raw": "Zerrenda idatziz aldatu",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|eztabaida]])",
+       "timezone-local": "Lokala",
        "duplicate-defaultsort": "Adi: Berezko \"$2\" antolatzeak aurreko berezko \"$1\" antolatzea gainditzen du.",
        "version": "Bertsioa",
        "version-extensions": "Instalatutako luzapenak",
        "pagelang-language": "Hizkuntza",
        "pagelang-use-default": "Hizkuntza lehenetsia erabili",
        "pagelang-select-lang": "Hizkuntza aukeratu",
+       "pagelang-submit": "Bidali",
        "right-pagelang": "Aldatu orrialdearen hizkuntza",
        "action-pagelang": "orrialdearen hizkuntza aldatu",
        "log-name-pagelang": "Hizkuntza aldatu:",
        "mw-widgets-dateinput-no-date": "Ez duzu datarik aukeratu",
        "mw-widgets-titleinput-description-new-page": "orri hori oraindik ez da existitzen",
        "mw-widgets-titleinput-description-redirect": "$1ra birzuzendu",
-       "api-error-blacklisted": "Aukera ezazu, mesedez, izenburu ezberdin eta deskriptiboago bat."
+       "api-error-blacklisted": "Aukera ezazu, mesedez, izenburu ezberdin eta deskriptiboago bat.",
+       "sessionprovider-generic": "$1 sesio"
 }
index cc7dcf8..0ef38b6 100644 (file)
@@ -48,7 +48,8 @@
                        "Macofe",
                        "Danialbehzadi",
                        "MRG90",
-                       "Mahdy Saffar"
+                       "Mahdy Saffar",
+                       "Arian Ar"
                ]
        },
        "tog-underline": "خط کشیدن زیر پیوندها:",
@@ -73,7 +74,7 @@
        "tog-enotifwatchlistpages": "اگر صفحه یا پرونده‌ای از فهرست پی‌گیری‌هایم ویرایش شد به من ایمیلی فرستاده شود",
        "tog-enotifusertalkpages": "هنگامی که در صفحهٔ بحث کاربری‌ام تغییری صورت می‌گیرد به من ایمیلی فرستاده شود",
        "tog-enotifminoredits": "برای تغییرات جزئی در صفحه‌ها و پرونده‌ها هم به من ایمیلی فرستاده شود",
-       "tog-enotifrevealaddr": "Ù\86شاÙ\86Û\8c Ù¾Ø³Øª Ø§Ù\84کترÙ\88Ù\86Û\8cÚ©Û\8c Ù\85Ù\86 Ø±Ø§ Ø¯Ø± Ø§Û\8cÙ\85Û\8cÙ\84â\80\8cÙ\87اÛ\8c Ø§Ø·Ù\84اعâ\80\8cرساÙ\86Û\8c Ù\87Ù\88Û\8cدا Ú¯Ø±Ø¯Ø¯",
+       "tog-enotifrevealaddr": "نشانی پست الکترونیکی من در ایمیل‌های اطلاع‌رسانی هویدا گردد",
        "tog-shownumberswatching": "شمار کاربران پی‌گیرندهٔ نمایش یابد",
        "tog-oldsig": "امضای کنونی:",
        "tog-fancysig": "امضا به صورت ویکی‌متن در نظر گرفته شود (بدون درج خودکار پیوند)",
        "create-this-page": "ایجاد این صفحه",
        "delete": "حذف",
        "deletethispage": "حذف این صفحه",
-       "undeletethispage": "بازگرداÙ\86ی این صفحه",
+       "undeletethispage": "احÛ\8cای این صفحه",
        "undelete_short": "احیای {{PLURAL:$1|یک ویرایش|$1 ویرایش}}",
        "viewdeleted_short": "نمایش {{PLURAL:$1|یک ویرایش حذف‌شده|$1 ویرایش حذف‌شده}}",
        "protect": "محافظت",
        "previewnote": "'''به یاد داشته باشید که این فقط پیش‌نمایش است.'''\nتغییرات شما هنوز ذخیره نشده‌است!",
        "continue-editing": "رفتن به قسمت ویرایش",
        "previewconflict": "این پیش‌نمایش منعکس‌کنندهٔ متن ناحیهٔ ویرایش متن بالایی است، به شکلی که اگر متن را ذخیره کنید نمایش خواهد یافت.",
-       "session_fail_preview": "'''شرمنده! به علت از دست رفتن اطلاعات نشست کاربری نمی‌توانیم ویرایش شما را پردازش کنیم.'''\nلطفاً دوباره سعی کنید.\nاگر دوباره به همین پیام برخوردید از سامانه [[Special:UserLogout|خارج شوید]] و دوباره وارد شوید.",
-       "session_fail_preview_html": "'''متأسفانه امکان ثبت ویرایش شما به خاطر از دست رفتن اطلاعات نشست کاربری وجود ندارد.'''\n\n''با توجه به این که در {{SITENAME}} امکان درج اچ‌تی‌ام‌ال خام فعال است، پیش‌نمایش صفحه پنهان شده تا امکان حملات مبتنی بر جاوااسکریپت وجود نداشته باشد.''\n\n'''اگر مطمئن هستید که این پیش‌نمایش یک ویرایش مجاز است، آن را تکرار کنید.'''\nاگر تکرار پیش‌نمایش نتیجه نداد، از سامانه [[Special:UserLogout|خارج شوید]] و دوباره وارد شوید.",
+       "session_fail_preview": "شرمنده! به علت از دست رفتن اطلاعات نشست کاربری نمی‌توانیم ویرایش شما را پردازش کنیم.\nاحتمالا شما از سامانه خارج شده‌اید.'''لطفا از اینکه وارد سامانه شده‌اید اطمینان حاصل کرده و دوباره امتحان کنید'''.\nاگر دوباره به همین پیام برخوردید از سامانه [[Special:UserLogout|خارج شوید]]، دوباره وارد شوید، و از این‌ که مرورگر شما اجازه دریافت کوکی از این وب‌گاه را می‌دهد اطمینان حاصل کنید.",
+       "session_fail_preview_html": "'''متأسفانه امکان ثبت ویرایش شما به خاطر از دست رفتن اطلاعات نشست کاربری وجود ندارد.'''\n\n''با توجه به این که در {{SITENAME}} امکان درج اچ‌تی‌ام‌ال خام فعال است، پیش‌نمایش صفحه پنهان شده تا امکان حملات مبتنی بر جاوااسکریپت وجود نداشته باشد.''\n\n'''اگر مطمئن هستید که این پیش‌نمایش یک ویرایش مجاز است، آن را تکرار کنید.'''\nاگر تکرار پیش‌نمایش نتیجه نداد، از سامانه [[Special:UserLogout|خارج شوید]] و دوباره وارد شوید و از این‌ که مرورگر شما اجازه دریافت کوکی از این وب‌گاه را می‌دهد اطمینان حاصل کنید.",
        "token_suffix_mismatch": "'''ویرایش شما ذخیره نشد، زیرا مرورگر شما نویسه‌های نقطه‌گذاری را در کد امنیتی ویرایش از هم پاشیده‌است.'''\nویرایش شما مردود شد تا از خراب شدن متن صفحه جلوگیری شود.\nگاهی این اشکال زمانی پیش می‌آید که شما از یک پروکسی تحت وب استفاده کنید.",
        "edit_form_incomplete": "'''بعضی قسمت‌های فرم ویرایش به سرور نرسیدند؛ اطمینان حاصل کنید که ویرایش‌های شما کامل است و دوباره تلاش کنید.'''",
        "editing": "در حال ویرایش $1",
        "mergehistory-empty": "هیچ‌یک از نسخه‌ها قابل ادغام نیستند.",
        "mergehistory-done": "$3 نسخه از $1 در [[:$2]] ادغام شد.",
        "mergehistory-fail": "ادغام تاریخچه ممکن نیست، لطفاً گزینه‌های صفحه و زمان را بازبینی کنید.",
+       "mergehistory-fail-bad-timestamp": "برچسب زمانی نامعتبر است.",
+       "mergehistory-fail-invalid-source": "صفحه مبدا نامعتبر است.",
+       "mergehistory-fail-invalid-dest": "صفحه مقصد نامعتبر است.",
+       "mergehistory-fail-no-change": "ادغام تاریخچه هیچ‌ کدام از  نسخه ها را ادغام نکرد. لطفا پارامتر های زمانی و صفحه را مجددا کنترل کنید.",
+       "mergehistory-fail-permission": "مجوزهای ناکافی برای ادغام تاریخچه.",
+       "mergehistory-fail-self-merge": "صفحهٔ مبدأ و مقصد یکی است.",
+       "mergehistory-fail-timestamps-overlap": "نسخه های منبع هم‌پوشانی دارند یا بعد از نسخه های مقصد آمده‌اند.",
        "mergehistory-fail-toobig": "نمی‌توان ادغام تاریخچه را انجام داد که بیشتر از محدودیت $1 {{PLURAL:$1|نسخه}} انتقال داده خواهد شد.",
        "mergehistory-no-source": "صفحهٔ مبدأ $1 وجود ندارد.",
        "mergehistory-no-destination": "صفحهٔ مقصد $1 وجود ندارد.",
        "uploaded-script-svg": "عنصر قابل برنامه‌ریزی «$1» در پرونده بارگذاری اس‌وی‌جی یافت شد.",
        "uploaded-hostile-svg": "سی‌اس‌اس نا امن در عنصر سبک پروندهٔ بارگذاری شدهٔ اس‌وی‌جی یافت شد.",
        "uploaded-event-handler-on-svg": "قرار دادن ویژگی‌های مدیریت رویداد <code>$1=\"$2\"</code> در پرونده‌های اس‌وی‌جی مجاز نیست.",
-       "uploaded-href-unsafe-target-svg": "در پرونده SVG بارگذاری‌شده برای هدف نادرست <code>&lt;$1 $2=\"$3\"&gt;</code> برچسب href یافت شد.",
+       "uploaded-href-attribute-svg": "ویژگی‌های href در پرونده‌های SVG فقط برای اهدافhttp:// &lrm; وhttps:// &lrm; مجاز هستند، <code>&lt;$1 $2=\"$3\"&gt;</code> یافت شد.",
+       "uploaded-href-unsafe-target-svg": "در پرونده SVG بارگذاری‌شده برای نشانی هدف <code>&lt;$1 $2=\"$3\"&gt;</code> برچسب href به اطلاعات ناامن یافت شد.",
        "uploaded-animate-svg": "برچسب  \"animate\" یافت شده ممکن است herf را تغییر دهد. از مشخصه \"from\" <code>&lt;$1 $2=\"$3\"&gt;</code> در پرونده SVG بارگذاری‌شده استفاده کنید.",
        "uploaded-setting-event-handler-svg": "تنظیمات مشخصه گرداننده رویداد بسته شده‌است. کد <code>&lt;$1 $2=\"$3\"&gt;</code>  در پرونده بارگذاری‌شده یافت شد.",
        "uploaded-setting-href-svg": "استفاده از برچسب \"set\" برای افزودن مشخصهٔ \"href\" به عنصر والد بسته شده",
        "upload-too-many-redirects": "نشانی اینترتی حاوی تعداد بیش از اندازه‌ای تغییرمسیر است",
        "upload-http-error": "یک خطای اچ‌تی‌تی‌پی رخ داد: $1",
        "upload-copy-upload-invalid-domain": "بارگذاری کپی پرونده‌ها از این دامنه امکان‌پذیر نیست.",
+       "upload-foreign-cant-upload": "این ویکی برای بارگذاری پرونده ها در مخزن پرونده های خارجی درخواست شده پیکربندی نشده است.",
        "upload-dialog-title": "بارگذاری پرونده",
        "upload-dialog-button-cancel": "لغو",
        "upload-dialog-button-done": "انجام شد",
        "upload-dialog-button-save": "ذخیره",
        "upload-dialog-button-upload": "بارگذاری",
-       "upload-form-label-select-file": "یک فایل انتخاب کنید",
        "upload-form-label-infoform-title": "جزئیات",
        "upload-form-label-infoform-name": "نام",
        "upload-form-label-infoform-name-tooltip": "یک عنوان توضیحی برای پرونده که به عنوان نام پرونده استفاده می‌شود. ممکن است از زبان ساده همرا با فاصله استفاده شود. شامل پسوند پرونده نباشد.",
        "foreign-structured-upload-form-label-own-work-message-shared": "تصدیق می‌کنم که مالک حق تکثیر این پرونده هستم و موافق اشتراک‌گذاری آن تحت مجوز [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0] هستم و موافق [https://wikimediafoundation.org/wiki/Terms_of_Use سیاست نحوهٔ استفاده] هستم.",
        "foreign-structured-upload-form-label-not-own-work-message-shared": "اگر مالک حق تکثیر این پرونده نیستید یا قصد بارگذاری تحت مجوز دیگری دارید، از [https://commons.wikimedia.org/wiki/Special:UploadWizard جادوگر بارگذاری ویکی‌انبار] استفاده کنید.",
        "foreign-structured-upload-form-label-not-own-work-local-shared": "در صورتی که سایت امکان بارگذاری پرونده را تحت سیاست‌ها بارگذاری می‌دهد ممکن است بخواهید از [[Special:Upload|پنجرهٔ بارگذاری در {{SITENAME}}]] استفاده کنید.",
-       "foreign-structured-upload-form-2-label-intro": "از این که تصویری را واگذار می‌کنید تا در {{SITENAME}} استفاده شود متشکریم. شما باید این کار را تنها در صورتی انجام دهید که چندین شرط برقرار باشد:",
-       "foreign-structured-upload-form-2-label-ownwork": "باید تماماً <strong>کار خود شما </strong> باشد، نه این که از اینترنت برداشته باشید",
-       "foreign-structured-upload-form-2-label-noderiv": "باید حاوی چیزی که <strong>دیگران خلق کرده باشند نباشد<strong>، و یا متاثر از اثر کسی دیگر نباشد",
-       "foreign-structured-upload-form-2-label-useful": "این باید <strong>مفید و دانشورانه</strong> برای تدریس به دیگران باشد.",
-       "foreign-structured-upload-form-2-label-ccbysa": "باید <strong>بشود برای همیشه</strong> آن را در اینترنت با مجوز [https://creativecommons.org/licenses/by-sa/4.0/ عامه خلاق با ذکر صاحب اثر و نشر بدون تغییر نسخه ۴٫۰] منتشر کرد",
-       "foreign-structured-upload-form-2-label-alternative": "اگر تمام شرایط بالا برقرار نیست، شما ممکن است کماکان بتوانید آن را از طریق [https://commons.wikimedia.org/wiki/Special:UploadWizard جادوگر بارگذاری ویکی‌انبار] بارگذاری کنید، به شرط آن که تحت یک مجوز آزاد باشد.",
-       "foreign-structured-upload-form-2-label-termsofuse": "با بارگذاری پرونده، شما تایید می‌کنید که صاحب حق تکثیر این پرونده هستید، و قبول می‌کنید که حقوق آن را طبق مجوز عامه خلاق با ذکر صاحب اثر و نشر بدون تغییر نسخه  ۴٫۰ و به صورت غیر قابل برگشت به ویکی‌انبار ببخشید، و نیز [https://wikimediafoundation.org/wiki/Terms_of_Use قوانین استفاده] ویکی‌مدیا را می‌پذیرید.",
-       "foreign-structured-upload-form-3-label-question-website": "آیا شما این تصویر را از یک وب‌سایت دانلود کرده‌اید یا از یک سرویس جستجوی تصویر استفاده کردید؟",
-       "foreign-structured-upload-form-3-label-question-ownwork": "آیا این تصویر را خودتان تولید کردید؟ (عکس گرفتن، طراحی با دست و غیره)",
-       "foreign-structured-upload-form-3-label-question-noderiv": "آیا این اثر متعلق یا مشتق شده از اثر فرد دیگری است مانند نشان؟",
-       "foreign-structured-upload-form-3-label-yes": "بله",
-       "foreign-structured-upload-form-3-label-no": "خیر",
-       "foreign-structured-upload-form-3-label-alternative": "متاسفانه در این شرایط این ابزار از بارگذاری این پرونده پشتیبانی نمی‌کند.  شما ممکن است کماکان بتوانید آن را از طریق [https://commons.wikimedia.org/wiki/Special:UploadWizard جادوگر بارگذاری ویکی‌انبار] بارگذاری کنید، به شرط آن که تحت یک مجوز آزاد باشد.",
-       "foreign-structured-upload-form-4-label-good": "با استفاده از این ابزار شما می‌توانید تصاویر آموزشی که خود ساخته‌اید یا خودتان عکاسی کرده‌اید را بارگذاری کنید، مادامی که حاوی اثری که دیگری تولید کرده نباشند.",
-       "foreign-structured-upload-form-4-label-bad": "شما نمی‌توانید تصویر بدست آمده از جستجو در موتورهای جستجو یا متعلق به سایر وب‌گاه‌ها را بارگذاری کنید.",
        "backend-fail-stream": "نمی‌توان پروندهٔ $1 را ارسال کرد.",
        "backend-fail-backup": "نمی‌توان نسخهٔ پشتیبان برای پروندهٔ $1 ایجاد کرد.",
        "backend-fail-notexists": "پروندهٔ $1 وجود ندارد.",
        "querypage-disabled": "این صفحه ویژه به دلایل عملکردی غیرفعال شده‌است.",
        "apihelp": "راهنمای API",
        "apihelp-no-such-module": "پودمان \" $1 \" یافت نشد.",
+       "apisandbox": "گودال ماسه‌بازی رابط برنامه‌نویسی",
+       "apisandbox-jsonly": "برای استفاده از صفحهٔ تمرین API به جاوااسکریپت نیاز دارید.",
+       "apisandbox-api-disabled": "رابط برنامه‌نویسی در این تارنما غیرفعال شده‌است.",
+       "apisandbox-intro": "از این صفحه برای آزمایش <strong>خدمات وب API مدیاویکی</strong> استفاده کنید.\nبرای جزئیات بیشتر دربارهٔ نحوهٔ استفاده از API به [[mw:API:Main page|مستندات API]] رجوع کنید. مثال: [//www.mediawiki.org/wiki/API#A_simple_example دریافت محتوای صفحهٔ اصلی]. برای دیدن مثال‌های بیشتر عملکردی را انتخاب کنید.",
+       "apisandbox-fullscreen": "گسترش پنل",
+       "apisandbox-fullscreen-tooltip": "بازکردن صفحهٔ تمرین برای پر کردن پنجرهٔ مرورگر.",
+       "apisandbox-unfullscreen": "نمایش صفحه",
+       "apisandbox-unfullscreen-tooltip": "بستن پانل تمرین، در نتیجه پیوندهای هدایت مدیاویکی در دسترس هستند.",
+       "apisandbox-submit": "ایجاد درخواست",
+       "apisandbox-reset": "پاک‌کردن",
+       "apisandbox-retry": "تلاش مجدد",
+       "apisandbox-loading": "بارگیری اطلاعات برای ماژول \"$1\" ...",
+       "apisandbox-load-error": "در زمان بارگیری اطلاعات برای ماژول \"$1\" خطایی رخ داده‌است: $2",
+       "apisandbox-no-parameters": "این ماژول API پارامتری ندارد.",
+       "apisandbox-helpurls": "پیوندهای راهنمایی",
+       "apisandbox-examples": "مثال‌ها",
+       "apisandbox-dynamic-parameters": "پارامترهای بیشتر",
+       "apisandbox-dynamic-parameters-add-label": "افزودن پارامتر:",
+       "apisandbox-dynamic-parameters-add-placeholder": "نام پارامتر",
+       "apisandbox-dynamic-error-exists": "پارامتری به نام \"$1\"هم اکنون وجود دارد.",
+       "apisandbox-deprecated-parameters": "پارامتر های نامناسب",
+       "apisandbox-fetch-token": "پرکردن خودکار توکن",
+       "apisandbox-submit-invalid-fields-title": "بعضی از بخش‌ها نامعتبر هستند.",
+       "apisandbox-submit-invalid-fields-message": "لطفا موارد مشخص شده را اصلاح کرده و دوباره امتحان کنید.",
+       "apisandbox-results": "نتیجه‌ها",
+       "apisandbox-sending-request": "ارسال درخواست ای‌پی‌آی...",
+       "apisandbox-loading-results": "دریافت درخواست‌های ای‌پی‌آی...",
+       "apisandbox-results-error": "در زمان بارگیری پاسخ کوئری API خطایی رخ داده‌است: $1.",
+       "apisandbox-request-url-label": "درخواست آدرس:",
+       "apisandbox-request-time": "زمان درخواست: {{PLURAL:$1|$1 ms}}",
+       "apisandbox-results-fixtoken": "توکن را اصلاح کنید و از نو ارسال کنید",
+       "apisandbox-results-fixtoken-fail": "خطا در دریافت توکن \"$1\"",
+       "apisandbox-alert-page": "بخش‌ها در این صفحه معتبر نیستند.",
+       "apisandbox-alert-field": "مقدار این بخش معتبر نیست.",
        "booksources": "منابع کتاب",
        "booksources-search-legend": "جستجوی منابع کتاب",
        "booksources-isbn": "شابک:",
        "lockedbyandtime": "(به وسیلهٔ $1 در $2 ساعت $3)",
        "move-page": "انتقال $1",
        "move-page-legend": "انتقال صفحه",
-       "movepagetext": "با استفاده از فرم زیر نام صفحه تغییر خواهد کرد، و تمام تاریخچه‌اش به نام جدید منتقل خواهد شد.\nعنوان قدیمی تبدیل به یک صفحهٔ تغییرمسیر به عنوان جدید خواهد شد.\nشما می‌توانید تغییرمسیرهایی که به عنوان اصلی اشاره دارند را به صورت خودکار به‌روزرسانی کنید.\nپیوندهای که به عنوان صفحهٔ قدیمی وجود دارند، تغییر نخواهند کرد؛ حتماً تغییرمسیرهای [[Special:DoubleRedirects|دوتایی]] یا [[Special:BrokenRedirects|خراب]] را بررسی کنید.\n'''شما''' مسئول اطمینان از این هستید که پیوندها هنوز به همان‌جایی که قرار است بروند.\n\nتوجه کنید که اگر از قبل صفحه‌ای در عنوان جدید وجود داشته باشد صفحه منتقل '''نخواهد شد'''،\nمگر این آخرین ویرایش تغییرمسیر باشد و در  تاریخچهٔ ویرایشی نداشته باشد.\nاین یعنی اگر اشتباه کردید می‌توانید صفحه را به همان جایی که از آن منتقل شده بود برگردانید، و این که نمی‌توانید روی صفحات موجود بنویسید.\n\n'''هشدار!'''\nانتقال صفحات به نام جدید ممکن است تغییر اساسی و غیرمنتظره‌ای برای صفحات محبوب باشد؛\nلطفاً مطمئن شوید که قبل از انتقال دادن صفحه، عواقب این کار را درک می‌کنید.",
-       "movepagetext-noredirectfixer": "استفاده از فرم زیر سبب تغییر نام یک صفحه و انتقال تمام تاریخچهٔ آن به نام جدید می‌شود.\nعنوان پیشین تغییرمسیری به عنوان جدید خواهد شد.\nبه خاطر داشته باشید که [[Special:DoubleRedirects|تغییرمسیرهای دوتایی]] یا [[Special:BrokenRedirects|تغییرمسیرهای خراب]] را بررسی کنید.\nشما مسئولید که مطمئن شوید پس از انتقال، پیوندها به عنوان پیشین به جایی منتهی می‌شوند که باید.\n\nتوجه کنید که اگر صفحه‌ای تحت عنوان جدید از قبل موجود باشد، انتقال انجام '''نخواهد شد'''، مگر اینکه صفحه خالی و یا تغییرمسیر باشد و تاریخچهٔ ویرایشی دیگری نداشته باشد.\nاین یعنی اگر صفحه را به نامی اشتباه منتقل کردید می‌توانید این تغییر را واگردانی کنید، اما نمی‌توانید یک صفحه را به صفحه‌ای که از قبل موجود است انتقال دهید.\n\n'''هشدار!'''\nانتقال صفحه‌های پربیننده ممکن است عملی غیرمنتظره باشد؛\nلطفاً پیش از انتقال مطمئن شوید از نتیجهٔ کار آگاهید.",
+       "movepagetext": "با استفاده از فرم زیر نام صفحه تغییر خواهد کرد، و تمام تاریخچه‌اش به نام جدید منتقل خواهد شد.\nعنوان قدیمی تبدیل به یک صفحهٔ تغییرمسیر به عنوان جدید خواهد شد.\nشما می‌توانید تغییرمسیرهایی که به عنوان اصلی اشاره دارند را به صورت خودکار به‌روزرسانی کنید.\nپیوندهای که به عنوان صفحهٔ قدیمی وجود دارند، تغییر نخواهند کرد؛ حتماً تغییرمسیرهای [[Special:DoubleRedirects|دوتایی]] یا [[Special:BrokenRedirects|خراب]] را بررسی کنید.\n'''شما''' مسئول اطمینان از این هستید که پیوندها هنوز به همان‌جایی که قرار است بروند.\n\nتوجه کنید که اگر از قبل صفحه‌ای در عنوان جدید وجود داشته باشد صفحه منتقل '''نخواهد شد'''،\nمگر این آخرین ویرایش تغییرمسیر باشد و در  تاریخچهٔ ویرایشی نداشته باشد.\nاین یعنی اگر اشتباه کردید می‌توانید صفحه را به همان جایی که از آن منتقل شده بود برگردانید، و این که نمی‌توانید روی صفحات موجود بنویسید.\n\n<strong>توضیح:</strong>\nانتقال صفحات به نام جدید ممکن است تغییر اساسی و غیرمنتظره‌ای برای صفحات محبوب باشد؛\nلطفاً مطمئن شوید که قبل از انتقال دادن صفحه، عواقب این کار را درک می‌کنید.",
+       "movepagetext-noredirectfixer": "استفاده از فرم زیر سبب تغییر نام یک صفحه و انتقال تمام تاریخچهٔ آن به نام جدید می‌شود.\nعنوان پیشین تغییرمسیری به عنوان جدید خواهد شد.\nبه خاطر داشته باشید که [[Special:DoubleRedirects|تغییرمسیرهای دوتایی]] یا [[Special:BrokenRedirects|تغییرمسیرهای خراب]] را بررسی کنید.\nشما مسئولید که مطمئن شوید پس از انتقال، پیوندها به عنوان پیشین به جایی منتهی می‌شوند که باید.\n\nتوجه کنید که اگر صفحه‌ای تحت عنوان جدید از قبل موجود باشد، انتقال انجام '''نخواهد شد'''، مگر اینکه صفحه خالی و یا تغییرمسیر باشد و تاریخچهٔ ویرایشی دیگری نداشته باشد.\nاین یعنی اگر صفحه را به نامی اشتباه منتقل کردید می‌توانید این تغییر را واگردانی کنید، اما نمی‌توانید یک صفحه را به صفحه‌ای که از قبل موجود است انتقال دهید.\n\n<strong>توضیح:</strong>\nانتقال صفحه‌های پربیننده ممکن است عملی غیرمنتظره باشد؛\nلطفاً پیش از انتقال مطمئن شوید از نتیجهٔ کار آگاهید.",
        "movepagetalktext": "اگر این گزینه را انتخاب کنید، صفحهٔ بحث مرتبط به صورت خودکار انتقال داده می‌شود به عنوان جدید و در صورت عدم انتخاب گزینه، صفحهٔ بحث جدید یک صفحهٔ خالی خواهد بود و در این حالت، باید صفحه را بطور دستی انتقال داده و یا محتویات دو صفحه را با ویرایش ادغام کنید.",
        "moveuserpage-warning": "'''هشدار:''' شما در حال انتقال دادن یک صفحهٔ کاربر هستید. توجه داشته باشید که تنها صفحه منتقل می‌شود و نام کاربر تغییر '''نمی‌یابد'''.",
        "movecategorypage-warning": "<strong>هشدار:</strong> شما در حال انتقال صفحه رده هستید. لطفاً توجه داشته باشید که فقط صفحه منتقل خواهد شد و  صفحات در رده قدیمی می‌مانند و به رده جدید <em>نمی‌روند</em>.",
        "movenosubpage": "این صفحه هیچ زیرصفحه‌ای ندارد.",
        "movereason": "دلیل:",
        "revertmove": "واگردانی",
-       "delete_and_move_text": "== نیاز به حذف ==\n\nمقالهٔ مقصد «[[:$1]]» وجود دارد. آیا می‌خواهید آن را حذف کنید تا انتقال ممکن شود؟",
+       "delete_and_move_text": "مقالهٔ مقصد «[[:$1]]» وجود دارد. آیا می‌خواهید آن را حذف کنید تا انتقال ممکن شود؟",
        "delete_and_move_confirm": "بله، صفحه حذف شود",
        "delete_and_move_reason": "حذف برای ممکن‌شدن انتقال  «[[$1]]»",
        "selfmove": "عنوان‌های صفحهٔ مبدأ و مقصد یکی است؛\nانتقال صفحه به خودش ممکن نیست.",
        "move-leave-redirect": "بر جا گذاشتن یک تغییرمسیر",
        "protectedpagemovewarning": "'''هشدار:''' این صفحه قفل شده‌است به طوری که تنها کاربران با دسترسی مدیریت می‌توانند آن را انتقال دهند.\nآخرین موارد سیاهه در زیر آمده است:",
        "semiprotectedpagemovewarning": "'''تذکر:''' این صفحه قفل شده‌است به طوری که تنها کاربران ثبت نام کرده می‌توانند آن را انتقال دهند.\nآخرین موارد سیاهه در زیر آمده است:",
-       "move-over-sharedrepo": "== پرونده موجود است ==\n[[:$1]] در یک مخزن مشترک وجود دارد. انتقال یک پرونده به این نام باعث باطل شدن پرونده مشترک خواهد شد.",
+       "move-over-sharedrepo": "[[:$1]] در یک مخزن مشترک وجود دارد. انتقال یک پرونده به این نام باعث باطل شدن پرونده مشترک خواهد شد.",
        "file-exists-sharedrepo": "نام پرونده انتخاب شده از قبل در یک مخزن مشترک استفاده شده‌است.\nلطفاً یک نام دیگر برگزینید.",
        "export": "برون‌بری صفحات",
        "exporttext": "شما می‌توانید متن و تاریخچهٔ ویرایش یک صفحهٔ مشخص یا مجموعه‌ای از صفحات را به شکل پوشیده در اکس‌ام‌ال برون‌بری کنید.\nاین اطلاعات را می‌توان در ویکی دیگری که نرم‌افزار «مدیاویکی» را اجرا می‌کند از طریق [[Special:Import|صفحهٔ درون‌ریزی]] وارد کرد.\n\nبرای برون‌بری صفحات، عنوان آن‌ها را در جعبهٔ زیر وارد کنید (در هر سطر فقط یک عنوان) و مشخص کنید که آیا نسخهٔ اخیر صفحه را به همراه نسخه‌های قدیمی‌تر و تاریخچهٔ صفحه می‌خواهید، یا تنها نسخهٔ اخیر صفحه و اطلاعات آخرین ویرایش را می‌خواهید.\n\nدر حالت دوم، شما می‌توانید از یک پیوند استفاده کنید، مثلاً [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]] برای صفحهٔ «[[{{MediaWiki:Mainpage}}]]».",
        "import-nonewrevisions": "نسخه‌ای درون‌ریزی نشد (همه یا در حال حاضر وجود دارند، یا به دلیل خطا‌ها نادیده گرفته شده‌اند).",
        "xml-error-string": "$1 در سطر $2، ستون $3 (بایت $4): $5",
        "import-upload": "بارگذاری داده اکس‌ام‌ال",
-       "import-token-mismatch": "از دست رفتن اطلاعات نشست کاربری. لطفاً دوباره امتحان کنید.",
+       "import-token-mismatch": "از دست رفتن اطلاعات نشست کاربری.\n\nاحتمالا شما از سامانه خارج شده‌اید.'''لطفا از اینکه وارد سامانه شده‌اید اطمینان حاصل کرده و دوباره امتحان کنید'''.\nاگر دوباره به همین پیام برخوردید از سامانه [[Special:UserLogout|خارج شوید]]، دوباره وارد شوید، و از این‌ که مرورگر شما اجازه دریافت کوکی از این وب‌گاه را می‌دهد اطمینان حاصل کنید.",
        "import-invalid-interwiki": "از ویکی مشخص شده نمی‌توان درون‌ریزی انجام داد.",
        "import-error-edit": "صفحهٔ «$1» وارد نشد، چون شما مجاز به ویرایش آن نیستید.",
        "import-error-create": "صفحهٔ «$1» وارد نشد، چون شما مجاز به ایجاد آن نیستید.",
        "lastmodifiedatby": "این صفحه آخرین بار در $2، $1 به دست $3 تغییر یافته‌است.",
        "othercontribs": "بر اساس اثری از $1",
        "others": "دیگران",
-       "siteusers": "$1، {{PLURAL:$2|کاربر|کاربران}} {{SITENAME}}",
+       "siteusers": "{{SITENAME}}{{PLURAL:$2|{{GENDER:$1|کاربر}}|کاربر}} $1",
        "anonusers": "$1 {{PLURAL:$2|کاربر|کاربران}} ناشناس {{SITENAME}}",
        "creditspage": "اعتبارات این صفحه",
        "nocredits": "اطلاعات سازندگان این صفحه موجود نیست.",
        "version-poweredby-others": "دیگران",
        "version-poweredby-translators": "مترجمان translatewiki.net",
        "version-credits-summary": "افراد زیر را به خاطر ویرایش‌هایش در [[Special:Version|مدیاویکی]] معرفی می‌نمائیم.",
-       "version-license-info": "مدیاویکی یک نرم‌افزار آزاد است. می‌توانید آن را با شرایط نگارش ۲، یا (با نظر خودتان) هر نگارش جدیدتری از پروانه جامع همگانی گنو که توسط بنیاد نرم‌افزار آزاد منتشر شده‌است، بازنشر کنید.\n\nمدیاویکی با این امید که مفید واقع شود منتشر شده‌است، ولی هیچ‌گونه ضمانتی، حتا ضمانت ضمنی تجاری یا مناسب بودن برای یک مصرف خاص را ارائه نمی‌کند. برای اطلاعات بیش‌تر، پروانه جامع همگانی گنو را مشاهده کنید.\n\nشما باید [{{SERVER}}{{SCRIPTPATH}}/COPYING یک نسخه از پروانه جامع همگانی گنو] را به همراه این برنامه دریافت کرده باشید. در غیر این صورت با Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA یا آن را [//www.gnu.org/licenses/old-licenses/gpl-2.0.html مکاتبه کرده یا آن را به صورت برخط بخوانید].",
+       "version-license-info": "مدیاویکی یک نرم‌افزار آزاد است. می‌توانید آن را با شرایط نگارش ۲، یا (با نظر خودتان) هر نگارش جدیدتری از پروانه جامع همگانی گنو که توسط بنیاد نرم‌افزار آزاد منتشر شده‌است، بازنشر کنید.\n\nمدیاویکی با این امید که مفید واقع شود منتشر شده‌است، ولی هیچ‌گونه ضمانتی، حتی ضمانت ضمنی تجاری یا مناسب بودن برای یک مصرف خاص را ارائه نمی‌کند. برای اطلاعات بیش‌تر، پروانه جامع همگانی گنو را مشاهده کنید.\n\nشما باید [{{SERVER}}{{SCRIPTPATH}}/COPYING یک نسخه از پروانه جامع همگانی گنو] را به همراه این برنامه دریافت کرده باشید. در غیر این صورت با Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA یا آن را [//www.gnu.org/licenses/old-licenses/gpl-2.0.html مکاتبه کرده یا آن را به صورت برخط بخوانید].",
        "version-software": "نسخهٔ نصب‌شده",
        "version-software-product": "محصول",
        "version-software-version": "نسخه",
        "expand_templates_generate_xml": "نمایش درخت تجزیهٔ XML",
        "expand_templates_generate_rawhtml": "نمایش اچ‌تی‌ام‌ال خام",
        "expand_templates_preview": "پیش‌نمایش",
-       "expand_templates_preview_fail_html": "<em>زیرا {{SITENAME}} تا به HTML خام فعال و یک دست رفتن اطلاعات نشست وجود دارد، پیش نمایش به عنوان یک اقدام احتیاطی در برابر حملات جاوا اسکریپت پنهان است.</em>\n\n<strong>اگر این تلاش پیشنمایش مشروع است، لطفا دوباره سعی کنید. اگر هنوز کار نمی کند، سعی کنید [[Special:UserLogout|خروج از سیستم]] را کلیک نموده و دوباره وارد شوید.",
+       "expand_templates_preview_fail_html": "<em>زیرا {{SITENAME}} تا به HTML خام فعال و یک دست رفتن اطلاعات نشست وجود دارد، پیش نمایش به عنوان یک اقدام احتیاطی در برابر حملات جاوا اسکریپت پنهان است.</em>\n\n<strong>اگر این تلاش پیش‌نمایش مشروع است، لطفا دوباره سعی کنید. اگر هنوز کار نمی کند، سعی کنید [[Special:UserLogout|خروج از سیستم]] را کلیک نموده و دوباره وارد شوید، و از این‌ که مرورگر شما اجازه دریافت کوکی از این وب‌گاه را می‌دهد اطمینان حاصل کنید.",
        "expand_templates_preview_fail_html_anon": "<em>زیرا {{SITENAME}} تا به HTML خام فعال و یک دست رفتن اطلاعات نشست وجود دارد، پیش نمایش به عنوان یک اقدام احتیاطی در برابر حملات جاوا اسکریپت پنهان است.</em>\n\n<strong>اگر این تلاش پیشنمایش مشروع است، لطفا دوباره سعی کنید. اگر هنوز کار نمی کند، سعی کنید [[Special:UserLogout|خروج از سیستم]] را کلیک نموده و دوباره وارد شوید.",
        "expand_templates_input_missing": "شما نیازمندید که حداقل متن‌هایی را برای وارد کردن تهیه کنید.",
-       "pagelanguage": "صÙ\81Ø­Ù\87 Ø§Ù\86تخاب Ø²Ø¨Ø§Ù\86",
+       "pagelanguage": "تغÛ\8cÛ\8cر Ø²Ø¨Ø§Ù\86 ØµÙ\81Ø­Ù\87",
        "pagelang-name": "صفحه",
        "pagelang-language": "زبان",
        "pagelang-use-default": "استفاده از زبان پیش‌فرض",
        "pagelang-submit": "اعمال",
        "right-pagelang": "تغییر صفحهٔ زبان",
        "action-pagelang": "تغییر زبان صفحه",
-       "log-name-pagelang": "تغÛ\8cÛ\8cر Ø³Û\8cاÙ\87Ù\87Ù\94 زبان",
+       "log-name-pagelang": "سÛ\8cاÙ\87Ù\87Ù\94 ØªØºÛ\8cÛ\8cر زبان",
        "log-description-pagelang": "این سیاههٔ تغییرات صفحهٔ زبان‌ها است.",
-       "logentry-pagelang-pagelang": "$1 {{GENDER:$2| تغییریافت}} زبان صفحه برای  $3  از  $4  به  $5 .",
+       "logentry-pagelang-pagelang": "$1 زبان $3  از  $4  به  $5 {{GENDER:$2| تغییریافت}}",
        "default-skin-not-found": "اوه! پوسته پیش‌فرض برای ویکی شما تعریف‌شده در <code dir=\"ltr\"<$wgDefaultSkin</code> به عنوان <code>$1</code>، در دسترس نیست.\n\nبه نظر می‌آید نصب شما شامل پوسته‌های زیر می‌شود. [https://www.mediawiki.org/wiki/Manual:Skin_configuration راهنما: تنظیمات پوسته] را برای کسب اطلاعات در باره چگونگی فعال‌ساختن آن‌ها و انتخاب پیش‌فرض ببینید.\n\n$2\n\n; اگر اخیراً مدیاویکی را نصب کرده‌اید:\n: احتمالاً از گیت، یا به طور مستقیم از کد مبدأ که از چند متد دیگر استفاده می‌کند نصب کردید. انتظار می‌رود. چند {{PLURAL:$4|پوسته|پوسته}} از [https://www.mediawiki.org/wiki/Category:All_skins فهرست پوسته mediawiki.org] نصب کنید، که همراه چندین پوسته و افزونه هستند. شما می‌توانید شاخه <code>skins/</code> را از آن نسخه‌برداری کرده و بچسبانید.\n\n:* [https://www.mediawiki.org/wiki/Download_from_Git#Using_Git_to_download_MediaWiki_skins استفاده از گیت برای دریافت پوسته‌ها].\n: انجام این کار با مخزن گیت‌تان تداخل نمی‌کند اگر توسعه‌دهنده مدیاویکی هستید.\n\n; اگر اخیراً مدیاویکی را ارتقاء دادید:\n: مدیاویکی ۱٫۲۴ و تازه‌تر دیگر به طور خودکار پوسته‌های نصب‌شده را فعال نمی‌کند ([https://www.mediawiki.org/wiki/Manual:Skin_autodiscovery راهنما: کشف خودکار پوسته] را ببینید). شما می‌توانید خطوط زیر را به داخل <code>LocalSettings.php</code> بچسبانید تا {{PLURAL:$5|همه|همه}} پوسته‌های نصب‌شده را فعال کنید:\n\n<pre dir=\"ltr\">$3</pre>\n\n; اگر اخیراً <code>LocalSettings.php</code> را تغییر دادید:\n: نام پوسته‌ها را برای غلط املایی دوباره بررسی کنید.",
        "default-skin-not-found-no-skins": "پوستهٔ پیش‌فرض برای ویکی شما تعریف‌شده در<code>$wgDefaultSkin</code> به عنوان <code>$1</code>، هست موجود نیست.\n\nشما پوسته‌ها را نصب نکرده‌اید.\n\n:اگر مدیاویکی را به‌روز یا نصب کرده‌اید:\n:ممکن است از گیت یا از کد منبع با روش‌های دیگر نصب کرده‌اید. انتظار می‌رود MediaWiki 1.24 یا جدیدتر در پوشهٔ اصلی هیچ پوسته‌ای نداشته باشند.\nسعی کنید تعدادی پوسته از [https://www.mediawiki.org/wiki/Category:All_skins پوشهٔ پوسته‌های مدیاویکی]، با:\n:*دریافت [https://www.mediawiki.org/wiki/Download نصب‌کننده تاربال]، که با چندین پوسته و افزونه هست. شما می توانید پوستهٔ <code>skins/</code> را از آن کپی و پیست کنید.\n:*کلون کردن یکی از <code dir=\"ltr\">mediawiki/skins/*</code> از مخزن در پوشهٔ <code>skins/</code> مدیاویکی‌تان.\n:اگر توسعه‌دهندهٔ مدیاویکی هستید، انجام این کار نباید تعارضی با مخزن گیت شما داشته باشد. برای اطلاعات بیشتر و فعال کردن پوسته‌ها و انتخاب آنها به عنوان پیش‌فرض [https://www.mediawiki.org/wiki/Manual:Skin_configuration Manual: تنظیمات پوسته] را مشاهده کنید.",
        "default-skin-not-found-row-enabled": "* <code>$1</code> / $2 (فعال)",
index 8fc2a21..5d5dd0c 100644 (file)
@@ -85,7 +85,7 @@
        "tog-watchlistreloadautomatically": "Päivitä tarkkailulista automaattisesti aina kun jotakin suodatinta on muutettu (vaatii JavaScriptiä)",
        "tog-watchlisthideanons": "Piilota rekisteröitymättömien käyttäjien muokkaukset tarkkailulistalta",
        "tog-watchlisthidepatrolled": "Piilota muutostentarkastajien hyväksymät muokkaukset tarkkailulistalta",
-       "tog-watchlisthidecategorization": "Piilota muutokset, jotka koskevat sivujeen lisäämistä tai poistamista luokkiin",
+       "tog-watchlisthidecategorization": "Piilota sivujen luokitusmuutokset",
        "tog-ccmeonemails": "Lähetä minulle kopio MediaWikin kautta lähetetyistä sähköposteista",
        "tog-diffonly": "Älä näytä sivun sisältöä eroavaisuusvertailun alapuolella",
        "tog-showhiddencats": "Näytä piilotetut luokat",
        "changepassword-throttled": "Olet tehnyt liian monta äskettäistä kirjautumisyritystä.\nOdota $1 ennen kuin yrität uudelleen.",
        "botpasswords": "Botin salasanat",
        "botpasswords-disabled": "Botin salasanat on poistettu käytöstä.",
+       "botpasswords-label-create": "Luo",
+       "botpasswords-label-update": "Päivitä",
+       "botpasswords-label-cancel": "Peruuta",
+       "botpasswords-label-delete": "Poista",
+       "botpasswords-label-resetpassword": "Uudista salasana",
+       "botpasswords-label-grants": "Valittavissa olevat toimintaoikeudet:",
        "resetpass_forbidden": "Salasanoja ei voi vaihtaa.",
        "resetpass-no-info": "Et voi nähdä tätä sivua kirjautumatta sisään.",
        "resetpass-submit-loggedin": "Muuta salasana",
        "mergehistory-empty": "Mitään versioita ei voida yhdistää.",
        "mergehistory-done": "$3 {{PLURAL:$3|versio|versiota}} sivusta $1 yhdistettiin onnistuneesti sivuun [[:$2]].",
        "mergehistory-fail": "Sivuhistorioiden yhdistämistä ei voida suorittaa. Tarkista lähde- ja kohdesivujen nimet sekä versioiden aikamääritys.",
+       "mergehistory-fail-bad-timestamp": "Aikaleima ei ole kelvollinen.",
+       "mergehistory-fail-invalid-source": "Lähdesivu ei ole kelvollinen.",
+       "mergehistory-fail-invalid-dest": "Kohdesivu ei ole kelvollinen.",
+       "mergehistory-fail-no-change": "Sivuhistorian yhdistämistoiminto ei yhdistänyt mitään versioita. Katso, ovatko sivun ja ajankohdan määritykset oikein.",
+       "mergehistory-fail-permission": "Käyttöoikeutesi eivät riitä sivuhistorioiden yhdistämiseen.",
+       "mergehistory-fail-self-merge": "Lähdesivu ja kohdesivu ovat samat.",
+       "mergehistory-fail-timestamps-overlap": "Lähdesivun versiot menevät päällekkäin tai ovat myöhemmin tehtyjä kuin kohdesivun versiot.",
        "mergehistory-fail-toobig": "Sivuhistorian yhdistämistä ei voi tehdä, koska enemmän kuin sallittu määrä $1 {{PLURAL:$1|versio|versiota}} siirrettäisiin.",
        "mergehistory-no-source": "Lähdesivua $1 ei ole olemassa.",
        "mergehistory-no-destination": "Kohdesivua $1 ei ole olemassa.",
        "upload-dialog-button-done": "Valmis",
        "upload-dialog-button-save": "Tallenna",
        "upload-dialog-button-upload": "Tallenna",
-       "upload-form-label-select-file": "Valitse tiedosto",
        "upload-form-label-infoform-title": "Yksityiskohdat",
        "upload-form-label-infoform-name": "Nimi",
        "upload-form-label-infoform-description": "Kuvaus",
        "foreign-structured-upload-form-label-own-work-message-shared": "Vakuutan, että minä omistan tämän tiedoston tekijänoikeudet, ja sitoudun siihen, että luovutan peruuttamattomasti tämän tiedoston kohteeseen Wikimedia Commons niillä ehdoilla, jotka liittyvät lisenssiin [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0]. Sitoudun myös noudattamaan [https://wikimediafoundation.org/wiki/Terms_of_Use käyttöehtoja].",
        "foreign-structured-upload-form-label-not-own-work-message-shared": "Jos sinulla ei ole tähän tiedostoon tekijänoikeutta tai jos haluat luovuttaa tiedoston käyttäen jotain toista lisenssiä, voit käyttää erityistä [https://commons.wikimedia.org/wiki/Special:UploadWizard Commons Upload Wizard] -toimintoa.",
        "foreign-structured-upload-form-label-not-own-work-local-shared": "Voit myös kokeilla [[Special:Upload|tallennussivua sivustolla {{SITENAME}}]]. Saattaa olla, että sivusto antaa tallentaa tämän tiedoston sinne siellä voimassa olevien käytäntöjen mukaisesti.",
-       "foreign-structured-upload-form-3-label-yes": "Kyllä",
-       "foreign-structured-upload-form-3-label-no": "Ei",
        "backend-fail-stream": "Tiedoston $1 virtauttaminen epäonnistui.",
        "backend-fail-backup": "Tiedostoa $1 ei voitu varmuuskopioida.",
        "backend-fail-notexists": "Tiedostoa $1 ei ole olemassa.",
        "querypage-disabled": "Tämä toimintosivu on poistettu käytöstä suorituskykyyn liittyvien syiden vuoksi.",
        "apihelp": "API-apu",
        "apihelp-no-such-module": "Moduulia ”$1” ei löydy.",
+       "apisandbox": "API-hiekkalaatikko",
+       "apisandbox-api-disabled": "API on poistettu käytöstä tällä sivustolla.",
+       "apisandbox-intro": "Tämä on '''MediaWiki API:n''' hiekkalaatikko.\n[//www.mediawiki.org/wiki/API:Main_page API-dokumentaatio] kertoo lisää API:en käytöstä.",
+       "apisandbox-fullscreen": "Laajenna paneeli",
+       "apisandbox-unfullscreen": "Näytä sivu",
+       "apisandbox-submit": "Tee pyyntö",
+       "apisandbox-reset": "Tyhjennä",
+       "apisandbox-retry": "Yritä uudestaan",
+       "apisandbox-no-parameters": "Tässä API-moduulissa ei ole parametreja.",
+       "apisandbox-helpurls": "Linkit ohjeisiin",
+       "apisandbox-examples": "Esimerkit",
+       "apisandbox-dynamic-parameters": "Lisäparametrit",
+       "apisandbox-dynamic-parameters-add-label": "Lisää parametri:",
+       "apisandbox-dynamic-parameters-add-placeholder": "Parametrin nimi",
+       "apisandbox-dynamic-error-exists": "Parametri nimellä \"$1\" on ennestään olemassa.",
+       "apisandbox-deprecated-parameters": "Käytöstä poistuneet parametrit",
+       "apisandbox-fetch-token": "Lisää \"token\" automaattisesti",
+       "apisandbox-submit-invalid-fields-title": "Jotkin kentät ovat epäkelpoja",
+       "apisandbox-submit-invalid-fields-message": "Korjaa merkityt kentät ja yritä uudestaan.",
+       "apisandbox-results": "Tulokset",
+       "apisandbox-sending-request": "API-pyyntöä lähetetään...",
+       "apisandbox-loading-results": "API-tuloksia vastaanotetaan...",
+       "apisandbox-request-url-label": "Pyynnön URL",
+       "apisandbox-request-time": "Pyyntöön kulunut aika: {{PLURAL:$1|$1 ms}}",
+       "apisandbox-alert-page": "Tällä sivulla olevat kentät eivät ole kelvollisia.",
+       "apisandbox-alert-field": "Tässä kentässä oleva arvo ei ole kelvollinen.",
        "booksources": "Kirjalähteet",
        "booksources-search-legend": "Etsi kirjalähteitä",
        "booksources-isbn": "ISBN",
        "checkbox-select": "Valitse: $1",
        "checkbox-all": "Kaikki",
        "checkbox-none": "Ei mitään",
-       "checkbox-invert": "Päinvastoin",
+       "checkbox-invert": "Käännä valinta",
        "allpages": "Kaikki sivut",
        "nextpage": "Seuraava sivu ($1)",
        "prevpage": "Edellinen sivu ($1)",
        "listgrouprights-namespaceprotection-namespace": "Nimiavaruus",
        "listgrouprights-namespaceprotection-restrictedto": "Käyttäjän muokkausoikeudet",
        "listgrants": "Toimintaoikeudet",
-       "listgrants-grant": "Toimintaoikeus",
-       "listgrants-rights": "Oikeudet",
+       "listgrants-grant": "Toimintaoikeus (grant)",
+       "listgrants-rights": "Oikeudet (rights)",
        "trackingcategories": "Tarkkailuluokat",
        "trackingcategories-summary": "Tällä sivulla on luettelo sellaisista ongelmia havaitsevista luokista (tarkkailuluokat), joiden sisällön koostaa automaattisesti MediaWiki-ohjelmisto. Luokkien nimiä voi vaihtaa muuttamalla asianomaista järjestelmäviestiä nimiavaruudessa {{ns:8}}.",
        "trackingcategories-msg": "Tarkkailuluokka",
        "lockedbyandtime": "(lukinnut {{GENDER:$1|$1}} $2 kello $3)",
        "move-page": "Siirrä $1",
        "move-page-legend": "Siirrä sivu",
-       "movepagetext": "Alla olevalla lomakkeella voit nimetä uudelleen sivuja, jolloin niiden koko historia siirtyy uuden nimen alle.\nVanhasta sivusta tulee ohjaussivu, joka osoittaa uuteen sivuun.\nVoit päivittää sivuun viittaavat ohjaukset automaattisesti ohjaamaan uudelle nimelle.\nJos et halua tätä tehtävän automaattisesti, muista tehdä tarkistukset [[Special:DoubleRedirects|kaksinkertaisten]] tai [[Special:BrokenRedirects|rikkinäisten]] ohjausten varalta.\nOlet vastuussa siitä, että linkit osoittavat sinne, mihin niiden on tarkoituskin osoittaa.\n\nHuomaa, että sivua '''ei''' siirretä mikäli uusi otsikko on olemassa olevan sivun käytössä, paitsi jos jälkimmäinen on ohjaus, jolla ei ole muokkaushistoriaa.\nTämä tarkoittaa sitä, että voit siirtää sivun takaisin vanhalle nimelleen mikäli teit virheen, mutta et voi kirjoittaa olemassa olevan sivun päälle.\n\nTämä saattaa olla suuri ja odottamaton muutos suositulle sivulle. Varmista, että tiedät seuraukset ennen kuin siirrät sivun.",
-       "movepagetext-noredirectfixer": "Alla olevalla lomakkeella voit nimetä uudelleen sivuja, jolloin niiden koko historia siirtyy uuden nimen alle. Vanhasta sivusta tulee ohjaussivu, joka osoittaa uuteen sivuun.\n\nTarkasta sivuun viittaavat ohjaukset [[Special:DoubleRedirects|kaksinkertaisten]] tai [[Special:BrokenRedirects|rikkinäisten]] ohjausten varalta. Olet vastuussa siitä, että linkit osoittavat sinne, mihin niiden on tarkoituskin osoittaa.\n\nHuomaa, että sivua '''ei''' siirretä mikäli uusi otsikko on olemassa olevan sivun käytössä, paitsi jos jälkimmäinen on ohjaus, jolla ei ole muokkaushistoriaa.\nTämä tarkoittaa sitä, että voit siirtää sivun takaisin vanhalle nimelleen mikäli teit virheen, mutta et voi kirjoittaa olemassa olevan sivun päälle.\n\nTämä saattaa olla suuri ja odottamaton muutos suositulle sivulle. Varmista, että tiedät seuraukset ennen kuin siirrät sivun.",
+       "movepagetext": "Alla olevalla lomakkeella voit nimetä uudelleen sivuja, jolloin niiden koko historia siirtyy uuden nimen alle.\nVanhasta sivusta tulee ohjaussivu, joka osoittaa uuteen sivuun.\nVoit päivittää sivuun viittaavat ohjaukset automaattisesti ohjaamaan uudelle nimelle.\nJos et halua tätä tehtävän automaattisesti, muista tehdä tarkistukset [[Special:DoubleRedirects|kaksinkertaisten]] tai [[Special:BrokenRedirects|rikkinäisten]] ohjausten varalta.\nOlet vastuussa siitä, että linkit osoittavat sinne, mihin niiden on tarkoituskin osoittaa.\n\nHuomaa, että sivua <strong>ei</strong> siirretä mikäli uusi otsikko on olemassa olevan sivun käytössä, paitsi jos jälkimmäinen on ohjaus, jolla ei ole muokkaushistoriaa.\nTämä tarkoittaa sitä, että voit siirtää sivun takaisin vanhalle nimelleen mikäli teit virheen, mutta et voi kirjoittaa olemassa olevan sivun päälle.\n\n<strong>Ota huomioon:</strong>\n\nTämä saattaa olla suuri ja odottamaton muutos suositulle sivulle. Varmista, että ymmärrät seuraukset ennen kuin siirrät sivun.",
+       "movepagetext-noredirectfixer": "Alla olevalla lomakkeella voit nimetä uudelleen sivuja, jolloin niiden koko historia siirtyy uuden nimen alle. Vanhasta sivusta tulee ohjaussivu, joka osoittaa uuteen sivuun.\n\nTarkasta sivuun viittaavat ohjaukset [[Special:DoubleRedirects|kaksinkertaisten]] tai [[Special:BrokenRedirects|rikkinäisten]] ohjausten varalta. Olet vastuussa siitä, että linkit osoittavat sinne, mihin niiden on tarkoituskin osoittaa.\n\nHuomaa, että sivua <strong>ei</strong> siirretä mikäli uusi otsikko on olemassa olevan sivun käytössä, paitsi jos jälkimmäinen on ohjaus, jolla ei ole muokkaushistoriaa.\nTämä tarkoittaa sitä, että voit siirtää sivun takaisin vanhalle nimelleen mikäli teit virheen, mutta et voi kirjoittaa olemassa olevan sivun päälle.\n\n<strong>Ota huomioon:</strong>\n\nTämä saattaa olla suuri ja odottamaton muutos suositulle sivulle. Varmista, että ymmärrät seuraukset ennen kuin siirrät sivun.",
        "movepagetalktext": "Jos valitset tämän vaihtoehdon, sivuun liittyvä keskustelusivu siirtyy automaattisesti uudelle nimelle, paitsi jos uudella nimellä on jo olemassa keskustelusivu, joka ei ole tyhjä.\n\nTällöin sivu täytyy siirtää tai yhdistää käsin, jos se on tarpeen.",
        "moveuserpage-warning": "'''Varoitus:''' Olet siirtämässä käyttäjäsivua. Huomaa, että vain sivu siirretään ja käyttäjää ''ei'' nimetä uudelleen.",
        "movecategorypage-warning": "<strong>Varoitus:</strong> Olet siirtämässä luokkasivua. Ota huomioon, että ainoastaan luokan oma sivu siirretään ja että tämä toiminto <em>ei</em> luokittele tai itsestään siirrä vanhassa luokassa olevia sivuja uuteen luokkaan.",
        "movenosubpage": "Tällä sivulla ei ole alasivuja.",
        "movereason": "Syy:",
        "revertmove": "kumoa siirto",
-       "delete_and_move_text": "==Poistamista edellyttävä siirto==\nKohdesivu [[:$1]] on jo olemassa. \nHaluatko poistaa sen, jotta nykyinen sivu voitaisiin siirtää?",
+       "delete_and_move_text": "Kohdesivu [[:$1]] on jo olemassa. \nHaluatko poistaa sen, jotta nykyinen sivu voitaisiin siirtää sen tilalle?",
        "delete_and_move_confirm": "Kyllä, poista kohdesivu",
        "delete_and_move_reason": "Sivu on sivun [[$1]] siirron tiellä.",
        "selfmove": "Lähteen ja kohteen nimi on sama.\nSivua ei voi siirtää itsensä päälle.",
        "move-leave-redirect": "Jätä paikalle ohjaus",
        "protectedpagemovewarning": "'''Varoitus:''' Tämä sivu on lukittu siten, että vain ylläpitäjät voivat siirtää sitä.\nAlla on viimeisin lokitapahtuma:",
        "semiprotectedpagemovewarning": "Tämä sivu on lukittu siten, että vain rekisteröityneet käyttäjät voivat siirtää sitä.\nAlla on viimeisin lokitapahtuma:",
-       "move-over-sharedrepo": "== Tiedosto on olemassa ==\n[[:$1]] on olemassa jaetussa tietovarastossa. Tiedoston siirtäminen tälle nimelle ohittaa jaetun tiedoston.",
+       "move-over-sharedrepo": "[[:$1]] on olemassa yhteisessä tietovarastossa. Tiedoston siirtäminen tälle nimelle korvaa yhteisen tiedoston.",
        "file-exists-sharedrepo": "Valittu tiedostonimi on jo käytössä jaetussa varastossa.\nValitse toinen nimi.",
        "export": "Vie sivuja",
        "exporttext": "Voit viedä (''export'') sivun tai usean sivun tekstin ja muokkaushistorian XML-muodossa.\nTämä tieto voidaan tuoda (''import'') toiseen wikiin käyttämällä MediaWikiä [[Special:Import|tuontisivun]] kautta.\n\nKirjoita sivujen nimet, jokainen nimike omalle rivilleen, alla olevaan tietolaatikkoon. Valitse, haluatko viedä sivun uusimman version sekä samalla kaikki vanhat versiot ja sivun historian tietorivit, vai haluatko viedä sivun uusimman version, jossa on tieto viimeisimmästä muokkauksesta.\n\nJälkimmäisessä tapauksessa voit myös käyttää linkkiä. Esimerkiksi sivun [[{{MediaWiki:Mainpage}}]] saa vietyä linkistä [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]].",
        "import-nonewrevisions": "Ei tuotu yhtään versiota, koska kaikki versiot ovat jo täällä tai ne on ohitettu virheiden vuoksi.",
        "xml-error-string": "$1 rivillä $2, sarakkeessa $3 (tavu $4): $5",
        "import-upload": "Tallenna XML-tiedosto",
-       "import-token-mismatch": "Istuntotiedot ovat kadonneet. Yritä uudelleen.",
+       "import-token-mismatch": "Istuntotietojen katoaminen.\n\nSaatat olla kirjautunut ulos. <strong>Ole hyvä ja varmista, että olet edelleen kirjautunut sisään, ja yritä sitten uudestaan.</strong>\nJos ei vieläkään onnistu, yritä [[Special:UserLogout|kirjautua ulos]] ja sitten takaisin sisään, ja tarkista, että selaimesi sallii evästeet tältä sivustolta.",
        "import-invalid-interwiki": "Määritellystä wikistä ei voi tuoda.",
        "import-error-edit": "Sivua $1 ei tuotu, koska sinulla ei ole oikeutta muokata sitä.",
        "import-error-create": "Sivua $1 ei tuotu, koska sinulla ei ole oikeutta luoda sitä.",
        "expand_templates_generate_xml": "Näytä XML-jäsennyspuu",
        "expand_templates_generate_rawhtml": "Näytä raaka HTML",
        "expand_templates_preview": "Esikatselu",
-       "expand_templates_preview_fail_html": "<em>Koska sivustolla {{SITENAME}} on käytössä puhdas HTML-koodi ja koska istunnon tiedot ovat kadonneet, esikatselu on piilotettu JavaScript-hyökkäyksien torjumiseksi.</em>\n\n<strong>Jos olet oikealla asialla, yritä uudestaan.</strong>\nJos esikatselu ei vieläkään toimi, kokeile [[Special:UserLogout|kirjautua ulos]] ja sen jälkeen kirjaudu uudestaan sisään.",
+       "expand_templates_preview_fail_html": "<em>Koska sivustolla {{SITENAME}} on käytössä puhdas HTML-koodi ja koska istunnon tiedot ovat kadonneet, esikatselu on piilotettu JavaScript-hyökkäyksien torjumiseksi.</em>\n\n<strong>Jos olet oikealla asialla, yritä uudestaan.</strong>\nJos esikatselu ei vieläkään toimi, yritä [[Special:UserLogout|kirjautua ulos]] ja sitten kirjautua uudestaan sisään. Tarkista myös, että selaimesi sallii evästeet tältä sivustolta.",
        "expand_templates_preview_fail_html_anon": "<em>Koska sivustolla {{SITENAME}} on käytössä puhdas HTML-koodi ja koska et ole kirjautunut sisään, esikatselu on piilotettu JavaScript-hyökkäyksien torjumiseksi.</em>\n\n<strong>Jos olet oikealla asialla, [[Special:UserLogin|kirjaudu sisään]] ja yritä uudestaan.</strong>",
        "expand_templates_input_missing": "Sinun on annettava edes jotakin tekstiä syötteeksi.",
-       "pagelanguage": "Vaihda sivun kieltä",
+       "pagelanguage": "Sivun kielen vaihto",
        "pagelang-name": "Sivu",
        "pagelang-language": "Kieli",
        "pagelang-use-default": "Käytä oletuskieltä",
        "mw-widgets-titleinput-description-new-page": "sivua ei ole olemassa vielä",
        "mw-widgets-titleinput-description-redirect": "ohjaus kohteeseen $1",
        "api-error-blacklisted": "Valitse toinen, kuvaava nimi.",
+       "sessionmanager-tie": "!!FYZZ!!Cannot combine multiple request authentication types: $1.",
        "sessionprovider-generic": "$1 istuntoa",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "istuntoja, joissa on evästeet käytössä",
        "sessionprovider-nocookies": "Evästeet on voitu poistaa käytöstä. Varmista, että sinulla on evästeet käytössä ja yritä sitten uudelleen.",
index 912bd1c..a884eb0 100644 (file)
                        "SRXcraft",
                        "StevenJ81",
                        "The RedBurn",
-                       "Fredlefred"
+                       "Fredlefred",
+                       "Lbayle"
                ]
        },
        "tog-underline": "Soulignement des liens :",
        "badaccess-group0": "Vous n’avez pas les droits suffisants pour réaliser l’action demandée.",
        "badaccess-groups": "L’action que vous essayez de réaliser n’est permise qu’aux utilisateurs {{PLURAL:$2|du groupe|d'un des groupes}} : $1.",
        "versionrequired": "Version $1 de MediaWiki nécessaire",
-       "versionrequiredtext": "La version $1 de MediaWiki est nécessaire pour utiliser cette page. Consultez [[Special:Version|la page des versions]].",
+       "versionrequiredtext": "La version $1 de MediaWiki est nécessaire pour utiliser cette page.\nConsultez [[Special:Version|la page des versions]].",
        "ok": "Valider",
        "pagetitle": "$1 — {{SITENAME}}",
        "retrievedfrom": "Récupérée de « $1 »",
        "previewnote": "'''Rappelez-vous que ce n'est qu'une prévisualisation.'''\nVos modifications n'ont pas encore été enregistrées !",
        "continue-editing": "Aller à la zone de modification",
        "previewconflict": "Cette prévisualisation montre le texte de la boîte supérieure de modification tel qu'il apparaîtra si vous choisissez de le publier.",
-       "session_fail_preview": "'''Nous ne pouvons enregistrer votre modification à cause d'une perte d'informations concernant votre session.'''\nVeuillez réessayer.\nSi cela échoue de nouveau, essayez en vous [[Special:UserLogout|déconnectant]], puis en vous reconnectant.",
-       "session_fail_preview_html": "'''Nous ne pouvons enregistrer votre modification à cause d'une perte d'informations concernant votre session.'''\n\n''Parce que {{SITENAME}} a activé le HTML brut, la prévisualisation a été masquée afin de prévenir les attaques par JavaScript.''\n\n'''Si la tentative de modification était légitime, veuillez réessayer.'''\nSi cela échoue de nouveau, [[Special:UserLogout|déconnectez-vous]], puis reconnectez-vous.",
+       "session_fail_preview": "Désolé, nous ne pouvons enregistrer votre modification à cause d’une perte d’informations concernant votre session.\n\nVous avez peut-être été déconnecté. <strong>Veuillez vérifier que vous êtes toujours connecté et réessayer.</strong>\nSi cela échoue de nouveau, essayez en vous [[Special:UserLogout|déconnectant]], puis en vous reconnectant, et vérifiez que votre navigateur accepte les cookies de ce site.",
+       "session_fail_preview_html": "Désolé, nous ne pouvons enregistrer votre modification à cause d’une perte d’informations concernant votre session.\n\n<em>Parce que {{SITENAME}} a activé le HTML brut, la prévisualisation est masquée afin de prévenir les attaques par JavaScript.</em>\n\n<strong>Si la tentative de modification est légitime, veuillez réessayer.</strong>\nSi cela échoue de nouveau, [[Special:UserLogout|déconnectez-vous]], puis reconnectez-vous, et vérifiez que votre navigateur accepte les cookies de ce site.",
        "token_suffix_mismatch": "'''Votre modification n'a pas été acceptée car votre navigateur a mal codé les caractères de ponctuation dans l'identifiant de modification.'''\nCe rejet est nécessaire pour empêcher la corruption du texte de la page.\nCe problème se produit parfois lorsque vous utilisez un serveur mandataire anonyme problématique basé sur le web.",
        "edit_form_incomplete": "'''Certaines parties du formulaire de modification n'ont pas atteint le serveur, vérifiez que vos modifications sont intactes et essayez à nouveau.'''",
        "editing": "Modification de $1",
        "mergehistory-empty": "Aucune version ne peut être fusionnée.",
        "mergehistory-done": "$3 version{{PLURAL:$3||s}} de $1 {{PLURAL:$3|a été fusionnée|ont été fusionnées}} dans [[:$2]].",
        "mergehistory-fail": "Impossible de procéder à la fusion des historiques. Resélectionner la page ainsi que les paramètres de date.",
+       "mergehistory-fail-bad-timestamp": "L’horodatage n’est pas valide.",
+       "mergehistory-fail-invalid-source": "La page source n’est pas valide.",
+       "mergehistory-fail-invalid-dest": "La page de destination n’est pas valide.",
+       "mergehistory-fail-no-change": "La fusions d’historique n’a fusionné aucune révision. Veuillez vérifier de nouveau la page et les paramètres de temps.",
+       "mergehistory-fail-permission": "Droits insuffisants pour fusionner l’historique.",
+       "mergehistory-fail-self-merge": "Les pages source et destination sont identiques.",
+       "mergehistory-fail-timestamps-overlap": "Les révisions de la source chevauchent ou suivent les révisions de destination.",
        "mergehistory-fail-toobig": "Impossible d’effectuer la fusion de l’historique car un nombre de {{PLURAL:$1|révisions}} supérieur à la limite de $1 devrait être déplacé.",
        "mergehistory-no-source": "La page d'origine $1 n'existe pas.",
        "mergehistory-no-destination": "La page de destination $1 n'existe pas.",
        "uploaded-script-svg": "Élément scriptable « $1 » trouvé dans le fichier SVG téléchargé.",
        "uploaded-hostile-svg": "CSS non sûr trouvé dans l’élément style d’un fichier SVG téléchargé.",
        "uploaded-event-handler-on-svg": "Fixer des attributs de gestionnaire d’événement <code>$1=\"$2\"</code> n’est pas autorisé dans les fichiers SVG.",
-       "uploaded-href-unsafe-target-svg": "href vers une cible non sûre <code>&lt;$1 $2=\"$3\"&gt;</code> trouvé dans le fichier SVG téléchargé.",
+       "uploaded-href-attribute-svg": "les attributs href dans les fichiers SVG ne sont autorisés que pour faire référence à des cibles http:// ou https://, <code>&lt;$1 $2=\"$3\"&gt;</code> trouvé.",
+       "uploaded-href-unsafe-target-svg": "href vers des données non sûres trouvé dans le fichier SVG téléchargé : URI cible <code>&lt;$1 $2=\"$3\"&gt;</code>.",
        "uploaded-animate-svg": "Balise « animate » trouvée, qui pourrait modifier le href en utilisant l’attribut « from » <code>&lt;$1 $2=\"$3\"&gt;</code> dans le fichier SVG téléchargé.",
        "uploaded-setting-event-handler-svg": "Positionner des attributs de gestionnaire d’événement est bloqué, <code>&lt;$1 $2=\"$3\"&gt;</code> trouvé dans le fichier SVG téléchargé.",
        "uploaded-setting-href-svg": "L’utilisation de la balise « set » pour ajouter un attribut « href » à l’élément parent est interdite.",
        "upload-dialog-button-done": "Fait",
        "upload-dialog-button-save": "Enregistrer",
        "upload-dialog-button-upload": "Téléverser",
-       "upload-form-label-select-file": "Sélectionner un fichier",
        "upload-form-label-infoform-title": "Détails",
        "upload-form-label-infoform-name": "Nom",
        "upload-form-label-infoform-name-tooltip": "Un titre descriptif unique pour le fichier, qui servira comme nom de fichier. Vous pouvez utiliser du langage courant avec des espaces. Ne pas inclure l’extension du fichier.",
        "foreign-structured-upload-form-label-own-work-message-shared": "Je certifie être le détenteur des droits d’auteur sur ce fichier, j’accepte de publier ce fichier sur Wikimedia Commons en le plaçant irrévocablement sous licence [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0] et j’accepte les [https://wikimediafoundation.org/wiki/Terms_of_Use conditions d’utilisation].",
        "foreign-structured-upload-form-label-not-own-work-message-shared": "Si vous n’êtes pas le détenteur des droits d’auteur sur ce fichier ou que vous voulez le publier sous une licence différente, vous pouvez utiliser l’[https://commons.wikimedia.org/wiki/Special:UploadWizard assistant d’import].",
        "foreign-structured-upload-form-label-not-own-work-local-shared": "Vous pouvez également essayer d’utiliser [[Special:Upload|la page de téléversement de {{SITENAME}}]], si les règles du site autorisent le téléversement du fichier.",
-       "foreign-structured-upload-form-2-label-intro": "Merci de faire don d’une image pour l’utiliser sur {{SITENAME}}. Ne continuez que si elle répond aux conditions suivantes :",
-       "foreign-structured-upload-form-2-label-ownwork": "Elle doit avoir été intégralement <strong>créée par vous</strong>, et pas simplement prise sur Internet.",
-       "foreign-structured-upload-form-2-label-noderiv": "Elle ne doit <strong>pas contenir d’œuvre créée par une autre personne</strong> ni être inspirée d’une telle œuvre.",
-       "foreign-structured-upload-form-2-label-useful": "Elle doit être <strong>éducative et utile</strong> pour instruire les lecteurs",
-       "foreign-structured-upload-form-2-label-ccbysa": "Vous devez accepter de la <strong>publier de façon irrévocable</strong> sur Internet selon les termes de la licence [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0]",
-       "foreign-structured-upload-form-2-label-alternative": "Si l’une de ces conditions n’est pas remplie, il est possible que vous puissiez tout de même importer l’image en utilisant l’[https://commons.wikimedia.org/wiki/Special:UploadWizard Assistant d’import de Commons], pourvu qu’elle soit disponible sous licence libre.",
-       "foreign-structured-upload-form-2-label-termsofuse": "En important ce fichier, vous certifiez détenir les droits d’auteur sur ce fichier, vous acceptez de publier irrévocablement ce fichier sur Wikimedia Commons selon les termes de la licence Creative Commons Attribution-ShareAlike 4.0 et vous acceptez les [https://wikimediafoundation.org/wiki/Terms_of_Use conditions d’utilisation].",
-       "foreign-structured-upload-form-3-label-question-website": "Avez-vous téléchargé cette image depuis un site web, ou l’avez-vous obtenue en faisant une recherche d’images ?",
-       "foreign-structured-upload-form-3-label-question-ownwork": "Avez-vous créé cette image (pris la photo, fait vous-même le dessin, etc.) ?",
-       "foreign-structured-upload-form-3-label-question-noderiv": "Contient-elle ou est-elle inspirée de l’œuvre d’une autre personne, par exemple un logo ?",
-       "foreign-structured-upload-form-3-label-yes": "Oui",
-       "foreign-structured-upload-form-3-label-no": "Non",
-       "foreign-structured-upload-form-3-label-alternative": "Malheureusement, dans ce cas, cet outil ne permet pas d’importer ce fichier. Il est possible que vous puissiez l’importer en utilisant l’[https://commons.wikimedia.org/wiki/Special:UploadWizard Assistant d’import de Commons], pourvu qu’il soit disponible sous licence libre.",
-       "foreign-structured-upload-form-4-label-good": "À l’aide de cet outil, vous pouvez importer des images éducatives que vous avez créées et des photographies que vous avez prises, pourvu qu’elles ne contiennent pas d’œuvres appartenant à d’autres personnes.",
-       "foreign-structured-upload-form-4-label-bad": "Vous ne pouvez pas importer des images trouvées sur un moteur de recherche ou téléchargées sur d’autres sites.",
        "backend-fail-stream": "Impossible de lire le fichier $1.",
        "backend-fail-backup": "Impossible de sauvegarder le fichier $1.",
        "backend-fail-notexists": "Le fichier $1 n’existe pas.",
        "querypage-disabled": "Cette page spéciale est désactivée pour des raisons de performances.",
        "apihelp": "Aide de l’API",
        "apihelp-no-such-module": "Le module « $1 » est introuvable.",
+       "apisandbox": "Bac à sable API",
+       "apisandbox-jsonly": "Le bac à sable de l'API nécessite JavaScript",
+       "apisandbox-api-disabled": "API est désactivé sur ce site.",
+       "apisandbox-intro": "Utilisez cette page pour expérimenter l’<strong>API webservice de MediaWiki</strong>.\nReportez-vous à [[mw:API:Main page|la documentation de l’API]] pour plus de détails sur l’utilisation de l’API. Exemple: [//www.mediawiki.org/wiki/API#A_simple_example obtenir le contenu d'une page principale]. Choisissez une option pour voir d'autres exemples.",
+       "apisandbox-fullscreen": "Développer le panneau",
+       "apisandbox-fullscreen-tooltip": "Étendre le panneau du bac à sable pour remplir la fenêtre du navigateur.",
+       "apisandbox-unfullscreen": "Afficher la page",
+       "apisandbox-unfullscreen-tooltip": "Réduire le panneau du bac à sable, pour que les liens de navigation de MédiaWiki soient disponibles.",
+       "apisandbox-submit": "Faire la demande",
+       "apisandbox-reset": "Effacer",
+       "apisandbox-retry": "Réessayer",
+       "apisandbox-loading": "Chargement des informations du module \"$1\" de l'API...",
+       "apisandbox-load-error": "Une erreur s'est produite lors du chargement des informations du module \"$1\" de l'API: $2",
+       "apisandbox-no-parameters": "Ce module de l’API n’a pas de paramètres.",
+       "apisandbox-helpurls": "Liens d'aide",
+       "apisandbox-examples": "Exemples",
+       "apisandbox-dynamic-parameters": "Paramètres supplémentaires",
+       "apisandbox-dynamic-parameters-add-label": "Ajout du paramètre:",
+       "apisandbox-dynamic-parameters-add-placeholder": "Nom du paramètre",
+       "apisandbox-dynamic-error-exists": "Un paramètre nommé \"$1\" existe déjà.",
+       "apisandbox-deprecated-parameters": "Paramètres obsolètes",
+       "apisandbox-fetch-token": "Auto-remplissage du jeton",
+       "apisandbox-submit-invalid-fields-title": "Certains champs ne sont pas valides",
+       "apisandbox-submit-invalid-fields-message": "Veuillez corriger les champs marqués et essayez de nouveau.",
+       "apisandbox-results": "Résultats",
+       "apisandbox-sending-request": "Envoi de la requête à l'API...",
+       "apisandbox-loading-results": "Réception des résultats de l'API...",
+       "apisandbox-results-error": "Une erreur s'est produite lors du chargement de la réponse à la requête de l'API: $1.",
+       "apisandbox-request-url-label": "Requête URL :",
+       "apisandbox-request-time": "Durée de la demande: {{PLURAL:$1|$1 ms}}",
+       "apisandbox-results-fixtoken": "Corrigez le jeton et renvoyez",
+       "apisandbox-results-fixtoken-fail": "Impossible de récupérer le jeton \"$1\"",
+       "apisandbox-alert-page": "Les champs de cette page ne sont pas valides.",
+       "apisandbox-alert-field": "La valeur de ce champ n'est pas valide.",
        "booksources": "Ouvrages de référence",
        "booksources-search-legend": "Rechercher parmi des ouvrages de référence",
        "booksources-isbn": "ISBN :",
        "undelete-error-long": "Des erreurs ont été rencontrées lors de la restauration du fichier :\n\n$1",
        "undelete-show-file-confirm": "Êtes-vous sûr{{GENDER:||e}} de vouloir consulter une version supprimée du fichier « <nowiki>$1</nowiki> » datant du $2 à $3 ?",
        "undelete-show-file-submit": "Oui",
-       "undelete-revision-row": "$1 $2 ($3) $4 — $5 $6 $7 $8 $9",
        "namespace": "Espace de noms :",
        "invert": "Inverser la sélection",
        "tooltip-invert": "Cochez cette case pour cacher les modifications des pages dans l'espace de noms sélectionné (et l'espace de noms associé si coché)",
        "move-page": "Renommer $1",
        "move-page-legend": "Renommer une page",
        "movepagetext": "Utilisez le formulaire ci-dessous pour renommer une page, en déplaçant tout son historique vers le nouveau nom. L’ancien titre deviendra une page de redirection vers le nouveau titre. Vous pouvez mettre à jour automatiquement les redirections actuelles qui pointent vers le titre original. Si vous choisissez de ne pas le faire, assurez-vous de vérifier toute [[Special:DoubleRedirects|double redirection]] ou [[Special:BrokenRedirects|redirection cassée]]. Vous avez la responsabilité de vous assurer que les liens continuent de pointer vers leur destination supposée.\n\nNotez que la page ne sera <string>pas</strong> renommée s’il existe déjà une page avec le nouveau titre, sauf si cette dernière est une simple redirection avec un historique de modifications vierge. Ceci permet de renommer une page vers sa position d’origine si le déplacement s’avère erroné.\n\n<strong>Attention !</strong>\nCeci peut provoquer un changement radical et imprévu pour une page souvent consultée ; assurez-vous d’en avoir compris les conséquences avant de continuer.",
-       "movepagetext-noredirectfixer": "Utilisez le formulaire ci-dessous pour renommer une page, en déplaçant tout son historique vers le nouveau nom.\nL’ancien titre deviendra une page de redirection vers le nouveau titre.\nVérifiez bien les [[Special:DoubleRedirects|doubles redirections]] ou les [[Special:BrokenRedirects|redirections cassées]].\nVous avez la responsabilité de vous assurer que les liens continuent de pointer vers leur destination supposée.\n\nNotez que la page ne sera <strong>pas</stong> déplacée s’il existe déjà une page avec le nouveau titre, sauf si cette dernière a un historique de modifications vierge et est soit vide, soit une simple redirection. Ceci permet de renommer une page vers sa position d’origine si le déplacement s’avère erroné, et il est impossible d’écraser une page existante.\n\n<strong>Attention !</stong>\nCeci peut provoquer un changement radical et imprévu pour une page souvent consultée ; assurez-vous d’en avoir compris les conséquences avant de continuer.",
+       "movepagetext-noredirectfixer": "Utilisez le formulaire ci-dessous pour renommer une page, en déplaçant tout son historique vers le nouveau nom.\nL’ancien titre deviendra une page de redirection vers le nouveau titre.\nVérifiez bien les [[Special:DoubleRedirects|doubles redirections]] ou les [[Special:BrokenRedirects|redirections cassées]].\nVous avez la responsabilité de vous assurer que les liens continuent de pointer vers leur destination supposée.\n\nNotez que la page ne sera <strong>pas</strong> déplacée s’il existe déjà une page avec le nouveau titre, sauf si cette dernière a un historique de modifications vierge et est soit vide, soit une simple redirection. Ceci permet de renommer une page vers sa position d’origine si le déplacement s’avère erroné, et il est impossible d’écraser une page existante.\n\n<strong>Attention !</strong>\nCeci peut provoquer un changement radical et imprévu pour une page souvent consultée ; assurez-vous d’en avoir compris les conséquences avant de continuer.",
        "movepagetalktext": "Si vous cochez cette case, la page de discussion associée sera automatiquement renommée, à moins qu’une page de discussion non vide existe déjà sous ce nouveau nom.\n\nDans ce cas, vous devrez renommer ou fusionner cette page de discussion manuellement si vous le désirez.",
        "moveuserpage-warning": "'''Attention :''' Vous êtes sur le point de renommer une page d’utilisateur. Veuillez noter que seule la page sera renommée et que l’utilisateur '''ne''' sera '''pas''' renommé.",
        "movecategorypage-warning": "<strong>Avertissement :</strong> Vous êtes sur le point de renommer une page de catégorie. Veuillez noter que seule la catégorie sera renommée et <em>qu’aucune</em> des pages de l’ancienne catégorie ne sera transférée dans la nouvelle.",
        "import-nonewrevisions": "Aucune révision importée (toutes étaient déjà présentes, ou ignorées du fait d’erreurs).",
        "xml-error-string": "$1 à la ligne $2, colonne $3 (octet $4) : $5",
        "import-upload": "Import de données XML",
-       "import-token-mismatch": "Perte des données de session. Veuillez réessayer.",
+       "import-token-mismatch": "Perte des données de session.\n\nVous avez peut-être été déconnecté. <strong>Veuillez vérifier que vous êtes toujours connecté et réessayez</strong>.\nSi cela ne fonctionne toujours pas, essayez de [[Special:UserLogout|vous déconnecter]] et reconnectez-vous, et vérifiez que votre navigateur accepte les cookies de ce site.",
        "import-invalid-interwiki": "Impossible d'importer depuis le wiki spécifié.",
        "import-error-edit": "La page « $1 » n’a pas été importée parce que vous n’êtes pas autorisé à la modifier.",
        "import-error-create": "La page « $1 » n’a pas été importée parce que vous n’êtes pas autorisé à la créer.",
        "expand_templates_generate_xml": "Voir l’arborescence d’analyse XML",
        "expand_templates_generate_rawhtml": "Afficher le HTML brut",
        "expand_templates_preview": "Aperçu du rendu",
-       "expand_templates_preview_fail_html": "<em>Comme {{SITENAME}} a HTML brut activé et qu’il y a eu une perte de données de session, l’aperçu est masqué par précaution contre les attaques JavaScript.</em>\n\n<strong>Si c’est une demande d’aperçu légitime, veuillez réessayer.</strong>\nSi cela ne fonctionne toujours pas, essayez de [[Special:UserLogout|vous déconnecter]] et vous reconnecter.",
+       "expand_templates_preview_fail_html": "<em>Comme {{SITENAME}} a HTML brut activé et qu’il y a eu une perte de données de session, l’aperçu est masqué par précaution contre les attaques JavaScript.</em>\n\n<strong>Si c’est une demande d’aperçu légitime, veuillez réessayer.</strong>\nSi cela ne fonctionne toujours pas, essayez de [[Special:UserLogout|vous déconnecter]] et vous reconnecter, et vérifiez que votre navigateur accepte les cookies de ce site.",
        "expand_templates_preview_fail_html_anon": "<em>Comme {{SITENAME}} a HTML brut activé et que vous n’êtes pas connecté, l’aperçu est masqué par précaution contre les attaques JavaScript.</em>\n\n<strong>Si c’est une demande d’aperçu légitime, veuillez [[Special:UserLogin|vous connecter]] et réessayer.</strong>",
        "expand_templates_input_missing": "Vous devez fournir au moins un texte d’entrée.",
        "pagelanguage": "Modifier la langue de la page",
index 7586ffc..f48ddca 100644 (file)
@@ -45,6 +45,7 @@
        "tog-watchlisthidebots": "Feranrangen faan bots bi a sidjen, diar ik uun't uug behual wal, fersteeg",
        "tog-watchlisthideminor": "Letj feranrangen bi a sidjen, diar ik uun't uug behual wal, fersteeg",
        "tog-watchlisthideliu": "Feranrangen faan uunmeldet brükern bi sidjen, diar ik uun't uug behual wal, fersteeg",
+       "tog-watchlistreloadautomatically": "List mä sidjen, diar dü uun't uug behual wel, nei loose, wan en filter anert wurden as (brükt JavaScript)",
        "tog-watchlisthideanons": "Feranrangen faan anonüüm brükern (IPs) bi sidjen, diar ik uun't uug behual wal, fersteeg",
        "tog-watchlisthidepatrolled": "Kontroliaret feranrangen bi a sidjen, diar ik uun't uug behual wal, fersteeg",
        "tog-watchlisthidecategorization": "Kategorisiarang faan sidjen fersteeg",
        "october-date": "$1. Oktuuber",
        "november-date": "$1. Nofember",
        "december-date": "$1. Detsember",
+       "period-am": "AM (iarmade)",
+       "period-pm": "PM (eftermade)",
        "pagecategories": "{{PLURAL:$1|Kategorii|Kategoriin}}",
        "category_header": "Sidjen uun kategorii \"$1\"",
        "subcategories": "Onerkategoriin",
        "databaseerror-query": "Uunfraag: $1",
        "databaseerror-function": "Funktjuun: $1",
        "databaseerror-error": "Feeler: $1",
+       "transaction-duration-limit-exceeded": "Transaktjuun ütj technisk grünjer ufbreegen. Det skriiwdüür ($1) wiar linger üs $2 {{PLURAL:$2|sekund|sekunden}}.\nWan dü flook objekten anerst, dial det ap tu letjer aktjuunen.",
        "laggedslavemode": "'''Paase üüb:''' Ferlicht wiset detdiar sidj ei a leetst stant.",
        "readonly": "Dootenbeenk speret.",
        "enterlockreason": "Wees so gud an du en grünj uun, huaram det dootenbeenk speret wurd skal, an hü loong det (amanbi) speret wurd skal.",
-       "readonlytext": "Det dootenbeenk as iarst ans speret för nei iindracher an feranrangen, woorskiinelk, auer diar jüst apredet woort. Ferschük det leeder man noch ans.\n\nGrünj för't sperin: $1",
+       "readonlytext": "Det dootenbeenk as iarst ans speret för nei iindracher an feranrangen. Ferschük det leeder man noch ans.\n\nGrünj för't sperin: $1",
        "missing-article": "Di tekst för „$1“ $2 as ei fünjen wurden uun't dootenbeenk.\n\nFerlicht as det sidj stregen of fersköwen wurden.\n\nWan't det ei as, do heest dü ferlicht en feeler uun't software fünjen. Wees so gud an skriiw det tu en [[Special:ListUsers/sysop|administraator]] an fertel ham, am hün URL det gongt.",
        "missingarticle-rev": "(Werjuunsnumer: $1)",
        "missingarticle-diff": "(Ferskeel tesken $1 an $2)",
        "readonly_lag": "Det dootenbeenk as speret wurden, amdat jo ferdiald dootenbeenken (slaves) jo mä di hoodserver (master) ufglik kön.",
+       "nonwrite-api-promise-error": "Di HTTP-header „Promise-Non-Write-API-Action“ as schüürd wurden, man det uunfraag ging tu en API-skriiwmodul.",
        "internalerror": "Süsteemfeeler",
        "internalerror_info": "Süsteemfeeler: $1",
        "internalerror-fatal-exception": "Böös ütjnoomfeeler faan di slach \"$1\"",
        "viewsource": "Kweltekst uunluke",
        "viewsource-title": "Code faan sidj $1 uunluke",
        "actionthrottled": "Taal faan aktjuunen limitiaret",
-       "actionthrottledtext": "Dü heest detdiar aktjuun tufölsis uun en kurten tidjrüm ütjfeerd.\nWees so gud an ferschük det glik noch ans weder.",
+       "actionthrottledtext": "Dü heest detdiar aktjuun tufölsis uun en kurten tidjrüm ütjfeerd. Am masbrük ütjtuslütjen, as din aktjuun ufbreegen wurden.\nWees so gud an ferschük det glik noch ans weder.",
        "protectedpagetext": "Detdiar sidj as seekert wurden, am dat diar näämen wat feranert.",
        "viewsourcetext": "Dü könst di kweltekst faan detdiar sidj uunluke an ham uk kopiare.",
        "viewyourtext": "Dü könst di code faan <strong>din feranrang</strong> faan detdiar sidj uunluke an kopiare.",
        "mypreferencesprotected": "Dü heest ei det brükerrocht, am din iinstelangen tu feranrin.",
        "ns-specialprotected": "Spezial-sidjen kön ei bewerket wurd.",
        "titleprotected": "En sidj mä didiar nööm koon ei uunlaanj wurd.\nDi brüker [[User:$1|$1]] hää det sidj speret, an di grünj as: \"''$2''\".",
-       "filereadonlyerror": "Det datei „$1“ koon ei feranert wurd, auer uun det fertiaknis „$2“ bluas leesen wurd koon.\nDi grünj faan di administraator as: „$3“.",
+       "filereadonlyerror": "Det datei „$1“ koon ei feranert wurd, auer uun det fertiaknis „$2“ bluas leesen wurd koon.\nDi grünj faan di süsteem-administraator as: „$3“.",
        "invalidtitle-knownnamespace": "Ferkiard auerskraft uun di nöömrüm „$2“ an tekst „$3“",
        "invalidtitle-unknownnamespace": "Ferkiard auerskraft uun di ünbekäänd nöömrüm „$1“ an tekst „$2“",
        "exception-nologin": "Ei uunmeldet",
        "createacct-reason": "Grünj",
        "createacct-reason-ph": "Huaram dü en ööder brükerkonto iinrachtst",
        "createacct-submit": "Din brükerkonto iinracht",
-       "createacct-another-submit": "En ööder brükerkonto iinracht",
+       "createacct-another-submit": "Brükerkonto iinracht",
        "createacct-benefit-heading": "{{SITENAME}} woort faan lidj üs di maaget.",
        "createacct-benefit-body1": "{{PLURAL:$1|feranrang|feranrangen}}",
        "createacct-benefit-body2": "{{PLURAL:$1|sidj|sidjen}}",
        "wrongpasswordempty": "Dü heest nian paaswurd iinden.\nFerschük det man noch ans.",
        "passwordtooshort": "Paaswurden skel tumanst {{PLURAL:$1|1 tiaken|$1 tiakens}} lung wees.",
        "passwordtoolong": "Paaswurden kön ei linger üs {{PLURAL:$1|1 tiaken|$1 tiakens}} wees.",
+       "passwordtoopopular": "Flooksis brükt paaswurden san ei tuläät. Wees so gud an schük en ööder, muar aparte paaswurd ütj.",
        "password-name-match": "Dü könst dan brükernööm ei üs paaswurd nem.",
        "password-login-forbidden": "Didiar brükernööm mä detdiar paaswurd as ei tuläät.",
        "mailmypassword": "Paaswurd turagsaat",
        "resetpass_submit": "Paaswurd saat an uunmelde",
        "changepassword-success": "Din paaswurd as feranert wurden!",
        "changepassword-throttled": "Dü heest tufölsis fersoocht, di uuntumeldin.\nWees so gud an teew $1, iar dü det noch ans ferschükst.",
+       "botpasswords": "Bot-paaswurden",
+       "botpasswords-summary": "Mä <em>bot-paaswurden</em> koon üüb en brükerkonto auer't API tugreben wurd. A brükerrochten bi uunmeldang mä en bot-paaswurd san kört.\n\nWan dü ei witjst, huaram dü det du wel, skulst dü det uk ei du. Näämen fraaget di, en paaswurd iinturachten, an det do widjertudun.",
+       "botpasswords-no-central-id": "Am bot-paaswurden tu brüken, skel dü bi en sentralisiaret brükerkonto uunmeldet wees.",
+       "botpasswords-existing": "Bot-paaswurden",
+       "botpasswords-createnew": "En nei bot-paaswurd maage",
+       "botpasswords-editexisting": "En bot-paaswurd feranre",
+       "botpasswords-label-appid": "Bot-nööm:",
+       "botpasswords-label-create": "Maage",
+       "botpasswords-label-update": "Aktualisiare",
+       "botpasswords-label-cancel": "Ufbreeg",
+       "botpasswords-label-delete": "Strik",
+       "botpasswords-label-resetpassword": "Paaswurd turagsaat",
+       "botpasswords-label-grants": "Mögelk brükerrochten:",
+       "botpasswords-help-grants": "Mä arke brükerrocht könst dü üüb ööder brükerrochten faan en brükerkonto tugrip. Luke iin uun det [[Special:ListGrants|tabel]] am muar informatjuun.",
+       "botpasswords-label-restrictions": "Kört brükerrochten:",
+       "botpasswords-label-grants-column": "Tugestenen",
+       "botpasswords-bad-appid": "Di bot-nööm \"$1\" gongt ei.",
+       "botpasswords-insert-failed": "Di bot-nööm \"$1\" küd ei apnimen wurd. Ferlicht as hi al diar?",
+       "botpasswords-update-failed": "Di bot-nööm \"$1\" küd ei apnimen wurd. As hi stregen wurden?",
+       "botpasswords-created-title": "Bot-paaswurd as iinracht wurden.",
+       "botpasswords-created-body": "Det bot-paaswurd \"$1\" as iinracht wurden an uun funktjuun.",
+       "botpasswords-updated-title": "Bot-paaswurd as aktualisiaret wurden.",
+       "botpasswords-updated-body": "Det bot-paaswurd \"$1\" as aktualisiaret wurden an uun funktjuun.",
+       "botpasswords-deleted-title": "Bot-paaswurd as stregen wurden.",
+       "botpasswords-deleted-body": "Det bot-paaswurd \"$1\" as stregen wurden.",
        "resetpass_forbidden": "Det paaswurd koon ei feranert wurd.",
        "resetpass-no-info": "Dü skel di uunmelde, am üüb det sidj tutugripen.",
        "resetpass-submit-loggedin": "Paaswurd feranre",
        "right-blockemail": "Brüker spere för't e-mail schüüren",
        "right-hideuser": "Brükernööm spere an fersteeg",
        "right-ipblock-exempt": "Ütjnoom faan IP-speren, automaatisk speren an range-speren",
-       "right-proxyunbannable": "Ütjnoom faan automaatisk proxy-speren",
        "right-unblockself": "Sper apheew för ään salew",
        "right-protect": "Sidjenseekerhaid feranre an kaskaaden-seekert sidjen bewerke",
        "right-editprotected": "Sidjen bewerke, diar mä „{{int:protect-level-sysop}}“ seekert san.",
        "watchthisupload": "Luke efter detdiar datei",
        "filewasdeleted": "En datei mä didiar nööm as al ans huuchschüürd an leederhen weder stregen wurden. Luke iarst ans iin uun $1, iar dü det datei würelk seekerst.",
        "filename-bad-prefix": "Di dateinööm begant mä '''„$1“'''. Sok nöömer kem miast faan digitaalkameras an sai ei föl ütj.\nNem en beedern nööm för det datei.",
-       "upload-success-subj": "Det huuchschüüren hää loket.",
-       "upload-success-msg": "Det huuchschüüren faan [$2] hää loket an stäänt nü diar: [[:{{ns:file}}:$1]]",
-       "upload-failure-subj": "Bi't huuchschüüren as wat skiaf gingen.",
-       "upload-failure-msg": "Diar as wat skiaf gingen bi't huuchschüüren faan [$2]:\n\n$1",
-       "upload-warning-subj": "Wäärnang",
-       "upload-warning-msg": "Diar as wat skiaf gingen bi't huuchschüüren faan [$2]. Gung turag tu't  [[Special:Upload/stash/$1|sidj för't huuchschüüren]], am det üüb a rä tu fun.",
        "upload-proto-error": "Ferkiard protokol",
        "upload-proto-error-text": "Det URL skal mä <code>http://</code> of <code>ftp://</code> began.",
        "upload-file-error": "Diar as wat skiaf gingen",
        "upload-dialog-button-done": "Klaar",
        "upload-dialog-button-save": "Seekre",
        "upload-dialog-button-upload": "Huuchschüür",
-       "upload-form-label-select-file": "Datei ütjschük",
        "upload-form-label-infoform-title": "Enkelthaiden",
        "upload-form-label-infoform-name": "Nööm",
        "upload-form-label-infoform-description": "Beskriiwang",
        "querypage-disabled": "Detdiar spezial-sidj as ei aktiif, am det süsteem ei tu auerläästin.",
        "apihelp": "Halep för API",
        "apihelp-no-such-module": "Moduul \"$1\" ei fünjen.",
+       "apisandbox": "API spelplaats",
        "booksources": "Schük efter ISBN-numer",
        "booksources-search-legend": "Schük efter bukloodens",
        "booksources-search": "Schük",
        "wlheader-showupdated": "Nei feranert sidjen wurd '''fäät''' uunwiset.",
        "wlnote": "Diar {{PLURAL:$1|stäänt det leetst feranrang|stun a leetst <strong>$1</strong> feranrangen}} faan a leetst {{PLURAL:$2|stünj|<strong>$2</strong> stünjen}}. Stant: $3, klook $4.",
        "wlshowlast": "Wise a feranrangen faan a leetst $1 stünjen, $2 daar.",
-       "watchlistall2": "aaltumaal",
        "watchlist-options": "Iinstelangen för't uunwisin",
        "watching": "Uun't uug behual ...",
        "unwatching": "Ei uun't uug behual ...",
        "special-characters-title-minus": "minus tiaken",
        "mw-widgets-dateinput-no-date": "Nian dootem ütjsoocht",
        "mw-widgets-titleinput-description-new-page": "sidj jaft at noch ei",
-       "mw-widgets-titleinput-description-redirect": "widjerfeerang tu $1"
+       "mw-widgets-titleinput-description-redirect": "widjerfeerang tu $1",
+       "randomrootpage": "Tufelag stamsidj"
 }
index 2a4e4a0..8364baa 100644 (file)
@@ -75,7 +75,7 @@
        "fri": "Dih",
        "sat": "DiS",
        "january": "dhen Fhaoilleach",
-       "february": "dhen Ghearrain",
+       "february": "dhen Ghearran",
        "march": "dhen Mhàrt",
        "april": "dhen Ghiblean",
        "may_long": "dhen Chèitean",
@@ -87,7 +87,7 @@
        "november": "dhen t-Samhain",
        "december": "dhen Dùbhlachd",
        "january-gen": "dhen Fhaoilleach",
-       "february-gen": "dhen Ghearrain",
+       "february-gen": "dhen Ghearran",
        "march-gen": "dhen Mhàrt",
        "april-gen": "dhen Ghiblean",
        "may-gen": "dhen Chèitean",
        "version-hook-subscribedby": "'Ga fho-sgrìobhadh le",
        "version-version": "($1)",
        "version-no-ext-name": "[gun ainm]",
-       "version-svn-revision": "r$1",
        "version-license": "Ceadachas MediaWiki",
        "version-ext-license": "Ceadachas",
        "version-ext-colheader-name": "Leudachan",
index b45bfc6..81c3986 100644 (file)
        "previewnote": "'''Lembre que esta é só unha vista previa e que aínda non gardou os seus cambios!'''",
        "continue-editing": "Ir ata a caixa de edición",
        "previewconflict": "Esta vista previa mostra o texto na área superior tal e como aparecerá se escolle gardar.",
-       "session_fail_preview": "'''O sistema non pode procesar a súa edición porque se perderon os datos de inicio da sesión.\nPor favor, inténteo de novo.\nSe segue sen funcionar, probe a [[Special:UserLogout|saír do sistema]] e volver entrar.'''",
-       "session_fail_preview_html": "'''O sistema non pode procesar a súa edición porque se perderon os datos de inicio da sesión.'''\n\n''Dado que {{SITENAME}} ten activado o HTML simple, agóchase a vista previa como precaución contra ataques mediante JavaScript.''\n\n'''Se este é un intento de facer unha edición lexítima, por favor, inténteo de novo.\nSe segue sen funcionar, probe a [[Special:UserLogout|saír do sistema]] e volver entrar.'''",
+       "session_fail_preview": "Sentímolo! Non podemos procesar a súa edición porque se perderon os datos de inicio da sesión.\n\nA súa sesión pode que fose pechada. <strong> Por favor, verifique se aínda está conectado e probe de novo</strong>. En caso de que siga sen funcionar, intente [[Special:UserLogout|saír]] e volver a entrar na súa conta, e verifique que o seu navegador permite o uso de \"cookies\" neste sitio.",
+       "session_fail_preview_html": "Sentímolo! Non foi posible procesar a edición debido á pérdida de datos da súa sesión.\n\n<em>Como a wiki {{SITENAME}} posibilita o uso de HTML puro, a vista previa está oculta por precaución contra ataques con JavaScript.</em>\n\n<strong>Se este é un intento lexítimo de edición probe de novo, por favor</strong>. \nEn caso de que continúe sen funcionar, intente [[Special:UserLogout|saír]] e volver a entrar na súa conta, e verifique se o seu navegador permite o uso de ''cookies'' deste sitio.",
        "token_suffix_mismatch": "'''Rexeitouse a súa edición porque o seu cliente confundiu os signos de puntuación na edición.'''\nRexeitouse a edición para evitar que se corrompa o texto do artigo.\nIsto pode acontecer porque estea a empregar un servizo de proxy anónimo defectuoso baseado na web.",
        "edit_form_incomplete": "'''Algunhas partes do formulario de edición non chegaron ao servidor; comprobe que a súa modificación está intacta e inténteo de novo.'''",
        "editing": "Editando \"$1\"",
        "mergehistory-empty": "Non hai revisións que se poidan fusionar.",
        "mergehistory-done": "$3 {{PLURAL:$3|revisión|revisións}} de $1 {{PLURAL:$3|fusionouse|fusionáronse}} sen problemas con [[:$2]].",
        "mergehistory-fail": "Non se puido fusionar o historial; comprobe outra vez os parámetros de páxina e data.",
+       "mergehistory-fail-bad-timestamp": "O selo de tempo é incorrecto.",
+       "mergehistory-fail-invalid-source": "A páxina de orixe é incorrecta.",
+       "mergehistory-fail-invalid-dest": "A páxina destino é incorrecta.",
+       "mergehistory-fail-no-change": "A fusión de históricos non fusionou ningunha revisión. Por favor, verifique os parámetros de páxina e tempo.",
+       "mergehistory-fail-permission": "Permisos insuficientes para fusionar os historiais.",
+       "mergehistory-fail-self-merge": "A páxina fonte e destino son a mesma.",
+       "mergehistory-fail-timestamps-overlap": "As revisións fonte sobreescriben ou veñen despois das revisións de destino.",
        "mergehistory-fail-toobig": "Non se puido fusionar o historial, xa que supón trasladar máis revisións que o límite de $1 {{PLURAL:$1|revisión|revisións}}.",
        "mergehistory-no-source": "Non existe a páxina de orixe \"$1\".",
        "mergehistory-no-destination": "Non existe a páxina de destino \"$1\".",
        "uploaded-script-svg": "Atopado elemento de comandos \"$1\" no ficheiro SVG subido.",
        "uploaded-hostile-svg": "Atopado CSS non seguro no elemento de estilo do ficheiro SVG subido.",
        "uploaded-event-handler-on-svg": "Fixar atributos de xestión de eventos <code>$1=\"$2\"</code> no está permitido en ficheiros SVG.",
-       "uploaded-href-unsafe-target-svg": "Atopado href a obxectivo non seguro <code>&lt;$1 $2=\"$3\"&gt;</code> no ficheiro SVG subido.",
+       "uploaded-href-attribute-svg": "os atributos href nos ficheiros SVG só están autorizados a ligar a direccións http:// ou https://, atopado <code>&lt;$1 $2=\"$3\"&gt;</code>.",
+       "uploaded-href-unsafe-target-svg": "Atopado href a datos non seguros: dirección URI <code>&lt;$1 $2=\"$3\"&gt;</code> no ficheiro SVG subido.",
        "uploaded-animate-svg": "Atopada etiqueta \"animate\" que podería estar cambiando a href, usando o atributo \"from\" <code>&lt;$1 $2=\"$3\"&gt;</code> no ficheiro SVG subido.",
        "uploaded-setting-event-handler-svg": "Fichar os atributos de xestión de eventos non está permitido, atopado <code>&lt;$1 $2=\"$3\"&gt;</code> no ficheiro SVG subido.",
        "uploaded-setting-href-svg": "Usar a etiqueta \"set\" para engadir o atributo \"href\" ó elemento pai non está permitido.",
        "upload-dialog-button-done": "Feito",
        "upload-dialog-button-save": "Gardar",
        "upload-dialog-button-upload": "Subir",
-       "upload-form-label-select-file": "Seleccionar un ficheiro",
        "upload-form-label-infoform-title": "Detalles",
        "upload-form-label-infoform-name": "Nome",
        "upload-form-label-infoform-name-tooltip": "Un título único descritivo para o ficheiro, que servirá como un nome de ficheiro. Pode usar unha linguaxe clara con espazos. Non inclúa a extensión do ficheiro.",
        "foreign-structured-upload-form-label-own-work-message-shared": "Certifico que son o propietario dos dereitos de autor deste ficheiro, e que concordo a liberar irrevocablemente este ficheiro a Wikimedia Commons baixo a licenza [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0], e que concordo cos [https://wikimediafoundation.org/wiki/Terms_of_Use Termos de uso].",
        "foreign-structured-upload-form-label-not-own-work-message-shared": "Se non posúe os dereitos de autor deste ficheiro, ou quere liberalo baixo unha licenza diferente, considere usar o [https://commons.wikimedia.org/wiki/Special:UploadWizard Asistente de subas de Commons].",
        "foreign-structured-upload-form-label-not-own-work-local-shared": "Tamén pode interesarlle usar [[Special:Upload|a páxina de carga en {{SITENAME}}]], se o sitio permite a suba deste ficheiro nas súas políticas.",
-       "foreign-structured-upload-form-2-label-intro": "Grazas por doar unha imaxe para ser usada en {{SITENAME}}. Só debería continuar se se cumpren varias condicións:",
-       "foreign-structured-upload-form-2-label-ownwork": "Debe ser completamente <strong>creada por vostede</strong>, non só tomada de Internet",
-       "foreign-structured-upload-form-2-label-noderiv": "Non ten que conter <strong>traballo creado por outra persoa</strong>, nin estar  inspirado por outras obras",
-       "foreign-structured-upload-form-2-label-useful": "Debe ser <strong>educativo e útil</strong> para ensinar a outros",
-       "foreign-structured-upload-form-2-label-ccbysa": "Debe aceptar a <strong>publicación de forma irrevogable</strong> en Internet baixo a licenza [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0]",
-       "foreign-structured-upload-form-2-label-alternative": "Se non todos os criterios de arriba son certos, aínda pode subir este ficheiro usando o [https://commons.wikimedia.org/wiki/Special:UploadWizard Asistente de subas de Commons], mentres estea dispoñible baixo unha licenza libre.",
-       "foreign-structured-upload-form-2-label-termsofuse": "Ó subir o ficheiro, vostede da fe de que é o dono dos dereitos de autor neste ficheiro, e acepta irrevogablemente liberar este ficheiro a Wikimedia Commons baixo a licenza Creative Commons Attribution-ShareAlike 4.0, e acepta os [https://wikimediafoundation.org/wiki/Terms_of_Use Termos de Uso].",
-       "foreign-structured-upload-form-3-label-question-website": "Descargou esta imaxe dun sitio web, ou obtívoa dunha busca de imaxes?",
-       "foreign-structured-upload-form-3-label-question-ownwork": "Creou esta imaxe (tomou a foto, fixo o debuxo, etc) vostede mesmo?",
-       "foreign-structured-upload-form-3-label-question-noderiv": "Contén, ou está inspirada por, traballo propiedade de calquera outra persoa, como un logotipo?",
-       "foreign-structured-upload-form-3-label-yes": "Si",
-       "foreign-structured-upload-form-3-label-no": "Non",
-       "foreign-structured-upload-form-3-label-alternative": "Desafortunadamente, neste caso, esta ferramenta non é compatible para subir este ficheiro. Aínda pode ser capaz de subir este ficheiro usando o [https://commons.wikimedia.org/wiki/Special:UploadWizard Asistente de subas de Commons], mentres estea dispoñible baixo unha licenza libre.",
-       "foreign-structured-upload-form-4-label-good": "Ó usar esta ferramenta, pode subir gráficos educativos que crease e fotografías que fixese, que non conteñan traballo de ninguén máis.",
-       "foreign-structured-upload-form-4-label-bad": "Non pode subir imaxes atopadas nun motor de buscas ou descargadas doutros sitios web.",
        "backend-fail-stream": "Non se puido transmitir o ficheiro \"$1\".",
        "backend-fail-backup": "Non se puido facer unha copia de seguridade do ficheiro \"$1\".",
        "backend-fail-notexists": "O ficheiro \"$1\" non existe.",
        "querypage-disabled": "Esta páxina especial está desactivada por razóns de rendemento.",
        "apihelp": "Axuda coa API",
        "apihelp-no-such-module": "Non se atopou o módulo \"$1\".",
+       "apisandbox": "Zona de probas API",
+       "apisandbox-jsonly": "É preciso activar o JavaScript para usar a zona de probas.",
+       "apisandbox-api-disabled": "API está desactivado neste sitio.",
+       "apisandbox-intro": "Use esta páxina para experimentar co <strong>servizo web da API de MediaWiki</strong>.\nConsulte a [[mw:API:Main page| documentación da API]] para obter máis información sobre o uso da API. Exemplo: [//www.mediawiki.org/wiki/API#A_simple_example obter o contido dunha páxina de inicio]. Seleccione unha acción para ollar máis exemplos.\n\nTeña en conta que, aínda que esta é unha páxina de probas, as accións que realice nesta páxina poden modificar o wiki.",
+       "apisandbox-fullscreen": "Expandir panel",
+       "apisandbox-fullscreen-tooltip": "Expande o panel da zona de probas para encher a pantalla do navegador.",
+       "apisandbox-unfullscreen": "Mostrar a páxina",
+       "apisandbox-unfullscreen-tooltip": "Reduce o panel da zona de probas, así as ligazóns de navegación MediaWiki están dispoñibles.",
+       "apisandbox-submit": "Facer a solicitude",
+       "apisandbox-reset": "Limpar",
+       "apisandbox-retry": "Reintentar",
+       "apisandbox-loading": "Cargando información para módulo de API \"$1\"...",
+       "apisandbox-load-error": "Houbo un erro mentres se cargaba a información do módulo API \"$1\": $2",
+       "apisandbox-no-parameters": "O módulo de API non ten parámetros.",
+       "apisandbox-helpurls": "Ligazóns de axuda",
+       "apisandbox-examples": "Exemplos",
+       "apisandbox-dynamic-parameters": "Parámetros adicionais",
+       "apisandbox-dynamic-parameters-add-label": "Engadir parámetro:",
+       "apisandbox-dynamic-parameters-add-placeholder": "Nome do parámetro",
+       "apisandbox-dynamic-error-exists": "Xa existe un parámetro co nome \"$1\".",
+       "apisandbox-deprecated-parameters": "Parámetros obsoletos",
+       "apisandbox-fetch-token": "Auto-enchido do identificador",
+       "apisandbox-submit-invalid-fields-title": "Algúns campos non son válidos",
+       "apisandbox-submit-invalid-fields-message": "Por favor, amañe os campos marcados e inténteo de novo.",
+       "apisandbox-results": "Resultados",
+       "apisandbox-sending-request": "Enviando a petición á API...",
+       "apisandbox-loading-results": "Recibindo os resultados da API...",
+       "apisandbox-results-error": "Houbo un erro mentres se cargaba a resposta á consulta da API: $1.",
+       "apisandbox-request-url-label": "URL da solicitude:",
+       "apisandbox-request-time": "Duración da solicitude: {{PLURAL:$1|$1 ms}}",
+       "apisandbox-results-fixtoken": "Correxir identificador e reenviar",
+       "apisandbox-results-fixtoken-fail": "Erro ó recuperar o identificador \"$1\".",
+       "apisandbox-alert-page": "Os campos nesta páxina non son válidos.",
+       "apisandbox-alert-field": "O valor deste campo non é válido.",
        "booksources": "Fontes bibliográficas",
        "booksources-search-legend": "Procurar fontes bibliográficas",
        "booksources-search": "Procurar",
        "import-nonewrevisions": "Non se importou ningunha revisión (xa estaban todas presentes ou saltáronse por erros).",
        "xml-error-string": "$1 na liña $2, columna $3 (byte $4): $5",
        "import-upload": "Cargar datos XML",
-       "import-token-mismatch": "Perdéronse os datos da sesión. Por favor, inténteo de novo.",
+       "import-token-mismatch": "Perda de datos da sesión.\n\nA súa sesión puido ser pechada. <strong>Por favor, verifique se aínda está conectado e probe de novo</strong>. \nEn caso de que continúe sen funcionar, intente [[Special:UserLogout|saír]] e volver a entrar na súa conta, e verifique se o seu navegador permite o uso de ''cookies'' deste sitio.",
        "import-invalid-interwiki": "Non se pode importar desde o wiki escificado.",
        "import-error-edit": "Non foi posible importar a páxina \"$1\" porque non ten os permisos necesarios para editala.",
        "import-error-create": "Non foi posible importar a páxina \"$1\" porque non ten os permisos necesarios para creala.",
        "expand_templates_generate_xml": "Mostrar a árbore de análise XML",
        "expand_templates_generate_rawhtml": "Mostrar o HTML en bruto",
        "expand_templates_preview": "Vista previa",
-       "expand_templates_preview_fail_html": "<em>Dado que o código HTML puro está activado en {{SITENAME}} e produciuse unha perda dos datos da sesión, a vista previa está oculta como precaución contra ataques mediante código JavaScript.</em>\n\n<strong>Se este é un intento lexítimo de acceso á vista previa, inténteo de novo.</strong>\nSe segue sen funcionar, probe a [[Special:UserLogout|saír]] e volver a entrar coa súa conta.",
+       "expand_templates_preview_fail_html": "<em>Dado que o código HTML puro está activado en {{SITENAME}} e produciuse unha perda dos datos da sesión, a vista previa está oculta como precaución contra ataques mediante código JavaScript.</em>\n\n<strong>Se este é un intento lexítimo de acceso á vista previa, inténteo de novo.</strong>\nSe segue sen funcionar, probe a [[Special:UserLogout|saír]] e volver a entrar coa súa conta, e revise que o seu navegador permite o uso de \"cookies\" deste sitio.",
        "expand_templates_preview_fail_html_anon": "<em>Dado que o código HTML puro está activado en {{SITENAME}} e produciuse unha perda dos datos da sesión, a vista previa está oculta como precaución contra ataques mediante código JavaScript.</em>\n\n<strong>Se este é un intento lexítimo de acceso á vista previa, probe a [[Special:UserLogout|saír]] e volver a entrar coa súa conta.</strong>",
        "expand_templates_input_missing": "Necesita proporcionar polo menos algún texto de entrada.",
        "pagelanguage": "Cambiar a lingua da páxina",
index 7d986c7..832a2c6 100644 (file)
        "passwordreset-domain": "डोमेन:",
        "passwordreset-email": "ईमेल नामो:",
        "passwordreset-emailelement": "वापरप्याचें नांव: \n$1\n\nतात्पुरतें गुपीत उतर: \n$2",
-       "passwordreset-emailsent": "गुपीत उतर परतून तयार करपाचो ईमेल धाडला",
+       "passwordreset-emailsentemail": "गुपीत उतर परतून तयार करपाचो ईमेल धाडला",
        "changeemail": "ईमेल संदेश बदल्ला",
        "changeemail-oldemail": "सद्याचो ईमेल नामो:",
        "changeemail-newemail": "नवो ईमेल नामो:",
        "booksources-search": "सोद",
        "log": "सोत्रां",
        "allpages": "सगळीं पाना",
+       "nextpage": "फुडलें पान ($1)",
+       "prevpage": "फाटलें पान ($1)",
        "allarticles": "सगळीं पानां",
        "allpagessubmit": "वचात",
        "categories": "वर्ग",
        "unwatch": "पळोवंक नासलें",
        "watchlist-details": "लक्ष {{PLURAL:$1|$1वळेरींतलें|$1 वळेंरींतली}} {{PLURAL:$1|$1पान|$1 पानां}} उलोवपाची पानां सोडून",
        "wlshowlast": "फाटलें $1 वरांचें $2 दिसांचें  दाखयात",
-       "watchlistall2": "सगळें",
        "watchlist-options": "लक्षवळेंरींतलो पर्याय",
        "delete-legend": "काडून उडयात",
        "actioncomplete": "क्रिया पुराय जाल्या",
index 9575ede..4028d1b 100644 (file)
        "nstab-template": "Saacho",
        "nstab-help": "Adarachem pan",
        "nstab-category": "Vorg",
+       "mainpage-nstab": "Mukhel pan",
        "nosuchaction": "Oslem torechem karya nam",
        "nosuchspecialpage": "Oslem kaich khashellem pan na",
        "error": "Chuk",
        "passwordreset-domain": "Domain:",
        "passwordreset-email": "Email potto:",
        "passwordreset-emailelement": "Vapurpeachem nanv: \n$1\n\nTatpurtem gupitutor: \n$2",
-       "passwordreset-emailsent": "Gupitutor portun tharaipacho email dhadla.",
+       "passwordreset-emailsentemail": "Gupitutor portun tharaipacho email dhadla.",
        "changeemail": "Email potto bodol",
        "changeemail-oldemail": "Sodhyacho email potto:",
        "changeemail-newemail": "Novo email potto:",
        "prevn": "adlem {{PLURAL:$1|$1}}",
        "nextn": "fuddlem {{PLURAL:$1|$1}}",
        "next-page": "Fuddlem pan",
-       "prevn-title": "{{PLURAL:$1|Fattlem $1 porinnam|Fattlem $1 porinam}}",
+       "prevn-title": "{{PLURAL:$1|Fattlo $1 porinam|Fattleo $1 porinaman}}",
        "nextn-title": "{{PLURAL:$1|Fuddlem $1 porinnam|Fudnlim $1 porinnam}}",
        "shown-title": "Dor eka panar {{PLURAL:$1|porinam}} dakhoi",
        "viewprevnext": "($1 {{int:pipe-separator}} $2) ($3) poloi",
        "speciallogtitlelabel": "Vishoi vo vapurpi:",
        "log": "Sotram",
        "allpages": "Sogllim panam",
-       "nextpage": "Mukklem pan ($1)",
-       "prevpage": "Ad'dlem pan ($1)",
+       "nextpage": "Fuddlem pan ($1)",
+       "prevpage": "Fattlem pan ($1)",
        "allpagesfrom": "Hanga thavn suru zatelea panank dakhoi:",
        "allarticles": "Sogllim panam",
        "allpagessubmit": "Voch",
        "watchlist-details": "Tujea sadurvollerint {{PLURAL:$1|$1 pan asa|$1 panam asat}}, ulovpachim panam veglim mezonastanam.",
        "wlheader-showupdated": "Tujea fatle bhette san bodol'lean tim panam '''datt''' dakhoileant.",
        "wlshowlast": "Xevottchim $1 voram $2 dis  dakhoi",
-       "watchlistall2": "soglle",
        "watchlist-options": "Sadurvollericheo poryay",
        "watching": "Disht dovortanv...",
        "unwatching": "Disht kaddthanv...",
        "contributions": "{{GENDER:$1|Vapuddpi}} yogdanam",
        "contributions-title": "$1 hea vapuddpean kelelim yogdanam",
        "mycontris": "Yogdanam",
+       "anoncontribs": "Yogdanam",
        "contribsub2": "{{GENDER:$3|$1}} hacheo ($2)",
        "uctop": "(atachem)",
        "month": "Mhoinea savn (ani adichem):",
        "allmessagesdefault": "Falta sondex mozkur",
        "thumbnail-more": "Vhodlem kor",
        "thumbnail_error": "Lhan-imaz toiar kortana chuk zali. Karonn: $1",
-       "tooltip-pt-userpage": "Tujem vapuddpachem pan",
-       "tooltip-pt-mytalk": "Tumchem bhasabhasachem pan",
-       "tooltip-pt-preferences": "Tumcheo avddi",
+       "tooltip-pt-userpage": "{{GENDER:|Tujem vapuddpachem}} pan",
+       "tooltip-pt-mytalk": "{{GENDER:|Tumchem}} bhasabhasachem pan",
+       "tooltip-pt-preferences": "{{GENDER:|Tumcheo}} avddi",
        "tooltip-pt-watchlist": "Bodlachea dekhrekh korpachea panachi volleri",
-       "tooltip-pt-mycontris": "Tujea yogdanachi suchi",
+       "tooltip-pt-mycontris": "{{GENDER:|Tujea}} yogdanachi suchi",
        "tooltip-pt-login": "Tumkam sotrormbh korunk protsavan asa; pun hem soktichem nhoi",
        "tooltip-pt-logout": "Sotr xevott",
        "tooltip-pt-createaccount": "Tumi khatem ugdun sotrorombh korunk protsavn asa; pun toxi sokti nam",
        "tooltip-t-whatlinkshere": "Hanga zoddlelea sogllea wiki pananchi volleri",
        "tooltip-t-recentchangeslinked": "Hea panak-sun zoddlelea panachim halinche bodol",
        "tooltip-feed-atom": "Hea panak Atom purovnni",
-       "tooltip-t-contributions": "Hea vapuddpeachea yogdanachi suchi",
+       "tooltip-t-contributions": "{{GENDER:$1|Hea vapuddpeachea}} yogdanachi suchi",
        "tooltip-t-emailuser": "Hea vapuddpeak email patthoi",
        "tooltip-t-upload": "Faili upload kor",
        "tooltip-t-specialpages": "Sogllea khaxelim pananchi volleri",
index 2081f83..936af3f 100644 (file)
        "querypage-disabled": "Die Spezialsyte isch deaktiviert wore us Leischtigserhaltigs-Grind.",
        "apihelp": "API-Hilff",
        "apihelp-no-such-module": "Ds Modul «$1» lat sech nid la finde.",
+       "apisandbox": "API-Sandchaschte",
+       "apisandbox-api-disabled": "D API isch uf däm Wiki deaktiviert wore.",
+       "apisandbox-intro": "Die Syte chasch bruche fir Versuech mit dr '''MediaWiki-API'''.\nIn dr [//www.mediawiki.org/wiki/API:Main_page/de Dokumäntation zue dr API] het s no meh Hiiwys zue ihre Nutzig. Byschpel: [//www.mediawiki.org/wiki/API:Main_page/de#Beispiel Dr Inhalt vu dr Hauptsyte abruefe]. Fir meh Byschpel eini vu dr verfiegbare Aktionen uuswehle.",
+       "apisandbox-submit": "Aafrog uusfiere",
+       "apisandbox-reset": "Lääre",
+       "apisandbox-examples": "Byyschpil",
+       "apisandbox-results": "Ergebnis",
+       "apisandbox-request-url-label": "Aaforderigs-URL:",
+       "apisandbox-request-time": "Aafrogzyt: $1",
        "booksources": "ISBN-Suech",
        "booksources-search-legend": "Suech no Bezugsquälle fir Biecher",
        "booksources-search": "Sueche",
index 9c70672..6b75f8f 100644 (file)
        "querypage-disabled": "કાર્યક્ષમતાના કારણે આ ખાસ પાનું નિષ્ક્રિ કરાયું છે.",
        "apihelp": "API મદદ",
        "apihelp-no-such-module": "સાધન જૂથ \"$1\" ન મળ્યું.",
+       "apisandbox-submit": "વિનંતી કરો",
+       "apisandbox-reset": "સાફ કરો",
+       "apisandbox-examples": "ઉદાહરણ",
+       "apisandbox-results": "પરિણામ",
        "booksources": "પુસ્તક સ્રોત",
        "booksources-search-legend": "પુસ્તક સ્રોત શોધો",
        "booksources-isbn": "આઇએસબીએન:",
index ca39584..b45fd6b 100644 (file)
@@ -12,7 +12,8 @@
                        "아라",
                        "Mywood",
                        "Impersonator 1",
-                       "LNDDYL"
+                       "LNDDYL",
+                       "唐吉訶德的侍從"
                ]
        },
        "tog-underline": "鏈接加底線:",
        "editfont-monospace": "等距字型",
        "editfont-sansserif": "無襯線字型",
        "editfont-serif": "襯線字型",
-       "sunday": "星期日",
-       "monday": "星期一",
-       "tuesday": "星期二",
-       "wednesday": "星期三",
-       "thursday": "星期四",
-       "friday": "星期五",
-       "saturday": "星期六",
+       "sunday": "禮拜日",
+       "monday": "禮拜一",
+       "tuesday": "禮拜二",
+       "wednesday": "禮拜三",
+       "thursday": "禮拜四",
+       "friday": "禮拜五",
+       "saturday": "禮拜六",
        "sun": "日",
        "mon": "一",
        "tue": "二",
index 2bd50a8..d86192e 100644 (file)
                        "LaG roiL",
                        "Eraab",
                        "Geagea",
-                       "פוילישער"
+                       "פוילישער",
+                       "DatGuy"
                ]
        },
        "tog-underline": "סימון קישורים בקו תחתי:",
        "tog-hideminor": "הסתרת שינויים משניים ברשימת השינויים האחרונים",
        "tog-hidepatrolled": "הסתרת שינויים בדוקים ברשימת השינויים האחרונים",
        "tog-newpageshidepatrolled": "הסתרת דפים בדוקים ברשימת הדפים החדשים",
-       "tog-hidecategorization": "×\94סתרת ×\94×\95ספ×\95ת ×\95×\94סר×\95ת ×©×\9c ×\93פ×\99×\9d ×\9eקטגוריות",
+       "tog-hidecategorization": "×\94סתרת ×¡×\99×\95×\95×\92 ×\93פ×\99×\9d ×\9cקטגוריות",
        "tog-extendwatchlist": "הרחבת רשימת המעקב כך שתציג את כל השינויים, לא רק את השינויים האחרונים בכל דף",
        "tog-usenewrc": "קיבוץ השינויים לפי דף בשינויים האחרונים וברשימת המעקב",
        "tog-numberheadings": "מספור כותרות אוטומטי",
@@ -70,7 +71,7 @@
        "tog-watchlistreloadautomatically": "רענון אוטומטי של רשימת המעקב בכל פעם שמסנן משתנה (נדרש JavaScript)",
        "tog-watchlisthideanons": "הסתרת עריכות של משתמשים אנונימיים ברשימת המעקב",
        "tog-watchlisthidepatrolled": "הסתרת עריכות בדוקות ברשימת המעקב",
-       "tog-watchlisthidecategorization": "×\94סתרת ×\94×\95ספ×\95ת ×\95×\94סר×\95ת ×©×\9c ×\93פ×\99×\9d ×\9eקטגוריות",
+       "tog-watchlisthidecategorization": "×\94סתרת ×¡×\99×\95×\95×\92 ×\93פ×\99×\9d ×\9cקטגוריות",
        "tog-ccmeonemails": "לשלוח אליי העתקים של הודעות דואר אלקטרוני ששלחתי למשתמשים אחרים",
        "tog-diffonly": "ביטול הצגת תוכן הדף מתחת להשוואות הגרסאות",
        "tog-showhiddencats": "הצגת קטגוריות מוסתרות",
        "changepassword-success": "סיסמתך שונתה בהצלחה!",
        "changepassword-throttled": "ביצעתם לאחרונה ניסיונות רבים מדי להיכנס לחשבון זה.\nאנא המתינו $1 לפני שתנסו שוב.",
        "botpasswords": "ססמאות בוט",
-       "botpasswords-summary": "<em>סס×\9e×\90×\95ת ×\91×\95×\98</em> ×\9e×\90פשר×\95ת ×\9bנס×\94 ×\9c×\97ש×\91×\95×\9f ×\9eשת×\9eש ×\93ר×\9a API ×\9c×\9c×\90 ×©×\99×\9e×\95ש ×\91נת×\95× ×\99 ×\94×\94×\96×\93×\94×\95ת ×\94ר×\90ש×\99×\99×\9d ×©×\9c ×\94×\97ש×\91×\95×\9f. ×\94רש×\90×\95ת ×\94×\9eשת×\9eש ×©×\96×\9e×\99× ×\95ת ×\9c×\9eשת×\9eש ×©× ×\9bנס ×¢×\9d ×¡×¡×\9eת ×\91×\95×\98 ×\99×\9b×\95×\9c×\95ת ×\9c×\94×\99×\95ת ×\9e×\95×\92×\91×\9c×\95ת.",
-       "botpasswords-disabled": "סס×\9e×\90×\95ת ×\91×\95×\98 ×\9b×\91×\95×\99×\95ת.",
-       "botpasswords-no-central-id": "×\9b×\93×\99 ×\9c×\94שת×\9eש ×\91סס×\9e×\90×\95ת ×\91×\95×\98, ×\99ש ×\9c×\94×\99×\9bנס ×\9c×\97ש×\91×\95×\9f ×\9eר×\9b×\96×\99.",
+       "botpasswords-summary": "<em>סס×\9e×\90×\95ת ×\91×\95×\98</em> ×\9e×\90פשר×\95ת ×\9b× ×\99ס×\94 ×\9c×\97ש×\91×\95×\9f ×\9eשת×\9eש ×\91×\90×\9eצע×\95ת API, ×\9c×\9c×\90 ×©×\99×\9e×\95ש ×\91נת×\95× ×\99 ×\94×\94×\96×\93×\94×\95ת ×\94ר×\90ש×\99×\99×\9d ×©×\9c ×\94×\97ש×\91×\95×\9f. × ×\99ת×\9f ×\9c×\94×\92×\91×\99×\9c ×\90ת ×\94רש×\90×\95ת ×\94×\9eשת×\9eש ×\94×\96×\9e×\99× ×\95ת ×\9b×\90שר × ×\9bנס×\99×\9d ×¢×\9d ×¡×¡×\9eת ×\91×\95×\98.",
+       "botpasswords-disabled": "×\90פשר×\95ת ×\94ש×\99×\9e×\95ש ×\91סס×\9e×\90×\95ת ×\91×\95×\98 ×\9e×\91×\95×\98×\9cת.",
+       "botpasswords-no-central-id": "×\9b×\93×\99 ×\9c×\94שת×\9eש ×\91סס×\9e×\90×\95ת ×\91×\95×\98, ×\99ש ×\9c×\94×\99×\9bנס ×¢×\9d ×\97ש×\91×\95×\9f ×\9eשת×\9eש ×\9e×\90×\95×\97×\93.",
        "botpasswords-existing": "ססמאות בוט קיימות",
        "botpasswords-createnew": "יצירת ססמת בוט חדשה",
        "botpasswords-editexisting": "עריכת ססמת בוט קיימת",
        "botpasswords-label-delete": "מחיקה",
        "botpasswords-label-resetpassword": "איפוס ססמה",
        "botpasswords-label-grants": "זיכיונות מתאימים",
-       "botpasswords-help-grants": "×\9b×\9c ×\96×\99×\9b×\99×\95×\9f × ×\95ת×\9f ×\92×\99ש×\94 ×\9c×\94רש×\90×\95ת ×\9eשת×\9eש ×¨×©×\95×\9e×\95ת ×©×\9b×\91ר ×\99ש ×\9c×\97ש×\91×\95×\9f ×\94×\9eשת×\9eש. ×¨' ×\90ת [[Special:ListGrants|טבלת הזיכיונות]] למידע נוסף.",
+       "botpasswords-help-grants": "×\9b×\9c ×\96×\99×\9b×\99×\95×\9f × ×\95ת×\9f ×\92×\99ש×\94 ×\9c×\94רש×\90×\95ת ×\9eשת×\9eש ×¨×©×\95×\9e×\95ת ×©×\99ש ×\9c×\97ש×\91×\95×\9f ×\94×\9eשת×\9eש. ×¢×\99×\99× ×\95 ×\91[[Special:ListGrants|טבלת הזיכיונות]] למידע נוסף.",
        "botpasswords-label-restrictions": "הגבלות שימוש:",
        "botpasswords-label-grants-column": "ניתן זיכיון",
-       "botpasswords-bad-appid": "ש×\9d ×\94×\91×\98×\95 \"$1\" אינו תקין.",
+       "botpasswords-bad-appid": "ש×\9d ×\94×\91×\95×\98 \"$1\" אינו תקין.",
        "botpasswords-insert-failed": "הוספת שם הבוט \"$1\" נכשלה. האם הוא כבר נוסף?",
        "botpasswords-update-failed": "לא היה אפשר לעדכן את שם הבוט \"$1\". האם הוא נמחק?",
-       "botpasswords-created-title": "ססמת בוט נוצרה",
+       "botpasswords-created-title": "סס×\9eת ×\94×\91×\95×\98 × ×\95צר×\94",
        "botpasswords-created-body": "ססמת הבוט \"$1\" נוצרה בהצלחה.",
        "botpasswords-updated-title": "ססמת הבוט עודכנה",
        "botpasswords-updated-body": "ססמת הבוט \"$1\" עודכנה בהצלחה.",
        "botpasswords-deleted-title": "ססמת הבוט נמחקה",
        "botpasswords-deleted-body": "ססמת הבוט \"$1\" נמחקה.",
-       "botpasswords-newpassword": "הססמה החדשה לכניסה <strong>$1</strong> היא <strong>$2</strong>. נא לרשום את זה לעיון עתידי.",
+       "botpasswords-newpassword": "הססמה החדשה לכניסה <strong>$1</strong> היא <strong>$2</strong>. <em>נא לשמור מידע זה לצורך עיון עתידי.</em>",
        "botpasswords-no-provider": "BotPasswordsSessionProvider אינו זמין.",
-       "botpasswords-restriction-failed": "×\94×\92×\91×\9c×\95ת ×©×\9c ×¡×¡×\9eת ×\91×\95×\98 ×\9e×\95× ×¢×\95ת ×\90ת ×\94×\9b× ×\99ס×\94 ×\94×\96×\90ת.",
-       "botpasswords-invalid-name": "ש×\9d ×\94×\9eשת×\9eש ×©× ×\99ת×\9f ×\90×\99× ×\95 ×\9e×\9b×\99×\9c ×\90ת ×¤×¨×\99×\93 ססמאות הבוט (\"$1\").",
+       "botpasswords-restriction-failed": "×\9b× ×\99ס×\94 ×\96×\95 × ×\9e× ×¢×\94 ×\91ש×\9c ×\94×\92×\91×\9c×\95ת ×¢×\9c ×¡×¡×\9e×\90×\95ת ×\91×\95×\98.",
+       "botpasswords-invalid-name": "ש×\9d ×\94×\9eשת×\9eש ×©× ×\99ת×\9f ×\90×\99× ×\95 ×\9e×\9b×\99×\9c ×\90ת ×ª×\95 ×\94פר×\93ת ססמאות הבוט (\"$1\").",
        "botpasswords-not-exist": "למשתמש \"$1\" אין ססמת בוט בשם \"$2\".",
        "resetpass_forbidden": "לא ניתן לשנות סיסמאות.",
        "resetpass-no-info": "נדרשת כניסה לחשבון כדי לגשת לדף זה באופן ישיר.",
        "nowiki_tip": "התעלמות מעיצוב ויקי",
        "image_tip": "קובץ המוצג בתוך הדף",
        "media_tip": "קישור לקובץ מדיה",
-       "sig_tip": "חתימה + שעה",
+       "sig_tip": "×\97ת×\99×\9e×\94 + ×ª×\90ר×\99×\9a ×\95שע×\94",
        "hr_tip": "קו אופקי (השתדלו להימנע משימוש בקו)",
        "summary": "תקציר:",
        "subject": "נושא:",
        "previewnote": "<strong>זִכרו שזו רק תצוגה מקדימה.</strong>\nהשינויים שלכם טרם נשמרו!",
        "continue-editing": "מעבר לאזור העריכה",
        "previewconflict": "תצוגה מקדימה זו מציגה כיצד ייראה הטקסט בחלון העריכה העליון, אם תבחרו לשמור אותו.",
-       "session_fail_preview": "'''לא ניתן לבצע את עריכתכם עקב אובדן מידע הכניסה.'''\nאנא נסו שוב.\nאם זה לא עוזר, נסו [[Special:UserLogout|לצאת מהחשבון]] ולהיכנס אליו שנית.",
-       "session_fail_preview_html": "'''לא ניתן לבצע את עריכתם עקב אובדן מידע הכניסה.'''\n\nכיוון שבאתר זה אפשרות השימוש ב־HTML מאופשרת, התצוגה המקדימה מוסתרת כדי למנוע התקפות JavaScript.\n\n'''אם זהו ניסיון עריכה לגיטימי, אנא נסו שוב.'''\nאם זה לא עוזר, נסו [[Special:UserLogout|לצאת מהחשבון]] ולהיכנס אליו שנית.",
+       "session_fail_preview": "מצטערים! לא ניתן לבצע את עריכתכם עקב אובדן מידע הכניסה.\n\nייתכן שנותקתם מהחשבון. <strong>אנא ודאו שאתם עדיין מחוברים לחשבון ונסו שוב.</strong>\nאם זה עדיין לא עובד, נסו [[Special:UserLogout|לצאת מהחשבון]] ולהיכנס אליו שנית, וודאו שהדפדפן שלכם מאפשר קבלת עוגיות מאתר זה.",
+       "session_fail_preview_html": "מצטערים! לא ניתן לבצע את עריכתם עקב אובדן מידע הכניסה.\n\n<em>כיוון שב{{grammar:תחילית|{{SITENAME}}}} אפשרות השימוש ב־HTML גולמי מופעלת, התצוגה המקדימה מוסתרת כדי למנוע התקפות JavaScript.</em>\n\n<strong>אם זהו ניסיון עריכה לגיטימי, אנא נסו שוב.</strong>\nאם זה עדיין לא עובד, נסו [[Special:UserLogout|לצאת מהחשבון]] ולהיכנס אליו שנית, וודאו שהדפדפן שלכם מאפשר קבלת עוגיות מאתר זה.",
        "token_suffix_mismatch": "'''עריכתך נדחתה כיוון שהדפדפן שלך מחק את תווי הפיסוק באסימון העריכה.'''\nהעריכה נדחתה כדי למנוע בעיות כאלה בטקסט של הדף.\nלעתים התקלה מתרחשת עקב שימוש בשירות פרוקסי אנונימי פגום.",
        "edit_form_incomplete": "'''כמה חלקים מטופס העריכה לא הגיעו לשרת; בדקו היטב שעריכותיכם לא נפגעו ונסו שוב.'''",
        "editing": "עריכת הדף $1",
        "mergehistory-empty": "אין גרסאות למיזוג.",
        "mergehistory-done": "{{PLURAL:$3|גרסה אחת|$3 גרסאות}} של $1 {{PLURAL:$3|מוזגה|מוזגו}} בהצלחה לתוך [[:$2]].",
        "mergehistory-fail": "לא ניתן לבצע את מיזוג הגרסאות, יש לבדוק שנית את הגדרות הדף והזמן.",
+       "mergehistory-fail-bad-timestamp": "התאריך והשעה אינם תקינים.",
+       "mergehistory-fail-invalid-source": "דף המקור אינו תקין.",
+       "mergehistory-fail-invalid-dest": "דף היעד אינו תקין.",
+       "mergehistory-fail-no-change": "מיזוג ההיסטוריה לא מיזג שום גרסה. אנא בדקו שוב את הדף והזמן שציינתם.",
+       "mergehistory-fail-permission": "הרשאות לא מספיקות למיזוג היסטוריה.",
+       "mergehistory-fail-self-merge": "דף המקור זהה לדף היעד.",
+       "mergehistory-fail-timestamps-overlap": "גרסאות המקור חופפות או מגיעות אחרי גרסאות היעד.",
        "mergehistory-fail-toobig": "לא ניתן לבצע את מיזוג הגרסאות כיוון שצריך להעביר יותר גרסאות מהמגבלה, שהיא {{PLURAL:$1|גרסה אחת|‏‏֫$1 גרסאות}}.",
        "mergehistory-no-source": "דף המקור $1 אינו קיים.",
        "mergehistory-no-destination": "דף היעד $1 אינו קיים.",
        "right-applychangetags": "החלת [[Special:Tags|תגיות]] יחד עם שינויים",
        "right-changetags": "הוספת והסרה של [[Special:Tags|תגיות]] כלשהן לגרסאות מסוימות ולרשומות יומן",
        "grant-generic": "חבילת ההרשאות \"$1\"",
-       "grant-group-page-interaction": "פע×\99×\9c×\95ת ×\94×\99×\93×\95×\93×\99ת ×\91דפים",
-       "grant-group-file-interaction": "פע×\99×\9c×\95ת ×\94×\99×\93×\95×\93×\99ת ×\91×\9e×\93×\99×\94",
-       "grant-group-watchlist-interaction": "פע×\99×\9c×\95ת ×\94×\99×\93×\95×\93×\99ת ×\91רשימת המעקב שלך",
+       "grant-group-page-interaction": "×\90×\99× ×\98ר×\90קצ×\99×\94 ×¢×\9d דפים",
+       "grant-group-file-interaction": "×\90×\99× ×\98ר×\90קצ×\99×\94 ×¢×\9d ×§×\91צ×\99×\9d",
+       "grant-group-watchlist-interaction": "×\90×\99× ×\98ר×\90קצ×\99×\94 ×¢×\9d רשימת המעקב שלך",
        "grant-group-email": "שליחת דוא\"ל",
        "grant-group-high-volume": "ביצוי פעולות מרובות",
        "grant-group-customization": "התאמה אישית והעדפות",
        "grant-blockusers": "חסימת משתמשים ושחרורם",
        "grant-createaccount": "יצירת חשבונות",
        "grant-createeditmovepage": "יצירה, עריכה והעברה של דפים",
-       "grant-delete": "×\9e×\97×\99קת ×\93פ×\99×\9d, ×\92רס×\90×\95ת ×\95×¢×\99×\95×\9c×\99 יומן",
+       "grant-delete": "×\9e×\97×\99קת ×\93פ×\99×\9d, ×\92רס×\90×\95ת ×\95רש×\95×\9e×\95ת יומן",
        "grant-editinterface": "עריכת מרחב השם מדיה ויקי ו־CSS/JavaScript של משתמשים",
        "grant-editmycssjs": "עריכת CSS/JavaScript שלך",
        "grant-editmyoptions": "עריכת העדפות המשתמש שלך",
        "grant-uploadfile": "העלאת קבצים חדשים",
        "grant-basic": "הרשאות בסיסיות",
        "grant-viewdeleted": "צפייה בקבצים ודפים שנמחקו",
-       "grant-viewmywatchlist": "צפייה ברשימת מעקב שלך",
+       "grant-viewmywatchlist": "צפ×\99×\99×\94 ×\91רש×\99×\9eת ×\94×\9eעק×\91 ×©×\9c×\9a",
        "newuserlogpage": "יומן רישום משתמשים",
        "newuserlogpagetext": "זהו יומן המכיל הרשמות של משתמשים.",
        "rightslog": "יומן תפקידים",
        "uploaded-script-svg": "נמצא אלמנט שאפשר לכתוב בו תסריט \"$1\" בקובץ ה־SVG שהועלה.",
        "uploaded-hostile-svg": "נמצא CSS בלתי־מאובטח באלמנט style בקובץ ה־SVG שהועלה.",
        "uploaded-event-handler-on-svg": "אסור להגדיר מאפייני טיפול באירועים <code dir=\"ltr\">$1=\"$2\"</code> בקובצי SVG.",
-       "uploaded-href-unsafe-target-svg": "נמצא href ליעד בלתי־מאובטח <code dir=\"ltr\">&lt;$1 $2=\"$3\"&gt;</code> בקובץ ה־SVG שהועלה.",
+       "uploaded-href-attribute-svg": "למאפייני href בקובצי SVG מותר לקשר רק ליעדי http או https, ונמצא <code dir=\"ltr\">&lt;$1 $2=\"$3\"&gt;</code>.",
+       "uploaded-href-unsafe-target-svg": "נמצא href לנתונים לא מאובטחים <code dir=\"ltr\">&lt;$1 $2=\"$3\"&gt;</code> בקובץ ה־SVG שהועלה.",
        "uploaded-animate-svg": "נמצא תג \"animate\" שיכול לשנות href באמצעות מאפיין \"from\"  בצורת <code dir=\"ltr\">&lt;$1 $2=\"$3\"&gt;</code> בקובץ ה־SVG שהועלה.",
        "uploaded-setting-event-handler-svg": "הגדרת מאפייני טיפול באירועים חסומה, נמצא <code dir=\"ltr\">&lt;$1 $2=\"$3\"&gt;</code> בקובץ ה־SVG שהועלה.",
        "uploaded-setting-href-svg": "השימוש בתג set כדי להוסיף מאפיין href לאלמנט הורה חסום.",
        "upload-too-many-redirects": "הכתובת מכילה הפניות רבות מדי",
        "upload-http-error": "התרחשה שגיאת HTTP‏: $1",
        "upload-copy-upload-invalid-domain": "העלאת קבצים משרת זה אינה אפשרית.",
-       "upload-foreign-cant-upload": "×\91×\95×\95×\99ק×\99 ×\94×\96×\94 ×\9c×\90 ×\9e×\95×\92×\93ר ×\90×\99×\9a ×\9c×\94×¢×\9c×\95ת ×§×\91צ×\99×\9d ×\9c×\9e×\90×\92ר ×§×\91צ×\99×\9d ×\96ר.",
+       "upload-foreign-cant-upload": "×\91×\90תר ×\94×\95×\95×\99ק×\99 ×\94×\96×\94 ×\9c×\90 ×\9e×\95פע×\9cת ×\94×\90פשר×\95ת ×\9c×\94×¢×\9c×\90ת ×§×\91צ×\99×\9d ×\9c×\9e×\90×\92ר ×\94ק×\91צ×\99×\9d ×\94×\96ר ×\94×\9e×\91×\95קש.",
        "upload-dialog-title": "העלאת קובץ",
        "upload-dialog-button-cancel": "ביטול",
        "upload-dialog-button-done": "בוצע",
        "upload-dialog-button-save": "שמירה",
        "upload-dialog-button-upload": "העלאה",
-       "upload-form-label-select-file": "בחירת קובץ",
        "upload-form-label-infoform-title": "פרטים",
        "upload-form-label-infoform-name": "שם",
        "upload-form-label-infoform-name-tooltip": "כותרת המהווה תיאור ייחודי לקובץ, שתשמש כשם הקובץ. ניתן להשתמש בשפה טבעית עם רווחים. אין לכלול סיומת קובץ.",
        "foreign-structured-upload-form-label-own-work-message-shared": "אני מאשר שאני מחזיק בזכויות היוצרים על הקובץ הזה, ואני מסכים לשחרר אותו באופן בלתי הפיך עבור ויקישיתוף תחת רישיון [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0], ומסכים ל[https://wikimediafoundation.org/wiki/Terms_of_Use תנאי השימוש].",
        "foreign-structured-upload-form-label-not-own-work-message-shared": "אם זכויות היוצרים על הקובץ הזה אינן בבעלותך, או שברצונך לשחרר אותו תחת רישיון אחר, באפשרותך להשתמש ב[https://commons.wikimedia.org/wiki/Special:UploadWizard אשף ההעלאה לוויקישיתוף].",
        "foreign-structured-upload-form-label-not-own-work-local-shared": "באפשרותך לנסות להשתמש ב[[Special:Upload|דף העלאת הקבצים ב{{grammar:תחילית|{{SITENAME}}}}]], אם ניתן להעלות את הקובץ הזה לשם לפי מדיניות האתר.",
-       "foreign-structured-upload-form-2-label-intro": "תודה על כך שתרמת תמונה לשימוש ב{{grammar:תחילית|{{SITENAME}}}}. מותר להמשיך אך ורק אם התמונה מקיימת מספר תנאים:",
-       "foreign-structured-upload-form-2-label-ownwork": "התמונה וכל חלקיה חייבים להיות <strong>יצירה שלך</strong>, ולא תמונה שנלקחה מהאינטרנט",
-       "foreign-structured-upload-form-2-label-noderiv": "התמונה חייבת <strong>לא לכלול עבודה של אחרים</strong>, ולא לקבל השראה מהם",
-       "foreign-structured-upload-form-2-label-useful": "התמונה חייבת להיות <strong>חינוכית ושימושית</strong> ללימוד אחרים",
-       "foreign-structured-upload-form-2-label-ccbysa": "התמונה חייבת להיות <strong>מותרת לפרסום לנצח</strong> באינטרנט תחת תנאי רישיון [https://creativecommons.org/licenses/by-sa/4.0/ ייחוס־שיתוף זהה 4.0 של Creative Commons]",
-       "foreign-structured-upload-form-2-label-alternative": "אם לא כל התנאים המפורטים לעיל מתקיימים, ייתכן שיהיה באפשרותך להעלות את הקובץ באמצעות [https://commons.wikimedia.org/wiki/Special:UploadWizard אשף העלאת הקבצים של ויקישיתוף], אם הוא זמין ברישיון חופשי.",
-       "foreign-structured-upload-form-2-label-termsofuse": "בהעלאת הקובץ, אתה מאשר שאתה מחזיק בזכויות היוצרים על הקובץ הזה, מסכים לשחרר אותו באופן בלתי הפיך עבור ויקישיתוף תחת רישיון [https://creativecommons.org/licenses/by-sa/4.0/ ייחוס־שיתוף זהה 4.0 של Creative Commons], ומסכים ל[https://wikimediafoundation.org/wiki/Terms_of_Use תנאי השימוש].",
-       "foreign-structured-upload-form-3-label-question-website": "האם הורדת את התמונה הזאת מאתר אינטרנט, או השגת אותה באמצעות חיפוש תמונות?",
-       "foreign-structured-upload-form-3-label-question-ownwork": "האם יצרת את התמונה הזאת (צילמת את התמונה, ציירת את הציור, וכו') בעצמך?",
-       "foreign-structured-upload-form-3-label-question-noderiv": "האם הוא מכיל עבודה שבבעלות אחרים (כגון לוגו), או קיבל השראה מעבודה כזו?",
-       "foreign-structured-upload-form-3-label-yes": "כן",
-       "foreign-structured-upload-form-3-label-no": "לא",
-       "foreign-structured-upload-form-3-label-alternative": "מצטערים, במקרה זה, הכלי הזה אינו תומך בהעלאת הקובץ. ייתכן שיהיה באפשרותך להעלות את הקובץ באמצעות [https://commons.wikimedia.org/wiki/Special:UploadWizard אשף העלאת הקבצים של ויקישיתוף], אם הוא זמין ברישיון חופשי.",
-       "foreign-structured-upload-form-4-label-good": "בעזרת הכלי הזה, באפשרותך להעלות איורים חינוכיים שיצרת ותמונות שצילמת, אם אינם מכילים עבודה בבעלות אחרים.",
-       "foreign-structured-upload-form-4-label-bad": "לא ניתן להעלות תמונות שנמצאו במנועי חיפוש או הורדו מאתרי אינטרנט אחרים.",
        "backend-fail-stream": "לא הייתה אפשרות להזרים את הקובץ \"$1\".",
        "backend-fail-backup": "לא הייתה אפשרות לגבות את הקובץ \"$1\".",
        "backend-fail-notexists": "הקובץ \"$1\" אינו קיים.",
        "querypage-disabled": "דף מיוחד זה מבוטל עקב בעיות ביצועים.",
        "apihelp": "עזרה עבור ה־API",
        "apihelp-no-such-module": "המודול \"$1\" לא נמצא.",
+       "apisandbox": "ארגז החול של ה־API",
+       "apisandbox-jsonly": "דרוש JavaScript כדי להשתמש בארגז החול של ה־API.",
+       "apisandbox-api-disabled": "API אינו פעיל באתר הזה.",
+       "apisandbox-intro": "השתמשו בדף הזה כדי להתנסות בשימוש ב<strong>שירות ה־API המבוסס Web של מדיה־ויקי</strong>.\nעיינו ב[[mw:API:Main page|תיעוד של ה־API]] (באנגלית) למידע נוסף של שימוש ב־API. למשל: [//www.mediawiki.org/wiki/API#A_simple_example איך לקבל את התוכן של העמוד הראשי]. בחרו באחת הפעולות (actions) לדוגמאות נוספות.\n\nשימו לב שאף שמדובר ב\"ארגז חול\", פעולות שנעשות כאן עשויות לשנות את התוכן של אתר הוויקי.",
+       "apisandbox-fullscreen": "הרחבת החלונית",
+       "apisandbox-fullscreen-tooltip": "הרחבת החלונית של ארגז החול כך שימלא את חלון הדפדפן.",
+       "apisandbox-unfullscreen": "הצגת הדף",
+       "apisandbox-unfullscreen-tooltip": "הקטנת החלונית של ארגז החול, כך שקישורי הניווט של מדיה־ויקי יהיו זמינים לשימוש.",
+       "apisandbox-submit": "ביצוע בקשה",
+       "apisandbox-reset": "ניקוי",
+       "apisandbox-retry": "ניסיון נוסף",
+       "apisandbox-loading": "המידע עבור מודול ה־API‏ \"$1\" בטעינה...",
+       "apisandbox-load-error": "אירעה שגיאה בעת טעינת המידע של מודול ה־API‏ \"$1\"‏: $2",
+       "apisandbox-no-parameters": "למודול ה־API אין פרמטרים.",
+       "apisandbox-helpurls": "קישורי עזרה",
+       "apisandbox-examples": "דוגמאות",
+       "apisandbox-dynamic-parameters": "פרמטרים נוספים",
+       "apisandbox-dynamic-parameters-add-label": "הוספת פרמטר:",
+       "apisandbox-dynamic-parameters-add-placeholder": "שם הפרמטר",
+       "apisandbox-dynamic-error-exists": "פרמטר בשם \"$1\" כבר קיים.",
+       "apisandbox-deprecated-parameters": "פרמטרים מיושנים",
+       "apisandbox-fetch-token": "מילוי אוטומטי של האסימון",
+       "apisandbox-submit-invalid-fields-title": "חלק מהשדות אינם תקינים",
+       "apisandbox-submit-invalid-fields-message": "אנא תקנו את השדות המסומנים ונסו שוב.",
+       "apisandbox-results": "תוצאות",
+       "apisandbox-sending-request": "בקשת ה־API בשליחה...",
+       "apisandbox-loading-results": "תוצאות ה־API בתהליך קבלה...",
+       "apisandbox-results-error": "אירעה שגיאה בעת טעינת תשובת ה־API לבקשה: $1.",
+       "apisandbox-request-url-label": "כתובת ה־URL של הבקשה:",
+       "apisandbox-request-time": "זמן הבקשה: {{PLURAL:$1|מילישנייה אחת|$1 מילישניות}}",
+       "apisandbox-results-fixtoken": "אנא תקנו את האסימון ושלחו שוב",
+       "apisandbox-results-fixtoken-fail": "קבלת האסימון \"$1\" נכשלה.",
+       "apisandbox-alert-page": "שדות בדף זה אינם תקינים.",
+       "apisandbox-alert-field": "הערך של שדה זה אינו תקין.",
        "booksources": "משאבי ספרות חיצוניים",
        "booksources-search-legend": "חיפוש משאבי ספרות חיצוניים",
        "booksources-isbn": "מסת\"ב (ISBN):",
        "wlshowhideanons": "משתמשים אנונימיים",
        "wlshowhidepatr": "עריכות בדוקות",
        "wlshowhidemine": "עריכות שלי",
-       "wlshowhidecategorization": "×\94×\95ספ×\95ת ×\95×\94סר×\95ת ×©×\9c ×\93פ×\99×\9d ×\9eקטגוריות",
+       "wlshowhidecategorization": "ס×\99×\95×\95×\92 ×\93פ×\99×\9d ×\9cקטגוריות",
        "watchlist-options": "אפשרויות ברשימת המעקב",
        "watching": "בהוספה לרשימת המעקב…",
        "unwatching": "בהסרה מרשימת המעקב…",
        "block-log-flags-hiddenname": "שם המשתמש הוסתר",
        "range_block_disabled": "האפשרות לחסום טווח כתובות אינה פעילה.",
        "ipb_expiry_invalid": "זמן פקיעת החסימה אינו תקין.",
-       "ipb_expiry_old": "×\96×\9e×\9f ×ª×¤×\95×\92×\94 ×\91עבר.",
+       "ipb_expiry_old": "×\96×\9e×\9f ×\94תפ×\95×\92×\94 ×\9b×\91ר עבר.",
        "ipb_expiry_temp": "חסימות הכוללות הסתרת שם משתמש חייבות להיות לזמן בלתי מוגבל.",
        "ipb_hide_invalid": "לא ניתן להעלים את החשבון הזה; {{PLURAL:$1|בוצעה ממנו יותר מעריכה אחת|בוצעו ממנו יותר מ‏‏֫־$1 עריכות}}.",
        "ipb_already_blocked": "המשתמש \"$1\" כבר נחסם.",
        "import-nonewrevisions": "כל הגרסאות יובאו בעבר.",
        "xml-error-string": "$1 בשורה $2, עמודה $3 (בייט מספר $4): $5",
        "import-upload": "העלאת קובץ XML",
-       "import-token-mismatch": "מידע הכניסה אבד.\nנא לנסות שוב.",
+       "import-token-mismatch": "מידע הכניסה אבד.\n\nייתכן שנותקתם מהחשבון. <strong>אנא ודאו שאתם עדיין מחוברים לחשבון ונסו שוב.</strong>\nאם זה עדיין לא עובד, נסו [[Special:UserLogout|לצאת מהחשבון]] ולהיכנס אליו שנית, וודאו שהדפדפן שלכם מאפשר קבלת עוגיות מאתר זה.",
        "import-invalid-interwiki": "לא ניתן לייבא מאתר הוויקי שצוין.",
        "import-error-edit": "לא ניתן לייבא את הדף \"$1\" כיוון שאין לך הרשאה לערוך אותו.",
        "import-error-create": "לא ניתן לייבא את הדף \"$1\" כיוון שאין לך הרשאה ליצור אותו.",
        "version-libraries-license": "רישיון",
        "version-libraries-description": "תיאור",
        "version-libraries-authors": "יוצרים",
-       "redirect": "×\94פנ×\99×\94 ×\9cפ×\99 ×§×\95×\91×¥, ×\9eשת×\9eש, ×\93×£, ×\92רס×\94 ×\90×\95 ×\9e×\96×\94×\94 ×\99×\95×\9e×\9dן",
+       "redirect": "×\94פנ×\99×\94 ×\9cפ×\99 ×©×\9d ×§×\95×\91×¥, ×\9eספר ×\9eשת×\9eש, ×\9eספר ×\93×£, ×\9eספר ×\92רס×\94 ×\90×\95 ×\9e×\96×\94×\94 ×\99×\95×\9eן",
        "redirect-legend": "הפניה לקובץ או לדף",
-       "redirect-summary": "×\93×£ ×\9e×\99×\95×\97×\93 ×\96×\94 ×\9eפנ×\94 ×\9cק×\95×\91×¥ (×\91×\94×\99נת×\9f ×©×\9d ×\94ק×\95×\91×¥), ×\9c×\93×£ (×\91×\94×\99נת×\9f ×\9eספר ×\92רס×\94 ×\90×\95 ×\9eספר ×\93×£), ×\90×\95 ×\9c×\93×£ ×\9eשת×\9eש (×\91×\94×\99נת×\9f ×\9eספר ×\9eשת×\9eש), ×\90×\95 ×¢×\99×\95×\9c יומן (בהינתן מזהה יומן). דוגמאות לשימוש: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], או [[{{#Special:Redirect}}/user/101]], או [[{{#Special:Redirect}}/logid/186]].",
+       "redirect-summary": "×\93×£ ×\9e×\99×\95×\97×\93 ×\96×\94 ×\9eפנ×\94 ×\9cק×\95×\91×¥ (×\91×\94×\99נת×\9f ×©×\9d ×\94ק×\95×\91×¥), ×\9c×\93×£ (×\91×\94×\99נת×\9f ×\9eספר ×\92רס×\94 ×\90×\95 ×\9eספר ×\93×£), ×\9c×\93×£ ×\9eשת×\9eש (×\91×\94×\99נת×\9f ×\9eספר ×\9eשת×\9eש), ×\90×\95 ×\9cרש×\95×\9eת יומן (בהינתן מזהה יומן). דוגמאות לשימוש: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], או [[{{#Special:Redirect}}/user/101]], או [[{{#Special:Redirect}}/logid/186]].",
        "redirect-submit": "מעבר",
        "redirect-lookup": "סוג:",
        "redirect-value": "ערך:",
        "expand_templates_generate_xml": "הצגת עץ הפענוח של XML",
        "expand_templates_generate_rawhtml": "הצגת HTML גולמי",
        "expand_templates_preview": "תצוגה מקדימה",
-       "expand_templates_preview_fail_html": "<em>×\9e×\9b×\99×\95×\95×\9f ×©×\91{{GRAMMAR:ת×\97×\99×\9c×\99ת|{{SITENAME}}}} ×\9e×\95פע×\9cת ×\94צ×\92ת HTML ×\92×\95×\9c×\9e×\99ת ×\95×\90×\99רע ×\90×\91×\93×\9f ×\9e×\99×\93×¢ ×\9b× ×\99ס×\94, ×\94תצ×\95×\92×\94 ×\94×\9eק×\93×\99×\9e×\94 ×\9e×\95סתרת, ×\95×\96×\90ת ×\9b×\90×\9eצע×\99 ×\96×\94×\99ר×\95ת ×\9eפנ×\99 ×\94תקפ×\95ת JavaScript.</em>\n\n<strong>×\90×\9d ×\96×\94 × ×\99ס×\99×\95×\9f ×ª×§×\99×\9f ×\9c×\94צ×\99×\92 ×ª×¦×\95×\92×\94 ×\9eק×\93×\99×\9e×\94, ×\99ש ×\9cנס×\95ת ×©×\95×\91.</strong>\n×\90×\9d ×\96×\94 ×¢×\93×\99×\99×\9f ×\9c×\90 ×¢×\95×\91×\93, ×\99ש ×\9cנס×\95ת [[Special:UserLogout|×\9cצ×\90ת ×\9e×\94×\97ש×\91×\95×\9f]] ×\95×\9c×\94×\99×\9bנס ×©×\95×\91.",
+       "expand_templates_preview_fail_html": "<em>×\9b×\99×\95×\95×\9f ×©×\91{{grammar:ת×\97×\99×\9c×\99ת|{{SITENAME}}}} ×\90פשר×\95ת ×\94ש×\99×\9e×\95ש ×\91Ö¾HTML ×\92×\95×\9c×\9e×\99 ×\9e×\95פע×\9cת ×\95×\9b×\99×\95×\95×\9f ×©×\94×\99×\94 ×\90×\95×\91×\93×\9f ×©×\9c ×\9e×\99×\93×¢ ×\94×\9b× ×\99ס×\94, ×\94תצ×\95×\92×\94 ×\94×\9eק×\93×\99×\9e×\94 ×\9e×\95סתרת ×\9b×\93×\99 ×\9c×\9e× ×\95×¢ ×\94תקפ×\95ת JavaScript.</em>\n\n<strong>×\90×\9d ×\96×\94×\95 × ×\99ס×\99×\95×\9f ×\9c×\92×\99×\98×\99×\9e×\99 ×\9c×\94צ×\92ת ×ª×¦×\95×\92×\94 ×\9eק×\93×\99×\9e×\94, ×\90× ×\90 × ×¡×\95 ×©×\95×\91.</strong>\n×\90×\9d ×\96×\94 ×¢×\93×\99×\99×\9f ×\9c×\90 ×¢×\95×\91×\93, × ×¡×\95 [[Special:UserLogout|×\9cצ×\90ת ×\9e×\94×\97ש×\91×\95×\9f]] ×\95×\9c×\94×\99×\9bנס ×\90×\9c×\99×\95 ×©× ×\99ת, ×\95×\95×\93×\90×\95 ×©×\94×\93פ×\93פ×\9f ×©×\9c×\9b×\9d ×\9e×\90פשר ×§×\91×\9cת ×¢×\95×\92×\99×\95ת ×\9e×\90תר ×\96×\94.",
        "expand_templates_preview_fail_html_anon": "<em>מכיוון שב{{GRAMMAR:תחילית|{{SITENAME}}}} מופעלת הצגת HTML גולמית ולא נכנסת לחשבון, התצוגה המקדימה מוסתרת, וזאת כאמצעי זהירות מפני התקפות JavaScript.</em>\n\n<strong>אם זה ניסיון תקין להציג תצוגה מקדימה, יש [[Special:UserLogin|להיכנס לחשבון]] ולנסות שוב.</strong>",
        "expand_templates_input_missing": "יש לכתוב טקסט (לפחות טקסט קצר).",
        "pagelanguage": "שינוי שפת הדף",
        "api-error-blacklisted": "נא לבחור כותרת אחרת, המתארת טוב יותר את התוכן.",
        "sessionmanager-tie": "לא ניתן לצרף מספר סוגי אימות זהות: $1.",
        "sessionprovider-generic": "התחברויות של $1",
-       "sessionprovider-mediawiki-session-cookiesessionprovider": "×\94ת×\97×\91ר×\95×\99×\95ת ×\9e×\91×\95סס×\95ת־עוגיות",
-       "sessionprovider-nocookies": "×\90פשר ×\9c×\9b×\91×\95ת ×¢×\95×\92×\99×\95ת. ×\99ש ×\9c×\95×\95×\93×\90 ×©×\94×¢×\95×\92×\99×\95ת ×\9e×\95פע×\9c×\95ת ולהתחיל מחדש.",
+       "sessionprovider-mediawiki-session-cookiesessionprovider": "×\94ת×\97×\91ר×\95×\99×\95ת ×\94×\9e×\91×\95סס×\95ת ×¢×\9c עוגיות",
+       "sessionprovider-nocookies": "×\99×\99ת×\9b×\9f ×©×\90פשר×\95ת ×\94ש×\99×\9e×\95ש ×\91×¢×\95×\92×\99×\95ת ×\9b×\91×\95×\99×\94. ×\99ש ×\9c×\95×\95×\93×\90 ×©×\90פשר×\95ת ×\94ש×\99×\9e×\95ש ×\91×¢×\95×\92×\99×\95ת ×\9e×\95פע×\9cת ולהתחיל מחדש.",
        "randomrootpage": "דף שורש אקראי"
 }
index ea03c06..9d85e4b 100644 (file)
@@ -70,7 +70,8 @@
                        "Sfic",
                        "Niharika29",
                        "जनक राज भट्ट",
-                       "YmKavishwar"
+                       "YmKavishwar",
+                       "Upendradutt93"
                ]
        },
        "tog-underline": "कड़ियाँ अधोरेखन:",
        "october-date": "$1 अक्टूबर",
        "november-date": "$1 नवम्बर",
        "december-date": "$1 दिसम्बर",
+       "period-am": "पूर्वाह्न",
+       "period-pm": "अपराह्न",
        "pagecategories": "{{PLURAL:$1|श्रेणी|श्रेणियाँ}}",
        "category_header": "\"$1\" श्रेणी में पृष्ठ",
        "subcategories": "उपश्रेणियाँ",
        "morenotlisted": "यह सूची पूर्ण नहीं है।",
        "mypage": "पृष्ठ",
        "mytalk": "वार्ता",
-       "anontalk": "à¤\87स à¤\86à¤\88॰पà¥\80 à¤\95à¥\87 à¤²à¤¿à¤¯à¥\87 à¤µà¤¾à¤°à¥\8dता",
+       "anontalk": "वार्ता",
        "navigation": "भ्रमण",
        "and": "&#32;और",
        "qbfind": "खोजें",
        "pool-servererror": "पूल काउंटर सेवा उपलब्ध नहीं है ($1)।",
        "poolcounter-usage-error": "उपयोग त्रुटि: $1",
        "aboutsite": "{{SITENAME}} के बारे में",
-       "aboutpage": "परियोजना:परिचय",
+       "aboutpage": "Project:परिचय",
        "copyright": "उपलब्ध सामग्री $1 के अधीन है जब तक अलग से उल्लेख ना किया गया हो।",
        "copyrightpage": "{{ns:project}}:कॉपीराइट",
        "currentevents": "हाल की घटनाएँ",
        "virus-scanfailed": "जाँच विफल (कूट $1)",
        "virus-unknownscanner": "अज्ञात ऐंटीवायरस:",
        "logouttext": "'''अब आप लॉग आउट कर चुके हैं।'''\n\nध्यान दें कि जब तक आप अपनी ब्राउज़र कैशे खाली नहीं करते हैं, कुछ पृष्ठ अब भी ऐसे दिख सकते हैं जैसे कि आप अभी भी लॉगिन हैं।",
+       "cannotlogoutnow-title": "अभी प्रस्थान नहीं हो रहा है",
        "welcomeuser": "आपका स्वागत है, $1!",
        "welcomecreation-msg": "आपका खाता बना दिया गया है।\nअपनी [[Special:Preferences|{{SITENAME}} वरीयताएँ]] बदलना ना भूलियेगा।",
        "yourname": "सदस्यनाम:",
        "remembermypassword": "इस ब्राउज़र पर मेरा लॉगिन याद रखें (अधिकतम $1 {{PLURAL:$1|दिन|दिनों}} के लिए)",
        "userlogin-remembermypassword": "मुझे लॉग्ड इन रखें",
        "userlogin-signwithsecure": "सुरक्षित कनेक्शन का प्रयोग करें",
+       "cannotloginnow-title": "अभी प्रवेश नहीं हो रहा है",
+       "cannotloginnow-text": "$1 का उपयोग करते समय प्रवेश नहीं हो सकता है।",
        "yourdomainname": "आपका डोमेन:",
        "password-change-forbidden": "आप इस विकि पर कूटशब्द नहीं बदल सकते हैं।",
        "externaldberror": "या तो प्रमाणिकरण डाटाबेस में त्रुटि हुई है या फिर आपको अपना बाह्य खाता अपडेट करने की अनुमति नहीं है।",
        "resetpass_submit": "कूटशब्द बनाएँ और लॉग इन करें",
        "changepassword-success": "आपका कूटशब्द बदल दिया गया है!",
        "changepassword-throttled": "आपने हाल ही में कई बार लॉग इन करने के प्रयास किये हैं।\nपुनः प्रयास करने से पहले कृपया $1 प्रतीक्षा करें।",
+       "botpasswords": "बॉट पासवर्ड",
+       "botpasswords-disabled": "बॉट पासवर्ड अभी निष्क्रिय है।",
+       "botpasswords-existing": "वर्तमान बॉट पासवर्ड",
+       "botpasswords-createnew": "बॉट के लिए नया पासवर्ड बनाएँ",
+       "botpasswords-editexisting": "बॉट के वर्तमान पासवर्ड को बदलें",
+       "botpasswords-label-appid": "बॉट नाम:",
+       "botpasswords-label-create": "बनाएँ",
+       "botpasswords-label-update": "अद्यतन",
+       "botpasswords-label-cancel": "रद्द करें",
+       "botpasswords-label-delete": "हटाएँ",
+       "botpasswords-label-resetpassword": "पासवर्ड पुनः तय करें",
+       "botpasswords-created-title": "बॉट पासवर्ड निर्मित हुआ",
+       "botpasswords-created-body": "बॉट पासवर्ड \"$1\" सफलतापूर्वक निर्मित हुआ।",
+       "botpasswords-updated-title": "बॉट पासवर्ड अद्यतन हुआ",
+       "botpasswords-updated-body": "बॉट पासवर्ड \"$1\" सफलतापूर्वक अद्यतन हुआ।",
+       "botpasswords-deleted-title": "बॉट पासवर्ड हट गया",
+       "botpasswords-deleted-body": "बॉट पासवर्ड \"$1\" हट गया।",
        "resetpass_forbidden": "कूटशब्द बदले नहीं जा सकते",
        "resetpass-no-info": "इस पृष्ठ का सीधे प्रयोग करने के लिए आपको लॉग इन करना होगा।",
        "resetpass-submit-loggedin": "कूटशब्द बदलें",
        "passwordreset-emailtext-user": "{{SITENAME}} ($4) पर सदस्य $1 ने आपके {{PLURAL:$3|खाते|खातों}} के कूटशब्द को रीसेट करने का अनुरोध किया है। इस ई-मेल पते से निम्न {{PLURAL:$3|खाता जुड़ा है|खाते जुड़े हैं}}:\n\n$2\n\n{{PLURAL:$3|यह|ये}} अस्थायी कूटशब्द {{PLURAL:$5|एक दिन|$5 दिनों}} के बाद काम नहीं करेंगे।\nआपको लॉग इन करके एक नया कूटशब्द अभी चुन लेना चाहिए। यदि यह अनुरोध किसी और ने किया है, या फिर आपको अपना मूल कूटशब्द याद आ गया है, और आप {{PLURAL:$3|अपना|अपने}} कूटशब्द नहीं बदलना चाहते, आप इस संदेश को अनदेखा कर के अपने पुराने कूटशब्द का प्रयोग जारी रख सकते हैं।",
        "passwordreset-emailelement": "सदस्यनाम: \n$1\n\nअस्थायी कूटशब्द: \n$2",
        "passwordreset-emailsentemail": "एक कूटशब्द रीसेट ई-मेल भेज दिया गया है।",
+       "passwordreset-emailsentusername": "यदि कोई ईमेल इस खाते से जुड़ी है तो पासवर्ड आपके ईमेल में भेज दिया जाएगा।",
        "passwordreset-emailsent-capture": "नीचे दिखाया गया कूटशब्द रीसेट ई-मेल भेज दिया गया है।",
        "passwordreset-emailerror-capture": "नीचे दृष्टित कूटशब्द रीसेट ई-मेल उत्पन्न किया गया था, परंतु उसे {{GENDER:$2|सदस्य}} को भेजना असफल रहा।\nत्रुटि: $1",
        "changeemail": "ई-मेल पता परिवर्तित करें",
        "mergehistory-empty": "कोई भी अवतरण एकत्रित नहीं कर सकते।",
        "mergehistory-done": "$1 {{PLURAL:$3|का|के}} $3 अवतरण [[:$2]] में एकत्रित कर {{PLURAL:$3|दिया गया है|दिये गए हैं}}।",
        "mergehistory-fail": "इतिहास एकत्रित नहीं कर सकते, कृपया पृष्ठ और समय की पुनः जाँच करें।",
+       "mergehistory-fail-invalid-source": "अमान्य स्रोत पृष्ठ",
+       "mergehistory-fail-invalid-dest": "अमान्य लक्ष्य पृष्ठ",
        "mergehistory-fail-toobig": "इतिहास विलय करना संभव नहीं है क्योंकि अवतरण सीमा $1 से अधिक {{PLURAL:$1|अवतरण|अवतरणों}} को स्थानांतरित करना होगा।",
        "mergehistory-no-source": "स्रोत पृष्ठ $1 मौजूद नहीं है।",
        "mergehistory-no-destination": "लक्ष्य पृष्ठ $1 मौजूद नहीं है।",
        "right-managechangetags": "डेटाबेस से [[Special:Tags|चिप्पियाँ]] बनायें और हटायें",
        "right-applychangetags": "प्रयोग में लाइये [[Special:Tags|tags]] किसी के बदलाव के साथ।",
        "right-changetags": "जमा करो और हटाओ स्वतंत्र [[Special:Tags|टैग]] व्यक्तिगत अवतरणों और लॉग प्रविक्तियों पर",
+       "grant-group-email": "ई-मेल भेजें",
+       "grant-createaccount": "खाता बनाएँ",
+       "grant-editmywatchlist": "ध्यानसूची संपादित करें",
+       "grant-basic": "सामान्य अधिकार",
+       "grant-viewmywatchlist": "अपनी ध्यानसूची देखें",
        "newuserlogpage": "सदस्य खाता निर्माण लॉग",
        "newuserlogpagetext": "यह सदस्य खातों के निर्माण का लॉग है।",
        "rightslog": "सदस्य अधिकार लॉग",
        "upload-dialog-button-done": "पूर्ण हुआ",
        "upload-dialog-button-save": "सहेजें",
        "upload-dialog-button-upload": "डालें",
-       "upload-form-label-select-file": "फ़ाइल चुनें",
        "upload-form-label-infoform-title": "विवरण",
        "upload-form-label-infoform-name": "नाम",
        "upload-form-label-infoform-description": "विवरण",
        "foreign-structured-upload-form-label-own-work-message-shared": "कम से कम इस फ़ाइल का प्रतिकृति अधिकार मेरे पास है और यह [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0] के अंतर्गत है, व [https://wikimediafoundation.org/wiki/Terms_of_Use विकि उपयोग की शर्तों] का भी पालन करता है।",
        "foreign-structured-upload-form-label-not-own-work-message-shared": "यदि आपके पास इस फ़ाइल का प्रतिकृति अधिकार नहीं है और आप इसे किसी और अधिकार के तहत प्रदर्शित करना चाहते हैं तो आप [https://commons.wikimedia.org/wiki/Special:UploadWizard Commons Upload Wizard] का उपयोग करें।",
        "foreign-structured-upload-form-label-not-own-work-local-shared": "यदि आप चाहें तो आप [[Special:Upload|{{SITENAME}} के पृष्ठ]] पर फ़ाइल डाल सकते हैं, यदि यह फ़ाइल वहाँ के नियम के अंतर्गत हो तो।",
-       "foreign-structured-upload-form-3-label-yes": "हाँ",
-       "foreign-structured-upload-form-3-label-no": "नहीं",
        "backend-fail-stream": "फ़ाइल $1 स्ट्रीम नहीं हो पाई।",
        "backend-fail-backup": "फ़ाइल $1 बैकअप नहीं हो पाई।",
        "backend-fail-notexists": "फ़ाइल $1 मौजूद नहीं है।",
        "usereditcount": "$1 {{PLURAL:$1|सम्पादन}}",
        "usercreated": "$1 को $2 बजे बनाया गया, सदस्यनाम $3 है",
        "newpages": "नए पृष्ठ",
+       "newpages-submit": "दिखाएँ",
        "newpages-username": "सदस्यनाम:",
        "ancientpages": "सबसे पुराने पृष्ठ",
        "move": "स्थानान्तरण",
        "querypage-disabled": "प्रदर्शन कारणों से यह विशेष पृष्ठ अक्षम किया गया है।",
        "apihelp": "ए पी आई सहाएता",
        "apihelp-no-such-module": "मॉड्यूल \"$1\" नहीं मिला",
+       "apisandbox": "ए॰पी॰आइ प्रयोगस्थल",
+       "apisandbox-api-disabled": "इस स्थल पर ए०पी०आई० सक्षम नहीं हैं।",
+       "apisandbox-intro": "याद रखिए कि, हालांकि यह प्रयोगपृष्ठ है, इस पृष्ठ पर किए गए आपके काम इस विकि में बदलाव ला सकते हैं।",
+       "apisandbox-unfullscreen": "पृष्ठ दिखाएँ",
+       "apisandbox-submit": "अनुरोध करें",
+       "apisandbox-reset": "स्पष्ट",
+       "apisandbox-retry": "दुबारा प्रयास करें",
+       "apisandbox-examples": "Example",
+       "apisandbox-results": "परिणाम",
+       "apisandbox-request-url-label": "अनुरोध URL:",
+       "apisandbox-request-time": "अनुरोध समय: $1",
        "booksources": "पुस्तकों के स्रोत",
        "booksources-search-legend": "पुस्तकों के स्रोत खोजें",
        "booksources-isbn": "आइ॰एस॰बी॰एन:",
        "specialloguserlabel": "कर्ता:",
        "speciallogtitlelabel": "प्रयोजन (शीर्षक अथवा सदस्यनाम):",
        "log": "लॉग",
+       "logeventslist-submit": "दिखाएँ",
        "all-logs-page": "सभी सार्वजनिक लॉग",
        "alllogstext": "{{SITENAME}} की सभी उपलब्ध लॉगों की प्रविष्टियों का मिला-जुला प्रदर्शन।\nआप और बारीकी के लिए लॉग का प्रकार, सदस्य नाम (लघु-दीर्घ-अक्षर संवेदी), या प्रभावित पृष्ठ (लघु-दीर्घ-अक्षर संवेदी) चुन सकते हैं।",
        "logempty": "लॉग में ऐसी प्रविष्टि नहीं है।",
        "cachedspecial-viewing-cached-ts": "आप इस पृष्ठ का कैश किया हुआ अवतरण देख रहे हैं, जो कि संभवतः वर्तमान अवस्था से भिन्न हो।",
        "cachedspecial-refresh-now": "नवीनतम देखें।",
        "categories": "श्रेणियाँ",
+       "categories-submit": "दिखाएँ",
        "categoriespagetext": "निम्नोक्त {{PLURAL:$1|श्रेणी|श्रेणियों}} में पृष्ठ या मीडिया है।\nजिन श्रेणियों का [[Special:UnusedCategories|अप्रयुक्त श्रेणियाँ]] यहाँ नहीं दिखाई गई हैं।\n[[Special:WantedCategories|वांछित श्रेणियाँ]] भी देखें।",
        "categoriesfrom": "इस अक्षर से शुरू होने वाली श्रेणीयाँ दर्शायें:",
        "special-categories-sort-count": "संख्यानुसार शक्रमांकित करें",
        "wlheader-showupdated": "पृष्ठ जो आपके द्वारा देखे जाने के बाद बदले गये हैं '''बोल्ड''' दिखेंगे।",
        "wlnote": "$3 को $4 बजे तक पिछले <strong>$2</strong> {{PLURAL:$2|घंटे|घंटों}} में {{PLURAL:$1|हुआ एक|हुए <strong>$1</strong>}} परिवर्तन निम्न {{PLURAL:$1|है|हैं}}।",
        "wlshowlast": "पिछले $1 घंटे $2 दिन  देखें",
+       "watchlist-hide": "छुपाएँ",
+       "watchlist-submit": "दिखाएँ",
        "wlshowtime": "अंतिम दिखाएँ:",
        "wlshowhideminor": "छोटा संपादन",
        "wlshowhidebots": "बॉट",
        "delete-confirm": "\"$1\" को हटाएँ",
        "delete-legend": "हटाएँ",
        "historywarning": "<strong>चेतावनी:<strong> आप जो पृष्ठ हटाने जा रहे हैं उसके इतिहास में $1 {{PLURAL:$1|अवतरण}} हैं:",
+       "historyaction-submit": "दिखाएँ",
        "confirmdeletetext": "आप एक पृष्ठ को उसके सभी अवतरणों सहित हटाने जा रहे हैं।\nजाँच लें कि आप ये करना चाहते हैं, आप इसके पर्निआमों से अवगत हैं, और आप ये [[{{MediaWiki:Policy-url}}|नीति]] के अनुसार कर रहे हैं।",
        "actioncomplete": "कार्य पूर्ण",
        "actionfailed": "क्रिया विफल",
index cdb3a9c..16dccb2 100644 (file)
        "rcshowhidemine-show": "prikaži",
        "rcshowhidemine-hide": "sakrij",
        "rcshowhidecategorization": "$1 kategorizaciju stranica",
-       "rcshowhidecategorization-show": "Prikaži",
+       "rcshowhidecategorization-show": "prikaži",
        "rcshowhidecategorization-hide": "Sakrij",
        "rclinks": "Prikaži posljednjih $1 promjena {{PLURAL:$2|prethodni dan|u posljednja $2 dana|u posljednjih $2 dana}}<br />$3",
        "diff": "razl",
        "upload-dialog-button-done": "Gotovo",
        "upload-dialog-button-save": "Spremi",
        "upload-dialog-button-upload": "Postavi",
-       "upload-form-label-select-file": "Odaberi datoteku",
        "upload-form-label-infoform-title": "Detalji",
        "upload-form-label-infoform-name": "Ime",
        "upload-form-label-infoform-description": "Opis",
        "foreign-structured-upload-form-label-own-work-message-shared": "Potvrđujem da posjedujem autorska prava ove datoteke i slažem se da je nepozivo postavljam na Zajednički poslužitelj pod licencijom  [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0], i pristajem na [https://wikimediafoundation.org/wiki/Terms_of_Use Uvjete uporabe].",
        "foreign-structured-upload-form-label-not-own-work-message-shared": "Ako ne posjedujete autorska prava za ovu datoteku, ili je želite objaviti pod drugom licencijom, razmislite o uporabi [https://commons.wikimedia.org/wiki/Special:UploadWizard Čarobnjaka za postavljanje] na Zajedničkom poslužitelju.",
        "foreign-structured-upload-form-label-not-own-work-local-shared": "Možete pokušati [[Special:Upload|postaviti datoteku na projektu {{SITENAME}}]], pod uvjetom da je dopušteno postavljanje ove datoteke, sukladno pravilima projekta.",
-       "foreign-structured-upload-form-3-label-yes": "Da",
        "backend-fail-stream": "Ne mogu prikazati datoteku $1.",
        "backend-fail-backup": "Izrada sigurnosne kopije datoteke \"$1\" nije uspjela.",
        "backend-fail-notexists": "Datoteka $1 ne postoji.",
        "suppress": "Nadzor",
        "querypage-disabled": "Ova posebna stranica onemogućena je jer bi usporila funkcioniranje projekta.",
        "apihelp": "Pomoć za API",
+       "apisandbox-submit": "Napraviti zahtjev",
+       "apisandbox-reset": "Očisti",
+       "apisandbox-examples": "Primjer",
+       "apisandbox-results": "Rezultat",
        "booksources": "Pretraživanje po ISBN-u",
        "booksources-search-legend": "Traženje izvora za knjigu",
        "booksources-search": "Traži",
index 839c2c5..5e7313f 100644 (file)
@@ -42,7 +42,8 @@
                        "Nyuszika7H",
                        "Matma Rex",
                        "JulesWinnfield-hu",
-                       "Bencoke"
+                       "Bencoke",
+                       "Máté"
                ]
        },
        "tog-underline": "Hivatkozások aláhúzása:",
@@ -77,6 +78,7 @@
        "tog-watchlisthidebots": "Robotok szerkesztéseinek elrejtése",
        "tog-watchlisthideminor": "Apró változtatások elrejtése",
        "tog-watchlisthideliu": "Bejelentkezett szerkesztők módosításainak elrejtése a figyelőlistáról",
+       "tog-watchlistreloadautomatically": "A figyelőlista automatikus újratöltése bármelyik szűrő megváltoztatása esetén (JavaScript szükséges)",
        "tog-watchlisthideanons": "Névtelen szerkesztések elrejtése",
        "tog-watchlisthidepatrolled": "Az ellenőrzött szerkesztések elrejtése",
        "tog-watchlisthidecategorization": "Lapok kategorizálásának elrejtése",
        "october-date": "Október $1",
        "november-date": "November $1",
        "december-date": "December $1",
+       "period-am": "de.",
+       "period-pm": "du.",
        "pagecategories": "{{PLURAL:$1|Kategória|Kategóriák}}",
        "category_header": "A(z) „$1” kategóriába tartozó lapok",
        "subcategories": "Alkategóriák",
        "readonly": "Az adatbázis le van zárva",
        "enterlockreason": "Add meg a lezárás okát, valamint egy becslést, hogy mikor lesz a lezárásnak vége",
        "readonlytext": "A wiki adatbázisa ideiglenesen le van zárva (valószínűleg adatbázis-karbantartás miatt). A lezárás időtartama alatt a lapok nem szerkeszthetők, és új szócikkek sem hozhatók létre, az oldalakat azonban lehet böngészni.\n\nAz rendszeradminisztrátor, aki lezárta az adatbázist, az alábbi indoklást adta: $1",
-       "missing-article": "Az adatbázisban nem található meg a(z) „$1” című lap szövege $2.\n\nEnnek az oka általában az, hogy egy olyan lapra vonatkozó linket követtél, amit már töröltek.\n\nHa ez nem így van, lehet, hogy hibát találtál a szoftverben.\nJelezd ezt egy [[Special:ListUsers/sysop|adminiszttrátornak]] az URL megadásával.",
+       "missing-article": "Az adatbázisban nem található meg a(z) „$1” című lap szövege $2.\n\nEnnek az oka általában az, hogy egy olyan lapra vonatkozó linket követtél, amelyet már töröltek.\n\nHa ez nem így van, lehet, hogy hibát találtál a szoftverben.\nJelezd ezt egy [[Special:ListUsers/sysop|adminiszttrátornak]] az URL megadásával.",
        "missingarticle-rev": "(változat azonosítója: $1)",
        "missingarticle-diff": "(eltérés: $1, $2)",
        "readonly_lag": "Az adatbázis automatikusan le lett zárva, amíg a mellékkiszolgálók utolérik a főkiszolgálót.",
        "badtitle": "Hibás cím",
        "badtitletext": "A kért oldal címe érvénytelen, üres, vagy rosszul hivatkozott nyelvközi vagy wikiközi cím volt. Olyan karaktereket is tartalmazhatott, melyek címekben nem használhatók.",
        "title-invalid-empty": "A kért lapcím üres vagy csak egy névtér nevét tartalmazza.",
-       "title-invalid-utf8": "A kért oldal címe tartalmazza érvénytelen UTF-8 karaktert tartalmaz.",
+       "title-invalid-utf8": "A kért oldal címe érvénytelen UTF-8 szekvenciát tartalmaz.",
        "title-invalid-interwiki": "A cím interwiki-hivatkozást tartalmaz, amelyet nem lehet címben használni.",
-       "title-invalid-talk-namespace": "A kért lapcím egy olyan vitalapra hivatkozik, ami nem létezhet.",
+       "title-invalid-talk-namespace": "A kért lapcím olyan vitalapra hivatkozik, amely nem létezhet.",
        "title-invalid-characters": "A kért lapcím érvénytelen karaktereket tartalmaz: „$1”",
        "title-invalid-relative": "A cím relatív útvonalat tartalmaz. A relatív lapcímek (./, ../) érvénytelenek, mert gyakran elérhetetlenné válnak, amikor a felhasználó böngészője feldolgozza őket.",
        "title-invalid-magic-tilde": "A kért oldal címe érvénytelen mágikus tilde sorozatot (<nowiki>~~~</nowiki>) tartalmaz.",
        "viewsource": "Lapforrás",
        "viewsource-title": "$1 forrásának megtekintése",
        "actionthrottled": "Művelet megszakítva",
-       "actionthrottledtext": "A spamek elleni védekezés miatt nem végezheted el a műveletet túl sokszor egy adott időn belül, és te átlépted a megengedett határt. Próbálkozz újra néhány perc múlva.",
-       "protectedpagetext": "Ez egy védett lap, így nem végezhető rajta szerkesztés és más tevékenység.",
+       "actionthrottledtext": "A visszaélések elleni védekezés miatt nem végezheted el a műveletet túl sokszor egy adott időn belül, és te átlépted a megengedett határt. Próbálkozz újra néhány perc múlva.",
+       "protectedpagetext": "Ez egy védett lap, így nem végezhető rajta szerkesztés és más művelet.",
        "viewsourcetext": "Megtekintheted és másolhatod a lap forrását.",
        "viewyourtext": "Megtekintheted és kimásolhatod a <strong>saját szerkesztéseidet</strong> az alábbi lapra.",
        "protectedinterface": "Ez a lap a szoftver felületéhez szolgáltat szöveget, és a visszaélések elkerülése miatt le van zárva.",
        "ns-specialprotected": "A speciális lapok nem szerkeszthetők.",
        "titleprotected": "Ilyen címmel nem lehet szócikket készíteni, [[User:$1|$1]] letiltotta.\nAz indoklás: „''$2''”.",
        "filereadonlyerror": "A(z) „$1” fájl nem módosítható, mert a(z) „$2” fájltároló csak olvasható módban üzemel.\n\nA lezárást végrehajtó rendszeradminisztrátor az alábbi indoklást adta meg: „$3”.",
-       "invalidtitle-knownnamespace": "Érvénytelen cím \"$2\" névtérrel és \"$3\" szöveggel",
-       "invalidtitle-unknownnamespace": "Érvénytelen cím az ismeretlen $1 névtérszámmal és \"$2\" szöveggel",
+       "invalidtitle-knownnamespace": "Érvénytelen cím „$2” névtérrel és „$3” szöveggel",
+       "invalidtitle-unknownnamespace": "Érvénytelen cím az ismeretlen $1 névtérszámmal és „$2” szöveggel",
        "exception-nologin": "Nem vagy bejelentkezve.",
        "exception-nologin-text": "Ezen lap vagy művelet eléréséhez kérlek [[Special:Userlogin|jelentkezz be]].",
        "exception-nologin-text-manual": "Ezen lap vagy művelet eléréséhez $1.",
        "virus-scanfailed": "az ellenőrzés nem sikerült (hibakód: $1)",
        "virus-unknownscanner": "ismeretlen antivírus:",
        "logouttext": "'''Sikeresen kijelentkeztél.'''\n\nLehetséges, hogy néhány oldalon továbbra is azt látod, be vagy jelentkezve, mindaddig, amíg nem üríted a böngésződ gyorsítótárát.",
+       "cannotlogoutnow-title": "Nem lehet most kijelentkezni",
+       "cannotlogoutnow-text": "A kijelentkezés nem lehetséges $1 használatakor.",
        "welcomeuser": "Üdvözlünk, $1!",
        "welcomecreation-msg": "A felhasználói fiókod elkészült.\nNe felejtsd el módosítani a [[Special:Preferences|{{SITENAME}} beállításaidat]].",
        "yourname": "Szerkesztőneved:",
        "remembermypassword": "Emlékezzen rám ezen a számítógépen (legfeljebb $1 napig)",
        "userlogin-remembermypassword": "Maradjak bejelentkezve",
        "userlogin-signwithsecure": "Biztonságos kapcsolat használata",
+       "cannotloginnow-title": "Nem lehet most bejelentkezni",
+       "cannotloginnow-text": "A bejelentkezés nem lehetséges $1 használatakor.",
        "yourdomainname": "A domainneved:",
        "password-change-forbidden": "Nem módosíthatod a jelszót ezen a wikin.",
        "externaldberror": "Hiba történt a külső adatbázis hitelesítése közben, vagy nem vagy jogosult a külső fiókod frissítésére.",
        "createacct-benefit-body2": "{{PLURAL:$1|lap|lap}}",
        "createacct-benefit-body3": "aktív {{PLURAL:$1|szerkesztő|szerkesztő}}",
        "badretype": "A megadott jelszavak nem egyeznek.",
+       "usernameinprogress": "Egy fiók létrehozása ezzel a névvel már folyamatban van.\nKérlek, várj.",
        "userexists": "A megadott felhasználónév már foglalt.\nKérlek, válassz másikat!",
        "loginerror": "Hiba történt a bejelentkezés során",
        "createacct-error": "Fióklétrehozási hiba",
        "wrongpasswordempty": "Nem adtál meg jelszót. Próbáld meg újra.",
        "passwordtooshort": "A jelszónak legalább $1 karakterből kell állnia.",
        "passwordtoolong": "A jelszó nem lehet hosszabb $1 karakternél.",
+       "passwordtoopopular": "A gyakori jelszavak nem használhatók. Válassz egy egyedibb jelszót.",
        "password-name-match": "A jelszavadnak különböznie kell a szerkesztőnevedtől.",
        "password-login-forbidden": "Ezen felhasználónév és jelszó használata tiltott.",
        "mailmypassword": "Jelszó alaphelyzetbe állítása",
        "resetpass_submit": "Add meg a jelszót és jelentkezz be",
        "changepassword-success": "A jelszavad megváltoztatása sikeresen befejeződött!",
        "changepassword-throttled": "Túl sok hibás bejelentkezés.\nVárj $1, mielőtt újra próbálkozol.",
-       "botpasswords-label-appid": "A Bot neve:",
+       "botpasswords": "Botjelszavak",
+       "botpasswords-summary": "A <em>botjelszavak</em> lehetővé teszik egy felhasználói fiókhoz való hozzáférést az API-n keresztül a fiók fő bejelentkezési adatainak megadása nélkül. A botjelszóval történő bejelentkezéskor a felhasználói jogok korlátozottak lehetnek.\n\nHa nem tudod, hogy miért szeretnél ilyet, valószínűleg nem kell csinálnod. Soha senkinek nem szabadna megkérnie téged, hogy generálj neki egyet, hogy odaadhasd neki.",
+       "botpasswords-disabled": "A botjelszavak le vannak tiltva.",
+       "botpasswords-no-central-id": "A botjelszavak használatához egy globális fiókba kell bejelentkezned.",
+       "botpasswords-existing": "Létező botjelszavak",
+       "botpasswords-createnew": "Új botjelszó létrehozása",
+       "botpasswords-editexisting": "Létező botjelszó szerkesztése",
+       "botpasswords-label-appid": "A bot neve:",
        "botpasswords-label-create": "Létrehozás",
        "botpasswords-label-update": "Frissítés",
        "botpasswords-label-cancel": "Mégsem",
        "botpasswords-label-delete": "Törlés",
        "botpasswords-label-resetpassword": "Új jelszó kérése",
+       "botpasswords-label-grants": "Elérhető jogosultságok:",
        "botpasswords-label-restrictions": "Használati korlátozások:",
-       "botpasswords-created-title": "Bot jelszó létrehozva",
-       "botpasswords-updated-title": "Bot jelszó frissítve",
-       "botpasswords-deleted-title": "Bot jelszó törölve",
+       "botpasswords-label-grants-column": "Megadva",
+       "botpasswords-bad-appid": "A(z) „$1” botnév érvénytelen.",
+       "botpasswords-insert-failed": "A(z) „$1” botnév hozzáadása sikertelen. Nem lehet, hogy már hozzá lett adva?",
+       "botpasswords-created-title": "Botjelszó létrehozva",
+       "botpasswords-created-body": "Bot jelszó \"$1\" sikeresen létrehozva.",
+       "botpasswords-updated-title": "Botjelszó frissítve",
+       "botpasswords-updated-body": "Bot jelszó \"$1\" frissítése sikerült.",
+       "botpasswords-deleted-title": "Botjelszó törölve",
+       "botpasswords-deleted-body": "Bot jelszó \"$1\" törölve.",
+       "botpasswords-no-provider": "A BotPasswordsSessionProvider nem áll rendelkezésre.",
        "resetpass_forbidden": "A jelszavak nem változtathatók meg",
        "resetpass-no-info": "Be kell jelentkezned, hogy közvetlenül elérd ezt a lapot.",
        "resetpass-submit-loggedin": "Jelszó megváltoztatása",
        "changeemail-no-info": "A lap közvetlen eléréséhez be kell jelentkezned.",
        "changeemail-oldemail": "Jelenlegi e-mail-cím:",
        "changeemail-newemail": "Új e-mail-cím:",
+       "changeemail-newemail-help": "Ha el akarod távolítani az e-mail-címed, ezt a mezőt üresen kell hagynod. Ha eltávolítod az e-mail-címed, nem fogod tudni visszaállítani a jelszavad, és nem fogsz tudni e-maileket fogadni erről a wikiről.",
        "changeemail-none": "(nincs)",
        "changeemail-password": "A {{SITENAME}} jelszavad:",
        "changeemail-submit": "E-mail cím megváltoztatása",
        "changeemail-throttled": "Túl sok hibás bejelentkezés.\nVárj $1, mielőtt újra próbálkozol.",
+       "changeemail-nochange": "Kérjük, adj meg egy másik új e-mail-címet.",
        "resettokens": "Tokenek törlése",
        "resettokens-text": "Újra generálhatod a tokeneket, amely a fiókodhoz rendelt bizonyos magánadatokhoz enged hozzáférést.\n\nEzt akkor érdemes használnod, hogy véletlenül megosztottad a tokeneket valakivel, vagy ha valaki feltörte a fiókodat.",
        "resettokens-no-tokens": "Nincs újragenerálható token.",
        "missingcommenttext": "Kérjük, írj összefoglalót a szerkesztésedhez.",
        "missingcommentheader": "<strong>Emlékeztető:</strong> Nem adtad meg a megjegyzés tárgyát.\nHa ismét a „{{int:savearticle}}” gombra kattintasz, akkor a szerkesztésed nélküle lesz elmentve.",
        "summary-preview": "A szerkesztési összefoglaló előnézete:",
-       "subject-preview": "A téma/főcím előnézete:",
+       "subject-preview": "Tárgy előnézete:",
        "previewerrortext": "Hiba történt a változások előnézete megjelenítése során.",
        "blockedtitle": "A szerkesztő blokkolva van",
        "blockedtext": "'''A szerkesztőnevedet vagy az IP-címedet blokkoltuk.'''\n\nA blokkolást $1 végezte el.\nAz általa felhozott indok: ''$2''.\n\n* A blokk kezdete: $8\n* A blokk lejárata: $6\n* Blokkolt szerkesztő: $7\n\nKapcsolatba léphetsz $1 szerkesztőnkkel, vagy egy másik [[{{MediaWiki:Grouppage-sysop}}|adminisztrátorral]], és megbeszélheted vele a blokkolást.\nAz 'E-mail küldése ennek a szerkesztőnek' funkciót csak akkor használhatod, ha érvényes e-mail címet adtál meg\n[[Special:Preferences|fiókbeállításaidban]], és nem blokkolták a használatát.\nJelenlegi IP-címed: $3, a blokkolás azonosítószáma: #$5.\nKérjük, hogy érdeklődés esetén mindkettőt add meg.",
        "previewnote": "'''Ne feledd, hogy ez csak egy előnézet.''' A változtatásaid még nincsenek elmentve!",
        "continue-editing": "Szerkesztés folytatása",
        "previewconflict": "Ez az előnézet a felső szerkesztődobozban levő szöveg mentés utáni megfelelőjét mutatja.",
-       "session_fail_preview": "<strong>Az elveszett munkamenetadatok miatt sajnos nem tudtuk feldolgozni a szerkesztésedet.</strong>\nKérjük, próbálkozz újra!\nAmennyiben továbbra sem sikerül, próbálj meg [[Special:UserLogout|kijelentkezni]], majd ismét bejelentkezni!",
-       "session_fail_preview_html": "'''Az elveszett munkamenetadatok miatt nem tudtuk feldolgozni a szerkesztésedet.'''\n\n''Mivel a wikiben engedélyezett a nyers HTML-kód használata, az előnézet el van rejtve a JavaScript-alapú támadások megakadályozása céljából.''\n\n'''Ha ez egy normális szerkesztési kísérlet, akkor próbálkozz újra. Amennyiben továbbra sem sikerül, próbálj meg [[Special:UserLogout|kijelentkezni]], majd ismét bejelentkezni!''' (a változtatásaidat mentsd el magadnak, különben elvesznek!)",
+       "session_fail_preview": "Az elveszett munkamenetadatok miatt sajnos nem tudtuk feldolgozni a szerkesztésedet.\nLehet, hogy ki vagy jelentkezve. <strong>Kérjük, győződj meg róla, hogy még mindig be vagy jelentkezve, majd próbálkozz újra!</strong>\nAmennyiben továbbra sem sikerül, próbálj meg [[Special:UserLogout|kijelentkezni]], majd ismét bejelentkezni, és ellenőrizd, hogy a böngésződ elfogad sütiket erről az oldalról!",
+       "session_fail_preview_html": "<strong>Az elveszett munkamenetadatok miatt nem tudtuk feldolgozni a szerkesztésedet.</strong>\n\n<em>Mivel a wikiben engedélyezett a nyers HTML-kód használata, az előnézet el van rejtve a JavaScript-alapú támadások megakadályozása céljából.</em>\n\n<strong>Ha ez egy normális szerkesztési kísérlet, akkor próbálkozz újra.</strong>\nAmennyiben továbbra sem sikerül, próbálj meg [[Special:UserLogout|kijelentkezni]], majd ismét bejelentkezni, és ellenőrizd, hogy a böngésződ elfogad sütiket erről az oldalról!",
        "token_suffix_mismatch": "<strong>A szerkesztésedet elutasítottuk, mert a kliensprogramod megváltoztatta a központozó karaktereket\na szerkesztési tokenben.</strong>\nA szerkesztés azért lett visszautasítva, hogy megelőzzük a lap szövegének sérülését.\nEz a probléma akkor fordulhat elő, ha hibás web-alapú proxyszolgáltatást használsz.",
        "edit_form_incomplete": "'''A szerkesztési űrlap egyes részei nem érkeztek meg a szerverre; ellenőrizd, hogy a szerkesztés sértetlen-e, majd próbáld újra.'''",
        "editing": "$1 szerkesztése",
        "permissionserrors": "Engedélyezési hiba",
        "permissionserrorstext": "A művelet elvégzése nem engedélyezett a számodra, a következő {{PLURAL:$1|ok|okok}} miatt:",
        "permissionserrorstext-withaction": "Nincs jogosultságod a következő művelet elvégzéséhez: $2, a következő {{PLURAL:$1|ok|okok}} miatt:",
+       "contentmodelediterror": "Nem szerkesztheted ezt a változatot, mert a tartalommodellje <code>$1</code>, ami eltér a jelenlegitől (<code>$2</code>).",
        "recreate-moveddeleted-warn": "'''Figyelem! Olyan lapot készülsz létrehozni, amit már legalább egyszer töröltek.'''\n\nMielőtt létrehoznád, nézd meg, miért törölték a lap korábbi tartalmát, és győződj meg róla, hogy a törlés indoka érvényes-e még. A törlési és átnevezési naplókban az érintett lapról az alábbi bejegyzések szerepelnek:",
        "moveddeleted-notice": "Az oldal korábban törölve lett.\nA lap törlési és átnevezési naplója alább olvasható.",
+       "moveddeleted-notice-recent": "Sajnáljuk, az oldalt nemrég törölték (az elmúlt 24 órában).\nA részletekért lásd lentebb a törlési és átnevezési naplót.",
        "log-fulllog": "Teljes napló megtekintése",
        "edit-hook-aborted": "A szerkesztés meg lett szakítva egy hook által.\nNem lett magyarázat csatolva.",
        "edit-gone-missing": "Nem lehet frissíteni a lapot.\nÚgy tűnik, hogy törölve lett.",
        "mergehistory-empty": "Nincs egyesíthető változás.",
        "mergehistory-done": "$1 $3 változata sikeresen egyesítve lett a(z) [[:$2]] lappal.",
        "mergehistory-fail": "Nem sikerült a laptörténetek egyesítése. Kérlek, ellenőrizd újra az oldalt és a megadott időparamétereket.",
+       "mergehistory-fail-bad-timestamp": "Érvénytelen időbélyeg.",
+       "mergehistory-fail-invalid-source": "Érvénytelen forráslap.",
+       "mergehistory-fail-invalid-dest": "Érvénytelen céllap.",
+       "mergehistory-fail-self-merge": "A forrás- és céllap megegyezik.",
        "mergehistory-fail-toobig": "Nem lehetséges a laptörténetek egyesítése, mivel több mint $1 {{PLURAL:$1|változást}} kellene áthelyezni.",
        "mergehistory-no-source": "Nem létezik forráslap $1 néven.",
        "mergehistory-no-destination": "Nem létezik céllap $1 néven.",
        "showingresultsinrange": "Lent <strong>$1</strong> találat látható ($2–$3.)",
        "search-showingresults": "{{PLURAL:$4|<strong>$1.</strong> a(z) <strong>$3</strong> találatból|<strong>$1–$2.</strong> a(z) <strong>$3</strong> találatból}}",
        "search-nonefound": "Nincs egyezés a megadott szöveggel.",
+       "search-nonefound-thiswiki": "Nincs egyezés a megadott szöveggel ezen a wikin.",
        "powersearch-legend": "Részletes keresés",
        "powersearch-ns": "Névterek:",
        "powersearch-togglelabel": "Megjelölés:",
        "prefs-help-recentchangescount": "Ez vonatkozik a friss változtatásokra, laptörténetekre és naplókra is.",
        "prefs-help-watchlist-token2": "Ez a titkos kulcs a figyelőlistádhoz.\nAki ismeri, meg tudja nézni, milyen lapokat figyelsz, úgyhogy ne oszdd meg másokkal.\n[[Special:ResetTokens|Kattints ide, ha meg akarod változtatni]].",
        "savedprefs": "Az új beállításaid érvénybe léptek.",
+       "savedrights": "$1 felhasználói jogai el lettek mentve.",
        "timezonelegend": "Időzóna:",
        "localtime": "Helyi idő:",
        "timezoneuseserverdefault": "Az alapértelmezett beállítás használata ($1)",
        "userrights": "Szerkesztői jogok beállítása",
        "userrights-lookup-user": "Szerkesztőcsoportok beállítása",
        "userrights-user-editname": "Add meg a szerkesztő nevét:",
-       "editusergroup": "Szerkesztőcsoportok módosítása",
+       "editusergroup": "{{GENDER:$1|Szerkesztőcsoportok}} módosítása",
        "editinguser": "<strong>[[User:$1|$1]]</strong> felhasználó jogainak megváltoztatása $2",
        "userrights-editusergroup": "Szerkesztőcsoportok módosítása",
-       "saveusergroups": "Szerkesztőcsoportok mentése",
+       "saveusergroups": "{{GENDER:$1|Szerkesztőcsoportok}} mentése",
        "userrights-groupsmember": "Csoporttag:",
        "userrights-groupsmember-auto": "Alapértelmezetten tagja:",
        "userrights-groups-help": "Beállíthatod, hogy a szerkesztő mely csoportokba tartozik.\n* A bepipált doboz azt jelenti, hogy a szerkesztő benne van a csoportban, az üres azt, hogy nem.\n* A * az olyan csoportokat jelöli, amelyeket ha egyszer hozzáadtál, nem távolíthatod el, vagy nem adhatod hozzá.",
        "right-createpage": "lapok készítése (nem vitalapok)",
        "right-createtalk": "vitalapok készítése",
        "right-createaccount": "új felhasználói fiók készítése",
+       "right-autocreateaccount": "automatikus bejelentkezés külső felhasználói fiókkal",
        "right-minoredit": "szerkesztések apróként jelölésének lehetősége",
        "right-move": "lapok átnevezése",
        "right-move-subpages": "lapok átnevezése az allapjukkal együtt",
        "right-reupload-shared": "felülírhatja a közös megosztóhelyen lévő fájlokat helyben",
        "right-upload_by_url": "fájl feltöltése URL-cím alapján",
        "right-purge": "oldal gyorsítótárának ürítése megerősítés nélkül",
-       "right-autoconfirmed": "Nem érinti az IP-alapú szerkesztéskorlátozás",
+       "right-autoconfirmed": "nem érinti az IP-alapú szerkesztéskorlátozás",
        "right-bot": "automatikus folyamatként való kezelés",
        "right-nominornewtalk": "felhasználói lapok apró szerkesztésével nem jelenik meg az új üzenet értesítés",
        "right-apihighlimits": "nagyobb mennyiségű lekérdezés az API-n keresztül",
        "right-deletedtext": "törölt változatok szövegének és a változatok közötti eltérés megtekintése",
        "right-browsearchive": "keresés a törölt lapok között",
        "right-undelete": "lap helyreállítása",
-       "right-suppressrevision": "Bármely felhasználó által végzett változatok megtekintése, elrejtése és felfedése",
+       "right-suppressrevision": "bármely felhasználó által végzett változatok megtekintése, elrejtése és felfedése",
        "right-viewsuppressed": "Bármely felhasználó elrejtett változatainak megjelenítése",
        "right-suppressionlog": "privát naplók megtekintése",
        "right-block": "szerkesztők blokkolása",
        "right-editusercssjs": "más felhasználók CSS és JS fájljainak szerkesztése",
        "right-editusercss": "más felhasználók CSS fájljainak szerkesztése",
        "right-edituserjs": "más felhasználók JS fájljainak szerkesztése",
-       "right-editmyusercss": "A saját szerkesztői CSS-fájlok szerkesztése",
-       "right-editmyuserjs": "Saját szerkesztői JavaScript-fájlok szerkesztése",
+       "right-editmyusercss": "Saját szerkesztői CSS-fájlok szerkesztése",
+       "right-editmyuserjs": "saját szerkesztői JavaScript-fájlok szerkesztése",
        "right-viewmywatchlist": "saját figyelőlista megtekintése",
        "right-editmywatchlist": "saját figyelőlista szerkesztése; bizonyos műveletek képesek lapok figyelőlistához adására ezen jog nélkül is",
        "right-viewmyprivateinfo": "saját személyes adatok megtekintése (pl. e-mail cím, valódi név)",
        "right-override-export-depth": "Lapok exportálása a hivatkozott lapokkal együtt, legfeljebb 5-ös mélységig",
        "right-sendemail": "e-mail küldése más felhasználóknak",
        "right-passwordreset": "Jelszó visszaállítási emailek megtekintése",
-       "right-managechangetags": "Adatbázis [[Special:Tags|címkék]] létrehozása és törlése",
+       "right-managechangetags": "[[Special:Tags|címkék]] létrehozása és törlése az adatbázisban",
        "right-applychangetags": "[[Special:Tags|címkék]] alkalmazása a változakra",
-       "right-changetags": "Egyedi változtatásokon és napló bejegyzéseken tetszőleges [[Special:Tags|címkék]] hozzáadása és törlése",
+       "right-changetags": "egyedi lapváltozatokon és naplóbejegyzéseken tetszőleges [[Special:Tags|címkék]] hozzáadása és törlése",
        "grant-generic": "„$1” jogosultságcsomag",
+       "grant-group-page-interaction": "interakció lapokkal",
+       "grant-group-file-interaction": "interakció médiával",
+       "grant-group-watchlist-interaction": "interakció a figyelőlistáddal",
        "grant-group-email": "E-mail küldése",
        "grant-group-high-volume": "Nagy mennyiségű szerkesztés végrehajtása",
        "grant-group-customization": "Személyre szabás és beállítások",
        "grant-group-administration": "Adminisztratív műveletek végrehajtása",
+       "grant-group-other": "egyéb műveletek",
        "grant-blockusers": "felhasználók blokkolása és blokk feloldása",
        "grant-createaccount": "fiókok létrehozása",
-       "grant-createeditmovepage": "Lapok készítése, szerkesztése és átnevezése",
+       "grant-createeditmovepage": "lapok létrehozása, szerkesztése és átnevezése",
        "grant-delete": "lapok, lapváltozatok és naplóbejegyzések törlése",
        "grant-editinterface": "MediaWiki-névtér és felhasználói CSS/JavaScript szerkesztése",
        "grant-editmycssjs": "Felhasználói CSS-ed/JavaScripted szerkesztése",
-       "grant-editmyoptions": "Felhasználói beállításaid szerkesztése",
+       "grant-editmyoptions": "felhasználói beállításaid módosítása",
        "grant-editmywatchlist": "figyelőlista szerkesztése",
        "grant-editpage": "létező lapok szerkesztése",
        "grant-editprotected": "védett lapok szerkesztése",
        "grant-rollback": "szerkesztések gyors visszaállítása",
        "grant-sendemail": "e-mail küldése más felhasználóknak",
        "grant-uploadeditmovefile": "fájlok feltöltése, felülírása és átnevezése",
-       "grant-uploadfile": "fájlok feltöltése",
-       "grant-viewdeleted": "Törölt fájlok és lapok megtekintése",
+       "grant-uploadfile": "új fájlok feltöltése",
+       "grant-basic": "alapvető jogosultságok",
+       "grant-viewdeleted": "törölt fájlok és lapok megtekintése",
        "grant-viewmywatchlist": "figyelőlista megtekintése",
        "newuserlogpage": "Új szerkesztők naplója",
        "newuserlogpagetext": "Ez a napló az újonnan regisztrált szerkesztők listáját tartalmazza.",
        "rcshowhidemine": "saját szerkesztések $1",
        "rcshowhidemine-show": "megjelenítése",
        "rcshowhidemine-hide": "elrejtése",
+       "rcshowhidecategorization": "lapok kategorizálásának $1",
+       "rcshowhidecategorization-show": "megjelenítése",
+       "rcshowhidecategorization-hide": "elrejtése",
        "rclinks": "Az elmúlt $2 nap utolsó $1 változtatása legyen látható<br />$3",
        "diff": "eltér",
        "hist": "történet",
        "newpageletter": "Ú",
        "boteditletter": "b",
        "number_of_watching_users_pageview": "[Jelenleg {{PLURAL:$1|egy|$1}} felhasználó figyeli]",
-       "rc_categories": "Szűkítés kategóriákra („|” jellel válaszd el őket)",
-       "rc_categories_any": "Bármelyik",
+       "rc_categories": "Szűkítés kategóriákra („|” jellel válaszd el őket):",
+       "rc_categories_any": "Választottak közül bármelyik",
        "rc-change-size-new": "$1 bájt módosítás után",
        "newsectionsummary": "/* $1 */ (új szakasz)",
        "rc-enhanced-expand": "Részletek megjelenítése",
        "recentchangeslinked-summary": "Alább azon lapoknak a legutóbbi változtatásai láthatóak, amelyekre hivatkozik egy megadott lap (vagy tagjai a megadott kategóriának).\nA [[Special:Watchlist|figyelőlistádon]] szereplő lapok '''félkövérrel''' vannak jelölve.",
        "recentchangeslinked-page": "Lap neve:",
        "recentchangeslinked-to": "Inkább az erre linkelő lapok változtatásait mutasd",
+       "recentchanges-page-added-to-category": "[[:$1]] hozzáadva a kategóriához",
+       "recentchanges-page-added-to-category-bundled": "[[:$1]] és {{PLURAL:$2|egy oldal|$2 oldal}} hozzáadva a kategóriához",
+       "recentchanges-page-removed-from-category": "[[:$1]] eltávolítva a kategóriából",
+       "recentchanges-page-removed-from-category-bundled": "[[:$1]] és {{PLURAL:$2|egy oldal|$2 oldal}} eltávolítva a kategóriából",
        "upload": "Fájl feltöltése",
        "uploadbtn": "Fájl feltöltése",
        "reuploaddesc": "Visszatérés a feltöltési űrlaphoz.",
        "upload-options": "Feltöltési beállítások",
        "watchthisupload": "Fájl figyelése",
        "filewasdeleted": "Korábban valaki már feltöltött ilyen néven egy fájlt, amelyet később töröltünk. Ellenőrizd a $1 bejegyzését, nehogy újra feltöltsd ugyanezt a fájlt.",
+       "filename-thumb-name": "Ez bélyegkép-fájlnévnek tűnik. Ne tölts vissza bélyegképeket ugyanarra a wikire. Ha nem erről van szó, akkor javítsd ki a fájlnevet informatívabbra, ami nem tartalmazza a bélyegkép-előtagot.",
        "filename-bad-prefix": "Annak a fájlnak a neve, amelyet fel akarsz tölteni '''„$1”''' karakterekkel kezdődik. Ilyeneket általában a digitális kamerák adnak a fájloknak, automatikusan, azonban ezek nem írják le annak tartalmát. Válassz egy leíró nevet!",
        "filename-prefix-blacklist": " #<!-- ezt a sort hagyd így --> <pre>\n#A szintaktika a következő:\n#   * Minden a „#” karaktertől a sor végéig megjegyzésnek számít\n#   * Minden nemüres sor egy, a digitális fényképezőképek által fájlok neveként használt előtag\nCIMG # Casio\nDSC_ # Nikon\nDSCF # Fuji\nDSCN # Nikon\nDUW # néhány mobiltelefon\nIMG # általános\nJD # Jenoptik\nMGP # Pentax\nPICT # ált.\n #</pre> <!-- ezt a sort hagyd így -->",
        "upload-proto-error": "Hibás protokoll",
        "upload-too-many-redirects": "Az URL túl sokszor volt átirányítva",
        "upload-http-error": "HTTP-hiba történt: $1",
        "upload-copy-upload-invalid-domain": "Másolás nem engedélyezett ebből a tartományból.",
+       "upload-dialog-title": "Fájl feltöltése",
+       "upload-dialog-button-cancel": "Mégse",
+       "upload-dialog-button-done": "Kész",
+       "upload-dialog-button-save": "Mentés",
+       "upload-dialog-button-upload": "Feltöltés",
+       "upload-form-label-infoform-title": "Részletek",
+       "upload-form-label-infoform-name": "Név",
+       "upload-form-label-infoform-name-tooltip": "Egy egyedi, leíró cím a fájlnak, ami fájlnévként fog szolgálni. Egyszerű nyelvezetet használhatsz szóközökkel. Ne tedd bele a kiterjesztést.",
+       "upload-form-label-infoform-description": "Leírás",
+       "upload-form-label-infoform-description-tooltip": "Röviden írj le minden említésre méltót a műről.\nFénykép esetén említsd meg a főbb látható dolgokat, a készítés alkalmát vagy helyszínét.",
+       "upload-form-label-usage-title": "Használat",
+       "upload-form-label-usage-filename": "Fájlnév",
+       "foreign-structured-upload-form-label-own-work": "Ez a saját munkám",
+       "foreign-structured-upload-form-label-infoform-categories": "Kategóriák",
+       "foreign-structured-upload-form-label-infoform-date": "Dátum",
+       "foreign-structured-upload-form-label-own-work-message-local": "Kijelentem, hogy a fájlt a(z) {{SITENAME}} következő felhasználási feltételei és licencirányelvei alapján töltöm fel.",
+       "foreign-structured-upload-form-label-not-own-work-message-local": "Ha nem tudod feltölteni a fájlt a(z) {{SITENAME}} irányelvei értelmében, zárd be ezt a panelt és próbálkozz egy másik módszerrel.",
+       "foreign-structured-upload-form-label-not-own-work-local-local": "Az [[Special:Upload|alapértelmezett feltöltőoldalt]] is kipróbálhatod.",
+       "foreign-structured-upload-form-label-own-work-message-default": "Megértettem, hogy a megosztott tárhelyre töltöm fel a fájlt. Kijelentem, hogy az ottani felhasználási feltételek és licencirányelvek szerint teszem ezt.",
+       "foreign-structured-upload-form-label-not-own-work-message-default": "Ha nem tudod feltölteni a fájlt a megosztott tárhely irányelvei értelmében, zárd be ezt a panelt és próbálkozz egy másik módszerrel.",
+       "foreign-structured-upload-form-label-not-own-work-local-default": "Megpróbálhatod használni [[Special:Upload|a(z) {{SITENAME}} feltöltési lapját]] is, ha ezt a fájlt fel lehet tölteni az itteni irányelvek szerint.",
+       "foreign-structured-upload-form-label-own-work-message-shared": "Igazolom, hogy én birtoklom a fájl szerzői jogait, és egyetértek azzal, hogy visszavonhatatlanul közzéteszem a fájlt a Wikimédia Commonson a [https://creativecommons.org/licenses/by-sa/4.0/deed.hu Creative Commons Nevezd meg! – Így add tovább! 4.0] licenc alatt, és egyetértek a [https://wikimediafoundation.org/wiki/Terms_of_Use felhasználási feltételekkel].",
+       "foreign-structured-upload-form-label-not-own-work-message-shared": "Ha nem birtoklod a fájl szerzői jogait vagy más licenc alatt szeretnéd közzétenni, fontold meg a [https://commons.wikimedia.org/wiki/Special:UploadWizard Commons Feltöltésvarázslójának] használatát.",
+       "foreign-structured-upload-form-label-not-own-work-local-shared": "Megpróbálhatod használni [[Special:Upload|a(z) {{SITENAME}} feltöltési lapját]] is, ha ezt a fájlt fel lehet tölteni az itteni irányelvek szerint.",
        "backend-fail-stream": "Nem sikerült sugározni ezt a fájlt: $1.",
        "backend-fail-backup": "Nem lehet elmenteni ezt a fájlt: $1.",
        "backend-fail-notexists": "Ez a fájl nem létezik: $1 .",
        "mostrevisions": "Legtöbbet szerkesztett lapok",
        "prefixindex": "Keresés előtag szerint",
        "prefixindex-namespace": "Összes lap adott előtaggal ($1 névtér)",
+       "prefixindex-submit": "Mutat",
        "prefixindex-strip": "Előtag eltüntetése a listában",
        "shortpages": "Rövid lapok",
        "longpages": "Hosszú lapok",
        "protectedpages-performer": "A levédést végrehajtó szerkesztő",
        "protectedpages-params": "A védelem paraméterei",
        "protectedpages-reason": "Indoklás",
+       "protectedpages-submit": "Lapok mutatása",
        "protectedpages-unknown-timestamp": "Ismeretlen",
        "protectedpages-unknown-performer": "Ismeretlen felhasználó",
        "protectedtitles": "Létrehozás ellen védett lapok",
        "protectedtitles-summary": "Az alábbi speciális lap listázza azokat a nem létező címeket, melyeket létrehozás ellen védtek le az adminisztrátorok, ellentétben azokkal a [[{{#special:ProtectedPages}}|létező lapokkal]], melyeket módosítás ellen védtek le.",
        "protectedtitlesempty": "Jelenleg nincsenek ilyen típusú védett lapok.",
+       "protectedtitles-submit": "Leírások mutatása",
        "listusers": "Szerkesztők",
        "listusers-editsonly": "Csak a szerkesztéssel rendelkező szerkesztők mutatása",
        "listusers-creationsort": "Rendezés létrehozási dátum szerint",
        "usereditcount": "{{PLURAL:$1|egy|$1}} szerkesztés",
        "usercreated": "{{GENDER:$3|Létrehozva}} $1, $2-kor",
        "newpages": "Új lapok",
+       "newpages-submit": "Mutat",
        "newpages-username": "Felhasználói név:",
        "ancientpages": "Régóta nem változott szócikkek",
        "move": "Átnevezés",
        "querypage-disabled": "Ez a speciális lap a megfelelő teljesítmény fenntartása érdekében le van tiltva.",
        "apihelp": "API segítség",
        "apihelp-no-such-module": "A(z) „$1\" modul nem található.",
+       "apisandbox": "API homokozó",
+       "apisandbox-jsonly": "Az API-homokozó használatához JavaScriptre van szükség.",
+       "apisandbox-api-disabled": "API le van tiltva ezen az oldalon.",
+       "apisandbox-intro": "Ezen az oldalon kísérletezhetsz a <strong>MediaWiki web service API</strong>-val.\nA használattal kapcsolatos további részletek az [[mw:API:Main page|API-dokumentációnál]] találhatók. Példa: [//www.mediawiki.org/wiki/API#A_simple_example olvasd el a főoldal tartalomjegyzékét]. További példákért válassz egy tevékenységet!\n\nFigyelj rá, hogy bár ez csak egy „homokozó”, ettől még az általad végzett műveletek módosíthatják a wikit!",
+       "apisandbox-submit": "Kérés végrehajtása",
+       "apisandbox-reset": "Törlés",
+       "apisandbox-retry": "Újra",
+       "apisandbox-loading": "Információ betöltése a(z) „$1” API-modulhoz…",
+       "apisandbox-load-error": "Hiba történt az információk betöltésekor a(z) „$1” API-modulhoz: $2",
+       "apisandbox-no-parameters": "Ennek az API-modulnak nincsenek paraméterei.",
+       "apisandbox-helpurls": "Segítő linkek",
+       "apisandbox-examples": "Példák",
+       "apisandbox-dynamic-parameters": "További paraméterek",
+       "apisandbox-dynamic-parameters-add-label": "Paraméter hozzáadása:",
+       "apisandbox-dynamic-parameters-add-placeholder": "Paraméter neve",
+       "apisandbox-dynamic-error-exists": "A(z) „$1” nevű paraméter már létezik.",
+       "apisandbox-deprecated-parameters": "Elavult paraméterek",
+       "apisandbox-submit-invalid-fields-title": "Egyes mezők érvénytelenek",
+       "apisandbox-submit-invalid-fields-message": "Javítsd ki a jelzett mezőket, és próbáld újra.",
+       "apisandbox-results": "Eredmények",
+       "apisandbox-sending-request": "API-kérés küldése…",
+       "apisandbox-loading-results": "API-válaszok fogadása…",
+       "apisandbox-request-url-label": "Kérő URL:",
+       "apisandbox-request-time": "Kérés hossza: $1 ms",
        "booksources": "Könyvforrások",
        "booksources-search-legend": "Könyvforrások keresése",
        "booksources-search": "Keresés",
        "specialloguserlabel": "Szerkesztő:",
        "speciallogtitlelabel": "Cél (cím vagy felhasználó):",
        "log": "Rendszernaplók",
+       "logeventslist-submit": "Mutat",
        "all-logs-page": "Minden nyilvános napló",
        "alllogstext": "A(z) {{SITENAME}} naplóinak összesített listája.\nA naplótípus, a felhasználónév (kis- és nagybetűérzékeny) vagy az érintett lap kiválasztásával (ez is kis- és nagybetűérzékeny) szűkítheted a találatok listáját.",
        "logempty": "Nincs illeszkedő naplóbejegyzés.",
        "log-title-wildcard": "Így kezdődő címek keresése",
        "showhideselectedlogentries": "Kijelölt napló bejegyzések megjelenítése/elrejtése",
        "log-edit-tags": "Kiválasztott napló címkék szerkesztése",
+       "checkbox-select": "Kiválasztás: $1",
+       "checkbox-all": "Mind",
+       "checkbox-none": "Nincs",
+       "checkbox-invert": "Megfordítás",
        "allpages": "Az összes lap listája",
        "nextpage": "Következő lap ($1)",
        "prevpage": "Előző lap ($1)",
        "cachedspecial-viewing-cached-ts": "Az oldal tárolt változatát látod, ami eltérhet az aktuálistól.",
        "cachedspecial-refresh-now": "A legfrissebb változat megjelenítése.",
        "categories": "Kategóriák",
+       "categories-submit": "Mutat",
        "categoriespagetext": "A következő {{PLURAL:$1|kategória tartalmaz|kategóriák tartalmaznak}} lapokat vagy fájlokat.\nA [[Special:UnusedCategories|nem használt kategóriák]] nem jelennek meg.\nLásd még a [[Special:WantedCategories|keresett kategóriák]] listáját.",
        "categoriesfrom": "Kategóriák listázása a következő névtől kezdve:",
        "special-categories-sort-count": "rendezés elemszám szerint",
        "activeusers-hidebots": "Botok elrejtése",
        "activeusers-hidesysops": "Adminisztrátorok elrejtése",
        "activeusers-noresult": "Nem található ilyen szerkesztő.",
+       "activeusers-submit": "Aktív szerkesztők megjelenítése",
        "listgrouprights": "Szerkesztői csoportok jogai",
        "listgrouprights-summary": "Lenn láthatóak a wikiben létező szerkesztői csoportok, valamint az azokhoz tartozó jogok.\nAz egyes csoportokról további információt [[{{MediaWiki:Listgrouprights-helppage}}|itt]] találhatsz.",
        "listgrouprights-key": "* <span class=\"listgrouprights-granted\">Kapott jog</span>\n* <span class=\"listgrouprights-revoked\">Elvett jog</span>",
        "listgrouprights-namespaceprotection-header": "Névtér korlátozások",
        "listgrouprights-namespaceprotection-namespace": "Névtér",
        "listgrouprights-namespaceprotection-restrictedto": "A szerkesztéshez szükséges jogosultság(ok)",
+       "listgrants": "Jogosultságok",
        "listgrants-summary": "Lenn láthatóak az OAuth által használt jogosultsági szintek, valamint az azokhoz tartozó jogok. A felhasználók engedélyezhetnek alkalmazásokat, hogy használják a fiókjukat, de csak korlátozott engedélyekkel, amelyek a felhasználó által engedélyezett jogosultsági szinteken alapulnak. Egy ilyen alkalmazás nem használhatja ezeket a jogokat, ha azokkal a felhasználó sem rendelkezik.\nLehetnek [[{{MediaWiki:Listgrouprights-helppage}}|további információk]] az egyes jogokról.",
        "listgrants-grant": "Jogosultsági szint",
-       "listgrants-rights": "Jogok",
+       "listgrants-rights": "Jogosultságok",
        "trackingcategories": "Nyomkövető kategóriák",
        "trackingcategories-summary": "Ez az oldal azokat a nyomkövető kategóriákat tartalmazza, amelyet a MediaWiki szoftver magától feltölt. Ezen neveket a megfelelő rendszer üzenet módosításával lehet megváltoztatni a {{ns:8}} névtérben.",
        "trackingcategories-msg": "Nyomkövető kategória",
        "wlshowhidebots": "botok",
        "wlshowhideliu": "bejelentkezett felhasználók",
        "wlshowhideanons": "névtelen felhasználók",
+       "wlshowhidepatr": "ellenőrzött szerkesztések",
        "wlshowhidemine": "saját szerkesztéseim",
+       "wlshowhidecategorization": "oldal kategorizálása",
        "watchlist-options": "A figyelőlista beállításai",
        "watching": "Figyelés...",
        "unwatching": "Figyelés befejezése...",
        "delete-confirm": "$1 törlése",
        "delete-legend": "Törlés",
        "historywarning": "<strong>Figyelem:</strong> a lapnak, amit törölni készülsz, $1 változattal rendelkező laptörténete van:",
+       "historyaction-submit": "Mutat",
        "confirmdeletetext": "Egy lapot vagy fájlt készülsz törölni a teljes laptörténetével együtt.\nKérjük, erősítsd meg, hogy valóban ezt szeretnéd tenni, átlátod a következményeit, és hogy a műveletet a [[{{MediaWiki:Policy-url}}|törlési irányelvekkel]] összhangban végzed.",
        "actioncomplete": "Művelet végrehajtva",
        "actionfailed": "A művelet nem sikerült",
        "block-log-flags-hiddenname": "rejtett felhasználónév",
        "range_block_disabled": "A rendszerfelelős tartományblokkolás létrehozási képessége letiltott.",
        "ipb_expiry_invalid": "Hibás lejárati dátum.",
+       "ipb_expiry_old": "A lejárati idő a múltban van.",
        "ipb_expiry_temp": "A láthatatlan felhasználóinév-blokkok lehetnek állandóak.",
        "ipb_hide_invalid": "A felhasználói fiókot nem lehet elrejteni; több mint $1 szerkesztése van.",
        "ipb_already_blocked": "\"$1\" már blokkolva",
        "movenosubpage": "Ez a lap nem rendelkezik allapokkal.",
        "movereason": "Indoklás:",
        "revertmove": "visszaállítás",
-       "delete_and_move_text": "== Törlés szükséges ==\n\nAz átnevezés céljaként megadott „[[:$1]]” szócikk már létezik.  Ha az átnevezést végre akarod hajtani, ezt a lapot törölni kell.  Valóban ezt szeretnéd?",
+       "delete_and_move_text": "Az átnevezés céljaként megadott „[[:$1]]” szócikk már létezik. Ha az átnevezést végre akarod hajtani, ezt a lapot törölni kell. Valóban ezt szeretnéd?",
        "delete_and_move_confirm": "Igen, töröld a lapot",
        "delete_and_move_reason": "átnevezendő lap célneve felszabadítva „[[$1]]” számára",
        "selfmove": "A cikk jelenlegi címe megegyezik azzal, amire át szeretnéd mozgatni. Egy szócikket saját magára mozgatni nem lehet.",
        "move-leave-redirect": "Átirányítás készítése a régi címről az új címre",
        "protectedpagemovewarning": "'''Figyelem:''' Ez a lap le van védve, így csak adminisztrátori jogosultságokkal rendelkező szerkesztők nevezhetik át.\nA legutolsó ide vonatkozó naplóbejegyzés alább látható:",
        "semiprotectedpagemovewarning": "'''Figyelem:''' Ez a lap le van védve, így csak regisztrált felhasználók nevezhetik át.\nA legutolsó ide vonatkozó naplóbejegyzés alább látható:",
-       "move-over-sharedrepo": "== Létező fájlnév ==\n\nA(z) [[:$1]] néven már létezik fájl egy megosztott tárhelyen. Ha ilyen néven töltöd fel, el fogja takarni a közös tárhelyen levőt.",
+       "move-over-sharedrepo": "A(z) [[:$1]] néven már létezik fájl egy megosztott tárhelyen. Ha ilyen néven töltöd fel, el fogja takarni a közös tárhelyen levőt.",
        "file-exists-sharedrepo": "A választott fájlnév már használatban van egy közös tárhelyen.\nKérlek válassz másik nevet.",
        "export": "Lapok exportálása",
        "exporttext": "Egy adott lap vagy lapcsoport szövegét és laptörténetét exportálhatod XML-be. A kapott\nfájlt importálhatod egy másik MediaWiki alapú rendszerbe\na Special:Import lapon keresztül.\n\nLapok exportálásához add meg a címüket a lenti szövegdobozban (minden címet külön sorba), és válaszd ki,\nhogy az összes korábbi változatra és a teljes laptörténetekre szükséged van-e, vagy csak az aktuális\nváltozatok és a legutolsó változtatásokra vonatkozó információk kellenek.\n\nAz utóbbi esetben közvetlen hivatkozást is használhatsz, például a [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]] a \"[[{{MediaWiki:Mainpage}}]]\" nevű lapot exportálja.",
        "export-download": "A fájlban történő mentés felkínálása",
        "export-templates": "Sablonok hozzáadása",
        "export-pagelinks": "Hivatkozott lapok hozzáadása, eddig a szintig:",
+       "export-manual": "Oldalak hozzáadása kézzel:",
        "allmessages": "Rendszerüzenetek",
        "allmessagesname": "Név",
        "allmessagesdefault": "Alapértelmezett szöveg",
        "newimages-legend": "Fájlnév",
        "newimages-label": "Fájlnév (vagy annak részlete):",
        "newimages-showbots": "Botos feltöltések mutatása",
+       "newimages-hidepatrolled": "Ellenőrzött szerkesztések elrejtése",
        "noimages": "Nem tekinthető meg semmi.",
        "ilsubmit": "Keresés",
        "bydate": "dátum szerint",
        "exif-compression-4": "CCITT Group 4 fax kódolás",
        "exif-copyrighted-true": "Szerzői jog által védett",
        "exif-copyrighted-false": "Szerzői jogi állapot nincs beállítva",
+       "exif-photometricinterpretation-1": "Fekete és fehér (Fekete 0)",
        "exif-unknowndate": "Ismeretlen dátum",
        "exif-orientation-1": "Normál",
        "exif-orientation-2": "Vízszintesen tükrözött",
        "watchlisttools-edit": "A figyelőlista megtekintése és szerkesztése",
        "watchlisttools-raw": "A nyers figyelőlista szerkesztése",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|vita]])",
+       "timezone-local": "Helyi",
        "duplicate-defaultsort": "Figyelem: a(z) „$2” rendezőkulcs felülírja a korábbit („$1”).",
        "duplicate-displaytitle": "<strong>Figyelmeztetés:</strong> A lapcímváltoztató (<tt>DISPLAYTITLE</tt>) „$2” felülírja a korábbi „$1”-t.",
        "invalid-indicator-name": "<strong>Hiba:</strong> A lapstátuszjelző <code>name</code> attribútuma nem lehet üres.",
        "pagelang-language": "Nyelv",
        "pagelang-use-default": "Alapértelmezett nyelv használata",
        "pagelang-select-lang": "Nyelv kiválasztása",
-       "right-pagelang": "Oldal nyelvének megváltoztatása",
+       "pagelang-submit": "Küldés",
+       "right-pagelang": "oldal nyelvének megváltoztatása",
        "action-pagelang": "oldal nyelvének módosítása",
        "log-name-pagelang": "Nyelvváltoztatások naplója",
        "log-description-pagelang": "Ebben a naplóban a lap nyelvének változásait követheted nyomon.",
        "mediastatistics": "Feltöltött fájlok statisztikája",
        "mediastatistics-summary": "Az alábbi statisztika a feltöltött fájlok alapján készült, mely a fájlok legfrissebb változatát tartalmazza a régi, vagy törölt fájlok kivételével.",
        "mediastatistics-nbytes": "{{PLURAL:$1|$1 bájt|$1 bájt}} ($2; $3%)",
+       "mediastatistics-bytespertype": "Szakasz fájljainak teljes mérete: {{PLURAL:$1|$1 byte}} ($2; $3%).",
+       "mediastatistics-allbytes": "Az összes fájl teljes mérete: {{PLURAL:$1|$1 byte}} ($2).",
        "mediastatistics-table-mimetype": "MIME-típus",
        "mediastatistics-table-extensions": "Lehetséges kiterjesztések",
        "mediastatistics-table-count": "Fájlok száma",
        "mediastatistics-header-text": "Szöveges",
        "mediastatistics-header-executable": "Futtatható",
        "mediastatistics-header-archive": "Tömörített formátumok",
+       "mediastatistics-header-total": "Összes fájl",
        "json-warn-trailing-comma": "$1 bevezető vessző eltávolítva a JSON-ból",
        "json-error-unknown": "Hiba volt a JSON-ban. Hiba: $1",
        "json-error-depth": "A verem maximális mélység túllépték",
        "mw-widgets-titleinput-description-new-page": "a lap még nem létezik",
        "mw-widgets-titleinput-description-redirect": "átirányítás ide: $1",
        "api-error-blacklisted": "Válasszon egy másik, leíró címet.",
+       "sessionprovider-generic": "$1-munkamenetek",
+       "sessionprovider-mediawiki-session-cookiesessionprovider": "sütialapú munkamenetek",
+       "sessionprovider-nocookies": "A sütik le lehetnek tiltva. Engedélyezd a sütiket, és próbáld meg újra!",
        "randomrootpage": "Véletlen lap a gyökérből"
 }
index 5819bba..e68c47a 100644 (file)
                        "GeoO",
                        "Դավիթ Սարոյան",
                        "Beko",
-                       "Vahe Gharakhanyan"
+                       "Vahe Gharakhanyan",
+                       "Aram1985"
                ]
        },
        "tog-underline": "ընդգծել հղումները՝",
        "tog-hideminor": "Թաքցնել չնչին խմբագրումները վերջին փոփոխությունների ցանկից",
        "tog-hidepatrolled": "Թաքցնել պարեկված խմբագրումները վերջին փոփոխությունների ցանկից",
        "tog-newpageshidepatrolled": "Թաքցնել պարեկված էջերը նոր էջերի ցանկից",
+       "tog-hidecategorization": "Թաքցնել էջերի կատեգորիզացիան",
        "tog-extendwatchlist": "Ընդարձակել հսկացանկը՝ ցույց տալով բոլոր փոփոխությունները, այլ ոչ միայն վերջինները",
        "tog-usenewrc": "Խմբավորել փոփոխությունները վերջին փոփոխություններում և հսկացանկում (պահանջում է JavaScript)",
        "tog-numberheadings": "Ինքնաթվագրել վերնագրերը",
        "tog-watchdefault": "Ավելացնել իմ խմբագրած էջերը և նիշքերը իմ հսկացանկում",
        "tog-watchmoves": "Ավելացնել իմ վերնավանած էջերը և նիշքերը իմ հսկացանկում",
        "tog-watchdeletion": "Ավելացնել իմ ջնջած էջերը և նիշքերը իմ հսկացանկում",
+       "tog-watchrollback": "Իմ հետ շրջած էջերն ավելացնել իմ հսկացանկում",
        "tog-minordefault": "Բոլոր խմբագրումները լռելյայն կերպով նշել որպես չնչին",
        "tog-previewontop": "Ցույց տալ նախադիտումը խմբագրման դաշտից առաջ",
        "tog-previewonfirst": "Ցուցադրել նախադիտումը մինչև առաջին խմբագրումը",
-       "tog-enotifwatchlistpages": "էլ-փոստով տեղեկացնել հսկվող էջերում փոփոխությունների մասին",
+       "tog-enotifwatchlistpages": "էլ-փոստով տեղեկացնել հսկվող էջերում կամ նիշքերում փոփոխությունների մասին",
        "tog-enotifusertalkpages": "էլ-փոստով տեղեկացնել իմ քննարկման էջի փոփոխության մասին",
        "tog-enotifminoredits": "էլ-փոստով տեղեկացնել նաև էջերի չնչին խմբագրումների մասին",
        "tog-enotifrevealaddr": "Ցույց տալ իմ էլ-փոստի հասցեն ծանուցման նամակներում",
        "tog-shownumberswatching": "Ցույց տալ հսկող մասնակիցների թիվը",
        "tog-oldsig": "Ընթացիկ ստորագրությունը՝",
        "tog-fancysig": "Ստորագրությունը վիքիտեքստի տեսքով (առանց ավտոմատ հղման)",
-       "tog-uselivepreview": "Օգտագործել անմիջական նախադիտում, առանց էջը վերբեռնելու",
+       "tog-uselivepreview": "Օգտագործել անմիջական նախադիտում",
        "tog-forceeditsummary": "Նախազգուշացնել խմբագրման ամփոփումը դատարկ թողնելու դեպքում",
        "tog-watchlisthideown": "Թաքցնել իմ խմբագրումները հսկացանկից",
        "tog-watchlisthidebots": "Թաքցնել բոտերի խմբագրումները հսկացանկից",
        "tog-watchlisthideminor": "Թաքցնել չնչին խմբագրումները հսկացանկից",
        "tog-watchlisthideliu": "Թաքցնել մուտք գործած մասնակիցների խմբագրումները հսկացանկից",
+       "tog-watchlistreloadautomatically": "Ֆիլտրի ամեն փոփոխության դեպքում ինքնաշխատ կերպով վերբեռնել հսկացանկը (անհրաժեշտ է JavaScript)",
        "tog-watchlisthideanons": "Թաքցնել անանուն մասնակիցների խմբագրումները հսկացանկից",
        "tog-watchlisthidepatrolled": "Թաքցնել պարեկված խմբագրումները հսկացանկից",
-       "tog-ccmeonemails": "Ուղարկել ինձ իմ կողմից մյուս մասնակիցներին ուղարկված նամակների պատճեններ",
+       "tog-watchlisthidecategorization": "Թաքցնել էջերի կատեգորիզացիան",
+       "tog-ccmeonemails": "Ուղարկել ինձ մյուս մասնակիցներին ուղարկված իմ նամակների պատճենները",
        "tog-diffonly": "Չցուցադրել էջի պարունակությունը տարբերությունների ներքևից",
        "tog-showhiddencats": "Ցուցադրել թաքնված կատեգորիաները",
        "tog-norollbackdiff": "Չցուցադրել տարբերությունները հետ գլորելուց հետո",
        "october-gen": "Հոկտեմբերի",
        "november-gen": "Նոյեմբերի",
        "december-gen": "Դեկտեմբերի",
-       "jan": "Õ°Õ¸Ö\82Õ¶Õ¾",
-       "feb": "փետ",
-       "mar": "Õ´Õ¡Ö\80",
-       "apr": "ապր",
-       "may": "Õ´Õ¡Õµ",
-       "jun": "Õ°Õ¸Ö\82Õ¶",
-       "jul": "Õ°Õ¸Ö\82Õ¬",
-       "aug": "օգո",
-       "sep": "սեպ",
-       "oct": "հոկ",
-       "nov": "նոյ",
-       "dec": "դեկ",
+       "jan": "Õ\80Õ¸Ö\82Õ¶Õ¾.",
+       "feb": "Փետրվ.",
+       "mar": "Õ\84Ö\80Õ¿",
+       "apr": "Ապր.",
+       "may": "Õ\84Õ¡Õµ.",
+       "jun": "Õ\80Õ¸Ö\82Õ¶.",
+       "jul": "Õ\80Õ¸Ö\82Õ¬.",
+       "aug": "օգոստ.",
+       "sep": "սեպտ.",
+       "oct": "հոկտ.",
+       "nov": "նոյ.",
+       "dec": "դեկտ.",
        "january-date": "Հունվարի $1",
        "february-date": "Փետրվարի $1",
        "march-date": "Մարտի $1",
        "category-file-count-limited": "Այս կատեգորիան պարունակում է հետևյալ {{PLURAL:$1|նիշքը|$1 նիշքերը}}։",
        "listingcontinuesabbrev": "շարունակ.",
        "index-category": "Ինդեքսավորված էջեր",
-       "noindex-category": "Ինդեքսավորված էջեր չկան",
+       "noindex-category": "Չինդեքսավորված էջեր",
        "broken-file-category": "Կոտրված ֆայլի հղումով էջեր",
        "about": "Նախագծի մասին",
        "article": "Հոդված",
-       "newwindow": "(Õ¢Õ¡Ö\81Õ¾Õ¥Õ¬Õ¸Ö\82 Õ§ Õ¶Õ¸Ö\80 ÕºÕ¡Õ¿Õ¸Ö\82Õ°Õ¡Õ¶Õ« Õ´Õ¥Õ»)",
-       "cancel": "Բեկանել",
+       "newwindow": "(Õ¢Õ¡Ö\81Õ¾Õ¸Ö\82Õ´ Õ§ Õ¶Õ¸Ö\80 ÕºÕ¡Õ¿Õ¸Ö\82Õ°Õ¡Õ¶Õ¸Ö\82Õ´)",
+       "cancel": "Չեղարկել",
        "moredotdotdot": "Ավելին...",
-       "morenotlisted": "Ô±ÕµÕ½ Ö\81Õ¡Õ¶Õ¯Õ¨ Õ¬Õ«Õ¡Ö\80ÕªÕ¥Ö\84 չէ։",
-       "mypage": "Ô»Õ´ Õ§Õ»Õ¨",
+       "morenotlisted": "Ô±ÕµÕ½ Ö\81Õ¡Õ¶Õ¯Õ¶ Õ¡Õ¾Õ¡Ö\80Õ¿Õ¸Ö\82Õ¶ չէ։",
+       "mypage": "Ô·Õ»",
        "mytalk": "Քննարկում",
        "anontalk": "Քննարկում այս IP-հասցեի համար",
        "navigation": "Շրջել կայքում",
        "and": "&#32;և",
        "qbfind": "Գտնել",
-       "qbbrowse": "Ô¹Õ¥Ö\80Õ©ել",
+       "qbbrowse": "Ô´Õ«Õ¿Õ¡Ö\80Õ¯ել",
        "qbedit": "Խմբագրել",
        "qbpageoptions": "Այս էջը",
        "qbmyoptions": "Իմ էջերը",
        "actions": "Գործողություններ",
        "namespaces": "Անվանատարածքներ",
        "variants": "Տարբերակներ",
-       "navigation-heading": "Õ\86Õ¡Õ¾Õ«Õ£Õ¡Ö\81Õ«Õ¸ն ցանկ",
+       "navigation-heading": "Õ\86Õ¡Õ¾Õ¡Ö\80Õ¯Õ´Õ¡ն ցանկ",
        "errorpagetitle": "Սխալ",
-       "returnto": "Վերադառնալ $1։",
+       "returnto": "Վերադարձ դեպի $1։",
        "tagline": "{{SITENAME}}-ից",
        "help": "Օգնություն",
        "search": "Որոնում",
        "view": "Դիտել",
        "view-foreign": "Նայել $1-ում",
        "edit": "Խմբագրել",
-       "edit-local": "Ô½Õ´Õ¢Õ¡Õ£Ö\80Õ¥Õ¬ Õ¿Õ¥Õ²Õ¡Õ¯Õ¡ն նկարագրությունը",
+       "edit-local": "Ô½Õ´Õ¢Õ¡Õ£Ö\80Õ¥Õ¬ Õ¿Õ¥Õ²Õ¡ÕµÕ«ն նկարագրությունը",
        "create": "Ստեղծել",
        "create-local": "Ավելացնել տեղային նկարագիր",
        "editthispage": "Խմբագրել այս էջը",
        "protect": "Պաշտպանել",
        "protect_change": "փոխել",
        "protectthispage": "Պաշտպանել այս էջը",
-       "unprotect": "Õ\93Õ¸Õ­Õ¥Õ¬ ÕºÕ¡Õ·Õ¿ÕºÕ¡Õ¶Õ¸Ö\82Õ´ը",
-       "unprotectthispage": "Õ\93Õ¸Õ­Õ¥Õ¬ Õ¡ÕµÕ½ Õ§Õ»Õ« ÕºÕ¡Õ·Õ¿ÕºÕ¡Õ¶Õ¸Ö\82Õ´ը",
+       "unprotect": "Õ\93Õ¸Õ­Õ¥Õ¬ ÕºÕ¡Õ·Õ¿ÕºÕ¡Õ¶Õ¸Ö\82Õ©ÕµÕ¸Ö\82Õ¶ը",
+       "unprotectthispage": "Õ\93Õ¸Õ­Õ¥Õ¬ Õ¡ÕµÕ½ Õ§Õ»Õ« ÕºÕ¡Õ·Õ¿ÕºÕ¡Õ¶Õ¸Ö\82Õ©ÕµÕ¸Ö\82Õ¶ը",
        "newpage": "Նոր էջ",
        "talkpage": "Քննարկել այս էջը",
        "talkpagelinktext": "Քննարկում",
-       "specialpage": "Õ\8dÕºÕ¡Õ½Õ¡Ö\80Õ¯Õ¸Õ² էջ",
+       "specialpage": "Õ\80Õ¡Õ¿Õ¸Ö\82Õ¯ էջ",
        "personaltools": "Անձնական գործիքներ",
-       "articlepage": "Ô´Õ«Õ¿Õ¥Õ¬ Õ°Õ¸Õ¤Õ¾Õ¡Õ®ը",
+       "articlepage": "Ô´Õ«Õ¿Õ¥Õ¬ Õ§Õ»Õ« ÕºÕ¡Ö\80Õ¸Ö\82Õ¶Õ¡Õ¯Õ¸Ö\82Õ©ÕµÕ¸Ö\82Õ¶ը",
        "talk": "Քննարկում",
        "views": "Դիտումները",
        "toolbox": "Գործիքներ",
        "userpage": "Դիտել մասնակցի էջը",
        "projectpage": "Դիտել նախագծի էջը",
        "imagepage": "Դիտել նիշքի էջը",
-       "mediawikipage": "Ô´Õ«Õ¿Õ¥Õ¬ Õ¸Ö\82Õ²Õ¥Ö\80Õ±Õ« էջը",
+       "mediawikipage": "Ô´Õ«Õ¿Õ¥Õ¬ Õ°Õ¡Õ²Õ¸Ö\80Õ¤Õ¡Õ£Ö\80Õ¸Ö\82Õ©ÕµÕ¡Õ¶ էջը",
        "templatepage": "Դիտել կաղապարի էջը",
        "viewhelppage": "Դիտել օգնության էջը",
        "categorypage": "Դիտել կատեգորիայի էջը",
        "otherlanguages": "Այլ լեզուներով",
        "redirectedfrom": "(Վերահղված է $1ից)",
        "redirectpagesub": "Վերահղման էջ",
-       "redirectto": "Վերահղել դեպի՝",
-       "lastmodifiedat": "Այս էջը վերջին անգամ փոփոխվել է ժամը $2-ին, $1 թվին։",
+       "redirectto": "Վերահղել դեպի",
+       "lastmodifiedat": "Այս էջը վերջին անգամ փոփոխվել է $1 թվականի ժամը $2-ին:",
        "viewcount": "Այս էջին դիմել են {{PLURAL:$1|մեկ անգամ|$1 անգամ}}։",
        "protectedpage": "Պաշտպանված էջ",
-       "jumpto": "Անցնել՝",
+       "jumpto": "Անցնել դեպի",
        "jumptonavigation": "նավարկություն",
        "jumptosearch": "որոնում",
-       "view-pool-error": "Õ\86Õ¥Ö\80Õ¥Ö\81Õ¥Ö\84Õ\9d Õ½Õ¥Ö\80Õ¾Õ¥Ö\80Õ¶Õ¥Ö\80Õ¨ Õ£Õ¥Ö\80Õ¢Õ¥Õ¼Õ¶Õ¾Õ¡Õ® Õ¥Õ¶ Õ¡ÕµÕ½ ÕºÕ¡Õ°Õ«Õ¶Ö\89\nÕ\89Õ¡Ö\83Õ«Ö\81 Õ·Õ¡Õ¿ Ö\85Õ£Õ¿Õ¾Õ¸Õ²Õ¶Õ¥Ö\80 Ö\83Õ¸Ö\80Õ±Õ¸Ö\82Õ´ Õ¥Õ¶ Õ¤Õ«Õ¿Õ¥Õ¬ Õ¡ÕµÕ½ Õ§Õ»Õ¨Ö\89\nÔ½Õ¶Õ¤Ö\80Õ¸Ö\82Õ´ Õ¥Õ¶Ö\84 Õ½ÕºÕ¡Õ½Õ¥Õ¬ Õ¸Ö\80Õ¸Õ· ÕªÕ¡Õ´Õ¡Õ¶Õ¡Õ¯ Õ§Õ»Õ¨ Õ¤Õ«Õ¿Õ¥Õ¬Õ¸Ö\82 Õ¯Ö\80Õ¯Õ«Õ¶ Õ°Õ¡ÕµÖ\81Õ¸Ö\82Õ´ Õ¡Õ¶Õ¥Õ¬Õ¸Ö\82Ö\81 Õ¡Õ¼Õ¡Õ»։\n\n$1",
-       "generic-pool-error": "Ներեցեք՝ սերվերները գերբեռնված են այս պահին։\nՉափից շատ օգտվողներ փորձում են դիտել այս էջը։\nԽնդրում ենք սպասել որոշ ժամանակ էջը դիտելու կրկին հայցում անելուց առաջ։",
+       "view-pool-error": "Õ\86Õ¥Ö\80Õ¥Ö\81Õ¥Ö\84Õ\9d Õ½Õ¥Ö\80Õ¾Õ¥Ö\80Õ¶Õ¥Ö\80Õ¨ Õ£Õ¥Ö\80Õ¢Õ¥Õ¼Õ¶Õ¾Õ¡Õ® Õ¥Õ¶ Õ¡ÕµÕ½ ÕºÕ¡Õ°Õ«Õ¶Ö\89\nÕ\87Õ¡Õ¿ Ö\85Õ£Õ¿Õ¾Õ¸Õ²Õ¶Õ¥Ö\80 Ö\83Õ¸Ö\80Õ±Õ¸Ö\82Õ´ Õ¥Õ¶ Õ¤Õ«Õ¿Õ¥Õ¬ Õ¡ÕµÕ½ Õ§Õ»Õ¨Ö\89\nÔ½Õ¶Õ¤Ö\80Õ¸Ö\82Õ´ Õ¥Õ¶Ö\84 Õ½ÕºÕ¡Õ½Õ¥Õ¬ Õ¸Ö\80Õ¸Õ· ÕªÕ¡Õ´Õ¡Õ¶Õ¡Õ¯ Õ§Õ» Õ¯Ö\80Õ¯Õ«Õ¶ Õ´Õ¸Ö\82Õ¿Ö\84 Õ£Õ¸Ö\80Õ®Õ¥Õ¬Õ¸Ö\82 Õ°Õ¡Õ´Õ¡Ö\80։\n\n$1",
+       "generic-pool-error": "Ներեցեք, սերվերները գերբեռնված են այս պահին։\nՇատ օգտվողներ փորձում են դիտել այս էջը։\nԽնդրում ենք սպասել որոշ ժամանակ էջը կրկին դիտելու համար։",
        "pool-errorunknown": "Անհայտ սխալ",
        "aboutsite": "{{grammar:genitive|{{SITENAME}}}} մասին",
        "aboutpage": "Project:Էությունը",
        "preview": "Նախադիտում",
        "showpreview": "Նախադիտել",
        "showdiff": "Կատարված փոփոխությունները",
-       "anoneditwarning": "<strong>Ուշադրություն,</strong> Դուք չեք մտել համակարգ։ Ցանկացած խմբագրման դեպքում Ձեր IP հասցեն կդառնա բոլորին տեսանելի։ Եթե դուք <strong>[$1 մուտք գործեք]</strong> կամ <strong>[$2 ստեղծեք մասնակցային հաշիվ]</strong>, Ձեր կատարած խմբագրումները կկավեն Ձեր մասնակցային անվան հետ և Դուք կունենաք այլ առավելություններ։",
+       "anoneditwarning": "<strong>Ուշադրություն,</strong> Դուք չեք մտել համակարգ։ Ցանկացած խմբագրման դեպքում Ձեր IP հասցեն կդառնա բոլորին տեսանելի։ Եթե Դուք <strong>[$1 մուտք գործեք]</strong> կամ <strong>[$2 ստեղծեք մասնակցային հաշիվ]</strong>, Ձեր կատարած խմբագրումները կկապվեն Ձեր մասնակցային անվան հետ, ինչպես նաև կունենաք այլ առավելություններ։",
        "anonpreviewwarning": "<em>Դուք չեք մտել համակարգ։\nՀիշելով Ձեր կատարած խմբագրումը, այն կպահանվի Ձեր IP հասցեի հետ միասին այս էջի խմբագրումների պատմության մեջ։</em>",
        "missingsummary": "'''Հիշեցում.''' Դուք չեք տվել խմբագրման ամփոփում։ «Հիշել» կոճակի կրկնակի մատնահարման դեպքում փոփոխությունները կհիշվեն առանց ամփոփման։",
        "missingcommenttext": "Խնդրում ենք մեկնաբանություն ավելացնել ստորև։",
        "watchthisupload": "Հսկել այս նիշքը",
        "filewasdeleted": "Այս անվանմամբ նիշք նախկինում բեռնվել է և հետագայում ջնջվել։ Այն կրկին բեռնելուց առաջ խնդրում ենք ստուգել $1։",
        "filename-bad-prefix": "Բեռնվող նիշքի անվանումը սկսվում է '''<tt>«$1»</tt>''' արտահայտությամբ, որը ոչ-նկարագրական է և սովորաբար տրվում է թվային լուսանկարչական ապարատների կողմից։ Խնդրում ենք ընտրել ավելի նկարագրական անվանում ձեր նիշքի համար։",
-       "upload-success-subj": "Բեռնումը կատարված է",
-       "upload-failure-subj": "Ներբեռնման սխալ",
-       "upload-warning-subj": "Ներբեռնման զգուշացում",
        "upload-proto-error": "Սխալ պրոտոկոլ",
        "upload-proto-error-text": "Հեռավոր բեռնումը պահանջում է URL-հասցե, որը սկսվում է <code>http://</code> կամ <code>ftp://</code> նախածանցով։",
        "upload-file-error": "Ներքին սխալ",
        "wlheader-showupdated": "Էջերը, որոնք փոփոխվել են ձեր դրանց վերջին այցից հետո բերված են '''թավատառ'''։",
        "wlnote": "Ստորև բերված {{PLURAL:$1|է վերջին փոփոխությունը|են վերջին '''$1''' փոփոխությունները}} վերջին <strong>$2</strong> ժամվա ընթացքում։",
        "wlshowlast": "Ցուցադրել վերջին $1 ժամերը $2 օրերը",
-       "watchlistall2": "բոլոր",
        "watchlist-submit": "Ցույց տալ",
+       "wlshowhideliu": "գրանցված մասնակիցներ",
+       "wlshowhidemine": "իմ խմբագրումները",
        "watchlist-options": "Հսկացանկի նախընտրություններ",
        "watching": "Հսկվում է...",
        "unwatching": "Հանվում է հսկումից...",
index 94b59e1..54471a6 100644 (file)
        "querypage-disabled": "Iste pagina special es disactivate pro evitar de supercargar le systema.",
        "apihelp": "Adjuta con le API",
        "apihelp-no-such-module": "Modulo \"$1\" non trovate.",
+       "apisandbox": "Cassa de sablo pro API",
+       "apisandbox-api-disabled": "Le API ha essite disactivate in iste sito.",
+       "apisandbox-intro": "Usa iste pagina pro experimentar con le '''API de servicio web de MediaWiki'''.\nConsulta [//www.mediawiki.org/wiki/API:Main_page le documentation del API] pro ulterior detalios concernente le uso del API. Per exemplo: [//www.mediawiki.org/wiki/API#A_simple_example obtener le contento de un Pagina principal]. Selige un action pro vider altere exemplos.",
+       "apisandbox-submit": "Facer requesta",
+       "apisandbox-reset": "Rader",
+       "apisandbox-examples": "Exemplo",
+       "apisandbox-results": "Resultato",
+       "apisandbox-request-url-label": "URL de requesta:",
+       "apisandbox-request-time": "Duration del requesta: $1",
        "booksources": "Fontes de libros",
        "booksources-search-legend": "Cercar fontes de libros",
        "booksources-search": "Cercar",
index 0aefcf4..c2d5f7d 100644 (file)
@@ -51,6 +51,7 @@
        "tog-hideminor": "Sembunyikan suntingan kecil di perubahan terbaru",
        "tog-hidepatrolled": "Sembunyikan suntingan terpatroli di perubahan terbaru",
        "tog-newpageshidepatrolled": "Sembunyikan halaman terpatroli dari daftar halaman baru",
+       "tog-hidecategorization": "Sembunyikan pengategorian halaman",
        "tog-extendwatchlist": "Kembangkan daftar pantauan untuk menunjukkan semua perubahan, tidak hanya yang terbaru",
        "tog-usenewrc": "Kelompokkan suntingan di tampilan perubahan terbaru dan daftar pantauan berdasarkan halaman",
        "tog-numberheadings": "Beri nomor judul secara otomatis",
        "tog-watchlisthidebots": "Sembunyikan suntingan bot di daftar pantauan",
        "tog-watchlisthideminor": "Sembunyikan suntingan kecil di daftar pantauan",
        "tog-watchlisthideliu": "Sembunyikan suntingan pengguna masuk log di daftar pantauan",
+       "tog-watchlistreloadautomatically": "Muat ulang daftar pantauan secara otomatis ketika sebuah penyaring berubah (JavaScript diperlukan)",
        "tog-watchlisthideanons": "Sembunyikan suntingan pengguna anonim di daftar pantauan",
        "tog-watchlisthidepatrolled": "Sembunyikan suntingan terpatroli di daftar pantauan",
+       "tog-watchlisthidecategorization": "Sembunyikan pengategorian halaman",
        "tog-ccmeonemails": "Kirimkan saya salinan surel yang saya kirimkan ke orang lain",
        "tog-diffonly": "Jangan tampilkan isi halaman di bawah perbedaan suntingan",
        "tog-showhiddencats": "Tampilkan kategori tersembunyi",
        "october-date": "$1 Oktober",
        "november-date": "$1 November",
        "december-date": "$1 Desember",
+       "period-am": "AM",
+       "period-pm": "PM",
        "pagecategories": "{{PLURAL:$1|Kategori}}",
        "category_header": "Halaman dalam kategori \"$1\"",
        "subcategories": "Subkategori",
        "morenotlisted": "Daftar ini belum lengkap.",
        "mypage": "Halaman",
        "mytalk": "Pembicaraan",
-       "anontalk": "Pembicaraan IP ini",
+       "anontalk": "Pembicaraan",
        "navigation": "Navigasi",
        "and": "&#32;dan",
        "qbfind": "Pencarian",
        "laggedslavemode": "Peringatan: Halaman mungkin tidak berisi perubahan terbaru.",
        "readonly": "Basis data dikunci",
        "enterlockreason": "Masukkan alasan penguncian, termasuk perkiraan kapan kunci akan dibuka",
-       "readonlytext": "Basis data sedang dikunci terhadap masukan baru. Pengurus yang melakukan penguncian memberikan penjelasan sebagai berikut: <p>$1",
+       "readonlytext": "Basis data sedang dikunci terhadap masukan dan perubahan baru, mungkin dalam pembenahan basis data, setelah selesai keadaan akan normal sedia kala. \n\nPengurus yang melakukan penguncian memberikan penjelasan sebagai berikut: $1",
        "missing-article": "Basis data tidak dapat menemukan teks dari halaman yang seharusnya ada, yaitu \"$1\" $2.\n\nHal ini biasanya disebabkan oleh pranala usang ke revisi terdahulu halaman yang telah dihapuskan.\n\nJika bukan ini penyebabnya, Anda mungkin telah menemukan sebuah bug dalam perangkat lunak.\nSilakan laporkan hal ini kepada salah seorang [[Special:ListUsers/sysop|Pengurus]], dengan menyebutkan alamat URL yang dituju.",
        "missingarticle-rev": "(revisi#: $1)",
        "missingarticle-diff": "(Beda: $1, $2)",
        "readonly_lag": "Basis data telah dikunci otomatis selagi basis data sekunder melakukan sinkronisasi dengan basis data utama",
+       "nonwrite-api-promise-error": "Kepala HTTP 'Promise-Non-Write-API-Action' telah dikirim tetapi permintaan dibuat kepada modul menulis API.",
        "internalerror": "Kesalahan internal",
        "internalerror_info": "Kesalahan internal: $1",
        "internalerror-fatal-exception": "Kekecualian fatal mengetik \"$1\"",
        "badtitle": "Judul tidak sah",
        "badtitletext": "Judul halaman yang diminta tidak sah, kosong, atau judul antarbahasa atau antarwiki yang salah sambung.",
        "title-invalid-empty": "Judul halaman yang diminta kosong atau berisi hanya nama sebuah ruang nama.",
+       "title-invalid-utf8": "Judul halaman yang diminta mengandung rangkaian UTF-8 yang tidak sah.",
+       "title-invalid-interwiki": "Judul mengandung pranala antarwiki yang tidak bisa digunakan dalam judul.",
+       "title-invalid-talk-namespace": "Judul situs yang diminta merujuk kepada halaman pembicaraan yang tidak dapat tersedia.",
+       "title-invalid-characters": "Judul halaman yang diminta mengandung karakter tak sah: \"$1\".",
+       "title-invalid-relative": "Judul mengandung alamat relatif. Judul halaman relatif (./, ../) tidaklah sah, karena dapat mengalami kegagalan ketika ditangani oleh peramban pengguna.",
+       "title-invalid-magic-tilde": "Judul halaman mengandung rangkaian tilda yang tidak sah (<nowiki>~~~</nowiki>).",
+       "title-invalid-too-long": "Judul halaman yang diminta terlalu panjang. Ia harus dikodekan dengan UTF-8 dan tidak melebihi $1 bita.",
+       "title-invalid-leading-colon": "Judul halaman yang diminta dimulai dengan tanda titik dua yang tidak sah.",
        "perfcached": "Data berikut ini diambil dari singgahan dan mungkin bukan data mutakhir. {{PLURAL:$1|Hasil}} maksimal ada di singgahan.",
        "perfcachedts": "Data berikut ini diambil dari singgahan dan terakhir diperbarui pada $1. {{PLURAL:$4|Hasil}} maksimal ada di singgahan.",
        "querypage-no-updates": "Pemutakhiran dari halaman ini sedang dimatikan. Data yang ada di sini saat ini tidak akan dimuat ulang.",
        "viewsource": "Lihat sumber",
        "viewsource-title": "Lihat sumber untuk $1",
        "actionthrottled": "Tindakan dibatasi",
-       "actionthrottledtext": "Anda dibatasi untuk melakukan tindakan ini terlalu banyak dalam waktu pendek. Silakan mencoba lagi setelah beberapa menit.",
+       "actionthrottledtext": "Anda dibatasi untuk melakukan tindakan ini terlalu banyak dalam waktu pendek, dan Anda telah melebihi batas yang diberikan. Silakan mencoba lagi setelah beberapa menit.",
        "protectedpagetext": "Halaman ini telah dikunci untuk menghindari penyuntingan atau tindakan lain.",
-       "viewsourcetext": "Anda dapat melihat atau menyalin sumber halaman ini:",
-       "viewyourtext": "Anda dapat melihat atau menyalin sumber dari '''suntingan Anda''' ke halaman ini:",
+       "viewsourcetext": "Anda dapat melihat atau menyalin sumber halaman ini.",
+       "viewyourtext": "Anda dapat melihat dan menyalin sumber dari '''suntingan Anda''' ke halaman ini.",
        "protectedinterface": "Halaman ini memuat teks antarmuka untuk perangkat lunak pada wiki ini, dan dilindungi terhadap penyalahgunaan. Untuk menambah atau mengubah terjemahan pada semua wiki, harap gunakan [//translatewiki.net/ translatewiki.net], proyek pelokalan MediaWiki.",
        "editinginterface": "<strong>Peringatan:</strong> Anda menyunting suatu halaman yang digunakan untuk menyediakan teks antarmuka bagi perangkat lunak.\nPerubahan pada halaman ini akan memengaruhi tampilan pada antarmuka pengguna untuk pengguna lain pada wiki ini.",
        "translateinterface": "Untuk menambah atau mengubah terjemahan semua wiki, mohon gunakan [//translatewiki.net/ translatewiki.net], proyek pelokalan MediaWiki.",
        "mypreferencesprotected": "Anda tidak memiliki izin untuk menyunting preferensi Anda.",
        "ns-specialprotected": "Halaman pada ruang nama {{ns:special}} tidak dapat disunting.",
        "titleprotected": "Judul ini dilindungi dari pembuatan oleh [[User:$1|$1]].\nAlasan yang diberikan adalah ''$2''.",
-       "filereadonlyerror": "Tidak dapat memodifikasi file \" $1 \" karena file repositori \" $2 \" adalah pada mode baca-saja.\n\nAdministrator yang terkunci menawarkan penjelasan ini: \" $3 \".",
+       "filereadonlyerror": "Tidak dapat memodifikasi berkas \"$1\" karena file repositori \"$2\" adalah pada mode baca-saja.\n\nPengurus yang menguncinya memberikan alasan: \"$3\".",
        "invalidtitle-knownnamespace": "Judul yang tidak sah dengan ruangnama \"$2\" dan teks \"$3\"",
        "invalidtitle-unknownnamespace": "Judul yang tidak sah dengan nomor ruang nama tidak diketahui $1 dan teks \"$2\"",
        "exception-nologin": "Belum masuk log",
        "virus-scanfailed": "Pemindaian gagal (kode $1)",
        "virus-unknownscanner": "Antivirus tidak dikenal:",
        "logouttext": "'''Anda telah keluar log dari sistem.'''\n\nIngatlah bahwa beberapa halaman mungkin masih menampilkan anda seperti masih masuk log, sampai Anda membersihkan singgahan penjelajah web Anda.",
+       "cannotlogoutnow-title": "Tidak dapat keluar log saat ini",
+       "cannotlogoutnow-text": "Keluar log tidak memungkinkan ketika menggunakan $1.",
        "welcomeuser": "Selamat datang,  $1 !",
        "welcomecreation-msg": "Akun Anda telah dibuat. Jangan lupa mengatur konfigurasi [[Special:Preferences|preferensi {{SITENAME}}]] Anda.",
        "yourname": "Nama pengguna:",
        "remembermypassword": "Ingat kata sandi saya di komputer ini (selama $1 {{PLURAL:$1|hari|hari}})",
        "userlogin-remembermypassword": "Biarkan saya tetap masuk",
        "userlogin-signwithsecure": "Gunakan server aman",
+       "cannotloginnow-title": "Tidak dapat masuk log saat ini",
+       "cannotloginnow-text": "Masuk log tidak memungkinkan ketika menggunakan $1.",
        "yourdomainname": "Domain Anda:",
        "password-change-forbidden": "Anda tidak dapat mengubah kata sandi pada wiki ini.",
        "externaldberror": "Telah terjadi kesalahan otentikasi basis data eksternal atau Anda tidak diizinkan melakukan kemaskini terhadap akun eksternal Anda.",
        "createacct-benefit-body2": "{{PLURAL:$1|halaman}}",
        "createacct-benefit-body3": "{{PLURAL:$1|kontributor}} terakhir",
        "badretype": "Kata sandi yang Anda masukkan salah.",
+       "usernameinprogress": "Pembuatan akun untuk nama pengguna ini sedang dijalankan. Silahkan tunggu.",
        "userexists": "Nama pengguna yang dimasukkan telah digunakan.\nSilakan tentukan nama yang lain.",
        "loginerror": "Kesalahan masuk log",
        "createacct-error": "Pembuatan akun gagal",
        "wrongpasswordempty": "Anda tidak memasukkan kata sandi. Silakan coba lagi.",
        "passwordtooshort": "Kata sandi paling tidak harus terdiri dari {{PLURAL:$1|1 karakter|$1 karakter}}.",
        "passwordtoolong": "Passwords tidak boleh lebih dari {{PLURAL:$1|1 karakter|$1 karakter}}.",
+       "passwordtoopopular": "Kata sandi yang umum tidak dapat digunakan. Silakan pilih kata sandi yang berbeda.",
        "password-name-match": "Kata sandi Anda harus berbeda dari nama pengguna Anda.",
        "password-login-forbidden": "Penggunaan nama pengguna dan sandi ini telah dilarang.",
        "mailmypassword": "Setel ulang kata sandi",
        "noemail": "Tidak ada alamat surel yang tercatat untuk pengguna \"$1\".",
        "noemailcreate": "Anda perlu menyediakan alamat surel yang sah",
        "passwordsent": "Kata sandi baru telah dikirimkan ke alamat surel yang didaftarkan untuk \"$1\".\nSilakan masuk log kembali setelah menerima surel tersebut.",
-       "blocked-mailpassword": "Alamat IP Anda diblokir dari penyuntingan dan karenanya tidak diizinkan menggunakan fungsi pengingat kata sandi untuk mencegah penyalahgunaan.",
+       "blocked-mailpassword": "Alamat IP Anda diblokir dari penyuntingan sehingga tidak diizinkan menggunakan fungsi pengingat kata sandi untuk mencegah penyalahgunaan.",
        "eauthentsent": "Sebuah surel untuk konfirmasi telah dikirim ke alamat surel. Sebelum surel lainnya dikirim ke akun tersebut, Anda harus mengikuti instruksi di dalam surel tersebut, untuk melakukan konfirmasi bahwa alamat tersebut adalah benar kepunyaan Anda.",
        "throttled-mailpassword": "Suatu pengingat kata sandi telah dikirimkan dalam {{PLURAL:$1|$1 jam}} terakhir.\nUntuk menghindari penyalahgunaan, hanya satu kata sandi yang akan dikirimkan setiap {{PLURAL:$1|$1 jam}}.",
        "mailerror": "Kesalahan dalam mengirimkan surel: $1",
        "resetpass_submit": "Atur kata sandi dan masuk log",
        "changepassword-success": "Kata sandi Anda telah berhasil diubah!",
        "changepassword-throttled": "Anda terlalu sering mencoba masuk log.\nMohon tunggu $1 sebelum mencoba lagi.",
+       "botpasswords": "Kata sandi bot",
+       "botpasswords-disabled": "Kata sandi bot dinonaktifkan.",
+       "botpasswords-no-central-id": "Untuk menggunakan kata sandi bot, Anda harus masuk log ke akun yang telah tersentralisasi.",
+       "botpasswords-existing": "Kata sandi bot tersedia",
+       "botpasswords-createnew": "Buat kata sandi bot baru",
+       "botpasswords-editexisting": "Ubah kata sandi bot yang sudah ada",
+       "botpasswords-label-appid": "Nama bot:",
+       "botpasswords-label-create": "Buat",
+       "botpasswords-label-update": "Perbarui",
+       "botpasswords-label-cancel": "Batalkan",
+       "botpasswords-label-delete": "Hapus",
+       "botpasswords-label-resetpassword": "Setel ulang kata sandi",
+       "botpasswords-label-grants": "Akses yang dapat diberikan:",
+       "botpasswords-label-restrictions": "Batasan penggunaan:",
+       "botpasswords-label-grants-column": "Izin diberikan",
+       "botpasswords-bad-appid": "Nama bot \"$1\" tidak valid.",
+       "botpasswords-insert-failed": "Gagal menambah nama bot \"$1\". Apakah sudah ditambahkan sebelum ini?",
+       "botpasswords-update-failed": "Gagal memperbarui nama bot \"$1\". Apakah sebelumnya sudah pernah dihapus?",
+       "botpasswords-created-title": "Kata sandi bot dibuat",
+       "botpasswords-created-body": "Kata sandi bot \"$1\" sukses dibuat.",
+       "botpasswords-updated-title": "Kata sandi bot diperbarui",
+       "botpasswords-updated-body": "Kata sandi bot \"$1\" sukses diperbarui.",
+       "botpasswords-deleted-title": "Kata sandi bot dihapus",
+       "botpasswords-deleted-body": "Kata sandi bot \"$1\" telah dihapus.",
+       "botpasswords-newpassword": "Kata sandi baru untuk masuk log dengan '''$1''' adalah '''$2'''. ''Mohon simpan untuk referensi di kemudian hari.''",
+       "botpasswords-no-provider": "BotPasswordsSessionProvider tidak tersedia.",
+       "botpasswords-restriction-failed": "Batasan kata sandi menghalangi masuk log ini.",
+       "botpasswords-invalid-name": "Nama pengguna yang diberikan tidak mengandung pemisah kata sandi bot (\"$1\").",
+       "botpasswords-not-exist": "Pengguna \"$1\" tidak memiliki kata sandi bot bernama \"$2\".",
        "resetpass_forbidden": "Kata sandi tidak dapat diubah",
        "resetpass-no-info": "Anda harus masuk log untuk mengakses halaman ini secara langsung.",
        "resetpass-submit-loggedin": "Ganti kata sandi",
        "passwordreset-emailtext-ip": "Seseorang (mungkin Anda, dari alamat IP $1) meminta pengingat\ndetail akun untuk {{SITENAME}} ($4). {{PLURAL:$3|Akun|Akun-akun}} berikut\nterkait dengan alamat surel ini:\n\n$2\n\n{{PLURAL:$3|Sandi sementara}} berikut akan kedaluwarsa dalam {{PLURAL:$5|$5 hari}}.\nAnda harus masuk dan memilih sandi baru sekarang. Jika orang lain membuat\npermintaan ini atau jika Anda ingat sandi asli dan tidak lagi\ningin mengubahnya, Anda dapat mengabaikan pesan ini dan terus menggunakan sandi lama.",
        "passwordreset-emailtext-user": "Seseorang (mungkin Anda, dari alamat IP $1) meminta pengingat detail akun untuk {{SITENAME}} ($4).\n{{PLURAL:$3|Akun|Akun-akun}} berikut terkait dengan alamat surel ini:\n\n$2\n\n{{PLURAL:$3|Sandi sementara}} berikut akan kedaluwarsa dalam {{PLURAL:$5|$5 hari}}.\nAnda harus masuk dan memilih sandi baru sekarang. Jika orang lain membuat\npermintaan ini atau jika Anda ingat sandi asli dan tidak lagi\ningin mengubahnya, Anda dapat mengabaikan pesan ini dan terus menggunakan sandi lama.",
        "passwordreset-emailelement": "Nama pengguna: \n$1\n\nSandi sementara: \n$2",
-       "passwordreset-emailsentemail": "Surel setel ulang kata sandi telah dikirimkan.",
+       "passwordreset-emailsentemail": "Jika alamat surel ini berkaitan dengan akun Anda, maka surel untuk menyetel ulang kata sandi akan dikirim.",
+       "passwordreset-emailsentusername": "Jika ada alamat surel yang berkaitan dengan nama pengguna ini, maka surel untuk menyetel ulang kata sandi akan dikirim.",
        "passwordreset-emailsent-capture": "Surel setel ulang kata sandi telah dikirim, yang ditampilkan di bawah.",
        "passwordreset-emailerror-capture": "Surel setel ulang kata sandi telah dibuat, yang ditampilkan di bawah, namun pengiriman pada {{GENDER:$2|pengguna}} gagal: $1",
-       "changeemail": "Ubah alamat surel",
-       "changeemail-header": "Ubah alamat surel akun",
+       "changeemail": "Ubah atau hapus alamat surel",
+       "changeemail-header": "Lengkapi formulir ini untuk mengubah alamat surel Anda. Jika Anda ingin menghapus seluruh alamat surel yang berkaitan dengan akun Anda, kosongkan alamat surel ketika mengirim formulir.",
+       "changeemail-passwordrequired": "Anda diharuskan memasukkan kata sandi untuk mengonfirmasikan perubahan ini.",
        "changeemail-no-info": "Anda harus masuk log untuk mengakses halaman ini secara langsung.",
        "changeemail-oldemail": "Alamat surel saat ini:",
        "changeemail-newemail": "Alamat surel baru:",
+       "changeemail-newemail-help": "Kolom ini harus dikosongkan jika Anda ingin menghapus alamat surel. Anda nanti tidak dapat menyetel ulang kata sandi yang terlupa dan tidak akan menerima surel dari wiki ini jika alamat surel dihapus.",
        "changeemail-none": "(tidak ada)",
        "changeemail-password": "Sandi {{SITENAME}} Anda:",
        "changeemail-submit": "Ubah surel",
        "changeemail-throttled": "Anda sudah terlalu banyak mencoba masuk log.\nSilakan menunggu $1 sebelum mencoba lagi.",
+       "changeemail-nochange": "Masukkan alamat surel yang berbeda.",
        "resettokens": "Ubah token",
        "resettokens-text": "Anda dapat me-reset Token yang memungkinkan akses ke data pribadi tertentu yang terkait dengan akun Anda di sini.\n\nAnda harus melakukannya jika Anda secara tidak sengaja berbagi dengan seseorang atau jika akun Anda telah disusupi.",
        "resettokens-no-tokens": "Tidak ada token untuk di-reset.",
        "sig_tip": "Tanda tangan Anda dengan tanda waktu",
        "hr_tip": "Garis horisontal",
        "summary": "Ringkasan:",
-       "subject": "Subjek/judul:",
+       "subject": "Subjek:",
        "minoredit": "Ini adalah suntingan kecil.",
        "watchthis": "Pantau halaman ini",
        "savearticle": "Simpan halaman",
        "missingsummary": "'''Peringatan:''' Anda tidak memasukkan ringkasan penyuntingan. Jika Anda kembali menekan tombol Simpan, suntingan Anda akan disimpan tanpa ringkasan penyuntingan.",
        "selfredirect": "<strong>Peringatan:</strong> Anda mengalihkan halaman ini kembali ke halaman semula.\nAnda bisa jadi telah memberikan tujuan pengalihan yang salah, atau telah menyunting halaman yang salah.\nJika Anda mengeklik \"{{int:savearticle}}\" sekali lagi, halaman pengalihan akan dibuat.",
        "missingcommenttext": "Harap masukkan komentar di bawah ini.",
-       "missingcommentheader": "''Peringatan:''' Anda belum memberikan subjek atau judul untuk komentar Anda. Jika Anda kembali menekan \"{{int:savearticle}}\", suntingan Anda akan disimpan tanpa komentar tersebut.",
+       "missingcommentheader": "'''Peringatan:''' Anda belum memberikan subjek atau judul untuk komentar Anda. Jika Anda kembali menekan \"{{int:savearticle}}\", suntingan Anda akan disimpan tanpa komentar tersebut.",
        "summary-preview": "Pratayang ringkasan:",
-       "subject-preview": "Pratayang subyek/tajuk:",
+       "subject-preview": "Pratayang subjek:",
        "previewerrortext": "Kesalahan terjadi saat mencoba memperlihatkan pratayang perubahan Anda.",
        "blockedtitle": "Pengguna diblokir",
        "blockedtext": "'''Nama pengguna atau alamat IP Anda telah diblokir.'''\n\nBlokir dilakukan oleh $1.\nAlasan yang diberikan adalah ''$2''.\n\n* Diblokir sejak: $8\n* Blokir kedaluwarsa pada: $6\n* Sasaran pemblokiran: $7\n\nAnda dapat menghubungi $1 atau [[{{MediaWiki:Grouppage-sysop}}|pengurus lainnya]] untuk membicarakan hal ini.\n\nAnda tidak dapat menggunakan fitur 'Kirim surel ke pengguna ini' kecuali Anda telah memasukkan alamat surel yang sah di [[Special:Preferences|preferensi akun]] dan Anda tidak diblokir untuk menggunakannya.\n\nAlamat IP Anda adalah $3, dan ID pemblokiran adalah $5.\nTolong sertakan salah satu atau kedua informasi ini pada setiap pertanyaan yang Anda buat.",
        "missing-revision": "Revisi #$1 halaman berjudul \"{{FULLPAGENAME}}\" tidak eksis.\n\nHal ini biasanya disebabkan oleh tautan versi terdahulu menuju halaman yang sudah dihapus.\nRinciannya dapat ditemukan di [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} log penghapusan].",
        "userpage-userdoesnotexist": "Akun pengguna \"<nowiki>$1</nowiki>\" tidak terdaftar.",
        "userpage-userdoesnotexist-view": "Pengguna \"$1\" tidak terdaftar.",
-       "blocked-notice-logextract": "Pengguna ini sedang diblokir.\nEntri log pemblokiran terakhir tersedia di bawah ini sebagai rujukan.",
+       "blocked-notice-logextract": "Pengguna ini sedang diblokir.\nEntri log pemblokiran terakhir tersedia di bawah ini sebagai rujukan:",
        "clearyourcache": "'''Catatan:''' Setelah menyimpan, Anda mungkin harus memintas singgahan peramban Anda untuk melihat perubahan.\n* '''Firefox / Safari:''' Tahan ''Shift'' sambil mengeklik ''Reload'', atau tekan ''Ctrl-F5'' atau ''Ctrl-R'' (''⌘-R'' di Mac)\n* '''Google Chrome:''' Tekan ''Ctrl-Shift-R'' (''⌘-Shift-R'' di Mac)\n* '''Internet Explorer:''' Tahan ''Ctrl'' sambl mengeklik ''Refresh'', atau tekan ''Ctrl-F5''\n* '''Opera:''' Bersihkan tembolok di ''Tools → Preferences''",
        "usercssyoucanpreview": "'''Tips:''' Gunakan tombol \"{{int:showpreview}}\" untuk menguji CSS baru Anda sebelum menyimpannya.",
        "userjsyoucanpreview": "'''Tips:''' Gunakan tombol \"{{int:showpreview}}\" untuk menguji JS baru Anda sebelum menyimpannya.",
        "previewnote": "'''Ingatlah bahwa ini hanya pratayang.'''\nPerubahan Anda belum disimpan!",
        "continue-editing": "Lanjutkan penyuntingan",
        "previewconflict": "Pratayang ini mencerminkan teks pada bagian atas kotak suntingan teks sebagaimana akan terlihat bila Anda menyimpannya.",
-       "session_fail_preview": "'''Maaf, kami tidak dapat mengolah suntingan Anda akibat terhapusnya data sesi.\nSilakan coba sekali lagi.\nJika masih tidak berhasil, cobalah [[Special:UserLogout|keluar lo]]g dan masuk log kembali.'''",
-       "session_fail_preview_html": "'''Kami tidak dapat memproses suntingan Anda karena hilangnya data sesi.'''\n\n''Karena {{SITENAME}} mengizinkan penggunaan HTML mentah, pratayang telah disembunyikan sebagai pencegahan terhadap serangan JavaScript.''\n\n'''Jika ini merupakan upaya suntingan yang sahih, silakan coba lagi.\nJika masih tetap tidak berhasil, cobalah [[Special:UserLogout|keluar log]] dan masuk kembali.'''",
+       "session_fail_preview": "Maaf, kami tidak dapat mengolah suntingan Anda akibat terhapusnya data sesi.\n\nAnda mungkin telah keluar log. '''Mohon periksa apakah Anda masih mauk log dan coba sekali lagi.'''\nJika masih tidak berhasil, cobalah [[Special:UserLogout|keluar lo]]g dan masuk log kembali, dan periksa bahwa peramban web Anda mengizinkan penyimpanan kuki dari situs ini.",
+       "session_fail_preview_html": "Kami tidak dapat memproses suntingan Anda karena hilangnya data sesi.\n\n''Karena {{SITENAME}} mengizinkan penggunaan HTML mentah, pratayang telah disembunyikan sebagai pencegahan terhadap serangan JavaScript.''\n\n'''Jika ini merupakan upaya suntingan yang sahih, silakan coba lagi.'''\nJika masih tetap tidak berhasil, cobalah [[Special:UserLogout|keluar log]] dan masuk kembali, dan periksa apakah peramban web Anda mengizinkan penyimpanan kuki dari situs ini.",
        "token_suffix_mismatch": "'''Suntingan Anda ditolak karena aplikasi klien Anda mengubah karakter tanda baca pada suntingan.'''\nSuntingan tersebut ditolak untuk mencegah kesalahan pada teks halaman.\nHal ini kadang terjadi jika Anda menggunakan layanan proxy anonim berbasis web yang bermasalah.",
        "edit_form_incomplete": "'''Beberapa bagian dari formulir suntingan tidak mencapai server; periksa ulang apakah suntingan Anda tetap utuh dan coba lagi.'''",
        "editing": "Menyunting $1",
        "yourdiff": "Perbedaan",
        "copyrightwarning": "Perhatikan bahwa semua kontribusi terhadap {{SITENAME}} dianggap dilisensikan sesuai dengan $2 (lihat $1 untuk informasi lebih lanjut). Jika Anda tidak ingin tulisan Anda disunting dan disebarkan ke halaman web yang lain, jangan kirimkan ke sini.<br />Anda juga berjanji bahwa ini adalah hasil karya Anda sendiri, atau disalin dari sumber milik umum atau sumber bebas yang lain. '''JANGAN KIRIMKAN KARYA YANG DILINDUNGI HAK CIPTA TANPA IZIN!'''",
        "copyrightwarning2": "Perhatikan bahwa semua kontribusi terhadap {{SITENAME}} dapat disunting, diubah, atau dihapus oleh penyumbang lainnya. Jika Anda tidak ingin tulisan Anda disunting orang lain, jangan kirimkan ke sini.<br />Anda juga berjanji bahwa ini adalah hasil karya Anda sendiri, atau disalin dari sumber milik umum atau sumber bebas yang lain (lihat $1 untuk informasi lebih lanjut). '''JANGAN KIRIMKAN KARYA YANG DILINDUNGI HAK CIPTA TANPA IZIN!'''",
+       "editpage-cannot-use-custom-model": "Model konten halaman ini tidak bisa diubah.",
        "longpageerror": "'''KESALAHAN: Teks yang Anda kirimkan sebesar $1 kilobita, yang berarti lebih besar daripada jumlah maksimum $2 kilobita. Teks tidak dapat disimpan.'''",
-       "readonlywarning": "'''PERINGATAN: Basis data sedang dikunci karena pemeliharaan, sehingga saat ini Anda tidak dapat menyimpan hasil suntingan Anda.'''\nAnda mungkin perlu menyalin teks suntingan Anda ini dan menyimpannya ke sebuah berkas teks dan memuatkannya lagi kemudian.\n\nPengurus yang mengunci basis data memberikan penjelasan berikut: $1",
+       "readonlywarning": "'''Peringatan: Basis data sedang dikunci karena pemeliharaan, sehingga saat ini Anda tidak dapat menyimpan hasil suntingan Anda.'''\nAnda mungkin perlu menyalin teks suntingan Anda ini dan menyimpannya ke sebuah berkas teks dan memuatkannya lagi kemudian.\n\nPengurus yang mengunci basis data memberikan penjelasan berikut: $1",
        "protectedpagewarning": "'''Peringatan: Halaman ini sedang dilindungi sehingga hanya pengguna dengan hak akses pengurus yang dapat menyuntingnya.'''\nEntri catatan terakhir disediakan di bawah untuk referensi:",
        "semiprotectedpagewarning": "'''Catatan:''' Halaman ini sedang dilindungi, sehingga hanya pengguna terdaftar yang bisa menyuntingnya.\nEntri catatan terakhir disediakan di bawah untuk referensi:",
-       "cascadeprotectedwarning": "'''PERINGATAN:''' Halaman ini sedang dilindungi sehingga hanya pengguna dengan hak akses pengurus saja yang dapat menyuntingnya karena disertakan dalam {{PLURAL:$1|halaman|halaman-halaman}} berikut yang telah dilindungi dengan opsi 'pelindungan runtun':",
+       "cascadeprotectedwarning": "'''Peringatan:''' Halaman ini sedang dilindungi sehingga hanya pengguna dengan hak akses pengurus saja yang dapat menyuntingnya karena disertakan dalam {{PLURAL:$1|halaman|halaman-halaman}} berikut yang telah dilindungi dengan opsi 'pelindungan runtun':",
        "titleprotectedwarning": "'''Peringatan: Halaman ini telah dilindungi sehingga diperlukan [[Special:ListGroupRights|hak khusus]] untuk membuatnya.'''\nEntri catatan terakhir disediakan di bawah untuk referensi:",
        "templatesused": "{{PLURAL:$1|Templat|Templat}} yang digunakan di halaman ini:",
        "templatesusedpreview": "{{PLURAL:$1|Templat|Templat}} yang digunakan di pratayang ini:",
        "permissionserrors": "Kesalahan Hak Akses",
        "permissionserrorstext": "Anda tak memiliki hak untuk melakukan hal itu karena {{PLURAL:$1|alasan|alasan-alasan}} berikut:",
        "permissionserrorstext-withaction": "Anda tidak memiliki hak akses untuk $2, karena {{PLURAL:$1|alasan|alasan}} berikut:",
+       "contentmodelediterror": "Anda tidak dapat menyunting revisi ini karena isi kontennya adalah <code>$1</code>, yang berbeda dari model isi terkini dari halaman <code>$2</code>.",
        "recreate-moveddeleted-warn": "'''Peringatan: Anda membuat ulang suatu halaman yang sudah pernah dihapus.'''\n\nHarap pertimbangkan apakah layak untuk melanjutkan suntingan Anda.\nBerikut adalah log penghapusan dan pemindahan dari halaman ini:",
        "moveddeleted-notice": "Halaman ini telah dihapus.\nSebagai referensi, berikut adalah log penghapusan dan pemindahan halaman ini.",
        "moveddeleted-notice-recent": "Maaf, halaman ini telah dihapus (dalam 24 jam ini). Sebagai referensi, berikut adalah log penghapusan atau pemindahan halaman ini.",
        "content-model-css": "CSS",
        "content-json-empty-object": "Objek kosong",
        "content-json-empty-array": "Larik kosong",
+       "duplicate-args-warning": "<strong>Peringatan:</strong> [[:$1]] memanggil [[:$2]] dengan nilai lebih dari satu untuk parameter \"$3\". Hanya nilai terakhir yang tersedia yang akan digunakan.",
        "duplicate-args-category": "Halaman dengan argumen ganda di pemanggilan templat",
        "duplicate-args-category-desc": "Halaman ini berisi pemanggilan templat yang menggunakan argumen ganda, seperti <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> atau <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "expensive-parserfunction-warning": "Peringatan: Halaman ini mengandung terlalu banyak panggilan fungsi parser.\n\nSaat ini terdapat {{PLURAL:$1|$1 panggilan|$1 panggilan}}, seharusnya kurang dari $2 {{PLURAL:$2|panggilan|panggilan}}.",
        "rev-showdeleted": "tampilkan",
        "revisiondelete": "Hapus/batal hapus revisi",
        "revdelete-nooldid-title": "Target revisi tak ditemukan",
-       "revdelete-nooldid-text": "Anda belum memberikan target revisi untuk menjalankan fungsi ini.",
+       "revdelete-nooldid-text": "Anda belum memberikan revisi tujuan yang akan menjalankan fungsi ini, atau revisi yang diberikan tidak tersedia, atau Anda mencoba menyembunyikan revisi terkini.",
        "revdelete-no-file": "Berkas yang dituju tidak ditemukan.",
        "revdelete-show-file-confirm": "Apakah Anda yakin ingin melihat revisi yang telah dihapus dari berkas \"<nowiki>$1</nowiki>\" per $3, $2?",
        "revdelete-show-file-submit": "Ya",
        "revdelete-edit-reasonlist": "Alasan penghapusan suntingan",
        "revdelete-offender": "Revisi penulis:",
        "suppressionlog": "Log penyembunyian",
-       "suppressionlogtext": "Berikut adalah daftar penghapusan dan pemblokiran, termasuk konten yang disembunyikan dari para pengurus.\nLihat [[Special:BlockList|daftar pemblokiran]] untuk daftar terkininya.",
+       "suppressionlogtext": "Berikut adalah daftar penghapusan dan pemblokiran, termasuk isi yang disembunyikan dari para pengurus.\nLihat [[Special:BlockList|daftar pemblokiran]] untuk daftar terkininya.",
        "mergehistory": "Riwayat penggabungan sejarah halaman",
        "mergehistory-header": "Halaman ini memperbolehkan Anda untuk menggabungkan revisi-revisi dari satu halaman sumber ke halaman yang lebih baru.\nPastikan bahwa perubahan ini tetap mempertahankan kontinuitas versi terdahulu halaman.",
        "mergehistory-box": "Gabung revisi-revisi dari dua halaman:",
        "mergehistory-empty": "Tidak ada revisi yang dapat digabung.",
        "mergehistory-done": "$3 {{PLURAL:$3|revisi|revisi}} dari $1 berhasil digabungkan ke [[:$2]].",
        "mergehistory-fail": "Tidak dapat melakukan penggabungan, harap periksa kembali halaman dan parameter waktu.",
+       "mergehistory-fail-bad-timestamp": "Stempel waktu tidak valid.",
+       "mergehistory-fail-invalid-source": "Halaman asal tidak valid.",
+       "mergehistory-fail-invalid-dest": "Halaman tujuan tidak valid.",
+       "mergehistory-fail-no-change": "Penggabungan sejarah tidak berhasil menggabungkan revisi apa pun. Mohon periksa kembali parameter halaman dan waktu.",
+       "mergehistory-fail-permission": "Izin penggabungan sejarah halaman tidak mencukupi.",
+       "mergehistory-fail-self-merge": "Halaman asal dan tujuan sama.",
+       "mergehistory-fail-timestamps-overlap": "Revisi asal tumpang tindih atau lebih baru dari revisi tujuan.",
        "mergehistory-fail-toobig": "Tidak dapat melakukan penggabungan sebagai lebih dari batas dari $1 {{PLURAL:$1|revisi|revisi}} akan dipindahkan.",
        "mergehistory-no-source": "Halaman sumber $1 tidak ada.",
        "mergehistory-no-destination": "Halaman tujuan $1 tidak ada.",
        "showingresultsinrange": "Menampilkan sampai dengan {{PLURAL:$1|<strong>1</strong> hasil|<strong>$1</strong> hasil}} dalam rentang #<strong>$2</strong> sampai #<strong>$3</strong>.",
        "search-showingresults": "{{PLURAL:$4|Hasil <strong>$1</strong> dari <strong>$3</strong>|Hasil <strong>$1 - $2</strong> dari <strong>$3</strong>}}",
        "search-nonefound": "Tidak ada hasil yang sesuai dengan kriteria.",
+       "search-nonefound-thiswiki": "Tidak ada hasil yang sesuai dengan permintaan di situs ini.",
        "powersearch-legend": "Pencarian lanjut",
        "powersearch-ns": "Mencari di ruang nama:",
        "powersearch-togglelabel": "Pilih:",
        "prefs-watchlist-token": "Token pantauan:",
        "prefs-misc": "Lain-lain",
        "prefs-resetpass": "Ganti kata sandi",
-       "prefs-changeemail": "Ubah surel",
+       "prefs-changeemail": "Ubah atau hapus alamat surel",
        "prefs-setemail": "Atur alamat surel",
        "prefs-email": "Opsi surel",
        "prefs-rendering": "Tampilan",
        "rows": "Baris:",
        "columns": "Kolom:",
        "searchresultshead": "Cari",
-       "stub-threshold": "Ambang batas untuk format <a href=\"#\" class=\"stub\">pranala rintisan</a>:",
+       "stub-threshold": "Ambang batas untuk format pranala rintisan ($1):",
+       "stub-threshold-sample-link": "contoh",
        "stub-threshold-disabled": "Dinonaktifkan",
        "recentchangesdays": "Jumlah hari yang ditampilkan di perubahan terbaru:",
        "recentchangesdays-max": "(maksimum $1 {{PLURAL:$1|hari|hari}})",
        "prefs-help-recentchangescount": "Opsi ini berlaku untuk perubahan terbaru, versi terdahulu halaman, dan log.",
        "prefs-help-watchlist-token2": "Ini adalah kunci rahasia (token) ke web feed dari daftar pantauan Anda.\nSiapa saja yang tahu akan dapat melihat daftar pantauan Anda, jadi jangan dibagikan.\n[[Special:ResetTokens|Klik di sini jika Anda perlu menyetel ulang]].",
        "savedprefs": "Preferensi Anda telah disimpan",
+       "savedrights": "Hak pengguna {{GENDER:$1|$1}} telah disimpan.",
        "timezonelegend": "Zona waktu:",
        "localtime": "Waktu setempat:",
        "timezoneuseserverdefault": "Gunakan bawaan wiki ($1)",
        "badsig": "Tanda tangan mentah tak sah; periksa tag HTML.",
        "badsiglength": "Tanda tangan Anda terlalu panjang.\nJangan lebih dari $1 {{PLURAL:$1|karakter|karakter}}.",
        "yourgender": "Jenis kelamin:",
-       "gender-unknown": "Tak dinyatakan",
+       "gender-unknown": "Ketika menyebut Anda, perangkat lunak akan menggunakan kata-kata yang netral ketika diperlukan",
        "gender-male": "Laki-laki",
        "gender-female": "Perempuan",
        "prefs-help-gender": "Opsional: digunakan untuk perbaikan penyebutan jenis kelamin oleh perangkat lunak. \nInformasi ini akan terbuka untuk umum.",
        "userrights": "Manajemen hak pengguna",
        "userrights-lookup-user": "Mengatur kelompok pengguna",
        "userrights-user-editname": "Masukkan nama pengguna:",
-       "editusergroup": "Sunting kelompok pengguna",
-       "editinguser": "Mengganti hak akses pengguna '''[[User:$1|$1]]''' $2",
+       "editusergroup": "Sunting kelompok {{GENDER:$1|pengguna}}",
+       "editinguser": "Mengubah hak pengguna untuk {{GENDER:$1|pengguna}} <strong>[[User:$1|$1]]</strong> $2",
        "userrights-editusergroup": "Sunting kelompok pengguna",
-       "saveusergroups": "Simpan kelompok pengguna",
+       "saveusergroups": "Simpan kelompok {{GENDER:$1|pengguna}}",
        "userrights-groupsmember": "Anggota dari:",
        "userrights-groupsmember-auto": "Anggota implisit dari:",
        "userrights-groupsmember-type": "$1",
        "grouppage-bot": "{{ns:project}}:Bot",
        "grouppage-sysop": "{{ns:project}}:Pengurus",
        "grouppage-bureaucrat": "{{ns:project}}:Birokrat",
-       "grouppage-suppress": "{{ns:project}}:Pengawas",
+       "grouppage-suppress": "{{ns:project}}:Peredam",
        "right-read": "Membaca halaman",
        "right-edit": "Menyunting halaman",
        "right-createpage": "Membuat halaman baru (yang bukan halaman pembicaraan)",
        "right-createtalk": "Membuat halaman pembicaraan",
        "right-createaccount": "Membuat akun baru",
+       "right-autocreateaccount": "Masuk log otomatis dengan akun pengguna luar",
        "right-minoredit": "Menandai suntingan sebagai minor",
        "right-move": "Memindahkan halaman",
        "right-move-subpages": "Memindahkan halaman dengan seluruh subhalamannya",
        "right-sendemail": "Mengirim surel ke pengguna lain",
        "right-passwordreset": "Lihat surel pengaturulangan kata sandi",
        "right-managechangetags": "Membuat dan menghapus [[Special:Tags|tag]] dari basis data",
+       "right-applychangetags": "Terapkan [[Special:Tags|tags]] bersamaan dengan perubahan pengguna",
        "grant-generic": "\"$1\" bundel hak akses",
        "grant-group-page-interaction": "Berinteraksi dengan halaman",
        "grant-group-file-interaction": "Berinteraksi dengan media",
        "grant-group-email": "Mengirim surel",
        "grant-group-high-volume": "Melakukan aktivitas yang amat banyak",
        "grant-group-customization": "Kustomisasi dan preferensi",
-       "grant-group-administration": "Lakukan tindakan administratif",
+       "grant-group-administration": "Melakukan tindakan administratif",
        "grant-group-other": "Aktivitas lain-lain",
        "grant-blockusers": "Blokir dan buka pemblokiran pengguna",
        "grant-createaccount": "Buat akun",
        "grant-delete": "Menghapus halaman, revisi, dan log entri",
        "grant-editinterface": "Menyunting ruang nama MediaWiki dan CSS/JavaScript pengguna",
        "grant-editmycssjs": "Menyunting halaman CSS/JavaScript Anda",
-       "grant-editmyoptions": "Menyunting preferensi Anda",
+       "grant-editmyoptions": "Menyunting preferensi pengguna Anda",
        "grant-editmywatchlist": "Menyunting daftar pantauan Anda",
        "grant-editpage": "Menyunting halaman yang ada",
        "grant-editprotected": "Menyunting halaman yang dilindungi",
-       "grant-highvolume": "Amat sering menyunting",
+       "grant-highvolume": "Penyuntingan dengan volume tinggi",
        "grant-oversight": "Sembunyikan pengguna dan revisinya",
        "grant-patrol": "Tandai halaman terpatroli",
        "grant-protect": "Melindungi dan membuka perlindungan halaman",
-       "grant-rollback": "Membalikkan halamn",
-       "grant-sendemail": "Mengirim surel pada pengguna lain",
+       "grant-rollback": "Membalikkan perubahan pada halaman",
+       "grant-sendemail": "Mengirim surel kepada pengguna lain",
        "grant-uploadeditmovefile": "Mengunggah, mengganti, dan memindahkan berkas",
        "grant-uploadfile": "Mengunggah berkas baru",
-       "grant-viewdeleted": "Melihat informasi halaman yang dihapus",
+       "grant-basic": "Akses dasar",
+       "grant-viewdeleted": "Melihat halaman dan berkas yang dihapus",
        "grant-viewmywatchlist": "Melihat daftar pantauan Anda",
        "newuserlogpage": "Log pengguna baru",
        "newuserlogpagetext": "Di bawah ini adalah log pendaftaran pengguna baru",
        "action-createpage": "membuat halaman baru",
        "action-createtalk": "membuat halaman pembicaraan baru",
        "action-createaccount": "membuat akun pengguna ini",
+       "action-autocreateaccount": "buat otomatis akun pengguna luar",
        "action-history": "lihat riwayat halaman ini",
        "action-minoredit": "menandai sebagai suntingan kecil",
        "action-move": "memindahkan halaman ini",
        "action-undelete": "membatalkan penghapusan halaman ini",
        "action-suppressrevision": "meninjau dan mengembalikan revisi yang disembunyikan ini",
        "action-suppressionlog": "melihat log privat ini",
-       "action-block": "memblokir pengguna ini dari menyunting",
+       "action-block": "memblokir pengguna ini dari penyuntingan",
        "action-protect": "mengganti tingkat pelindungan halaman ini",
        "action-rollback": "mengembalikan dengan cepat suntingan-suntingan pengguna terakhir yang menyunting halaman tertentu",
        "action-import": "mengimpor halaman ini dari wiki lain",
        "action-editmyprivateinfo": "sunting informasi pribadi Anda",
        "action-editcontentmodel": "mengedit model konten sebuah halaman",
        "action-managechangetags": "Membuat dan menghapus tag dari basis data",
+       "action-applychangetags": "terapkan tag bersamaan dengan perubahan Anda",
+       "action-changetags": "menambah dan menghapus tag semaunya pada revisi individu dan entri log",
        "nchanges": "$1 {{PLURAL:$1|perubahan|perubahan}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|sejak kunjungan terakhir}}",
        "enhancedrc-history": "riwayat",
        "recentchanges-label-plusminus": "Perubahan ukuran halaman dalam bita",
        "recentchanges-legend-heading": "'''Keterangan:'''",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (lihat pula [[Special:NewPages|daftar halaman baru]])",
+       "recentchanges-submit": "Tampilkan",
        "rcnotefrom": "Di bawah ini adalah {{PLURAL:$5|perubahan}} sejak <strong>$3, $4</strong> (ditampilkan sampai <strong>$1</strong> perubahan).",
        "rclistfrom": "Perlihatkan perubahan terbaru sejak $3 $2",
        "rcshowhideminor": "$1 suntingan kecil",
        "rcshowhidemine": "$1 suntingan saya",
        "rcshowhidemine-show": "Tampilkan",
        "rcshowhidemine-hide": "Sembunyikan",
+       "rcshowhidecategorization": "$1 kategorisasi halaman",
        "rcshowhidecategorization-show": "Tampilkan",
        "rcshowhidecategorization-hide": "Sembunyikan",
        "rclinks": "Perlihatkan $1 perubahan terbaru dalam $2 hari terakhir<br />$3",
        "boteditletter": "b",
        "unpatrolledletter": "!",
        "number_of_watching_users_pageview": "[$1 {{PLURAL:$1|pemantau|pemantau}}]",
-       "rc_categories": "Batasi sampai kategori (dipisah dengan \"|\")",
-       "rc_categories_any": "Apa pun",
+       "rc_categories": "Batasi sampai kategori (dipisah dengan \"|\"):",
+       "rc_categories_any": "Setiap yang terpilih",
        "rc-change-size": "$1",
        "rc-change-size-new": "$1 {{PLURAL:$1|bita}} setelah perubahan",
        "newsectionsummary": "/* $1 */ bagian baru",
        "recentchangeslinked-summary": "Ini adalah daftar perubahan pada halaman yang terkait ke halaman yang spesifik (atau bagian dari kategori yang spesifik).\nHalaman pada [[Special:Watchlist|daftar pantauan Anda]] terlihat <strong>dicetak tebal</strong>.",
        "recentchangeslinked-page": "Nama halaman:",
        "recentchangeslinked-to": "Perlihatkan perubahan dari halaman-halaman yang terhubung dengan halaman yang disajikan",
+       "recentchanges-page-added-to-category": "[[:$1]] ditambahkan pada kategori",
+       "recentchanges-page-added-to-category-bundled": "[[:$1]] dan {{PLURAL:$2|satu halaman|$2 halaman-halaman}} lagi halaman ditambahkan pada kategori",
+       "recentchanges-page-removed-from-category": "[[:$1]] dihapus dari kategori",
+       "recentchanges-page-removed-from-category-bundled": "[[:$1]] dan {{PLURAL:$2|satu halaman|$2 halaman-halaman}} lagi halaman dihapus dari kategori",
+       "autochange-username": "Perubahan otomatis MediaWiki",
        "upload": "Pengunggahan berkas",
        "uploadbtn": "Muatkan berkas",
        "reuploaddesc": "Kembali ke formulir pemuatan",
        "uploaddisabledtext": "Pemuatan berkas tidak diizinkan.",
        "php-uploaddisabledtext": "Pemuatan berkas dimatikan di PHP. Silakan cek pengaturan file_uploads.",
        "uploadscripted": "Berkas ini mengandung HTML atau kode yang dapat diinterpretasikan dengan keliru oleh penjelajah web.",
+       "upload-scripted-pi-callback": "Tidak dapat mengunggah berkas yang mengandung arahan pengolahan hamparan XML.",
+       "uploaded-script-svg": "Terdapat elemen terskrip \"$1\" dalam berkas SVG yang diunggah.",
+       "uploaded-hostile-svg": "Terdapat CSS yang tidak aman dalam elemen gaya berkas SVG yang diunggah.",
+       "uploaded-event-handler-on-svg": "Penetapan atribut <i>event-handler</i> $1=\"$2\" tidak diizinkan dalam berkas SVG.",
        "uploadscriptednamespace": "Berkas SVG ini memuat ruang nama ilegal \"$1\"",
        "uploadinvalidxml": "XML dalam berkas yang diunggah tidak bisa diuraikan.",
        "uploadvirus": "Berkas tersebut mengandung virus! Rincian: $1",
        "filewasdeleted": "Suatu berkas dengan nama ini pernah dimuat dan selanjutnya dihapus. Harap cek $1 sebelum memuat lagi berkas tersebut.",
        "filename-bad-prefix": "Nama berkas yang Anda muat diawali dengan '''\"$1\"''', yang merupakan nama non-deskriptif yang biasanya diberikan secara otomatis oleh kamera digital. Harap pilih nama lain yang lebih deskriptif untuk berkas Anda.",
        "filename-prefix-blacklist": " #<!-- biarkan baris ini seperti adanya --> <pre>\n# Contohnya sebagai berikut:\n#   * Semuanya dari karekter \"#\" sampai akhir baris ini adalah komentar\n#   * Setiap garis \"_\" adalah awalan untuk nama file khas yang diberikan secara otomatis oleh kamera digital\nCIMG # Casio\nDSC_ # Nikon\nDSCF # Fuji\nDSCN # Nikon\nDUW # beberapa model telpon seluler\nIMG # generik\nJD # Jenoptik\nMGP # Pentax\nPICT # lainnya.\n #</pre> <!-- biarkan baris ini seperti adanya -->",
-       "upload-success-subj": "Berhasil dimuat",
-       "upload-success-msg": "Pengunggahan Anda dari [$2] berhasil. Hasilnya tersedia di sini: [[:{{ns:file}}:$1]]",
-       "upload-failure-subj": "Masalah pengunggahan",
-       "upload-failure-msg": "Ada masalah dengan unggahan Anda dari [$2]:\n\n$1",
-       "upload-warning-subj": "Peringatan pemuatan",
-       "upload-warning-msg": "Terjadi masalah dengan unggahan Anda dari [$2]. Anda dapat kembali ke [[Special:Upload/stash/$1|formulir pengunggahan]] untuk memerbaiki masalah ini.",
        "upload-proto-error": "Protokol tak tepat",
        "upload-proto-error-text": "Pemuatan jarak jauh membutuhkan URL yang diawali dengan <code>http://</code> atau <code>ftp://</code>.",
        "upload-file-error": "Kesalahan internal",
        "upload-dialog-button-done": "Selesai",
        "upload-dialog-button-save": "Simpan",
        "upload-dialog-button-upload": "Unggah",
-       "upload-form-label-select-file": "Pilih berkas",
        "upload-form-label-infoform-title": "Rincian",
        "upload-form-label-infoform-name": "Nama",
        "upload-form-label-infoform-name-tooltip": "Judul singkat yang unik untuk berkas, yang akan menjadi nama berkas. Anda dapat gunakan bahasa yang sederhana berikut spasi. Jangan menyertakan ekstensi berkas.",
        "mostrevisions": "Halaman dengan perubahan terbanyak",
        "prefixindex": "Semua halaman dengan awalan (Indeks awalan)",
        "prefixindex-namespace": "Semua halaman dengan awalan (ruang nama $1)",
+       "prefixindex-submit": "Tampilkan",
        "prefixindex-strip": "Strip awalan dalam daftar",
        "shortpages": "Halaman pendek",
        "longpages": "Halaman panjang",
        "protectedpages-performer": "Melindungi pengguna",
        "protectedpages-params": "Parameter perlindungan",
        "protectedpages-reason": "Alasan",
+       "protectedpages-submit": "Halaman tampilan",
        "protectedpages-unknown-timestamp": "Tidak diketahui",
        "protectedpages-unknown-performer": "Pengguna yang tidak diketahui",
        "protectedtitles": "Judul yang dilindungi",
        "protectedtitles-summary": "Halaman ini mendaftarkan judul-judul yang saat ini dilindungi dari pembuatan. Untuk daftar halaman yang sudah ada dan dilindungi, lihat [[{{#special:ProtectedPages}}|{{int:protectedpages}}]].",
        "protectedtitlesempty": "Tidak ada judul yang dilindungi.",
+       "protectedtitles-submit": "Judul tampilan",
        "listusers": "Daftar pengguna",
        "listusers-editsonly": "Tampilkan hanya pengguna yang memiliki kontribusi",
        "listusers-creationsort": "Urutkan menurut tanggal pendaftaran",
        "usereditcount": "$1 {{PLURAL:$1|suntingan|suntingan}}",
        "usercreated": "{{GENDER:$3|Dibuat}} pada $1 pukul $2",
        "newpages": "Halaman baru",
+       "newpages-submit": "Tampilkan",
        "newpages-username": "Nama pengguna:",
        "ancientpages": "Halaman terlama",
        "move": "Pindahkan",
        "nopagetext": "Halaman yang Anda tuju tidak ditemukan.",
        "pager-newer-n": "{{PLURAL:$1|1 lebih baru|$1 lebih baru}}",
        "pager-older-n": "{{PLURAL:$1|$1 lebih lama}}",
-       "suppress": "Pengawas",
+       "suppress": "Sembunyikan",
        "querypage-disabled": "Halaman istimewa ini dinonaktifkan demi alasan kinerja.",
        "apihelp": "Bantuan API",
        "apihelp-no-such-module": "Modul \"$1\" tidak ditemukan.",
+       "apisandbox": "Bak pasir API",
+       "apisandbox-jsonly": "JavaScript dibutuhkan untuk menggunakan kotak pasir API.",
+       "apisandbox-api-disabled": "API dinonaktifkan pada situs ini.",
+       "apisandbox-intro": "Gunakan halaman ini untuk bereksperimen dengan '''API layanan web MediaWiki'''.\nLihat [//www.mediawiki.org/wiki/API:Main_page dokumentasi API] untuk perincian lanjut penggunaan API. Contoh: [//www.mediawiki.org/wiki/API#A_simple_example dapatkan konten Halaman Utama]. Pilih sebuah tindakan untuk melihat contoh lain.\n\nPerhatikan bahwa, meskipun ini adalah bak pasir, tindakan yang Anda lakukan pada halaman ini dapat mengubah wiki.",
+       "apisandbox-fullscreen": "Kembangkan panel",
+       "apisandbox-fullscreen-tooltip": "Kembangkan panel kotak pasir untuk mengisi jendela peramban.",
+       "apisandbox-unfullscreen": "Tampilkan halaman",
+       "apisandbox-unfullscreen-tooltip": "Ciutkan panel kotak pasir, sehingga pranala navigasi MediaWiki dapat tersedia.",
+       "apisandbox-submit": "Kirim permintaan",
+       "apisandbox-reset": "Kosongkan",
+       "apisandbox-retry": "Coba lagi",
+       "apisandbox-loading": "Memuat informasi modul API \"$1\"...",
+       "apisandbox-load-error": "Sebuah galat terjadi ketika memuat informasi modul API \"$1\": $2",
+       "apisandbox-no-parameters": "Modul API ini tidak memiliki parameter.",
+       "apisandbox-helpurls": "Pranala bantuan",
+       "apisandbox-examples": "Contoh",
+       "apisandbox-dynamic-parameters": "Parameter tambahan",
+       "apisandbox-dynamic-parameters-add-label": "Tambah parameter:",
+       "apisandbox-dynamic-parameters-add-placeholder": "Nama parameter",
+       "apisandbox-dynamic-error-exists": "Parameter bernama \"$1\" telah tersedia.",
+       "apisandbox-deprecated-parameters": "Parameter usang",
+       "apisandbox-fetch-token": "Isi token dengan otomatis",
+       "apisandbox-submit-invalid-fields-title": "Beberapa kolom tidak valid",
+       "apisandbox-submit-invalid-fields-message": "Silakan perbaiki kolom yang ditandai dan coba kembali.",
+       "apisandbox-results": "Hasil",
+       "apisandbox-sending-request": "Mengirim permintaan API...",
+       "apisandbox-loading-results": "Menerima hasil API...",
+       "apisandbox-results-error": "Sebuah galat terjadi ketika memuat permintaan respon API: $1.",
+       "apisandbox-request-url-label": "URL Permintaan:",
+       "apisandbox-request-time": "Lama permintaan: {{PLURAL:$1|$1 ms}}",
+       "apisandbox-results-fixtoken": "Perbaiki token dan kirim kembali",
+       "apisandbox-results-fixtoken-fail": "Gagal mendapatkan token \"$1\".",
+       "apisandbox-alert-page": "Kolom dalam halaman ini tidak valid.",
+       "apisandbox-alert-field": "Nilai dalam kolom ini tidak valid.",
        "booksources": "Sumber buku",
        "booksources-search-legend": "Cari di sumber buku",
        "booksources-isbn": "ISBN:",
        "specialloguserlabel": "Pengguna:",
        "speciallogtitlelabel": "Target (judul atau{{ns:pengguna}}:nama pengguna untuk pengguna)",
        "log": "Catatan (Log)",
+       "logeventslist-submit": "Tampilkan",
        "all-logs-page": "Semua log publik",
        "alllogstext": "Gabungan tampilan semua log yang tersedia di {{SITENAME}}.\nAnda dapat melakukan pembatasan tampilan dengan memilih jenis log, nama pengguna (sensitif kapitalisasi), atau judul halaman (juga sensitif kapitalisasi).",
        "logempty": "Tidak ditemukan entri log yang sesuai.",
        "log-title-wildcard": "Cari judul yang diawali dengan teks tersebut",
        "showhideselectedlogentries": "Tampilkan/sembunyikan entri log terpilih",
        "log-edit-tags": "Sunting tag dari entri log yang terpilih",
+       "checkbox-select": "Pilih: $1",
+       "checkbox-all": "Semua",
+       "checkbox-none": "Tidak ada",
+       "checkbox-invert": "Balikkan",
        "allpages": "Daftar halaman",
        "nextpage": "Halaman selanjutnya ($1)",
        "prevpage": "Halaman sebelumnya ($1)",
        "cachedspecial-viewing-cached-ts": "Anda melihat versi tembolok halaman ini, yang mungkin tidak akan benar-benar aktual.",
        "cachedspecial-refresh-now": "Lihat versi terbaru.",
        "categories": "Daftar kategori",
+       "categories-submit": "Tampilkan",
        "categoriespagetext": "{{PLURAL:$1|Kategori berikut|Kategori-kategori berikut}} memiliki isi halaman atau media.\n[[Special:UnusedCategories|Kategori yang tak digunakan]] tidak ditampilkan di sini.\nLihat pula [[Special:WantedCategories|kategori yang diinginkan]].",
        "categoriesfrom": "Tampilkan kategori-kategori dimulai dengan:",
        "special-categories-sort-count": "urutkan menurut jumlah",
        "activeusers-hidebots": "Sembunyikan bot",
        "activeusers-hidesysops": "Sembunyikan pengurus",
        "activeusers-noresult": "Pengguna tidak ditemukan.",
+       "activeusers-submit": "Tampilkan pengguna aktif",
        "listgrouprights": "Daftar hak kelompok",
        "listgrouprights-summary": "Berikut adalah daftar kelompok pengguna yang terdapat di wiki ini, dengan daftar hak akses mereka masing-masing. Informasi lebih lanjut mengenai hak masing-masing dapat ditemukan di [[{{MediaWiki:Listgrouprights-helppage}}|halaman bantuan hak pengguna]].",
        "listgrouprights-key": "* <span class=\"listgrouprights-granted\">Hak yang diberikan</span>\n* <span class=\"listgrouprights-revoked\">Hak yang dicabut</span>",
        "listgrouprights-namespaceprotection-header": "Batasan ruang nama",
        "listgrouprights-namespaceprotection-namespace": "Ruang nama",
        "listgrouprights-namespaceprotection-restrictedto": "Hak yang mengizinkan pengguna untuk menyunting",
+       "listgrants": "Izin",
        "listgrants-grant": "Izin",
        "listgrants-rights": "Hak",
        "trackingcategories": "Kategori pelacak",
        "wlheader-showupdated": "Halaman-halaman yang telah berubah sejak kunjungan terakhir Anda ditampilkan dengan '''huruf tebal'''.",
        "wlnote": "Di bawah ini adalah {{PLURAL:$1|perubahan|<strong>$1</strong> perubahan}} terakhir dalam {{PLURAL:$2|jam|<strong>$2</strong> jam}}, per $3, $4.",
        "wlshowlast": "Tampilkan $1 jam $2 hari terakhir",
-       "watchlistall2": "semua",
-       "wlshowtime": "Tampilkan hingga:",
+       "watchlist-hide": "Sembunyikan",
+       "watchlist-submit": "Tampilkan",
+       "wlshowtime": "Periode waktu untuk ditampilkan:",
        "wlshowhideminor": "suntingan kecil",
        "wlshowhidebots": "bot",
        "wlshowhideliu": "pengguna terdaftar",
        "wlshowhideanons": "pengguna anonim",
        "wlshowhidepatr": "suntingan terpatroli",
        "wlshowhidemine": "suntingan saya",
+       "wlshowhidecategorization": "kategorisasi halaman",
        "watchlist-options": "Opsi daftar pantauan",
        "watching": "Memantau...",
        "unwatching": "Menghilangkan pemantauan...",
        "deletepage": "Hapus halaman",
        "confirm": "Konfirmasi",
        "excontent": "isi sebelumnya: '$1'",
-       "excontentauthor": "isinya hanya berupa: '$1' (dan satu-satunya penyumbang adalah '[[Special:Contributions/$2|$2]]')",
+       "excontentauthor": "isinya hanya berupa: \"$1\", dan satu-satunya penyumbang adalah \"[[Special:Contributions/$2|$2]]\" ([[User talk:$2|bicara]])",
        "exbeforeblank": "isi sebelum dikosongkan: '$1'",
        "delete-confirm": "Hapus \"$1\"",
        "delete-legend": "Hapus",
        "historywarning": "<strong>Peringatan:</strong> Laman yang akan Anda hapus memiliki riwayat dengan $1 perubahan:",
+       "historyaction-submit": "Tampilkan",
        "confirmdeletetext": "Anda akan menghapus halaman atau berkas ini secara permanen berikut semua sejarahnya dari basis data. Pastikan bahwa Anda memang ingin melakukannya, mengetahui segala akibatnya, dan apa yang Anda lakukan ini adalah sejalan dengan [[{{MediaWiki:Policy-url}}|kebijakan {{SITENAME}}]].",
        "actioncomplete": "Proses selesai",
        "actionfailed": "Eksekusi gagal",
        "rollback-success": "Pembatalan suntingan oleh $1; dibatalkan ke versi terakhir oleh $2.",
        "sessionfailure-title": "Kegagalan sesi",
        "sessionfailure": "Sepertinya ada masalah dengan sesi log Anda; log Anda telah dibatalkan untuk mencegah pembajakan. Silakan tekan tombol \"kembali\" dan muat kembali halaman sebelum Anda masuk, lalu coba lagi.",
+       "changecontentmodel": "Ubah model isi sebuah halaman",
+       "changecontentmodel-legend": "Ubah model isi",
        "changecontentmodel-title-label": "Judul halaman",
        "changecontentmodel-model-label": "Model konten baru",
        "changecontentmodel-reason-label": "Alasan:",
        "changecontentmodel-success-title": "Model konten ini telah diubah",
        "changecontentmodel-success-text": "Jenis konten [[:$1]] telah diubah",
+       "changecontentmodel-cannot-convert": "Isi pada [[:$1]] tidak dapat ditukar kepada jenis $2.",
+       "changecontentmodel-nodirectediting": "Model isi $1 tidak mendukung suntingan langsung",
+       "log-name-contentmodel": "Log perubahan model konten",
+       "log-description-contentmodel": "Peristiwa terkait model konten sebuah halaman",
+       "logentry-contentmodel-change": "$1 {{GENDER:$2|mengubah}} model konten halaman $3 dari \"$4\" ke \"$5\"",
        "logentry-contentmodel-change-revertlink": "batalkan",
        "logentry-contentmodel-change-revert": "batalkan",
        "protectlogpage": "Log pelindungan",
        "protect_expiry_old": "Waktu kedaluwarsa adalah pada masa lampau.",
        "protect-unchain-permissions": "Aktifkan opsi pelindungan lanjutan",
        "protect-text": "Anda dapat melihat atau mengganti tingkatan pelindungan untuk halaman '''$1''' di sini.",
-       "protect-locked-blocked": "Anda tak dapat mengganti tingkat pelindungan selagi diblokir. Berikut adalah konfigurasi saat ini untuk halaman '''$1''':",
+       "protect-locked-blocked": "Anda tak dapat mengganti tingkat pelindungan selagi diblokir. Berikut adalah konfigurasi saat ini untuk halaman <strong>$1</strong>:",
        "protect-locked-dblock": "Tingkat pelindungan tak dapat diganti karena aktifnya penguncian basis data. Berikut adalah konfigurasi saat ini untuk halaman '''$1''':",
        "protect-locked-access": "Akun Anda tidak dapat memiliki hak untuk mengganti tingkat pelindungan halaman. Berikut adalah konfigurasi saat ini untuk halaman '''$1''':",
        "protect-cascadeon": "Halaman ini sedang dilindungi karena disertakan dalam {{PLURAL:$1|halaman|halaman-halaman}} berikut yang telah dilindungi dengan pilihan pelindungan runtun diaktifkan.\nPenggantian tingkat pelindungan untuk halaman ini tidak akan mempengaruhi pelindungan runtun.",
        "undeletepagetext": "{{PLURAL:$1|Halaman berikut|Sejumlah $1 halaman}} telah dihapus tapi masih ada di dalam arsip dan dapat dikembalikan. Arsip tersebut mungkin akan dibersihkan secara berkala.",
        "undelete-fieldset-title": "Mengembalikan revisi",
        "undeleteextrahelp": "Untuk mengembalikan seluruh versi terdahulu halaman, biarkan semua kotak cek tidak terpilih dan klik '''''{{int:undeletebtn}}'''''.\nUntuk melakukan pengembalian selektif, cek kotak revisi yang sesuai dengan revisi yang ingin dikembalikan, dan klik '''''{{int:undeletebtn}}'''''.",
-       "undeleterevisions": "$1 {{PLURAL:$1|revisi|revisi}} diarsipkan",
+       "undeleterevisions": "$1 {{PLURAL:$1|revisi|revisi}} dihapus",
        "undeletehistory": "Jika Anda mengembalikan halaman tersebut, semua revisi juga akan dikembalikan ke dalam daftar versi terdahulu halaman.\nJika sebuah halaman baru dengan nama yang sama telah dibuat sejak penghapusan, revisi-revisi yang dikembalikan tersebut akan ditampilkan dalam daftar versi terdahulu.",
        "undeleterevdel": "Pembatalan penghapusan tidak akan dilakukan jika hal tersebut akan mengakibatkan revisi terkini halaman terhapus sebagian. Pada kondisi tersebut, Anda harus menghilangkan cek atau menghilangkan penyembunyian revisi yang dihapus terakhir. Revisi berkas yang tidak dapat Anda lihat tidak akan dipulihkan.",
        "undeletehistorynoadmin": "Halaman ini telah dihapus.\nAlasan penghapusan diberikan pada ringkasan di bawah ini, berikut rincian pengguna yang telah melakukan penyuntingan pada halaman ini sebelum dihapus. Isi terakhir dari revisi yang telah dihapus ini hanya tersedia untuk pengurus.",
        "contributions": "Kontribusi {{GENDER:$1|pengguna}}",
        "contributions-title": "Kontribusi pengguna untuk $1",
        "mycontris": "Kontribusi",
+       "anoncontribs": "Kontribusi",
        "contribsub2": "Untuk {{GENDER:$3|$1}} ($2)",
        "contributions-userdoesnotexist": "Pengguna \"$1\" tidak terdaftar.",
        "nocontribs": "Tidak ada perubahan yang sesuai dengan kriteria tersebut.",
        "sp-contributions-logs": "log",
        "sp-contributions-talk": "bicara",
        "sp-contributions-userrights": "pengelolaan hak pengguna",
-       "sp-contributions-blocked-notice": "Pengguna ini sedang diblokLog pemblokiran terakhir ditampilkan berikut untuk referensi:",
-       "sp-contributions-blocked-notice-anon": "Alamat IP ini diblokir pada saat ini.\nCatatan log pemblokiran terakhir tersedia di bawah ini sebagai rujukan:",
+       "sp-contributions-blocked-notice": "Pengguna ini sedang diblokir.\nLog pemblokiran terakhir ditampilkan berikut untuk referensi:",
+       "sp-contributions-blocked-notice-anon": "Alamat IP ini sedang diblokir saat ini.\nEntri log pemblokiran terakhir tersedia di bawah ini sebagai rujukan:",
        "sp-contributions-search": "Cari kontribusi",
        "sp-contributions-username": "Alamat IP atau nama pengguna:",
        "sp-contributions-toponly": "Tampilkan hanya revisi teratas",
        "whatlinkshere-hidelinks": "$1 pranala",
        "whatlinkshere-hideimages": "$1 pranala berkas",
        "whatlinkshere-filters": "Penyaring",
+       "whatlinkshere-submit": "Tuju ke",
        "autoblockid": "Blokir otomatis #$1",
        "block": "Blokir pengguna",
        "unblock": "Buka blokir pengguna",
        "ipb-unblock": "Hilangkan blokir seorang pengguna atau suatu alamat IP",
        "ipb-blocklist": "Lihat blokir yang diterapkan",
        "ipb-blocklist-contribs": "Kontribusi untuk {{GENDER:$1|$1}}",
-       "unblockip": "Hilangkan blokir terhadap alamat IP atau pengguna",
+       "unblockip": "Buka blokir pengguna",
        "unblockiptext": "Gunakan formulir di bawah untuk mengembalikan kemampuan menulis sebuah alamat IP atau pengguna yang sebelumnya telah diblokir.",
        "ipusubmit": "Hilangkan blokir ini",
-       "unblocked": "Blokir terhadap [[User:$1|$1]] telah dicabut",
+       "unblocked": "Blokir terhadap [[User:$1|$1]] telah dicabut.",
        "unblocked-range": "$1 telah diblokir",
-       "unblocked-id": "Blokir $1 telah dicabut",
-       "unblocked-ip": "[[Special:Contributions/$1|$1]] telah dibuka blokirnya.",
-       "blocklist": "Daftar pemblokiran pengguna",
+       "unblocked-id": "Blokir $1 telah dicabut.",
+       "unblocked-ip": "Pemblokiran [[Special:Contributions/$1|$1]] telah dicabut.",
+       "blocklist": "Daftar pengguna yang diblokir",
        "ipblocklist": "Daftar pemblokiran pengguna",
        "ipblocklist-legend": "Cari pengguna yang diblokir",
        "blocklist-userblocks": "Sembunyikan pemblokiran akun",
        "blocklist-timestamp": "Stempel waktu",
        "blocklist-target": "Target",
        "blocklist-expiry": "Kedaluwarsa",
-       "blocklist-by": "Admin pemblokir",
+       "blocklist-by": "Pengurus yang memblokir",
        "blocklist-params": "Parameter pemblokiran",
        "blocklist-reason": "Alasan",
        "ipblocklist-submit": "Cari",
-       "ipblocklist-localblock": "Blok lokal",
+       "ipblocklist-localblock": "Blokir lokal",
        "ipblocklist-otherblocks": "{{PLURAL:$1|pemblokiran|pemblokiran}} lain",
        "infiniteblock": "tak terbatas",
        "expiringblock": "kedaluwarsa pada $1 $2",
        "emailblock": "surel diblokir",
        "blocklist-nousertalk": "tidak dapat menyunting halaman pembicaraan sendiri",
        "ipblocklist-empty": "Daftar pemblokiran kosong.",
-       "ipblocklist-no-results": "alamat IP atau pengguna yang diminta tidak diblokir.",
+       "ipblocklist-no-results": "Alamat IP atau pengguna yang diminta tidak diblokir.",
        "blocklink": "blokir",
        "unblocklink": "hilangkan blokir",
-       "change-blocklink": "ubah blokir",
+       "change-blocklink": "ubah pemblokiran",
        "contribslink": "kontrib",
        "emaillink": "kirim surel",
        "autoblocker": "Diblokir secara otomatis karena alamat IP Anda digunakan oleh \"[[User:$1|$1]]\".\nAlasan yang diberikan untuk pemblokiran $1 adalah: \"$2\"",
        "blocklogpage": "Log pemblokiran",
-       "blocklog-showlog": "Pengguna ini telah diblokir sebelumnya. Log pemblokiran disediakan di bawah untuk referensi:",
+       "blocklog-showlog": "Pengguna ini telah diblokir sebelumnya.\nLog pemblokiran disediakan di bawah untuk referensi:",
        "blocklog-showsuppresslog": "Pengguna ini telah diblokir dan disembunyikan sebelumnya. Log supresi disediakan di bawah untuk referensi:",
        "blocklogentry": "memblokir [[$1]] dengan waktu kedaluwarsa $2 $3",
        "reblock-logentry": "mengubah pemblokiran [[$1]] dengan waktu kedaluwarsa $2 $3",
-       "blocklogtext": "Di bawah ini adalah log pemblokiran dan pembukaan blokir terhadap pengguna.\nAlamat IP yang diblokir secara otomatis tidak terdapat di dalam daftar ini.\nLihat [[Special:BlockList|daftar pemblokiran]] untuk semua pengguna yang saat ini diblokir.",
+       "blocklogtext": "Berikut adalah log tindakan pemblokiran dan pembukaan blokir terhadap pengguna.\nAlamat IP yang diblokir secara otomatis tidak terdapat di dalam daftar ini.\nLihat [[Special:BlockList|daftar pemblokiran]] untuk semua pengguna yang saat ini diblokir.",
        "unblocklogentry": "menghilangkan blokir \"$1\"",
        "block-log-flags-anononly": "hanya pengguna anonim",
        "block-log-flags-nocreate": "pembuatan akun dimatikan",
        "block-log-flags-hiddenname": "nama pengguna tersembunyi",
        "range_block_disabled": "Kemampuan pengurus dalam membuat blokir blok IP dimatikan.",
        "ipb_expiry_invalid": "Waktu kedaluwarsa tidak sah.",
+       "ipb_expiry_old": "Waktu kedaluwarsa adalah pada masa lampau.",
        "ipb_expiry_temp": "Pemblokiran atas nama pengguna yang disembunyikan harus permanen.",
        "ipb_hide_invalid": "Tak dapat menutup akun ini; akun tersebut memiliki {{PLURAL:$1|satu suntingan|$1 suntingan}}.",
-       "ipb_already_blocked": "\"$1\" telah diblokir",
+       "ipb_already_blocked": "\"$1\" telah diblokir.",
        "ipb-needreblock": "$1 sudah diblokir. Apakah Anda ingin mengubah set pemblokiran yang bersangkutan?",
        "ipb-otherblocks-header": "{{PLURAL:$1|Blok|Blok}} lain",
        "unblock-hideuser": "Anda tidak dapat membuka blokir pengguna ini karena nama pengguna mereka telah disembunyikan.",
        "ipb_cant_unblock": "Kesalahan: Blokir dengan ID $1 tidak ditemukan. Blokir tersebut kemungkinan telah dibuka.",
-       "ipb_blocked_as_range": "Kesalahan: IP $1 tidak diblok secara langsung dan tidak dapat dilepaskan. IP $1 diblok sebagai bagian dari pemblokiran kelompok IP $2, yang dapat dilepaskan.",
+       "ipb_blocked_as_range": "Kesalahan: IP $1 tidak diblokir secara langsung dan tidak dapat dilepaskan. IP $1 diblok sebagai bagian dari pemblokiran kelompok IP $2, yang dapat dilepaskan.",
        "ip_range_invalid": "Blok IP tidak sah.",
        "ip_range_toolarge": "Rentang blok lebih besar dari /$1 tidak diperbolehkan.",
        "proxyblocker": "Pemblokir proxy",
-       "proxyblockreason": "Alamat IP Anda telah diblokir karena alamat IP Anda adalah proxy terbuka. Silakan hubungi penyedia jasa internet Anda atau dukungan teknis dan beritahukan mereka masalah keamanan serius ini.",
+       "proxyblockreason": "Alamat IP Anda telah diblokir karena alamat IP Anda adalah ''proxy'' terbuka. Silakan hubungi penyedia jasa internet Anda atau dukungan teknis dan beritahukan mereka masalah keamanan serius ini.",
        "sorbs": "DNSBL",
        "sorbsreason": "Alamat IP anda terdaftar sebagai proxy terbuka di DNSBL.",
        "sorbs_create_account_reason": "Alamat IP anda terdaftar sebagai proxy terbuka di DNSBL. Anda tidak dapat membuat akun.",
-       "xffblockreason": "Sebuah alamat IP di kepala X-Forwarded-For, entah milik Anda atau server proxy yang Anda pakai, telah diblokir. Alasan pemblokirannya adalah: $1",
-       "cant-see-hidden-user": "Pengguna yang anda coba blokir telah di blokir dan di sembunyikan. Selama anda tidak memiliki hak sembunyikan pengguna, anda tidak dapat melihat atau menyunting pemblokiran pengguna ini.",
-       "ipbblocked": "Anda tidak dapat memblokir atau membuka blokir pengguna lain, karena anda sendiri diblokir",
+       "xffblockreason": "Sebuah alamat IP terdapat di kepala X-Forwarded-For, entah milik Anda atau peladen ''proxy'' yang Anda gunakan, telah diblokir. Alasan pemblokirannya adalah: $1",
+       "cant-see-hidden-user": "Pengguna yang Anda coba blokir telah diblokir dan disembunyikan. Selama Anda tidak memiliki hak sembunyikan pengguna, Anda tidak dapat melihat atau menyunting pemblokiran pengguna ini.",
+       "ipbblocked": "Anda tidak dapat memblokir atau membuka blokir pengguna lain, karena Anda sendiri diblokir.",
        "ipbnounblockself": "Anda tidak diizinkan untuk membuka blokir sendiri",
        "lockdb": "Kunci basis data",
        "unlockdb": "Buka kunci basis data",
        "lockedbyandtime": "(oleh $1 pada $2 $3)",
        "move-page": "Pindahkan $1",
        "move-page-legend": "Pindahkan halaman",
-       "movepagetext": "Menggunakan formulir di bawah ini akan mengubah nama suatu halaman dan memindahkan semua data sejarah ke nama baru.\nJudul lama akan menjadi halaman pengalihan ke judul baru.\nAnda dapat memperbarui pengalihan yang menuju ke judul asli secara otomatis.\nJika Anda memilih tidak, pastikan untuk memeriksa\n[[Special:DoubleRedirects|pengalihan ganda]] atau [[Special:BrokenRedirects|pengalihan rusak]].\nAnda bertanggung jawab untuk memastikan bahwa pranala terhubung ke tempat seharusnya.\n\nPerhatikan bahwa halaman '''tidak''' akan dipindah apabila telah ada halaman pada judul yang baru, kecuali bila halaman peralihan dan tidak mempunyai sejarah penyuntingan. \nIni berarti Anda dapat mengubah kembali nama halaman seperti semula apabila Anda membuat kesalahan, dan Anda tidak dapat menimpa halaman yang telah ada.\n\n'''Peringatan:'''\nIni dapat mengakibatkan perubahan drastis dan tak terduga bagi halaman yang populer; pastikan Anda mengerti konsekuensinya sebelum melanjutkan.",
-       "movepagetext-noredirectfixer": "Formulir di bawah ini digunakan untuk mengubah nama suatu halaman dan memindahkan semua data sejarah ke nama baru.\nJudul yang lama akan menjadi halaman peralihan menuju judul yang baru.\nPastikan untuk memeriksa pengalihan [[Special:DoubleRedirects|ganda]] atau [[Special:BrokenRedirects|rusak]].\nAnda bertanggung jawab untuk memastikan bahwa pranala terus menyambung ke halaman yang seharusnya.\n\nPerhatikan bahwa halaman '''tidak''' akan dipindah apabila telah ada halaman yang menggunakan judul yang baru, kecuali bila halaman tersebut kosong atau merupakan halaman peralihan dan tidak mempunyai sejarah penyuntingan.\nIni berarti Anda dapat mengubah nama halaman kembali seperti semula apabila Anda membuat kesalahan, dan Anda tidak dapat menimpa halaman yang telah ada.\n\n'''Peringatan:'''\nHal ini dapat mengakibatkan perubahan yang tak terduga dan drastis bagi halaman yang populer;\nPastikan Anda mengerti konsekuensi dari perbuatan ini sebelum melanjutkan.",
-       "movepagetalktext": "Halaman pembicaraan yang berkaitan juga akan dipindahkan secara otomatis '''kecuali apabila:'''\n\n*Sebuah halaman pembicaraan yang tidak kosong telah ada di bawah judul baru, atau\n*Anda tidak memberi tanda cek pada kotak di bawah ini\n\nDalam kasus tersebut, apabila diinginkan, Anda dapat memindahkan atau menggabungkan halaman secara manual.",
+       "movepagetext": "Menggunakan formulir di bawah ini akan mengubah nama suatu halaman dan memindahkan semua data sejarah ke nama baru.\nJudul lama akan menjadi halaman pengalihan ke judul baru.\nAnda dapat memperbarui pengalihan yang menuju ke judul asli secara otomatis.\nJika Anda memilih tidak, pastikan untuk memeriksa\n[[Special:DoubleRedirects|pengalihan ganda]] atau [[Special:BrokenRedirects|pengalihan rusak]].\nAnda bertanggung jawab untuk memastikan bahwa pranala terhubung ke tempat seharusnya.\n\nPerhatikan bahwa halaman '''tidak''' akan dipindah apabila telah ada halaman pada judul yang baru, kecuali bila halaman peralihan dan tidak mempunyai sejarah penyuntingan. \nIni berarti Anda dapat mengubah kembali nama halaman seperti semula apabila Anda membuat kesalahan, dan Anda tidak dapat menimpa halaman yang telah ada.\n\n'''Catatan:'''\nIni dapat mengakibatkan perubahan drastis dan tak terduga bagi halaman yang populer; pastikan Anda mengerti konsekuensinya sebelum melanjutkan.",
+       "movepagetext-noredirectfixer": "Formulir di bawah ini digunakan untuk mengubah nama suatu halaman dan memindahkan semua data sejarah ke nama baru.\nJudul yang lama akan menjadi halaman peralihan menuju judul yang baru.\nPastikan untuk memeriksa pengalihan [[Special:DoubleRedirects|ganda]] atau [[Special:BrokenRedirects|rusak]].\nAnda bertanggung jawab untuk memastikan bahwa pranala terus menyambung ke halaman yang seharusnya.\n\nPerhatikan bahwa halaman '''tidak''' akan dipindah apabila telah ada halaman yang menggunakan judul yang baru, kecuali bila halaman tersebut kosong atau merupakan halaman peralihan dan tidak mempunyai sejarah penyuntingan.\nIni berarti Anda dapat mengubah nama halaman kembali seperti semula apabila Anda membuat kesalahan, dan Anda tidak dapat menimpa halaman yang telah ada.\n\n'''Catatan:'''\nHal ini dapat mengakibatkan perubahan yang tak terduga dan drastis bagi halaman yang populer;\nPastikan Anda mengerti konsekuensi dari perbuatan ini sebelum melanjutkan.",
+       "movepagetalktext": "Jika Anda memberikan ceklis pada kotak ini, halaman pembicaraan yang berkaitan juga akan dipindahkan secara otomatis kecuali sebuah halaman pembicaraan yang tidak kosong telah ada di bawah judul baru.\n\nDalam kasus tersebut, apabila diinginkan, Anda dapat memindahkan atau menggabungkan halaman secara manual.",
        "moveuserpage-warning": "'''Peringatan:''' Anda tengah memindahkan halaman pengguna. Perlu diketahui bahwa hanya halaman yang akan dipindahkan namun pengguna ''tidak akan'' berganti nama.",
        "movecategorypage-warning": "<strong>Peringatan:</strong> Anda akan memindahkan halaman kategori. Perlu diketahui bahwa hanya halaman yang akan dipindahkan dan setiap halaman dalam kategori lama <em>tidak</em> akan dikategorikan ulang ke yang baru.",
        "movenologintext": "Anda harus menjadi pengguna terdaftar dan telah [[Special:UserLogin|masuk log]] untuk dapat memindahkan suatu halaman.",
        "movenosubpage": "Halaman ini tak memiliki subhalaman.",
        "movereason": "Alasan:",
        "revertmove": "batalkan",
-       "delete_and_move_text": "==Penghapusan diperlukan==\nHalaman yang dituju, \"[[:$1]]\", telah mempunyai isi. Apakah Anda hendak menghapusnya untuk memberikan ruang bagi pemindahan?",
+       "delete_and_move_text": "Halaman yang dituju, \"[[:$1]]\", telah berisi.\nApakah Anda ingin menghapusnya untuk memberikan ruang bagi pemindahan?",
        "delete_and_move_confirm": "Ya, hapus halaman tersebut",
        "delete_and_move_reason": "Dihapus untuk mengantisipasikan pemindahan halaman dari \"[[$1]]\"",
        "selfmove": "Pemindahan halaman tidak dapat dilakukan karena judul sumber dan judul tujuan sama.",
        "move-leave-redirect": "Buat pengalihan ke judul baru",
        "protectedpagemovewarning": "'''Peringatan''': Halaman ini telah dikunci sehingga hanya pengguna dengan hak akses pengurus yang bisa memindahkannya.\nEntri catatan terakhir disediakan di bawah untuk referensi:",
        "semiprotectedpagemovewarning": "'''Catatan:''' Halaman ini telah dikunci sehingga hanya pengguna terdaftar yang dapat memindahkannya.\nEntri catatan terakhir disediakan di bawah untuk referensi:",
-       "move-over-sharedrepo": "== Berkas sudah ada ==\n\n[[:$1]] sudah ada pada penyimpanan bersama. Memindahkan berkas ke judul ini akan menimpa berkas bersama.",
+       "move-over-sharedrepo": "[[:$1]] sudah tersedia pada penyimpanan bersama. Memindahkan berkas ke judul ini akan menimpa berkas bersama.",
        "file-exists-sharedrepo": "Nama berkas yang dipilih sudah digunakan pada suatu penyimpanan bersama.\nSilakan pilih nama lain.",
        "export": "Ekspor halaman",
        "exporttext": "Anda dapat mengekspor teks dan sejarah penyuntingan suatu halaman tertentu atau suatu set halaman dalam bentuk XML tertentu.\nHasil ekspor ini selanjutnya dapat diimpor ke wiki lainnya yang menggunakan perangkat lunak MediaWiki, dengan menggunakan fasilitas [[Special:Import|halaman impor]].\n\nUntuk mengekspor halaman, masukkan judul dalam kotak teks di bawah ini, satu judul per baris, dan pilih apakah Anda ingin mengekspor lengkap dengan versi terdahulunya, atau hanya versi terbaru dengan catatan penyuntingan terakhir.\n\nJika Anda hanya ingin mengimpor versi terbaru, Anda melakukannya lebih cepat dengan cara menggunakan pranala khusus, sebagai contoh: [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]] untuk mengekspor halaman \"[[{{MediaWiki:Mainpage}}]]\".",
        "export-download": "Tawarkan untuk menyimpan sebagai suatu berkas",
        "export-templates": "Termasuk templat",
        "export-pagelinks": "Sertakan halaman terkait hingga kedalaman:",
+       "export-manual": "Tambahkan halaman secara manual:",
        "allmessages": "Pesan sistem",
        "allmessagesname": "Nama",
        "allmessagesdefault": "Teks baku",
        "import-nonewrevisions": "Tidak ada revisi yang diimpor (semua revisi telah ada atau dilewatkan karena kesalahan).",
        "xml-error-string": "$1 pada baris $2, kolom $3 (bita $4): $5",
        "import-upload": "Memuat data XML",
-       "import-token-mismatch": "Kehilangan data sesi. Silakan mencoba kembali.",
+       "import-token-mismatch": "Kehilangan data sesi.\n\nAnda mungkin telah keluar log. '''Mohon periksa apakah Anda masih masuk log dan coba kembali.'''\nJika masih belum berhasil, coba [[Special:UserLogout|keluar log]] dan masuk log kembali, dan periksa apakah peramban web mengizinkan penyimpanan kuki dari situs ini.",
        "import-invalid-interwiki": "Tidak dapat mengimpor dari wiki tersebut.",
        "import-error-edit": "Halaman \"$1\" tidak diimpor karena Anda tidak diizinkan untuk menyuntingnya.",
        "import-error-create": "Halaman \"$1\" tidak diimpor karena Anda tidak diizinkan untuk membuatnya.",
        "import-error-interwiki": "Halaman \" $1 \" tidak diimpor karena namanya dicadangkan untuk pranala eksternal (interwiki).",
-       "import-error-special": "Halaman \" $1 \" tidak diimpor karena milik ruang nama khusus yang tidak mengizinkan adanya halaman.",
+       "import-error-special": "Halaman \"$1\" tidak diimpor karena milik ruang nama khusus yang tidak mengizinkan adanya halaman.",
        "import-error-invalid": "Halaman \"$1\" tidak diimpor karena namanya tidak valid.",
        "import-error-unserialize": "Revisi  $2  halaman \" $1 \" tidak bisa diurutkan. Revisi dilaporkan untuk menggunakan konten model $3 urutan sebagai $4 .",
        "import-error-bad-location": "Revisi $2 menggunakan konten model $3 tidak dapat disimpan di \"$1\" di wiki ini karena model tidak didukung pada halaman tersebut.",
        "import-rootpage-nosubpage": "Ruang nama \"$1\" di halaman turunan tidak mengizinkan subhalaman.",
        "importlogpage": "Log impor",
        "importlogpagetext": "Di bawah ini adalah log impor administratif dari halaman-halaman, berikut riwayat suntingannya dari wiki lain.",
-       "import-logentry-upload-detail": "$1 {{PLURAL:$1|revisi|revisi}}",
-       "import-logentry-interwiki-detail": "$1 {{PLURAL:$1|revisi}} dari $2",
+       "import-logentry-upload-detail": "$1 {{PLURAL:$1|revisi|revisi}} diimpor",
+       "import-logentry-interwiki-detail": "$1 {{PLURAL:$1|revisi}} diimpor dari $2",
        "javascripttest": "Pengujian JavaScript",
        "javascripttest-pagetext-noframework": "Halaman ini disediakan untuk pengujian JavaScript yang sedang berjalan.",
        "javascripttest-pagetext-unknownframework": "Pengujian kerangka kerja \"$1\" tidak diketahui",
+       "javascripttest-pagetext-unknownaction": "Tindakan \"$1\" tidak dikenali.",
        "javascripttest-pagetext-frameworks": "Silakan pilih satu di antara kerangka kerja pengujian berikut: $1",
        "javascripttest-pagetext-skins": "Pilih kulit yang ingin Anda uji:",
        "javascripttest-qunit-intro": "Lihat [$1 dokumentasi pengujian] di mediawiki.org.",
-       "tooltip-pt-userpage": "Halaman pengguna Anda",
+       "tooltip-pt-userpage": "Halaman {{GENDER:|pengguna Anda}}",
        "tooltip-pt-anonuserpage": "Halaman pengguna IP Anda",
-       "tooltip-pt-mytalk": "Halaman pembicaraan Anda",
+       "tooltip-pt-mytalk": "Halaman {{GENDER:|pembicaraan Anda}}",
        "tooltip-pt-anontalk": "Pembicaraan tentang suntingan dari alamat IP ini",
-       "tooltip-pt-preferences": "Preferensi saya",
+       "tooltip-pt-preferences": "Preferensi {{GENDER:|Anda}}",
        "tooltip-pt-watchlist": "Daftar halaman yang saya pantau.",
-       "tooltip-pt-mycontris": "Daftar kontribusi Anda",
+       "tooltip-pt-mycontris": "Daftar kontribusi {{GENDER:|Anda}}",
+       "tooltip-pt-anoncontribs": "Daftar suntingan yang dibuat dari alamat IP ini",
        "tooltip-pt-login": "Anda disarankan untuk masuk log, meskipun hal itu tidak diwajibkan.",
        "tooltip-pt-logout": "Keluar log",
        "tooltip-pt-createaccount": "Anda dianjurkan untuk membuat akun dan masuk log; meskipun, hal itu tidak diwajibkan",
        "tooltip-t-recentchangeslinked": "Perubahan terbaru halaman-halaman yang memiliki pranala ke halaman ini",
        "tooltip-feed-rss": "Umpan RSS untuk halaman ini",
        "tooltip-feed-atom": "Umpan Atom untuk halaman ini",
-       "tooltip-t-contributions": "Lihat daftar kontribusi pengguna ini",
-       "tooltip-t-emailuser": "Kirimkan surel kepada pengguna ini",
+       "tooltip-t-contributions": "Daftar kontribusi {{GENDER:$1|pengguna ini}}",
+       "tooltip-t-emailuser": "Kirimkan surel kepada {{GENDER:$1|pengguna ini}}",
+       "tooltip-t-info": "Informasi lanjut tentang halaman ini",
        "tooltip-t-upload": "Unggah berkas",
        "tooltip-t-specialpages": "Daftar semua halaman istimewa",
        "tooltip-t-print": "Versi cetak halaman ini",
        "creditspage": "Penghargaan halaman",
        "nocredits": "Tidak ada informasi penghargaan yang tersedia untuk halaman ini.",
        "spamprotectiontitle": "Filter pencegah spam",
-       "spamprotectiontext": "Halaman yang ingin Anda simpan telah diblokir oleh filter spam.\nIni mungkin disebabkan oleh pranala ke situs luar yang termasuk dalam daftar hitam.",
+       "spamprotectiontext": "Halaman yang ingin Anda simpan telah diblokir oleh penyaring spam.\nHal ini mungkin disebabkan oleh pranala ke situs luar yang termasuk dalam daftar hitam.",
        "spamprotectionmatch": "Teks berikut ini memancing filter spam kami: $1",
        "spambot_username": "Pembersihan span MediaWiki",
        "spam_reverting": "Membatalkan ke versi terakhir yang tak memiliki pranala ke $1",
        "pageinfo-robot-index": "Diperbolehkan",
        "pageinfo-robot-noindex": "Tidak diperbolehkan",
        "pageinfo-watchers": "Jumlah pemantau halaman",
+       "pageinfo-visiting-watchers": "Jumlah pemantau halaman yang mengunjungi suntingan terkini",
        "pageinfo-few-watchers": "Kurang dari $1 {{PLURAL:$1|pengunjung}}",
+       "pageinfo-few-visiting-watchers": "Mungkin ada atau tidak ada pemantau halaman yang mengunjungi suntingan terkini",
        "pageinfo-redirects-name": "Jumah pengalihan ke halaman ini",
        "pageinfo-redirects-value": "$1",
        "pageinfo-subpages-name": "Subhalaman halaman ini",
        "pageinfo-protect-cascading-yes": "Ya",
        "pageinfo-protect-cascading-from": "Perlindungan mulai dari",
        "pageinfo-category-info": "Kategori informasi",
+       "pageinfo-category-total": "Jumlah anggota",
        "pageinfo-category-pages": "Jumlah halaman",
        "pageinfo-category-subcats": "Jumlah subkategori",
        "pageinfo-category-files": "Jumlah berkas",
        "markaspatrolleddiff": "Tandai telah dipatroli",
        "markaspatrolledtext": "Tandai halaman ini telah dipatroli",
+       "markaspatrolledtext-file": "Tandai versi berkas sebagai terpatroli",
        "markedaspatrolled": "Ditandai telah dipatroli",
        "markedaspatrolledtext": "Revisi yang terpilih dari [[:$1]] telah ditandai sebagai terpatroli.",
        "rcpatroldisabled": "Patroli perubahan terbaru dimatikan",
        "patrol-log-page": "Log patroli",
        "patrol-log-header": "Ini adalah log revisi terpatroli.",
        "log-show-hide-patrol": "$1 log patroli",
+       "log-show-hide-tag": "log tag $1",
        "deletedrevision": "Revisi lama yang dihapus $1",
        "filedeleteerror-short": "Kesalahan waktu menghapus berkas: $1",
        "filedeleteerror-long": "Terjadi kesalahan sewaktu menghapus berkas:\n\n$1",
        "svg-long-error": "Berkas SVG tidak sah: $1",
        "show-big-image": "Ukuran asli",
        "show-big-image-preview": "Ukuran pratayang ini: $1.",
+       "show-big-image-preview-differ": "Ukuran pratayang $3 ini dari berkas $2 ini: $1",
        "show-big-image-other": "{{PLURAL:$2|Resolusi}} lain: $1.",
        "show-big-image-size": "$1 × $2 piksel",
        "file-info-gif-looped": "melingkar",
        "newimages-legend": "Penyaring",
        "newimages-label": "Nama berkas (atau sebagian dari nama berkas):",
        "newimages-showbots": "Tampilkan unggahan oleh bot",
+       "newimages-hidepatrolled": "Sembunyikan unggahan yang telah dipatroli",
        "noimages": "Tidak ada yang dilihat.",
        "ilsubmit": "Cari",
        "bydate": "berdasarkan tanggal",
        "exif-compression-34712": "JPEG2000",
        "exif-copyrighted-true": "Berhak cipta",
        "exif-copyrighted-false": "Status hak cipta belum diatur",
+       "exif-photometricinterpretation-1": "Hitam dan putih (Hitam adalah 0)",
        "exif-photometricinterpretation-2": "RGB",
        "exif-photometricinterpretation-6": "YCbCr",
        "exif-unknowndate": "Tanggal tak diketahui",
        "scarytranscludefailed-httpstatus": "[Pengambilan templat $1 gagal: HTTP $2]",
        "scarytranscludetoolong": "[URL terlalu panjang]",
        "deletedwhileediting": "'''Peringatan''': Halaman ini telah dihapus setelah Anda mulai melakukan penyuntingan!",
-       "confirmrecreate": "Pengguna [[User:$1|$1]] ([[User talk:$1|bicara]]) telah menghapus halaman selagi Anda mulai melakukan penyuntingan dengan alasan:\n: ''$2''\nSilakan konfirmasi jika Anda ingin membuat ulang halaman ini.",
-       "confirmrecreate-noreason": "Pengguna [[User:$1|$1]] ([[User talk:$1|bicara]]) telah menghapus halaman ini setelah Anda mulai menyunting. Harap konfirmasikan bahwa Anda ingin membuat ulang halaman ini.",
+       "confirmrecreate": "Pengguna [[User:$1|$1]] ([[User talk:$1|bicara]]) telah {{GENDER:$1|menghapus}} halaman selagi Anda mulai melakukan penyuntingan dengan alasan:\n: ''$2''\nSilakan konfirmasi jika Anda ingin membuat ulang halaman ini.",
+       "confirmrecreate-noreason": "Pengguna [[User:$1|$1]] ([[User talk:$1|bicara]]) telah {{GENDER:$1|menghapus}} halaman ini setelah Anda mulai menyunting. Harap konfirmasikan bahwa Anda ingin membuat ulang halaman ini.",
        "recreate": "Buat ulang",
        "unit-pixel": "px",
        "confirm_purge_button": "OK",
        "hebrew-calendar-m12-gen": "Elul",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|bicara]])",
        "timezone-utc": "UTC",
+       "timezone-local": "Lokal",
        "duplicate-defaultsort": "Peringatan: Kunci pengurutan baku \"$2\" mengabaikan kunci pengurutan baku \"$1\" sebelumnya.",
        "duplicate-displaytitle": "<strong>Peringatan:</strong> Menampilkan judul \"$2\" menimpa judul tampilan \"$1\" sebelumnya.",
+       "invalid-indicator-name": "'''Galat:''' Atribut ''nama'' indikator status halaman tidak boleh kosong.",
        "version": "Versi",
        "version-extensions": "Ekstensi terinstal",
        "version-skins": "Kulit yang diinstal",
        "version-hook-subscribedby": "Dilanggani oleh",
        "version-version": "($1)",
        "version-no-ext-name": "[tanpa nama]",
-       "version-svn-revision": "(r$2)",
        "version-license": "Lisensi MediaWiki",
        "version-ext-license": "Lisensi",
        "version-ext-colheader-name": "Ekstensi",
        "version-entrypoints-header-url": "URL",
        "version-entrypoints-articlepath": "[https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgArticlePath Artikel path]",
        "version-entrypoints-scriptpath": "[https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgScriptPath Skrip path]",
+       "version-libraries": "perpustakaan yang terpasang",
        "version-libraries-library": "Perpustakaan",
        "version-libraries-version": "Versi",
        "version-libraries-license": "Lisensi",
        "version-libraries-description": "Deskripsi",
        "version-libraries-authors": "Pembuat",
-       "redirect": "Pengalihan berdasarkan ID berkas, pengguna, halaman atau revisi",
+       "redirect": "Pengalihan berdasarkan ID berkas, pengguna, halaman, revisi, atau log",
        "redirect-legend": "Pengalihan ke sebuah berkas atau halaman",
        "redirect-summary": "Halaman istimewa ini beralih ke berkas (sesuai nama berkasnya), halaman (sesuai ID revisinya), atau halaman pengguna (sesuai ID numerik penggunanya). Penggunaan: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/revision/328429]], atau [[{{#Special:Redirect}}/user/101]].",
        "redirect-submit": "Lanjut",
        "redirect-page": "ID Halaman",
        "redirect-revision": "Revisi halaman",
        "redirect-file": "Nama berkas",
+       "redirect-logid": "ID log",
        "redirect-not-exists": "Nilai tidak ditemukan",
        "fileduplicatesearch": "Pencarian berkas duplikat",
        "fileduplicatesearch-summary": "Pencarian duplikat berkas berdasarkan nilai hash-nya.",
        "tags-actions-header": "Tindakan",
        "tags-active-yes": "Ya",
        "tags-active-no": "Tidak",
+       "tags-source-extension": "Ditetapkan oleh suatu ekstensi",
+       "tags-source-manual": "Digunakan secara manual oleh pengguna dan bot",
        "tags-source-none": "Tidak digunakan lagi",
        "tags-edit": "sunting",
        "tags-delete": "hapus",
        "tags-activate": "aktifkan",
        "tags-deactivate": "nonaktifkan",
        "tags-hitcount": "$1 {{PLURAL:$1|perubahan}}",
+       "tags-manage-no-permission": "Anda tak memiliki hak akses untuk mengatur perubahan tag.",
+       "tags-manage-blocked": "Anda tidak dapat mengganti tag ketika sedang diblokir.",
        "tags-create-heading": "Buat sebuah tag baru",
+       "tags-create-explanation": "Secara baku, tag yang baru dibuat akan tersedia untuk digunakan oleh pengguna dan bot.",
+       "tags-create-tag-name": "Nama tag:",
        "tags-create-reason": "Alasan:",
        "tags-create-submit": "Buat",
+       "tags-create-no-name": "Anda harus memberikan nama tag.",
+       "tags-create-invalid-chars": "Nama tag tidak boleh mengandung koma (<code>,</code>) atau garis miring (<code>/</code>).",
+       "tags-create-invalid-title-chars": "Nama tag tidak boleh mengandung karakter yang tidak bisa digunakan dalam judul halaman.",
+       "tags-create-already-exists": "Tag \"$1\" sudah ada.",
        "tags-delete-reason": "Alasan:",
        "tags-activate-reason": "Alasan:",
        "tags-activate-submit": "Aktifkan",
        "tags-deactivate-reason": "Alasan:",
        "tags-deactivate-submit": "Matikan",
+       "tags-apply-blocked": "Anda tidak dapat menerapkan perubahan tag dan perubahan lainnya ketika sedang diblokir.",
+       "tags-update-blocked": "Anda tidak dapat menambah atau menghapus tag ketika sedang diblokir.",
        "tags-edit-reason": "Alasan:",
        "comparepages": "Bandingkan halaman",
        "compare-page1": "Halaman 1",
        "revdelete-restricted": "akses telah dibatasi untuk opsis",
        "revdelete-unrestricted": "pembatasan akses opsis dihapuskan",
        "logentry-block-block": "$1 {{GENDER:$2|memblokir}} {{GENDER:$4|$3}} dengan waktu kedaluwarsa $5 $6",
+       "logentry-block-unblock": "$1 telah {{GENDER:$2|mencabut pemblokiran}} atas {{GENDER:$4|$3}}",
        "logentry-block-reblock": "$1 {{GENDER:$2|mengubah}} pemblokiran {{GENDER:$4|$3}} dengan waktu kedaluwarsa $5 $6",
        "logentry-suppress-block": "$1 {{GENDER:$2|memblokir}} {{GENDER:$4|$3}} dengan waktu kedaluwarsa $5 $6",
        "logentry-suppress-reblock": "$1 {{GENDER:$2|mengubah}} pemblokiran {{GENDER:$4|$3}} dengan waktu kedaluwarsa $5 $6",
index 6c13a83..a4f8828 100644 (file)
        "querypage-disabled": "Daytoy nga espesial a panid ket nabaldado gapu kadagiti rason ti kasayaat ti panagpataray.",
        "apihelp": "Tulong ti API",
        "apihelp-no-such-module": "Saan a nabirukan ti modulo ti \"$1\".",
+       "apisandbox": "Pagsubokan ti API",
+       "apisandbox-api-disabled": "Ti API ket nabaldado iti daytoy a sitio.",
+       "apisandbox-intro": "Usaren daytoy a panid iti panagsubok ti '''MediaWiki a serbisio ti web ti API'''.\nKitaen ti [//www.mediawiki.org/wiki/API:Main_page the API dokumentasion] para iti ad-adu pay a salaysay ti panagusar ti API. Kas pagarigan: [//www.mediawiki.org/wiki/API#A_simple_example alaen ti linaon ti Umuna a Panid].  Agpili ti maaramid tapno makakita ti adu pay a kas pagarigan.\n\nLaglagipen nga uray daytoy ket pagipadasan, dagiti tignay nga aramidem iti daytoy a panid ket mabalin a mangbaliw iti wiki.",
+       "apisandbox-submit": "Agaramid ti kiddaw",
+       "apisandbox-reset": "Dalusan",
+       "apisandbox-examples": "Kas pagarigan",
+       "apisandbox-results": "Nagbanagan",
+       "apisandbox-request-url-label": "Agkiddaw ti URL:",
+       "apisandbox-request-time": "Oras ti kiddaw: $1",
        "booksources": "Dagiti taudan ti libro",
        "booksources-search-legend": "Agbiruk para kadagiti taudan ti libro",
        "booksources-search": "Biruken",
index f1b13ed..63d5d27 100644 (file)
                        "Sveinn í Felli",
                        "Jonbg",
                        "Matma Rex",
-                       "Xð"
+                       "Xð",
+                       "Sveinki"
                ]
        },
        "tog-underline": "Undirstrika tengla:",
        "tog-hideminor": "Fela minniháttar breytingar í nýlegum breytingum",
        "tog-hidepatrolled": "Fela yfirfarnar breytingar í nýlegum breytingum",
        "tog-newpageshidepatrolled": "Fela yfirfarnar breytingar í listanum yfir nýjar síður",
+       "tog-hidecategorization": "Fela flokkun á síðum",
        "tog-extendwatchlist": "Sýna allar breytingar á vaktlistanum, ekki einungis þær nýjustu",
        "tog-usenewrc": "Flokka breytingar eftir síðu í nýlegum breytingum og vaktlista",
        "tog-numberheadings": "Númera fyrirsagnir sjálfkrafa",
@@ -58,6 +60,7 @@
        "tog-watchlisthideliu": "Ekki sýna breytingar innskráðra notenda á vaktlistanum",
        "tog-watchlisthideanons": "Ekki sýna breytingar óþekktra notenda á vaktlistanum",
        "tog-watchlisthidepatrolled": "Fela yfirfarnar breytingar í vaktlistanum",
+       "tog-watchlisthidecategorization": "Fela flokkun á síðum",
        "tog-ccmeonemails": "Senda mér afrit af tölvupóstum sem ég sendi öðrum notendum",
        "tog-diffonly": "Ekki sýna síðuefni undir mismunum",
        "tog-showhiddencats": "Sýna falda flokka",
        "october-date": "$1. október",
        "november-date": "$1. nóvember",
        "december-date": "$1. desember",
+       "period-am": "FH",
+       "period-pm": "EH",
        "pagecategories": "{{PLURAL:$1|Flokkur|Flokkar}}",
        "category_header": "Síður í flokknum „$1“",
        "subcategories": "Undirflokkar",
        "nospecialpagetext": "Þú hefur beðið um kerfissíðu sem ekki er til. Listi yfir gildar kerfissíður er að finna á [[Special:SpecialPages|kerfissíður]].",
        "error": "Villa",
        "databaseerror": "Gagnagrunnsvilla",
+       "databaseerror-query": "Fyrirspurn: $1",
        "databaseerror-error": "Villa: $1",
        "laggedslavemode": "Viðvörun: Síðan inniheldur ekki nýjustu uppfærslur.",
        "readonly": "Gagnagrunnur læstur",
        "filerenameerror": "Gat ekki endurnefnt skrána „$1“ í „$2“.",
        "filedeleteerror": "Gat ekki eytt skránni „$1“.",
        "directorycreateerror": "Gat ekki búið til efnisskrána \"$1\".",
+       "directoryreadonlyerror": "Mappan \"$1\" er skrifvarin.",
+       "directorynotreadableerror": "Mappan \"$1\" er ekki lesanleg.",
        "filenotfound": "Gat ekki fundið skrána „$1“.",
        "unexpected": "Óvænt gildi: „$1“=„$2“.",
        "formerror": "Villa: gat ekki sent eyðublað",
        "passwordreset-emailerror-capture": "Tölvupóstur til að endursetja lykilorðið var búinn til, sem er sýndur hér fyrir neðan, en ekki tókst að senda hana til {{GENDER:$2|notandans}}: $1",
        "changeemail": "Breyta eða fjarlægja netfang",
        "changeemail-header": "Fylltu út þetta eyðublað til að breyta netfanginu þínu. Ef þú vilt fjarlægja tengingu allra netfanga frá aðganginum þínum skildu þá netfangs reitinn eftir tóman.",
+       "changeemail-passwordrequired": "Þú verður að setja inn lykilorðið þitt til að staðfesta þessa breytingu.",
        "changeemail-no-info": "Þú verður að vera skráð(ur) inn til að hafa aðgang að þessari síðu.",
        "changeemail-oldemail": "Núverandi netfang:",
        "changeemail-newemail": "Nýtt netfang:",
        "edit-conflict": "Breytingaárekstur.",
        "edit-no-change": "Breyting þín var hunsuð, því engin breyting var á textanum.",
        "postedit-confirmation-created": "Síðan hefur verið búin til.",
-       "postedit-confirmation-saved": "Breyting þín hefur verið vistuð.",
+       "postedit-confirmation-restored": "Síðan hefur verið endurheimt.",
+       "postedit-confirmation-saved": "Breytingin þín hefur verið vistuð.",
        "edit-already-exists": "Gat ekki skapað nýja síðu.\nHún er nú þegar til.",
-       "defaultmessagetext": "Sjálfgefinn skilaboða texti",
+       "defaultmessagetext": "Sjálfgefinn texti skilaboða",
        "content-failed-to-parse": "Gat ekki þáttað $2 efni samkvæmt $1 líkani: $3",
        "invalid-content-data": "Ógild efnisgögn.",
        "content-not-allowed-here": "„$1“ efni er ekki leyfilegt á síðunni [[$2]]",
        "notextmatches": "Engar samsvaranir á texta í síðum",
        "prevn": "síðustu {{PLURAL:$1|$1}}",
        "nextn": "næstu {{PLURAL:$1|$1}}",
+       "prev-page": "fyrri síða",
+       "next-page": "næsta síða",
        "prevn-title": "Fyrri $1 {{PLURAL:$1|niðurstaða|niðurstöður}}",
        "nextn-title": "{{PLURAL:$1|Næsta|Næstu}} $1 {{PLURAL:$1|niðurstaða|niðurstöður}}",
        "shown-title": "Sýna $1 {{PLURAL:$1|niðurstöðu|niðurstöður}} á hverri síðu",
        "search-result-category-size": "{{PLURAL:$1|1 meðlimur|$1 meðlimir}} ({{PLURAL:$2|1 undirflokks|$2 undirflokka}}, {{PLURAL:$3|1 skrá|$3 skrár}})",
        "search-redirect": "(tilvísun $1)",
        "search-section": "(hluti $1)",
+       "search-category": "(flokkur $1)",
        "search-file-match": "(passar við innihald skráa)",
        "search-suggest": "Varstu að leita að: $1",
        "search-interwiki-caption": "Systurverkefni",
        "columns": "Dálkar",
        "searchresultshead": "Leit",
        "stub-threshold": "Þröskuldur fyrir stílsnið stubbatengla ($1):",
+       "stub-threshold-sample-link": "dæmi",
        "stub-threshold-disabled": "Óvirkt",
        "recentchangesdays": "Fjöldi daga sem nýlegar breytingar ná yfir:",
        "recentchangesdays-max": "(hámark $1 {{PLURAL:$1|dag|daga}})",
        "right-override-export-depth": "Flytja út síður með greinum þar sem allt að 5 greinar tengja þær saman.",
        "right-sendemail": "Senda tölvupóst til annara notenda",
        "right-passwordreset": "Skoða tölvupósta um endurstillingu lykilorðs",
+       "grant-group-email": "Senda tölvupóst",
+       "grant-createaccount": "Stofna aðganga",
+       "grant-createeditmovepage": "Búa til, breyta og færa síður",
+       "grant-delete": "Eyða síðum, yfirferðum og annálsfærslum",
        "newuserlogpage": "Skrá yfir nýja notendur",
        "newuserlogpagetext": "Þetta er skrá yfir nýskráða notendur.",
        "rightslog": "Réttindaskrá notenda",
        "rcshowhidemine": "$1 mínar breytingar",
        "rcshowhidemine-show": "Sýna",
        "rcshowhidemine-hide": "Fela",
+       "rcshowhidecategorization-show": "Birta",
+       "rcshowhidecategorization-hide": "Fela",
        "rclinks": "Sýna síðustu $1 breytingar síðustu $2 daga<br />$3",
        "diff": "breyting",
        "hist": "breytingaskrá",
        "watchthisupload": "Vakta þessa skrá",
        "filewasdeleted": "Skrá af sama nafni hefur áður verið hlaðið inn og síðan eytt. Þú ættir að athuga $1 áður en þú hleður skránni inn.",
        "filename-bad-prefix": "Sráarnafnið lýsir ekki skránni, heldur var það búið til af myndavélinni, því það byrjar á '''\"$1\"'''.\nVeldu lýsandi nafn fyrir skránna og reyndu aftur.",
-       "upload-success-subj": "Innhlaðning tókst",
-       "upload-success-msg": "Upphlöðun frá [$2] tókst. Það er aðgengilegt hér: [[:{{ns:file}}:$1]]",
-       "upload-failure-subj": "Vandamál við upphleðslu",
-       "upload-failure-msg": "Upphlaðið frá [$2] mistókst:\n\n$1",
-       "upload-warning-subj": "Aðvörun",
-       "upload-warning-msg": "Upphal þitt [$2] mistókst. Þú getur farið aftur á [[Special:Upload/stash/$1|upphlaðsviðmótið]] og leiðrétt villuna.",
        "upload-proto-error": "Vitlaus samskiptaregla",
        "upload-proto-error-text": "Upphlöðun frá öðrum vefþjón þarfnast vefslóðar sem byrjar á <code>http://</code> eða <code>ftp://</code>.",
        "upload-file-error": "Innri villa",
        "upload-too-many-redirects": "Vefslóðin inniheldur of margar tilvísanir.",
        "upload-http-error": "HTTP villa kom upp: $1",
        "upload-copy-upload-invalid-domain": "Lokað er fyrir afritun skráa frá öðrum vefþjón á þessu vefsvæði.",
+       "upload-dialog-title": "Hlaða inn skrá",
+       "upload-dialog-button-cancel": "Hætta við",
+       "upload-dialog-button-done": "Lokið",
+       "upload-dialog-button-save": "Vista",
+       "upload-dialog-button-upload": "Hlaða inn",
+       "upload-form-label-infoform-title": "Nánar",
+       "upload-form-label-infoform-name": "Heiti",
+       "upload-form-label-infoform-name-tooltip": "Einstakur og lýsandi titill, sem mun verða skráarnafn. Þú mátt nota einfalt mál með bilum. Ekki hafa með neina skráarendingu.",
+       "upload-form-label-infoform-description": "Lýsing",
+       "upload-form-label-infoform-description-tooltip": "Lýstu stuttlega öllu því sem er markvert um verkið.\nFyrir ljósmyndir, nefndu aðalatriði myndarinnar, tilefni eða staðsetningu.",
+       "upload-form-label-usage-title": "Notkun",
+       "upload-form-label-usage-filename": "Skráarheiti",
+       "foreign-structured-upload-form-label-own-work": "Það er mitt eigið verk",
+       "foreign-structured-upload-form-label-infoform-categories": "Flokkar",
+       "foreign-structured-upload-form-label-infoform-date": "Dagsetning",
        "backend-fail-stream": "Gat ekki streymt skránni „$1“.",
        "backend-fail-backup": "Öryggisafritun skrárinnar $1 mistókst.",
        "backend-fail-notexists": "Skráin $1 er ekki til.",
        "nolicense": "Ekkert valið",
        "licenses-edit": "Breyta leyfisvali",
        "license-nopreview": "(Forskoðun ekki fáanleg)",
-       "upload_source_url": "(þín valda skrá frá gildri, aðgengilegri vefslóð)",
-       "upload_source_file": "(þín valda skrá frá tölvunni þinni)",
+       "upload_source_url": "(skrá sem þú velur frá gildri og aðgengilegri vefslóð)",
+       "upload_source_file": "(skrá sem þú velur á tölvunni þinni)",
        "listfiles-delete": "eyða",
        "listfiles-summary": "Þessi kerfissíða sýnir allar upphlaðnar skrár.",
        "listfiles_search_for": "Leita að miðilsnafni:",
        "randomincategory-nopages": "Það eru engar síður í flokkinum [[:Category:$1|$1]].",
        "randomincategory-category": "Flokkur:",
        "randomincategory-legend": "Handhófsvalin síða í flokki",
+       "randomincategory-submit": "Fara",
        "randomredirect": "Handahófsvalin tilvísun",
        "randomredirect-nopages": "Það eru engar tilvísanir í nafnrýminu „$1“.",
        "statistics": "Tölfræði",
        "mostrevisions": "Síður eftir fjölda breytinga",
        "prefixindex": "Allar síður með forskeyti",
        "prefixindex-namespace": "Allar síður með forskeyti ($1 nafnrými)",
+       "prefixindex-submit": "Birta",
        "prefixindex-strip": "Fjarlægja forskeyti í listanum",
        "shortpages": "Stuttar síður",
        "longpages": "Langar síður",
        "protectedpages-performer": "Vernduð af",
        "protectedpages-params": "Verndunar stikar",
        "protectedpages-reason": "Ástæða",
+       "protectedpages-submit": "Birta síður",
        "protectedpages-unknown-timestamp": "Óþekktur",
        "protectedpages-unknown-performer": "Óþekktur notandi",
        "protectedtitles": "Verndaðir titlar",
        "protectedtitlesempty": "Engir titlar eru verndaðir með þessum stikum.",
+       "protectedtitles-submit": "Sýna titla",
        "listusers": "Notendalisti",
        "listusers-editsonly": "Sýna eingöngu notendur með breytingar",
        "listusers-creationsort": "Raða eftir stofndegi",
        "usereditcount": "$1 {{PLURAL:$1|breyting|breytingar}}",
        "usercreated": "{{GENDER:$3|Stofnað|}} $1 $2",
        "newpages": "Nýjustu greinar",
+       "newpages-submit": "Birta",
        "newpages-username": "Notandanafn:",
        "ancientpages": "Síst uppfærðar síður",
        "move": "Færa",
        "specialloguserlabel": "Gerandi:",
        "speciallogtitlelabel": "Beinist að (titill eða {{ns:user}}:notendanafn fyrir notanda):",
        "log": "Aðgerðaskrár",
+       "logeventslist-submit": "Birta",
        "all-logs-page": "Allar aðgerðir",
        "alllogstext": "Safn allra aðgerðaskráa {{SITENAME}}.\nÞú getur takmarkað listann með því að velja tegund aðgerðaskráar, notandanafn, eða síðu.",
        "logempty": "Engin slík aðgerð fannst.",
        "log-title-wildcard": "Leita að titlum sem byrja á þessum texta",
        "showhideselectedlogentries": "Sýna/fela valdar aðgerða færslur",
+       "checkbox-select": "Velja: $1",
+       "checkbox-all": "Allt",
+       "checkbox-none": "Ekkert",
+       "checkbox-invert": "Snúa við",
        "allpages": "Allar síður",
        "nextpage": "Næsta síða ($1)",
        "prevpage": "Fyrri síða ($1)",
        "cachedspecial-viewing-cached-ts": "Þetta er útgáfa þessarar síðu úr skyndiminni og sem endurspeglar ekki endilega núverandi ástand.",
        "cachedspecial-refresh-now": "Skoða síðustu",
        "categories": "Flokkar",
+       "categories-submit": "Birta",
        "categoriespagetext": "Eftirfarandi {{PLURAL:$1|flokkur inniheldur|flokkar innihalda}} síður eða skrár.\n[[Special:UnusedCategories|Ónotaðir flokkar]] birtast ekki hér.\nSjá einnig [[Special:WantedCategories|eftirsótta flokka]].",
        "categoriesfrom": "Sýna flokka frá:",
        "special-categories-sort-count": "raða eftir fjölda",
        "activeusers-hidebots": "Fela vélmenni",
        "activeusers-hidesysops": "Fela möppudýr",
        "activeusers-noresult": "Enginn notandi fannst.",
+       "activeusers-submit": "Sýna virka notendur",
        "listgrouprights": "Notandahópréttindi",
        "listgrouprights-summary": "Hér er listi yfir notendahópa á þessum wiki, með þeirra réttindum. \nÞað gæti verið til síða með [[{{MediaWiki:Listgrouprights-helppage}}|frekari upplýsingar]] um einstök réttindi.",
        "listgrouprights-key": "Skýringar:\n* <span class=\"listgrouprights-granted\">Veitt réttindi</span>\n* <span class=\"listgrouprights-revoked\">Afturkölluð réttindi</span>",
        "listgrouprights-removegroup-self": "Fjarlægja sjálfan sig úr {{PLURAL:$2|hópinum|hópunum}}: $1",
        "listgrouprights-addgroup-self-all": "Bæta sjálfum sér í alla hópa",
        "listgrouprights-removegroup-self-all": "Fjarlægja sjálfan sig úr öllum hópum",
+       "listgrouprights-namespaceprotection-header": "Takmarkanir nafnrýmis",
+       "listgrouprights-namespaceprotection-namespace": "Nafnrými",
+       "listgrants-rights": "Réttindi",
+       "trackingcategories-nodesc": "Enginn lýsing tiltæk.",
+       "trackingcategories-disabled": "Flokkurinn er óvirkur",
        "mailnologin": "Ekkert netfang til að senda á",
        "mailnologintext": "Þú verður að vera [[Special:UserLogin|innskráð(ur)]] auk þess að hafa gilt netfang í [[Special:Preferences|stillingunum]] þínum til að senda tölvupóst til annara notenda.",
        "emailuser": "Senda þessum notanda tölvupóst",
        "wlheader-showupdated": "Síðum sem hefur verið breytt síðan þú skoðaðir þær síðast eru '''feitletraðar'''.",
        "wlnote": "Hér fyrir neðan {{PLURAL:$1|er síðasta <strong>$1</strong> breyting|eru síðustu <strong>$1</strong> breytingar}} {{PLURAL:$2|síðasta <strong>$2</strong> klukkutímann|síðustu <strong>$2</strong> klukkutímana}}, frá $3, $4.",
        "wlshowlast": "Sýna síðustu $1 klukkutíma, $2 daga",
-       "watchlistall2": "allt",
+       "watchlist-hide": "Fela",
+       "watchlist-submit": "Birta",
+       "wlshowhideliu": "skráðir notendur",
+       "wlshowhideanons": "óskráðir notendur",
+       "wlshowhidepatr": "vaktaðar breytingar",
+       "wlshowhidemine": "mínar breytingar",
        "watchlist-options": "Vaktlistastillingar",
        "watching": "Vakta...",
        "unwatching": "Afvakta...",
        "rollback-success": "Tók til baka breytingar eftir $1; núverandi $2.",
        "sessionfailure-title": "Mistök í setu",
        "sessionfailure": "Líklega er vandamál með innskráningar setuna þína;\nhætt hefur verið við þessa aðgerð sem vörn gegn mögulegu samskiptaráni setunar.\nFarðu aftur á fyrri síðu, endurhladdu hana og reyndu aftur.",
+       "changecontentmodel-title-label": "Titill síðu",
+       "changecontentmodel-reason-label": "Ástæða:",
        "protectlogpage": "Verndunarskrá",
        "protectlogtext": "Fyrir neðan er listi yfir síðuverndanir og -afverndanir.\nSjáðu [[Special:ProtectedPages|Verndunarskrá]] fyrir núverandi lista yfir verndaðar síður.",
        "protectedarticle": "verndaði „[[$1]]“",
        "whatlinkshere-hidelinks": "$1 tengla",
        "whatlinkshere-hideimages": "$1 skrátenglar",
        "whatlinkshere-filters": "Síur",
+       "whatlinkshere-submit": "Fara",
        "autoblockid": "Sjálfvirkt bann $1",
        "block": "Banna notanda",
        "unblock": "Afbanna notanda",
        "export-pagelinks": "Innifela tengdar síður með dýptinni:",
        "allmessages": "Meldingar",
        "allmessagesname": "Titill",
-       "allmessagesdefault": "Sjálfgefinn skilaboða texti",
+       "allmessagesdefault": "Sjálfgefinn texti skilaboða",
        "allmessagescurrent": "Núverandi texti",
        "allmessagestext": "Þetta er listi yfir kerfismeldingar í Melding-nafnrýminu.\nVinsamlegast heimsæktu [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation MediaWiki-staðfæringuna] og [//translatewiki.net translatewiki.net] ef þú vilt taka þátt í almennri MediaWiki-staðfæringu.",
        "allmessagesnotsupportedDB": "Það er ekki hægt að nota '''{{ns:special}}:Allmessages''' því '''$wgUseDatabaseMessages''' hefur verið gerð óvirk.",
        "tooltip-t-recentchangeslinked": "Nýlegar breytingar á ítengdum síðum",
        "tooltip-feed-rss": "RSS fyrir þessa síðu",
        "tooltip-feed-atom": "Atom fyrir þessa síðu",
-       "tooltip-t-contributions": "Sýna framlagslista þessa notanda",
+       "tooltip-t-contributions": "Listi yfir framlög frá þessum notanda",
        "tooltip-t-emailuser": "Senda þessum notanda tölvupóst",
        "tooltip-t-upload": "Hlaða inn skrám",
        "tooltip-t-specialpages": "Listi yfir kerfissíður",
        "logentry-block-unblock": "$1 {{GENDER:$2|afbannaði}} {{GENDER:$4|$3}}",
        "logentry-block-reblock": "$1 {{GENDER:$2|breytti}} bann stillingum fyrir {{GENDER:$4|$3}}, rennur út $5 $6",
        "logentry-suppress-block": "$1 {{GENDER:$2|bannaði}} {{GENDER:$4|$3}}, rennur út eftir $5 $6",
+       "logentry-merge-merge": "$1 {{GENDER:$2|sameinaði}} $3 inn í $4 (útgáfur til $5)",
        "logentry-move-move": "$1 {{GENDER:$2|færði}} $3 á $4",
        "logentry-move-move-noredirect": "$1 {{GENDER:$2|færði}} $3 á $4 án þess að skilja eftir tilvísun",
        "logentry-move-move_redir": "$1 {{GENDER:$2|færði}} $3 á $4 yfir tilvísun",
        "logentry-newusers-create2": "$1 {{GENDER:$2|stofnaði}} notandaaðganginn $3",
        "logentry-newusers-byemail": "Notandaaðgangurinn $3 var {{GENDER:$2|búinn til}} af $1 og lykilorðið var sent með tölvupósti",
        "logentry-newusers-autocreate": "Aðgangurinn $1 var {{GENDER:$2|stofnaður}} sjálfvirkt",
+       "logentry-protect-protect": "$1 {{GENDER:$2|verndaði}} $3 $4",
        "logentry-rights-rights": "$1 {{GENDER:$2|breytti}} réttindum $3 frá $4 í $5",
        "logentry-rights-rights-legacy": "$1 {{GENDER:$2|breytti}} réttindum $3",
        "logentry-rights-autopromote": "$1 fékk sjálfvirkt {{GENDER:$2|aukin}} réttindi frá $4 til $5",
index 21cf9c2..5a0ddc4 100644 (file)
@@ -94,7 +94,8 @@
                        "Oggioniale",
                        "Wim b",
                        "V6rg",
-                       "JackLantern"
+                       "JackLantern",
+                       "Mpiva"
                ]
        },
        "tog-underline": "Sottolinea i collegamenti:",
        "previewnote": "'''Ricorda che questa è solo un'anteprima.'''\nLe tue modifiche NON sono ancora state salvate!",
        "continue-editing": "Vai all'area di modifica",
        "previewconflict": "L'anteprima corrisponde al testo presente nella casella di modifica superiore e rappresenta la pagina come apparirà se si sceglie di salvarla in questo momento.",
-       "session_fail_preview": "'''Non è stato possibile elaborare la modifica perché sono andati persi i dati relativi alla sessione.\nRiprovare.\nSe il problema persiste, si può tentare di [[Special:UserLogout|scollegarsi]] ed effettuare un nuovo accesso.'''",
-       "session_fail_preview_html": "'''Non è stato possibile elaborare la modifica perché sono andati persi i dati relativi alla sessione.'''\n\n''Poiché in {{SITENAME}} è abilitato l'uso di HTML senza limitazioni, l'anteprima non viene visualizzata; si tratta di una misura di sicurezza contro gli attacchi JavaScript.''\n\n'''Se questo è un legittimo tentativo di modifica, riprovare. Se il problema persiste, si può provare a [[Special:UserLogout|scollegarsi]] ed effettuare un nuovo accesso.'''",
+       "session_fail_preview": "Spiacenti! Non è stato possibile elaborare la modifica perché sono andati persi i dati relativi alla sessione.\n\nPotresti essere stato disconnesso. <strong>Verifica che sei ancora collegato e riprova</strong>.\nSe il problema persiste, si può tentare di [[Special:UserLogout|scollegarsi]] ed effettuare un nuovo accesso, e controllare che il tuo browser accetti i cookie da questo sito.",
+       "session_fail_preview_html": "Spiacenti! Non è stato possibile elaborare la modifica perché sono andati persi i dati relativi alla sessione.\n\n<em>Poiché {{SITENAME}} ha dell'HTML grezzo attivato e c'è stata una perdita dei dati della sessione, l'anteprima è nascosta come precauzione contro gli attacchi JavaScript.</em>\n\n<strong>Se si tratta di un normale tentativo d'anteprima, riprova.</strong> \nSe il problema persiste, si può tentare di [[Special:UserLogout|scollegarsi]] ed effettuare un nuovo accesso, e controllare che il tuo browser accetti i cookie da questo sito.",
        "token_suffix_mismatch": "'''La modifica non è stata salvata perché il client ha mostrato di gestire in modo errato i caratteri di punteggiatura nel token associato alla stessa. Per evitare una possibile corruzione del testo della pagina, è stata rifiutata l'intera modifica. Questa situazione può verificarsi, talvolta, quando vengono usati alcuni servizi di proxy anonimi via web che presentano dei bug.'''",
        "edit_form_incomplete": "'''Alcune parti del modulo di modifica non hanno raggiunto il server; controllare che le modifiche siano intatte e riprovare.'''",
        "editing": "Modifica di $1",
        "mergehistory-empty": "Nessuna versione da unire.",
        "mergehistory-done": "{{PLURAL:$3|Una versione di $1 è stata unita|$3 versioni di $1 sono state unite}} alla cronologia di [[:$2]].",
        "mergehistory-fail": "Impossibile unire le cronologie. Verificare la pagina e i parametri temporali.",
+       "mergehistory-fail-bad-timestamp": "Il timestamp non è valido.",
+       "mergehistory-fail-invalid-source": "La pagina di origine non è valida.",
+       "mergehistory-fail-invalid-dest": "La pagina di destinazione non è valida.",
+       "mergehistory-fail-no-change": "L'unione delle cronologie non ha unito alcuna versione. Ricontrolla le pagine ed i parametri temporali.",
+       "mergehistory-fail-permission": "Autorizzazioni insufficienti per unire cronologie.",
+       "mergehistory-fail-self-merge": "Le pagine di origine e di destinazione sono le stesse.",
+       "mergehistory-fail-timestamps-overlap": "Le versioni di origine si sovrappongono o vengono dopo le versioni di destinazione.",
        "mergehistory-fail-toobig": "Impossibile eseguire l'unione della cronologia essendoci oltre $1 {{PLURAL:$1|versione|versioni}} da spostare.",
        "mergehistory-no-source": "La pagina di origine $1 non esiste.",
        "mergehistory-no-destination": "La pagina di destinazione $1 non esiste.",
        "uploaded-script-svg": "Trovato elemento di script \"$1\" nel file caricato in formato SVG.",
        "uploaded-hostile-svg": "Trovato CSS non sicuro nell'elemento di stile del file in formato SVG caricato.",
        "uploaded-event-handler-on-svg": "Impostazione gestione eventi ed attributi <code>$1=\"$2\"</code> non è consentito in file SGV",
-       "uploaded-href-unsafe-target-svg": "Trovati href ad un bersaglio non sicuro <code>&lt;$1 $2=\"$3\"&gt;</code> caricato nel file SVG",
+       "uploaded-href-attribute-svg": "attributi href in file SVG sono consentiti collegamenti solo verso destinazioni http:// o https://, trovato <code>&lt;$1 $2=\"$3\"&gt;</code>.",
+       "uploaded-href-unsafe-target-svg": "Trovati href a dati non sicuri: destinazione URI <code>&lt;$1 $2=\"$3\"&gt;</code> caricato nel file SVG",
        "uploaded-animate-svg": "Trovato il tag \"animate\" che potrebbe cambiare href, usando l'attributo \"from\" <code>&lt;$1 $2=\"$3\"&gt;</code> nel file SVG caricato.",
        "uploaded-setting-event-handler-svg": "La configurazione di attributi per il gestore di eventi è bloccata, trovato <code>&lt;$1 $2=\"$3\"&gt;</code> nel file SVG caricato.",
        "uploaded-setting-href-svg": "Utilizzare il tag \"set\" per aggiungere l'attributo \"href\" all'elemento parentale è bloccato.",
        "upload-dialog-button-done": "Fatto",
        "upload-dialog-button-save": "Salva",
        "upload-dialog-button-upload": "Carica",
-       "upload-form-label-select-file": "Seleziona file",
        "upload-form-label-infoform-title": "Dettagli",
        "upload-form-label-infoform-name": "Nome",
        "upload-form-label-infoform-name-tooltip": "Un titolo breve e distintivo per il file, che verrà utilizzato come suo nome. Puoi usare un linguaggio semplice con spazi. Non includere l'estensione del file.",
        "foreign-structured-upload-form-label-own-work-message-shared": "Attesto che possiedo i diritti d'autore su questo file, e accetto irrevocabilmente il rilascio di questo file su Wikimedia Commons in base alla licenza [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribuzione-Condividi allo stesso modo 4.0], e accetto le [https://wikimediafoundation.org/wiki/Terms_of_Use Condizioni d'uso].",
        "foreign-structured-upload-form-label-not-own-work-message-shared": "Se non possiedi i diritti d'autore su questo file, o se lo vuoi rilasciare con una licenza diversa, usa il [https://commons.wikimedia.org/wiki/Special:UploadWizard caricamento guidato di Commons].",
        "foreign-structured-upload-form-label-not-own-work-local-shared": "Puoi anche provare ad usare la [[Special:Upload|pagina di caricamento su {{SITENAME}}]], se il sito consente il caricamento di questo file in base alle sue politiche.",
-       "foreign-structured-upload-form-2-label-intro": "Grazie per aver donato un'immagine da poter usare su {{SITENAME}}. Dovresti continuare solo se soddisfa alcune condizioni:",
-       "foreign-structured-upload-form-2-label-ownwork": "Deve essere interamente <strong>una tua creazione</strong>, non semplicemente prese da Internet",
-       "foreign-structured-upload-form-2-label-noderiv": "Non deve contenere <strong>alcun lavoro da parte di chiunque altro</strong>, o ispirato da alcuno",
-       "foreign-structured-upload-form-2-label-useful": "Dovrebbe essere <strong>educativo e utile</strong> per insegnare agli altri",
-       "foreign-structured-upload-form-2-label-ccbysa": "Devi accettare di <strong>pubblicare irrevocabilmente</strong> su internet in base alla licenza [https://creativecommons.org/licenses/by-sa/4.0/deed.it Creative Commons Attribuzione-Condividi allo stesso modo 4.0]",
-       "foreign-structured-upload-form-2-label-alternative": "Se non tutte le precedenti condizioni sono vere, puoi comunque caricare questo file usando il [https://commons.wikimedia.org/wiki/Special:UploadWizard caricamento guidato su Commons], a patto che sia disponibile con una licenza libera.",
-       "foreign-structured-upload-form-2-label-termsofuse": "Caricando questo file, affermi che possiedi il copyright su questo file, e accetti irrevocabilmente di rilasciare questo file su Wikimedia Commons il base alla licenza Creative Commons Attribuzione-Condividi allo stesso modo 4.0, ed accetti inoltre le [https://wikimediafoundation.org/wiki/Terms_of_Use condizioni d'uso].",
-       "foreign-structured-upload-form-3-label-question-website": "Hai scaricato questa immagine da un sito web, o l'hai presa da un motore di ricerca per immagini?",
-       "foreign-structured-upload-form-3-label-question-ownwork": "Hai creato tu questa immagine (scattato la foto, creato il disegno, ecc.)?",
-       "foreign-structured-upload-form-3-label-question-noderiv": "Contiene, o è ispirato, da opere di altri, come un logo?",
-       "foreign-structured-upload-form-3-label-yes": "Sì",
-       "foreign-structured-upload-form-3-label-no": "No",
-       "foreign-structured-upload-form-3-label-alternative": "Purtroppo, in questo caso, questo strumento non supporta il caricamento di questo file. Puoi ancora caricarlo usando il [https://commons.wikimedia.org/wiki/Special:UploadWizard caricamento guidato su Commons], a patto che sia disponibile con una licenza libera.",
-       "foreign-structured-upload-form-4-label-good": "Usando questo strumento, puoi caricare grafici educativi che hai creato e fotografie che hai scattato, e che non contengono lavoro di proprietà di qualcun altro.",
-       "foreign-structured-upload-form-4-label-bad": "Non puoi caricare immagini trovate su un motore di ricerca o scaricate da un altro sito web.",
        "backend-fail-stream": "Impossibile trasmettere il file $1.",
        "backend-fail-backup": "Impossibile eseguire il backup del file $1 .",
        "backend-fail-notexists": "Il file $1 non esiste.",
        "querypage-disabled": "Questa pagina speciale è disattivata per motivi di prestazioni.",
        "apihelp": "Aiuto API",
        "apihelp-no-such-module": "Modulo \"$1\" non trovato.",
+       "apisandbox": "Pagina di prova API",
+       "apisandbox-jsonly": "È richiesto JavaScript per utilizzare la sandbox API.",
+       "apisandbox-api-disabled": "Le funzionalità API sono disabilitate su questo sito.",
+       "apisandbox-intro": "Utilizza questa pagina per sperimentare con le <strong>API web service MediaWiki</strong>.\nPer ulteriori dettagli di utilizzo delle API, fai riferimento alla [[mw:API:Main page|documentazione API]]. Esempio: [//www.mediawiki.org/wiki/API#A_simple_example ottenere il contenuto della pagina principale]. Seleziona un'azione per vedere altri esempi.\n\nNota che, anche se questa è una pagina per le prove, le azioni che esegui qui potrebbero modificare il wiki.",
+       "apisandbox-fullscreen": "Espandi pannello",
+       "apisandbox-fullscreen-tooltip": "Espandi il pannello sandbox per riempire la finestra del browser.",
+       "apisandbox-unfullscreen": "Mostra la pagina",
+       "apisandbox-unfullscreen-tooltip": "Riduci il pannello sandbox, così che i collegamenti di navigazione MediaWiki siano disponibili.",
+       "apisandbox-submit": "Inoltra richiesta",
+       "apisandbox-reset": "Pulisci",
+       "apisandbox-retry": "Riprova",
+       "apisandbox-loading": "Caricamento delle informazioni per il modulo API \"$1\"...",
+       "apisandbox-load-error": "Si è verificato un errore durante il caricamento delle informazioni per il modulo API \"$1\": $2",
+       "apisandbox-no-parameters": "Questo modulo API non ha parametri.",
+       "apisandbox-helpurls": "Collegamenti alla guida",
+       "apisandbox-examples": "Esempi",
+       "apisandbox-dynamic-parameters": "Parametri aggiuntivi",
+       "apisandbox-dynamic-parameters-add-label": "Aggiungi parametro:",
+       "apisandbox-dynamic-parameters-add-placeholder": "Nome del parametro",
+       "apisandbox-dynamic-error-exists": "Un parametro denominato \"$1\" esiste già.",
+       "apisandbox-deprecated-parameters": "Parametri sconsigliati",
+       "apisandbox-fetch-token": "Auto-compila il token",
+       "apisandbox-submit-invalid-fields-title": "Alcuni campi non sono validi",
+       "apisandbox-submit-invalid-fields-message": "Correggi i campi evidenziati e riprova.",
+       "apisandbox-results": "Risultati",
+       "apisandbox-sending-request": "Invio richiesta di API...",
+       "apisandbox-loading-results": "Ricezione dei risultati di API in corso...",
+       "apisandbox-results-error": "Si è verificato un errore durante il caricamento della risposta all'interrogazione API: $1",
+       "apisandbox-request-url-label": "URL di richiesta:",
+       "apisandbox-request-time": "Tempo richiesto: {{PLURAL:$1|$1 ms}}",
+       "apisandbox-results-fixtoken": "Correggi token e reinvia",
+       "apisandbox-results-fixtoken-fail": "Impossibile recuperare il token \"$1\".",
+       "apisandbox-alert-page": "I campi su questa pagina non sono validi.",
+       "apisandbox-alert-field": "Il valore di questo campo non è valido.",
        "booksources": "Fonti librarie",
        "booksources-search-legend": "Ricerca di fonti librarie",
        "booksources-isbn": "Codice ISBN:",
        "import-nonewrevisions": "Nessuna versione importata (erano già tutte presenti, o saltate a causa di errori)",
        "xml-error-string": "$1 a riga $2, colonna $3 (byte $4): $5",
        "import-upload": "Carica dati XML",
-       "import-token-mismatch": "I dati relativi alla sessione sono andati persi. Riprovare.",
+       "import-token-mismatch": "I dati relativi alla sessione sono andati persi. Riprovare.\n\nPotresti essere stato disconnesso. <strong>Verifica che sei ancora collegato e riprova</strong>.\nSe il problema persiste, si può tentare di [[Special:UserLogout|scollegarsi]] ed effettuare un nuovo accesso, e controllare che il tuo browser accetti i cookie da questo sito.",
        "import-invalid-interwiki": "Impossibile importare dal progetto wiki indicato.",
        "import-error-edit": "La pagina \"$1\" non è stata importata poiché non sei autorizzato a modificarla.",
        "import-error-create": "La pagina \"$1\" non è stata importata poiché non sei autorizzato a crearla.",
        "expand_templates_generate_xml": "Mostra albero sintattico XML",
        "expand_templates_generate_rawhtml": "Mostra HTML",
        "expand_templates_preview": "Anteprima",
-       "expand_templates_preview_fail_html": "<em>Poiché {{SITENAME}} ha dell'HTML grezzo attivato e c'è stata una perdita dei dati della sessione, l'anteprima è nascosta come precauzione contro gli attacchi JavaScript.</em>\n\n<strong>Se si tratta di un normale tentativo d'anteprima, riprova.</strong> \nSe comunque non dovesse funzionare, prova ad [[Special:UserLogout|uscire]] ed a rientrare.",
+       "expand_templates_preview_fail_html": "<em>Poiché {{SITENAME}} ha dell'HTML grezzo attivato e c'è stata una perdita dei dati della sessione, l'anteprima è nascosta come precauzione contro gli attacchi JavaScript.</em>\n\n<strong>Se si tratta di un normale tentativo d'anteprima, riprova.</strong> \nSe il problema persiste, si può tentare di [[Special:UserLogout|scollegarsi]] ed effettuare un nuovo accesso, e controllare che il tuo browser accetti i cookie da questo sito.",
        "expand_templates_preview_fail_html_anon": "<em>Poiché {{SITENAME}} ha dell'HTML grezzo attivato e non hai effettuato l'accesso, l'anteprima è nascosta come precauzione contro gli attacchi JavaScript.</em>\n\n<strong>Se si tratta di un normale tentativo d'anteprima, [[Special:UserLogin|entra]] e riprova.</strong>",
        "expand_templates_input_missing": "Devi inserire del testo come input.",
        "pagelanguage": "Modifica lingua della pagina",
index b8293a2..1615403 100644 (file)
@@ -76,7 +76,7 @@
        "tog-hideminor": "最近の更新に細部の編集を表示しない",
        "tog-hidepatrolled": "最近の更新に巡回済みの編集を表示しない",
        "tog-newpageshidepatrolled": "新しいページの一覧に、巡回済みのページを表示しない",
-       "tog-hidecategorization": "ページのカテゴリ化を隠す",
+       "tog-hidecategorization": "ページのカテゴリ追加・除去を表示しない",
        "tog-extendwatchlist": "ウォッチリストを拡張し、最新のものだけではなくすべての変更を表示",
        "tog-usenewrc": "最近の更新とウォッチリストで、複数の変更をページごとにまとめる",
        "tog-numberheadings": "見出しに番号を自動的に振る",
@@ -87,7 +87,7 @@
        "tog-watchdefault": "自分が編集したページやファイルをウォッチリストに追加",
        "tog-watchmoves": "自分が移動したページやファイルをウォッチリストに追加",
        "tog-watchdeletion": "自分が削除したページやファイルをウォッチリストに追加",
-       "tog-watchrollback": "è\87ªå\88\86ã\81\8cå·»ã\81\8dæ\88»ã\81\97ã\81\9fã\83\9aã\83¼ã\82¸ã\82\92ã\80\81ã\82¦ã\82©ã\83\83ã\83\81ã\83ªã\82¹ã\83\88ã\81«è¿½å\8a ",
+       "tog-watchrollback": "自分が巻き戻したページをウォッチリストに追加",
        "tog-minordefault": "編集をすべて既定で細部の編集とする",
        "tog-previewontop": "プレビューを編集ボックスの前に配置",
        "tog-previewonfirst": "編集開始時にもプレビューを表示",
        "tog-oldsig": "既存の署名:",
        "tog-fancysig": "署名をウィキ文として扱う (自動リンクなし)",
        "tog-uselivepreview": "ライブプレビューを使用",
-       "tog-forceeditsummary": "要約欄が空欄の場合に確認をうながす",
+       "tog-forceeditsummary": "要約欄が空欄の場合に確認をす",
        "tog-watchlisthideown": "自分の編集をウォッチリストに表示しない",
        "tog-watchlisthidebots": "ボットによる編集をウォッチリストに表示しない",
        "tog-watchlisthideminor": "細部の編集をウォッチリストに表示しない",
        "tog-watchlisthideliu": "ログイン利用者による編集をウォッチリストに表示しない",
-       "tog-watchlistreloadautomatically": "フィルタが変更されるたびに、ウォッチリストを自動的に再読み込みする(JavaScript が必要)",
+       "tog-watchlistreloadautomatically": "フィルタが変更されるたびに、ウォッチリストを自動的に再読み込みする (JavaScript が必要)",
        "tog-watchlisthideanons": "匿名利用者による編集をウォッチリストに表示しない",
        "tog-watchlisthidepatrolled": "巡回済みの編集をウォッチリストに表示しない",
-       "tog-watchlisthidecategorization": "ページのカテゴリ化を隠す",
+       "tog-watchlisthidecategorization": "ページのカテゴリ追加・除去を表示しない",
        "tog-ccmeonemails": "他の利用者に送信したメールの控えを自分にも送信",
        "tog-diffonly": "差分の下にページ内容を表示しない",
        "tog-showhiddencats": "隠しカテゴリを表示",
        "morenotlisted": "この一覧は完全ではありません。",
        "mypage": "ページ",
        "mytalk": "トーク",
-       "anontalk": "議論",
+       "anontalk": "トーク",
        "navigation": "案内",
        "and": "&#32;と",
        "qbfind": "検索",
        "cannotdelete-title": "「$1」というページを削除できません",
        "delete-hook-aborted": "フックによって削除が中止されました。\n理由は不明です。",
        "no-null-revision": "ページ「$1」に新しい空編集の版を作成できませんでした。",
-       "badtitle": "正しくないページ名",
+       "badtitle": "不適切なページ名",
        "badtitletext": "無効または空のページ名が指定されたか、言語間/ウィキ間リンクの方法に誤りがあります。\nページ名に使用できない文字が含まれている可能性があります。",
        "title-invalid-empty": "指定されたページ名は空であるか、もしくは名前空間しか含んでいません。",
        "title-invalid-utf8": "指定されたページ名が無効なUTF-8シーケンスを含んでいます。",
        "userlogin-remembermypassword": "ログイン状態を保持",
        "userlogin-signwithsecure": "安全な接続の使用",
        "cannotloginnow-title": "今はログインできません",
+       "cannotloginnow-text": "$1 使用中には、ログインは不可能です。",
        "yourdomainname": "あなたのドメイン:",
        "password-change-forbidden": "このウィキではパスワードを変更できません。",
        "externaldberror": "認証データベースでエラーが発生したか、または外部アカウントの更新が許可されていません。",
        "botpasswords-disabled": "ボットのパスワードは無効です。",
        "botpasswords-existing": "既存のボットのパスワード",
        "botpasswords-createnew": "ボットのパスワードの新規作成",
+       "botpasswords-editexisting": "既存のボットのパスワードを編集",
        "botpasswords-label-appid": "ボット名:",
        "botpasswords-label-create": "作成",
        "botpasswords-label-update": "更新",
        "botpasswords-label-cancel": "中止",
        "botpasswords-label-delete": "削除",
        "botpasswords-label-resetpassword": "パスワードをリセット",
+       "botpasswords-label-restrictions": "使用制限:",
        "botpasswords-bad-appid": "ボット「$1」は有効ではありません。",
        "botpasswords-insert-failed": "ボット「$1」の追加に失敗しました。既に追加されていないか確認してください。",
        "botpasswords-update-failed": "ボット「$1」の更新に失敗しました。削除されていないか確認してください。",
        "hr_tip": "水平線を挿入 (利用は控えめに)",
        "summary": "編集内容の要約:",
        "subject": "題名:",
-       "minoredit": "これは細部の編集です",
+       "minoredit": "細部の編集",
        "watchthis": "このページをウォッチ",
        "savearticle": "ページを保存",
        "preview": "プレビュー",
        "newarticletext": "まだ存在しないページへのリンクをたどりました。\nこのページを新規作成するには、ページの内容を以下のボックスに記入してください (詳しくは[$1 ヘルプページ]を参照してください)。\n誤ってこのページにたどり着いた場合には、ブラウザーの<strong>戻る</strong>ボタンで前のページに戻ってください。",
        "anontalkpagetext": "----\n<em>このページはアカウントをまだ作成していないか使用していない匿名利用者のための議論ページです。</em>\n\n匿名利用者を識別するために、利用者名の代わりにIPアドレスが使用されています。IP アドレスは複数の利用者で共有されている場合があります。もし、あなたが匿名利用者であり、自分に関係のないコメントが寄せられていると考えられる場合は、[[Special:UserLogin/signup|アカウントを作成する]]か[[Special:UserLogin|ログインして]]他の匿名利用者と間違えられないようにしてください。",
        "noarticletext": "現在このページには内容がありません。\n他のページ内で[[Special:Search/{{PAGENAME}}|このページ名を検索]]、\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} 関連する記録を検索]、\nまたは[{{fullurl:{{FULLPAGENAME}}|action=edit}} このページを編集]</span>できます。",
-       "noarticletext-nopermission": "ç\8f¾å\9c¨ã\81\93ã\81®ã\83\9aã\83¼ã\82¸ã\81«ã\81¯å\86\85容ã\81\8cã\81\82ã\82\8aã\81¾ã\81\9bã\82\93ã\80\82\nä»\96ã\81®ã\83\9aã\83¼ã\82¸å\86\85ã\81§[[Special:Search/{{PAGENAME}}|ã\81\93ã\81®ã\83\9aã\83¼ã\82¸å\90\8dã\82\92æ¤\9cç´¢]]ã\80\81ã\81¾ã\81\9fã\81¯<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} é\96¢é\80£ã\81\99ã\82\8bè¨\98é\8c²ã\82\92æ¤\9cç´¢]</span>ã\81§ã\81\8dã\81¾ã\81\99ã\81\8cã\80\81ã\81\82ã\81ªã\81\9fã\81«ã\81¯ã\81\93ã\81®ã\83\9aã\83¼ã\82¸ã\82\92ä½\9cæ\88\90ã\81\99ã\82\8b権é\99\90ã\81¯ありません。",
+       "noarticletext-nopermission": "ç\8f¾å\9c¨ã\81\93ã\81®ã\83\9aã\83¼ã\82¸ã\81«ã\81¯å\86\85容ã\81\8cã\81\82ã\82\8aã\81¾ã\81\9bã\82\93ã\80\82\nä»\96ã\81®ã\83\9aã\83¼ã\82¸å\86\85ã\81§[[Special:Search/{{PAGENAME}}|ã\81\93ã\81®ã\83\9aã\83¼ã\82¸å\90\8dã\82\92æ¤\9cç´¢]]ã\80\81ã\81¾ã\81\9fã\81¯<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} é\96¢é\80£ã\81\99ã\82\8bè¨\98é\8c²ã\82\92æ¤\9cç´¢]</span>ã\81§ã\81\8dã\81¾ã\81\99ã\81\8cã\80\81ã\81\82ã\81ªã\81\9fã\81«ã\81¯ã\81\93ã\81®ã\83\9aã\83¼ã\82¸ã\82\92ä½\9cæ\88\90ã\81\99ã\82\8b権é\99\90ã\81\8cありません。",
        "missing-revision": "「{{FULLPAGENAME}}」というページの版番号 $1 の版は存在しません。\n\n通常、削除されたページの版への古い差分表示や固定リンクをたどった際に、このようなことが起きます。 \n詳細は[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} 削除記録]を参照してください。",
        "userpage-userdoesnotexist": "「$1」という利用者アカウントは登録されていません。\nこのページの作成/編集が適切かご確認ください。",
        "userpage-userdoesnotexist-view": "利用者アカウント「$1」は登録されていません。",
        "previewnote": "<strong>これはプレビューです。</strong>\n変更内容はまだ保存されていません!",
        "continue-editing": "編集を続行",
        "previewconflict": "これは、上の編集エリアの文章を保存した場合にどう表示されるかを示すプレビューです。",
-       "session_fail_preview": "<strong>申し訳ありません! セッションデータが消失したため編集を処理できませんでした。</strong>\nもう一度やり直してください。\nそれでも失敗する場合、[[Special:UserLogout|ログアウト]]してからログインし直してください。",
-       "session_fail_preview_html": "<strong>申し訳ありません! セッション データが消失したため編集を処理できませんでした。</strong>\n\n<em>{{SITENAME}}では生のHTMLが有効であり、JavaScriptでの攻撃を予防するためにプレビューを表示していません。</em>\n\n<strong>この編集が問題ない場合はもう一度保存してください。</strong>\nそれでもうまくいかない場合は一度[[Special:UserLogout|ログアウト]]して、ログインし直してみてください。",
+       "session_fail_preview": "申し訳ありません! セッションデータが消失したため編集を処理できませんでした。\n\nアカウントがログアウトされている可能性があります。<strong>アカウントにログインしていることを確認して、もう一度やり直してください</strong>。\nそれでも失敗する場合、[[Special:UserLogout|ログアウト]]してからログインし直し、現在使用しているブラウザでこのサイトからのクッキーが許可されていることを確認してください。",
+       "session_fail_preview_html": "申し訳ありません! セッション データが消失したため編集を処理できませんでした。\n\n<em>{{SITENAME}}では生のHTMLが有効であり、JavaScriptでの攻撃を予防するためにプレビューを表示していません。</em>\n\n<strong>この編集が問題ない場合はもう一度保存してください。</strong>\nそれでも失敗する場合、[[Special:UserLogout|ログアウト]]してからログインし直し、現在使用しているブラウザでこのサイトからのクッキーが許可されていることを確認してください。",
        "token_suffix_mismatch": "<strong>ご使用中のクライアントが編集トークン内の句読点を正しく処理していないため、編集を受け付けられません。</strong>\nページ本文の破損を防ぐため、編集は反映されません。\n問題のある匿名プロキシ サービスを使用していると、これが発生する場合があります。",
        "edit_form_incomplete": "<strong>編集フォームの一部がサーバーに届きませんでした。ご確認の上、そのまま再度投稿してください。</strong>",
        "editing": "「$1」を編集中",
        "sectioneditnotsupported-title": "節単位編集はサポートされていません",
        "sectioneditnotsupported-text": "このページでは節単位編集はサポートされません。",
        "permissionserrors": "権限エラー",
-       "permissionserrorstext": "ã\81\82ã\81ªã\81\9fã\81«ã\81¯ã\81\93ã\81®æ\93\8dä½\9cã\82\92è¡\8cã\81\86権é\99\90ã\81¯ありません。{{PLURAL:$1|理由}}は以下の通りです:",
-       "permissionserrorstext-withaction": "ã\81\82ã\81ªã\81\9fã\81«ã\81¯ã\80\8c$2ã\80\8dã\82\92è¡\8cã\81\86権é\99\90ã\81¯ありません。{{PLURAL:$1|理由}}は以下の通りです:",
+       "permissionserrorstext": "ã\81\82ã\81ªã\81\9fã\81«ã\81¯ã\81\93ã\81®æ\93\8dä½\9cã\82\92è¡\8cã\81\86権é\99\90ã\81\8cありません。{{PLURAL:$1|理由}}は以下の通りです:",
+       "permissionserrorstext-withaction": "ã\81\82ã\81ªã\81\9fã\81«ã\81¯ã\80\8c$2ã\80\8dã\82\92è¡\8cã\81\86権é\99\90ã\81\8cありません。{{PLURAL:$1|理由}}は以下の通りです:",
        "contentmodelediterror": "コンテンツモデルが <code>$1</code> であるため、この版を編集することができません。ページの現在のコンテンツモデルは <code>$2</code> です。",
        "recreate-moveddeleted-warn": "<strong>警告: 以前削除されたページを再作成しようとしています。</strong>\n\nこのページの編集を続行するのが適切かどうかご確認ください。\n参考までに、このページの削除と移動の記録を以下に示します:",
        "moveddeleted-notice": "このページは削除されています。\n参考のため、このページの削除と移動の記録を以下に表示します。",
        "mergehistory-empty": "統合できる版がありません。",
        "mergehistory-done": "$1の $3 {{PLURAL:$3|版}}は[[:$2]]に統合されました。",
        "mergehistory-fail": "履歴の統合を実行できません。ページと時刻の引数を再確認してください。",
+       "mergehistory-fail-bad-timestamp": "タイムスタンプが無効です。",
+       "mergehistory-fail-invalid-source": "統合元のページが無効です。",
+       "mergehistory-fail-invalid-dest": "統合先のページが無効です。",
+       "mergehistory-fail-no-change": "履歴の統合はどの版でも実行されませんでした。ページと時間のパラメーターを再度確認してください。",
+       "mergehistory-fail-permission": "履歴の統合を実行するのに十分な権限がありません。",
+       "mergehistory-fail-self-merge": "統合元と統合先のページを同じにすることはできません。",
+       "mergehistory-fail-timestamps-overlap": "統合元の版を同じにしたり、統合先の版よりも後にすることはできません。",
        "mergehistory-fail-toobig": "移動させた{{PLURAL:$1|版}}の数が上限を超えているため、履歴の統合を実行できません。",
        "mergehistory-no-source": "統合元ページ $1 が存在しません。",
        "mergehistory-no-destination": "統合先ページ $1 が存在しません。",
        "right-bot": "自動処理と認識させる",
        "right-nominornewtalk": "議論ページの細部の編集をした際に、新着メッセージとして通知しない",
        "right-apihighlimits": "API要求でより高い制限値を使用",
-       "right-writeapi": "書き込みAPIを使用",
+       "right-writeapi": "書き込み API を使用",
        "right-delete": "ページを削除",
        "right-bigdelete": "大きな履歴があるページを削除",
        "right-deletelogentry": "特定の記録項目を削除/復元",
        "grant-group-email": "メールの送信",
        "grant-group-customization": "カスタマイズと個人設定",
        "grant-group-other": "その他の活動",
-       "grant-blockusers": "利用者をブロック/ブロック解除",
+       "grant-blockusers": "利用者をブロックおよびブロック解除",
        "grant-createaccount": "アカウントを作成",
        "grant-createeditmovepage": "ページを作成、編集、および移動",
        "grant-delete": "ページ、版、記録項目を削除",
        "grant-editinterface": "MediaWiki 名前空間および利用者 CSS/JavaScript を編集",
        "grant-editmycssjs": "あなた自身の利用者 CSS/JavaScript を編集",
        "grant-editmyoptions": "あなたの個人設定を編集",
-       "grant-editmywatchlist": "自身のウォッチリストを編集",
+       "grant-editmywatchlist": "あなたのウォッチリストを編集",
        "grant-editpage": "既存のページを編集",
        "grant-editprotected": "保護されたページを編集",
        "grant-oversight": "利用者名および版を秘匿",
        "recentchanges-label-minor": "細部の編集",
        "recentchanges-label-bot": "ボットによる編集",
        "recentchanges-label-unpatrolled": "巡回されていない編集",
-       "recentchanges-label-plusminus": "ページ サイズの増減 (バイト単位)",
+       "recentchanges-label-plusminus": "ページサイズの増減 (バイト単位)",
        "recentchanges-legend-heading": "'''凡例:'''",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} ([[Special:NewPages|新しいページ一覧]]も参照)",
        "recentchanges-legend-plusminus": "(<em>±123</em>)",
        "rcshowhidemine": "自分の編集を$1",
        "rcshowhidemine-show": "表示",
        "rcshowhidemine-hide": "非表示",
-       "rcshowhidecategorization": "ページ カテゴリ化を$1",
+       "rcshowhidecategorization": "ページのカテゴリー追加・除去を$1",
        "rcshowhidecategorization-show": "表示",
        "rcshowhidecategorization-hide": "非表示",
        "rclinks": "最近 $2 日間の更新を最大 $1 件表示<br />$3",
        "recentchangeslinked-summary": "これは指定したページからリンクされている (または指定したカテゴリに含まれている) ページの最近の変更の一覧です。\n[[Special:Watchlist|自分のウォッチリスト]]にあるページは<strong>太字</strong>で表示されます。",
        "recentchangeslinked-page": "ページ名:",
        "recentchangeslinked-to": "このページへのリンク元での変更の表示に切り替え",
-       "recentchanges-page-added-to-category": "[[:$1]] カテゴリに追加",
-       "recentchanges-page-added-to-category-bundled": "[[:$1]]と他{{PLURAL:$2|1ページ|$2ページ}}をカテゴリに追加しました",
-       "recentchanges-page-removed-from-category": "[[:$1]] カテゴリから削除",
-       "recentchanges-page-removed-from-category-bundled": "[[:$1]]と他{{PLURAL:$2|1ページ|$2ページ}}をカテゴリから削除しました",
+       "recentchanges-page-added-to-category": "[[:$1]]カテゴリに追加",
+       "recentchanges-page-added-to-category-bundled": "[[:$1]]と他{{PLURAL:$2|1ページ|$2ページ}}をカテゴリに追加",
+       "recentchanges-page-removed-from-category": "[[:$1]]をカテゴリから除外",
+       "recentchanges-page-removed-from-category-bundled": "[[:$1]]と他{{PLURAL:$2|1ページ|$2ページ}}をカテゴリから除外",
        "autochange-username": "メディアウィキ自動変更",
        "upload": "ファイルをアップロード",
        "uploadbtn": "ファイルをアップロード",
        "uploaded-script-svg": "アップロードされたSVGファイルにスクリプト可能な要素「$1」が見つかりました。",
        "uploaded-hostile-svg": "アップロードされたSVGファイルのスタイル要素に安全ではないCSSが見つかりました。",
        "uploaded-event-handler-on-svg": "イベントハンドラをセットする属性 <code>$1=\"$2\"</code> は、SVGファイルを許可されていません。",
-       "uploaded-href-unsafe-target-svg": "アップロードされたSVGファイルに、安全ではないターゲット <code>&lt;$1 $2=\"$3\"&gt;</code> の href が見つかりました。",
+       "uploaded-href-attribute-svg": "SVG ファイルの href 属性が http:// または https:// のターゲットのみにリンクする <code>&lt;$1 $2=\"$3\"&gt;</code> が見つかりました。",
+       "uploaded-href-unsafe-target-svg": "アップロードされた SVG ファイルの、安全ではないデータ URI にターゲット <code>&lt;$1 $2=\"$3\"&gt;</code> の href が見つかりました。",
        "uploaded-animate-svg": "アップロードされたSVGファイルに、「from」属性 <code>&lt;$1 $2=\"$3\"&gt;</code> を使用した、href を変更させる可能性がある「animate」タグが見つかりました。",
        "uploaded-setting-event-handler-svg": "アップロードされたSVGファイルに、ブロックされているイベントハンドラ属性が設定された <code>&lt;$1 $2=\"$3\"&gt;</code> が見つかりました。",
        "uploaded-setting-href-svg": "親要素に「href」属性を追加する「set」タグの使用がブロックされています。",
        "upload-dialog-button-done": "完了",
        "upload-dialog-button-save": "保存",
        "upload-dialog-button-upload": "アップロード",
-       "upload-form-label-select-file": "ファイル選択",
        "upload-form-label-infoform-title": "詳細",
        "upload-form-label-infoform-name": "名前",
        "upload-form-label-infoform-name-tooltip": "ファイル固有の説明的な表題。ファイル名として使われます。平易な言葉を使い、空白を入れることができます。拡張子は含めないでください。",
        "foreign-structured-upload-form-label-own-work-message-shared": "私は、このファイルの著作権を所有していることを宣誓し、取消し不能な形で  [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0] ライセンスのもとでウィキメディア・コモンズに、このファイルを解放することに同意します。そして私は、  [https://wikimediafoundation.org/wiki/Terms_of_Use Terms of Use] に同意します。",
        "foreign-structured-upload-form-label-not-own-work-message-shared": "このファイルの著作権を所有していない場合、または別のライセンスの下でそれをリリースしたい場合には、 [https://commons.wikimedia.org/wiki/Special:UploadWizard Commons Upload Wizard] を使用することを検討してください。",
        "foreign-structured-upload-form-label-not-own-work-local-shared": "もしサイトが、それらの方針の下にて、このファイルのアップロードを許可している場合は、[[Special:Upload|{{SITENAME}}上でのアップロードページ]]の利用も検討できます。",
-       "foreign-structured-upload-form-3-label-yes": "はい",
-       "foreign-structured-upload-form-3-label-no": "いいえ",
        "backend-fail-stream": "ファイル $1 をストリームできませんでした。",
        "backend-fail-backup": "ファイル $1 をバックアップできませんでした。",
        "backend-fail-notexists": "ファイル $1 は存在しません。",
        "shared-repo": "共有リポジトリ",
        "shared-repo-name-wikimediacommons": "ウィキメディア・コモンズ",
        "filepage.css": "/* ここに記述したCSSはファイル解説ページにて読み込まれます。また外部のクライアントウィキにも影響します */",
-       "upload-disallowed-here": "ã\81\82ã\81ªã\81\9fã\81¯ã\81\93ã\81®ã\83\95ã\82¡ã\82¤ã\83«ã\82\92上書きできません。",
+       "upload-disallowed-here": "ã\81\93ã\81®ã\83\95ã\82¡ã\82¤ã\83«ã\81¯上書きできません。",
        "filerevert": "$1を差し戻す",
        "filerevert-legend": "ファイルを差し戻す",
        "filerevert-intro": "ファイル<strong>[[Media:$1|$1]]</strong>を[$4 $2$3版]に差し戻そうとしています。",
        "querypage-disabled": "パフォーマンスに悪影響を与えるおそれがあるため、この特別ページは無効になっています。",
        "apihelp": "API のヘルプ",
        "apihelp-no-such-module": "モジュール「$1」が見つかりません。",
+       "apisandbox": "APIサンドボックス",
+       "apisandbox-api-disabled": "このウェブサイトでは、API は無効になっています。",
+       "apisandbox-intro": "このページでは、<strong>MediaWiki ウェブサービス API</strong> を試用できます。\nAPI の使用方法の詳細は[[mw:API:Main page|API のドキュメント]]をご覧ください。例: [//www.mediawiki.org/wiki/API#A_simple_example Main Pageの内容を取得]。操作を選択すると他の例を閲覧できます。\n\nこれはサンドボックスですが、このページで実行した操作によってウィキが変更される場合があることにご注意ください。",
+       "apisandbox-fullscreen": "パネルを展開",
+       "apisandbox-unfullscreen": "ページを表示",
+       "apisandbox-submit": "リクエストする",
+       "apisandbox-reset": "消去",
+       "apisandbox-retry": "再試行",
+       "apisandbox-no-parameters": "この API モジュールにはパラメーターがありません。",
+       "apisandbox-helpurls": "ヘルプリンク",
+       "apisandbox-examples": "例",
+       "apisandbox-dynamic-parameters-add-placeholder": "引数名",
+       "apisandbox-results": "結果",
+       "apisandbox-request-url-label": "リクエスト URL:",
+       "apisandbox-request-time": "リクエスト時間: {{PLURAL:$1|$1ミリ秒}}",
        "booksources": "書籍情報源",
        "booksources-search-legend": "書籍情報源を検索",
        "booksources-isbn": "ISBN:",
        "wlshowhideanons": "IP利用者",
        "wlshowhidepatr": "巡回された編集",
        "wlshowhidemine": "自分の編集",
-       "wlshowhidecategorization": "ページのカテゴリ",
+       "wlshowhidecategorization": "ページのカテゴリー追加・除去",
        "watchlist-options": "ウォッチリストのオプション",
        "watching": "ウォッチリストに追加中...",
        "unwatching": "ウォッチリストから除去中...",
        "import-nonewrevisions": "版のインポートはされませんでした(すべての版が以前に取り込み済みだったか、エラーにより飛ばされたため)。",
        "xml-error-string": "$1、$2 行の $3 文字目 ($4バイト目): $5",
        "import-upload": "XMLデータをアップロード",
-       "import-token-mismatch": "セッションデータを損失しました。\nもう一度試してください。",
+       "import-token-mismatch": "セッションデータを損失しました。\n\nアカウントがログアウトされている可能性があります。<strong>アカウントにログインしていることを確認して、もう一度やり直してください</strong>。\nそれでも失敗する場合、[[Special:UserLogout|ログアウト]]してからログインし直し、現在使用しているブラウザでこのサイトからのクッキーが許可されていることを確認してください。",
        "import-invalid-interwiki": "指定されたウィキから取り込めませんでした。",
        "import-error-edit": "あなたにそのページを編集する許可がないため、ページ「$1」は取り込まれませんでした。",
        "import-error-create": "あなたにそのページを作成する許可がないため、ページ「$1」は取り込まれませんでした。",
        "tooltip-ca-nstab-category": "カテゴリページを閲覧",
        "tooltip-minoredit": "この編集に細部の変更の印を付ける",
        "tooltip-save": "変更を保存する",
-       "tooltip-preview": "変更内容をプレビューで確認できます。保存前に使用してください!",
+       "tooltip-preview": "変更内容をプレビューで確認できます。保存前に使用してください",
        "tooltip-diff": "文章への変更箇所を表示する",
        "tooltip-compareselectedversions": "選択した2つの版の差分を表示する",
        "tooltip-watch": "このページをウォッチリストに追加する",
        "filedelete-old-unregistered": "指定されたファイルの版「$1」はデータベース内にありません。",
        "filedelete-current-unregistered": "指定されたファイル「$1」はデータベース内にありません。",
        "filedelete-archive-read-only": "保存版ディレクトリ「$1」は、ウェブサーバーから書き込み不可になっています。",
-       "previousdiff": "←古い編集",
-       "nextdiff": "新しい編集→",
+       "previousdiff": "← 古い編集",
+       "nextdiff": "新しい編集 →",
        "mediawarning": "<strong>警告:</strong> この種類のファイルは、悪意があるコードを含んでいる可能性があります。\n実行するとシステムが危険にさらされるおそれがあります。",
        "imagemaxsize": "画像のサイズ制限: <br /><em>(ファイルページに対する)</em>",
        "thumbsize": "サムネイルの大きさ:",
        "tags-deactivate-reason": "理由:",
        "tags-deactivate-not-allowed": "タグ「$1」は無効化できません。",
        "tags-deactivate-submit": "無効化",
-       "tags-apply-no-permission": "あなたは変更と同時に変更タグを適応する権限がありません。",
+       "tags-apply-no-permission": "ã\81\82ã\81ªã\81\9fã\81«ã\81¯å¤\89æ\9b´ã\81¨å\90\8cæ\99\82ã\81«å¤\89æ\9b´ã\82¿ã\82°ã\82\92é\81©å¿\9cã\81\99ã\82\8b権é\99\90ã\81\8cã\81\82ã\82\8aã\81¾ã\81\9bã\82\93ã\80\82",
        "tags-apply-not-allowed-one": "タグ \"$1\" の手動適用は認められていません。",
        "tags-apply-not-allowed-multi": "以下の {{PLURAL:$2|タグ}} は手動適用が認められていません: $1",
-       "tags-update-no-permission": "ã\81\82ã\81ªã\81\9fã\81«ã\81¯å\80\8bã\80\85ã\81®ç\89\88ã\81¾ã\81\9fã\81¯è¨\98é\8c²é \85ç\9b®ã\81®ã\82¿ã\82°ã\81®è¿½å\8a ã\81¾ã\81\9fã\81¯é\99¤å\8e»ã\82\92è¡\8cã\81\86権é\99\90ã\81¯ありません。",
+       "tags-update-no-permission": "ã\81\82ã\81ªã\81\9fã\81«ã\81¯å\80\8bã\80\85ã\81®ç\89\88ã\81¾ã\81\9fã\81¯è¨\98é\8c²é \85ç\9b®ã\81®ã\82¿ã\82°ã\81®è¿½å\8a ã\81¾ã\81\9fã\81¯é\99¤å\8e»ã\82\92è¡\8cã\81\86権é\99\90ã\81\8cありません。",
        "tags-update-add-not-allowed-one": "タグ \"$1\" の手動追加は認められていません。",
        "tags-update-add-not-allowed-multi": "以下の {{PLURAL:$2|タグ}} は手動追加が認められていません: $1",
        "tags-update-remove-not-allowed-one": "タグ \"$1\" の除去は認められていません。",
        "logentry-suppress-block": "$1 が {{GENDER:$4|$3}} を$5で{{GENDER:$2|ブロックしました}} $6",
        "logentry-suppress-reblock": "$1 が {{GENDER:$4|$3}} のブロックの期限を$5に{{GENDER:$2|変更しました}} $6",
        "logentry-import-upload": "$1 がファイルをアップロードして $3 を{{GENDER:$2|インポートしました}}",
+       "logentry-import-upload-details": "$1 がファイルのアップロードにより、$3 を{{GENDER:$2|インポートしました}} ({{PLURAL:$4|版}} $4)",
        "logentry-import-interwiki": "$1 が他のウィキから $3 を{{GENDER:$2|インポートしました}}",
        "logentry-import-interwiki-details": "$1 が $3 を $5 から{{GENDER:$2|取り込み}}ました ($4 {{PLURAL:$4|版}})",
        "logentry-merge-merge": "$1{{GENDER:$2|統合元}} と$3を$4に統合(改訂版を$5に掲載)",
        "expand_templates_generate_xml": "XML 構文解析ツリーを表示",
        "expand_templates_generate_rawhtml": "HTML ソースを表示",
        "expand_templates_preview": "プレビュー",
-       "expand_templates_preview_fail_html": "<em>{{SITENAME}} ではHTMLソースが有効になっており、セッションデータの損失が生じているので、JavaScript の攻撃に対する予防措置としてプレビューは表示されません。</em>\n\n<strong>これが合法的なプレビューの試みである場合には、もう一度試してください。</strong>\nそれでも動作しない場合は、[[Special:UserLogout|ログアウト]]して再度ログインしてみてください。",
+       "expand_templates_preview_fail_html": "<em>{{SITENAME}} ではHTMLソースが有効になっており、セッションデータの損失が生じているので、JavaScript の攻撃に対する予防措置としてプレビューは表示されません。</em>\n\n<strong>これが合法的なプレビューの試みである場合には、もう一度試してください。</strong>\nそれでも動作しない場合は、[[Special:UserLogout|ログアウト]]してからログインし直し、現在使用しているブラウザでこのサイトからのクッキーが許可されていることを確認してください。",
        "expand_templates_preview_fail_html_anon": "<em>{{SITENAME}} ではHTMLソースが有効になっており、ログインしていないため、JavaScript の攻撃に対する予防措置としてプレビューは表示されません。</em>\n\n<strong>これが合法的なプレビューの試みである場合には、[[Special:UserLogin|ログイン]]してもう一度試してください。</strong>",
        "expand_templates_input_missing": "文章を入力してください。",
        "pagelanguage": "ページ言語の変更",
index 735e630..8cab5c3 100644 (file)
        "cancel": "Kiansl",
        "moredotdotdot": "Muo...",
        "mypage": "Fimi Piej",
-       "mytalk": "Mi chat",
+       "mytalk": "Taak",
        "anontalk": "Taak fi dis IP ajres",
        "navigation": "Navigieshan",
        "and": "&#32;ahn",
        "permalink": "Poermanint lingk",
        "print": "Print",
        "view": "Riid",
+       "view-foreign": "Vyuu pah $1",
        "edit": "Edit",
        "create": "Kriet",
+       "create-local": "Ad luokal diskripshan‎",
        "editthispage": "Edit dis piej",
        "create-this-page": "Kriet dis piej",
        "delete": "Diliit",
        "otherlanguages": "Ina ada langwij",
        "redirectedfrom": "(Riidirek frahn $1)",
        "redirectpagesub": "Riidirek piej",
+       "redirectto": "Ridirek tu:",
        "lastmodifiedat": "Dis piej laas madifai pahn $1, a $2",
        "viewcount": "Dis piej akses {{PLURAL:$1|wans|$1 taim}}.",
        "protectedpage": "Protek piej",
        "pool-queuefull": "Puul kyuu fulop",
        "pool-errorunknown": "Anuon era",
        "aboutsite": "Habowt {{SITENAME}}",
-       "aboutpage": "Project:About",
+       "aboutpage": "Project:Bout‎",
        "copyright": "Kantent avielobl anda $1.",
        "copyrightpage": "{{ns:project}}:Kapirait",
        "currentevents": "Korant ivent",
        "virus-unknownscanner": "anuon antivairos:",
        "logouttext": "'''Yu nou lag out.'''\n\nYu kiahn kantiniu yuuz {{SITENAME}} ananimosli, ar yu kiahn <span class='plainlinks'>[$1 lag iin agen]</span> az di siem ar az difrant yuuza.\nNuot se som piej maita kantiniu fi displie laik se yu stil log iin, antel yu klier yu brouza kiash.",
        "yourname": "Yuuzaniem:",
+       "userlogin-yourname": "Yuuzaniem",
+       "userlogin-yourname-ph": "Enta yu yuuzaniem",
        "yourpassword": "Paaswod:",
+       "userlogin-yourpassword": "Paaswod",
+       "userlogin-yourpassword-ph": "Enta yu paaswod‎",
+       "createacct-yourpassword-ph": "Enta paaswod",
        "yourpasswordagain": "Ritaip paaswod:",
+       "createacct-yourpasswordagain": "Kanfoerm paaswod",
+       "createacct-yourpasswordagain-ph": "Enta paaswod agen",
        "remembermypassword": "Memba mi lagiin pan dis brouza (fi a maximom a $1 {{PLURAL:$1|die|die}})",
+       "userlogin-remembermypassword": "Kip mi lagiin‎",
        "yourdomainname": "Yu domien:",
        "externaldberror": "Aida aatentikieshan dietabies era okor ar yu no lou fi opdiet yu extoernal akount.",
        "login": "Lag iin",
        "logout": "Lag out",
        "userlogout": "Lag out",
        "notloggedin": "No lag iin",
+       "userlogin-noaccount": "No gat no akount?‎",
+       "userlogin-joinproject": "Jain {{SITENAME}}‎",
        "nologin": "Naa no akount? $1.",
        "nologinlink": "Kriet a akount",
        "createaccount": "Kriet akount",
        "gotaccount": "Aredi gat akount? $1.",
        "gotaccountlink": "Lag iin",
        "userlogin-resetlink": "Figet yu lagin detail dem?",
+       "userlogin-resetpassword-link": "Figat yu paaswod?‎",
+       "userlogin-helplink2": "Elp wid lagiin‎",
+       "createacct-emailoptional": "Iimiel ajres (apshanal)",
+       "createacct-email-ph": "Enta yu iimiel ajres",
        "createaccountmail": "Bai e-miel",
        "createaccountreason": "Riizn:",
+       "createacct-submit": "Kriet yu akount",
+       "createacct-benefit-heading": "{{SITENAME}} mek bai smadi laka yu.",
+       "createacct-benefit-body1": "{{PLURAL:$1|edit|edits}}",
+       "createacct-benefit-body2": "{{PLURAL:$1|page|pages}}",
+       "createacct-benefit-body3": "riisent {{PLURAL:$1|kanchribiuta|kanchribiutadem}}",
        "badretype": "Di paaswod yu enta no mach.",
        "userexists": "Yuuza niem enta aredi a yuuz.\nBegyu chuuz wahn difrahn niem.",
        "loginerror": "Lagiin era",
        "loginlanguagelabel": "Langwij: $1",
        "suspicious-userlogout": "Yu rikwes fi lag out dinai bikaa iluk laik se isen bai a brok brouza ar kiashin praxi.",
        "pt-login": "Lagiin‎",
+       "pt-login-button": "Lagiin‎",
        "pt-createaccount": "Kriet akount‎",
+       "pt-userlogout": "Lag out",
        "resetpass_announce": "Yu lag iin wid a tempareri e-miel kuod.\nFi finish lag iin, yu mos set a nyuu paaswod yaso:",
        "resetpass_header": "Chienj akount paaswod",
        "oldpassword": "Uol paaswod:",
        "resetpass-submit-cancel": "Kiansl",
        "resetpass-wrong-oldpass": "Invalid ar tempareri paaswod.\nYu maita chienj yu paaswod soksesfuli aredi ar rikwes wahn nyuu tempareri paaswod.",
        "resetpass-temp-password": "Tempareri paaswod",
+       "passwordreset": "Riiset paaswod‎",
        "passwordreset-username": "Yuuzaniem:",
        "passwordreset-domain": "Domien:",
        "bold_sample": "Buol tex",
        "preview": "Priivyuu",
        "showpreview": "Shuo priivyuu",
        "showdiff": "Shuo chienjdem",
-       "anoneditwarning": "'''Waanin:''' Yu no lag iin.\nYu IP ajres wi rikaad ina dis piej edit ischri.",
+       "anoneditwarning": "<strong>Waanin:</strong> Yu no lagiin. Yu IP ajres wi vizibl tu poblik ef yu mek eni edit. Ef yu <strong>[$1 lagiin]</strong> ar <strong>[$2 kriet akount]</strong>, yu editdem wi get achribiut tu yu yuuzaniem, wid adaels benifit.‎",
        "anonpreviewwarning": "''Yu no lag iin. Sievin wi rikaad yu IP ajres ina dis piej edit ischri.''",
        "missingsummary": "'''Rimainda:''' Yu no provaid no edit somari.\nEf yu klik \"{{int:savearticle}}\" agen, yu edit wi siev widoutn wan.",
        "missingcommenttext": "Begyu enta a kament biluo.",
        "newarticletext": "Yu fala lingk tu piej we no egzis yet.\nFi kriet di piej, taat taip ina di bax biluo (si di [$1 elp piej] fi muo infamieshan).\nEf yu de ya by mistiek, klik yu brouza '''bak''' botn.",
        "anontalkpagetext": "----''Dis a di diskoshan piej fi ananimos yuuza uu no kriet no akount yet, ar uu no yuuzi.\nWi dierfuor afi yuuz di nyuumerikal IP ajres fi aidentifai im/ar.\nSoch a IP ajres kiahn shier bai sebral yuuza.\nEf yu a ananimos yuuza ahn fiil se irelivant kament dairek tu yu, begyu [[Special:UserLogin/signup|kriet a akount]] ar [[Special:UserLogin|lag iin]] fi avaid fyuucha kanfyuujan wid ada ananimos yuuza.''",
        "noarticletext": "Korentli no tex no de ina dis piej.\nYu kiahn [[Special:Search/{{PAGENAME}}|saach fi dis piej taikl]] ina ada piej,\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} saach di rilietid lagdem],\nar [{{fullurl:{{FULLPAGENAME}}|action=edit}} edit dis piej]</span>.",
+       "noarticletext-nopermission": "Korantli no tex no de ina dis piej.\nYu kiah [[Special:Search/{{PAGENAME}}|saach fi dis piej taikl]] ina ada piej, ar <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} saach di rilietid lagdem]</span>, bot yu not ab pomishan fi kriet dis piej.‎",
        "userpage-userdoesnotexist": "Yuuza akount \"<nowiki>$1</nowiki>\" no rejista.\nBegyu chek ef yu waahn fi kriet/edit dis piej.",
        "userpage-userdoesnotexist-view": "Yuuza akount \"$1\" no rejista.",
        "blocked-notice-logextract": "Dis yuuza korantli blak.\nDi lietis blak lag enchri provaid biluo fi refrans:",
        "session_fail_preview_html": "'''Sari! Wi kudn pruoses yu edit juu tu laas a seshan dieta.'''\n\n''Bikaa {{SITENAME}} ab raa HTML eniebl, di priivyuu aidwe az prikaashan gens JavaScript atak.''\n\n'''Ef dis a lejitimet edit atemp, begyu chrai agen.'''\nEf istil no wok, chrai [[Special:UserLogout|lag out]] ahn lag bak iin.",
        "token_suffix_mismatch": "'''Yu edit rijek bikaa yu klayant manggl di pongtyueshan kiaraktadem ina di edit tuokn.'''\nDi edit rijek fi privent koropshan a di piej tex.\nDis somtaim apn wen yu a yuuz a bogi web-bies ananimos praxi saabis.",
        "editing": "Editin $1",
+       "creating": "Krietin $1",
        "editingsection": "Editin $1 (sekshan)",
        "editingcomment": "Editin $1 (nyuu sekshan)",
        "editconflict": "Edit kanflik: $1",
        "hiddencategories": "Dis piej a memb a {{PLURAL:$1|1 idn kiatigari|$1 idn kiatigari}}:",
        "permissionserrors": "Permishan herro",
        "permissionserrorstext-withaction": "Yu no ab no poermishan fi $2, fi di falarin {{PLURAL:$1|riizn|riizndem}}:",
+       "moveddeleted-notice": "Dis piej eh diliit.\nDi diliishan ah muuv lag fi di piej provaid biluo fi refrans.‎",
        "edit-conflict": "Hedit kanflik: $1",
        "cantcreateaccounttitle": "Cyannat mek di hakkount",
        "viewpagelogs": "Vyuu lagdem fi dis piej",
        "currentrev-asof": "Lietis rivijan az av $1",
        "revisionasof": "Rivijan az av $1",
+       "revision-info": "Rivijan optel $1 by {{GENDER:$6|$2}}$7‎",
        "previousrevision": "← Uola rivijan",
        "nextrevision": "Nyuwa rivijan",
        "currentrevisionlink": "Lietis rivijan",
        "revdel-restore": "chienj vizibiliti",
        "revertmerge": "Anmoerj",
        "history-title": "Rivijan ischri a \"$1\"",
+       "difference-title": "Difrans bitwiin rivijan a \"$1\"",
        "lineno": "Lain $1:",
        "compareselectedversions": "Kompier silektid rivijan",
        "editundo": "andu",
+       "diff-multi-sameuser": "({{PLURAL:$1|Wan intamidiet rivijan|$1 intamidiet rivijandem}} bai di siem yuuza we no shuo)",
        "searchresults": "Saach rizolt",
        "searchresults-title": "Saach rizolt fi \"$1\"",
        "notextmatches": "No piej tex mach",
        "prevn": "priivos {{PLURAL:$1|$1}}",
        "nextn": "nex {{PLURAL:$1|$1}}",
+       "nextn-title": "Nex $1 {{PLURAL:$1|rizolt|rizoltdem}}‎",
+       "shown-title": "Shuo $1 {{PLURAL:$1|result|results}} po piej",
        "viewprevnext": "Vyuu ($1 {{int:pipe-separator}} $2) ($3)",
+       "searchmenu-new": "<strong>Kriet di piej \"[[:$1]]\" pah dis wiki!</strong> {{PLURAL:$2|0=|Si azwel di piej we fain wid yu saach.|Si azwel di saach rizolt wa fain.}}‎",
        "searchprofile-articles": "Kantent piej",
+       "searchprofile-images": "Moltimidia",
+       "searchprofile-everything": "Ebriting",
+       "searchprofile-advanced": "Advans",
+       "searchprofile-articles-tooltip": "Saach ina $1",
+       "searchprofile-images-tooltip": "Saach fi fail",
+       "searchprofile-everything-tooltip": "Saach aal kantent (inkluudn taak piej)",
+       "searchprofile-advanced-tooltip": "Saach ina kostom niemspies",
        "search-result-size": "$1 ({{PLURAL:$2|1 wod|$2 wod}})",
        "search-redirect": "(riidirek $1)",
        "search-section": "(sekshan $1)",
        "search-interwiki-caption": "Sista prajek",
        "search-interwiki-default": "$1 rizoltdem:",
        "search-interwiki-more": "(muo)",
+       "searchall": "aal",
+       "search-showingresults": "{{PLURAL:$4|Rizolt <strong>$1</strong> a <strong>$3</strong>|Rizoltdem <strong>$1 - $2</strong> a <strong>$3</strong>}}‎",
+       "search-nonefound": "No rizolt no de we mach di kweiri.",
        "powersearch-legend": "Advans saach",
        "powersearch-ns": "Saach ina niemspies:",
        "preferences": "Prefrens",
-       "mypreferences": "Mi prefrans",
+       "mypreferences": "Prefrans",
        "prefs-help-realname": "Riil niem apshanal. Ef yu giit, imaita yuuz az achribyuushan fi yu wok.",
        "group-sysop": "Adminischrieta",
        "grouppage-sysop": "{{ns:project}}:Adminischrieta",
+       "right-writeapi": "Yuus a di rait API",
        "newuserlogpage": "Yuuza krieshan lag",
        "rightslog": "Yuuza raits lag",
        "action-edit": "edit dis piej",
        "nchanges": "$1 {{PLURAL:$1|chienj|chienjdem}}",
+       "enhancedrc-history": "ischri",
        "recentchanges": "Riisant chienjdem",
        "recentchanges-legend": "Riisant chienj apshan",
+       "recentchanges-summary": "Chrak di muos riisent chienj tu di wiki pah dis piej.‎",
        "recentchanges-feed-description": "Chrak di muos riisant chienjdem tu di wiki ina dis fiid.",
+       "recentchanges-label-newpage": "Dis edit kriet nyuu piej",
+       "recentchanges-label-minor": "Dis a maina edit",
+       "recentchanges-label-bot": "Dis edit pofaam bai bot",
+       "recentchanges-label-unpatrolled": "Dis edit no get pachuol yet",
+       "recentchanges-label-plusminus": "Di piej saiz chienj bai dis nomba a bait",
+       "recentchanges-legend-heading": "'''Lejen:'''",
+       "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (azwel si [[Special:NewPages|lis a nyuu piej]])",
        "rclistfrom": "Shuo nyuu chienjdem we taat frahn $3 $2",
        "rcshowhideminor": "$1 maina editdem",
+       "rcshowhideminor-show": "Shuo",
+       "rcshowhideminor-hide": "Aid",
        "rcshowhidebots": "$1 batdem",
-       "rcshowhideliu": "$1 lag-iin yuuzadem",
+       "rcshowhidebots-show": "Shuo",
+       "rcshowhidebots-hide": "Aid",
+       "rcshowhideliu": "$1 rejista yuuzadem",
+       "rcshowhideliu-hide": "Aid",
        "rcshowhideanons": "$1 ananimos yuuzadem",
+       "rcshowhideanons-show": "Shuo",
+       "rcshowhideanons-hide": "Aid",
        "rcshowhidemine": "$1 mi editdem",
+       "rcshowhidemine-show": "Shuo",
+       "rcshowhidemine-hide": "Aid",
        "rclinks": "Shuo laas $1 chienj ina laas $2 die<br />$3",
        "diff": "dif",
        "hist": "isch",
        "minoreditletter": "m",
        "newpageletter": "N",
        "boteditletter": "b",
+       "rc-change-size-new": "$1 {{PLURAL:$1|bait|baitdem}} afta chienj‎",
        "rc-enhanced-expand": "Shuo ditiel (rikwaya JavaScript)",
        "rc-enhanced-hide": "Aid ditiel",
        "recentchangeslinked": "Rilietid chienj",
        "recentchangeslinked-to": "Shuo chienjdem tu piej wa lingk tu di gibn piej insted",
        "upload": "Opluod fail",
        "uploadlogpage": "Opluod lag",
+       "filedesc": "Somari‎",
+       "license-header": "Laisnsin‎",
+       "imgfile": "fail",
        "file-anchor-link": "Fail",
        "filehist": "Fail ischri",
        "filehist-help": "Klik pan a diet/taim fi vyuu di fail az ou iapier a di taim.",
        "filehist-comment": "Kament",
        "imagelinks": "Fail Yuusij",
        "linkstoimage": "Di falarin {{PLURAL:$1|piej lingk|$1 piejdem lingk}}",
+       "nolinkstoimage": "No piej no de we lingk dis fail.",
        "sharedupload": "Dis fail kom frahn $1 ahn kiahn yuuz bai ada prajek.",
+       "sharedupload-desc-here": "Dis fail koh frah $1 ah kiah yuuz pah adaels prajek. Di diskripshan fiit [$2 fail diskripshan piej] shuo biluo.‎",
        "uploadnewversion-linktext": "Opluod nyuu voerjan a dis fail",
+       "upload-disallowed-here": "Yu kyaah uobarait dis fail.",
        "randompage": "Random piej",
        "statistics": "Tatistik",
        "nbytes": "$1 {{PLURAL:$1|bait|bait}}",
        "pager-older-n": "{{PLURAL:$1|uola 1|uola $1}}",
        "booksources": "Buk suos",
        "booksources-search-legend": "Saach fi buk suos",
+       "booksources-search": "Saach",
        "log": "Lagdem",
        "allpages": "Aal piej",
        "prevpage": "Priivos piej ($1)",
        "allpagesto": "Displie piej en a:",
        "allarticles": "Aal piej",
        "allpagessubmit": "Gwaan",
+       "categories": "Kiatigaridem‎",
        "linksearch": "Extoernal lingk",
        "listgrouprights-members": "(lis a memba)",
        "emailuser": "E-miel dis yuuza",
        "deleteotherreason": "Ada/adishanal riizn:",
        "deletereasonotherlist": "Ada riizn",
        "rollbacklink": "ruolbak",
+       "rollbacklinkcount": "roulbak $1 {{PLURAL:$1|edit|editdem}}‎",
        "protectlogpage": "Protekshan lag",
        "protectedarticle": "don protek \"[[$1]]\"",
        "modifiedarticleprotection": "don chienj protekshan lebl fi \"[[$1]]\"",
        "undeletelink": "vyuu/ristuor",
        "namespace": "Niemspies",
        "invert": "Invoert silekshan",
+       "tooltip-invert": "Chek dis bax fi aid chienj tu piej widin di silek niemspies (ah di asuosietid niemspies ef ichek)",
+       "namespace_association": "Asuosietid niemspies",
+       "tooltip-namespace_association": "Chek dis bax fi azwel ingkluud di taak ar sobjek niemspies asuosiet wid di silek niemspies",
        "blanknamespace": "(Mien)",
-       "contributions": "Yuuza kanchribyuushan",
+       "contributions": "{{GENDER:$1|Yuuza}} kanchribyuushan",
        "contributions-title": "Yuuza kanchribiushan fi $1",
        "mycontris": "Mi kanchribyuushan",
+       "anoncontribs": "Kanchribyuushan",
        "contribsub2": "Fi $1 ($2)",
        "uctop": "(tap)",
        "month": "Frahn mont (ahn oerlia):",
        "linkshere": "Di falarin piejdem lingk tu '''[[:$1]]''':",
        "isredirect": "riidirek piej",
        "istemplate": "chranskluujan",
-       "isimage": "imij lingk",
+       "isimage": "fail lingk",
        "whatlinkshere-prev": "{{PLURAL:$1|priivos|priivos $1}}",
        "whatlinkshere-next": "{{PLURAL:$1|nex|nex $1}}",
        "whatlinkshere-links": "← lingkdem",
        "thumbnail-more": "Inlaaj",
        "import": "Himpuot piejdem",
        "import-comment": "Kament:",
-       "tooltip-pt-userpage": "Yu yuuza piej",
-       "tooltip-pt-mytalk": "Yu taak piej",
-       "tooltip-pt-preferences": "Yu prefrans",
+       "tooltip-pt-userpage": "{{GENDER:|Yu yuuza}} piej",
+       "tooltip-pt-mytalk": "{{GENDER:|Fiyu}} taak piej‎",
+       "tooltip-pt-preferences": "{{GENDER:|Yu}} prefrans",
        "tooltip-pt-watchlist": "Di lis a piej yu a manita fi chienj",
-       "tooltip-pt-mycontris": "Lis a yu kanchribyuushan",
+       "tooltip-pt-mycontris": "Lis a {{GENDER:|Fiyu}} kanchribyuushan‎",
        "tooltip-pt-login": "Yu inkorij fi lag iin; ousomeba, ino mos ahn boun",
        "tooltip-pt-logout": "Lag out",
        "tooltip-pt-createaccount": "Yu inkorij fi kriet wah akount ah lagiin; ousomeba, a no mos",
        "tooltip-t-recentchangeslinked": "Riisant chienj ina piej wa lingk frahn dis piej",
        "tooltip-feed-rss": "RSS fiid fi dis piej",
        "tooltip-feed-atom": "Atom fiid fi dis piej",
-       "tooltip-t-contributions": "Vyuu di lis a kanchribyuushan a dis yuuza",
+       "tooltip-t-contributions": "Lis a kanchribyuushan bai {{GENDER:$1|dis yuuza}}‎",
        "tooltip-t-emailuser": "Sen e-miel tu dis yuuza",
        "tooltip-t-info": "Muo infamieshan bout da piej ya",
        "tooltip-t-upload": "Opluod fail",
        "tooltip-watch": "Ad dis piej tu yu wachlis",
        "tooltip-rollback": "\"Ruolbak\" rivoert edit(dem) tu dis piej a di laas kanchribiuta ina wan klik",
        "tooltip-undo": "\"Andu\" rivoert dis edit ahn opin di edit faam ina priivyuu muod. Ilou yu fi ad riizn ina di somari.",
+       "tooltip-summary": "Enta shaat somari",
+       "simpleantispam-label": "Anti-spam chek.\n<strong>No</strong> fuliin dis!‎",
        "pageinfo-toolboxlink": "Piej infamieshan",
        "previousdiff": "← Uola edit",
        "nextdiff": "Nyuwa edit",
        "file-info-size": "$1 × $2 pixl, fail saiz: $3, MIME taip: $4",
        "file-nohires": "No aya rezaluushan no avielobl.",
        "svg-long-desc": "SVG fail, naminali $1 × $2 pixl, fail saiz: $3",
-       "show-big-image": "Ful rezaluushan",
+       "show-big-image": "Orijinal fail",
+       "show-big-image-preview": "Saiz a dis priivyuu: $1.",
+       "show-big-image-other": "Adaels {{PLURAL:$2|rezaluushan|rezaluushandem}}: $1.",
+       "show-big-image-size": "$1 × $2 pixels",
        "bad_image_list": "Di faamat go so:\n\nOnggl lis aitem (lain taat wid *) wi kansida.\nDi fos lingk pan a lain mos bi a lingk tu a bad fail.\nEni sobsikwent lingk pahn di siem lain kansida fi bi eksepshan, i.e. piej we di fail maita okor inlain.",
        "metadata": "Metadieta",
        "metadata-help": "Dis fail kantien adishanal infamieshan, prabli wa ad frahn di dijital kiamara ar skiana yuuz fi kriet ar dijitaizi.\nEf di fail madifai frahn iarijinal stiet, som ditiel maita no fuli riflek di madifai fail.",
        "metadata-expand": "Shuo extendid ditiel",
        "metadata-collapse": "Aid extendid ditiel",
        "metadata-fields": "EXIF metadieta fiil wa lis ina dis mechiz wi inkluud pahn imij piej displie wen di metadieta tiebl get kalaps.\nAda wandem wi aid bai difaalt.\n* make\n* model\n* datetimeoriginal\n* exposuretime\n* fnumber\n* isospeedratings\n* focallength\n* artist\n* copyright\n* imagedescription\n* gpslatitude\n* gpslongitude\n* gpsaltitude",
+       "exif-orientation": "Orientieshan‎",
+       "exif-xresolution": "Arizantal rezaluushan‎",
+       "exif-yresolution": "Voertikal rezaluushan‎",
+       "exif-datetime": "Fail chienj diet ah taim‎",
+       "exif-make": "Kiamara maniufakchra‎",
+       "exif-model": "Kiamara magl‎",
+       "exif-software": "Saafwier yuuz‎",
+       "exif-exifversion": "Exif voerjan",
+       "exif-colorspace": "Kola spies‎",
+       "exif-datetimeoriginal": "Diet ah taim a dieta jinarieshan‎",
+       "exif-datetimedigitized": "Diet ah taim a dijitaizin‎",
+       "exif-orientation-1": "Naamal‎",
        "namespacesall": "aal",
        "monthsall": "aal",
        "watchlisttools-view": "Vyuu rilivant chienjdem",
        "watchlisttools-edit": "Vyuu ahn edit wachlis",
        "watchlisttools-raw": "Edit raa wachlis",
+       "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1| talk]])‎",
        "specialpages": "Peshal piej",
+       "tag-filter": "[[Special:Tags|Tag]] filta:",
+       "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Tag|Tags}}]]: $2)",
+       "logentry-delete-delete": "$1 {{GENDER:$2|diliitid}} piej $3‎",
+       "logentry-move-move": "$1 {{GENDER:$2|muuv}} piej $3 tu $4‎",
+       "logentry-newusers-create": "Yuuza akount $1 eh {{GENDER:$2|krietid}}‎",
+       "logentry-upload-upload": "$1 {{GENDER:$2|uploaded}} $3",
        "searchsuggest-search": "Saach‎"
 }
index edc1178..bcff5d2 100644 (file)
        "databaseerror-query": "მოთხოვნა: $1",
        "databaseerror-function": "ფუნქცია: $1",
        "databaseerror-error": "შეცდომა: $1",
+       "transaction-duration-limit-exceeded": "თავიდან რომ ავიცილოთ რეპლიკაციის მაღალი ლაგი, ეს ტრანზაქცია გაუქმნა, რადგან ჩაწერის ხანგრძლივობა ($1) ასცდა $2 წამიან ლიმიტს.",
        "laggedslavemode": "ყურადღება: გვერდი შესაძლოა არ შეიცავდეს ბოლო ცვლილებებს.",
        "readonly": "მონაცემთა ბაზა დახურულია",
        "enterlockreason": "მიუთიეთ ბლოკირების მიზეზი და ხანგრძლივობის ვადა",
        "missingarticle-rev": "(ჩასწორება#: $1)",
        "missingarticle-diff": "(ცვლილება: $1, $2)",
        "readonly_lag": "მონაცემთა ბაზა ავტომატურად დაიხურა, სანამ შვილობილი ბაზის სერვერები მთავარ ბაზასთან სინქრონიზაციას ახდენს",
+       "nonwrite-api-promise-error": "'Promise-Non-Write-API-Action' HTTP-header გაიგზავნა, თუმცა მიმღები იყო API წერის მოდული.",
        "internalerror": "შიდა შეცდომა",
        "internalerror_info": "შიდა შეცდომა: $1",
        "internalerror-fatal-exception": "ფატალური გამონაკლისის ტიპი „$1“",
        "changepassword-success": "თქვენი პაროლი წარმატებით შეიცვალა!",
        "changepassword-throttled": "თქვენ განახორციელეთ ანგარიშში შესვლის ზედმეტად ბევრი მცდელობა. გამორებით შეყვანამდე გთხოვთ დაიცადოთ $1.",
        "botpasswords": "ბოტის პაროლები",
+       "botpasswords-summary": "<em>ბოტების კოდები</em> საშუალებას იძლევა მომხმარებლის ანგარიშთან დაკავშირების API-ის გამოყენებით ანგარიშის ძირითადი შესვლის მონაცემების გამოყენების გარეშე. მომხმარებლის უფლებები ასეთი შესვლისას შესაძლოა შეზღუდული იყოს.\n\nთუ არ იცით ეს რატომ უნდა გააკეთოთ, ალბათ არც უნდა გააკეთოთ. ასეთი კოდის წამოქმნა და სხვისთვის გადაცემა არაა რეკომენდირებული.",
        "botpasswords-disabled": "ბოტის პაროლები გათიშულია.",
        "botpasswords-no-central-id": "ბოტის პაროლების გამოსაყენებლად, საჭიროა ცენტრალიზებული ანგარიშით შესვლა.",
        "botpasswords-existing": "არსებული ბოტის პაროლები",
        "previewnote": "'''დაიმახსოვრეთ, ეს მხოლოდ წინასწარი გადახედვაა.'''\nთქვენი ცვლილებები ჯერ არ შენახულა!",
        "continue-editing": "რედაქტირებაზე გადასვლა",
        "previewconflict": "შავი ნიმუში უჩვენებს ტექსტს ზედა რედაქტირების ფანჯარაში, როგორც ის გამოჩნდება თუ თქვენ მას შეინახავთ.",
-       "session_fail_preview": "'''უკაცრავად! ვერ შევძელით თქვენი რედაქტირების შენახვა სესიის მონაცემთა დაკარგვის გამო.\nგთხოვთ ისევ სცადოთ.\nთუ პრობლემა განმეორდა, სცადეთ [[Special:UserLogout|სისტემიდან გასვლა]] და ხელახლა შემოსვლა.'''",
-       "session_fail_preview_html": "'''სამწუხაროდ, ჩვენ ვერ განვახორციელეთ თქვენი რედაქტირება სესიის მონაცემთა დაკარგვის გამო.'''\n\n''რადგანაც ამ ვიკის აქვს დაუმუშავებელი HTML ჩართული, წინასწარი გადახედვა დამალულია ჯავასკრიპტის შეტევის საწინააღმდეგოდ სიფრთხილის მიზნით.''\n\n'''თუ ეს მიღებული რედაქტირების მცდელობა იყო, გთხოვთ ისევ სცადოთ. თუ იგი კვლავ არ მუშაობს, სცადეთ თავიდან [[Special:UserLogout/დარეგისტრირებ]]ა (შესვლა)'''",
+       "session_fail_preview": "უკაცრავად! ვერ შევძელით თქვენი რედაქტირების შენახვა სესიის მონაცემთა დაკარგვის გამო.\n\nშესაძლოა თქვენ სისტემიდან გამოვარდით. <strong>გთხოვთ დაადასტურეთ რომ კვლავ სისტემაში ხართ და სცადეთ კიდევ ერთხელ</strong>.\nთუ პრობლემა განმეორდება, გთხოვთ [[Special:UserLogout|გადით სისტემიდან]] და კვლავ შემოდით, ასევე დარწმუნდით, რომ თქვენი ბრაუზერი იძლევა ამ საიტიდან ქუქის გამოყენების უფლებას.",
+       "session_fail_preview_html": "უკაცრავად, ჩვენ ვერ განვახორციელეთ თქვენი რედაქტირება სესიის მონაცემთა დაკარგვის გამო.\n\n<em>რადგანაც {{SITENAME}}-ს აქვს დაუმუშავებელი HTML ჩართული, წინასწარი გადახედვა დამალულია ჯავასკრიპტის შეტევის საწინააღმდეგოდ სიფრთხილის მიზნით.</em>\n\n<strong>თუ ეს რედაქტირების მცდელობა იყო, გთხოვთ ისევ სცადოთ.</strong> თუ პრობლემა განმეორდება, [[Special:UserLogout|გადით სისტემიდან]] და კვლავ შემოდით, ასევე დარწმუნდით, რომ თქვენი ბრაუზერი იძლევა ამ საიტიდან ქუქის გამოყენების უფლებას.",
        "token_suffix_mismatch": "'''თქვენი შესწორება გაუქმდა რადგანაც პროგრამა არასწორედ აღიქვამს პუნქტუაციის ნიშნებს რედაქტირების ფანჯარაში. შესწრება გაუქმდა სტატიის არ გაფუჭების მიზნით. შესაძლოა ეს გამოწვეულია გაფუჭებული ვებ პროქსის გამოყენებით.'''",
        "edit_form_incomplete": "'''რედაქტირებების ნაწილმა სერვერამდე ვერ მიაღწია; გთხოვთ, შეამოწმეთ თქვენი რედაქტირებების სრულყოფილება და სცადეთ განმეორებით.'''",
        "editing": "რედაქტირება: $1",
        "permissionserrors": "ნებართვის შეცდომა",
        "permissionserrorstext": "თქვენ არ გაქვთ ამის გაკეთების უფლება, შემდეგი {{PLURAL:$1|მიზეზის|მიზეზების}} გამო:",
        "permissionserrorstext-withaction": "თქვენ არ გაქვთ ამ მოქმედების - „$2“ განხორციელების ნებართვა შემდეგი {{PLURAL:$1|მიზეზის|მიზეზის}} გამო:",
+       "contentmodelediterror": "არ შეგიძლიათ ამ ვერსიის რედაქტირება, რადგან მისი კონტენტის მოდელი არის <code>$1</code>, რაც განსხვავდება გვერდის მიმდინარე კონტენტის მოედლისაგან <code>$2</code>.",
        "recreate-moveddeleted-warn": "'''გაფრთხილება: თქვენ ხელახლა ქმნით გვერდს, რომელიც ადრე წაიშალა.'''\n\nგთხოვთ დაფიქრდეთ, მისაღები არის თუ არა ამ გვერდის რედაქტირების გაგრძელება.\nინფორმაციისთვის ქვემოთ მოყვანილია ამ გვერდის წაშლის ისტორია:",
        "moveddeleted-notice": "ეს გვერდი წაიშალა. ინფორმაციის მისაღებად ქვემოთ წარმოდგენილია შესაბამისი ჩანაწერები წაშლისა და გადარქმევის ჟურნალებიდან.",
+       "moveddeleted-notice-recent": "ბოდიში, ეს გვერდი წაშლილია (ბოლო 24 საათის განმავლობაში).\nწაშლისა და გადატანის ჟურნალი ქმენოთ არის მოცემული.",
        "log-fulllog": "ყველა ჟურნალის ხილვა",
        "edit-hook-aborted": "შესწორება გაუქმებულია გადამჭერით.\nდამატებითი ახსნა არ ჩაწერილა.",
        "edit-gone-missing": "გვერდის განახლეა შეუძლებელია.\nშესაძლოა, იგი წაიშალა.",
        "mergehistory-empty": "რაიმე ცვლილების შერწყმა შეუძლებელია.",
        "mergehistory-done": "$3 {{PLURAL:$3|შესწორება|შესწორებები}} $1-დან წარმატებით {{PLURAL:$3|შეერწყა|შეერწყა}} [[:$2]]-ს.",
        "mergehistory-fail": "ვერ მოხერხდა გვერდების ისტორიის გაერთიანება, გთხოვთ შეამოწმოთ გვერდის პაარამეტრები და დრო.",
+       "mergehistory-fail-bad-timestamp": "მიმდევრობა არასწორია.",
+       "mergehistory-fail-invalid-source": "წყაროს გვერდი არასწორია.",
+       "mergehistory-fail-invalid-dest": "დანიშნულების გვერდი არასწორია.",
+       "mergehistory-fail-no-change": "ისტორიის გაერთიანებას ცვლილებები არ გაუერთიანებია. გთხოვთ კვლავ შეამოწმოთ გვერდი და დროის პარამეტრები.",
+       "mergehistory-fail-permission": "არ გაქვთ საკმარისი ნებართვა ისტორიის გასაერთიანებლად.",
+       "mergehistory-fail-self-merge": "წყაროსი და დანიშნულების გვერდები ერთიდაიგივეა.",
        "mergehistory-fail-toobig": "არ ხერხდება ისტორიების შერწყმა, რამეთუ აუცილებელია დაშვებული ლიმიტის მეტი ნაწილის გადატანა $1 ვერსიაში.",
        "mergehistory-no-source": "დანიშნულების გვერდი $1 არ არსებობს.",
        "mergehistory-no-destination": "დანიშნულების გვერდი $1 არ არსებობს.",
        "badsig": "არასწორი ნედლი ხელმოწერა; შეამოწმეთ HTML ჭდეები.",
        "badsiglength": "ხელმოწერა ძალიან გრძელია.\nუნდა შედგებოდეს მაქსიმუმ $1 ნიშნისაგან.",
        "yourgender": "რომელი აღწერა უფრო შეგეფერებათ თქვენ?",
-       "gender-unknown": "á\83\9bá\83\98á\83\97á\83\98á\83\97á\83\94á\83\91á\83\90á\83¡ á\83\90á\83  á\83\95á\83\97á\83\95á\83\9aá\83\98 á\83¡á\83\90á\83­á\83\98á\83 á\83\9dá\83\93",
+       "gender-unknown": "á\83\9eá\83 á\83\9dá\83\92á\83 á\83\90á\83\9bá\83£á\83\9aá\83\98 á\83£á\83\96á\83 á\83£á\83\9cá\83\95á\83\94á\83\9aá\83§á\83\9dá\83¤á\83\90 á\83\97á\83¥á\83\95á\83\94á\83\9cá\83\96á\83\94 á\83¡á\83\90á\83£á\83\91á\83 á\83\98á\83¡á\83\90á\83¡ á\83\92á\83\94á\83\9cá\83\93á\83\94á\83 á\83£á\83\9aá\83\90á\83\93 á\83\9cá\83\94á\83\98á\83¢á\83 á\83\90á\83\9aá\83£á\83  á\83¡á\83\98á\83¢á\83§á\83\95á\83\94á\83\91á\83¡ á\83\92á\83\90á\83\9bá\83\9dá\83\98á\83§á\83\94á\83\9cá\83\94á\83\91á\83¡, á\83 á\83\9dá\83\93á\83\94á\83¡á\83\90á\83ª á\83¨á\83\94á\83¡á\83\90á\83«á\83\9aá\83\94á\83\91á\83\94á\83\9aá\83\98á\83\90",
        "gender-male": "ის (მამრობითი) არედაქტირებს ვიკი-გვერდებს",
        "gender-female": "ის (მდედრობითი) არედაქტირებს ვიკი-გვერდებს",
        "prefs-help-gender": "ამ პარამეტრის დაყენება არასავალდებულოა.\nპროგრამული უზრუნველყოფა ამ ინფორმაციას იყენებს მხოლოდ სწორი გრამატიკული სქესით მომართვისათვის.\nეს ინფორმაცია საჯარო იქნება ყველასათვის.",
        "group": "ჯგუფი:",
        "group-user": "მომხმარებლები",
        "group-autoconfirmed": "ავტომატურად დადასტურებული მომხმარებლები",
-       "group-bot": "á\83 á\83\9dá\83\91á\83\9dá\83¢á\83\94á\83\91á\83\98",
+       "group-bot": "ბოტები",
        "group-sysop": "ადმინისტრატორები",
        "group-bureaucrat": "ბიუროკრატები",
        "group-suppress": "რევიზორები",
        "group-sysop-member": "{{GENDER:$1|ადმინისტრატორი}}",
        "group-bureaucrat-member": "{{GENDER:$1|ბიუროკრატი}}",
        "group-suppress-member": "{{GENDER:$1|რევიზორები}}",
-       "grouppage-user": "{{ns:project}}:á\83\9bá\83\9dá\83\9bá\83®á\83\9bá\83\90á\83 á\83\94á\83\91á\83\94á\83\9aი",
+       "grouppage-user": "{{ns:project}}:á\83\9bá\83\9dá\83\9bá\83®á\83\9bá\83\90á\83 á\83\94á\83\91á\83\9aá\83\94á\83\91ი",
        "grouppage-autoconfirmed": "{{ns:project}}:ავტომატურად დადასტურებული მომხმარებლები",
-       "grouppage-bot": "{{ns:project}}:á\83 á\83\9dá\83\91á\83\9dá\83¢á\83\94á\83\91á\83\98",
+       "grouppage-bot": "{{ns:project}}:ბოტები",
        "grouppage-sysop": "{{ns:project}}:ადმინისტრატორები",
        "grouppage-bureaucrat": "{{ns:project}}:ბიუროკრატები",
        "grouppage-suppress": "{{ns:project}}:რევიზორები",
        "right-undelete": "გვერდის აღდგენა",
        "right-suppressrevision": "გვერდების დამალული ვერსიების ხილვა, დამალვა და აღდგენა ყველა მომხმარებლისგან",
        "right-viewsuppressed": "ვერსიის ხილვა, რომელიც დამალულია ყველა მომხმარებლისათვის",
-       "right-suppressionlog": "á\83\99á\83\94á\83 á\83«á\83\9d ჟურნალების ნახვა",
+       "right-suppressionlog": "á\83\9eá\83\98á\83 á\83\90á\83\93á\83£á\83\9aá\83\98 ჟურნალების ნახვა",
        "right-block": "სხვა მომხმარებლების მიერ რედაქტირების აკრძალვა",
        "right-blockemail": "ელ ფოსტის გაგზავნის აკრძალვა",
        "right-hideuser": "მომხმარებლის სახელის დაბლოკვა და მისი დამალვა საზოგადოებისგან",
        "grant-group-file-interaction": "კავშირი მედია-ფაილებთან",
        "grant-group-watchlist-interaction": "კავშირი კონტროლის სიასთან",
        "grant-group-email": "ელექტრონული ფოსტის გაგზავნა",
+       "grant-group-high-volume": "მაღალი სიხშირის მოქმედების შესრულება",
        "grant-group-customization": "კონფიგურაცია",
        "grant-group-administration": "ადმინისტრაციული მოქმედებების შესრულება",
+       "grant-group-other": "სხვადასხვა ქმედებები",
        "grant-blockusers": "მომხმარებლების დაბლოკვა და ბლოკის მოხსნა",
        "grant-createaccount": "ანგარიშების შექმნა",
        "grant-createeditmovepage": "გვერდის შექმნა, რედაქტირება და გადატანა",
        "grant-editmywatchlist": "თქვენი კონტროლის სიის რედაქტირება",
        "grant-editpage": "არსებული გვერდების რედაქტირება",
        "grant-editprotected": "დაცული გვერდების რედაქტირება",
+       "grant-highvolume": "დიდი მოცულობით რედაქტირება",
        "grant-oversight": "მომხმარებლებისა და შესწორებების დამალვა",
        "grant-patrol": "გვერდების რედაქტირებების შემოწმება",
        "grant-protect": "გვერდების და დაცვა და დაცვის მოხსნა",
        "filename-tooshort": "ფაილის სახელი ზედმეტად მოკლეა",
        "filetype-banned": "ფაილის ეს ტიპი აკრძალულია",
        "verification-error": "ამ ფაილმა არ გაიარა შემოწმების პროცედურა.",
-       "hookaborted": "á\83\97á\83¥á\83\95á\83\94á\83\9c á\83\9bá\83\98á\83\94á\83  á\83¨á\83\94á\83\9bá\83\9dá\83\97á\83\90á\83\95á\83\90á\83\96á\83\94á\83\91á\83£á\83\9aá\83\98 á\83ªá\83\95á\83\9aá\83\98á\83\9aá\83\94á\83\91á\83\90 á\83\9bá\83\9dá\83\98á\83\9cá\83\98á\83¨á\83\9cá\83\90 á\83\92á\83\90á\83¤á\83\90á\83 á\83\97á\83\9dá\83\94á\83\91á\83\98á\83¡ á\83\93á\83\9dá\83\99á\83£á\83\9bá\83\94á\83\9cá\83¢á\83\90á\83ªá\83\98á\83\90á\83¨á\83\98.",
+       "hookaborted": "á\83\97á\83¥á\83\95á\83\94á\83\9c á\83\9bá\83\98á\83\94á\83  á\83¨á\83\94á\83\9bá\83\9dá\83\97á\83\90á\83\95á\83\90á\83\96á\83\94á\83\91á\83£á\83\9aá\83\98 á\83ªá\83\95á\83\9aá\83\98á\83\9aá\83\94á\83\91á\83\90 á\83\92á\83\90á\83£á\83¥á\83\9bá\83\93á\83\90 á\83\92á\83\90á\83¤á\83\90á\83 á\83\97á\83\9dá\83\94á\83\91á\83\98á\83¡ á\83\9bá\83\98á\83\94á\83 .",
        "illegal-filename": "ფაილის ეს სახელი აკრძალულია.",
        "overwrite": "არსებული ფაილის შეცვლა მიუღებელია.",
        "unknown-error": "აღმოჩენილია უცნობი შეცდომა.",
        "uploaddisabledtext": "ფაილების ატვირთვა შეუძლებელია.",
        "php-uploaddisabledtext": "ფაილების ატვირთვა შეჩერებულია PHP-ით. გთხოვთ შეამოწმოთ file_uploads-ის მნიშვნელობა.",
        "uploadscripted": "ფაილი შეიცავს HTML-კოდს, ან სკრიპტს, რომელიც ბროუზერმა შეიძლება არასწორედ გაანალიზოს.",
+       "upload-scripted-pi-callback": "შეუძლებელია ფაილის ატვირთვა, რომელიც XML-stylesheet წარმოქმნის ინსტრუქციას შეიცავს.",
+       "uploaded-script-svg": "ნაპოვნია \"$1\" ელემენტი ატვირთულ SVG ფაილში.",
        "uploaded-hostile-svg": "ატვირთულ SVG-ფაილის style ელემენტში ნაპოვნია საფრთხის შემცვლელი CSS-ის კოდი.",
+       "uploaded-href-unsafe-target-svg": "ნაპოვნია href საშიშ მონაცემში: URI <code>&lt;$1 $2=\"$3\"&gt;</code> ატვირთულ SVG ფაილში.",
+       "uploaded-setting-href-svg": "დაბლოკილია \"set\" ტეგის გამოყენების ფუნქცია \"href\" ატრიბუტის დასამატებლად \"მშობელ\" ელემენტში.",
+       "uploaded-image-filter-svg": "ნაპოვნია სურათის ფილტრი URL-ით: <code>&lt;$1 $2=\"$3\"&gt;</code> ატვირთულ SVG ფაილში.",
        "uploadscriptednamespace": "ეს SVG ფაილი შეიცავს სახელთა არაკორექტულ სივრცეს \"$1\".",
        "uploadinvalidxml": "XML ჩატვირთულ ფაილში არ შეიძლება იყოს ანალიზირებული",
        "uploadvirus": "ფაილი ვირუსს შეიცავს! დეტალები: $1",
        "upload-dialog-button-done": "შესრულდა",
        "upload-dialog-button-save": "შენახვა",
        "upload-dialog-button-upload": "ატვირთვა",
-       "upload-form-label-select-file": "ფაილის არჩევა",
        "upload-form-label-infoform-title": "დეტალები",
        "upload-form-label-infoform-name": "სახელი",
        "upload-form-label-infoform-description": "აღწერა",
+       "upload-form-label-infoform-description-tooltip": "მოკლედ აღწერეთ ამ ნამუშევრის შესახებ ყველაფერი მნიშვნელოვანი.\nფოტოსათვის, მიუთითეთ რა არის გამოსახული, სად არის გადაღებული, რა ვითარებაში.",
        "upload-form-label-usage-title": "გამოყენება",
        "upload-form-label-usage-filename": "ფაილის სახელი",
        "foreign-structured-upload-form-label-own-work": "ეს ჩემი პირადი ნამუშევარია",
        "foreign-structured-upload-form-label-infoform-categories": "კატეგორიები",
        "foreign-structured-upload-form-label-infoform-date": "თარიღი",
+       "foreign-structured-upload-form-label-own-work-message-local": "ვადასტურებ, რომ ვტვირთავვ ამ ფაილს მომსახურების პირობებისა და ლიცენსიის პოლიტიკის შესაბამისად {{SITENAME}}-ზე.",
+       "foreign-structured-upload-form-label-not-own-work-message-local": "თუ ვერ ტვირთავთ ამ ფაილს {{SITENAME}}-ის წესების დაცვით, გთხოვთ დახურეთ ეს ფანჯარა და სცადეთ სხვა მეთოდი.",
        "foreign-structured-upload-form-label-not-own-work-local-local": "შეგიძლიათ სცადოთ [[Special:Upload|მთავარი ატვირთვის გვერდი]].",
+       "foreign-structured-upload-form-label-own-work-message-default": "ვიცი, რომ ამ ფაილს ვტვირთავ საზიარო ბაზაში. ვადასტურებ, რომ ამას ვაკეთებ მომსახურების პირობებისა და ლიცენზიის პოლიტიკის შესაბამისად.",
+       "foreign-structured-upload-form-label-not-own-work-message-default": "თუ ვერ ტვირთავთ ამ ფაილს {{SITENAME}}-ის წესების დაცვით, გთხოვთ დახურეთ ეს ფანჯარა და სცადეთ სხვა მეთოდი.",
+       "foreign-structured-upload-form-label-not-own-work-local-default": "შეგიძლიათ ასევე სცადოთ [[Special:Upload|ატვირთვის გვერდი {{SITENAME}}-ზე]], თუ ამ ფაილის ატვირთვა დაშვებულია მათი პოლიტიკის მიხედვით.",
        "foreign-structured-upload-form-label-own-work-message-shared": "მე ვადასტურებ, რომ ამ ფაილზე საავტორო უფლებების მფლობელი ვარ და ვთანხმდები ამ ფაილის შეუქცევადად განთავსებაზე ვიკისაწყობში [https://creativecommons.org/licenses/by-sa/4.0/deed.ka Creative Commons Attribution-ShareAlike 4.0] ლიცენზიით, აგრეთვე ვეთანხმები [https://wikimediafoundation.org/wiki/Terms_of_Use გამოყენების წესებს].",
-       "foreign-structured-upload-form-2-label-noderiv": "<strong>არ უნდა შეიცავდეს</strong> სხვის ნამუშევარს, ასევე არ უნდა იგრძნობოდეს სხვისი ნამუშევრის გავლენა",
-       "foreign-structured-upload-form-2-label-useful": "უნდა იყოს სხვებისთვის <strong>საგანმანათლებლო და სასარგებლო</strong>",
-       "foreign-structured-upload-form-3-label-yes": "დიახ",
-       "foreign-structured-upload-form-3-label-no": "არა",
+       "foreign-structured-upload-form-label-not-own-work-local-shared": "შეგიძლიათ ასევე სცადოთ [[Special:Upload|ატვირთვის გვერდი {{SITENAME}}-ზე]], თუ ამ ფაილის ატვირთვა დაშვებულია მათი პოლიტიკის შესაბამისად.",
        "backend-fail-stream": "ფაილი $1 ტრანსლირება ვერ მოხერხდა.",
        "backend-fail-backup": "ფაილი $1 სარეზერვო ასლის გაკეთება ვერ მოხერხდა.",
        "backend-fail-notexists": "ფაილი $1 არ არსებობს.",
        "querypage-disabled": "ეს სპეცგვერდი გამორთულია წარმადობის გასაზრდელად.",
        "apihelp": "API დახმარება",
        "apihelp-no-such-module": "მოდული „$1“ ვერ მოიძებნა.",
+       "apisandbox": "API-ს სავარჯიშო",
+       "apisandbox-jsonly": "API-ის სავარჯიშოს გამოსაყენებლად საჭიროა JavaScript.",
+       "apisandbox-api-disabled": "API ამ საიტზე გამორთულია.",
+       "apisandbox-fullscreen": "პანელის გაშლა",
+       "apisandbox-fullscreen-tooltip": "პანელის გაშლა ისე, რომ ბრაუზერის მთელი ფანფარა დაიკავოს.",
+       "apisandbox-unfullscreen": "გვერდის ჩვენება",
+       "apisandbox-unfullscreen-tooltip": "შეამცირე სავარჯიშოს პანელი, რათა გამოჩნდეს მედიავიკის სანავიგაციო ბმულები.",
+       "apisandbox-submit": "მოთხოვნის გაკეთება",
+       "apisandbox-reset": "წაშლა",
+       "apisandbox-retry": "ხელახლა ცდა",
+       "apisandbox-loading": "API მოდულ \"$1\"-ზე ინფორმაცია იტვირთება...",
+       "apisandbox-load-error": "API-ის მოდულ \"$1\"-ზე ინფორმაციის ჩატვირთვისას მოხდა შეცდომა: $2",
+       "apisandbox-no-parameters": "API მოდულს არ აქვს პარამეტრები.",
+       "apisandbox-helpurls": "დახმარების ბმულები",
+       "apisandbox-examples": "მაგალითები",
+       "apisandbox-dynamic-parameters": "დამატებითი პარამეტრები",
+       "apisandbox-dynamic-parameters-add-label": "პარამეტრის დამატება:",
+       "apisandbox-dynamic-parameters-add-placeholder": "პარამეტრის სახელი",
+       "apisandbox-dynamic-error-exists": "პარამეტრი \"$1\" სახელით უკვე არსებობს.",
+       "apisandbox-deprecated-parameters": "შემცირებული პარამეტრები",
+       "apisandbox-fetch-token": "ტოკენის ავტომატური შევსება",
+       "apisandbox-submit-invalid-fields-title": "ზოგიერთი ველი არ აკმაყოფილებს კრიტერიუმებს",
+       "apisandbox-submit-invalid-fields-message": "გთხოვთ, შეასწორეთ მონიშნული ველები და თავიდან სცადეთ.",
+       "apisandbox-results": "შედეგები",
+       "apisandbox-sending-request": "API მოთხოვნის გაგზავნა...",
+       "apisandbox-loading-results": "API შედეგების მიღება...",
+       "apisandbox-request-url-label": "მოთხოვნის URL:",
+       "apisandbox-request-time": "თხოვნის დრო: $1მწ",
+       "apisandbox-results-fixtoken": "ტოკენის შესწორება და თავიდან გაგზავნა",
+       "apisandbox-results-fixtoken-fail": "ვერ მოხერხდა $1 ტოკენის მოძიება.",
+       "apisandbox-alert-page": "ველები ამ გვერდზე არ არის ვალიდური",
+       "apisandbox-alert-field": "ამ ველის მნიშვნელობა არ არის ვალიდური",
        "booksources": "წიგნის წყაროები",
        "booksources-search-legend": "წიგნის წყაროს ძებნა",
        "booksources-isbn": "ISBN:",
        "changecontentmodel-success-title": "შინაარსის მოდელი შეიცვალა",
        "changecontentmodel-success-text": "[[:$1]]-ის კონტენტის ტიპი შეიცვალა.",
        "changecontentmodel-cannot-convert": "[[:$1]]-ის შინაარსის $2-ის ტიპზე კონვერტაცია შეუძლებელია.",
+       "changecontentmodel-nodirectediting": "$1 შინაარსის მოდელს არ აქვს პირდაპირი რედაქტირების მხარდაჭერა",
+       "log-name-contentmodel": "შინაარსის მოდელის შეცვლის ჟურნალი",
+       "log-description-contentmodel": "გვერდის შინაარსის მოდელთან დაკავშირებული მოვლენები",
+       "logentry-contentmodel-change": "$1-მა {{GENDER:$2|შეცვალა}} $3-ის გვერდის შინაარსის მოდელი \"$4\"-დან \"$5\"-ზე",
        "logentry-contentmodel-change-revertlink": "დაბრუნება",
        "logentry-contentmodel-change-revert": "დაბრუნება",
        "protectlogpage": "დაცვის ისტორია",
        "move-page-legend": "გვერდის გადატანა",
        "movepagetext": "ქვემოთ მოცემული ფორმა გვერდს სახელს გადაარქმევს, რაც გადაიტანს მასთან დაკავშირებულ ისტორიასაც ახალ სახელზე. \nძველი სათაური გახდება გადამისამართების გვერდი ახალ სათაურზე. \nბმულები ძველი გვერდის სათაურზე არ შეიცვლება; \nშეამოწმეთ [[Special:DoubleRedirects|ორმაგი]] ან [[Special:BrokenRedirects|გამწყდარი გადამისამართებები]]. \nთქვენ ხართ პასუხისმგებელი, რომ ბმულები მკითხველს დანიშნულებისამებრ მიიყვანს.\n\nგაითვალისწინეთ, რომ გვერდი არ გადავა, თუ ახალი სათაურით სტატია უკვე არსებობს, გარდა იმ შემთხვევისა, როდესაც მსგავსი გვერდი ცარიელია ან გადამისამართებაა და არ აქვს გვერდის რედაქტირების ისტორია. \nეს ნიშნავს, რომ თქვენ შეგიძლიათ დაუბრუნოთ ძველი სახელი გვერდს, თუ შეცდომა დაუშვით, მაგრამ არ შეგიძლიათ ზემოთ გადააწეროთ არსებულ გვერდს.\n\n'''ფრთხილად!'''\nამ მოქმედებამ შეიძლება მნიშვნელოვანი და მოულოდნელი ცვლილება გამოიწვის პოპულარულ გვერდზე; \nსანამ გააგრძელებდეთ, გთხოვთ დარწმუნდეთ, რომ თქვენ გესმით თქვენი ქმედების შედეგები.",
        "movepagetext-noredirectfixer": "ქვემოთ მოცემული ფორმა გვერდს სახელს გადაარქმევს, რაც გადაიტანს მასთან დაკავშირებულ ისტორიასაც ახალ სახელზე. \nძველი სათაური გახდება გადამისამართების გვერდი ახალ სათაურზე.\nშეამოწმეთ [[Special:DoubleRedirects|ორმაგი]] ან [[Special:BrokenRedirects|გამწყდარი]] გადამისამართებები. \nთქვენ ხართ პასუხისმგებელი, რომ ბმულები მკითხველს დანიშნულებისამებრ მიიყვანს.\n\nგაითვალისწინეთ, რომ გვერდი არ გადავა, თუ ახალი სათაურით სტატია უკვე არსებობს, გარდა იმ შემთხვევისა, თუ ის ცარიელია ან გადამისამართებაა და არ აქვს გვერდის რედაქტირების ისტორია. ეს ნიშნავს, რომ თქვენ შეგიძლიათ დაუბრუნოთ ძველი სახელი გვერდს, თუ შეცდომა დაუშვით, მაგრამ არ შეგიძლიათ ზემოთ გადააწეროთ არსებულ გვერდს.\n\n'''გაფრთხილებთ!''' \nამ მოქმედებამ შეიძლება მნიშვნელოვანი და მოულოდნელი ცვლილება გამოიწვის პოპულარულ გვერდზე; სანამ გააგრძელებდეთ, გთხოვთ დარწმუნდეთ, რომ თქვენ გესმით თქვენი ქმედების შედეგები.",
-       "movepagetalktext": "დაკავშირებული განხილვის გვერდი ავტომატურად გადავა მასთან ერთად, '''გარდა იმ შემთხვევისა, თუ''':\n*განხილვის გვერდი ახალი სათაურით და გარკვეული შინაარსით უკვე არსებობს, ან\n*თქვენ მოხსნით ნიშნულს ქვევით დაფაზე.\n\nამ შემთხვევებში, თქვენ თავად მოგიწევთ ამ გვერდის გადატანა, სურვილისამებრ.",
+       "movepagetalktext": "დაკავშირებული განხილვის გვერდი ავტომატურად გადავა მასთან ერთად, გარდა იმ შემთხვევისა, თუ განხილვის გვერდი ახალი სათაურით და გარკვეული შინაარსით უკვე არსებობს.\n\nამ შემთხვევებში, თქვენ თავად მოგიწევთ ამ გვერდის გადატანა, სურვილისამებრ.",
        "moveuserpage-warning": "'''გაფრთხილება:''' თქვენ გადაგაქვთ მომხმარებლის გვერდი. გთხოვთ გაითვალისწინეთ, რომ გადატანა შესრულდება, მომხმარებლის სახელის გადარქმევა კი ''არა''.",
        "movecategorypage-warning": "<strong>გაფრთხილება:</strong> თქვენ გადაგაქვთ კატეგორიის გვერდი. გაითვალისწინეთ, რომ გვერდი გადავა, თუმცა მასში შემავალი გვერდები <em>დარჩება</em> ძველ კატეგორიაში. საჭირო იქნება კატეგორიის ჩასწორება სტატიებში ინდივიდუალურად.",
        "movenologintext": "თქვენ უნდა [[Special:UserLogin|წარუდგინოთ თავი]],\nსისტემას რათა გადაიტანოთ გვერდები.",
        "import-nonewrevisions": "ყველა რედაქცია იმპორტირებული იქნა.",
        "xml-error-string": "$1 ხაზში $2, პოზიციის $3 (ბაიტი $4): $5",
        "import-upload": "XML მონაცემების ატვირთვა",
-       "import-token-mismatch": "á\83¡á\83\94á\83\90á\83\9cá\83¡á\83\98á\83¡ á\83\9bá\83\9dá\83\9cá\83\90á\83ªá\83\94á\83\9bá\83\94á\83\91á\83\98 á\83\93á\83\90á\83\98á\83\99á\83\90á\83 á\83\92á\83\90. á\83\99á\83\98á\83\93á\83\94á\83\95 á\83\94á\83 á\83\97á\83®á\83\94á\83\9a á\83¡á\83ªá\83\90á\83\93á\83\94á\83\97!",
+       "import-token-mismatch": "á\83¡á\83\94á\83¡á\83\98á\83\98á\83¡ á\83\9bá\83\9dá\83\9cá\83\90á\83ªá\83\94á\83\9bá\83\94á\83\91á\83\98 á\83\93á\83\90á\83\98á\83\99á\83\90á\83 á\83\92á\83\90.\n\ná\83¨á\83\94á\83¡á\83\90á\83«á\83\9aá\83\9dá\83\90 á\83\97á\83¥á\83\95á\83\94á\83\9c á\83¡á\83\98á\83¡á\83¢á\83\94á\83\9bá\83\98á\83\93á\83\90á\83\9c á\83\92á\83\90á\83\9bá\83\9dá\83\95á\83\90á\83 á\83\93á\83\98á\83\97. <strong>á\83\92á\83\97á\83®á\83\9dá\83\95á\83\97 á\83\93á\83\90á\83\90á\83\93á\83\90á\83¡á\83¢á\83£á\83 á\83\94á\83\97 á\83 á\83\9dá\83\9b á\83\99á\83\95á\83\9aá\83\90á\83\95 á\83¡á\83\98á\83¡á\83¢á\83\94á\83\9bá\83\90á\83¨á\83\98 á\83®á\83\90á\83 á\83\97 á\83\93á\83\90 á\83¡á\83ªá\83\90á\83\93á\83\94á\83\97 á\83\99á\83\98á\83\93á\83\94á\83\95 á\83\94á\83 á\83\97á\83®á\83\94á\83\9a</strong>.\ná\83\97á\83£ á\83\9eá\83 á\83\9dá\83\91á\83\9aá\83\94á\83\9bá\83\90 á\83\92á\83\90á\83\9cá\83\9bá\83\94á\83\9dá\83 á\83\93á\83\94á\83\91á\83\90, á\83\92á\83\97á\83®á\83\9dá\83\95á\83\97 [[Special:UserLogout|á\83\92á\83\90á\83\93á\83\98á\83\97 á\83¡á\83\98á\83¡á\83¢á\83\94á\83\9bá\83\98á\83\93á\83\90á\83\9c]] á\83\93á\83\90 á\83\99á\83\95á\83\9aá\83\90á\83\95 á\83¨á\83\94á\83\9bá\83\9dá\83\93á\83\98á\83\97, á\83\90á\83¡á\83\94á\83\95á\83\94 á\83\93á\83\90á\83 á\83¬á\83\9bá\83£á\83\9cá\83\93á\83\98á\83\97, á\83 á\83\9dá\83\9b á\83\97á\83¥á\83\95á\83\94á\83\9cá\83\98 á\83\91á\83 á\83\90á\83£á\83\96á\83\94á\83 á\83\98 á\83\98á\83«á\83\9aá\83\94á\83\95á\83\90 á\83\90á\83\9b á\83¡á\83\90á\83\98á\83¢á\83\98á\83\93á\83\90á\83\9c á\83¥á\83£á\83¥á\83\98á\83¡ á\83\92á\83\90á\83\9bá\83\9dá\83§á\83\94á\83\9cá\83\94á\83\91á\83\98á\83¡ á\83£á\83¤á\83\9aá\83\94á\83\91á\83\90á\83¡.",
        "import-invalid-interwiki": "შეუძლებელია იმპორტირება მოცემული ვიკიდან.",
        "import-error-edit": "გვერდი „$1“ იმპორტირება არ მოხდა, რადგან თქვენ არ გაქვთ მისი რედაქტირების უფლება.",
        "import-error-create": "გვერდი „$1“ იმპორტირება არ მოხდა, რადგან თქვენ არ გაქვთ მისი შექმნის უფლება.",
        "tooltip-search": "ძიება {{SITENAME}}",
        "tooltip-search-go": "მოიძიე გვერდი ზუსტად ამ სახელით",
        "tooltip-search-fulltext": "მოძებნე გვერდები, რომლებიც ამ ტექსტს შეიცავენ",
-       "tooltip-p-logo": "მთავარი გვერდი",
+       "tooltip-p-logo": "á\83\98á\83®á\83\98á\83\9aá\83\94á\83\97 á\83\9bá\83\97á\83\90á\83\95á\83\90á\83 á\83\98 á\83\92á\83\95á\83\94á\83 á\83\93á\83\98",
        "tooltip-n-mainpage": "იხილეთ მთავარი გვერდი",
        "tooltip-n-mainpage-description": "იხილეთ მთავარი გვერდი",
        "tooltip-n-portal": "პროექტის შესახებ, რა შეგიძლიათ გააკეთოთ, სად იპოვოთ",
        "pageinfo-watchers": "გვერდის დამკვირვებელთა რაოდენობა",
        "pageinfo-visiting-watchers": "გვერდის მნახველთა სია, რომლებიც ესტუმრნენ უკანასკნელ ცვლილებებს",
        "pageinfo-few-watchers": "სულ მცირე $1 {{PLURAL:$1|დამკვირვებელი|დამკვირვებელი}}",
+       "pageinfo-few-visiting-watchers": "შესაძლოა არის ან არ არის მომხმარებელი, რომელიც აკონტროლებს ბოლო ცვლილებებს",
        "pageinfo-redirects-name": "გადამისამართებების რაოდენობა ამ გვერდზე",
        "pageinfo-redirects-value": "$1",
        "pageinfo-subpages-name": "ამ გვერდის ქვეგვერდები",
        "version-libraries-authors": "ავტორები",
        "redirect": "გადამისამართება ფაილიდან, მომხმარებლიდან, გვერდიდან, ვერსიის ან ავტორიზაციის იდენტიფიკატორიდან",
        "redirect-legend": "გადამისამართება ფაილზე ან გვერდზე",
-       "redirect-summary": "á\83\94á\83¡ á\83\93á\83\90á\83\9bá\83®á\83\9bá\83\90á\83 á\83\94 á\83\92á\83\95á\83\94á\83 á\83\93á\83\98 á\83\90á\83\9bá\83\98á\83¡á\83\90á\83\9bá\83\90á\83 á\83\97á\83\94á\83\91á\83¡ á\83¤á\83\90á\83\98á\83\9aá\83\98á\83¡ (á\83¤á\83\90á\83\98á\83\9aá\83\98á\83¡ á\83¡á\83\90á\83®á\83\94á\83\9aá\83\98á\83\93á\83\90á\83\9c) á\83\92á\83\95á\83\94á\83 á\83\93á\83\96á\83\94, (á\83\92á\83\95á\83\94á\83 á\83\93á\83\98á\83¡ á\83\90á\83\9c á\83\95á\83\94á\83 á\83¡á\83\98á\83\98á\83¡ á\83\98á\83\93á\83\94á\83\9cá\83¢á\83\98á\83¤á\83\98á\83\99á\83\90á\83¢á\83\9dá\83 á\83\98á\83\93á\83\90á\83\9c) á\83\90á\83\9c á\83\9bá\83\9dá\83\9bá\83®á\83\9bá\83\90á\83 á\83\94á\83\91á\83\9aá\83\98á\83¡ á\83\92á\83\95á\83\94á\83 á\83\93á\83\96á\83\94 (á\83\9bá\83\9dá\83\9bá\83®á\83\9bá\83\90á\83 á\83\94á\83\91á\83\9aá\83\98á\83¡ á\83 á\83\90á\83\9dá\83\93á\83\94á\83\9cá\83\9dá\83\91á\83 á\83\98á\83\95á\83\98 á\83\98á\83\93á\83\94á\83\9cá\83¢á\83\98á\83¤á\83\98á\83\99á\83\90á\83¢á\83\9dá\83 á\83\98á\83\93á\83\90á\83\9c). á\83\92á\83\90á\83\9bá\83\9dá\83§á\83\94á\83\9cá\83\94á\83\91á\83\90: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]] á\83\90á\83\9c [[{{#Special:Redirect}}/user/101]].",
+       "redirect-summary": "á\83\94á\83¡ á\83\93á\83\90á\83\9bá\83®á\83\9bá\83\90á\83 á\83\94 á\83\92á\83\95á\83\94á\83 á\83\93á\83\98 á\83\90á\83\9bá\83\98á\83¡á\83\90á\83\9bá\83\90á\83 á\83\97á\83\94á\83\91á\83¡ á\83¤á\83\90á\83\98á\83\9aá\83\96á\83\94 (á\83¤á\83\90á\83\98á\83\9aá\83\98á\83¡ á\83¡á\83\90á\83®á\83\94á\83\9aá\83\98á\83\93á\83\90á\83\9c) á\83\92á\83\95á\83\94á\83 á\83\93á\83\96á\83\94, (á\83\92á\83\95á\83\94á\83 á\83\93á\83\98á\83¡ á\83\90á\83\9c á\83\95á\83\94á\83 á\83¡á\83\98á\83\98á\83¡ á\83\98á\83\93á\83\94á\83\9cá\83¢á\83\98á\83¤á\83\98á\83\99á\83\90á\83¢á\83\9dá\83 á\83\98á\83\93á\83\90á\83\9c), á\83\9bá\83\9dá\83\9bá\83®á\83\9bá\83\90á\83 á\83\94á\83\91á\83\9aá\83\98á\83¡ á\83\92á\83\95á\83\94á\83 á\83\93á\83\96á\83\94 (á\83\9bá\83\9dá\83\9bá\83®á\83\9bá\83\90á\83 á\83\94á\83\91á\83\9aá\83\98á\83¡ á\83 á\83\90á\83\9dá\83\93á\83\94á\83\9cá\83\9dá\83\91á\83 á\83\98á\83\95á\83\98 á\83\98á\83\93á\83\94á\83\9cá\83¢á\83\98á\83¤á\83\98á\83\99á\83\90á\83¢á\83\9dá\83 á\83\98á\83\93á\83\90á\83\9c) á\83\90á\83\9c á\83\9fá\83£á\83 á\83\9cá\83\90á\83\9aá\83\98á\83¡ á\83\9bá\83\9dá\83\9cá\83\90á\83ªá\83\94á\83\9bá\83\96á\83\94. á\83\92á\83\90á\83\9bá\83\9dá\83§á\83\94á\83\9cá\83\94á\83\91á\83\90:\n[[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], [[{{#Special:Redirect}}/user/101]], or [[{{#Special:Redirect}}/logid/186]].",
        "redirect-submit": "მიდი",
        "redirect-lookup": "ძიება:",
        "redirect-value": "მნიშვნელობა:",
        "tags-deactivate": "დეაქტივაცია",
        "tags-hitcount": "$1 ცვლილება",
        "tags-manage-no-permission": "თქვენ არ გაქვთ შეცვლილი დასათაურების მართვის უფლება",
+       "tags-manage-blocked": "თქვენ ვერ შეძლებთ ცვლილებების ტეგების მართვას სანამ დაბლოკილი ხართ.",
        "tags-create-heading": "ახალი ტეგის შექმნა",
        "tags-create-explanation": "კვლავ შექმნილი ტეგები ნაგულისხმევად იქნება შექმნილი და იქნება ხელმისაწვდომი მომხმარებლებისა და ბოტებისათვის",
        "tags-create-tag-name": "ტეგის სახელი:",
        "tags-apply-not-allowed-one": "დასათაურება «$1» ავტომატურად არ შეიძლება იქნას მიღებული.",
        "tags-apply-not-allowed-multi": "შემდეგი {{PLURAL:$2|ტეგი|ტეგები}} არ შეიძლება იყოს ხელით მიღებული: $1",
        "tags-update-no-permission": "თქვენ არ გაქვთ ტეგების დამატების ან შეცვლის უფლება ცალკეული ვერსიების ცვლილებებიდან ან ჟურნალების ჩანაწერებიდან",
+       "tags-update-blocked": "თქვენ ვერ შეძლებთ ცვლილებების ტეგების დამატებას ან წაშლას სანამ დაბლოკილი ხართ.",
        "tags-update-add-not-allowed-one": "ტეგი \"$1\" არ შეიძლება იყოს დამატებული მანუალურად",
        "tags-update-add-not-allowed-multi": "შემდეგი {{PLURAL:$2|ტეგი|ტეგები}} არ შეიძლება დამატებული იყოს ხელით: $1",
        "tags-update-remove-not-allowed-one": "დასათაურება \"$1\" არ შეიძლება იყოს წაშლილი",
        "logentry-suppress-block": "მომხმარებელმა $1 {{GENDER:$2|დაბლოკა}} {{GENDER:$4|$3}} ბლოკირების ვადაა $5 $6",
        "logentry-suppress-reblock": "მომხმარებელმა $1 {{GENDER:$2|შეცვალა}} ბლოკირების პარამეტრები {{GENDER:$4|$3}}-თვის  ბლოკირების ვადაა $5 $6",
        "logentry-import-upload": "$1 {{GENDER:$2|იმპორტირებული}} $3 ატვირთული ფაილი",
+       "logentry-import-upload-details": "$1-მ {{GENDER:$2|გადაიტანა}} $3 ფაილის ატვირთვით ($4 {{PLURAL:$4|ცვლილება|ცვლილება}})",
        "logentry-import-interwiki": "$1 {{GENDER:$2|იმპორტირებული}} $3 სხვა ვიკიდან",
+       "logentry-import-interwiki-details": "$1-მ {{GENDER:$2|გადაიტანა}} $3 $5-ზე ($4 {{PLURAL:$4|ცვლილება|ცვლილება}})",
        "logentry-merge-merge": "მომხმარებელმა $1 {{GENDER:$2|გააერთიანა}} $3 $4-ში ($5-მდე ვერსია)",
        "logentry-move-move": "მომხმარებელმა $1 გვერდი „$3“ {{GENDER:$2|გადაიტანა}} გვერდზე „$4“",
        "logentry-move-move-noredirect": "მომხმარებელმა $1 გვერდი „$3“ {{GENDER:$2|გადაიტანა}} გვერდზე „$4“ გადამისამართების დატოვების გარეშე",
        "logentry-newusers-create2": "მომხმარებლის ანგარიში $3 {{GENDER:$2|შექმნა}} მომხმარებელმა $1",
        "logentry-newusers-byemail": "მომხმარებლის ანგარიში $3 {{GENDER:$2|შექმნა}} მომხმარებელმა $1 და პაროლი გაგზავნა ელ. ფოსტით",
        "logentry-newusers-autocreate": "ავტომატურად {{GENDER:$2|შეიქმნა}} მომხმარებლის ანგარიში $1",
+       "logentry-protect-move_prot": "$1-მ {{GENDER:$2|გადაიტანა}} დაცვის კონფიგურაცია $4-დან $3-ზე",
+       "logentry-protect-unprotect": "$1-მა {{GENDER:$2|მოუხსნა}} დაცვა $3-ს",
        "logentry-protect-protect": "$1-მ {{GENDER:$2|დაიცვა}} $3 $4",
        "logentry-protect-protect-cascade": "$1-მ {{GENDER:$2|დაიცვა}} $3 $4 [კასკადური]",
        "logentry-protect-modify": "$1-მ {{GENDER:$2|შეცვალა}} დაცვის დონე $3 $4-სთვის",
        "expand_templates_generate_xml": "XML-ის ხის გარჩევის ჩვენება",
        "expand_templates_generate_rawhtml": "HTML ჩვენება",
        "expand_templates_preview": "წინა",
-       "expand_templates_preview_fail_html": "რადგან translatewiki.net-ზე ჩართული დაუმუშავებელი HTML-ის  მონაცემთა სესიის დაკარვა მოხდა, წინასწარი გადახედვა დამალულია უსაფრთხოების მიზნით, JavaScript-შეტევების საწინააღმდეგოდ.  \n\nთუ ეს იყო წინასწარი გადახედვის სწორი ცდა, გთხოვთ, კიდევ ერთხელ სცადეთ. თუკი თქვენ ისევ და ისევ არ გამოგდით, სცადეთ [[Special:UserLogout|სამუშაო სეანსის დასრულება]] და ხელახალი ავტორიზება.",
+       "expand_templates_preview_fail_html": "<em>რადგან {{SITENAME}}-ზე ჩართული დაუმუშავებელი HTML-ის  მონაცემთა სესიის დაკარვა მოხდა, წინასწარი გადახედვა დამალულია უსაფრთხოების მიზნით, JavaScript-შეტევების საწინააღმდეგოდ. </em> \n\n<strong>თუ ეს იყო წინასწარი გადახედვის სწორი ცდა, გთხოვთ, კიდევ ერთხელ სცადეთ.</strong> თუკი პრობლემა განმეორდება, [[Special:UserLogout|გადით სისტემიდან]] და კვლავ შემოდით, ასევე დარწმუნდით, რომ თქვენი ბრაუზერი იძლევა ამ საიტიდან ქუქის გამოყენების უფლებას.",
        "expand_templates_preview_fail_html_anon": "რადგან translatewiki.net-ზე ჩართული დაუმუშავებელი HTML და თქვენ არ გაქვთ გავლილი ავტორიზაცია, წინასწარი გადახედვა დამალულია უსაფრთხოების მიზნით, JavaScript-შეტევების საწინააღმდეგოდ.  \n\nთუ ეს იყო წინასწარი გადახედვის სწორი ცდა, გთხოვთ, [[Special:UserLogin|შედით]] და ხელმეორედ სცადეთ.",
        "expand_templates_input_missing": "თქვენ უნდა შეიყვანოთ მცირე ტექსტი მაინც.",
        "pagelanguage": "გვერდის ენის შეცვლა",
        "api-error-blacklisted": "გთხოვთ, აირჩიეთ სხვა, აღწერილობითი სათაური.",
        "sessionprovider-generic": "$1 სესიები",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "cookie-სთან დაკავშირებული სესიები",
+       "sessionprovider-nocookies": "შესაძლოა ქუქები გათიშულია. გთხოვთ ჩართეთ და სცადეთ განმეორებით.",
        "randomrootpage": "შემთხვევითი ძირეული გვერდი"
 }
index 15d9801..4cf2d2c 100644 (file)
@@ -56,7 +56,8 @@
                        "Macofe",
                        "Yearning",
                        "고솜",
-                       "Sternradio"
+                       "Sternradio",
+                       "Joolee0104"
                ]
        },
        "tog-underline": "링크에 밑줄:",
@@ -91,6 +92,7 @@
        "tog-watchlisthidebots": "주시문서 목록에서 봇 편집을 숨기기",
        "tog-watchlisthideminor": "주시문서 목록에서 사소한 편집을 숨기기",
        "tog-watchlisthideliu": "주시문서 목록에서 로그인한 사용자의 편집을 숨기기",
+       "tog-watchlistreloadautomatically": "필터가 수정될 때마다 주시문서 목록 자동으로 새로 고치기 (자바스크립트 필요)",
        "tog-watchlisthideanons": "주시문서 목록에서 익명 사용자의 편집을 숨기기",
        "tog-watchlisthidepatrolled": "주시문서 목록에서 점검한 편집을 숨기기",
        "tog-watchlisthidecategorization": "페이지 종류 숨기기",
        "october-date": "10월 $1일",
        "november-date": "11월 $1일",
        "december-date": "12월 $1일",
+       "period-am": "오전",
+       "period-pm": "오후",
        "pagecategories": "{{PLURAL:$1|분류}}",
        "category_header": "\"$1\" 분류에 속하는 문서",
        "subcategories": "하위 분류",
        "title-invalid-interwiki": "요청한 페이지 제목에 제목에는 사용될 수 없는 위키간 링크가 있습니다.",
        "title-invalid-talk-namespace": "요청한 페이지 제목이 존재하지 않는 토론 문서를 가리킵니다.",
        "title-invalid-characters": "요청된 문서 제목이 잘못된 문자를 포함하고 있습니다: \"$1\".",
+       "title-invalid-too-long": "페이지 제목이 너무 깁니다. 페이지 제목 길이는 최대 $1 까지 설정할 수 있습니다.",
+       "title-invalid-leading-colon": "페이지 제목에 잘못된 문자가 포함되어 있습니다.",
        "perfcached": "다음 자료는 캐시된 것이며 최신이 아닐 수 있습니다. 캐시에 최대 {{PLURAL:$1|결과 한 개|결과 $1개}}가 있습니다.",
        "perfcachedts": "다음 자료는 캐시된 것으로, $1에 마지막으로 업데이트되었습니다. 캐시에 최대 {{PLURAL:$4|결과 한 개|결과 $4개}}가 있습니다.",
        "querypage-no-updates": "이 문서의 갱신이 현재 중지되어 있습니다.\n자료가 잠시 갱신되지 않을 것입니다.",
        "virus-scanfailed": "검사 실패 (코드 $1)",
        "virus-unknownscanner": "알 수 없는 안티 바이러스:",
        "logouttext": "<strong>이제 로그아웃했습니다.</strong>\n\n브라우저 캐시를 지울 때까지 일부 문서에서 아직 로그인이 되어 있는 것처럼 보일 수 있음에 유의하세요.",
+       "cannotlogoutnow-title": "지금 로그아웃 할 수 없습니다",
+       "cannotlogoutnow-text": "$1 사용 중에는 로그아웃이 불가능합니다.",
        "welcomeuser": "$1님, 환영합니다!",
        "welcomecreation-msg": "계정이 만들어졌습니다.\n[[Special:Preferences|{{SITENAME}} 사용자 환경 설정]]을 바꿀 수 있습니다.",
        "yourname": "사용자 이름:",
        "remembermypassword": "이 브라우저에서 로그인 상태를 저장하기 (최대 $1{{PLURAL:$1|일}})",
        "userlogin-remembermypassword": "로그인 상태를 유지하기",
        "userlogin-signwithsecure": "보안 연결 사용",
+       "cannotloginnow-title": "지금 로그인 할 수 없습니다.",
+       "cannotloginnow-text": "$1 사용 중에는 로그인이 불가능합니다.",
        "yourdomainname": "도메인 이름:",
        "password-change-forbidden": "이 위키에서 비밀번호를 바꿀 수 없습니다.",
        "externaldberror": "바깥 인증 데이터베이스에 오류가 있거나 바깥 계정을 새로 고칠 권한이 없습니다.",
        "wrongpasswordempty": "비밀번호를 입력하지 않았습니다.\n다시 시도하세요.",
        "passwordtooshort": "비밀번호는 {{PLURAL:$1|$1 글자}} 이상이어야 합니다.",
        "passwordtoolong": "비밀번호는 {{PLURAL:$1|1자|$1자}}보다 길어서는 안 됩니다.",
+       "passwordtoopopular": "자주 사용되는 비밀번호는 쓸 수 없습니다. 더 고유한 비밀번호를 선택하세요.",
        "password-name-match": "비밀번호는 사용자 계정 이름과 반드시 달라야 합니다.",
        "password-login-forbidden": "이 사용자 계정 이름과 비밀번호는 사용할 수 없습니다.",
        "mailmypassword": "비밀번호 재설정",
        "resetpass_submit": "비밀번호를 설정하고 로그인하기",
        "changepassword-success": "비밀번호가 성공적으로 바뀌었습니다!",
        "changepassword-throttled": "최근 너무 많이 로그인을 시도했습니다.\n$1 뒤에 다시 시도하세요.",
+       "botpasswords": "봇 비밀번호",
        "resetpass_forbidden": "비밀번호를 바꿀 수 없습니다",
        "resetpass-no-info": "이 특수 문서에 직접 접근하려면 반드시 로그인해야 합니다.",
        "resetpass-submit-loggedin": "비밀번호 바꾸기",
        "showpreview": "미리 보기",
        "showdiff": "차이 보기",
        "blankarticle": "<strong>경고:</strong> 만들려는 문서가 비어 있습니다.\n\"{{int:savearticle}}\"을 다시 클릭하면, 문서에 내용이 없이 만들어집니다.",
-       "anoneditwarning": "<strong>경고:</strong> 로그인하고 있지 않습니다. 편집하면 당신의 IP 주소가 공개적으로 보여집니다. <strong>[$1 로그인]</strong>하거나 <strong>[$2 계정을 만들면]</strong>, 당신의 편집에 다른 이익과 함께, 사용자 이름이 표시됩니다.",
+       "anoneditwarning": "<strong>경고:</strong> 로그인을 하고 있지 않습니다. 편집을 하게 되면 IP 주소가 공개적으로 보여집니다. <strong>[$1 로그인]</strong>하거나 <strong>[$2 계정을 생성하면]</strong>, 편집 시에 다른 이점과 함께 사용자 이름이 표시됩니다.",
        "anonpreviewwarning": "<em>로그인하고 있지 않습니다. 문서를 저장하면 당신의 IP 주소가 문서의 편집 역사에 남게 됩니다.</em>",
        "missingsummary": "'''알림:''' 편집 요약을 적지 않았습니다.\n이대로 \"{{int:savearticle}}\"을 클릭하면 편집 요약 없이 저장됩니다.",
        "selfredirect": "<strong>경고:</strong> 자기 자신으로 문서를 넘겨주고 있습니다.\n넘겨줄 대상을 잘못 입력했거나, 잘못된 문서를 편집하고 있을 수 있습니다.\n\"{{int:savearticle}}\"을 입력하면, 넘겨주기 문서가 생성될 것입니다.",
        "recentchanges-submit": "보기",
        "rcnotefrom": "아래는 <strong>$3, $4</strong>부터 시작하는 {{PLURAL:$5|바뀜이 있습니다}}. (최대 <strong>$1</strong>개가 보여집니다)",
        "rclistfrom": "$3 $2부터 시작하는 새로 바뀐 문서 보기",
-       "rcshowhideminor": "사소한 편집 $1",
+       "rcshowhideminor": "사소한 편집 $1",
        "rcshowhideminor-show": "보이기",
        "rcshowhideminor-hide": "숨기기",
-       "rcshowhidebots": "봇 $1",
+       "rcshowhidebots": "봇 $1",
        "rcshowhidebots-show": "보이기",
        "rcshowhidebots-hide": "숨기기",
-       "rcshowhideliu": "등록된 사용자 $1",
+       "rcshowhideliu": "등록된 사용자 $1",
        "rcshowhideliu-show": "보이기",
        "rcshowhideliu-hide": "숨기기",
        "rcshowhideanons": "익명 사용자를 $1",
        "rcshowhidepatr": "점검한 편집을 $1",
        "rcshowhidepatr-show": "보이기",
        "rcshowhidepatr-hide": "숨기기",
-       "rcshowhidemine": "내 편집 $1",
+       "rcshowhidemine": "내 편집 $1",
        "rcshowhidemine-show": "보이기",
        "rcshowhidemine-hide": "숨기기",
+       "rcshowhidecategorization": "문서 분류 $1",
        "rcshowhidecategorization-show": "보이기",
        "rcshowhidecategorization-hide": "숨기기",
        "rclinks": "최근 $2일간의 $1개 바뀐 문서 보기<br />$3",
        "upload-dialog-button-done": "완료",
        "upload-dialog-button-save": "저장",
        "upload-dialog-button-upload": "올리기",
-       "upload-form-label-select-file": "파일을 선택해주세요.",
        "upload-form-label-infoform-title": "자세한 사항",
        "upload-form-label-infoform-name": "이름",
        "upload-form-label-infoform-description": "설명",
        "foreign-structured-upload-form-label-infoform-categories": "분류",
        "foreign-structured-upload-form-label-infoform-date": "날짜",
        "foreign-structured-upload-form-label-not-own-work-message-shared": "이 파일의 저작권을 소유하지 않거나 다른 라이선스로 배포하고 싶다면 [https://commons.wikimedia.org/wiki/Special:UploadWizard 공용 파일 올리기 마법사]를 이용해 보세요.",
-       "foreign-structured-upload-form-3-label-yes": "예",
-       "foreign-structured-upload-form-3-label-no": "아니오",
        "backend-fail-stream": "\"$1\" 파일을 스트림할 수 없습니다.",
        "backend-fail-backup": "\"$1\" 파일을 백업할 수 없습니다.",
        "backend-fail-notexists": "$1 파일이 존재하지 않습니다.",
        "querypage-disabled": "이 특수 문서는 성능상의 이유로 비활성화되었습니다.",
        "apihelp": "API 도움말",
        "apihelp-no-such-module": "\"$1\" 모듈을 찾을 수 없습니다.",
+       "apisandbox": "API 실험실",
+       "apisandbox-api-disabled": "이 사이트에서는 API가 꺼져 있습니다.",
+       "apisandbox-intro": "'''미디어위키 웹 서비스 API'''를 시험해보려면 이 페이지를 이용해보세요. API 용법에 대해서는 [//www.mediawiki.org/wiki/API:Main_page API 문서]을 참고하십시오. 예: [//www.mediawiki.org/wiki/API#A_simple_example 대문의 내용 요청하기]. 더 많은 예를 보려면 액션을 선택하세요.\n\n여기가 연습장이라도 이 페이지에서 실행하는 동작때문에 위키를 변경할 수도 있다는 점에 유의하십시오.",
+       "apisandbox-unfullscreen": "페이지 보기",
+       "apisandbox-submit": "요청하기",
+       "apisandbox-reset": "지우기",
+       "apisandbox-retry": "재시도",
+       "apisandbox-helpurls": "도움 링크들",
+       "apisandbox-examples": "예시",
+       "apisandbox-results": "결과",
+       "apisandbox-request-url-label": "요청 URL:",
+       "apisandbox-request-time": "요청 처리 시간: $1",
        "booksources": "책 찾기",
        "booksources-search-legend": "책 원본 검색",
        "booksources-isbn": "ISBN:",
        "log-title-wildcard": "다음 글로 시작하는 제목 검색",
        "showhideselectedlogentries": "선택한 기록 항목 보이기/숨기기",
        "log-edit-tags": "선택한 기록 항목의 태그를 편집",
+       "checkbox-select": "선택: $1",
+       "checkbox-all": "모두",
+       "checkbox-none": "모두 제외",
+       "checkbox-invert": "선택 반전",
        "allpages": "모든 문서 목록",
        "nextpage": "다음 문서 ($1)",
        "prevpage": "이전 문서 ($1)",
index c1d1cff..b2bdd0b 100644 (file)
        "uploaded-script-svg": "Mer han e verbodde Skrepp_Elemänd en dä huhjelahde <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Scalable Vector Graphics\">SVG</i>_Dattei jefonge: „$1“",
        "uploaded-hostile-svg": "Mer han onseescher <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Cascading Style Sheet\">CSS</i>-Befähle en enem „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">style</code>“-Ellemänt vun dä huhjelahde <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Scalable Vector Graphics\">SVG</i>_Dattei jefonge.",
        "uploaded-event-handler-on-svg": "Projramme för Ä'eijschneße ze behanndelle „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">&lt;$1=\"$2\"&gt;</code>“ ennzesäze es en <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Scalable Vector Graphics\">SVG</i>_Datteije verbodde.",
+       "uploaded-href-attribute-svg": "En <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Scalable Vector Graphics\">SVG</i>_Datteije darrev övver <code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">href</code> blohß op <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"HyperText Transfer Protocol\">HTTP://</i> udder <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"HyperText Transfer Protocol Secure - HTTP övver SSL - HTTP övver TSL\">HTTPS://</i> verlenk wähde. Heh es ävver <code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">&lt;$1 $2=\"$3\"&gt;</code> dren.",
        "uploaded-href-unsafe-target-svg": "Mer han ene „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">href</code>“-Befähl obb e onseescher Zihl „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">&lt;$1 $2=\"$3\"&gt;</code>“ en dä huhjelahde <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Scalable Vector Graphics\">SVG</i>_Dattei jefonge.",
        "uploaded-animate-svg": "Mer han dä Befähl „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">animate</code>“ en dä huhjelahde \n<i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Scalable Vector Graphics\">SVG</i>_Dattei jefonge, dä ene „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">href</code>“-Befähl verändere künnt övver de „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">from</code>“-Eijeschaff „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">&lt;$1 $2=\"$3\"&gt;</code>“.",
        "uploaded-setting-event-handler-svg": "Ed es verbodde, Projramme för Ä'eijschneße ze behanndelle ennzesäze, un de Datteije, di dat donn, wähde jeschpächt. Mer han „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">&lt;$1 $2=\"$3\"&gt;</code>“ en dä huhjelahde <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Scalable Vector Graphics\">SVG</i>_Dattei jefonge.",
        "upload-dialog-button-done": "Jedonn",
        "upload-dialog-button-save": "Faßhalde",
        "upload-dialog-button-upload": "Lohß Jonn!",
-       "upload-form-label-select-file": "De ußjesöhk Dattei",
        "upload-form-label-infoform-title": "Eijnzelheijte",
        "upload-form-label-infoform-name": "Nahme",
        "upload-form-label-infoform-description": "Äkliehrong",
        "querypage-disabled": "Heh di {{int:specialpage}} es ußjeschalldt, domet dä ẞööver jät winnijer ze brassele hät.",
        "apihelp": "Hölp för de <i lang=\"en\" xml:lang=\"en\" title=\"Application Programmers Interface\">API</i>",
        "apihelp-no-such-module": "Et Moduhl „$1“ wood nit jefonge.",
+       "apisandbox": "De <i lang=\"en\">API</i> ußprobeere",
+       "apisandbox-api-disabled": "Dat <i lang=\"en\">API</i> es en heh dämm Wiki afjeschalldt.",
+       "apisandbox-intro": "Op heh dä Sigg kanns De met dä '''MediaWiki web service <i lang=\"en\">API</i>''' eröm schpelle.\nBeloor Der de Einzelheite, wi di jebruch weed, op dä iere [//www.mediawiki.org/wiki/API:Main_page Sigg met de Verklieronge].\nE Beiscpell: [//www.mediawiki.org/wiki/API#A_simple_example De Houpsigg holle].\nSöhk ene {{int:Apisb-label-action}} uß, öm mieh Beishpell aanjezeisch ze krijje.\nOch wann dat heh nor zom Ußprobeere es, kann dat, wat De heh mähß, et Wiki verändere.",
+       "apisandbox-submit": "Lohß jonn!",
+       "apisandbox-reset": "Läddesch maache",
+       "apisandbox-examples": "Bäijshpell",
+       "apisandbox-dynamic-parameters": "Zohsäzlejje Parrameetere",
+       "apisandbox-results": "Erus jekumme es",
+       "apisandbox-request-url-label": "Dä <i lang=\"en\">URL</i> vun dä Aanfrooch:",
+       "apisandbox-request-time": "De Zigg vum Afroof: $1",
        "booksources": "Böcher",
        "booksources-search-legend": "Söök noh Bezochsquelle för Bööcher",
        "booksources-isbn": "ISBN:",
index bc6c06b..5303796 100644 (file)
        "retypenew": "Şîfreya nû careke din binîvîse",
        "resetpass_submit": "Şîfreyê pêkbîne û têkeve",
        "changepassword-success": "Guhertine şîfreya te serkeftî bû!",
+       "botpasswords-label-update": "Rojane bike",
+       "botpasswords-label-cancel": "Betal bike",
        "resetpass_forbidden": "Şîfre nikarin werin guhertin",
        "resetpass-submit-loggedin": "Şîfreyê biguherîne",
        "resetpass-submit-cancel": "Betal bike",
        "sig_tip": "Îmze û demxeya wext ya te",
        "hr_tip": "Rastexêza berwarî (kêm bi kar bîne)",
        "summary": "Kurte (Te çi kir?):",
-       "subject": "Mijar/sernivîs:",
+       "subject": "Mijar:",
        "minoredit": "Ev guhertineke biçûk e",
        "watchthis": "Vê gotarê bişopîne",
        "savearticle": "Rûpelê tomar bike",
        "preview": "Pêşdîtin",
        "showpreview": "Pêşdîtinê nîşan bide",
        "showdiff": "Guherandinan nîşan bide",
-       "anoneditwarning": "<strong>Hişyarî:<strong> Tu netêketî yî! Navnîşana IP'ya te wê di dîroka guherandina vê rûpelê de bê tomarkirin. Heke tu <strong>[$1 têkevî]</strong>, li gel sûdên te yên din guhertinên ku tu bikî jî wê ji nasnavê te re bê atfkirin.",
+       "anoneditwarning": "<strong>Hişyarî:<strong> Tu netêketî yî! Navnîşana IP'ya te wê di dîroka guherandina vê rûpelê de bê tomarkirin. Heke tu <strong>[$1 têkevî]</strong> an jî  <strong>[$2 hesabekî çêbikî]</strong>, li gel sûdên te yên din guhertinên ku tu bikî jî wê ji nasnavê te re bê atfkirin.",
        "anonpreviewwarning": "''Tu ne têketî yî. Tomarkirin wê navnîşana IP'ya te di dîroka guhertinan de nîşan bide.''",
        "missingsummary": "<span style=\"color:#990000;\">'''Zanibe:'''</span> Te nivîsekî kurt ji bo guherandinê ra nenivîsand. Eger tu niha carekî din li Tomar xê, guherandinê te vê nivîsekî kurt yê were tomarkirin.",
        "missingcommenttext": "Ji kerema xwe kurteya naverokê li jêr binivisîne.",
        "editusergroup": "Komên bikarhêneran biguherîne",
        "editinguser": "Mafên bikarhêner '''[[User:$1|$1]]''' ([[User talk:$1|{{int:talkpagelinktext}}]]{{int:pipe-separator}}[[Special:Contributions/$1|{{int:contribslink}}]]) tên guhertin",
        "userrights-editusergroup": "Komên bikarhêneran biguherîne",
-       "saveusergroups": "Komên bikarhêneran tomar bike",
+       "saveusergroups": "Komên {{GENDER:$1|bikarhêneran}} tomar bike",
        "userrights-groupsmember": "Endamê/a:",
        "userrights-groups-help": "Tu dikarî komên bikarhêneran ên vê/vî bikarhênerê/î biguherînî:\n* Qutiyeke nîşankirî dibêje ku ev bikarhêner di wê komê de ye.\n* Qutiyeke nenîşankirî dibêje ku ev bikarhêner ne di wê komê de ye.\n* Stêrkek (*) nîşan dide ku tu nikarî wê komê jêbibî, heke te berê ew lê zêde kiribe.",
        "userrights-reason": "Sedem:",
        "right-userrights-interwiki": "Mafên bikarhênerên li ser wîkiyên din biguherîne",
        "right-sendemail": "Ji bikarhênerên di re ename bişîne",
        "newuserlogpage": "Çêkirina hesabê nû",
+       "newuserlogpagetext": "Ev têketina hesabên bikarhêneriyê ye ên ku nû hatine afirandin.",
        "rightslog": "Guhertina mafê bikarhêneriyê",
        "rightslogtext": "Ev guhertineke ji bo mafên bikarhêneriyê ye.",
        "action-read": "vê rûpelê bixwîne",
        "boteditletter": "b",
        "number_of_watching_users_pageview": "[{{PLURAL:$1|bikarhênerek|$1 bikarhêner}} vê rûpelê {{PLURAL:$1|dişopîne|dişopînin}}.]",
        "rc_categories_any": "Qet",
+       "rc-change-size-new": "Piştî guhertinê $1 {{PLURAL:$1|bayt}}",
        "newsectionsummary": "/* $1 */ beşeke nû",
        "rc-enhanced-expand": "Hûragahiyan nîşan bide",
        "rc-enhanced-hide": "Kitûmatan veşêre",
        "watchthisupload": "Vê rûpelê bişopîne",
        "filewasdeleted": "Data'yek bi vê navê hatibû barkirin û jêbirin. Xêra xwe li $1 seke ku barkirina te hêja ye ya na.",
        "filename-bad-prefix": "Nava wê data'yê, yê tu niha bardikê, bi '''\"$1\"''' destpêdike. Kamêrayên dîjîtal wan navan didin wêneyên xwe. Ji kerema xwe navekî baştir binivisîne ji bo mirov zûtir zanibin ku şayeşê vê wêneyê çî ye.",
-       "upload-success-subj": "Barkirina serkeftî",
-       "upload-failure-subj": "Pirsgirêka barkirinê",
-       "upload-warning-subj": "Hişyariya barkirinê",
        "upload-file-error": "Çewtiya navxweyî",
        "upload-dialog-button-cancel": "Betal bike",
        "upload-dialog-button-done": "Çêbû",
        "upload-dialog-button-save": "Tomar bike",
        "upload-dialog-button-upload": "Bar bike",
-       "upload-form-label-select-file": "Dosyeyê hilbijêre",
        "upload-form-label-infoform-title": "Detay",
        "upload-form-label-infoform-name": "Nav",
        "upload-form-label-infoform-description": "Danasîn",
        "notargettitle": "Armanc tune",
        "pager-newer-n": "{{PLURAL:$1|nûtir 1|nûtir $1}}",
        "pager-older-n": "{{PLURAL:$1|kevintir 1|kevintir $1}}",
+       "apisandbox-unfullscreen": "Rûpelê nîşan bide",
+       "apisandbox-helpurls": "Girêdanên alîkariyê",
+       "apisandbox-examples": "Mînak",
+       "apisandbox-results": "Encam",
        "booksources": "Çavkaniyên pirtûkan",
        "booksources-search-legend": "Li çavkanîyên pirtûkan bigere",
        "booksources-search": "Lêgerîn",
        "wlheader-showupdated": "Ev rûpela hatî guhertin dema te lê meyzand bi '''nivîsa stûr''' tê xuyakirin.",
        "wlnote": "Niha {{PLURAL:$1|xeyrandinê|'''$1''' xeyrandinên}} dawî yê {{PLURAL:$2|seetê|'''$2''' seetên}} dawî {{PLURAL:$1|tê|tên}} dîtin.",
        "wlshowlast": "Guhertinên berî $1 saetan, $2 rojan, ya  nîşan bide",
-       "watchlistall2": "hemû",
        "watchlist-hide": "Veşêre",
        "watchlist-submit": "Nîşan bide",
        "wlshowhideliu": "bikarhênerên tomarkirî",
        "databasenotlocked": "Danegeh ne girtî ye.",
        "move-page": "$1 bigerîne",
        "move-page-legend": "Vê rûpelê bigerîne",
+       "movepagetext": "Bi bikaranîna formê li jêr tu dikarî navê rûpelekê biguherînî, û dikarî hemû dîroka wê bibî rûpela bi navê nû.\nSernavê kevn dê wek beralîkirineke ber bi sernavê nû ve bê xuyakirin.\nTu dikarî beralîkirinên ku nîşanî rûpela orjînal didin, bidî sererastkirin.Ji bîr neke ku tu divê piştre [[Special:DoubleRedirects|beralîkirinên dubare]] an jî [[Special:BrokenRedirects|xirabûyî]] kontrol bikî. Tu ji bo nîşandana girêdanê ber bi armanca rast ve berpirsiyar î.\n\nBizanibe navê rûpelekê dê <strong>neyê</strong> guherandin heke ku rûpeleke nû bi vî navî hebe; lê heke ew rûpela nû bi wî navî beralîkirinek be û dîroka wê tune be, dê nav bê guherandin. Ev tê wê wateyê ku tu dikarî guherandina nav bizîvirînî pêş gava te çewtiyek kiribe.Lê tu nikarî li ser rûpeleka heye binivîsînî.\n\n<strong>Agahî:</strong>\nDibe ku beralîkirinek bibe sedemê encamên bibandor bo rûpelên ku gelek caran serlêdan lê tên kirin; lewma pêwist e ku tu di derbarê van encaman de agahdar bî, berî ku vî karî bidomînî.",
        "movepagetalktext": "Rûpela '''gotûbêjê''' vê rûpelê wê were, eger hebe, gerandin. Lê ev tişta nameşe, eger\n\n*berê gotûbêjek bi wî navî hebe ya\n*tu tiştekî jêr hilbijêrê.\n\nEger ev mişkla çêbû, tu gireke vê rûpelê bi xwe bigerînê.\n\nXêra xwe navê nû û sedemê navgerandinê binivisîne.",
        "movenologintext": "Tu dive bikarhênereke qeydkirî bî û [[Special:UserLogin|werî nav sîstemê]]\nda bikarî navê wê rûpelê biguherînî.",
        "movenotallowed": "Mafên te bo guherandina navên gotaran tune ye.",
        "import-upload": "Daneyên XMLê bar bike",
        "importlogpage": "Têketina tevlîkirinê",
        "javascripttest": "JavaScript tê testkirin",
-       "tooltip-pt-userpage": "Rûpela min",
+       "tooltip-pt-userpage": "Rûpela {{GENDER:|Te}}",
        "tooltip-pt-anonuserpage": "Rûpela bikarhênerê ji bo navnîşana ÎP ku tu sererast dikî wekî",
-       "tooltip-pt-mytalk": "Gotûbêja min",
-       "tooltip-pt-preferences": "Hevyazên min",
+       "tooltip-pt-mytalk": "Gotûbêja {{GENDER:|Te}}",
+       "tooltip-pt-preferences": "Tercîhên {{GENDER:|te}}",
        "tooltip-pt-watchlist": "The list of pages you",
-       "tooltip-pt-mycontris": "Lîsteya beşdariyên min",
+       "tooltip-pt-mycontris": "Lîsteyekê beşdariyên {{GENDER:|te}}",
+       "tooltip-pt-login": "Têketina we tê teşwîqkirin; lêbelê, en ne elzem e",
        "tooltip-pt-logout": "Derkeve",
        "tooltip-ca-talk": "Gotûbêj li ser rûpela naverokê",
        "tooltip-ca-edit": "Vê rûpelê biguherîne",
        "tooltip-t-recentchangeslinked": "Recent changes in pages linking to this page",
        "tooltip-feed-rss": "RSS feed'ên ji bo rûpelê",
        "tooltip-feed-atom": "Atom feed'ên ji bo vê rûpelê",
-       "tooltip-t-contributions": "Lîsteya beşdariyên bikarhêner bibîne",
+       "tooltip-t-contributions": "Lîsteyekî beşdariyên {{GENDER:$1|vê bikarhênerê}} bibîne",
        "tooltip-t-emailuser": "Jê re name bişîne",
        "tooltip-t-info": "Bêhtir agahî di derbarê vê rûpelê de",
        "tooltip-t-upload": "Dosyeyan bar bike",
        "expand_templates_output": "Encam",
        "expand_templates_ok": "Baş e",
        "expand_templates_preview": "Pêşdîtin",
+       "pagelanguage": "Zimanê rûpelê biguherîne",
        "pagelang-language": "Ziman",
        "pagelang-select-lang": "Zimanekî hilbijêre",
        "pagelang-submit": "Tomar bike",
        "right-pagelang": "Zimanê rûpelê biguherîne",
        "action-pagelang": "zimanê rûpelê biguherîne",
-       "log-name-pagelang": "Têketina ziman biguherîne",
+       "log-name-pagelang": "Têketina guherandina ziman",
        "mediastatistics-header-total": "Hemû dosye",
        "special-characters-group-latin": "Latînî",
        "special-characters-group-latinextended": "Latînî berfirehkirî",
index b274dfe..5bb32c1 100644 (file)
        "otherlanguages": "In aliis linguis",
        "redirectedfrom": "(Redirectum de $1)",
        "redirectpagesub": "Pagina redirectionis",
-       "lastmodifiedat": "Novissima mutatio die $2 hora $1 facta.",
+       "lastmodifiedat": "Novissima mutatio die $1 hora $2 facta.",
        "viewcount": "Haec pagina iam vista est {{PLURAL:$1|semel|$1 vices}}.",
        "protectedpage": "Pagina protecta",
        "jumpto": "Salire ad:",
        "shown-title": "Monstrare $1 {{PLURAL:$1|eventum|eventus}} per paginam",
        "viewprevnext": "Videre ($1 {{int:pipe-separator}} $2) ($3).",
        "searchmenu-exists": "'''Iam est pagina \"[[:$1]]\"'''",
-       "searchmenu-new": "<strong>Si vis, paginam \"[[:$1]]\" crea!<strong> {{PLURAL:$2|0=|Conferatur etiam pagina sequens, ubi quaesitum quodam modo continetur.|Conferantur etiam paginae $2 sequentes, in quibus quaesitum quodam modo continetur.}}",
+       "searchmenu-new": "<strong>Si vis, paginam \"[[:$1]]\" crea!</strong> {{PLURAL:$2|0=|Conferatur etiam pagina sequens, ubi quaesitum quodam modo continetur.|Conferantur etiam paginae $2 sequentes, in quibus quaesitum quodam modo continetur.}}",
        "searchprofile-articles": "Paginae contentorum",
        "searchprofile-images": "Multimedia",
        "searchprofile-everything": "Omnia",
index 30252c6..5867da1 100644 (file)
        "upload-dialog-button-done": "Fäerdeg",
        "upload-dialog-button-save": "Späicheren",
        "upload-dialog-button-upload": "Eroplueden",
-       "upload-form-label-select-file": "Fichier eraussichen",
        "upload-form-label-infoform-title": "Detailer",
        "upload-form-label-infoform-name": "Numm",
        "upload-form-label-infoform-description": "Beschreiwung",
        "foreign-structured-upload-form-label-own-work": "Dëst ass mäin eegent Wierk",
        "foreign-structured-upload-form-label-infoform-categories": "Kategorien",
        "foreign-structured-upload-form-label-infoform-date": "Datum",
-       "foreign-structured-upload-form-3-label-question-website": "Hutt Dir dëst Bild vun engem Internetsite erofgelueden, oder beim Sichen no engem Bild fonnt?",
-       "foreign-structured-upload-form-3-label-question-ownwork": "Hutt Dir dëst Bild (Foto, Zeechnung, asw.) selwer gemaacht?",
-       "foreign-structured-upload-form-3-label-yes": "Jo",
-       "foreign-structured-upload-form-3-label-no": "Neen",
        "backend-fail-stream": "De Fichier $1 konnt net iwwerdroe ginn.",
        "backend-fail-backup": "De Fichier $1 konnt net geséchert ginn.",
        "backend-fail-notexists": "De Fichier $1 gëtt et net.",
        "querypage-disabled": "Dës Spezialsäit ass aus Performance-Grënn ausgeschalt.",
        "apihelp": "API-Hëllef",
        "apihelp-no-such-module": "Modul \"$1\" net fonnt.",
+       "apisandbox": "API-Sandkëscht",
+       "apisandbox-api-disabled": "API ass op dësem Site ausgeschalt.",
+       "apisandbox-unfullscreen": "Säit weisen",
+       "apisandbox-submit": "Ufro maachen",
+       "apisandbox-reset": "Eidel maachen",
+       "apisandbox-retry": "Nach eng Kéier probéieren",
+       "apisandbox-helpurls": "Hëllef-Linken",
+       "apisandbox-examples": "Beispiller",
+       "apisandbox-dynamic-parameters": "Zousätzlech Parameteren",
+       "apisandbox-dynamic-parameters-add-label": "Parameter derbäisetzen:",
+       "apisandbox-dynamic-parameters-add-placeholder": "Numm vum Parameter",
+       "apisandbox-deprecated-parameters": "Vereelst Parameter",
+       "apisandbox-submit-invalid-fields-title": "E puer Felder sinn net valabel.",
+       "apisandbox-results": "Resultater",
+       "apisandbox-request-url-label": "URL fir Ufroen:",
+       "apisandbox-request-time": "Dauer vun der Ufro: {{PLURAL:$1|$1 ms}}",
+       "apisandbox-alert-page": "Felder op dëser Säit sinn net valabel.",
        "booksources": "Bicherreferenzen",
        "booksources-search-legend": "No Bicherreferenze sichen",
        "booksources-search": "Sichen",
        "unwatchthispage": "Net méi iwwerwaachen",
        "notanarticle": "Keng Säit",
        "notvisiblerev": "Versioun gouf geläscht",
-       "watchlist-details": "{{PLURAL:$1|1 Säit|$1 Säiten}} sinn op ärer Iwwerwaachungsklëscht, d'Diskussiounssäiten net matgezielt.",
+       "watchlist-details": "{{PLURAL:$1|1 Säit|$1 Säite}} sinn op Ärer Iwwerwaachungslëscht, d'Diskussiounssäiten net matgezielt.",
        "wlheader-enotif": "E-Mail-Notifikatioun ass ageschalt.",
        "wlheader-showupdated": "Säiten déi zanter Ärer leschter Visite geännert goufen, si '''fett''' geschriwwen",
        "wlnote": "Hei {{PLURAL:$1|ass déi lescht Ännerung|sinn déi lescht <strong>$1</strong> Ännerunge}} vun {{PLURAL:$2|der leschter Stonn|de leschte(n) <strong>$2</strong> Stonnen}}, Stand: $3 ëm $4 Auer.",
        "movenosubpage": "Dës Säit huet keng Ënnersäiten.",
        "movereason": "Grond:",
        "revertmove": "zréck réckelen",
-       "delete_and_move_text": "== Läsche vun der Destinatiounssäit néideg ==\nD'Säit \"[[:$1]]\" existéiert schonn. \nWëll Dir se läsche fir d'Réckelen ze erméiglechen?",
+       "delete_and_move_text": "D'Zilsäit \"[[:$1]]\" gëtt et schonn. \nWëll Dir se läsche fir d'Réckelen ze erméiglechen?",
        "delete_and_move_confirm": "Jo, läsch d'Säit",
        "delete_and_move_reason": "Geläscht fir Plaz ze maache fir \"[[$1]]\" heihin ze réckelen",
        "selfmove": "Source- an Destinatiounsnumm sinn dselwecht; eng Säit kann net op sech selwer geréckelt ginn.",
index d79cfe2..93f2e58 100644 (file)
        "watchthisupload": "ای جانیا نه بوینیت",
        "filewasdeleted": "جانیایی وا همی نوم دماتر سوار بیه و نهاش پاکسا بیه\nشما واس $1 نه دما یه که میهایت سوار بکیت بینیتش.",
        "filename-bad-prefix": "نوم جانیایی که سوار می کیت وا شرو '''$1''' موئه که دماون ویجه سی عسگیا ثوت بیه وا دیربین یا دیجیتاله.\nلطفن یه گل نوم بی تر سی جانیا انتخاو بکیت.",
-       "upload-success-subj": "سوار کرد خوش سرانجوم",
-       "upload-success-msg": "سوارکرد شما سی [$2] خوو بی.وه د ایچه هئش:[[:{{ns:file}}:$1]]",
-       "upload-failure-subj": "مشگل د سوارکردن",
-       "upload-failure-msg": "یه گل مشلگل د سوارکردتو سی [$2] بی:$1",
-       "upload-warning-subj": "هشدار سوارکرد",
-       "upload-warning-msg": "د نوم بلگه سوارکرد مشگلی بی [$2]. شما می تونیت د[[Special:Upload/stash/$1|نوم بلگه سوارکرد]] ؤیرئیت تا ای مشگل نه برطرف بکیت.",
        "upload-proto-error": "پروتکل نادروس",
        "upload-proto-error-text": "سوارکرد د د دیرادیر ائتیاج وه تیرنشونیایی داره که وا <code dir=ltr>http://</code> یا <code dir=ltr>ftp://</code> شرو بان.",
        "upload-file-error": "خطا مینونه",
        "upload-dialog-button-done": "أنجوم بییە",
        "upload-dialog-button-save": "ئمایە کئردئن",
        "upload-dialog-button-upload": "سوڤار کئردئن",
-       "upload-form-label-select-file": "ئنتئخاڤ جانیا",
        "upload-form-label-infoform-title": "جوزئیات",
        "upload-form-label-infoform-name": "نوم",
        "upload-form-label-infoform-description": "توضی",
        "wlheader-showupdated": "بلگه یایی که د آخرین کرتی که شما دشو دیئن کردیته آلشت بینه د <strong>توپر</strong>نشون دئه بینه",
        "wlnote": "د هار {{PLURAL:$1|آلشت|<strong>$1</strong> آلشتی}} که د {{PLURAL:$2|ساعت|<strong>$2</strong> ساعت}} دماتر انجوم بیه هیئش، ویرگار آخرین واجوری انجام شده موجود است، ویرگار آخری واجوری: $3، $4",
        "wlshowlast": "آخرین$1 ساعتیا $2و روزیا  نشو بیئه",
-       "watchlistall2": "ھأمە",
        "wlshowtime": "نئشوٙ دأئن د آخأر",
        "wlshowhideminor": "ڤیرایئشتیا فئرە کوچئک",
        "wlshowhidebots": "بوتیا",
        "protect-otherreason-op": "دألیل ھأنی",
        "protect-dropdown": "*دلیلیا جاافتائه سی پر و پیم کاری\n** خراوکاری گپ کلون\n** هرزه نیسی گپ کلون\n** جئن ویرایشتی وه درد نحور\n** بلگه فره تماشاکار دار",
        "protect-edit-reasonlist": "دلیلا پر و پیم بیین ویرایشت",
-       "protect-expiry-options": "1 ساعت:1 ساعت,1 روز:1 روز,1 هفته:1 هفته,2 هفته:2 هفته,1 ما:1 ما,3 ما:3 ما,6 ما:6 ما,1 سال:1 سال,بی حساو:بی حساو",
+       "protect-expiry-options": "۱ ساعأت:1 hour,۱ روٙز:1 day,۱ ھأفتە:1 week,۲ ھأفتە:2 weeks,۱ ما:1 month,۳ ما:3 months,۶ ما:6 months,۱ سال:1 year,بی حئسۉ:infinite",
        "restriction-type": "دسرسی:",
        "restriction-level": "ریتراز محدودیت:",
        "minimum-size": "انازه کمترونه",
        "ipbenableautoblock": "بستن خودانجوم آخری تیرنشون آی پی وه کار گرته بیه وه دس کاریار و تیرنشونیا هنی که که د ونو سی ویرایشت وه سعی می کن.",
        "ipbsubmit": "نهاگری ای کاریار",
        "ipbother": "وخت هنی:",
-       "ipboptions": "2 ساعتیا:2 ساعت,1 رو:1 رو,3 روزا:3 رو,1 هفته:1 هفته,2 هفته یا:2 هفته,1 ما:1 ما,3 ما:3 میا,6 ما:6 مایا,1 سال:1سال,بی حساو:بی حساو",
+       "ipboptions": "۱ ساعأت:1 hour,۱ روٙز:1 day,۱ ھأفتە:1 week,۲ ھأفتە:2 weeks,۱ ما:1 month,۳ ما:3 months,۶ ما:6 months,۱ سال:1 year,بی حئسۉ:infinite",
        "ipbhidename": "نوم کاروری نه سی ویرایشت یا و نوم گه یا قام کو",
        "ipbwatchuser": "پی گری بلگه کاریاری و بلگه چک چنه ای کاریار",
        "ipb-disableusertalk": "نها ای کاریار نه اوسه که میها د بلگه چک چنه ش ویرایشت بکه و وه قلف بیه بئر",
index db89208..64339a4 100644 (file)
        "querypage-disabled": "Šiame specialiajame puslapyje yra išjungta dėl neefektyvumo.",
        "apihelp": "API pagalba",
        "apihelp-no-such-module": "Nerasta modulio $1.",
+       "apisandbox": "API smėlio dėžės",
+       "apisandbox-api-disabled": "API yra išjungtas šioje svetainėje.",
+       "apisandbox-intro": "Naudokite šį puslapį norėdami eksperimentuoti su '''MediaWiki API \"„.\n\tIeškokite [//www.mediawiki.org/wiki/API:Main_page API dokumentacijoje] Išsamesnės informacijos apie API naudojimo.",
+       "apisandbox-submit": "Pateikti prašymą",
+       "apisandbox-reset": "Išvalyti",
+       "apisandbox-examples": "Pavyzdys",
+       "apisandbox-results": "Rezultatai",
+       "apisandbox-request-url-label": "Prašyti URL:",
+       "apisandbox-request-time": "Užklausos laikas: $1",
        "booksources": "Knygų šaltiniai",
        "booksources-search-legend": "Knygų šaltinių paieška",
        "booksources-search": "Ieškoti",
index 280a99c..3fca0eb 100644 (file)
        "rcshowhidemine": "$1 हमर सम्पादन सभ",
        "rcshowhidemine-show": "देखाउ",
        "rcshowhidemine-hide": "नुकाऊ",
-       "rcshowhidecategorization-show": "दà¥\87à¤\96ाà¤\8a",
-       "rcshowhidecategorization-hide": "नà¥\81à¤\95ाà¤\8a",
+       "rcshowhidecategorization-show": "दà¥\87à¤\96ाबà¥\80",
+       "rcshowhidecategorization-hide": "नà¥\81à¤\95ाबà¥\80",
        "rclinks": "देखाऊ अंतिम $1 परिवर्त्तन अंतिम $2 दिनमे<br />$3",
        "diff": "अन्तर",
        "hist": "इति.",
        "querypage-disabled": "ई विशिष्ट पन्ना कार्य दक्षता लेल अशक्त कएल गेल अछि।",
        "apihelp": "API मद्दत",
        "apihelp-no-such-module": "मोड्युल \"$1\" नै भेटल।",
+       "apisandbox": "ए॰पी॰आइ प्रयोगपृष्ठ",
+       "apisandbox-submit": "अनुरोध करु",
+       "apisandbox-reset": "स्पष्ट",
+       "apisandbox-examples": "उदाहरण",
+       "apisandbox-results": "परिणाम",
+       "apisandbox-request-url-label": "अनुरोध URL:",
+       "apisandbox-request-time": "अनुरोध समय: $1",
        "booksources": "किताबक सन्दर्भ सभ",
        "booksources-search-legend": "किताबक सन्दर्भक लेल ताकू",
        "booksources-isbn": "आइ.एस.बी.एन.:",
        "contributions": "प्रयोक्ताक योगदान सभ",
        "contributions-title": "$1 लेल प्रयोक्ताक अवदान",
        "mycontris": "योगदान",
+       "anoncontribs": "योगदानसभ",
        "contribsub2": "$1 ($2) लेल",
        "contributions-userdoesnotexist": "प्रयोक्ता खाता \"$1\" पंजीकृत नै अछि।",
        "nocontribs": "कोनो परिवर्तन ऐ सँ मेल नै खाइए।",
        "javascripttest-pagetext-frameworks": "कृपया निम्न परीक्षण ढाँचा सभ में सँ एक चुनु: $1",
        "javascripttest-pagetext-skins": "परीक्षण करए के लेल त्वचा चुनु:",
        "javascripttest-qunit-intro": "mediawiki.org पर [$1 परीक्षण के प्रलेखन] देखु।",
-       "tooltip-pt-userpage": "अहाँक खेसरा पन्ना",
+       "tooltip-pt-userpage": "{{GENDER:|अहाँक प्रयोगकर्ता}} पृष्ठ",
        "tooltip-pt-anonuserpage": "सम्पाद्न कएल जा रहल स्थानक  अनिकेतक प्रयोक्ता पन्ना",
-       "tooltip-pt-mytalk": "अहाँक वार्त्ता पृष्ठ",
+       "tooltip-pt-mytalk": "{{GENDER:|अहाँक}} वार्ता पृष्ठ",
        "tooltip-pt-anontalk": "ऐ अनिकेतसँ भेल सम्पादनक वार्ता",
-       "tooltip-pt-preferences": "हमर मोनपसंद",
+       "tooltip-pt-preferences": "{{GENDER:|अहाँक}} अभिरुचीसभ",
        "tooltip-pt-watchlist": "पन्ना सभ जकर परिवर्त्तन पर अहाँक नजरि अछि",
-       "tooltip-pt-mycontris": "अहाँक योगदानक सूची",
+       "tooltip-pt-mycontris": "{{GENDER:|अहाँक}} योगदानक सूची",
        "tooltip-pt-login": "अहाँक खाता खोलक लेल प्रोत्साहित केल जाएत अछि; मुदा ई अनिवार्य नै अछि",
        "tooltip-pt-logout": "फेर आयब",
        "tooltip-pt-createaccount": "अहाँक खाता खोलक लेल प्रोत्साहित केल जाएत अछि; मुदा ई अनिवार्य नै छै",
index 9df1e70..df1de22 100644 (file)
        "querypage-disabled": "Оваа службена страница е оневозможена за да не попречува на делотворноста.",
        "apihelp": "Помош со извршникот",
        "apihelp-no-such-module": "Модулот „$1“ не е пронајден.",
+       "apisandbox": "Извршнички песочник",
+       "apisandbox-api-disabled": "Извршникот е оневозможен на ова мрежно место.",
+       "apisandbox-intro": "Страницава служи за вршење проби со '''Извршник на МедијаВики'''.\n\nПовеќе за употребата на овој извршник ќе најдете во [//www.mediawiki.org/wiki/API:Main_page неговата документација].  Пример: [//www.mediawiki.org/wiki/API#A_simple_example преземање на содржината на главната страница].  Одберете дејство за да видите повеќе примери.\n\nИмајте предвид дека она шо го правите на страницава може да се одрази врз викито, иако ова е песочник.",
+       "apisandbox-submit": "Постави барање",
+       "apisandbox-reset": "Исчисти",
+       "apisandbox-examples": "Пример",
+       "apisandbox-results": "Извод",
+       "apisandbox-request-url-label": "URL на барањето:",
+       "apisandbox-request-time": "Време за барањето: $1",
        "booksources": "Печатени извори",
        "booksources-search-legend": "Пребарување на извори за книга",
        "booksources-isbn": "ISBN:",
index a2cd1cb..fe7396e 100644 (file)
        "querypage-disabled": "പ്രവർത്തനമികവിനെ ബാധിക്കുന്ന കാരണങ്ങളാൽ ഈ പ്രത്യേക താൾ പ്രവർത്തന രഹിതമാക്കിയിരിക്കുന്നു.",
        "apihelp": "എ.പി.ഐ. സഹായം",
        "apihelp-no-such-module": "ഘടകം \"$1\" കണ്ടെത്താനായില്ല.",
+       "apisandbox": "എ.പി.ഐ. എഴുത്തുകളരി",
+       "apisandbox-api-disabled": "ഈ സൈറ്റിൽ എ.പി.ഐ. പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നു.",
+       "apisandbox-intro": "'''മീഡിയവിക്കി വെബ്‌ സെർവീസ് എ.പി.ഐ.'''യിൽ പരീക്ഷണങ്ങൾ നടത്താൻ ഈ താൾ ഉപയോഗിക്കുക.\nഎ.പി.ഐ.യുടെ ഉപയോഗത്തെക്കുറിച്ചുള്ള കൂടുതൽ വിവരങ്ങൾക്കായി [//www.mediawiki.org/wiki/API:Main_page the എ.പി.ഐ. സഹായം] പരിശോധിക്കുക. ഉദാഹരണം: [//www.mediawiki.org/wiki/API#A_simple_example പ്രധാന താളിന്റെ ഉള്ളടക്കം എടുക്കുക]. കൂടുതൽ ഉദാഹരണങ്ങൾക്കായി പ്രവൃത്തി തിരഞ്ഞെടുക്കുക.\n\nഇതൊരു പരീക്ഷണകളരിയാണെങ്കിലും ഇവിടെ ചെയ്യുന്നവ വിക്കിയിൽ മാറ്റങ്ങൾ വരുത്തിയേക്കാമെന്ന് ഓർക്കുക.",
+       "apisandbox-submit": "അഭ്യർത്ഥിക്കുക",
+       "apisandbox-reset": "ശൂന്യമാക്കുക",
+       "apisandbox-examples": "ഉദാഹരണം",
+       "apisandbox-results": "ഫലം",
+       "apisandbox-request-url-label": "അഭ്യർത്ഥനാ യൂ.ആർ.എൽ.:",
+       "apisandbox-request-time": "നടപ്പിലാക്കാൻ എടുത്ത സമയം: $1",
        "booksources": "പുസ്തക സ്രോതസ്സുകൾ",
        "booksources-search-legend": "പുസ്തകസ്രോതസ്സുകൾക്കായി തിരയുക",
        "booksources-isbn": "ഐ.എസ്.ബി.എൻ.:",
index d7670a0..27d49c6 100644 (file)
        "expand_templates_input": "Оруулах бичиг:",
        "expand_templates_output": "Үр дүн",
        "expand_templates_remove_comments": "Товч агуулгыг авч хаях",
-       "pagelanguage": "Хуудасны хэлийг өөрчлөх"
+       "pagelanguage": "Хуудасны хэлийг өөрчлөх",
+       "log-name-pagelang": "Хэлний өөрчлөлтийн лог"
 }
index 0a57052..cad0551 100644 (file)
        "querypage-disabled": "Laman khas ini dilumpuhkan atas sebab-sebab prestasi.",
        "apihelp": "Bantuan API",
        "apihelp-no-such-module": "Modul \"$1\" tidak dijumpai.",
+       "apisandbox": "Kotak pasir API",
+       "apisandbox-api-disabled": "API dimatikan di tapak web ini.",
+       "apisandbox-intro": "Gunakan laman ini untuk bereksperimen dengan '''API perkhidmatan sesawang MediaWiki'''.\nRujuk [//www.mediawiki.org/wiki/API:Main_page dokumentasi API] untuk keterangan lanjut tentang penggunaan API.\nContoh: [//www.mediawiki.org/wiki/API#A_simple_example dapatkan kandungan Laman Utama].  Pilih satu tindakan untuk melihat banyak lagi contoh.",
+       "apisandbox-submit": "Buat permintaan",
+       "apisandbox-reset": "Padamkan",
+       "apisandbox-examples": "Contoh",
+       "apisandbox-results": "Hasil",
+       "apisandbox-request-url-label": "URL permohonan:",
+       "apisandbox-request-time": "Waktu pemohonan: $1",
        "booksources": "Sumber buku",
        "booksources-search-legend": "Cari sumber buku",
        "booksources-search": "Cari",
index ba28a76..9bfd0f7 100644 (file)
        "helppage-top-gethelp": "အကူအညီ",
        "mainpage": "ဗဟိုစာမျက်နှာ",
        "mainpage-description": "ဗ​ဟို​စာ​မျက်​နှာ​",
-       "policy-url": "Project:မူ​ဝါ​ဒ",
+       "policy-url": "Project:မူဝါဒ",
        "portal": "ပေါင်းကူးနေရာ",
        "portal-url": "Project:ပေါင်းကူးနေရာ",
        "privacy": "ကိုယ်ပိုင်ရေးရာ မူဝါဒ",
        "password-login-forbidden": "ဤအသုံးပြုသူအမည်နှင့် စကားဝှက်အား အသုံးပြုခြင်းကို တားမြစ်ထားသည်။",
        "mailmypassword": "စကားဝှက်ကို ပြန်ချိန်ရန်",
        "passwordremindertitle": "{{SITENAME}} အတွက် ယာယီစကားဝှက်အသစ်",
+       "passwordremindertext": "တစ်စုံတစ်ယောက် ($1 အိုင်ပီလိပ်စာမှ သင်လည်းဖြစ်နိုင်) သည် {{SITENAME}} ($4) အတွက် စကားဝှက်အသစ်ကို တောင်းဆိုခဲ့သည်။ \nအသုံးပြုသူ \"$2\" အတွက် ယာယီစကားဝှက်အသစ်ကို ဖန်တီးပြီး \"$3\" အဖြစ် သတ်မှတ်လိုက်သည်။ \nဤအရာကို သင်ရည်ရွယ်သည်ဆိုပါက လော့ဂ်အင်ဝင်ရန်ပြီး စကားဝှက်အသစ် ရွေးချယ်ရန် လိုအပ်ပါသည်။ \nသင်၏ ယာယီစကားဝှက်သည် {{PLURAL:$5|တစ်ရက်|$5 ရက်}}အတွင်း သက်တမ်းကုန်ပါလိမ့်မည်။\n\nအကယ်၍ တစ်စုံတစ်ယောက်က ဤတောင်းဆိုမှုကို ပြုလုပ်ခဲ့ပါက၊ သို့မဟုတ် သင်သည် သင့်စကားဝှက်အား မှတ်မိပြီး \nပြောင်းလဲရန် ဆန္ဒမရှိပါ ဤစာလွှာကို မျက်ကွယ်ပြုပြီး စကားဝှက်အဟောင်းဖြင့် ဆက်လက်သုံးစွဲနိုင်ပါသည်။",
        "noemail": "အသုံးပြုသူ \"$1\" အတွက် မည်သည့်အီးမေးလိပ်စာမှ မှတ်သားထားခြင်း မရှိပါ။",
        "noemailcreate": "တရာဝင်အီးမေးလိပ်စာ ပေးရန် လိုအပ်သည်",
        "mailerror": "မေးပို့ခြင်း အမှား - $1",
        "loginreqpagetext": "အခြားစာမျက်နှာများကို ကြည့်ရန် $1ရမည်။",
        "accmailtitle": "စကားဝှက်ကို ပို့ပြီးပြီ",
        "newarticle": "(အသစ်)",
-       "newarticletext": "သင်သည် မရှိသေးသော စာမျက်နှာလင့် ကို ရောက်လာခြင်းဖြစ်သည်။\nစာမျက်နှာအသစ်စတင်ရန် အောက်မှ သေတ္တာထဲတွင် စတင်ရိုက်ထည့်ပါ (နောက်ထပ် သတင်းအချက်အလက်များအတွက်[$1 အကူအညီ စာမျက်နှာ]ကို ကြည့်ပါ)။\nမတော်တဆရောက်လာခြင်း ဖြစ်ပါက ဘရောက်ဆာ၏ နောက်ပြန်ပြန်သွားသော'''back''' ခလုတ်ကို နှိပ်ပါ။",
+       "newarticletext": "သင်သည် မရှိသေးသော စာမျက်နှာလင့် ကို ရောက်လာခြင်းဖြစ်သည်။\nစာမျက်နှာအသစ်စတင်ရန် အောက်မှ သေတ္တာထဲတွင် စတင်ရိုက်ထည့်ပါ (နောက်ထပ် သတင်းအချက်အလက်များအတွက်[$1 အကူအညီ စာမျက်နှာ]ကို ကြည့်ပါ)။\nမတော်တဆရောက်လာခြင်း ဖြစ်ပါက ဘရောက်ဆာ၏ နောက်ပြန်ပြန်သွားသော <strong>back</strong> ခလုတ်ကို နှိပ်ပါ။",
        "noarticletext": "ဤစာမျက်နှာတွင် ယခုလက်ရှိတွင် မည်သည့်စာသားမှ မရှိပါ။\nသင်သည် အခြားစာမျက်နှာများတွင် [[Special:Search/{{PAGENAME}}|ဤစာမျက်နှာ၏ ခေါင်းစဉ်ကို ရှာနိုင်သည်]]၊ <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} ဆက်စပ်ရာ Logs များကို ရှာနိုင်သည်]၊ သို့မဟုတ် [{{fullurl:{{FULLPAGENAME}}|action=edit}} ဤစာမျက်နှာကို တည်းဖြတ်နိုင်သည်]</span>။",
        "noarticletext-nopermission": "ဤစာမျက်နှာတွင် ယခုလက်ရှိတွင် မည်သည့်စာသားမှ မရှိပါ။\nသင်သည် အခြားစာမျက်နှာများတွင် [[Special:Search/{{PAGENAME}}|ဤစာမျက်နှာ၏ ခေါင်းစဉ်ကို ရှာနိုင်သည်]]၊ သို့မဟုတ် <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} ဆက်စပ်ရာ Logs များကို ရှာနိုင်သည်]</span>။ သို့သော် ဤစာမျက်နှာကို ဖန်တီးရန် သင့်တွင် အခွင့်အရေး မရှိပါ။",
        "note": "'''မှတ်ချက် -'''",
        "right-createtalk": "ဆွေးနွေးချက်စာမျက်နှာများ စတင်ရေးသားရန်",
        "right-createaccount": "အသုံးပြုသူအကောင့်အသစ်ကို ဖန်တီးရန်",
        "right-minoredit": "တည်းဖြတ်မှုများကို အရေးမကြီးဟု မှတ်သားရန်",
-       "right-move": "á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\80á\80­á\80¯ရွှေ့ပြောင်းပါ",
+       "right-move": "á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\99á\80»á\80¬á\80¸á\80\80á\80­á\80¯ ရွှေ့ပြောင်းပါ",
        "right-move-subpages": "စာမျက်နှာများကို ယင်းတို့၏စာမျက်နှာအခွဲတို့နှင့်တကွ ရွှေ့ရန်",
        "right-movefile": "ဖိုင်များရွှေ့ရန်",
        "right-suppressredirect": "စာမျက်နှာများကို ရွှေ့သောအခါ မူရင်းစာမျက်နှာတို့မှ ပြန်ညွှန်းများ မဖန်တီးရန်",
        "right-autoconfirmed": "အိုင်ပီအခြေပြု ကန့်သတ်ချက်များကို အကျိုးမသက်ရောက်ပါ",
        "right-bot": "အလိုအလျောက်ပြုမူသော ဖြစ်စဉ်အဖြစ်ဆောင်ရွက်ရန်",
        "right-writeapi": "ရေးသားမှု API ကို သုံးရန်",
-       "right-delete": "စာမျက်နှာများကိုဖျက်ပါ။",
+       "right-delete": "စာမျက်နှာများကို ဖျက်ပါ",
        "right-bigdelete": "လွန်စွာများပြားသော ရာဇဝင်များရှိသည့် စာမျက်နှာများကို ဖျက်ရန်",
        "right-browsearchive": "ဖျက်ပစ်လိုက်သော စာမျက်နှာများကို ရှာရန်",
        "right-undelete": "စာမျက်နှာတစ်ခုကို မဖျက်တော့ရန်",
        "removedwatchtext": "\"[[:$1]]\" နှင့် ၎င်း၏ ဆွေးနွေးချက်စာမျက်နှာကို သင်၏ [[Special:Watchlist|စောင့်ကြည့်စာရင်း]] မှ ဖယ်ထုတ်ပြီး ဖြစ်သည်။",
        "watch": "စောင့်ကြည့်ရန်",
        "watchthispage": "ဤစာမျက်နှာကို စောင့်ကြည့်ရန်",
-       "unwatch": "á\80\86á\80\80á\80ºá\80\9cá\80\80á\80º á\80\85á\80±á\80¬á\80\84á\80·á\80ºá\80\99á\80\80á\80¼á\80\8aá\80·á\80ºá\80\90á\80±á\80¬á\80·á\80\9bá\80\94်",
+       "unwatch": "á\80\85á\80±á\80¬á\80\84á\80·á\80ºá\80\99á\80\80á\80¼á\80\8aá\80·á\80ºá\80\95á\80«á\80\94á\80¾á\80\84á\80·်",
        "unwatchthispage": "စောင့်ကြည့်ခြင်းကို ရပ်တန့်ရန်",
        "notanarticle": "မာတိကာစာမျက်နှာတစ်ခု မဟုတ်",
        "watchlist-details": "{{PLURAL:$1|စာမျက်နှာ $1 ခု|စာမျက်နှာ $1 ခု}} သည် သင့်စောင့်ကြည့်စာရင်းတွင် ရှိပြီး ဆွေးနွေးချက်စာမျက်နှာများကို ထည့်တွက် မထားပါ။",
        "ipb_already_blocked": "\"$1\" ကို အစကတည်းက ပိတ်ထားသည်",
        "move-page": "$1 ကို ရွှေ့ရန်",
        "move-page-legend": "စာ​မျက်​နှာ​ကို ရွှေ့ပြောင်းရန်",
-       "movepagetext": "á\80¡á\80±á\80¬á\80\80á\80ºá\80\95á\80«á\80\95á\80¯á\80¶á\80\85á\80¶á\80\80á\80­á\80¯ á\80¡á\80\9eá\80¯á\80¶á\80¸á\80\95á\80¼á\80¯á\80\81á\80¼á\80\84á\80ºá\80¸á\80\9eá\80\8aá\80º á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\80á\80­á\80¯ á\80¡á\80\99á\80\8aá\80ºá\80\95á\80¼á\80±á\80¬á\80\84á\80ºá\80¸á\80\9cá\80²á\80\95á\80±á\80¸á\80\99á\80\8aá\80º á\80\96á\80¼á\80\85á\80ºá\80\95á\80¼á\80®á\80¸ á\80¡á\80\99á\80\8aá\80ºá\80\9eá\80\85á\80ºá\80\9eá\80­á\80¯á\80· á\80\9aá\80\84á\80ºá\80¸á\81\8f á\80\99á\80¾á\80\90á\80ºá\80\90á\80\99á\80ºá\80¸á\80\94á\80¾á\80\84á\80·á\80ºá\80\90á\80\80á\80½ á\80\9bá\80½á\80¾á\80±á\80·á\80\95á\80±á\80¸á\80\99á\80\8aá\80º á\80\96á\80¼á\80\85á\80ºá\80\9eá\80\8aá\80ºá\81\8b\ná\80¡á\80\99á\80\8aá\80ºá\80\9fá\80±á\80¬á\80\84á\80ºá\80¸á\80\9eá\80\8aá\80º á\80¡á\80\99á\80\8aá\80ºá\80\9eá\80\85á\80ºá\80\9eá\80­á\80¯á\80· á\80\95á\80¼á\80\94á\80ºá\80\8aá\80½á\80¾á\80\94á\80ºá\80¸á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬ á\80\96á\80¼á\80\85á\80ºá\80\9cá\80¬á\80\99á\80\8aá\80ºá\81\8b\ná\80\9eá\80\84á\80ºá\80\9eá\80\8aá\80º á\80\99á\80°á\80\9cá\80\81á\80±á\80«á\80\84á\80ºá\80¸á\80\85á\80\89á\80ºá\80\9eá\80­á\80¯á\80· á\80\95á\80¼á\80\94á\80ºá\80\8aá\80½á\80¾á\80\94á\80ºá\80¸á\80\99á\80»á\80¬á\80¸á\80\80á\80­á\80¯ á\80¡á\80\9cá\80­á\80¯á\80¡á\80\9cá\80»á\80±á\80¬á\80\80á\80º á\80¡á\80\95á\80ºá\80\92á\80­á\80\90á\80º update á\80\9cá\80¯á\80\95á\80ºá\80\94á\80­á\80¯á\80\84á\80ºá\80\9eá\80\8aá\80ºá\81\8b\ná\80¡á\80\80á\80\9aá\80ºá\81\8d á\80\99á\80\95á\80¼á\80¯á\80\9cá\80¯á\80\95á\80ºá\80\9cá\80­á\80¯á\80\95á\80«á\80\80 [[Special:DoubleRedirects|á\80\94á\80¾á\80\85á\80ºá\80\81á\80«á\80\91á\80\95á\80º]][[Special:BrokenRedirects|á\80\95á\80¼á\80\94á\80ºá\80\8aá\80½á\80¾á\80\94á\80ºá\80¸ á\80¡á\80\95á\80»á\80\80á\80ºá\80\99á\80»á\80¬á\80¸]] á\80\80á\80­á\80¯ á\80\99á\80¾á\80\90á\80ºá\80\9eá\80¬á\80¸á\80\9bá\80\94á\80º á\80\99á\80\99á\80±á\80·á\80\95á\80«á\80\94á\80¾á\80\84á\80·á\80ºá\81\8b\ná\80\9cá\80\84á\80·á\80ºá\80\99á\80»á\80¬á\80¸ á\80\8aá\80½á\80¾á\80\94á\80ºá\80¸á\80\9cá\80­á\80¯á\80\9eá\80\8aá\80·á\80º á\80\94á\80±á\80\9bá\80¬á\80\9eá\80­á\80¯á\80· á\80\8aá\80½á\80¾á\80\94á\80ºá\80\95á\80¼á\80\94á\80±á\80\9bá\80\94á\80º á\80\9eá\80\84á\80·á\80ºá\80\90á\80½á\80\84á\80º á\80\90á\80¬á\80\9dá\80\94á\80º á\80\9bá\80¾á\80­á\80\9eá\80\8aá\80ºá\81\8b\n\ná\80¡á\80\80á\80\9aá\80ºá\81\8d á\80\81á\80±á\80«á\80\84á\80ºá\80¸á\80\85á\80\89á\80ºá\80¡á\80\9eá\80\85á\80ºá\80\90á\80½á\80\84á\80º á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\90á\80\85á\80ºá\80\81á\80¯ á\80\9bá\80¾á\80­á\80\94á\80¾á\80\84á\80·á\80ºá\80\95á\80¼á\80®á\80¸ á\80\96á\80¼á\80\85á\80ºá\80\95á\80«á\80\80 (á\80\9eá\80­á\80¯á\80·) á\80\9aá\80\84á\80ºá\80¸á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\9eá\80\8aá\80º á\80¡á\80\9cá\80½á\80\90á\80ºá\80\99á\80\96á\80¼á\80\85á\80ºá\80\95á\80«á\80\80 (á\80\9eá\80­á\80¯á\80·) á\80\95á\80¼á\80\94á\80ºá\80\8aá\80½á\80¾á\80\94á\80ºá\80¸á\80\90á\80\85á\80ºá\80\81á\80¯ á\80\99á\80\9bá\80¾á\80­á\80\95á\80«á\80\80 (á\80\9eá\80­á\80¯á\80·) á\80\9aá\80\81á\80\84á\80ºá\80\80 á\80\95á\80¼á\80¯á\80\95á\80¼á\80\84á\80ºá\80\91á\80¬á\80¸á\80\9eá\80±á\80¬ á\80\99á\80¾á\80\90á\80ºá\80\90á\80\99á\80ºá\80¸ á\80\99á\80\9bá\80¾á\80­á\80\95á\80«á\80\80 á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\9eá\80\8aá\80º <strong>á\80\9bá\80½á\80±á\80·á\80\99á\80\8aá\80ºá\80\99á\80\9fá\80¯á\80\90á\80º</strong> á\80\9eá\80\8aá\80ºá\80\80á\80­á\80¯ á\80\9eá\80\90á\80­á\80\95á\80¼á\80¯á\80\95á\80«á\81\8b \ná\80\86á\80­á\80¯á\80\9cá\80­á\80¯á\80\9eá\80\8aá\80ºá\80\99á\80¾á\80¬ á\80\9eá\80\84á\80ºá\80\9eá\80\8aá\80º á\80¡á\80\99á\80¾á\80¬á\80¸á\80\90á\80\85á\80ºá\80\81á\80¯ á\80\95á\80¼á\80¯á\80\9cá\80¯á\80\95á\80ºá\80\99á\80­á\80\95á\80«á\80\80 á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\80á\80­á\80¯ á\80\9aá\80\81á\80\84á\80ºá\80¡á\80\99á\80\8aá\80ºá\80\80á\80­á\80¯ á\80\95á\80¼á\80\94á\80ºá\80\9cá\80\8aá\80º á\80\95á\80¼á\80±á\80¬á\80\84á\80ºá\80¸á\80\9cá\80²á\80\95á\80±á\80¸á\80\94á\80­á\80¯á\80\84á\80ºá\80\9eá\80\8aá\80ºá\81\8b á\80\9bá\80¾á\80­á\80\95á\80¼á\80®á\80\9eá\80¬á\80¸á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\90á\80\85á\80ºá\80\81á\80¯á\80\80á\80­á\80¯ á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬ á\80¡á\80\9eá\80\85á\80ºá\80\94á\80¾á\80\84á\80·á\80º á\80\95á\80¼á\80\94á\80ºá\80¡á\80¯á\80\95á\80º overwrite á\80\81á\80¼á\80\84á\80ºá\80¸ á\80\99á\80\95á\80¼á\80¯á\80\94á\80­á\80¯á\80\84á\80ºá\81\8b\n\n<strong>á\80\9eá\80\90á\80­á\80\95á\80±á\80¸á\80\81á\80»á\80\80á\80º!</strong>\nဤသည်မှာ လူဖတ်များသော စာမျက်နှာတစ်ခုဖြစ်ပါက မမျှော်လင့်ထားသော၊ ကြီးမားသော အပြောင်းအလဲတစ်ခု ဖြစ်ပေါ်လာနိုင်သည်။\nထို့ကြောင့် ဆက်လက် မဆောင်ရွက်မီ သင်သည် နောက်ဆက်တွဲ အကျိုးဆက်များကို နားလည်ကြောင်း ကျေးဇူးပြု၍ သေချာပါစေ။",
+       "movepagetext": "á\80¡á\80±á\80¬á\80\80á\80ºá\80\95á\80«á\80\95á\80¯á\80¶á\80\85á\80¶á\80\80á\80­á\80¯ á\80¡á\80\9eá\80¯á\80¶á\80¸á\80\95á\80¼á\80¯á\80\81á\80¼á\80\84á\80ºá\80¸á\80\9eá\80\8aá\80º á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\80á\80­á\80¯ á\80¡á\80\99á\80\8aá\80ºá\80\95á\80¼á\80±á\80¬á\80\84á\80ºá\80¸á\80\9cá\80²á\80\95á\80±á\80¸á\80\99á\80\8aá\80º á\80\96á\80¼á\80\85á\80ºá\80\95á\80¼á\80®á\80¸ á\80¡á\80\99á\80\8aá\80ºá\80\9eá\80\85á\80ºá\80\9eá\80­á\80¯á\80· á\80\9aá\80\84á\80ºá\80¸á\81\8f á\80\99á\80¾á\80\90á\80ºá\80\90á\80\99á\80ºá\80¸á\80\94á\80¾á\80\84á\80·á\80ºá\80\90á\80\80á\80½ á\80\9bá\80½á\80¾á\80±á\80·á\80\95á\80±á\80¸á\80\99á\80\8aá\80º á\80\96á\80¼á\80\85á\80ºá\80\9eá\80\8aá\80ºá\81\8b\ná\80¡á\80\99á\80\8aá\80ºá\80\9fá\80±á\80¬á\80\84á\80ºá\80¸á\80\9eá\80\8aá\80º á\80¡á\80\99á\80\8aá\80ºá\80\9eá\80\85á\80ºá\80\9eá\80­á\80¯á\80· á\80\95á\80¼á\80\94á\80ºá\80\8aá\80½á\80¾á\80\94á\80ºá\80¸á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬ á\80\96á\80¼á\80\85á\80ºá\80\9cá\80¬á\80\99á\80\8aá\80ºá\81\8b\ná\80\9eá\80\84á\80ºá\80\9eá\80\8aá\80º á\80\99á\80°á\80\9cá\80\81á\80±á\80«á\80\84á\80ºá\80¸á\80\85á\80\89á\80ºá\80\9eá\80­á\80¯á\80· á\80\95á\80¼á\80\94á\80ºá\80\8aá\80½á\80¾á\80\94á\80ºá\80¸á\80\99á\80»á\80¬á\80¸á\80\80á\80­á\80¯ á\80¡á\80\9cá\80­á\80¯á\80¡á\80\9cá\80»á\80±á\80¬á\80\80á\80º á\80¡á\80\95á\80ºá\80\92á\80­á\80\90á\80º update á\80\9cá\80¯á\80\95á\80ºá\80\94á\80­á\80¯á\80\84á\80ºá\80\9eá\80\8aá\80ºá\81\8b\ná\80¡á\80\80á\80\9aá\80ºá\81\8d á\80\99á\80\95á\80¼á\80¯á\80\9cá\80¯á\80\95á\80ºá\80\9cá\80­á\80¯á\80\95á\80«á\80\80 [[Special:DoubleRedirects|á\80\94á\80¾á\80\85á\80ºá\80\81á\80«á\80\91á\80\95á\80º]][[Special:BrokenRedirects|á\80\95á\80¼á\80\94á\80ºá\80\8aá\80½á\80¾á\80\94á\80ºá\80¸ á\80¡á\80\95á\80»á\80\80á\80ºá\80\99á\80»á\80¬á\80¸]] á\80\80á\80­á\80¯ á\80\99á\80¾á\80\90á\80ºá\80\9eá\80¬á\80¸á\80\9bá\80\94á\80º á\80\99á\80\99á\80±á\80·á\80\95á\80«á\80\94á\80¾á\80\84á\80·á\80ºá\81\8b\ná\80\9cá\80\84á\80·á\80ºá\80\99á\80»á\80¬á\80¸ á\80\8aá\80½á\80¾á\80\94á\80ºá\80¸á\80\9cá\80­á\80¯á\80\9eá\80\8aá\80·á\80º á\80\94á\80±á\80\9bá\80¬á\80\9eá\80­á\80¯á\80· á\80\8aá\80½á\80¾á\80\94á\80ºá\80\95á\80¼á\80\94á\80±á\80\9bá\80\94á\80º á\80\9eá\80\84á\80·á\80ºá\80\90á\80½á\80\84á\80º á\80\90á\80¬á\80\9dá\80\94á\80º á\80\9bá\80¾á\80­á\80\9eá\80\8aá\80ºá\81\8b\n\ná\80¡á\80\80á\80\9aá\80ºá\81\8d á\80\81á\80±á\80«á\80\84á\80ºá\80¸á\80\85á\80\89á\80ºá\80¡á\80\9eá\80\85á\80ºá\80\90á\80½á\80\84á\80º á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\90á\80\85á\80ºá\80\81á\80¯ á\80\9bá\80¾á\80­á\80\94á\80¾á\80\84á\80·á\80ºá\80\95á\80¼á\80®á\80¸ á\80\96á\80¼á\80\85á\80ºá\80\95á\80«á\80\80 (á\80\9eá\80­á\80¯á\80·) á\80\9aá\80\84á\80ºá\80¸á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\9eá\80\8aá\80º á\80¡á\80\9cá\80½á\80\90á\80ºá\80\99á\80\96á\80¼á\80\85á\80ºá\80\95á\80«á\80\80 (á\80\9eá\80­á\80¯á\80·) á\80\95á\80¼á\80\94á\80ºá\80\8aá\80½á\80¾á\80\94á\80ºá\80¸á\80\90á\80\85á\80ºá\80\81á\80¯ á\80\99á\80\9bá\80¾á\80­á\80\95á\80«á\80\80 (á\80\9eá\80­á\80¯á\80·) á\80\9aá\80\81á\80\84á\80ºá\80\80 á\80\95á\80¼á\80¯á\80\95á\80¼á\80\84á\80ºá\80\91á\80¬á\80¸á\80\9eá\80±á\80¬ á\80\99á\80¾á\80\90á\80ºá\80\90á\80\99á\80ºá\80¸ á\80\99á\80\9bá\80¾á\80­á\80\95á\80«á\80\80 á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\9eá\80\8aá\80º <strong>á\80\9bá\80½á\80±á\80·á\80\99á\80\8aá\80ºá\80\99á\80\9fá\80¯á\80\90á\80º</strong> á\80\9eá\80\8aá\80ºá\80\80á\80­á\80¯ á\80\9eá\80\90á\80­á\80\95á\80¼á\80¯á\80\95á\80«á\81\8b \ná\80\86á\80­á\80¯á\80\9cá\80­á\80¯á\80\9eá\80\8aá\80ºá\80\99á\80¾á\80¬ á\80\9eá\80\84á\80ºá\80\9eá\80\8aá\80º á\80¡á\80\99á\80¾á\80¬á\80¸á\80\90á\80\85á\80ºá\80\81á\80¯ á\80\95á\80¼á\80¯á\80\9cá\80¯á\80\95á\80ºá\80\99á\80­á\80\95á\80«á\80\80 á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\80á\80­á\80¯ á\80\9aá\80\81á\80\84á\80ºá\80¡á\80\99á\80\8aá\80ºá\80\80á\80­á\80¯ á\80\95á\80¼á\80\94á\80ºá\80\9cá\80\8aá\80º á\80\95á\80¼á\80±á\80¬á\80\84á\80ºá\80¸á\80\9cá\80²á\80\95á\80±á\80¸á\80\94á\80­á\80¯á\80\84á\80ºá\80\9eá\80\8aá\80ºá\81\8b á\80\9bá\80¾á\80­á\80\95á\80¼á\80®á\80\9eá\80¬á\80¸á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\90á\80\85á\80ºá\80\81á\80¯á\80\80á\80­á\80¯ á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬ á\80¡á\80\9eá\80\85á\80ºá\80\94á\80¾á\80\84á\80·á\80º á\80\95á\80¼á\80\94á\80ºá\80¡á\80¯á\80\95á\80º overwrite á\80\81á\80¼á\80\84á\80ºá\80¸ á\80\99á\80\95á\80¼á\80¯á\80\94á\80­á\80¯á\80\84á\80ºá\81\8b\n\n<strong>á\80\99á\80¾á\80\90á\80ºá\80\81á\80»á\80\80á\80ºá\81\8b</strong>\nဤသည်မှာ လူဖတ်များသော စာမျက်နှာတစ်ခုဖြစ်ပါက မမျှော်လင့်ထားသော၊ ကြီးမားသော အပြောင်းအလဲတစ်ခု ဖြစ်ပေါ်လာနိုင်သည်။\nထို့ကြောင့် ဆက်လက် မဆောင်ရွက်မီ သင်သည် နောက်ဆက်တွဲ အကျိုးဆက်များကို နားလည်ကြောင်း ကျေးဇူးပြု၍ သေချာပါစေ။",
        "movepagetalktext": "ဤအကွက်ကို အမှန်ခြစ်လိုက်ခြင်းဖြင့် ဗလာမဟုတ်သော ဆွေးနွေးချက်စာမျက်နှာသည် ရှိနှင့်ပြီး မဟုတ်လျှင် ဆက်နွယ်နေသော ဆွေးနွေးချက် စာမျက်နှာကို ခေါင်းစဉ်အသစ်သို့  အလိုအလျောက် ရွှေ့ပစ်မည် ဖြစ်သည်။\n\nဤကိစ္စရပ်တွင် သင် ဆန္ဒရှိလျှင် စာမျက်နှာကို မိမိကိုယ်တိုင် သွားရောက်ရွှေ့ပြောင်း ပေါင်းစပ်နိုင်သည်။",
        "newtitle": "ခေါင်းစဉ်အသစ်:",
        "move-watch": "မူရင်းစာမျက်နှာနှင့် ဦးတည်ထားသော စာမျက်နှာတို့ကို စောင့်ကြည့်ရန်",
index d484e60..a228f8d 100644 (file)
        "previewnote": "'''Chesta è sola n'anteprimma; 'e cagnamiénte â paggena nun songo ancora sarvate!'''",
        "continue-editing": "Trasite int'a l'area 'e modifica",
        "previewconflict": "L'anteprimma currisponne a 'o testo presente dint'a cascia 'e modifica ccà ncoppa e rappresentasse 'a paggena comme cumpare si sciglite 'e Sarvà ind'a stu mumento.",
-       "session_fail_preview": "'''Nun è possibbile prucessà 'o cagnamiento pecché se so' sperdut' 'e date d' 'a sessione.\nProva n'ata vota.\nSi nun funziona ancora, può pruvà 'a te [[Special:UserLogout|n'ascì]] e a trasì n'ata vota.'''",
-       "session_fail_preview_html": "'''Nun è possibbile prucessà 'o cagnamiento pecché se so' sperdut' 'e date d' 'a sessione.\nProva n'ata vota.'''\n''Siccome dint' 'o {{SITENAME}} è abilitato l'uso 'e l'HTML cruro, 'o buttone d'anteprimma nun è abbiàto comme misura 'e sicurezza annanza cocch'attacco JavaScript''\n'''Si chest'era nu tentativo legittimo 'e cagnamiento, prova n'ata vota. Si nun funziona ancora, può pruvà 'a te [[Special:UserLogout|n'ascì]] e a trasì n'ata vota.'''",
+       "session_fail_preview": "Scusate! nun è possibbile prucessà 'o cagnamiento pecché se so' sperdut' 'e date d' 'a sessione.\n\nPuò darse ca d' 'a parta vosta nun eravate trasute.<strong>Pe' piacere cuntrullate ca site ancora dinto e tentate n'ata vota</strong>.\nSi chesto nun funziunasse ancora, tentate a ve [[Special:UserLogout|n'ascì]] e a trasì n'ata vota, e cuntrullate si 'o navigatóre vuosto tenisse 'e cookies appicciàte.",
+       "session_fail_preview_html": "Scusate! Nun è possibbile prucessà 'o cagnamiento pecché se so' sperdut' 'e date d' 'a sessione.\nProva n'ata vota.\n\n<em>Siccome dint' 'o {{SITENAME}} è abilitato l'uso 'e l'HTML cruro, 'o buttone d'anteprimma nun è abbiàto comme misura 'e sicurezza annanza cocch'attacco JavaScript</em>\n\n<strong>Si chest'era nu tentativo legittimo 'e cagnamiento, tentate n'ata vota.</strong>\nSi nun funziunass'ancora, putite pruvà a ve [[Special:UserLogout|n'ascì]] e a trasì n'ata vota, e cuntrullate si 'o navigatóre vuosto premmettesse 'e cookies ca veneno 'a stu sito.",
        "token_suffix_mismatch": "'''Stu cagnamiento nun è stato sarvato pecché 'o client ave mmustato nu sbaglio dint'o scrivere d' 'e carattere d' 'a punteggiatura token. Pe luvà na possibbile corruzione d' 'o testo dint'a paggena, s'è rifiutat' 'a modifeca.\n\nSta situazione se può truvà, quanno staje ausanno nu servizio 'e proxy anonime via web cu d' 'e bug.'''",
        "edit_form_incomplete": "'''Cocche parte d' 'o modulo 'e cagnamiento nun ha arrivato a 'o server; cuntrolla ch' 'e cagnamiente songo intatte e prova n'ata vota.'''",
        "editing": "Cagnamiento 'e $1",
        "mergehistory-empty": "Nun ce stanno virziune pe' putè ffà l'aunione.",
        "mergehistory-done": "{{PLURAL:$3|Na virziona 'e $1 è stata aunita|$3 versiune 'e $1 so' state aunite}} â cronologgia 'e [[:$2]].",
        "mergehistory-fail": "Nun se ponno aunì 'e cronologgie. Pe' piacere cuntrullate n'ata vota 'a paggena e li parametre tempurale.",
+       "mergehistory-fail-bad-timestamp": "'O timestamp nun è buono.",
+       "mergehistory-fail-invalid-source": "'A paggena d'origgene nun è bbona.",
+       "mergehistory-fail-invalid-dest": "'A paggena 'e destinaziona nun è bbona.",
+       "mergehistory-fail-no-change": "L'aunione d' 'e cronologgie nun accucchiaje nisciuna verziona. Cuntrullate n'ata vota 'e paggene e parammetre 'e tiempo.",
+       "mergehistory-fail-permission": "Auturizzaziune nsufficiente pe' puté aunì cronologgìe.",
+       "mergehistory-fail-self-merge": "'E paggene d'origgine e destinaziona songh' 'e stesse.",
+       "mergehistory-fail-timestamps-overlap": "'E verziune d'origgene se mettono una ncuoll'a l'ata o veneno aropp' 'e verziune 'e destinaziona.",
        "mergehistory-fail-toobig": "Nun se può fà l'aunione d' 'a cronologgia cu nu lémmeto 'e n'ati $1 {{PLURAL:$1|revisione|rivisiune}} 'a cagnà posto.",
        "mergehistory-no-source": "'A paggena d'origgine $1 nun esiste.",
        "mergehistory-no-destination": "'A paggena 'e destinazione $1 nun esiste.",
        "uploaded-script-svg": "Truvato n'elemento pe script \"$1\" int' 'o file SVG carrecato.",
        "uploaded-hostile-svg": "Truvato nu CSS insecuro int'a l'elemente 'e stile d' 'o file SVG carrecate.",
        "uploaded-event-handler-on-svg": "Mpustà 'e parametre 'e gistore-evente <code>$1=\"$2\"</code> nun è premmesso dint' 'e file SVG.",
-       "uploaded-href-unsafe-target-svg": "S'è truvato nu href a nu target ca nun era sicuro <code>&lt;$1 $2=\"$3\"&gt;</code> dint' 'o file SVG carrecato.",
+       "uploaded-href-attribute-svg": "ll'attribbute href dint' 'e file SVG songo premmesse sulamente mmerz'a na destinaziona http:// o https:// , ca se truvasse <code>&lt;$1 $2=\"$3\"&gt;</code>.",
+       "uploaded-href-unsafe-target-svg": "S'è truvato nu href a nu target 'e date: URI ca nun era sicuro <code>&lt;$1 $2=\"$3\"&gt;</code> dint' 'o file SVG carrecato.",
        "uploaded-animate-svg": "Truvato 'o tag \"animate\" ca putesse stà a cagnà href, ausanno l'attribbuto \"from\" <code>&lt;$1 $2=\"$3\"&gt;</code> int' 'o file carrecato SVG.",
        "uploaded-setting-event-handler-svg": "Mpustà n'attributo event-handler è bluccato, truvato <code>&lt;$1 $2=\"$3\"&gt;</code> int' 'o fie carrecato SVG.",
        "uploaded-setting-href-svg": "Ausà 'o tag \"set\" pe' putè azzeccà attribbute \"href\" a l'elemento parente è bluccato.",
        "upload-too-many-redirects": "L'URL teneva troppe redirect",
        "upload-http-error": "N'errore HTTP è succiesso: $1",
        "upload-copy-upload-invalid-domain": "Nun è permessa 'a carreca 'e copie 'a chistu dumminio.",
+       "upload-foreign-cant-upload": "Stu wiki nun è mpustato pe' puté carrecà 'e file dint' 'o repository 'e file 'e fore addimannato.",
        "upload-dialog-title": "Carreca file",
        "upload-dialog-button-cancel": "Canciella",
        "upload-dialog-button-done": "Fatto",
        "upload-dialog-button-save": "Sarva",
        "upload-dialog-button-upload": "Carreca",
-       "upload-form-label-select-file": "Sceglie file",
        "upload-form-label-infoform-title": "Dettaglie",
        "upload-form-label-infoform-name": "Nomme",
        "upload-form-label-infoform-name-tooltip": "Nu titolo unico e distintivo p' 'o file, ca serverrà comm'o nomme file. Putite ausà lenguaggio semprice ch' 'e spazi. Nun azzeccà l'estensione d' 'o file.",
        "foreign-structured-upload-form-label-own-work-message-shared": "Faccio attestato ca songo 'o detentore d' 'o copyright 'e stu file, e so' d'accordo 'e lassà irrevocabbelmente stu file a Wikimedia Commons sott'a licienza [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribuziona-SparteEguale 4.0], e so' d'accordo cu sti [https://wikimediafoundation.org/wiki/Terms_of_Use Termene d'Uso].",
        "foreign-structured-upload-form-label-not-own-work-message-shared": "Si nun tenite 'o copyright 'e stu file, o pure 'o vulite lassà libbero cu n'ata licienza, cunziderate 'ausà 'o [https://commons.wikimedia.org/wiki/Special:UploadWizard Commons Upload Wizard].",
        "foreign-structured-upload-form-label-not-own-work-local-shared": "Putite pure tentà 'e ausà [[Special:Upload|'a paggena 'e carreche 'e {{SITENAME}}]], si stu sito ve premmettesse 'e carrecà llanno pe' bbìa d' 'e pulitiche.",
-       "foreign-structured-upload-form-2-label-intro": "Ve ringraziammo p' 'o dono 'e n'immaggene p' 'a puté ausà dint'a {{SITENAME}}. Avita cuntinuà surtanto si se danno 'e condeziune ccà:",
-       "foreign-structured-upload-form-2-label-ownwork": "Adda essere sana sana <strong>na criaziona propria vosta</strong>, nun fosse na fiurella pigliata 'a ll'Internet",
-       "foreign-structured-upload-form-2-label-noderiv": "<strong>Nun adda mmescà ati fatiche fatte 'a n'ato</strong>, nun adda piglià ispirazione artistica 'e na fatica ca nun fose d' 'a vosta",
-       "foreign-structured-upload-form-2-label-useful": "Adda essere <strong>educazionale e utile</strong> pe' puté mparà ll'ata ggente",
-       "foreign-structured-upload-form-2-label-ccbysa": "Adda essere <strong>OK pe' pubbrecà pe' sempe</strong> ncopp'a ll'Internet sott' 'e condeziune d' 'a licienza [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0]",
-       "foreign-structured-upload-form-2-label-alternative": "Si chiste punte ccà ncoppa nun songo overe, putite ancora carrecà 'o file ausanno [https://commons.wikimedia.org/wiki/Special:UploadWizard Commons Upload Wizard], chest'è: si fosse a disposizione sott'a na licienza libbera.",
-       "foreign-structured-upload-form-2-label-termsofuse": "Carrecanno stu file, vuje lassate attestato 'e ricanoscenza ca site 'o possessore d' 'o copyright 'e stu file, e ca site d'accordo senza puté turnà arreto 'e libberà stu file p' 'o fà trasì dint'a Wikimedia Commons sott'a licienza Creative Commons Attribution-ShareAlike 4.0, e che site d'accordo ch' 'e [https://wikimediafoundation.org/wiki/Terms_of_Use Tiérmene d'auso].",
-       "foreign-structured-upload-form-3-label-question-website": "Avite scarrecata st'immaggene 'a nu sito internet o l'avite pigliato 'a na cerca-immaggene?",
-       "foreign-structured-upload-form-3-label-question-ownwork": "Avite criato vuje st'immaggene (scattat' 'a foto, criato 'o disegno, etc.) vuje stisso/a?",
-       "foreign-structured-upload-form-3-label-question-noderiv": "Cuntene, o s'ispirasse 'a, fatiche c' 'o copyright 'e coccherun'ato, comm'a nu logo?",
-       "foreign-structured-upload-form-3-label-yes": "Sì",
-       "foreign-structured-upload-form-3-label-no": "No",
-       "foreign-structured-upload-form-3-label-alternative": "Spiacente, ind'a sta situaziona, stu strumiento nun ve premmettesse 'e carrecà stu file. Putite tentà ancora 'e carrecà stu file ausanno [https://commons.wikimedia.org/wiki/Special:UploadWizard Commons Upload Wizard], si chesto se truvasse libbero sott'a na licienza libbera.",
-       "foreign-structured-upload-form-4-label-good": "Ausanno stu tool, putite carrecà grafiche educazionale c'avite criato vuje stisse, immaggene c'avite pigliato, e ca nun cunteneno fatiche 'a coccherun'ato.",
-       "foreign-structured-upload-form-4-label-bad": "Vuje nun putite carrecà immaggene truvate pe' miez' 'e nu mutor' 'e ricerca o c'avite scarrecat'a n'atu sito.",
        "backend-fail-stream": "Nun se può mannà 'o file \"$1\".",
        "backend-fail-backup": "Nun se può ffà 'o backup d' 'o file \"$1\".",
        "backend-fail-notexists": "'O file $1 nun esiste.",
        "querypage-disabled": "Sta paggena speciale è stutata pe' mutive 'e prestaziune.",
        "apihelp": "Ajuto cu l'API",
        "apihelp-no-such-module": "'O modulo \"$1\" nun se trova.",
+       "apisandbox": "Casciulella 'e pprove API",
+       "apisandbox-jsonly": "'O JavaScript è necessario pe' puté ausà 'a casciulella 'e pprove 'e ll'API.",
+       "apisandbox-api-disabled": "Ll'API è stutata ind'a stu sito.",
+       "apisandbox-intro": "Aùsa sta paggena pe' puté ffà prove c' 'o  <strong>servizio web 'e ll'API MediaWiki</strong>.\nVedite e ve piglià riferimento ncopp' 'a [[mw:API:Main page|documentazione 'e ll'API]] pe' n'avé cchiù dettaglie 'e comm'ausà n'API. Esempio: [//www.mediawiki.org/wiki/API#A_simple_example piglia 'e ccuntenute 'e na Paggena Prencepale]. Sceglie n'aziona pe' n'avé cchiù dettaglie.\n\nTenite a mmente ca, pure si chest'è na casciulella 'e pprove, ll'aziune ca vuje facite putessero cagnà sta wiki.",
+       "apisandbox-fullscreen": "Spanne pannello",
+       "apisandbox-fullscreen-tooltip": "Spanne 'o pannello 'e casciulella 'e pprove pe' puté ghienchere tuttaquanta 'a fenestella d' 'o navigatóre.",
+       "apisandbox-unfullscreen": "Mmusta paggena",
+       "apisandbox-unfullscreen-tooltip": "Reduce 'o pannello sandbox, accussì facenno ch' 'e cullegamente 'e navigazione 'e MediaWiki fossero a disposizione.",
+       "apisandbox-submit": "Fà 'na richiesta",
+       "apisandbox-reset": "Pulezza",
+       "apisandbox-retry": "Riprova",
+       "apisandbox-loading": "Carreca 'e nfurmaziune p' 'o modulo API \"$1\"...",
+       "apisandbox-load-error": "Se scassaje coccosa pe' tramente ca se carrecava nfurmazione p' 'o modulo API \"$1\": $2",
+       "apisandbox-no-parameters": "Stu modulo API nun tene parametre.",
+       "apisandbox-helpurls": "Cullegamente â guida",
+       "apisandbox-examples": "Esempio",
+       "apisandbox-dynamic-parameters": "Parametre addiziunale",
+       "apisandbox-dynamic-parameters-add-label": "Azzecca nu parametro:",
+       "apisandbox-dynamic-parameters-add-placeholder": "Nomme d' 'o parametro",
+       "apisandbox-dynamic-error-exists": "Nu parametro denommenato \"$1\" esiste già.",
+       "apisandbox-deprecated-parameters": "Parametre mò obsoleti",
+       "apisandbox-fetch-token": "Auto-ghienche 'o token",
+       "apisandbox-submit-invalid-fields-title": "Cocche campo nun è buono",
+       "apisandbox-submit-invalid-fields-message": "Pe' piacere curriggite 'e campe nzegnàte e tentate n'ata vota.",
+       "apisandbox-results": "Rezurtate",
+       "apisandbox-sending-request": "Mannanno na richiesta 'API..",
+       "apisandbox-loading-results": "Ricezione d' 'e risultate 'e ll'API 'ncurzo...",
+       "apisandbox-results-error": "N'errore cumparette pe' tramente ca se steva carrecanno na risposta 'e query API: $1.",
+       "apisandbox-request-url-label": "URL addimannata:",
+       "apisandbox-request-time": "Tiempo addimannato: {{PLURAL:$1|$1 ms}}",
+       "apisandbox-results-fixtoken": "Curregge 'e token e manna n'ata vota",
+       "apisandbox-results-fixtoken-fail": "Scassaje a se piglià 'o token \"$1\".",
+       "apisandbox-alert-page": "'E campe int'a sta paggene nun so' valide.",
+       "apisandbox-alert-field": "'O valore int'a stu campo nun è valido.",
        "booksources": "Funte libbrarie",
        "booksources-search-legend": "Ascìa 'e fonte ncopp' 'e libbre",
        "booksources-search": "Ascìa",
        "lockedbyandtime": "(pe' {{GENDER:$1|$1}} 'o $2 a 'e $3)",
        "move-page": "Mòve $1",
        "move-page-legend": "Mòve paggena",
-       "movepagetext": "Ausanno stu modulo ccà abbascio s'anommenarrà 'a paggena n'ata vota, movenno tutt' 'a cronologgia suja a l'atu nomme.\n'O titolo viecchio s'addeventarrà nu redirect â paggena c' 'o titolo nuovo. Putite agghiurnà 'e redirect ca puntassero ô titolo origgenale automaticamente.\nSi chesto nun facite, state sicuro 'e cuntrullà [[Special:DoubleRedirects|doppie ridirezionamiente]] o [[Special:BrokenRedirects|ridirezionamiente scassate]]. Vuje site 'o responsabbile 'e chillo ca cumbinate, assicurateve ca 'o cullegamiento cuntinua a spuntà addò avess'a spuntà.\n\nVedite bbuono ca 'a paggena <strong>nun</strong> se muoverrà si esiste n'ata paggena c' 'o titolo nuovo, a meno ca è abbacante o ca ce sta na paggena 'e ridirezionamiento senza cronologgia. Chesto significasse ca putite fà turnà 'o nomme viecchio â paggena addò ce steva apprimma si avite cumbinato nu nguacchio p'errore, e nun può sovrascrivere 'a paggena ch'esiste già. <strong>Attenzione!</strong> Chisto può essere nu cagnamiento drastico e inaspettato 'e na paggena famosa assaje; pe' piacere, avite 'a essere sicure-sicure d' 'e conseguenze apprimm' 'e cuntinuà.",
-       "movepagetext-noredirectfixer": "Ausanno stu modulo ccà abbascio s'anommenarrà 'a paggena n'ata vota, movenno tutt' 'a cronologgia suja a l'atu nomme.\n'O titolo viecchio s'addeventarrà nu redirect â paggena c' 'o titolo nuovo. State sicuro 'e cuntrullà [[Special:DoubleRedirects|doppie ridirezionamiente]] o [[Special:BrokenRedirects|ridirezionamiente scassate]]. Vuje site 'o responsabbile 'e chillo ca cumbinate, assicurateve ca 'o cullegamiento cuntinua a spuntà addò avess'a spuntà.\n\nVedite bbuono ca 'a paggena <strong>nun</strong> se muoverrà si esiste n'ata paggena c' 'o titolo nuovo, a meno ca è abbacante o ca ce sta na paggena 'e ridirezionamiento senza cronologgia. Chesto significasse ca putite fà turnà 'o nomme viecchio â paggena addò ce steva apprimma si avite cumbinato nu nguacchio p'errore, e nun può sovrascrivere 'a paggena ch'esiste già. <strong>Attenzione!</strong> Chisto può essere nu cagnamiento drastico e inaspettato 'e na paggena famosa assaje; pe' piacere, avite 'a essere sicure-sicure d' 'e conseguenze apprimm' 'e cuntinuà.",
+       "movepagetext": "Ausanno stu modulo ccà abbascio s'anommenarrà 'a paggena n'ata vota, movenno tutt' 'a cronologgia suja a l'atu nomme.\n'O titolo viecchio s'addeventarrà nu redirect â paggena c' 'o titolo nuovo. Putite agghiurnà 'e redirect ca puntassero ô titolo origgenale automaticamente.\nSi chesto nun facite, state sicuro 'e cuntrullà [[Special:DoubleRedirects|doppie ridirezionamiente]] o [[Special:BrokenRedirects|ridirezionamiente scassate]]. Vuje site 'o responsabbile 'e chillo ca cumbinate, assicurateve ca 'o cullegamiento cuntinua a spuntà addò avess'a spuntà.\n\nVedite bbuono ca 'a paggena <strong>nun</strong> se muoverrà si esiste n'ata paggena c' 'o titolo nuovo, a meno ca è abbacante o ca ce sta na paggena 'e ridirezionamiento senza cronologgia. Chesto significasse ca putite fà turnà 'o nomme viecchio â paggena addò ce steva apprimma si avite cumbinato nu nguacchio p'errore, e nun può sovrascrivere 'a paggena ch'esiste già.\n<strong>Attenzione!</strong> Chisto può essere nu cagnamiento drastico e inaspettato 'e na paggena famosa assaje; pe' piacere, avite 'a essere sicure-sicure d' 'e conseguenze apprimm' 'e cuntinuà.",
+       "movepagetext-noredirectfixer": "Ausanno stu modulo ccà abbascio s'anommenarrà 'a paggena n'ata vota, movenno tutt' 'a cronologgia suja a l'atu nomme.\n'O titolo viecchio s'addeventarrà nu redirect â paggena c' 'o titolo nuovo. State sicuro 'e cuntrullà [[Special:DoubleRedirects|doppie ridirezionamiente]] o [[Special:BrokenRedirects|ridirezionamiente scassate]]. Vuje site 'o responsabbile 'e chillo ca cumbinate, assicurateve ca 'o cullegamiento cuntinua a spuntà addò avess'a spuntà.\n\nVedite bbuono ca 'a paggena <strong>nun</strong> se muoverrà si esiste n'ata paggena c' 'o titolo nuovo, a meno ca è abbacante o ca ce sta na paggena 'e ridirezionamiento senza cronologgia. Chesto significasse ca putite fà turnà 'o nomme viecchio â paggena addò ce steva apprimma si avite cumbinato nu nguacchio p'errore, e nun può sovrascrivere 'a paggena ch'esiste già. \n<strong>Attenzione!</strong> Chisto può essere nu cagnamiento drastico e inaspettato 'e na paggena famosa assaje; pe' piacere, avite 'a essere sicure-sicure d' 'e conseguenze apprimm' 'e cuntinuà.",
        "movepagetalktext": "Si vuje facite click a sta casciulella, 'a paggena 'e chiacchiera suoccia a chesta sarrà spustata automaticamente addò 'o titolo nuovo, si nun è ca na paggena abbacante 'e chiacchiera esiste mo' mo' llàn\n\nInd' 'a stu caso, 'a paggena nun se muoverrà, ma 'a putite sempe scagnà manualmente si vulite.",
        "moveuserpage-warning": "<strong>Attenziò:</strong> Vuje state a muovere na paggena utente. Vedite bbuono ca sulamente 'a paggena sarrà spustata e l'utente <em>nun</em> sarrà reanummenato.",
        "movecategorypage-warning": "<strong>Attenziò:</strong> Vuje state a muovere na categurìa. Vedite bbuono ca sulamente 'a paggena sarrà spustata e 'a categurìa viecchia <em>nun</em> sarrà cagnata â nnova.",
        "movenosubpage": "Sta paggena nun tene sottopaggene.",
        "movereason": "Raggióne",
        "revertmove": "arrepiglia",
-       "delete_and_move_text": "== Scancellamiento richiesto ==\n'A paggena 'e destinazione \"[[:$1]]\" esiste già.\n'A vulite scancellà pe' ne putè ffà 'o spazio abbacante necessario?",
+       "delete_and_move_text": "'A paggena 'e destinazione \"[[:$1]]\" esiste già.\n'A vulite scancellà pe' ne putè ffà 'o spazio abbacante necessario?",
        "delete_and_move_confirm": "Sì, suprascrivi 'a paggena asistente",
        "delete_and_move_reason": "Scancellata pe ne fà spazio abbacante e putè spustà 'a \"[[$1]]\"",
        "selfmove": "'O titolo 'e sorgente e destinazione songh' 'e stesse;\nnun se può muovere na paggena ncopp' 'a essa stessa.",
        "move-leave-redirect": "Lassa nu ridirezionamiento arreto",
        "protectedpagemovewarning": "<strong>Attenziò:</strong> sta paggena è stata prutetta 'n modo tale ca sulamente l'utente ch' 'e privilegge d'ammenistratore 'a ponno muovere.\nL'urdemo elemento d' 'o riggistro è scritto ccà abbascio pe' n'avé riferimento:",
        "semiprotectedpagemovewarning": "'''Nota:''' Sta paggena è prutetta 'n modo ca sulamente l'utente riggistrate 'a ponno mòvere.\nL'urdemo elemento d' 'o riggistro è scritto ccà abbascio pe n'avé nfurmazione:",
-       "move-over-sharedrepo": "== 'O file esiste ==\n[[:$1]] esiste ncopp'a l'archivio spartuto. Spustanno 'o file ncopp'a stu titolo sovrascreverrà 'o file spartuto.",
+       "move-over-sharedrepo": "[[:$1]] esiste ncopp'a l'archivio spartuto. Spustanno 'o file ncopp'a stu titolo sovrascreverrà 'o file spartuto.",
        "file-exists-sharedrepo": "'O nomme d' 'o file c'avite scigliuto se sta ausanno dint'a l'archivio spartuto.\nPe' piacere, scigliete n'atu nomme.",
        "export": "Spurta 'e ppaggene",
        "exporttext": "Vuje putite espurtà 'e teste e cagnà 'a cronologgia 'e na paggena particulare o n'inzieme 'e paggena ca stessero dint' 'a cocche XML.\nChisto po' essere 'mpurtato int'a n'ata wiki ausanno [[Special:Import|mporta pàggene]] 'e MediaWiki.\n\nPe' spurtà paggene, mettite 'o titolo dint' 'e casciulelle ccà abbascio, nu titolo pe' linea e sciglite si vulite 'a verziona 'e mò cu tutt' 'e verziun' 'assieme, o pùre sulamente 'a verziona 'e mò c' 'a nfurmaziona ncopp' 'a ll'urdemo cagnamiento.\n\nComme urtema possibbiletà, putite pure ausà nu cullegamento, p'esempio [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]] p' 'a pàggena \"[[{{MediaWiki:Mainpage}}]]\".",
        "lastmodifiedatby": "Sta paggena è stata cagnata l'urdema vota 'e $2, d' 'o $1 da $3.",
        "othercontribs": "Basata ncopp' 'a fatica 'e $1.",
        "others": "ate",
-       "siteusers": "{{PLURAL:$2|utente|utente}} 'e {{SITENAME}} $1",
+       "siteusers": "{{PLURAL:$2|{{GENDER:$1|utente}}|utente}} 'e {{SITENAME}} $1",
        "anonusers": "{{PLURAL:$2|utente|utente}} anonime 'e {{SITENAME}} $1",
        "creditspage": "Auture d' 'a paggena",
        "nocredits": "Nun ce stanno nfurmaziune ncopp'a l'auture 'e sta paggena.",
        "expand_templates_preview_fail_html": "<em>Siccomme {{SITENAME}} téne 'o HTML 'ncruro appicciato e se songhe spierze 'e date d' 'a sessiona, 'a previsualizzaziona s'è annascunnuta comm'a na prutezione annanz'e uerre 'e JavaScript.</em>\n\n<strong>Si chist'è nu tentativo giustificato 'e previsualizzaziona, pe' piacere facite n'ata vota.</strong>\nSi nun funziona ancora, facite d'[[Special:UserLogout|ascì]] e trasì n'ata vota.",
        "expand_templates_preview_fail_html_anon": "<em>Siccomme {{SITENAME}} téne 'o HTML 'ncruro e vuje nun site trasute 'o sito, 'a previsualizzaziona s'è annascunnuta comm'a na prutezione annanz'e uerre 'e JavaScript.</em>\n\n<strong>Si chist'è nu tentativo giustificato 'e previsualizzaziona, pe' piacere facite d'[[Special:UserLogout|ascì]] e trasì n'ata vota.</strong>",
        "expand_templates_input_missing": "Avita dà minimo nu poco 'e testo scritto.",
-       "pagelanguage": "Scigliete 'a lengua d' 'a paggena pe' bbìa e stu strumiento",
+       "pagelanguage": "Cagna 'o nomme d' 'a paggena",
        "pagelang-name": "Paggena",
        "pagelang-language": "Lengua",
        "pagelang-use-default": "Aùsa 'a lengua predefinita",
        "pagelang-submit": "Manna",
        "right-pagelang": "Cagnate 'a lengua d' 'a paggena",
        "action-pagelang": "càgna 'a lengua d' 'a paggena",
-       "log-name-pagelang": "Càgna 'o riggistro 'e llengue",
+       "log-name-pagelang": "Cagna 'o riggistro d' 'a lengua",
        "log-description-pagelang": "Chest'è nu riggistro 'e cagnamiente 'e lengua d' 'e paggene.",
        "logentry-pagelang-pagelang": "$1 {{GENDER:$2|ave cagnato}} 'a lengua d' 'a paggena $3 'a $4 a $5.",
        "default-skin-not-found": "Oops! 'A skin predefinta ' 'o wiki vuosto, definita 'n <code dir=\"ltr\">$wgDefaultSkin</code> comme <code>$1</code>, nun se tròva.\n\n'A installazione pare ca tenesse {{PLURAL:$4|'a skin|'e skin}} ccà abbascio. Vedite [https://www.mediawiki.org/wiki/Manual:Skin_configuration Manuale: configurazione skin] pe' n'avè cchiù nfurmaziune ncopp' 'a manera {{PLURAL:$4|'e ll'abbià}} o scegliere chilla predefinita.\n\n$2\n\n; Si avite installato MediaWiki mò mò:\n: Probabbilmente l'avite installato 'a git, o direttamente 'a 'o codece sorgente ausanno cocch'atu metodo. Chesto era permesso. Verite 'e installà cocche skin 'a [https://www.mediawiki.org/wiki/Category:All_skins directory ncoppa mediawiki.org], tramite:\n:* Scarrecanno 'o [https://www.mediawiki.org/wiki/Download programma 'e installazione tarball], ca venesse fornito ch' 'e diverze skin ed estenziune. Putite fare copia-azzecca d' 'a directory <code dir=\"ltr\">skins/</code>.\n:* Scarrecanne 'e tarballs individuale 'e skin 'a [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org].\n:* [https://www.mediawiki.org/wiki/Download_from_Git#Using_Git_to_download_MediaWiki_skins Ausanno Git pe' scarrecà skin].\n: Facenno accussì nun se mmescasse 'o repository git vuosto si site sviluppatore MediaWiki.\n\n; Si avite MediaWiki agghiurnato MediaWiki mò mò:\n: MediaWiki 1.24 e verziune appriesso nun abbìa automatecamente 'e skin installate (vedite [https://www.mediawiki.org/wiki/Manual:Skin_autodiscovery Manuale: rilevamento automateco skin]). Putite copià {{PLURAL:$5|'a linea|'e linee}} ccà abbascio dint' 'o <code>LocalSettings.php</code> pe' putè appiccià {{PLURAL:$5|'o|tutt' 'e}} {{PLURAL:$5|skin}} installate mò mò:\n\n<pre dir=\"ltr\">$3</pre>\n\n; Si avite cagnato mò mò <code>LocalSettings.php</code>:\n: Cuntrullate 'e nomme d' 'e skin n'ata vota pe' ve sparagnà cocch'errore 'e battitura.",
index 86971d1..46f849e 100644 (file)
        "passwordreset-emailtext-ip": "Noen (sannsynligvis deg fra IP-adressen $1) ba om en tilbakestilling av ditt passord for {{SITENAME}} ($4). {{PLURAL:$3|Den følgende brukerkontoen|De følgende brukerkontoene}} er\ntilknyttet denne e-postadressen:\n\n$2\n\n{{PLURAL:$3|Dette midlertidige passordet|Disse midlertidige passordene}} utløper om {{PLURAL:$5|én dag|$5 dager}}.\nDu bør logge på og velge et nytt passord nå. Dersom noen andre kom med denne\nforespørselen, eller du har kommet på ditt opprinnelige passord, og ikke lenger\nønsker å endre det, kan du ignorere denne meldingen og fortsette å bruke ditt gamle\npassord.",
        "passwordreset-emailtext-user": "Brukeren $1 på {{SITENAME}} ba om en tilbakestilling av passordet ditt for {{SITENAME}}\n($4). {{PLURAL:$3|Den følgende brukerkontoen|De følgende brukerkontoene}} er tilknyttet denne e-postadressen:\n\n$2\n\n{{PLURAL:$3|Dette midlertidige passordet|Disse midlertidige passordene}} utløper om {{én dag|$5 dager}}.\nDu bør logge på og velge et nytt passord nå. Dersom noen andre kom med denne\nforespørselen, eller du har kommet på ditt opprinnelige passord, og ikke lenger\nønsker å endre det, kan du ignorere denne meldingen og fortsette å bruke ditt gamle\npassord.",
        "passwordreset-emailelement": "Brukernavn: \n$1\n\nMidlertidig passord: \n$2",
-       "passwordreset-emailsentemail": "Hvis dette er en registrert epostadresse for din konto, vil det bli sendt ut en passordtilbakestillingsepost.",
+       "passwordreset-emailsentemail": "Hvis denne epostadressen er koblet til din konto, så vil det bli sendt en epost om tilbakestilling av passord.",
        "passwordreset-emailsentusername": "Hvis det finnes en epostadresse knyttet til dette brukernavnet, vil en epost med informasjon om tilbakestilling av passord bli sendt.",
        "passwordreset-emailsent-capture": "Passordtilbakestillingseposten vist under har blitt sendt ut.",
        "passwordreset-emailerror-capture": "En passordtilbakestillingsepost ble laget, men det lyktes ikke å sende denne til {{GENDER:$2|brukeren}}: $1",
        "previewnote": "'''Husk at dette bare er en forhåndsvisning.'''\nEndringene dine har ikke blitt lagret ennå!",
        "continue-editing": "Gå til redigeringsfeltet",
        "previewconflict": "Slik vil teksten i redigeringsvinduet se ut dersom du lagrer den.",
-       "session_fail_preview": "'''Beklager! Klarte ikke å lagre redigeringen din på grunn av tap av øktdata.'''\nPrøv igjen.\nOm det fortsetter å gå galt, prøv å [[Special:UserLogout|logge ut]] og så inn igjen.'''",
+       "session_fail_preview": "'''Beklager! Klarte ikke å lagre redigeringen din på grunn av tap av øktdata.'''\n\nDu kan ha blitt logget ut. <strong>Sjekk at du fortsatt er innlogget og prøv igjen.</strong>\nOm det fortsetter å gå galt, prøv å [[Special:UserLogout|logge ut]] og så inn igjen, og sjekk at nettleseren din godtar informasjonskapsler fra denne siden.",
        "session_fail_preview_html": "'''Beklager! Klarte ikke å lagre redigeringen din på grunn av tap av øktdata.'''\n\n''Fordi {{SITENAME}} har rå HTML slått på, er forhåndsvisningen skjult for å forhindre JavaScript-angrep.''\n\n'''Om dette er et legitimt redigeringsforsøk, prøv igjen. Om det da ikke fungerer, prøv å [[Special:UserLogout|logge ut]] og logge inn igjen.'''",
        "token_suffix_mismatch": "'''Redigeringen din har blitt avvist fordi klienten din ikke hadde punktasjonstegn i redigeringsteksten. Redigeringen har blitt avvist for å hindre ødeleggelse av artikkelteksten. Dette forekommer av og til når man bruker vevbaserte anonyme proxytjenester.'''",
        "edit_form_incomplete": "'''Deler av redigeringsskjemaet nådde ikke tjeneren; dobbelsjekk at redigeringen er korrekt og prøv igjen.'''",
        "mergehistory-empty": "Ingen revisjoner kan flettes.",
        "mergehistory-done": "{{PLURAL:$3|Én revisjon|$3 revisjoner}} av $1 ble flettet til [[:$2]].",
        "mergehistory-fail": "Klarte ikke å utføre historikkfletting; sjekk siden og tidsparameterne igjen.",
+       "mergehistory-fail-bad-timestamp": "Tidsangivelsen er ugyldig.",
+       "mergehistory-fail-invalid-source": "Kildesiden er ugyldig.",
+       "mergehistory-fail-invalid-dest": "Målsiden er ugyldig.",
+       "mergehistory-fail-permission": "Utilstrekkelige tillatelser for å flette historikk.",
+       "mergehistory-fail-self-merge": "Kilde- og målsiden er den samme.",
+       "mergehistory-fail-timestamps-overlap": "Kilderevisjoner overlapper eller kommer etter målrevisjoner.",
        "mergehistory-fail-toobig": "Det er ikke mulig å utføre historikk-fletting fordi flere enn tillatte $1 {{PLURAL:$1|revisjon|revisjoner}} ville blitt flyttet.",
        "mergehistory-no-source": "Kildesiden $1 finnes ikke.",
        "mergehistory-no-destination": "Målsiden $1 finnes ikke.",
        "searchprofile-images-tooltip": "Søk etter filer",
        "searchprofile-everything-tooltip": "Søk i alt innhold (inkldert diskusjonssider)",
        "searchprofile-advanced-tooltip": "Søk i visse navnerom",
-       "search-result-size": "$1 ({{PLURAL:$2|ett|$2}} ord)",
+       "search-result-size": "$1 ({{PLURAL:$2|ett ord|$2 ord}})",
        "search-result-category-size": "{{PLURAL:$1|1 medlem|$1 medlemmer}} ({{PLURAL:$2|1 underkategori|$2 underkategorier}}, {{PLURAL:$3|1 fil|$3 filer}})",
        "search-redirect": "(omdirigering $1)",
        "search-section": "(avsnitt $1)",
        "prefs-help-prefershttps": "Denne preferansen vil virke etter neste innlogging.",
        "prefswarning-warning": "Du har gjort endringer i dine innstillinger som ikke er lagret ennå.\nDersom du forlater denne siden utenk å klikke på \"$1\" blir ikke innstillingene dine oppdatert.",
        "prefs-tabs-navigation-hint": "Tips: Du kan bruke venstre- og høyrepiltastene for å navigere mellom fanene i fanelisten",
-       "userrights": "Brukerrettighetskontroll",
+       "userrights": "Bruker&shy;rettighets&shy;kontroll",
        "userrights-lookup-user": "Ordne brukergrupper",
        "userrights-user-editname": "Fyll inn et brukernavn:",
        "editusergroup": "Endre {{GENDER:$1|brukergrupper}}",
        "grant-createeditmovepage": "Opprette, redigere eller flytte sider",
        "grant-delete": "Slette sider, revisjoner og logginnlegg",
        "grant-editinterface": "Redigere i MediaWiki-navnerommet og CSS/JavaScript i brukernavnerommet",
-       "grant-editmycssjs": "Redigere din bruker-CSS/JavaScript",
+       "grant-editmycssjs": "Redigere din bruker-CSS/-JavaScript",
        "grant-editmyoptions": "Rediger dine brukerinnstillinger",
        "grant-editmywatchlist": "Redigere overvåkningslisten din",
        "grant-editpage": "Redigere eksisterende sider",
        "grant-editprotected": "Redigere beskyttede sider",
-       "grant-highvolume": "Høyvolumredigering",
+       "grant-highvolume": "Høy&shy;volum&shy;redigering",
        "grant-oversight": "Skjule brukere og undertrykke revisjoner",
        "grant-patrol": "Patruljere sideendringer",
        "grant-protect": "Beskytte og avbeskytte sider",
-       "grant-rollback": "Tilbakestille sideendringer",
+       "grant-rollback": "Tilbakestille side&shy;endringer",
        "grant-sendemail": "Sende e-post til andre brukere",
        "grant-uploadeditmovefile": "Laste opp, erstatte, og flytte filer",
        "grant-uploadfile": "Laste opp nye filer",
+       "grant-basic": "Grunnleggende rettigheter",
        "grant-viewdeleted": "Vise slettede filer og sider",
        "grant-viewmywatchlist": "Vise overvåkningslisten din",
        "newuserlogpage": "Brukeropprettelseslogg",
        "number_of_watching_users_pageview": "[$1 overvåkende {{PLURAL:$1|bruker|brukere}}]",
        "rc_categories": "Begrens til kategorier (skilletegn: «|»)",
        "rc_categories_any": "Alle",
-       "rc-change-size-new": "$1 {{PLURAL:$1|byte}} etter endring",
+       "rc-change-size-new": "$1 {{PLURAL:$1|byte|bytes}} etter endring",
        "newsectionsummary": "/* $1 */ ny seksjon",
        "rc-enhanced-expand": "Vis detaljer",
        "rc-enhanced-hide": "Skjul detaljer",
        "upload-scripted-pi-callback": "Det er ikke tillatt å laste opp en fil som inneholder et kjørbart XML-stilark.",
        "uploaded-script-svg": "Fant et skriptelement \"$1\" i den opplastede SVG-koden.",
        "uploaded-hostile-svg": "Fant usikker CSS i stilelementet til opplastet SVG-fil",
+       "uploaded-href-attribute-svg": "href-attributter i SVG-filer tillates kun for http://- eller https://-mål; fant <code>&lt;$1 $2=\"$3\"%gt;</code>.",
+       "uploaded-href-unsafe-target-svg": "Fant href til usikre data: URI-mål <code>&lt;$1 $2=\"$3\"&gt;</code> i den opplastede SVG-filen.",
        "uploadscriptednamespace": "Denne SVG-filen inneholder et ulovlig navnerom \"$1\"",
        "uploadinvalidxml": "XML-en i den opplastede filen kunne ikke tolkes.",
        "uploadvirus": "Denne filen inneholder virus! Detaljer: $1",
        "upload-dialog-button-done": "Utført",
        "upload-dialog-button-save": "Lagre",
        "upload-dialog-button-upload": "Last opp",
-       "upload-form-label-select-file": "Velg fil",
        "upload-form-label-infoform-title": "Detaljer",
        "upload-form-label-infoform-name": "Navn",
        "upload-form-label-infoform-description": "Beskrivelse",
        "foreign-structured-upload-form-label-own-work-message-shared": "Jeg bekrefter at jeg har opphavsretten til denne filen, samtykker til å ugjenkallelig slippe filen til Wikimedia Commons under lisensen [https://creativecommons.org/licenses/by-sa/4.0/deed.no Creative Commons Navngivelse-DelPåSammeVilkår 4.0], og samtykker til [https://wikimediafoundation.org/wiki/Terms_of_Use bruksvilkårene].",
        "foreign-structured-upload-form-label-not-own-work-message-shared": "Hvis du ikke sitter på opphavsretten til filen, eller ønsker å slippe den under en annen lisens, prøv [https://commons.wikimedia.org/wiki/Special:UploadWizard Opplastingsveiviseren på Commons].",
        "foreign-structured-upload-form-label-not-own-work-local-shared": "Du kan eventuelt forsøke [[Special:Upload|den ordinære opplastingssiden på {{SITENAME}}]] hvis filen kan lastes opp under politikken som gjelder der.",
-       "foreign-structured-upload-form-2-label-intro": "Takk for at du donerer et bilde til bruk på {{SITENAME}}. Du kan kun fortsette hvis det oppfyller følgende krav:",
-       "foreign-structured-upload-form-2-label-ownwork": "Det må være <strong>ditt eget verk</strong>, ikke tatt fra internett",
-       "foreign-structured-upload-form-2-label-noderiv": "Det må <strong>ikke inneholde eller være sterkt inspirert av andres verk</strong>",
-       "foreign-structured-upload-form-2-label-useful": "Det bør være <strong>illustrerende og nyttig</strong>",
-       "foreign-structured-upload-form-2-label-ccbysa": "Det må være <strong>OK å publisere for evig tid</strong> på internett under [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0]-lisensen",
-       "foreign-structured-upload-form-2-label-alternative": "Hvis ikke alle kriteriene ovenfor er oppfylt, kan du i stedet laste opp filen med [https://commons.wikimedia.org/wiki/Special:UploadWizard Opplastingsveiviseren på Commons], gitt at filen er tilgjengelig under en fri lisens.",
-       "foreign-structured-upload-form-2-label-termsofuse": "Ved å laste opp filen går du god for at du har opphavsretten til den, du samtykker til å ugjenkallelig slippe filen til Wikimedia Commons under lisensen Creative Commons Navngivelse-DelPåSammeVilkår 4.0, og du samtykker til [https://wikimediafoundation.org/wiki/Terms_of_Use bruksvilkårene].",
-       "foreign-structured-upload-form-3-label-question-website": "Er bildet hentet fra nettside eller fra bildesøk?",
-       "foreign-structured-upload-form-3-label-question-ownwork": "Har du laget (fotografert, skisset, tegnet, …) bildet selv?",
-       "foreign-structured-upload-form-3-label-question-noderiv": "Inneholder det, eller er det sterkt inspirert av, arbeid som eies av noen andre, som f.eks. en logo?",
-       "foreign-structured-upload-form-3-label-yes": "Ja",
-       "foreign-structured-upload-form-3-label-no": "Nei",
-       "foreign-structured-upload-form-3-label-alternative": "Uheldigvis kan ikke filen lastes opp med dette verktøyet i dette tilfellet. Du kan fremdeles prøve å laste opp filen med [https://commons.wikimedia.org/wiki/Special:UploadWizard opplastingsveiviseren på Commons], så lenge filen er tilgjengelig under en fri lisens.",
-       "foreign-structured-upload-form-4-label-good": "Med dette verktøyet kan du laste opp illustrasjoner du selv har laget selv og fotografier du selv har tatt, som ikke inneholder andres arbeid.",
-       "foreign-structured-upload-form-4-label-bad": "Du kan ikke laste opp bilder du har funnet ved søk på internett eller lastet ned fra andre nettsider.",
        "backend-fail-stream": "Kunne ikke strømme filen $1.",
        "backend-fail-backup": "Kunne ikke sikkerhetskopiere filen $1.",
        "backend-fail-notexists": "Filen $1 finnes ikke.",
        "querypage-disabled": "Denne spesialsiden er deaktivert av ytelsesårsaker.",
        "apihelp": "API hjelp",
        "apihelp-no-such-module": "Modulen «$1» ikke funnet.",
+       "apisandbox": "API-sandkasse",
+       "apisandbox-api-disabled": "API er deaktivert på dette nettstedet.",
+       "apisandbox-intro": "Bruk denne siden for å eksperimentere med '''MediaWiki web service APIet'''.\nSjekk [//www.mediawiki.org/wiki/API:Main_page API-dokumentasjonen] for mer informasjon om bruk av APIet. Eksempel: [//www.mediawiki.org/wiki/API#A_simple_example hente innholdet til en hovedside]. Velg en handling for å se flere eksempler.\n\nMerk at du kan utføre handlinger her som fører til endringer på wikien.",
+       "apisandbox-submit": "Foreta en forespørsel",
+       "apisandbox-reset": "Tilbakestill",
+       "apisandbox-examples": "Eksempel",
+       "apisandbox-results": "Resultat",
+       "apisandbox-request-url-label": "Forespurt URL:",
+       "apisandbox-request-time": "Forespørselstid: $1",
        "booksources": "Bokkilder",
        "booksources-search-legend": "Søk etter bokkilder",
        "booksources-search": "Søk",
        "log-title-wildcard": "Søk i titler som starter med denne teksten",
        "showhideselectedlogentries": "Vis/skjul de valgte logghendelsene",
        "log-edit-tags": "Rediger merker til valgte loggposter",
+       "checkbox-select": "Velg: $1",
+       "checkbox-all": "Alle",
+       "checkbox-none": "Ingen",
+       "checkbox-invert": "Inverter",
        "allpages": "Alle sider",
        "nextpage": "Neste side ($1)",
        "prevpage": "Forrige side ($1)",
        "listgrouprights-namespaceprotection-namespace": "Navnerom",
        "listgrouprights-namespaceprotection-restrictedto": "Rettighet(er) som tillater at brukeren redigerer",
        "listgrants-summary": "Følgende er en liste over OAuth-tildelinger og hvilke brukerrettigheter de gir tilgang til. Brukere kan autorisere applikasjoner til å bruke kontoen deres, med rettigheter begrenset til de gitt av tildelingene brukeren har godkjent. En applikasjon som handler på vegne av en bruker kan imidlertid aldri benytte seg av rettigheter brukeren ikke selv har.\nDet kan finnes [[{{MediaWiki:Listgrouprights-helppage}}|ytterligere informasjon]] om de ulike rettighetene.",
-       "listgrants-rights": "Tildeling",
+       "listgrants-rights": "Rettigheter",
        "trackingcategories": "Sporingskategori",
        "trackingcategories-summary": "Denne siden lister sporingskategorier som er automatisk befolket av Mediawiki-programvaren. Navnene deres kan endres ved å redigere de tilhørende systembeskjedene i {{ns:8}}-navnerommet.",
        "trackingcategories-msg": "Sporingskategori",
        "sp-contributions-toponly": "Vis kun endringer som er gjeldende revisjoner",
        "sp-contributions-newonly": "Bare vis bidrag som er sideopprettinger",
        "sp-contributions-submit": "Søk",
-       "whatlinkshere": "Lenker hit",
+       "whatlinkshere": "Hva lenker hit",
        "whatlinkshere-title": "Sider som lenker til «$1»",
        "whatlinkshere-page": "Side:",
        "linkshere": "Følgende sider lenker til '''[[:$1]]''':",
        "javascripttest-pagetext-frameworks": "Vennligst velg en av følgende testerammeverk: $1",
        "javascripttest-pagetext-skins": "Velg et utseende for testene:",
        "javascripttest-qunit-intro": "Se [$1 testedokumentasjonen] på mediawiki.org.",
-       "tooltip-pt-userpage": "Din brukerside",
+       "tooltip-pt-userpage": "{{GENDER:|Din brukerside}}",
        "tooltip-pt-anonuserpage": "Brukersiden for IP-adressen du redigerer fra",
-       "tooltip-pt-mytalk": "Din diskusjonsside",
+       "tooltip-pt-mytalk": "{{GENDER:|Din}} diskusjonsside",
        "tooltip-pt-anontalk": "Diskusjon om redigeringer fra denne IP-adressen",
-       "tooltip-pt-preferences": "Dine innstillinger",
+       "tooltip-pt-preferences": "{{GENDER:|Dine}} innstillinger",
        "tooltip-pt-watchlist": "Liste over sider du overvåker for endringer.",
-       "tooltip-pt-mycontris": "Liste over dine bidrag",
+       "tooltip-pt-mycontris": "En liste over {{GENDER:|dine}} bidrag",
        "tooltip-pt-anoncontribs": "En liste over redigeringer gjort fra denne IP-adressen",
        "tooltip-pt-login": "Du oppfordres til å logge inn, men det er ikke obligatorisk",
        "tooltip-pt-logout": "Logg ut",
        "tooltip-search-fulltext": "Søk etter sider som innholder denne teksten",
        "tooltip-p-logo": "Gå til hovedsiden",
        "tooltip-n-mainpage": "Gå til hovedsiden",
-       "tooltip-n-mainpage-description": "Gå til hovedsiden",
+       "tooltip-n-mainpage-description": "Besøk hovedsiden",
        "tooltip-n-portal": "Om prosjektet, hva du kan gjøre, hvor du kan finne ting",
        "tooltip-n-currentevents": "Finn bakgrunnsinformasjon om aktuelle hendelser",
        "tooltip-n-recentchanges": "Liste over siste endringer på wikien.",
        "tooltip-t-recentchangeslinked": "Siste endringer i sider som blir lenket fra denne siden",
        "tooltip-feed-rss": "RSS-mating for denne siden",
        "tooltip-feed-atom": "Atom-mating for denne siden",
-       "tooltip-t-contributions": "Vis liste over bidrag fra denne brukeren",
+       "tooltip-t-contributions": "En liste over bidrag fra {{GENDER:$1|denne brukeren}}",
        "tooltip-t-emailuser": "Send en e-post til denne brukeren",
        "tooltip-t-info": "Mer informasjon om denne siden",
        "tooltip-t-upload": "Last opp filer",
        "metadata-help": "Denne filen inneholder tilleggsinformasjon, antagligvis lagt til av digitalkameraet eller skanneren brukt til å lage eller digitalisere det.\nHvis filen har blitt forandret fra utgangspunktet, kan enkelte detaljer være unøyaktige.",
        "metadata-expand": "Vis utvidede detaljer",
        "metadata-collapse": "Skjul utvidede detaljer",
-       "metadata-fields": "Bildemetadatafelt listet i denne meldingen inkluderes på bildesiden når metadatatabellen er slått sammen.\nAndre vil skjules som standard.\n* make\n* model\n* datetimeoriginal\n* exposuretime\n* fnumber\n* isospeedratings\n* focallength\n* artist\n* copyright\n* imagedescription\n* gpslatitude\n* gpslongitude\n* gpsaltitude",
+       "metadata-fields": "Bildemetadatafelt listet i denne meldingen inkluderes på bildesiden når metadatatabellen har kollapset.\nAndre vil skjules som standard.\n* make\n* model\n* datetimeoriginal\n* exposuretime\n* fnumber\n* isospeedratings\n* focallength\n* artist\n* copyright\n* imagedescription\n* gpslatitude\n* gpslongitude\n* gpsaltitude",
        "exif-imagewidth": "Bredde",
        "exif-imagelength": "Høyde",
        "exif-bitspersample": "Bits per komponent",
        "mw-widgets-titleinput-description-new-page": "siden eksisterer ikke ennå",
        "mw-widgets-titleinput-description-redirect": "omdiriger til $1",
        "api-error-blacklisted": "Vennligst velg en annen beskrivende tittel.",
+       "sessionprovider-generic": "$1 sesjoner",
+       "sessionprovider-mediawiki-session-cookiesessionprovider": "informasjons&shy;kapsel-baserte sesjoner",
        "randomrootpage": "Tilfeldig rotside"
 }
index 87d1d9c..6174c80 100644 (file)
        "previewnote": "'''Let op: dit is een controlepagina.'''\nUw tekst is niet opgeslagen!",
        "continue-editing": "Naar het bewerkingsvenster gaan",
        "previewconflict": "Deze voorvertoning geeft aan hoe de tekst in het bovenste veld eruit ziet als u deze opslaat.",
-       "session_fail_preview": "'''Uw bewerking is helaas niet opgeslagen omdat de sessiegegevens verloren zijn gegaan.'''\nProbeer het opnieuw.\nAls het dan nog niet lukt, [[Special:UserLogout|meld uzelf dan af]] en vervolgens weer aan.",
-       "session_fail_preview_html": "'''Uw bewerking is niet verwerkt, omdat de sessiegegevens verloren zijn gegaan.'''\n\n''Omdat in {{SITENAME}} ruwe HTML is ingeschakeld, is een voorvertoning niet mogelijk als bescherming tegen aanvallen met JavaScript.''\n\n'''Als dit een legitieme bewerking is, probeer het dan opnieuw.'''\nAls het dan nog niet lukt, [[Special:UserLogout|meld uzelf dan af]] en vervolgens weer aan.",
+       "session_fail_preview": "Uw bewerking is helaas niet opgeslagen omdat de sessiegegevens verloren zijn gegaan.\n\nMogelijk bent u automatisch afgemeld. <strong>Stel vast of u nog bent aangemeld en probeer het opnieuw.</strong>\nAls het dan nog niet lukt, [[Special:UserLogout|meld uzelf dan af]] en vervolgens weer aan, en controleer of uw browser wel cookies van deze site toestaat.",
+       "session_fail_preview_html": "Uw bewerking is niet verwerkt, omdat de sessiegegevens verloren zijn gegaan.\n\n<em>Omdat in {{SITENAME}} ruwe HTML is ingeschakeld, is een voorvertoning niet mogelijk als bescherming tegen aanvallen met JavaScript.</em>\n\n<strong>Als dit een legitieme bewerking is, probeer het dan opnieuw.</strong>\nAls het dan nog niet lukt, [[Special:UserLogout|meld uzelf dan af]] en vervolgens weer aan, en controleer of uw browser wel cookies van deze site toestaat.",
        "token_suffix_mismatch": "'''Uw bewerking is geweigerd, omdat uw browser de leestekens in het bewerkingstoken onjuist heeft behandeld.'''\nDe bewerking is geweigerd om verminking van de paginatekst te voorkomen.\nDit gebeurt soms als er een webgebaseerde proxydienst wordt gebruikt die fouten bevat.",
        "edit_form_incomplete": "'''Sommige onderdelen van het bewerkingsformulier hebben de server niet bereikt. Controleer of uw bewerkingen intact zijn en probeer het opnieuw.'''",
        "editing": "Bewerken van $1",
        "mergehistory-empty": "Er zijn geen versies die samengevoegd kunnen worden.",
        "mergehistory-done": "$3 {{PLURAL:$3|versie|versies}} van $1 {{PLURAL:$3|is|zijn}} samengevoegd naar [[:$2]].",
        "mergehistory-fail": "Kan geen geschiedenis samenvoegen, controleer opnieuw de pagina- en tijdinstellingen.",
+       "mergehistory-fail-bad-timestamp": "Het tijdstempel is ongeldig.",
+       "mergehistory-fail-invalid-source": "De bronpagina is ongeldig.",
+       "mergehistory-fail-invalid-dest": "De doelpagina is ongeldig.",
+       "mergehistory-fail-no-change": "Het samenvoegen van de geschiedenis is mislukt. Controleer de pagina- en tijdinstellingen.",
+       "mergehistory-fail-permission": "Onvoldoende rechten om geschiedenis samen te voegen.",
+       "mergehistory-fail-self-merge": "De bron- en doelpagina's zijn dezelfde.",
+       "mergehistory-fail-timestamps-overlap": "De bronversies overlappen of komen na de versies van de doelpagina.",
        "mergehistory-fail-toobig": "Niet in staat om geschiedenis samen te voegen omdat meer dan de limiet van $1 {{PLURAL:$1|versie wordt|versie worden}} verplaatst.",
        "mergehistory-no-source": "De bronpagina $1 bestaat niet.",
        "mergehistory-no-destination": "De bestemmingspagina $1 bestaat niet.",
        "uploaded-script-svg": "Scriptbaar element \"$1\" in het geüploade SVG-bestand gevonden.",
        "uploaded-hostile-svg": "Onveilige CSS in het \"style\"-element van het geüploade SVG-bestand gevonden.",
        "uploaded-event-handler-on-svg": "Het instellen van de event-handlereigenschappen <code>$1=\"$2\"</code> is niet toegestaan in SVG-bestanden.",
-       "uploaded-href-unsafe-target-svg": "href met onveilig doel <code>&lt;$1 $2=\"$3\"&gt;</code> in het geüploade SVG-bestand gevonden.",
+       "uploaded-href-attribute-svg": "\"href\"-kenmerken in SVG-bestanden zijn alleen toegestaan als koppeling naar http:// of https://. Aangetroffen is  <code>&lt;$1 $2=\"$3\"&gt;</code>.",
+       "uploaded-href-unsafe-target-svg": "href naar onveilig gegevens: URI-doel <code>&lt;$1 $2=\"$3\"&gt;</code> in het geüploade SVG-bestand gevonden.",
        "uploaded-animate-svg": "\"animate\"-label gevonden in het geüploade svg-bestand die href zou kunnen veranderen, met behulp van het \"from\"-attribuut <code>&lt;$1 $2=\"$3\"&gt;</code>.",
        "uploaded-setting-event-handler-svg": "Het instellen van de event-handlereigenschappen is geblokkeerd, <code>&lt;$1 $2=\"$3\"&gt;</code> gevonden in het geüploade SVG-bestand.",
        "uploaded-setting-href-svg": "Het label \"set\" gebruiken om het attribuut \"href\" toe te voegen aan het ouderelement is niet mogelijk.",
        "upload-dialog-button-done": "Afgerond",
        "upload-dialog-button-save": "Opslaan",
        "upload-dialog-button-upload": "Uploaden",
-       "upload-form-label-select-file": "Selecteer bestand",
        "upload-form-label-infoform-title": "Details",
        "upload-form-label-infoform-name": "Naam",
-       "upload-form-label-infoform-name-tooltip": "Een korte beschrijvende naam voor het bestand, die als de bestandsnaam wordt gebruikt. U kunt platte tekst met spaties gebruiken. Neem de bestandsextensie niet op.",
+       "upload-form-label-infoform-name-tooltip": "Een korte beschrijvende naam voor het bestand, die als de bestandsnaam wordt gebruikt. U kunt tekst met spaties gebruiken. Neem de bestandsextensie niet op.",
        "upload-form-label-infoform-description": "Beschrijving",
        "upload-form-label-infoform-description-tooltip": "Beschrijf kort alles wat voor het werk van belang is.\nBenoem voor een afbeelding de belangrijkste zaken die zijn afgebeeld, alsmede de plaats of de gelegenheid.",
        "upload-form-label-usage-title": "Gebruik",
        "foreign-structured-upload-form-label-own-work-message-shared": "Ik verklaar dat ik de auteursrechten bezit op dit bestand en ik ga onherroepbaar akkoord met het vrijgeven van dit bestand aan Wikimedia Commons onder de licentie [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Naamsvermelding-GelijkDelen 4.0] en ik ga akkoord met de [https://wikimediafoundation.org/wiki/Terms_of_Use Gebruiksvoorwaarden].",
        "foreign-structured-upload-form-label-not-own-work-message-shared": "Als u geen eigenaar bent van de auteursrechten van dit bestand, of als u het onder een andere licentie wilt vrijgeven, overweeg dan gebruik te maken van de [https://commons.wikimedia.org/wiki/Special:UploadWizard Commons Upload Wizard].",
        "foreign-structured-upload-form-label-not-own-work-local-shared": "U kunt ook de [[Special:Upload|uploadpagina op {{SITENAME}}]] gebruiken, als de site het uploaden van dit bestand onder hun beleid toestaat.",
-       "foreign-structured-upload-form-2-label-intro": "Fijn dat u een afbeelding wilt doneren voor gebruik op {{SITENAME}}! U kunt alleen verder gaan als uw afbeelding aan een aantal voorwaarden voldoet:",
-       "foreign-structured-upload-form-2-label-ownwork": "Het moet geheel <strong>zelfgemaakt</strong> zijn, niet van het internet gehaald",
-       "foreign-structured-upload-form-2-label-noderiv": "Het moet <strong>geen werk of inspiratie van anderen</strong> bevatten",
-       "foreign-structured-upload-form-2-label-useful": "Het moet <strong>leerzaam en nuttig</strong> zijn",
-       "foreign-structured-upload-form-2-label-ccbysa": "Het moet <strong>voor altijd gepubliceerd mogen worden</strong> op het internet onder de [https://creativecommons.org/licenses/by-sa/4.0/deed.nl Creative Commons Attribution-ShareAlike 4.0]-licentie.",
-       "foreign-structured-upload-form-2-label-alternative": "Als uw afbeelding niet aan al deze voorwaarden voldoet, kunt u hem wellicht alsnog uploaden via de [https://commons.wikimedia.org/wiki/Special:UploadWizard?uselang=nl wizard op Commons], zolang de afbeelding beschikbaar is onder een vrije licentie.",
-       "foreign-structured-upload-form-2-label-termsofuse": "Door dit bestand te uploaden, bevestigt u dat u het auteursrecht hebt op dit bestand, en gaat u ermee akkoord om dit bestand onherroepelijk vrij te geven aan Wikimedia Commons onder de Creative Commons Attribution-ShareAlike 4.0-licentie, en gaat u akkoord met de [https://wikimediafoundation.org/wiki/Terms_of_Use/nl gebruiksvoorwaarden].",
-       "foreign-structured-upload-form-3-label-question-website": "Hebt u deze afbeelding gedownload van een website, of gevonden met een zoekmachine?",
-       "foreign-structured-upload-form-3-label-question-ownwork": "Hebt u deze afbeelding zelf gemaakt (zelf een foto genomen, zelf een tekening getekend, o.i.d.)?",
-       "foreign-structured-upload-form-3-label-question-noderiv": "Bevat deze afbeelding werk dat eigendom is van iemand anders, zoals een logo, of is het geïnspireerd op werk van anderen?",
-       "foreign-structured-upload-form-3-label-yes": "Ja",
-       "foreign-structured-upload-form-3-label-no": "Nee",
-       "foreign-structured-upload-form-3-label-alternative": "Helaas kunt u dit bestand niet uploaden met deze software. Wellicht kunt u het alsnog uploaden via de [https://commons.wikimedia.org/wiki/Special:UploadWizard?uselang=nl wizard op Commons], zolang het bestand beschikbaar is onder een vrije licentie.",
-       "foreign-structured-upload-form-4-label-good": "Met deze software kunt u educationele afbeeldingen uploaden die u zelf gemaakt hebt, en foto's die u zelf genomen hebt, die geen werk bevatten dat eigendom is van anderen.",
-       "foreign-structured-upload-form-4-label-bad": "U kunt geen afbeeldingen uploaden die u gevonden hebt met een zoekmachine of gedownload hebt van andere websites.",
        "backend-fail-stream": "Het was niet mogelijk het bestand \"$1\" te streamen.",
        "backend-fail-backup": "Het was niet mogelijk een reservekopie van het bestand $1 te maken.",
        "backend-fail-notexists": "Het bestand $1 bestaat niet.",
        "querypage-disabled": "Deze speciale pagina is uitgeschakeld om performanceredenen.",
        "apihelp": "API-hulp",
        "apihelp-no-such-module": "Module \"$1\" niet gevonden.",
+       "apisandbox": "API-zandbak",
+       "apisandbox-jsonly": "JavaScript is vereist om de API-zandbak te kunnen gebruiken.",
+       "apisandbox-api-disabled": "De API is uitgeschakeld op deze site.",
+       "apisandbox-intro": "Gebruik deze pagina om te experimenteren met de <strong>MediaWiki-API</strong>.\nZie de [[mw:API:Main page|API-documentatie]] voor verdere details over het gebruik van de API. Voorbeeld: [//www.mediawiki.org/wiki/API#A_simple_example hoe de inhoud van een Hoofdpagina ophalen]. Selecteer een handeling om meer voorbeelden te zien.\n\nHoewel dit een testfunctie is, kunnen sommige handelingen toch wijzigingen in de wiki maken.",
+       "apisandbox-fullscreen": "Paneel uitvouwen",
+       "apisandbox-fullscreen-tooltip": "Het zandbakvenster uitvoeren om het browservenster te vullen.",
+       "apisandbox-unfullscreen": "Pagina weergeven",
+       "apisandbox-unfullscreen-tooltip": "Het zandbakvenster inklappen zodat de navigatie voor MediaWiki weer beschikbaar is.",
+       "apisandbox-submit": "Verzoek uitvoeren",
+       "apisandbox-reset": "Wissen",
+       "apisandbox-retry": "Opnieuw proberen",
+       "apisandbox-loading": "Bezig met laden van gegevens voor API-module \"$1\"...",
+       "apisandbox-examples": "Voorbeelden",
+       "apisandbox-results": "Resultaten",
+       "apisandbox-request-url-label": "Verzoek-URL:",
+       "apisandbox-request-time": "Doorlooptijd verzoek: $1",
        "booksources": "Boekinformatie",
        "booksources-search-legend": "Bronnen en gegevens over een boek zoeken",
        "booksources-search": "Zoeken",
        "tooltip-minoredit": "Deze wijziging als een kleine wijziging markeren",
        "tooltip-save": "Wijzigingen opslaan",
        "tooltip-preview": "Een voorvertoning maken. Gebruik dit voordat u opslaat!",
-       "tooltip-diff": "Gemaakte wijzigingen bekijken (zoals het in de geschiedenis zal te zien zijn)",
+       "tooltip-diff": "Weergeven welke wijzigingen u aan de tekst hebt gemaakt",
        "tooltip-compareselectedversions": "De verschillen tussen de geselecteerde versies van deze pagina bekijken.",
        "tooltip-watch": "Deze pagina aan uw volglijst toevoegen",
        "tooltip-watchlistedit-normal-submit": "Pagina's verwijderen",
index 26e87c0..36c098f 100644 (file)
        "recentchanges-label-plusminus": "Storleiken til sida vart endra med så mange byte",
        "recentchanges-legend-heading": "'''Tyding:'''",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (sjå dessutan [[Special:NewPages|lista over nye sider]])",
+       "recentchanges-submit": "Vis",
        "rcnotefrom": "Nedanfor er endringane gjorde sidan <strong>$2</strong> viste (opp til <strong>$1</strong> stykke)",
        "rclistfrom": "Vis nye endringar sidan $3 $2",
        "rcshowhideminor": "$1 småplukk",
        "rcshowhidemine": "$1 endringane mine",
        "rcshowhidemine-show": "Vis",
        "rcshowhidemine-hide": "Gøym",
+       "rcshowhidecategorization": "$1 kategorisering av sider",
+       "rcshowhidecategorization-show": "Vis",
        "rclinks": "Vis dei siste $1 endringane dei siste $2 dagane<br />$3",
        "diff": "skil",
        "hist": "hist",
        "mostrevisions": "Sidene med flest endringar",
        "prefixindex": "Alle sider med forstaving",
        "prefixindex-namespace": "Alle sider med førefeste ($1-namnerommet)",
+       "prefixindex-submit": "Vis",
        "prefixindex-strip": "Fjern førefestet i lista",
        "shortpages": "Korte sider",
        "longpages": "Lange sider",
        "usereditcount": "{{PLURAL:$1|éi endring|$1 endringar}}",
        "usercreated": "{{GENDER:$3|Oppretta}} den $1 $2",
        "newpages": "Nye sider",
+       "newpages-submit": "Vis",
        "newpages-username": "Brukarnamn:",
        "ancientpages": "Eldste sider",
        "move": "Flytt",
        "pager-older-n": "{{PLURAL:$1|eldre|eldre $1}}",
        "suppress": "Historikkfjerning",
        "querypage-disabled": "Spesialsida er slegen av for skuld yting.",
+       "apisandbox": "API-sandkasse",
+       "apisandbox-api-disabled": "API er slege av på nettstaden.",
+       "apisandbox-intro": "Nytt sida til å røyna ut '''MediaWiki web service API''-en.\nSjå [//www.mediawiki.org/wiki/API:Main_page API-dokumentasjonen] for meir informasjon om bruk av API-en. Døme: [//www.mediawiki.org/wiki/API#A_simple_example hent innhaldet til ei hovudside].\nVel ei handling for å sjå fleire døme.",
+       "apisandbox-submit": "Gjer førespurnad",
+       "apisandbox-reset": "Tøm",
+       "apisandbox-examples": "Døme",
+       "apisandbox-results": "Utfall",
+       "apisandbox-request-url-label": "Førespurd URL:",
+       "apisandbox-request-time": "Førespurnadstid: $1",
        "booksources": "Bokkjelder",
        "booksources-search-legend": "Søk etter bokkjelder",
        "booksources-search": "Søk",
        "specialloguserlabel": "Utøvar:",
        "speciallogtitlelabel": "Mål (tittel eller brukar):",
        "log": "Loggar",
+       "logeventslist-submit": "Vis",
        "all-logs-page": "Alle offentlege loggar",
        "alllogstext": "Kombinert vising av alle loggane på {{SITENAME}}. Du kan avgrense resultatet ved å velje loggtype, brukarnamn eller den sida som er påverka (hugs å skilje mellom store og små bokstavar)",
        "logempty": "Ingen element i loggen passar.",
        "log-title-wildcard": "Søk i titlar som byrjar med denne teksten",
        "showhideselectedlogentries": "Vis/gøym valde loggoppføringar",
+       "checkbox-all": "Alle",
+       "checkbox-none": "Ingen",
+       "checkbox-invert": "Vreng",
        "allpages": "Alle sider",
        "nextpage": "Neste side ($1)",
        "prevpage": "Førre sida ($1)",
        "cachedspecial-viewing-cached-ts": "Du ser på ein mellomlagra versjon av sida, som ikkje tarv vera heilt oppdatert.",
        "cachedspecial-refresh-now": "Sjå siste.",
        "categories": "Kategoriar",
+       "categories-submit": "Vis",
        "categoriespagetext": "Følgjande {{PLURAL:$1|category contains|kategoriar inneheld}} sider eller media.\n[[Special:UnusedCategories|Unytta kategoriar]] vert ikkje vist her.\nSjå òg [[Special:WantedCategories|ønska kategoriar]].",
        "categoriesfrom": "Vis kategoriar frå og med:",
        "special-categories-sort-count": "sorter etter storleik",
        "wlshowhideanons": "anonyme brukarar",
        "wlshowhidepatr": "patruljerte endringar",
        "wlshowhidemine": "mine endringar",
+       "wlshowhidecategorization": "kategorisering av sider",
        "watchlist-options": "Alternativ for overvakingslista",
        "watching": "Overvakar...",
        "unwatching": "Fjernar frå overvakinglista...",
        "delete-confirm": "Slett «$1»",
        "delete-legend": "Slett",
        "historywarning": "'''Åtvaring:''' Sida du held på å slette har ein historikk med om lag $1 {{PLURAL:$1|versjon|versjonar}}:",
+       "historyaction-submit": "Vis",
        "confirmdeletetext": "Du held på å varig slette ei side eller eit bilete saman med heile den tilhøyrande historikken frå databasen. Stadfest at du verkeleg vil gjere dette, at du skjønar konsekvensane, og at du gjer dette i tråd med [[{{MediaWiki:Policy-url}}|retningslinene]].",
        "actioncomplete": "Ferdig",
        "actionfailed": "Handlinga kunne ikkje verta utførd",
        "tooltip-pt-logout": "Logg ut",
        "tooltip-pt-createaccount": "Me oppfordrar til at du oppretter ein konto og loggar inn, men det er ikkje påkravd.",
        "tooltip-ca-talk": "Diskusjon om innhaldssida",
-       "tooltip-ca-edit": "Du kan endre denne sida. Bruk førehandsvisings-knappen før du lagrar.",
+       "tooltip-ca-edit": "Endre denne sida",
        "tooltip-ca-addsection": "Start ein ny bolk",
        "tooltip-ca-viewsource": "Denne sida er verna, men du kan sjå kjeldeteksten.",
        "tooltip-ca-history": "Eldre versjonar av sida",
        "tooltip-t-recentchangeslinked": "Nylege endringar på sider denne sida lenkjar til",
        "tooltip-feed-rss": "RSS-mating for denne sida",
        "tooltip-feed-atom": "Atom-mating for denne sida",
-       "tooltip-t-contributions": "Sjå liste over bidrag frå denne brukaren",
+       "tooltip-t-contributions": "Sjå liste over bidrag frå {{GENDER:$1|denne brukaren}}",
        "tooltip-t-emailuser": "Send ein e-post til denne brukaren",
        "tooltip-t-info": "Meir informasjon om sida",
        "tooltip-t-upload": "Last opp filer",
        "spam_reverting": "Attenderullar til siste versjon utan lenkje til $1",
        "spam_blanking": "Alle versjonar inneheldt lenkje til $1, tømmer sida",
        "spam_deleting": "Alle versjonane inneheldt lenkjer til $1, slettar.",
-       "simpleantispam-label": "Antispam-kontroll.\n<strong>IKKJE</strong> fyll ut dette feltet!",
+       "simpleantispam-label": "Antispam-kontroll.\n<strong>ikkje</strong> fyll ut dette feltet!",
        "pageinfo-title": "Informasjon om «$1»",
        "pageinfo-not-current": "Diverre er det umogeleg å gje ut denne informasjonen for gamle versjonar.",
        "pageinfo-header-basic": "Grunnleggjande informasjon",
        "patrol-log-page": "Patruljeringslogg",
        "patrol-log-header": "Dette er ein logg over patruljerte sideversjonar.",
        "log-show-hide-patrol": "$1 patruljeringslogg",
+       "log-show-hide-tag": "$1 merkelogg",
        "deletedrevision": "Slett gammal versjon $1",
        "filedeleteerror-short": "Feil ved sletting av fila: $1",
        "filedeleteerror-long": "Det vart ein feil under filslettinga av:\n\n$1",
        "tags-create-reason": "Årsak:",
        "tags-create-submit": "Opprett",
        "tags-create-no-name": "Du må oppgje eit merkenamn.",
+       "tags-edit-existing-tags-none": "«Ingen»",
+       "tags-edit-chosen-placeholder": "Vel nokre merke",
        "comparepages": "Samanlikna sider",
        "compare-page1": "Side 1",
        "compare-page2": "Side 2",
        "special-characters-group-gujarati": "Gujarati",
        "mw-widgets-dateinput-placeholder-day": "ÅÅÅÅ-MM-DD",
        "mw-widgets-dateinput-placeholder-month": "ÅÅÅÅ-MM",
+       "mw-widgets-titleinput-description-new-page": "sida finst ikkje enno",
+       "mw-widgets-titleinput-description-redirect": "omdiriger til $1",
        "api-error-blacklisted": "Vel eit anna namn som er meir skildrande.",
        "randomrootpage": "Tilfeldig rotsida"
 }
index df9c0d3..c1b8b59 100644 (file)
@@ -20,7 +20,7 @@
        "tog-underline": "ଲିଙ୍କତଳେଗାର ଟାଣିବା:",
        "tog-hideminor": "ନିକଟରେ ହୋଇଥିବା ଛୋଟ ବଦଳସବୁକୁ ଲୁଚାଇବେ",
        "tog-hidepatrolled": "ନଗଦ ବଦଳରେ ନିରିକ୍ଷଣ କରାଯାଇଥିବା ବଦଳ ସବୁକୁ ଲୁଚାଇବେ",
-       "tog-newpageshidepatrolled": "ନà­\82à¬\86 à¬ªà­\83ଷà­\8dଠାତାଲିà¬\95ାରà­\81 à¬\9cà¬\97ାହୋଇଥିବା ବଦଳସବୁକୁ ଲୁଚାଇବେ",
+       "tog-newpageshidepatrolled": "ନà­\82à¬\86 à¬ªà­\83ଷà­\8dଠାତାଲିà¬\95ାରà­\81 à¬ªà¬°à¬\96ା ହୋଇଥିବା ବଦଳସବୁକୁ ଲୁଚାଇବେ",
        "tog-hidecategorization": "ପୃଷ୍ଠାସବୁର ଶ୍ରେଣୀବିଭାଗ ଲୁଚାନ୍ତୁ",
        "tog-extendwatchlist": "କେବଳ ନଗଦ ହିଁ ନୁହେଁ, ସବୁଯାକ ବଦଳକୁ ଦେଖାଇବା ପାଇଁ ଦେଖଣାତାଲିକାକୁ ପୂରା ଦେଖାଇବେ",
        "tog-usenewrc": "ନଗଦ ବଦଳରେ ପୃଷ୍ଠା ଅନୁଯାୟୀ ଗୋଷ୍ଠୀ ବଦଳ ଏବଂ ଦେଖଣା",
@@ -33,7 +33,7 @@
        "tog-watchmoves": "ମୁଁ ଘୁଞ୍ଚାଇଥିବା ପୃଷ୍ଠା ଏବଂ ଫାଇଲଗୁଡ଼ିକୁ ମୋର ଦେଖଣାତାଲିକାରେ ଯୋଡ଼ନ୍ତୁ",
        "tog-watchdeletion": "ମୁଁ ଲିଭାଇଥିବା ପୃଷ୍ଠା ଏବଂ ଫାଇଲଗୁଡ଼ିକୁ ମୋର ଦେଖଣାତାଲିକାରେ ଯୋଡ଼ନ୍ତୁ",
        "tog-watchrollback": "ମୁଁ ପଛକୁ ଫେରାଇଦେଇଥିବା ମୋ ଦେଖଣାତାଲିକାର ପୃଷ୍ଠାସବୁକୁ ଯୋଡ଼ନ୍ତୁ",
-       "tog-minordefault": "ସବà­\81ଯାà¬\95 à¬¸à¬®à­\8dପାଦନାà¬\95à­\81 à¬\9bାà¬\8fà¬\81 ଛୋଟ ବଦଳ ଭାବରେ ସୂଚିତ କରିବେ",
+       "tog-minordefault": "ସବà­\81ଯାà¬\95 à¬¸à¬®à­\8dପାଦନାà¬\95à­\81 à¬\86ପà­\87 ଛୋଟ ବଦଳ ଭାବରେ ସୂଚିତ କରିବେ",
        "tog-previewontop": "ଏଡ଼ିଟ ବାକ୍ସ ଆଗରୁ ଦେଖଣା ଦେଖାଇବେ",
        "tog-previewonfirst": "ପ୍ରଥମ ବଦଳର ଦେଖଣା ଦେଖାଇବେ",
        "tog-enotifwatchlistpages": "ମୋ ଦେଖଣାତାଲିକାରେ ଥିବା ପୃଷ୍ଠା ବା ଫାଇଲରେ କିଛି ବଦଳ ହେଲେ ମୋତେ ଇମେଲ କରିବେ",
        "tog-watchlisthideminor": "ଦେଖଣା ତାଲିକାରେ ଛୋଟ ଛୋଟ ବଦଳ ଗୁଡ଼ିକ ଲୁଚାଇବେ",
        "tog-watchlisthideliu": "ଲଗ ଇନ କରିଥିବା ସଭ୍ୟମାନଙ୍କ ଦେଇ କରାହୋଇଥିବା ବଦଳଗୁଡ଼ିକ ଲୁଚାଇବେ",
        "tog-watchlisthideanons": "ଅଜଣା ସଭ୍ୟମାନଙ୍କ ଦେଇ କରାହୋଇଥିବା ବଦଳଗୁଡ଼ିକ ଲୁଚାଇବେ",
-       "tog-watchlisthidepatrolled": "ମà­\8b à¬¦à­\87à¬\96ଣା à¬¤à¬¾à¬²à¬¿à¬\95ାରà­\81 à¬\9cà¬\97ାଯାଇଥିବା ସମ୍ପାଦନାଗୁଡ଼ିକ ଲୁଚାଇବେ",
+       "tog-watchlisthidepatrolled": "ମà­\8b à¬¦à­\87à¬\96ଣା à¬¤à¬¾à¬²à¬¿à¬\95ାରà­\81 à¬ªà¬°à¬\96ା ଯାଇଥିବା ସମ୍ପାଦନାଗୁଡ଼ିକ ଲୁଚାଇବେ",
        "tog-watchlisthidecategorization": "ପୃଷ୍ଠାସବୁର ଶ୍ରେଣୀବିଭାଗ ଲୁଚାନ୍ତୁ",
        "tog-ccmeonemails": "ମୁଁ ଯେଉଁ ଇ-ମେଲ ସବୁ ଅନ୍ୟମାନଙ୍କୁ ପଠାଉଛି ସେସବୁର ନକଲ ମୋତେ ପଠାଇବେ ।",
        "tog-diffonly": "ତୁଳନା ତଳେ ପୃଷ୍ଠାର ଭିତର ଭାଗ ଦେଖାନ୍ତୁ ନାହିଁ",
        "tog-showhiddencats": "ଲୁଚାଯାଇଥିବା ଶ୍ରେଣୀଗୁଡ଼ିକ ଦେଖାଇବେ",
-       "tog-norollbackdiff": "ରà­\8bଲବà­\8dà­\9fାà¬\95 à¬\95ଲାପରେ ତୁଳନା ଦେଖାନ୍ତୁ ନାହିଁ",
+       "tog-norollbackdiff": "ପà¬\9bà¬\95à­\81 à¬«à­\87ରାà¬\87ଲାପରେ ତୁଳନା ଦେଖାନ୍ତୁ ନାହିଁ",
        "tog-useeditwarning": "ଯେତେବେଳେ ମୁଁ ଗୋଟିଏ ସାଇତାଯାଇନଥିବା ପୃଷ୍ଠାକୁ ବନ୍ଦ କରିଦିଏ ମୋତେ ଚେତାବନୀ ଦେବେ",
        "tog-prefershttps": "ଲଗ ଇନ କଲାପରେ ସର୍ବଦା ସୁରକ୍ଷିତ କନେକ୍ସନ ବ୍ୟବହାର କରିବେ",
        "underline-always": "ସବୁବେଳେ",
@@ -64,7 +64,7 @@
        "editfont-style": "ଫଣ୍ଟ ଶୈଳୀକୁ ବଦଳାଇବେ:",
        "editfont-default": "ବ୍ରାଉଜରରେ ଆଗରୁ ଥିବା ସୁବିଧା",
        "editfont-monospace": "ମନୋସ୍ପେସ ଥିବା ଫଣ୍ଟ",
-       "editfont-sansserif": "ସାନସ-ସେରିଫ ଫଣ୍ଟ",
+       "editfont-sansserif": "ସାନà­\8dସ-ସà­\87ରିଫ à¬«à¬£à­\8dà¬\9f",
        "editfont-serif": "ସେରିଫ ଫଣ୍ଟ",
        "sunday": "ରବିବାର",
        "monday": "ସୋମବାର",
        "listingcontinuesabbrev": "ଆହୁରି ଅଛି..",
        "index-category": "ସୂଚୀଥିବା ପୃଷ୍ଠାସବୁ",
        "noindex-category": "ସୂଚୀହୀନ ପୃଷ୍ଠାସବୁ",
-       "broken-file-category": "ଭà¬\99à­\8dà¬\97ା ଫାଇଲ ଲିଙ୍କ ଥିବା ପୃଷ୍ଠାସମୂହ",
+       "broken-file-category": "à¬\85ବà­\8cଧ ଫାଇଲ ଲିଙ୍କ ଥିବା ପୃଷ୍ଠାସମୂହ",
        "about": "ଏହା ବାବଦରେ",
        "article": "ସୂଚୀପତ୍ର",
        "newwindow": "(ଏହା ନୂଆ ଉଇଣ୍ଡୋରେ ଖୋଲିବ)",
        "navigation": "ଦିଗବାରେଣି",
        "and": "&#32;ଓ",
        "qbfind": "ଖୋଜନ୍ତୁ",
-       "qbbrowse": "ବà­\8dରାà¬\89à¬\9c",
+       "qbbrowse": "ଦà­\87à¬\96ିବà­\87",
        "qbedit": "ସମ୍ପାଦନା (Edit)",
        "qbpageoptions": "ଏହି ପୃଷ୍ଠାଟି",
        "qbmyoptions": "ମୋ ପୃଷ୍ଠାଗୁଡ଼ିକ",
        "viewtalkpage": "ଆଲୋଚନାଗୁଡ଼ିକୁ ଦେଖନ୍ତୁ",
        "otherlanguages": "ଅଲଗା ଭାଷାରେ",
        "redirectedfrom": "($1 ରୁ ଲେଉଟି ଆସିଛି)",
-       "redirectpagesub": "à¬\86à¬\89ଥରà­\87 à¬«à­\87ରିବା ପୃଷ୍ଠା",
+       "redirectpagesub": "ପà­\81ନà¬\83ପà­\8dରà­\87ରଣ ପୃଷ୍ଠା",
        "redirectto": "କେଉଁଠାକୁ ଲେଉଟାଣି:",
        "lastmodifiedat": "ଏହି ପୃଷ୍ଠାଟି $1 ତାରିଖ $2 ବେଳେ ବଦଳାଯାଇଥିଲା ।",
        "viewcount": "ଏହି ପୃଷ୍ଠାଟି {{PLURAL:$1|ଥରେ|$1 ଥର}} ଖୋଲାଯାଇଛି ।",
        "createacct-another-username-ph": "ଆପଣଙ୍କ ଇଉଜର ନାମ ଟାଇପ କରନ୍ତୁ",
        "yourpassword": "ପାସୱାର୍ଡ଼",
        "userlogin-yourpassword": "ପାସୱାର୍ଡ଼",
-       "userlogin-yourpassword-ph": "à¬\86ପଣà¬\99à­\8dà¬\95 à¬ªà¬¾à¬¸à­±à¬¾à¬°à­\8dଡ଼ à¬²à­\87à¬\96ନ୍ତୁ",
+       "userlogin-yourpassword-ph": "à¬\86ପଣà¬\99à­\8dà¬\95 à¬ªà¬¾à¬¸à­±à¬°à­\8dଡ଼ à¬¦à¬¿à¬\85ନ୍ତୁ",
        "createacct-yourpassword-ph": "ପାସୱର୍ଡ଼ ଦିଅନ୍ତୁ",
        "yourpasswordagain": "ପାସୱାର୍ଡ଼ ଆଉଥରେ:",
        "createacct-yourpasswordagain": "ପାସୱର୍ଡ଼ ନିଶ୍ଚିତ କରିବେ",
        "right-blockemail": "ଇ-ମେଲ ପଠାଇବାରୁ ଜଣେ ସଭ୍ୟଙ୍କୁ ବାରଣ କରିବେ",
        "right-hideuser": "ସାଧାରଣରୁ ଲୁଚାଇ ଏକ ଇଉଜର ନାମକୁ ଅଟକାଇବେ",
        "right-ipblock-exempt": "IP ଅଟକ, ଆପେଆପେ-ଅଟକ ଓ ସୀମା ଅଟକସବୁକୁ ଅଲଗା ଦିଗଗାମୀ କରିବେ",
-       "right-proxyunbannable": "ପ୍ରକ୍ସିର ଆପେଆପେ ହେଉଥିବା ଅଟକସବୁକୁ ଅଲଗା ଦିଗଗାମୀ କରିବେ",
        "right-unblockself": "ଜଣଙ୍କୁ ଅଟକରୁ ଚାଡ଼କରିବେ",
        "right-protect": "ନିରାପତ୍ତା ବଢ଼ାଇ କ୍ୟାସକେଡ଼-ନିରାପତ୍ତା ପୃଷ୍ଠାମାନଙ୍କୁ ବଦଳାନ୍ତୁ",
        "right-editprotected": "କିଳାଯାଇଥିବା ପୃଷ୍ଠାମାନଙ୍କର ସମ୍ପାଦନା କରିବେ (କ୍ୟାସକେଡ଼କରା କିଳଣା ବିନା)",
        "watchthisupload": "ଏହି ପୃଷ୍ଠାଟିକୁ ଦେଖିବେ",
        "filewasdeleted": "ଆଗରୁ ଏହି ନାମରେ ଅପଲୋଡ଼ କରାଯାଇଥିବା ଫାଇଲଟିଏ ଲିଭାଇ ଦିଆଯାଇଅଛି  ।\nଆଉଥରେ ଅପଲୋଡ଼ କରିବା ଆଗରୁ ଆପଣ $1ଟି ଠାରେ ପରଖି ନିଅନ୍ତୁ ।",
        "filename-bad-prefix": "ଆପଣ ଅପଲୋଡ଼ କରୁଥିବା '''\"$1\"'' ନାମରେ ଆରମ୍ଭ ହେଉଥିବା ଫାଇଲ, ଯାହାକି ଏକ ବଖଣାଯାଇନଥିବା ନାମରେ ନାମିତ ଓ ଆପେଆପେ ଡିଜିଟାଲ କ୍ୟାମେରାରୁ ଆସିଥାଏ ।\nଦୟାକରି ଏହି ଫାଇଲ ପାଇଁ ଏକ ବୁଝାପଡୁଥିବା ନାମଟିଏ ଦିଅନ୍ତୁ ।",
-       "upload-success-subj": "ଅପଲୋଡ଼ ସଫଳ",
-       "upload-success-msg": "ଆପଣଙ୍କ ଅପଲୋଡ଼ ଫର୍ମ [$2] ସଫଳ ହେଲା । ଏହା [[:{{ns:file}}:$1]] ଠାରେ ମିଳୁଅଛି ।",
-       "upload-failure-subj": "ଅପଲୋଡ଼ରେ ଅସୁବିଧା",
-       "upload-failure-msg": " [$2]ରୁ ଆପଣଙ୍କ କରିଥିବା ଅପଲୋଡ଼ରେ ଗୋଟିଏ ଅସୁବିଧା ଥିଲା:\n\n$1",
-       "upload-warning-subj": "ଅପଲୋଡ଼ ଚେତାବନୀ",
-       "upload-warning-msg": "[$2]ରୁ ଆପଣ କରିଥିବା ଅପଲୋଡରେ ଗୋଟିଏ ଅସୁବିଧା ଥିଲା । ଆପଣ [[Special:Upload/stash/$1|ଅପଲୋଡ଼ ଫର୍ମ]]କୁ ଫେରି ଏହି ଅସୁବିଧାଟିକୁ ସୁଧାରି ପାରନ୍ତି ।",
        "upload-proto-error": "ଭୁଲ ପ୍ରଟକଲ",
        "upload-proto-error-text": "ସୁଦୂରର ଅପଲୋଡ଼ପାଇଁ URL ସବୁ <code>http://</code> କିମ୍ବା <code>ftp://</code> ରେ ଆରମ୍ଭ ହେଉଥିବା ଲୋଡ଼ା ।",
        "upload-file-error": "ଭିତରର ଭୁଲ",
        "pager-older-n": "{{PLURAL:$1|ପୁରୁଣା 1|ପୁରୁଣା $1}}",
        "suppress": "ଅଜାଣତ ଅଣଦେଖା",
        "querypage-disabled": "ଏହି ବିଶେଷ ପୃଷ୍ଠାଟି ଦେଖଣା କାରଣରୁ ଅଚଳ କରାଯାଇଅଛି ।",
+       "apisandbox": "API ପରଖଘର",
+       "apisandbox-api-disabled": "API ଟି ଏହି ସାଇଟରେ ଅଚଳ କରାଯାଇଛି ।",
+       "apisandbox-submit": "ଅନୁରୋଧ କରିବେ",
+       "apisandbox-reset": "ଖାଲି କରିଦିଅନ୍ତୁ",
+       "apisandbox-examples": "ଉଦାହରଣ",
+       "apisandbox-results": "ପରିଣାମ",
+       "apisandbox-request-url-label": "URL ଅନୁରୋଧ କରିବେ:",
+       "apisandbox-request-time": "ଅନୁରୋଧ ସମୟ: $1",
        "booksources": "ବହିର ମୁଳାଧାର",
        "booksources-search-legend": "ବହିର ସ୍ରୋତସବୁକୁ ଖୋଜିବେ",
        "booksources-search": "ଖୋଜିବେ",
        "wlheader-showupdated": "ଆପଣ ଶେଷଥର ଦେଖିଥିବା ପୃଷ୍ଠାଗୁଡ଼ିକ '''ମୋଟା ଅକ୍ଷର'''ରେ ଦେଖାଯାଉଅଛି ।",
        "wlnote": "$3, $4 ଅନୁସାରେ ବିଗତ {{PLURAL:$2|ଘଣ୍ଟାକରେ|<strong>$2</strong> ଘଣ୍ଟାରେ}}{{PLURAL:$1|ଶେଷ ବଦଳ|ଶେଷ <strong>$1</strong> ବଦଳ ତଳେ ଦିଆଗଲା}} ।",
        "wlshowlast": "ଗତ $1 ଘଣ୍ଟା $2 ଦିନ ଦେଖାନ୍ତୁ",
-       "watchlistall2": "ସବୁ",
        "watchlist-options": "ଦେଖଣା ବିକଳ୍ପସବୁ",
        "watching": "ଦେଖୁଛି...",
        "unwatching": "ଦେଖୁନାହିଁ...",
        "javascripttest-pagetext-frameworks": "ଦୟାକରି ନିମ୍ନରେ ଥିବା ଏକ ପରଖ ପ୍ରକ୍ରିୟାକୁ ବାଛନ୍ତୁ :$1",
        "javascripttest-pagetext-skins": "ଏହି ପରଖକୁ ଚାଲୁ କରିବା ପାଇଁ ଏକ ଆବରଣ ବାଛନ୍ତୁ ।",
        "javascripttest-qunit-intro": "mediawiki.orgରେ [$1 testing documentation]କୁ ଦେଖନ୍ତୁ ।",
-       "tooltip-pt-userpage": "ଆପଣଙ୍କ ବ୍ୟବହାରକାରୀ ପୃଷ୍ଠା",
+       "tooltip-pt-userpage": "{{GENDER:|ଆପଣଙ୍କ}} ବ୍ୟବହାରକାରୀ ପୃଷ୍ଠା",
        "tooltip-pt-anonuserpage": "ଆପଣ ଯେଉଁ IP ଠିକଣାର ବ୍ୟବହାରକାରୀ ପୃଷ୍ଠାଟି ବଦଳାଇବା ପାଇଁ ଚେଷ୍ଟା କରୁଛନ୍ତି",
-       "tooltip-pt-mytalk": "ଆପଣଙ୍କ ଆଲୋଚନା ପୃଷ୍ଠା",
+       "tooltip-pt-mytalk": "{{GENDER:|ଆପଣଙ୍କ}} ଆଲୋଚନା ପୃଷ୍ଠା",
        "tooltip-pt-anontalk": "ଏହି IP ଠିକଣାରୁ କେହିଜଣେ କରିଥିବା ସମ୍ପାଦନାର ଆଲୋଚନା",
-       "tooltip-pt-preferences": "ମୋ ପସନ୍ଦ",
+       "tooltip-pt-preferences": "{{GENDER:|ମୋ}} ପସନ୍ଦ",
        "tooltip-pt-watchlist": "ବଦଳ ପାଇଁ ଆପଣ ଦେଖାଶୁଣା କରୁଥିବା ପୃଷ୍ଠାଗୁଡ଼ିକର ତାଲିକା",
-       "tooltip-pt-mycontris": "ଆପଣଙ୍କ ଅବଦାନ",
+       "tooltip-pt-mycontris": "{{GENDER:|ଆପଣଙ୍କ}} ଅବଦାନ ତାଲିକା",
        "tooltip-pt-login": "ଆପଣଙ୍କୁ ଲଗ-ଇନ କରିବାକୁ କୁହାଯାଉଅଛି ସିନା, ବାଧ୍ୟ କରାଯାଉନାହିଁ",
        "tooltip-pt-logout": "ଲଗଆଉଟ",
        "tooltip-pt-createaccount": "ଆପଣଙ୍କୁ ଗୋଟେ ଖାତା ଖୋଲି ଲଗ ଇନ କରିବା ପାଇଁ ପ୍ରୋତ୍ସାହିତ କରାଯାଉଛି, ଏମିତିବି ଏହା କରିବା ନିତାନ୍ତ ଆବଶ୍ୟକ ନୁହେଁ ।",
        "tooltip-t-recentchangeslinked": "ଏହି ପୃଷ୍ଠା ସଙ୍ଗେ ଯୋଡ଼ା ଫରଦଗୁଡ଼ିକରେ ଏବେ ଏବେ କରାଯାଇଥିବା ଅଦଳବଦଳ",
        "tooltip-feed-rss": "ଏହି ପୃଷ୍ଠାଟି ପାଇଁ RSS ଫିଡ଼",
        "tooltip-feed-atom": "ଏହି ପୃଷ୍ଠାଟି ପାଇଁ ଆଟମ ଫିଡ଼",
-       "tooltip-t-contributions": "ଏହି ଇଉଜରଙ୍କର ଦ୍ଵାରା ହୋଇଥିବା ବଦଳ ତାଲିକା",
+       "tooltip-t-contributions": "{{GENDER:$1|ଏହି ଇଉଜରଙ୍କର}} ଦ୍ଵାରା ହୋଇଥିବା ବଦଳ ତାଲିକା",
        "tooltip-t-emailuser": "ଏହି ସଭ୍ୟଙ୍କୁ ଇ-ମେଲଟିଏ ପଠାଇବେ",
        "tooltip-t-upload": "ଫାଇଲ ଅପଲୋଡ଼ କରିବେ",
        "tooltip-t-specialpages": "ବିଶେଷ ପୃଷ୍ଠାମାନଙ୍କର ଏକ ତାଲିକା",
index cc18bde..f2e641b 100644 (file)
        "remembermypassword": "Zapamiętaj moje logowanie na tym komputerze (maksymalnie przez $1 {{PLURAL:$1|dzień|dni}})",
        "userlogin-remembermypassword": "Nie wylogowuj mnie",
        "userlogin-signwithsecure": "Użyj bezpiecznego połączenia",
+       "cannotloginnow-title": "W tej chwili nie można się teraz zalogować",
+       "cannotloginnow-text": "Podczas korzystania z $1 nie można się zalogować.",
        "yourdomainname": "Twoja domena:",
        "password-change-forbidden": "Nie można zmieniać haseł na tej wiki.",
        "externaldberror": "Wystąpił błąd autentyfikacyjnej bazy danych lub nie posiadasz uprawnień koniecznych do aktualizacji zewnętrznego konta.",
        "copyrightwarning2": "Wszelki wkład na {{SITENAME}} może być edytowany, zmieniany lub usunięty przez innych użytkowników.\nJeśli nie chcesz, żeby Twój tekst był dowolnie zmieniany przez każdego i rozpowszechniany bez ograniczeń, nie umieszczaj go tutaj.<br />\nZapisując swoją edycję, oświadczasz, że ten tekst jest Twoim dziełem lub pochodzi z materiałów dostępnych na warunkach ''domeny publicznej'' lub kompatybilnych (zobacz także $1).\n'''PROSZĘ NIE WPROWADZAĆ MATERIAŁÓW CHRONIONYCH PRAWEM AUTORSKIM BEZ POZWOLENIA WŁAŚCICIELA!'''",
        "editpage-cannot-use-custom-model": "Model zawartości tej strony nie może być zmieniony.",
        "longpageerror": "'''Błąd! Wprowadzony przez Ciebie tekst ma {{PLURAL:$1|1 kilobajt|$1 kilobajty|$1 kilobajtów}}. Długość tekstu nie może przekraczać {{PLURAL:$2|1 kilobajt|$2 kilobajty|$2 kilobajtów}}. Tekst nie może być zapisany.'''",
-       "readonlywarning": "<strong>Uwaga! Baza danych została zablokowana do celów administracyjnych. W tej chwili nie można zapisać nowej wersji strony. Jeśli chcesz, może skopiować ją do pliku, aby móc zapisać ją później.<strong>\n\nAdministrator systemu, który zablokował bazę, podał następujący powód: $1",
+       "readonlywarning": "<strong>Uwaga! Baza danych została zablokowana do celów administracyjnych. W tej chwili nie można zapisać nowej wersji strony. Jeśli chcesz, może skopiować ją do pliku, aby móc zapisać ją później.</strong>\n\nAdministrator systemu, który zablokował bazę, podał następujący powód: $1",
        "protectedpagewarning": "'''Uwaga! Możliwość modyfikacji tej strony została zabezpieczona. Mogą ją edytować jedynie użytkownicy z uprawnieniami administratora.'''\nOstatni wpis z rejestru jest pokazany poniżej.",
        "semiprotectedpagewarning": "'''Uwaga!''' Ta strona została zabezpieczona i tylko zarejestrowani użytkownicy mogą ją edytować.\nOstatni wpis z rejestru jest pokazany poniżej.",
        "cascadeprotectedwarning": "<strong>Uwaga:</strong> Ta strona została zabezpieczona i tylko użytkownicy z uprawnieniami administratora mogą ją edytować. Została ona osadzona w {{PLURAL:$1|następującej stronie, która została zabezpieczona|następujących stronach, które zostały zabezpieczone}} z włączoną opcją dziedziczenia:",
        "mergehistory-empty": "Brak historii zmian do scalenia.",
        "mergehistory-done": "$3 {{PLURAL:$3|zmiana|zmiany|zmian}} w $1 {{PLURAL:$3|została scalona|zostały scalone|zostało scalonych}} z [[:$2]].",
        "mergehistory-fail": "Scalenie historii zmian jest niewykonalne. Zmień ustawienia parametrów.",
+       "mergehistory-fail-bad-timestamp": "Znacznik czasu jest nieprawidłowy.",
+       "mergehistory-fail-invalid-source": "Strona źródłowa jest nieprawidłowa.",
+       "mergehistory-fail-invalid-dest": "Strona docelowa jest nieprawidłowa.",
+       "mergehistory-fail-self-merge": "Strona źródłowa i docelowa są takie same.",
        "mergehistory-fail-toobig": "Nie można połączyć historii, gdyż wymagałoby to przeniesienia więcej niż maksymalnej dopuszczalnej liczby $1 {{PLURAL:$1|wersji}}.",
        "mergehistory-no-source": "Strona źródłowa $1 nie istnieje.",
        "mergehistory-no-destination": "Strona docelowa $1 nie istnieje.",
        "right-createpage": "Tworzenie stron (niebędących stronami dyskusji)",
        "right-createtalk": "Tworzenie stron dyskusji",
        "right-createaccount": "Tworzenie kont użytkowników",
+       "right-autocreateaccount": "Automatyczne logowanie za pomocą zewnętrznego konta użytkownika",
        "right-minoredit": "Oznaczanie edycji jako drobnych",
        "right-move": "Przenoszenie stron",
        "right-move-subpages": "Przenoszenie stron razem z ich podstronami",
        "upload-dialog-button-done": "Gotowe",
        "upload-dialog-button-save": "Zapisz",
        "upload-dialog-button-upload": "Prześlij",
-       "upload-form-label-select-file": "Wybierz plik",
        "upload-form-label-infoform-title": "Szczegóły",
        "upload-form-label-infoform-name": "Nazwa",
        "upload-form-label-infoform-name-tooltip": "Podaj krótką, opisującą i unikalną nazwę, która będzie służyła jako nazwa pliku. Możesz używać prostego języka i spacji. Nie dodawaj rozszerzenia pliku.",
        "foreign-structured-upload-form-label-own-work-message-shared": "Oświadczam, że mam prawa autorskie do tego pliku, nieodwołalnie publikuję go na Wikimedia Commons na licencji [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Uznanie autorstwa-Na tych samych warunkach 4.0] i zgadzam się na [https://wikimediafoundation.org/wiki/Terms_of_Use/pl warunki użytkowania].",
        "foreign-structured-upload-form-label-not-own-work-message-shared": "Jeżeli nie masz praw autorskich do tego pliku albo chcesz go opublikować na innej licencji, rozważ użycie [https://commons.wikimedia.org/wiki/Special:UploadWizard kreatora przesyłania plików].",
        "foreign-structured-upload-form-label-not-own-work-local-shared": "Możesz spróbować użyć [[Special:Upload|strony przesyłania plików {{GRAMMAR:D.lp|{{SITENAME}}}}]], jeżeli zasady tej strony dopuszczają publikację tego pliku.",
-       "foreign-structured-upload-form-2-label-intro": "Dziękujemy za przesłanie pliku na {{GRAMMAR:B.lp|{{SITENAME}}}}. Zanim kontynuujesz, upewnij się, że spełnia poniższe warunki:",
-       "foreign-structured-upload-form-2-label-ownwork": "Musi być w całości <strong>Twoją pracą</strong>, a nie pobrany z Internetu",
-       "foreign-structured-upload-form-2-label-noderiv": "<strong>Nie może</strong> zawierać fragmentów <strong>cudzych prac</strong> ani być nimi zainspirowany",
-       "foreign-structured-upload-form-2-label-useful": "Musi mieć <strong>wartość edukacyjną</strong> i ułatwiać przekazywanie wiedzy",
-       "foreign-structured-upload-form-2-label-ccbysa": "Musi być możliwe <strong>udostępnienie go na zawsze</strong> w Internecie na licencji [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Uznanie autorstwa-Na tych samych warunkach 4.0]",
-       "foreign-structured-upload-form-2-label-alternative": "Jeśli nie wszystkie z tych warunków są spełnione, być może możesz mimo wszystko przesłać go za pomocą [https://commons.wikimedia.org/wiki/Special:UploadWizard kreatora przesyłania plików Commons], o ile jest udostępniony na wolnej licencji.",
-       "foreign-structured-upload-form-2-label-termsofuse": "Przesyłając plik, oświadczasz, że masz prawa autorskie do tego pliku, nieodwołalnie publikujesz go na Wikimedia Commons na licencji Creative Commons Uznanie autorstwa-Na tych samych warunkach 4.0 i zgadzasz się na [https://wikimediafoundation.org/wiki/Terms_of_Use/pl warunki użytkowania].",
-       "foreign-structured-upload-form-3-label-question-website": "Czy {{GENDER:|pobrałeś|pobrałaś|pobrałeś/aś}} tę grafikę ze strony internetowej lub wyszukiwarki grafiki?",
-       "foreign-structured-upload-form-3-label-question-ownwork": "Czy {{GENDER:|stworzyłeś|stworzyłaś|stworzyłeś/aś}} tę grafikę ({{GENDER:|zrobiłeś|zrobiłaś|zrobiłeś/aś}} zdjęcie, {{GENDER:|naszkicowałeś|naszkicowałaś|naszkicowałeś/aś}} rysunek, itp.)  {{GENDER:|samemu|samej|samemu/samej}}?",
-       "foreign-structured-upload-form-3-label-question-noderiv": "Czy zawiera fragmenty cudzych prac (np. logo) lub jest nimi zainspirowana?",
-       "foreign-structured-upload-form-3-label-yes": "Tak",
-       "foreign-structured-upload-form-3-label-no": "Nie",
-       "foreign-structured-upload-form-3-label-alternative": "Niestety, w tej sytuacji nie jest możliwe przesłanie tego pliku za pomocą tego narzędzia. Być może możesz mimo to przesłać go za pomocą [https://commons.wikimedia.org/wiki/Special:UploadWizard kreatora przesyłania plików Commons], o ile jest udostępniony na wolnej licencji.",
-       "foreign-structured-upload-form-4-label-good": "Przy pomocy tego narzędzia możesz przesłać edukacyjne grafiki i fotografie, które {{GENDER:|zrobiłeś|zrobiłaś|zrobiłeś/aś}} i które nie zawierają fragmentów cudzych prac.",
-       "foreign-structured-upload-form-4-label-bad": "Nie możesz przesyłać grafik znalezionych w wyszukiwarkach lub pobranych z innych stron internetowych.",
        "backend-fail-stream": "Nie można odczytać pliku $1.",
        "backend-fail-backup": "Nie można utworzyć kopii zapasowej pliku  $1 .",
        "backend-fail-notexists": "Plik  $1  nie istnieje.",
        "querypage-disabled": "Ta strona specjalna została wyłączona ze względu na ograniczenia wydajności.",
        "apihelp": "Pomoc API",
        "apihelp-no-such-module": "Moduł \"$1\" nie znaleziony.",
+       "apisandbox": "Środowisko testowe API",
+       "apisandbox-jsonly": "Do korzystania z brudnopisu API wymagany jest JavaScript.",
+       "apisandbox-api-disabled": "API jest wyłączone na tej stronie.",
+       "apisandbox-intro": "Użyj tej strony do eksperymentowania z <strong>serwisem API MediaWiki</strong>.\nWięcej szczegółów na temat wykorzystywania API można znaleźć w [[mw:API:Main page|dokumentacji API]]. Przykład: [//www.mediawiki.org/wiki/API#A_simple_example pobranie zawartości strony głównej]. Wybierz akcję, by zobaczyć więcej przykładów.\n\nZwróć uwagę, że chociaż jest to brudnopis, to działania, które można przeprowadzać na tej stronie, mogą zmienić wiki.",
+       "apisandbox-unfullscreen": "Pokaż stronę",
+       "apisandbox-submit": "Wykonaj zapytanie",
+       "apisandbox-reset": "Wyczyść",
+       "apisandbox-retry": "Ponów próbę",
+       "apisandbox-no-parameters": "Ten moduł API nie posiada parametrów.",
+       "apisandbox-helpurls": "Linki pomocy",
+       "apisandbox-examples": "Przykłady",
+       "apisandbox-dynamic-parameters": "Parametry dodatkowe",
+       "apisandbox-dynamic-parameters-add-label": "Dodaj parametr:",
+       "apisandbox-dynamic-parameters-add-placeholder": "Nazwa parametru",
+       "apisandbox-dynamic-error-exists": "Parametr o nazwie „$1” już istnieje.",
+       "apisandbox-submit-invalid-fields-title": "Niektóre pola są nieprawidłowe",
+       "apisandbox-submit-invalid-fields-message": "Popraw zaznaczone pola i spróbuj ponownie.",
+       "apisandbox-results": "Wyniki",
+       "apisandbox-loading-results": "Pobieranie wyników API...",
+       "apisandbox-request-url-label": "URL zapytania:",
+       "apisandbox-request-time": "Czas przetwarzania zapytania: {{PLURAL:$1|$1 ms}}",
+       "apisandbox-alert-page": "Pola na tej stronie są nieprawidłowe.",
+       "apisandbox-alert-field": "Wartość tego pola jest nieprawidłowa.",
        "booksources": "Książki",
        "booksources-search-legend": "Szukaj informacji o książkach",
        "booksources-search": "Szukaj",
        "expand_templates_generate_rawhtml": "Pokaż surowy HTML",
        "expand_templates_preview": "Podgląd",
        "expand_templates_preview_fail_html": "<em>Ponieważ {{SITENAME}} ma włączony surowy kod HTML i zaistniała strata danych z sesji, podgląd jest ukryty jako zabezpieczenie przed atakiem JavaScript.</em>\n\n<strong>Jeśli to jest próba słusznego podglądu, proszę spróbować ponownie.</strong>\nJeśli to nadal nie działa, spróbuj [[Special:UserLogout|wylogować się]] i zalogować się z powrotem.",
-       "pagelanguage": "Wybór języka strony",
+       "pagelanguage": "Zmiana języka strony",
        "pagelang-name": "Strona",
        "pagelang-language": "Język",
        "pagelang-use-default": "Użyj domyślnego języka",
index 0ffcec6..a416870 100644 (file)
        "querypage-disabled": "Sta pàgina special a l'é disabilità për dle rason ëd prestassion.",
        "apihelp": "Agiut ëd l'API",
        "apihelp-no-such-module": "Ël mòdol «$1» as treuva nen.",
+       "apisandbox": "Spassi dle preuve API",
+       "apisandbox-api-disabled": "API a l'é disabilità ansima a 's sit.",
+       "apisandbox-intro": "Ch'a deuvra sta pàgina për sperimenté ël '''servissi an sl'aragnà MediaWiki API'''.\nCh'a fasa riferiment a [//www.mediawiki.org/wiki/API:Main_page la documentassion ëd l'API] për d'àutri detaj an sl'utilisassion ëd l'API. Për esempi: [//www.mediawiki.org/wiki/API#A_simple_example oten-e ël contnù ëd na pàgina d'Intrada]. Ch'a selession-a n'assion për vëdde d'àutri esempi.",
+       "apisandbox-submit": "Fé l'arcesta",
+       "apisandbox-reset": "Scancela",
+       "apisandbox-examples": "Esempi",
+       "apisandbox-results": "Arzultà",
+       "apisandbox-request-url-label": "Anliura d'arcesta:",
+       "apisandbox-request-time": "Temp necessari: $1",
        "booksources": "Andoa trové dij lìber",
        "booksources-search-legend": "Sërché antra ij lìber d'arferiment",
        "booksources-search": "Arserché",
index 94f20a7..c092d3f 100644 (file)
        "mergehistory-go": "اخږلو وړ سمونونه ښکاره کول",
        "mergehistory-submit": "بڼې سره يوځای کول",
        "mergehistory-done": "د $1 $3 {{PLURAL:$3|بڼه|بڼې}} په برياليتوب سره و [[:$2]] کې {{PLURAL:$3|واخږل شو|واخږل شول}}.",
+       "mergehistory-fail-bad-timestamp": "وخت ټاپه ناسمه ده.",
        "mergehistory-no-source": "د سرچينې مخ $1 نشته.",
        "mergehistory-no-destination": "د $1 موخنيز مخ نشته.",
        "mergehistory-invalid-source": "د سرچينې مخ بايد يو سم سرليک وي.",
        "prefs-watchlist-edits-max": "د شمېر اکثر بريد: 1000",
        "prefs-misc": "بېلابېل",
        "prefs-resetpass": "پټنوم بدلول",
-       "prefs-changeemail": "برېښليک بدلول",
+       "prefs-changeemail": "برېښليک پته بدلول يا ليرې کول",
        "prefs-setemail": "يوه برېښليک پته ورکړۍ",
        "prefs-email": "د برېښليک خوښنې",
        "prefs-rendering": "ښکارېدنه",
        "rcshowhidemine": "زما سمونې $1",
        "rcshowhidemine-show": "ښکاره کول",
        "rcshowhidemine-hide": "پټول",
+       "rcshowhidecategorization": "د مخ وېشنيزې $1",
        "rcshowhidecategorization-show": "ښکاره کول",
        "rcshowhidecategorization-hide": "پټول",
        "rclinks": "هغه وروستي $1 بدلونونه ښکاره کړی چې په $2 ورځو کې پېښ شوي<br />$3",
        "upload-dialog-button-done": "ترسره شو",
        "upload-dialog-button-save": "خوندي کول",
        "upload-dialog-button-upload": "پورته کول",
-       "upload-form-label-select-file": "دوتنه ټاکل",
        "upload-form-label-infoform-title": "ځانگړنې",
        "upload-form-label-infoform-name": "نوم",
        "upload-form-label-infoform-description": "څرگندونه",
        "foreign-structured-upload-form-label-own-work": "دا زما خپل کار دی",
        "foreign-structured-upload-form-label-infoform-categories": "وېشنيزې",
        "foreign-structured-upload-form-label-infoform-date": "نېټه",
-       "foreign-structured-upload-form-3-label-question-ownwork": "آيا دا انځور (انځور مو اخيستی، انځور مو کښلی، همداسې نور.) تاسو پخپله جوړ کړئ؟",
-       "foreign-structured-upload-form-3-label-yes": "هو",
-       "foreign-structured-upload-form-3-label-no": "نه",
        "backend-fail-notexists": "د $1 په نوم دوتنه نشته.",
        "backend-fail-delete": "د \"$1\" دوتنه ړنګه نه شوه.",
        "backend-fail-alreadyexists": "د $1 دوتنه له پخوا نه شته.",
        "suppress": "ځپل",
        "apihelp": "API لارښود",
        "apihelp-no-such-module": "د \"$1\" ماډيول و نه موندل شو.",
+       "apisandbox": "API آزمونمخ",
+       "apisandbox-unfullscreen": "مخ ښکاره کول",
+       "apisandbox-submit": "غوښته کول",
+       "apisandbox-reset": "سپينول",
+       "apisandbox-retry": "بيا هڅه کول",
+       "apisandbox-helpurls": "لارښود تړنې",
+       "apisandbox-examples": "بېلگې",
+       "apisandbox-dynamic-parameters-add-label": "پاراميټرونه ورگډول",
+       "apisandbox-results": "پايلې",
+       "apisandbox-request-url-label": "د URL غوښتنه کول:",
+       "apisandbox-request-time": "د غوښتنې وخت: {{PLURAL:$1|$1 م.ث}}",
        "booksources": "د کتاب سرچينې",
        "booksources-search-legend": "د کتابي سرچينو پلټنه",
        "booksources-isbn": "ISBN:",
        "redirect-user": "کارن پېژند",
        "redirect-page": "د مخ پېژند",
        "redirect-file": "د دوتنې نوم",
+       "redirect-logid": "پېژند يادښت",
        "redirect-not-exists": "ارزښت و نه موندل شو",
        "fileduplicatesearch": "د دوه گونو دوتنو پلټنه",
        "fileduplicatesearch-legend": "د دوه گونو دوتنو پلټنه",
        "expand_templates_remove_nowiki": "په پايلو کې د <nowiki> نښلنونه ځپل",
        "expand_templates_generate_rawhtml": "خام HTML ښکاره کول",
        "expand_templates_preview": "مخليدنه",
-       "pagelanguage": "د Ù\85Ø® Ú\98بټاکÙ\88Ù\86Ú©Û\8c",
+       "pagelanguage": "د Ù\85Ø® Ú\98بÙ\87 Ø¨Ø¯Ù\84Ù\88Ù\84",
        "pagelang-name": "مخ",
        "pagelang-language": "ژبه",
        "pagelang-use-default": "تلواليزه ژبه کارول",
        "pagelang-submit": "سپارل",
        "right-pagelang": "د مخ ژبه بدلول",
        "action-pagelang": "د مخ ژبه بدلول",
-       "log-name-pagelang": "ژبيادښت بدلول",
+       "log-name-pagelang": "د ژب بدلون يادښت",
        "default-skin-not-found-row-enabled": "* <code>$1</code> / $2 (چارن)",
        "default-skin-not-found-row-disabled": "* <code>$1</code> / $2 ('''ناچارن''')",
        "mediastatistics": "د رسنيو شمار",
index 3230f75..9b21344 100644 (file)
@@ -88,7 +88,9 @@
                        "Claudio Emanuel Weiler",
                        "Almondega",
                        "Eduardo Addad de Oliveira",
-                       "Raphaelras"
+                       "Raphaelras",
+                       "Arthurteb303",
+                       "Rffontenelle"
                ]
        },
        "tog-underline": "Sublinhar links:",
        "tog-watchlisthidebots": "Ocultar edições de bots da lista de páginas vigiadas",
        "tog-watchlisthideminor": "Ocultar edições menores da lista de páginas vigiadas",
        "tog-watchlisthideliu": "Ocultar edições de usuários autenticados da lista de páginas vigiadas",
+       "tog-watchlistreloadautomatically": "Recarregar a lista de páginas vigiadas automaticamente sempre que um filtro for alterado (requer JavaScript)",
        "tog-watchlisthideanons": "Ocultar edições de usuários anônimos da lista de páginas vigiadas",
        "tog-watchlisthidepatrolled": "Ocultar edições patrulhadas da lista de páginas vigiadas",
        "tog-watchlisthidecategorization": "Ocultar a categorização das páginas",
        "missingarticle-rev": "(revisão#: $1)",
        "missingarticle-diff": "(Dif.: $1, $2)",
        "readonly_lag": "O banco de dados foi automaticamente bloqueado enquanto os servidores secundários se sincronizam com o principal",
+       "nonwrite-api-promise-error": "O cabeçalho HTTP \"Promise-Non-Write-API-Action\" foi enviado, mas a requisição era para um módulo de escrita de API.",
        "internalerror": "Erro interno",
        "internalerror_info": "Erro interno: $1",
        "internalerror-fatal-exception": "Excepção fatal do tipo \"$1\"",
        "formerror": "Erro: Não foi possível enviar o formulário",
        "badarticleerror": "Esta ação não pode ser realizada nesta página.",
        "cannotdelete": "Não foi possível eliminar a página ou arquivo $1.\nÉ possível que ele já tenha sido eliminado por outra pessoa.",
-       "cannotdelete-title": "Não é possível excluir a página \" $1 \"",
+       "cannotdelete-title": "Não é possível eliminar a página \"$1\"",
        "delete-hook-aborted": "A eliminação foi cancelada por um \"hook\".\nNão foi dada nenhuma explicação.",
        "no-null-revision": "Não foi possível criar nova revisão nula para a página \"$1\"",
        "badtitle": "Título inválido",
        "mypreferencesprotected": "Você não tem permissão para editar suas preferências.",
        "ns-specialprotected": "Não é possível editar páginas especiais",
        "titleprotected": "Este título foi protegido, para que não seja criado.\nQuem o protegeu foi [[User:$1|$1]], com a justificativa: ''$2''.",
-       "filereadonlyerror": "Não é possível modificar o arquivo \"$1\" porque o repositório do arquivo \"$2\" está em modo somente leitura.\n\nO administrador que bloqueou ofereceu a seguinte explicação: \"$3\".",
+       "filereadonlyerror": "Não é possível modificar o arquivo \"$1\" porque o repositório do arquivo \"$2\" está em modo somente leitura.\n\nO administrador de sistema que bloqueou ofereceu a seguinte explicação: \"$3\".",
        "invalidtitle-knownnamespace": "Título inválido para o espaço nominal \"$2\" e texto \"$3\"",
        "invalidtitle-unknownnamespace": "Título inválido para o espaço nominal de número desconhecido ($1) e texto \"$2\"",
        "exception-nologin": "Não está autenticado",
        "virus-unknownscanner": "antivírus desconhecido:",
        "logouttext": "'''Agora você encontra-se desautenticado.'''\n\nNote que algumas páginas podem continuar sendo exibidas como se você ainda estivesse autenticado até que você limpe a ''cache'' do seu navegador.",
        "cannotlogoutnow-title": "Não é possível encerrar a sessão agora",
+       "cannotlogoutnow-text": "Não é possível sair usando $1.",
        "welcomeuser": "Bem-vindo, $1!",
        "welcomecreation-msg": "A sua conta foi criada.\nNão se esqueça de personalizar as suas [[Special:Preferences|preferências no wiki {{SITENAME}}]].",
        "yourname": "Nome de usuário:",
        "userlogin-remembermypassword": "Mantenha-me conectado",
        "userlogin-signwithsecure": "Use a conexão segura",
        "cannotloginnow-title": "Não é possível iniciar a sessão agora",
+       "cannotloginnow-text": "Não é possível autenticar usando $1.",
        "yourdomainname": "Seu domínio:",
        "password-change-forbidden": "Você não pode alterar senhas nessa wiki.",
        "externaldberror": "Ocorreu ou um erro no banco de dados durante a autenticação ou não lhe é permitido atualizar a sua conta externa.",
        "wrongpasswordempty": "Foi fornecida uma senha em branco.\nTente novamente.",
        "passwordtooshort": "As senhas devem ter no mínimo {{PLURAL:$1|1 caractere|$1 caracteres}}.",
        "passwordtoolong": "Senhas não podem ser maiores do que {{PLURAL:$1|1 caractere|$1 caracteres}}.",
+       "passwordtoopopular": "Senhas comuns não podem ser usadas. Por favor escolha uma senha mais difícil",
        "password-name-match": "A sua senha deve ser diferente do seu nome de usuário.",
        "password-login-forbidden": "O uso deste nome de usuário e senha foi desautorizado.",
        "mailmypassword": "Redefinir senha",
        "resetpass_submit": "Definir senha e entrar",
        "changepassword-success": "Sua senha foi alterada com sucesso!",
        "changepassword-throttled": "Você realizou demasiadas tentativas de se registrar.\nPor favor, aguarde $1 antes de tentar novamente.",
-       "botpasswords-existing": "Senhas bot existentes",
-       "botpasswords-createnew": "Crie uma nova senha bot",
-       "botpasswords-editexisting": "Editar uma senha existente bot",
+       "botpasswords": "Senhas de robôs",
+       "botpasswords-summary": "<em>Senhas de robôs</em> permitem acesso a uma conta de usuário via a API sem usar as credenciais da conta para autenticação. Os direitos disponíveis do usuário ao se autenticar com uma senha bot podem ser restritos.\n\nSe você não sabe porque você poderia precisar fazer isso, provavelmente você não deveria fazer. Ninguém jamais deve perguntar a você para criar uma dessas e fornecê-la.",
+       "botpasswords-disabled": "Senhas de robôs estão desabilitadas.",
+       "botpasswords-no-central-id": "Para usar senhas de robôs, você deve estar autenticada para uma conta centralizada.",
+       "botpasswords-existing": "Senhas de robôs existentes",
+       "botpasswords-createnew": "Crie uma nova senha de robô",
+       "botpasswords-editexisting": "Editar uma senha de robô existente",
        "botpasswords-label-appid": "Nome do robô:",
        "botpasswords-label-create": "Criar",
        "botpasswords-label-update": "Atualizar",
        "botpasswords-label-grants": "Permissões aplicáveis",
        "botpasswords-label-restrictions": "Restrições de uso:",
        "botpasswords-label-grants-column": "Concedido",
-       "botpasswords-created-title": "Password Bot criado",
-       "botpasswords-updated-title": "Password Bot atualizado",
-       "botpasswords-deleted-title": "Bot senha excluído",
+       "botpasswords-bad-appid": "O nome de robô \"$1\" não é válido.",
+       "botpasswords-insert-failed": "Falha ao adicionar o nome de robô \"$1\". Ele já foi adicionado?",
+       "botpasswords-update-failed": "Falha ao atualizar o nome do robô \"$1\". Elo já foi apagado?",
+       "botpasswords-created-title": "Senha de robô criada",
+       "botpasswords-created-body": "A senha do robô \"$1\" foi criada com sucesso.",
+       "botpasswords-updated-title": "Senha de robô atualizada",
+       "botpasswords-updated-body": "A senha de robô \"$1\" foi atualizada com sucesso.",
+       "botpasswords-deleted-title": "Senha de bot apagada",
+       "botpasswords-deleted-body": "A senha de bot \"$1\" foi apagada.",
+       "botpasswords-no-provider": "BotPasswordsSessionProvider não está disponível.",
+       "botpasswords-restriction-failed": "Restrições de senha de robô evitam esta autenticação.",
+       "botpasswords-invalid-name": "O nome de usuário especificado não contém o separador de senha de robô (\"$1\").",
+       "botpasswords-not-exist": "O usuário \"$1\" não possui uma senha de robô \"$2\".",
        "resetpass_forbidden": "As senhas não podem ser alteradas",
        "resetpass-no-info": "Você precisa estar autenticado para acessar esta página diretamente.",
        "resetpass-submit-loggedin": "Alterar senha",
        "previewnote": "'''Lembre-se de que isto é apenas uma previsão.'''\nSuas alterações ainda não foram salvas!",
        "continue-editing": "Ir para a área de edição",
        "previewconflict": "Esta previsão reflete o texto que está na área de edição acima e como ele aparecerá se você escolher salvar.",
-       "session_fail_preview": "'''Pedimos desculpas, mas não foi possível processar a sua edição devido à perda de dados da sua sessão.\nPor favor tente novamente.\nCaso continue não funcionando, tente [[Special:UserLogout|sair]] e voltar a entrar na sua conta.'''",
+       "session_fail_preview": "Pedimos desculpas, mas não foi possível processar a sua edição devido à perda de dados da sua sessão.\n'''Por favor, verifique se você ainda está autenticado e tente novamente.'''\nCaso continue não funcionando, tente [[Special:UserLogout|sair]] e voltar a entrar na sua conta, e cheque se seu navegador permite cookies desse site.",
        "session_fail_preview_html": "'''Desculpe-nos! Não foi possível processar a sua edição devido a uma perda de dados de sessão.'''\n\n''Como o projeto {{SITENAME}} possui HTML bruto ativo, a previsão não será exibida, como uma precaução contra ataques por JavaScript.''\n\n'''Se esta é uma tentativa de edição legítima, por favor tente novamente.\nCaso continue não funcionando, tente [[Special:UserLogout|desautenticar-se]] e voltar a entrar na sua conta.'''",
        "token_suffix_mismatch": "'''A sua edição foi rejeitada uma vez que seu software de navegação mutilou os sinais de pontuação do sinal de edição. A edição foi rejeitada para evitar perdas no texto da página.\nIsso acontece ocasionalmente quando se usa um serviço de proxy anonimizador mal configurado.'''",
        "edit_form_incomplete": "'''Algumas partes do formulário de edição não chegaram ao servidor; verifique que a sua edição continua intacta e tente novamente, por favor.'''",
        "permissionserrorstext-withaction": "Você não possui permissão para $2, {{PLURAL:$1|pelo seguinte motivo|pelos motivos a seguir}}:",
        "recreate-moveddeleted-warn": "'''Atenção: Você está recriando uma página já eliminada em outra ocasião.'''\n\nConsidere se é realmente adequado continuar editando esta página.\nOs registros de eliminação e de movimentação desta página são exibidos a seguir, para sua comodidade:",
        "moveddeleted-notice": "Esta página foi eliminada.\nOs registros de eliminação e de movimentação para esta página estão disponibilizados abaixo, para referência.",
-       "moveddeleted-notice-recent": "Desculpe, esta página foi excluído recentemente (nos últimos 24 horas). A eliminação e se mover para a página de log são fornecidos abaixo para referência.",
+       "moveddeleted-notice-recent": "Desculpe, esta página foi eliminada recentemente (nos últimos 24 horas). A eliminação e se mover para a página de log são fornecidos abaixo para referência.",
        "log-fulllog": "Ver registro detalhado",
        "edit-hook-aborted": "Edição abortada por ''hook''.\nEle não deu nenhuma explicação.",
        "edit-gone-missing": "Não foi possível atualizar a página.\nEla parece ter sido eliminada.",
        "last": "ant",
        "page_first": "primeira",
        "page_last": "última",
-       "histlegend": "Como selecionar: marque as caixas de seleção das versões que deseja comparar e pressione enter ou clique no botão na parte inferior do formulário.<br />\nLegenda: '''({{int:cur}})''' = diferenças em relação a última versão, '''({{int:last}})''' = diferenças em relação a versão anterior, '''{{int:minoreditletter}}''' = edição menor.",
+       "histlegend": "Como selecionar: marque as caixas de seleção das versões que deseja comparar e pressione enter ou clique no botão na parte inferior do formulário.<br />\nLegenda: <strong>({{int:cur}})</strong> = diferenças em relação a última versão, <strong>({{int:last}})</strong> = diferenças em relação a versão anterior, <strong>{{int:minoreditletter}}</strong> = edição menor.",
        "history-fieldset-title": "Navegar pelo histórico",
        "history-show-deleted": "Apenas as eliminadas",
        "histfirst": "Mais antigas",
        "right-editmyprivateinfo": "Editar seus próprios dados privados (exemplo: endereço de e-mail, nome real)",
        "right-editmyoptions": "Modifique suas preferências.",
        "right-rollback": "Reverter rapidamente o último usuário que editou uma página em particular",
-       "right-markbotedits": "Marcar edições revertidas como edições de bot",
+       "right-markbotedits": "Marcar edições revertidas como edições de robôs",
        "right-noratelimit": "Não afetado pelos limites de velocidade de operação",
        "right-import": "Importar páginas de outros wikis",
        "right-importupload": "Importar páginas de um arquivo carregado",
        "upload-dialog-button-done": "Feito",
        "upload-dialog-button-save": "Salvar",
        "upload-dialog-button-upload": "Enviar",
-       "upload-form-label-select-file": "Selecionar arquivo",
        "upload-form-label-infoform-title": "Detalhes",
        "upload-form-label-infoform-name": "Nome",
        "upload-form-label-infoform-description": "Descrição",
        "foreign-structured-upload-form-label-infoform-categories": "Categorias",
        "foreign-structured-upload-form-label-infoform-date": "Data",
        "foreign-structured-upload-form-label-not-own-work-local-local": "Você pode também querer tentar [[Special:Upload|the default upload page]]",
-       "foreign-structured-upload-form-2-label-useful": "Deve ser <strong>educativo e útil</strong> para ensinar a outros.",
-       "foreign-structured-upload-form-3-label-yes": "Sim",
-       "foreign-structured-upload-form-3-label-no": "Não",
        "backend-fail-stream": "Não foi possível transmitir o arquivo  $1.",
        "backend-fail-backup": "Não foi possível fazer backup do arquivo  $1 .",
        "backend-fail-notexists": "O arquivo $1 não existe.",
        "querypage-disabled": "Esta página especial está desativada para não prejudicar o desempenho.",
        "apihelp": "Ajuda de API",
        "apihelp-no-such-module": "Modulo \"$1\" não foram achados.",
+       "apisandbox": "Caixa de areia da API",
+       "apisandbox-api-disabled": "A API está desabilitada neste site.",
+       "apisandbox-intro": "Use esta página para realizar testes com o '''serviço web de API do MediaWiki'''.\nConsulte a [//www.mediawiki.org/wiki/API:Main_page a documentação API] para obter mais detalhes de uso da API.  Exemplo: [//www.mediawiki.org/wiki/API#A_simple_example obter o conteúdo de uma Página principal].  Selecione uma ação para mais exemplos.\n\nNote que, embora esta seja uma área de testes, as operações que executar nesta página podem modificar a wiki.",
+       "apisandbox-submit": "Fazer requisição",
+       "apisandbox-reset": "Limpar",
+       "apisandbox-examples": "Exemplo",
+       "apisandbox-results": "Resultado",
+       "apisandbox-request-url-label": "URL solicitante:",
+       "apisandbox-request-time": "Tempo do pedido: $1",
        "booksources": "Fontes bibliográficas",
        "booksources-search-legend": "Pesquisar referências bibliográficas",
        "booksources-search": "Pesquisar",
        "listgrouprights-namespaceprotection-header": "Restrições de namespace",
        "listgrouprights-namespaceprotection-namespace": "Namespace",
        "listgrouprights-namespaceprotection-restrictedto": "Direito(s) permitindo edições do usuário",
-       "listgrants": "Permissões",
-       "listgrants-grant": "Permissão",
+       "listgrants": "Atribuições",
+       "listgrants-grant": "Atribuição",
        "listgrants-rights": "Direitos",
        "trackingcategories": "Categorias de rastreamento",
        "trackingcategories-summary": "Esta página lista categorias de monitoramento que são preenchidas automaticamente pelo software MediaWiki. Seus nomes podem ser alterados através da alteração das mensagens de sistema relevantes no namespace {{ns: 8}}.",
        "newimages-summary": "Esta página especial mostra os arquivos mais recentemente enviados",
        "newimages-legend": "Filtrar",
        "newimages-label": "Nome de arquivo (ou parte dele):",
-       "newimages-showbots": "Mostrar uploads realizados por bots",
+       "newimages-showbots": "Mostrar uploads realizados por robôs",
        "newimages-hidepatrolled": "Ocultar os carregamentos patrulhados.",
        "noimages": "Nada para ver.",
        "ilsubmit": "Pesquisar",
index 72b7b3e..32c1057 100644 (file)
        "previewnote": "'''Lembre-se que esta é apenas uma antevisão do resultado.'''\nAs modificações ainda não foram gravadas!",
        "continue-editing": "Ir para a área de edição",
        "previewconflict": "Esta antevisão do resultado apresenta o texto da caixa de edição acima tal como este aparecerá se escolher gravá-lo.",
-       "session_fail_preview": "'''Não foi possível processar a edição devido à perda dos dados da sua sessão.\nTente novamente, por favor.\nCaso continue a não funcionar, tente [[Special:UserLogout|sair]] e voltar a entrar na sua conta.'''",
-       "session_fail_preview_html": "'''Não foi possível processar a edição devido à perda dos dados da sua sessão.'''\n\n''Como a wiki {{SITENAME}} possibilita o uso de HTML bruto, a antevisão está oculta por precaução contra ataques com JavaScript.''\n\n'''Se esta é uma tentativa legítima de edição tente novamente, por favor.'''\nCaso continue a não funcionar, tente [[Special:UserLogout|sair]] e voltar a entrar na sua conta.",
+       "session_fail_preview": "Desculpe! Não foi possível processar a edição devido à perda dos dados da sua sessão.\n\nA sua sessão poderá ter sido encerrada. <strong>Por favor, verifique se ainda está autenticado e tente novamente</strong>. \nCaso continue a não funcionar, tente [[Special:UserLogout|sair]] e voltar a entrar na sua conta, e verifique se o seu navegador permite a utilização de ''cookies'' deste sítio.",
+       "session_fail_preview_html": "Desculpe! Não foi possível processar a edição devido à perda de dados da sua sessão.\n\n<em>Como a wiki {{SITENAME}} possibilita o uso de HTML puro, a antevisão está oculta por precaução contra ataques com JavaScript.</em>\n\n<strong>Se esta é uma tentativa legítima de edição tente novamente, por favor</strong>. \nCaso continue a não funcionar, tente [[Special:UserLogout|sair]] e voltar a entrar na sua conta, e verifique se o seu navegador permite a utilização de ''cookies'' deste sítio.",
        "token_suffix_mismatch": "'''A edição foi rejeitada porque o seu navegador alterou os sinais de pontuação no editor.'''\nA edição foi rejeitada para evitar perdas no texto da página.\nIsso acontece ocasionalmente quando se usa um serviço de proxy anonimizador mal configurado.'''",
        "edit_form_incomplete": "'''Algumas partes do formulário de edição não chegaram ao servidor; verifique que a sua edição continua intacta e tente novamente, por favor.'''",
        "editing": "A editar $1",
        "mergehistory-empty": "Não existem revisões fundíveis.",
        "mergehistory-done": "Foram fundidas $3 {{PLURAL:$3|edição|edições}} de $1 {{PLURAL:$3|em}} [[:$2]].",
        "mergehistory-fail": "Não foi possível fundir os históricos; verifique a página e os parâmetros de tempo, por favor.",
+       "mergehistory-fail-bad-timestamp": "Registo data/hora inválido",
+       "mergehistory-fail-invalid-source": "Página de origem inválida.",
+       "mergehistory-fail-invalid-dest": "Página de destino inválida.",
+       "mergehistory-fail-self-merge": "As páginas de origem e de destino não podem ser a mesma.",
        "mergehistory-fail-toobig": "Não é possível fundir o histórico, já que um número de revisão(ões) acima do limite ($1 {{PLURAL:$1|revisão|revisões}}) seriam movidos.",
        "mergehistory-no-source": "A página de origem $1 não existe.",
        "mergehistory-no-destination": "A página de destino $1 não existe.",
        "upload-dialog-button-done": "Feito",
        "upload-dialog-button-save": "Gravar",
        "upload-dialog-button-upload": "Carregar",
-       "upload-form-label-select-file": "Selecionar ficheiro",
        "upload-form-label-infoform-title": "Detalhes",
        "upload-form-label-infoform-name": "Nome",
        "upload-form-label-infoform-description": "Descrição",
        "foreign-structured-upload-form-label-own-work-message-local": "Confirmo que estou a carregar este ficheiro segundo as condições de serviço e política de licenças de {{SITENAME}}.",
        "foreign-structured-upload-form-label-not-own-work-message-local": "Se não é capaz de carregar este ficheiro sob as políticas de {{SITENAME}}, por favor feche esta janela e tente outro método.",
        "foreign-structured-upload-form-label-not-own-work-local-local": "Poderá querer experimentar [[Special:Upload|a página padrão de carregamento]].",
-       "foreign-structured-upload-form-label-own-work-message-default": "Entendo que estou a carregar este ficheiro em um repositório partilhado. Confirmo que estou a fazê-lo cumprindo com os termos de serviço e com as políticas de licenciamento dali.",
+       "foreign-structured-upload-form-label-own-work-message-default": "Entendo que estou a carregar este ficheiro em um repositório partilhado. Confirmo que estou a fazê-lo cumprindo com os termos de serviço e com as políticas de licenciamento.",
        "foreign-structured-upload-form-label-not-own-work-message-default": "Se não é capaz de carregar este ficheiro sob as políticas do repositório partilhado, por favor feche esta janela e tente outro método.",
-       "foreign-structured-upload-form-label-not-own-work-local-default": "Pode querer tentar utilizar [[Special:Upload|a página de carregamento em {{SITENAME}}]], se este ficheiro puder ser carregado de acordo com suas políticas.",
-       "foreign-structured-upload-form-label-own-work-message-shared": "Confirmo que sou o proprietário dos direitos autorais deste ficheiro, e aceito liberar irrevogavelmente este ficheiro para o Wikimedia Commons nos termos da licença [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Atribuição-CompartilhaIgual 4.0], e concordo com os [https://wikimediafoundation.org/wiki/Terms_of_Use Termos de Utilização].",
-       "foreign-structured-upload-form-label-not-own-work-message-shared": "Se não é o proprietário dos direitos autorais deste ficheiro, ou caso deseje liberá-lo sob uma licença diferente, considere utilizar o [https://commons.wikimedia.org/wiki/Special:UploadWizard Assistente de Envio de Ficheiros do Commons].",
-       "foreign-structured-upload-form-label-not-own-work-local-shared": "Pode querer tentar utilizar [[Special:Upload|a página de carregamento em {{SITENAME}}]], se o sítio aceitar o carregamento deste ficheiro de acordo com suas políticas.",
-       "foreign-structured-upload-form-2-label-intro": "Obrigado por doar uma imagem para utilização em {{SITENAME}}. Deverá continuar apenas se cumprir algumas condições:",
-       "foreign-structured-upload-form-2-label-ownwork": "Deve ser inteiramente <strong>sua obra própria</strong>, não apenas retirada da Internet",
-       "foreign-structured-upload-form-2-label-noderiv": "Não pode conter <strong>nenhuma obra de qualquer outra pessoa</strong>, ou inspirado por elas",
-       "foreign-structured-upload-form-2-label-useful": "Deve ser <strong>educativo e útil</strong> para ensinar a outros",
-       "foreign-structured-upload-form-2-label-ccbysa": "Deve estar <strong>aceito para publicar para sempre</strong> na Internet nos termos da licença [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Atribuição-CompartilhaIgual 4.0]",
-       "foreign-structured-upload-form-2-label-alternative": "Caso nenhum dos itens acima for o correcto, ainda pode ser capaz de carregar este ficheiro ao utilizar o [https://commons.wikimedia.org/wiki/Special:UploadWizard Assistente para Envio de Ficheiros do Commons], desde que esteja disponível sob uma licença livre.",
-       "foreign-structured-upload-form-3-label-yes": "Sim",
-       "foreign-structured-upload-form-3-label-no": "Não",
-       "foreign-structured-upload-form-4-label-bad": "Não pode carregar imagens encontradas num motor de busca ou descarregadas de outros sítios.",
+       "foreign-structured-upload-form-label-not-own-work-local-default": "Pode querer tentar utilizar [[Special:Upload|a página de carregamento em {{SITENAME}}]], se este ficheiro puder ser carregado de acordo com suas as políticas.",
+       "foreign-structured-upload-form-label-own-work-message-shared": "Confirmo que sou o proprietário dos direitos de autor deste ficheiro, e aceito partilhar irrevogavelmente este ficheiro para o Wikimedia Commons nos termos da licença [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Atribuição-CompartilhaIgual 4.0], e concordo com os [https://wikimediafoundation.org/wiki/Terms_of_Use Termos de Utilização].",
+       "foreign-structured-upload-form-label-not-own-work-message-shared": "Se não é o proprietário dos direitos de autor deste ficheiro, ou caso deseje partilhá-lo sob uma licença diferente, considere utilizar o [https://commons.wikimedia.org/wiki/Special:UploadWizard Assistente de Envio de Ficheiros do Commons].",
+       "foreign-structured-upload-form-label-not-own-work-local-shared": "Pode querer tentar utilizar [[Special:Upload|a página de carregamento em {{SITENAME}}]], se o sítio aceitar o carregamento deste ficheiro de acordo com as suas políticas.",
        "backend-fail-stream": "Não foi possível transmitir o ficheiro \"$1\".",
        "backend-fail-backup": "Não foi possível fazer cópia de segurança do ficheiro \"$1\".",
        "backend-fail-notexists": "O ficheiro $1 não existe.",
        "querypage-disabled": "Esta página especial está desativada para não prejudicar o desempenho.",
        "apihelp": "Ajuda API",
        "apihelp-no-such-module": "Módulo \"$1\" não encontrado.",
+       "apisandbox": "Testes da API",
+       "apisandbox-api-disabled": "A API está desativada neste site.",
+       "apisandbox-intro": "Use esta página para fazer experiências com a '''API de <i>web services</i> do MediaWiki'''.\nConsulte a [//www.mediawiki.org/wiki/API:Main_page documentação da API] para informações sobre o uso da API. Exemplo: [//www.mediawiki.org/wiki/API#A_simple_example obter o conteúdo da Página Principal]. Selecione uma operação para ver mais exemplos.\n\nNote que, embora esta seja uma área de testes, as operações que executar nesta página podem modificar a wiki.",
+       "apisandbox-fullscreen": "Expandir painel",
+       "apisandbox-unfullscreen": "Mostrar página",
+       "apisandbox-submit": "Fazer o pedido",
+       "apisandbox-reset": "Limpar",
+       "apisandbox-retry": "Tentar novamente",
+       "apisandbox-examples": "Exemplos",
+       "apisandbox-dynamic-parameters-add-label": "Adicionar parâmetro:",
+       "apisandbox-dynamic-parameters-add-placeholder": "Nome do parâmetro",
+       "apisandbox-dynamic-error-exists": "Um parâmetro com o nome \"$1\" já existe.",
+       "apisandbox-deprecated-parameters": "Parâmetros obsoletos",
+       "apisandbox-submit-invalid-fields-title": "Alguns campos são inválidos",
+       "apisandbox-submit-invalid-fields-message": "Por favor, corrija os campos marcados e tente novamente.",
+       "apisandbox-results": "Resultados",
+       "apisandbox-request-url-label": "URL do pedido:",
+       "apisandbox-request-time": "Tempo de processamento: {{PLURAL:$1|$1 ms}}",
        "booksources": "Fontes bibliográficas",
        "booksources-search-legend": "Pesquisar referências bibliográficas",
        "booksources-search": "Pesquisar",
        "listgrouprights-namespaceprotection-header": "Restrições do domínio",
        "listgrouprights-namespaceprotection-namespace": "Domínio",
        "listgrouprights-namespaceprotection-restrictedto": "Direito(s) do utilizador para editar",
-       "listgrants": "Privilégios",
-       "listgrants-grant": "Privilégio",
+       "listgrants": "Atribuições",
+       "listgrants-summary": "Esta é uma lista de atribuições os respectivos acessos às permissões de utilizador. Os utilizadores podem autorizar aplicações a utilizar suas contas, mas com permissões limitadas baseadas nas atribuições dadas pelos usuários a cada aplicação. No entanto, uma aplicação agindo em nome de um utilizador não tem como utilizar permissões que o utilizador não possui.\nPode haver [[{{MediaWiki:Listgrouprights-helppage}}|informação adicional]] sobre permissões individuais.",
+       "listgrants-grant": "Atribuição",
        "listgrants-rights": "Direitos",
        "trackingcategories": "Categorias de monitorização",
        "trackingcategories-summary": "Esta página lista as categorias monitoradas que foram geradas automaticamente pelo software MediaWiki. Os seus nomes podem ser alterados ao editar sua mensagem correspondente no domínio {{ns:8}}.",
        "import-nonewrevisions": "Nenhuma revisão foi importada (já estavam todas presentes ou foram ignoradas devido a erros).",
        "xml-error-string": "$1 na linha $2, coluna $3 (byte $4): $5",
        "import-upload": "Enviar dados em XML",
-       "import-token-mismatch": "Perda dos dados da sessão. Tente novamente, por favor.",
+       "import-token-mismatch": "Perda de dados da sessão.\n\nA sua sessão poderá ter sido encerrada. <strong>Por favor, verifique se ainda está autenticado e tente novamente</strong>. \nCaso continue a não funcionar, tente [[Special:UserLogout|sair]] e voltar a entrar na sua conta, e verifique se o seu navegador permite a utilização de ''cookies'' deste sítio.",
        "import-invalid-interwiki": "Não é possível importar da wiki especificada.",
        "import-error-edit": "A página \"$1\" não foi importada porque não tem permissão para editá-la.",
        "import-error-create": "A página \"$1\" não foi importada porque não tem permissão para criá-la.",
        "expand_templates_generate_xml": "Mostrar a árvore de análise sintáctica do XML",
        "expand_templates_generate_rawhtml": "Mostrar o HTML puro",
        "expand_templates_preview": "Antevisão do resultado",
-       "expand_templates_preview_fail_html": "<em>Devido ao fato de {{SITENAME}} possuir código HTML puro ativado e de ter havido perda de dados da sessão, a pré-visualização ficará oculta como precaução contra ataques por JavaScript.</em>\n\n<strong>Se esta é uma legítima tentativa de visualização, por favor tente novamente.</strong> Se ainda não funcionar, experimente [[Special:UserLogout|sair]] e iniciar sessão de novo.",
+       "expand_templates_preview_fail_html": "<em>Devido ao fato de {{SITENAME}} possuir código HTML puro ativado e de ter havido perda de dados da sessão, a pré-visualização ficará oculta como precaução contra ataques por JavaScript.</em>\n\n<strong>Se esta é uma legítima tentativa de visualização, por favor tente novamente.</strong>\nCaso continue a não funcionar, tente [[Special:UserLogout|sair]] e voltar a entrar na sua conta, e verifique se o seu navegador permite a utilização de ''cookies'' deste sítio.",
        "expand_templates_preview_fail_html_anon": "<em>Devido ao fato de {{SITENAME}} possuir código HTML puro ativado e de não ter sessão iniciada, a pré-visualização ficará oculta como precaução contra ataques do JavaScript.</em>\n\n<strong>Se esta é uma legítima tentativa de visualização, por favor [[Especial:UserLogin|inicie sessão]] e tente novamente.</strong>",
        "pagelanguage": "Alterar idioma da página",
        "pagelang-name": "Página",
index 30b6c52..405f24b 100644 (file)
                        "J. 'mach' wust",
                        "Ciencia Al Poder",
                        "Aursani",
-                       "Robin van der Vliet"
+                       "Robin van der Vliet",
+                       "Conquistador"
                ]
        },
        "sidebar": "{{notranslate}}",
        "helppage": "{{ignored}}\nThe link destination used by default in the sidebar, and in {{msg-mw|Noarticletext}}.",
        "helppage-top-gethelp": "Link to some MediaWiki.org help page or tutorial.\n{{Identical|Help}}",
        "mainpage": "Defines the link and display name of the main page of the wiki. Shown as the top link in the navigation part of the interface. Please do not change it too often, that could break things!\n\nSee also:\n* {{msg-mw|Mainpage}}\n* {{msg-mw|Accesskey-n-mainpage}}\n* {{msg-mw|Tooltip-n-mainpage}}\n{{Identical|Main page}}",
-       "mainpage-description": "The same as {{msg-mw|mainpage}}, used as link text on [[MediaWiki:Sidebar]].\n\nThis makes it possible to the change the link destination (the message \"mainpage\") without changing the link text or without disabling translations.\n\nSee also:\n* {{msg-mw|Mainpage-description}}\n* {{msg-mw|Accesskey-n-mainpage-description}}\n* {{msg-mw|Tooltip-n-mainpage-description}}\n{{Identical|Main page}}",
+       "mainpage-description": "The same as {{msg-mw|mainpage}}, used as link text on [[MediaWiki:Sidebar]].\n\nThis makes it possible to change the link destination (the message \"mainpage\") without changing the link text or without disabling translations.\n\nSee also:\n* {{msg-mw|Mainpage-description}}\n* {{msg-mw|Accesskey-n-mainpage-description}}\n* {{msg-mw|Tooltip-n-mainpage-description}}\n{{Identical|Main page}}",
        "policy-url": "{{doc-important|Do not change the <code>Project:</code> part.}}\nThe URL of the project page describing the policies of the wiki.\n\nThis is shown below every page (the left link).",
        "portal": "Display name for the 'Community portal', shown in the sidebar menu of all pages. The target page is meant to be a portal for users where useful links are to be found about the wiki's operation.\n\nSee also:\n* {{msg-mw|Portal}}\n* {{msg-mw|Portal-url}}\n* {{msg-mw|Accesskey-n-portal}}\n* {{msg-mw|Tooltip-n-portal}}",
        "portal-url": "{{doc-important|Do not change the <code>Project:</code> part.}}\nDescription: The URL of the community portal. This is shown in the sidebar by default (removed on translatewiki.net).\n\nSee also:\n* {{msg-mw|Portal}}\n* {{msg-mw|Portal-url}}\n* {{msg-mw|Accesskey-n-portal}}\n* {{msg-mw|Tooltip-n-portal}}",
        "readonly_lag": "Error message displayed when the database is locked.",
        "nonwrite-api-promise-error": "Error message displayed when the 'Promise-Non-Write-API-Action' HTTP header is misused.",
        "internalerror": "{{Identical|Internal error}}",
-       "internalerror_info": "Parameters:\n* $1 - error message",
+       "internalerror_info": "Parameters:\n* $1 - error message\n{{Identical|Internal error}}",
        "internalerror-fatal-exception": "Error message displayed by MediaWiki itself when the request failed, inside an error box which also contains a code, a timestamp and a colon before this message.\nParameters:\n* $1 - proper name of the kind of error\n* $2 - alphanumeric code identifying the error in the server logs\n* $3 - URL which resulted in the error\n$2 and $3 are not used by default and only available for wiki customisations, because they are useful for communication to the wiki system administrator.",
        "filecopyerror": "Parameters:\n* $1 - source file name\n* $2 - destination file name",
        "filerenameerror": "Parameters:\n* $1 - old file name\n* $2 - new file name",
        "showpreview": "The text of the button to preview the page you are editing. See also {{msg-mw|showdiff}} and {{msg-mw|savearticle}} for the other buttons.\n\nSee also:\n* {{msg-mw|Showpreview}}\n* {{msg-mw|Accesskey-preview}}\n* {{msg-mw|Tooltip-preview}}\n{{Identical|Show preview}}",
        "showdiff": "Button below the edit page. See also {{msg-mw|Showpreview}} and {{msg-mw|Savearticle}} for the other buttons.\n\nSee also:\n* {{msg-mw|Showdiff}}\n* {{msg-mw|Accesskey-diff}}\n* {{msg-mw|Tooltip-diff}}\n{{Identical|Show change}}",
        "blankarticle": "Notice displayed once after the user tries to save an empty page.",
-       "anoneditwarning": "Shown when editing a page anonymously.\n\nParameters:\n* $1 – A link to log in, <nowiki>{{fullurl:Special:UserLogin|returnto={{FULLPAGENAMEE}}}}</nowiki>\n* $2 – A link to sign up, <nowiki>{{fullurl:Special:UserLogin/signup|returnto={{FULLPAGENAMEE}}}}</nowiki>\n\nSee also:\n* {{msg-mw|mobile-frontend-editor-anoneditwarning}}",
+       "anoneditwarning": "Shown when editing a page anonymously.\n\nParameters:\n* $1 – A link to log in, <nowiki>{{fullurl:Special:UserLogin|returnto={{FULLPAGENAMEE}}}}</nowiki>\n* $2 – A link to sign up, <nowiki>{{fullurl:Special:UserLogin/signup|returnto={{FULLPAGENAMEE}}}}</nowiki>\n\nSee also:\n* {{msg-mw|Mobile-frontend-editor-anonwarning}}",
        "anonpreviewwarning": "See also:\n* {{msg-mw|Anoneditwarning}}",
        "missingsummary": "The text \"edit summary\" is in {{msg-mw|Summary}}.\n\nSee also:\n* {{msg-mw|Missingcommentheader}}\n* {{msg-mw|Savearticle}}",
        "selfredirect": "Notice displayed once after the user tries to create a redirect to the same article.",
        "previewnote": "Note displayed when clicking on Show preview",
        "continue-editing": "{{doc-actionlink}}\nA link to the beginning of the editing textarea on the same page.\n\nDisplayed after {{msg-mw|previewnote}}.",
        "previewconflict": "Used in Preview page.",
-       "session_fail_preview": "Error message in Preview page.\n\nSee also:\n* {{msg-mw|Token suffix mismatch}}\n* {{msg-mw|Session fail preview}}\n* {{msg-mw|Edit form incomplete}}",
-       "session_fail_preview_html": "Used as error message in Preview page.",
+       "session_fail_preview": "Error message in Preview page.\n\nSee also:\n* {{msg-mw|Token suffix mismatch}}\n* {{msg-mw|Session fail preview}}\n* {{msg-mw|Edit form incomplete}}\n\n{{Identical|Loss of session data}}",
+       "session_fail_preview_html": "Used as error message in Preview page.\n\n{{Identical|Loss of session data}}",
        "token_suffix_mismatch": "Error message in Preview page.\n\nSee also:\n* {{msg-mw|Token suffix mismatch}}\n* {{msg-mw|Session fail preview}}\n* {{msg-mw|Edit form incomplete}}",
        "edit_form_incomplete": "Error message in Preview page.\n\nSee also:\n* {{msg-mw|Token suffix mismatch}}\n* {{msg-mw|Session fail preview}}\n* {{msg-mw|Edit form incomplete}}",
        "editing": "Shown as page title when editing a page. Parameters:\n* $1 - the name of the page that is being edited. e.g. \"Editing Main Page\"\n{{Related|Editing}}\n{{Identical|Editing}}",
        "mergehistory-empty": "Used in [[Special:MergeHistory]].",
        "mergehistory-done": "Success message shown on [[Special:MergeHistory]].\n* $1 - link to target page\n* $2 - destination page title\n* $3 - number of revisions which succeeded to merge",
        "mergehistory-fail": "Used as error message in [[Special:MergeHistory]].",
-       "mergehistory-fail-toobig": "Used as error message in [[Special:MergeHistory]].\n* $1 - maximum allowed number of revisions that can be moved",
+       "mergehistory-fail-bad-timestamp": "Used as error message in [[Special:MergeHistory]] API.",
+       "mergehistory-fail-invalid-source": "Used as error message in [[Special:MergeHistory]] API.",
+       "mergehistory-fail-invalid-dest": "Used as error message in [[Special:MergeHistory]] API.",
+       "mergehistory-fail-no-change": "Used as error message in [[Special:MergeHistory]] API.",
+       "mergehistory-fail-permission": "Used as error message in [[Special:MergeHistory]] API.",
+       "mergehistory-fail-self-merge": "Used as error message in [[Special:MergeHistory]] API.",
+       "mergehistory-fail-timestamps-overlap": "Used as error message in [[Special:MergeHistory]] API.",
+       "mergehistory-fail-toobig": "Used as error message in [[Special:MergeHistory]] and API.\n* $1 - maximum allowed number of revisions that can be moved",
+       "mergehistory-warning-redirect-not-created": "Used as warning message in [[Special:MergeHistory]] API.",
        "mergehistory-no-source": "Used as error message in [[Special:MergeHistory]].\n* $1 - source page title\nSee also:\n* {{msg-mw|mergehistory-invalid-source}}\n* {{msg-mw|mergehistory-invalid-destination}}\n* {{msg-mw|mergehistory-no-destination}}\n* {{msg-mw|mergehistory-same-destination}}",
        "mergehistory-no-destination": "Used as error message in [[Special:MergeHistory]].\n* $1 - destination page title\nSee also:\n* {{msg-mw|mergehistory-invalid-source}}\n* {{msg-mw|mergehistory-no-source}}\n* {{msg-mw|mergehistory-invalid-destination}}\n* {{msg-mw|mergehistory-same-destination}}",
        "mergehistory-invalid-source": "Used as error message in [[Special:MergeHistory]].\n\nSee also:\n* {{msg-mw|mergehistory-no-source}}\n* {{msg-mw|mergehistory-invalid-destination}}\n* {{msg-mw|mergehistory-no-destination}}\n* {{msg-mw|mergehistory-same-destination}}",
        "mergehistory-same-destination": "Error message shown on [[Special:MergeHistory]] when the user entered the same page title to both source and destination\n\nSee also:\n* {{msg-mw|mergehistory-invalid-source}}\n* {{msg-mw|mergehistory-no-source}}\n* {{msg-mw|mergehistory-invalid-destination}}\n* {{msg-mw|mergehistory-no-destination}}",
        "mergehistory-reason": "{{Identical|Reason}}",
        "mergehistory-revisionrow": "{{Optional}}\nA revision row in the merge history page. Parameters:\n* $1 - a radio button to indicate a merge point\n* $2 - a link to the last revision of a page ({{msg-mw|Last}})\n* $3 - a page link\n* $4 - a user link\n* $5 - a revision size\n* $6 - a revision comment",
+       "mergehistory-redirect-text": "{{ignored}}The text that's added to a redirected page when that redirect is created as part of a history merge.",
        "mergelog": "{{doc-logpage}}\n\nThis is the name of a log of merge actions done on [[Special:MergeHistory]]. This special page and this log is not enabled by default.",
        "pagemerge-logentry": "{{ignored}}This is a ''logentry'' message only used on IRC.\n\nParameters:\n* $1 - the page name of the source of the content to be merged\n* $2 - the page into which the content is merged\n* $3 - a timestamp of limit\n\nThe log and its associated special page 'MergeHistory' is not enabled by default.\n\nPlease note that the parameters in a log entry will appear in the log only in the default language of the wiki. View [[Special:Log]] for examples on translatewiki.net with English default language.",
        "revertmerge": "Used as link text",
        "upload-dialog-button-done": "Button to close the dialog once upload is complete\n{{Identical|Done}}",
        "upload-dialog-button-save": "Button to save the file after upload finishes and metadata is filled out, part 2 of a multi-step upload form\n{{Identical|Save}}",
        "upload-dialog-button-upload": "Button to initiate upload, part 1 of a multi-step upload form\n{{Identical|Upload}}",
-       "upload-form-label-select-file": "Label for the select file widget\n{{Identical|Select file}}",
        "upload-form-label-infoform-title": "Title for the information form\n{{Identical|Detail}}",
        "upload-form-label-infoform-name": "Label for the file name input\n{{Identical|Name}}",
        "upload-form-label-infoform-name-tooltip": "The tooltip documenting the title field for the file - used as the filename on-wiki.\n\nIdentical to {{msg-mw|mwe-upwiz-tooltip-title}}.",
        "foreign-structured-upload-form-label-own-work-message-default": "Message shown by default when a user affirms that they are allowed to upload a file to a remote wiki.",
        "foreign-structured-upload-form-label-not-own-work-message-default": "Message shown by default when a user cannot upload a file to a remote wiki.",
        "foreign-structured-upload-form-label-not-own-work-local-default": "Suggests uploading a file locally instead of to a remote wiki.",
-       "foreign-structured-upload-form-label-own-work-message-shared": "[[File:Cross-wiki media upload dialog, December 2015 AB test option 1.png|thumb]] Legal message, confirming that the user is allowed to upload the file. Almost identical to {{msg-mw|foreign-structured-upload-form-2-label-termsofuse}}.",
+       "foreign-structured-upload-form-label-own-work-message-shared": "[[File:Cross-wiki media upload dialog, December 2015 AB test option 1.png|thumb]] Legal message, confirming that the user is allowed to upload the file.",
        "foreign-structured-upload-form-label-not-own-work-message-shared": "[[File:Cross-wiki media upload dialog, December 2015 AB test option 1.png|thumb]] Explains alternatives when the copyright isn't owned by the uploader.",
        "foreign-structured-upload-form-label-not-own-work-local-shared": "[[File:Cross-wiki media upload dialog, December 2015 AB test option 1.png|thumb]] Message suggesting the user might want to upload a file locally instead of to Wikimedia Commons.",
-       "foreign-structured-upload-form-2-label-intro": "[[File:Cross-wiki media upload dialog, December 2015 AB test option 2.png|thumb]] Introductory text in cross-wiki upload dialog.",
-       "foreign-structured-upload-form-2-label-ownwork": "[[File:Cross-wiki media upload dialog, December 2015 AB test option 2.png|thumb]] Affirmative statement, used as checkbox label. The user must tick it to continue.",
-       "foreign-structured-upload-form-2-label-noderiv": "[[File:Cross-wiki media upload dialog, December 2015 AB test option 2.png|thumb]] Affirmative statement, used as checkbox label. The user must tick it to continue.",
-       "foreign-structured-upload-form-2-label-useful": "[[File:Cross-wiki media upload dialog, December 2015 AB test option 2.png|thumb]] Affirmative statement, used as checkbox label. The user must tick it to continue.",
-       "foreign-structured-upload-form-2-label-ccbysa": "[[File:Cross-wiki media upload dialog, December 2015 AB test option 2.png|thumb]] Affirmative statement, used as checkbox label. The user must tick it to continue.",
-       "foreign-structured-upload-form-2-label-alternative": "[[File:Cross-wiki media upload dialog, December 2015 AB test option 2.png|thumb]] Explains alternatives when the copyright isn't owned by the uploader.\n\nSimilar to {{msg-mw|foreign-structured-upload-form-3-label-alternative}}.",
-       "foreign-structured-upload-form-2-label-termsofuse": "[[File:Cross-wiki media upload dialog, December 2015 AB test option 2.png|thumb]] Legal message, confirming that the user is allowed to upload the file. Almost identical to {{msg-mw|foreign-structured-upload-form-label-own-work-message-shared}}.",
-       "foreign-structured-upload-form-3-label-question-website": "[[File:Cross-wiki media upload dialog, December 2015 AB test option 3.png|thumb]] Question to the user, with Yes/No answer ({{msg-mw|foreign-structured-upload-form-3-label-yes}} / {{msg-mw|foreign-structured-upload-form-3-label-no}}). The answer determines whether the user will be able to continue.",
-       "foreign-structured-upload-form-3-label-question-ownwork": "[[File:Cross-wiki media upload dialog, December 2015 AB test option 3.png|thumb]] Question to the user, with Yes/No answer ({{msg-mw|foreign-structured-upload-form-3-label-yes}} / {{msg-mw|foreign-structured-upload-form-3-label-no}}). The answer determines whether the user will be able to continue.",
-       "foreign-structured-upload-form-3-label-question-noderiv": "[[File:Cross-wiki media upload dialog, December 2015 AB test option 3.png|thumb]] Question to the user, with Yes/No answer ({{msg-mw|foreign-structured-upload-form-3-label-yes}} / {{msg-mw|foreign-structured-upload-form-3-label-no}}). The answer determines whether the user will be able to continue.",
-       "foreign-structured-upload-form-3-label-yes": "[[File:Cross-wiki media upload dialog, December 2015 AB test option 3.png|thumb]] {{Identical|Yes}}",
-       "foreign-structured-upload-form-3-label-no": "[[File:Cross-wiki media upload dialog, December 2015 AB test option 3.png|thumb]] {{Identical|No}}",
-       "foreign-structured-upload-form-3-label-alternative": "[[File:Cross-wiki media upload dialog, December 2015 AB test option 3.png|thumb]] Explains alternatives when the copyright isn't owned by the uploader.\n\nSimilar to {{msg-mw|foreign-structured-upload-form-2-label-alternative}}.",
-       "foreign-structured-upload-form-4-label-good": "[[File:Cross-wiki media upload dialog, December 2015 AB test option 4.png|thumb]] Gives examples of good content that is welcome. There is limited space for this text in the interface, please keep it short.",
-       "foreign-structured-upload-form-4-label-bad": "[[File:Cross-wiki media upload dialog, December 2015 AB test option 4.png|thumb]] Gives examples of bad content that is unacceptable. There is limited space for this text in the interface, please keep it short.",
        "backend-fail-stream": "Parameters:\n* $1 - a filename",
        "backend-fail-backup": "Parameters:\n* $1 - a filename",
        "backend-fail-notexists": "Parameters:\n* $1 - a filename",
        "withoutinterwiki-submit": "{{Identical|Show}}",
        "fewestrevisions": "{{doc-special|FewestRevisions}}",
        "fewestrevisions-summary": "{{doc-specialpagesummary|fewestrevisions}}",
-       "nbytes": "Message used on the history page of a wiki page. Each version of a page consist of a number of bytes. $1 is the number of bytes that the page uses. Uses plural as configured for a language based on $1.",
+       "nbytes": "Message used on the history page of a wiki page. Each version of a page consist of a number of bytes. $1 is the number of bytes that the page uses. Uses plural as configured for a language based on $1.\n{{Identical|Byte}}",
        "ncategories": "Used in the special page '[[Special:MostCategories]]' in brackets after each entry on the list signifying how many categories a page is part of. $1 is the number of categories.",
        "ninterwikis": "Used in the special page '[[Special:MostInterwikis]]' in brackets after each entry on the list signifying how many interwikis a page is part of.\n\nParameters:\n* $1 - the number of interwiki links",
        "nlinks": "This appears in brackets after each entry on the special page [[Special:MostLinked]]. $1 is the number of wiki links.",
        "apihelp-summary": "{{doc-specialpagesummary|ApiHelp}}",
        "apihelp-no-such-module": "Used as an error message if the requested API module is not found.\n\nParameters:\n* $1 - Requested module name",
        "apihelp-link": "{{notranslate}} Used to construct a link to [[Special:ApiHelp]]\n\nParameters:\n* $1 - module to link\n* $2 - link text",
+       "apisandbox": "{{doc-special|ApiSandbox}}",
+       "apisandbox-summary": "{{ignored}}\n{{doc-specialpagesummary|ApiSandbox}}",
+       "apisandbox-jsonly": "Displayed as an error message if the browser does not have JavaScript enabled.",
+       "apisandbox-api-disabled": "Displayed as an error message if the API is disabled on this site.",
+       "apisandbox-intro": "Displayed (from JavaScript) as a header on [[Special:ApiSandbox]].",
+       "apisandbox-fullscreen": "JavaScript button label for enabling full-page mode.",
+       "apisandbox-fullscreen-tooltip": "Tooltip for the {{msg-mw|apisandbox-fullscreen}} button.",
+       "apisandbox-unfullscreen": "JavaScript button label for disabling full-page mode.",
+       "apisandbox-unfullscreen-tooltip": "Tooltip for the {{msg-mw|apisandbox-unfullscreen}} button.",
+       "apisandbox-submit": "JavaScript button label for submitting the request.",
+       "apisandbox-reset": "JavaScript button label for clearing the form.\n{{Identical|Clear}}",
+       "apisandbox-retry": "JavaScript button label for retrying the submission.\n{{Identical|Retry}}",
+       "apisandbox-loading": "JavaScript message displayed while data is loading.\n\nParameters:\n* $1 - Module being loaded",
+       "apisandbox-load-error": "Displayed as an error message from JavaScript when data failed to load.\n\nParameters:\n* $1 - Module being loaded\n* $2 - Error message from the API",
+       "apisandbox-no-parameters": "Displayed (from JavaScript) when the loaded API module has no parameters.",
+       "apisandbox-helpurls": "JavaScript button label for showing help URLs.",
+       "apisandbox-examples": "JavaScript button label for showing example queries.\n{{Identical|Example}}",
+       "apisandbox-dynamic-parameters": "JavaScript fieldset legend for the section containing the widgets to add arbitrary parameters to a module that can accept dynamic parameters.",
+       "apisandbox-dynamic-parameters-add-label": "JavaScript label for the widget to add a new arbitrary parameter.",
+       "apisandbox-dynamic-parameters-add-placeholder": "JavaScript text field placeholder for the widget to add a new arbitrary parameter.",
+       "apisandbox-dynamic-error-exists": "Displayed as an error message from JavaScript when trying to add a new arbitrary parameter with a name that already exists. Parameters:\n* $1 - Parameter name that failed.",
+       "apisandbox-deprecated-parameters": "JavaScript button label and fieldset legend for separating deprecated parameters in the UI.",
+       "apisandbox-fetch-token": "Tooltop for the button that fetches a CSRF token.",
+       "apisandbox-submit-invalid-fields-title": "Title for a JavaScript error message when fields are invalid.",
+       "apisandbox-submit-invalid-fields-message": "Content for a JavaScript error message when fields are invalid.",
+       "apisandbox-results": "JavaScript tab label for the tab displaying the API query results.\n{{Identical|Result}}",
+       "apisandbox-sending-request": "JavaScript message displayed while the request is being sent.",
+       "apisandbox-loading-results": "JavaScript message displayed while the response is being read.",
+       "apisandbox-results-error": "Displayed as an error message from JavaScript when the request failed.\n\nParameters:\n* $1 - Error message",
+       "apisandbox-request-url-label": "Label for the text field displaying the URL used to make this request.",
+       "apisandbox-request-time": "Label and value for displaying the time taken by the request.\n\nParameters:\n* $1 - Time taken in milliseconds",
+       "apisandbox-results-fixtoken": "JavaScript button label",
+       "apisandbox-results-fixtoken-fail": "Displayed as an error message from JavaScript when a CSRF token could not be fetched.\n\nParameters:\n* $1 - Token type",
+       "apisandbox-alert-page": "Tooltip for the alert icon on a module's page tab when the page contains fields with issues.",
+       "apisandbox-alert-field": "Tooltip for the alert icon on a field when the field has issues.",
        "booksources": "{{doc-special|BookSources}}\n\n'''This message shouldn't be changed unless it has serious mistakes.'''\n\nIt's used as the page name of the configuration page of [[Special:BookSources]]. Changing it breaks existing sites using the default version of this message.\n\nSee also:\n* {{msg-mw|Booksources|title}}\n* {{msg-mw|Booksources-text|text}}",
        "booksources-summary": "{{doc-specialpagesummary|booksources}}",
        "booksources-search-legend": "Box heading on [[Special:BookSources|book sources]] special page. The box is for searching for places where a particular book can be bought or viewed.",
        "undelete-error-long": "Used as error message. Parameters:\n* $1 - ...\nSee also:\n* {{msg-mw|Undelete-error-short}}",
        "undelete-show-file-confirm": "A confirmation message shown on [[Special:Undelete]] when the request does not contain a valid token (e.g. when a user clicks a link received in mail).\n\nParameters:\n* $1 - the name of the file being undeleted\n* $2 - the date of the displayed revision\n* $3 - the time of the displayed revision\n{{Identical|Are you sure you want to view the deleted revision of the file...}}",
        "undelete-show-file-submit": "{{Identical|Yes}}",
-       "undelete-revision-row": "{{Optional}}\nA revision row in the undelete page. Parameters:\n* $1 is a checkBox to indicate whether to restore this specific revision\n* $2 is a link to the revision\n* $3 is a link to the last revision of a page ({{msg-mw|last}})\n* $4 is a link to the page\n* $5 is a link to the revision's user\n* $6 is the revision's minor edit identifier\n* $7 is the revision size\n* $8 is the revision comment\n* $9 is the revision's tags",
+       "undelete-revision-row2": "{{Optional}}\nA revision row in the undelete page. Parameters:\n* $1 is a checkBox to indicate whether to restore this specific revision\n* $2 is a link to the last revision of a page ({{msg-mw|last}})\n* $3 is a link to the page\n* $4 is a link to the revision's user\n* $5 is the revision's minor edit identifier\n* $6 is the revision size\n* $7 is the revision comment\n* $8 is the revision's tags",
        "namespace": "This message is located at [[Special:Contributions]].\n{{Identical|Namespace}}",
        "invert": "Displayed in [[Special:RecentChanges|RecentChanges]], [[Special:RecentChangesLinked|RecentChangesLinked]] and [[Special:Watchlist|Watchlist]].\n\nThis message means \"Invert selection of namespace\".\n\nThis message has a tooltip {{msg-mw|tooltip-invert}}\n{{Identical|Invert selection}}",
        "tooltip-invert": "Used in [[Special:Recentchanges]] as a tooltip for the invert checkbox. See also the message {{msg-mw|invert}}",
        "import-nonewrevisions": "Used in [[Special:Import]].",
        "xml-error-string": "Parameters:\n* $1 - Some kind of message, perhaps name of the error?\n* $2 - line number\n* $3 - column number\n* $4 - ?? $this->mByte . $this->mContext\n* $5 - error description\nExample:\n* Import failed: XML import parse failure at line 1, col 1 (byte 3; \"- <mediawiki xml\"): Empty document",
        "import-upload": "Used on [[Special:Import]].\n\nRelated messages:\n* {{msg-mw|right-importupload}} (the user right for this)",
-       "import-token-mismatch": "Used as error message in [[Special:Import]].\n\nSee also:\n* {{msg-mw|import-token-mismatch}}\n* {{msg-mw|import-invalid-interwiki}}\n* {{msg-mw|Importunknownsource}}",
+       "import-token-mismatch": "Used as error message in [[Special:Import]].\n\nSee also:\n* {{msg-mw|import-token-mismatch}}\n* {{msg-mw|import-invalid-interwiki}}\n* {{msg-mw|Importunknownsource}}\n\n{{Identical|Loss of session data}}",
        "import-invalid-interwiki": "Used as error message in [[Special:Import]].\n\nSee also:\n* {{msg-mw|import-token-mismatch}}\n* {{msg-mw|import-invalid-interwiki}}\n* {{msg-mw|Importunknownsource}}",
        "import-error-edit": "Import error message displayed when importing user has no edit rights for a page.\n\nParameters:\n* $1 - a page name\n{{Related|Import-error}}",
        "import-error-create": "Import error message displayed when importing user has no create rights for a page.\n\nParameters:\n* $1 - a page name\n{{Related|Import-error}}",
        "expand_templates_generate_xml": "Used as checkbox label.",
        "expand_templates_generate_rawhtml": "Used as checkbox label.",
        "expand_templates_preview": "{{Identical|Preview}}",
-       "expand_templates_preview_fail_html": "Used as error message in Preview section of [[Special:ExpandTemplates]] page.",
+       "expand_templates_preview_fail_html": "Used as error message in Preview section of [[Special:ExpandTemplates]] page.\n\n{{Identical|Loss of session data}}",
        "expand_templates_preview_fail_html_anon": "Used as error message in Preview section of [[Special:ExpandTemplates]] page.",
        "expand_templates_input_missing": "Used on [[Special:ExpandTemplates]] as an error message, if no input text was provided.",
        "pagelanguage": "Title for page Special:PageLanguage",
index fd35f68..21c998e 100644 (file)
        "querypage-disabled": "Această pagină specială este dezactivată din motive de performanță.",
        "apihelp": "Ajutor API",
        "apihelp-no-such-module": "Modulul „$1” nu a fost găsit.",
+       "apisandbox": "Cutia cu nisip pentru API",
+       "apisandbox-api-disabled": "API este dezactivat pe acest site.",
+       "apisandbox-submit": "Efectuați cererea",
+       "apisandbox-reset": "Curăță",
+       "apisandbox-examples": "Exemplu",
+       "apisandbox-results": "Rezultat",
+       "apisandbox-request-url-label": "URL cerere:",
+       "apisandbox-request-time": "Durata cererii: $1",
        "booksources": "Surse de cărți",
        "booksources-search-legend": "Căutare surse pentru cărți",
        "booksources-search": "Caută",
index 79fa244..1dbfd5f 100644 (file)
        "querypage-disabled": "Sta pàgena speciale jè desabbilitate pe mutive de prestaziune.",
        "apihelp": "Aijute de l'API",
        "apihelp-no-such-module": "Module \"$1\" none acchiate.",
+       "apisandbox": "Sandbox de l'API",
+       "apisandbox-api-disabled": "API non g'è abbiletate sus a stu site.",
+       "apisandbox-intro": "Ause sta pàgene pe sperimendà cu le '''API de le web service pe MediaUicchi'''.\nFà referimende a [//www.mediawiki.org/wiki/API:Main_page 'a documendazione de l'API] pe cchiù dettaglie de l'ause de l'API.\nEsembie: [//www.mediawiki.org/wiki/API#A_simple_example pigghie 'u condenute d'a Pàgene Prengepàle]. Scacchie 'n'azione pe 'ndrucà otre esembie.\n\nVide ca, pure ca queste jè 'na buatte de sabbie tu puè carrescià le cangiaminde de sta pàgene sus 'a uicchi.",
+       "apisandbox-submit": "Fà 'na richieste",
+       "apisandbox-reset": "Pulizze",
+       "apisandbox-examples": "Esembie",
+       "apisandbox-results": "Resultate",
+       "apisandbox-request-url-label": "URL richieste:",
+       "apisandbox-request-time": "Tiembe cercate: $1",
        "booksources": "Sorgende de le libbre",
        "booksources-search-legend": "Cirche pe le fonde de le libbre",
        "booksources-isbn": "ISBN:",
index 570efb3..cb3eb69 100644 (file)
@@ -99,7 +99,7 @@
        "tog-extendwatchlist": "Расширенный список наблюдения, включающий все изменения, а не только последние",
        "tog-usenewrc": "Группировать изменения в свежих правках и списке наблюдения",
        "tog-numberheadings": "Автоматически нумеровать заголовки",
-       "tog-showtoolbar": "Ð\9fоказÑ\8bваÑ\82Ñ\8c Ð²ÐµÑ\80Ñ\85нÑ\8eÑ\8e Ð¿Ð°Ð½ÐµÐ»Ñ\8c Ð¸Ð½Ñ\81Ñ\82Ñ\80Ñ\83менÑ\82ов Ð¿Ñ\80и Ñ\80едакÑ\82иÑ\80овании",
+       "tog-showtoolbar": "Показывать панель инструментов при редактировании",
        "tog-editondblclick": "Править страницы по двойному щелчку",
        "tog-editsectiononrightclick": "Править секции при правом щелчке мышью на заголовке",
        "tog-watchcreations": "Добавлять в список наблюдения созданные мной страницы и загруженные мной файлы",
        "previewnote": "'''Помните, что это только предварительный просмотр.'''\nВаши изменения ещё не были сохранены!",
        "continue-editing": "Продолжить редактирование",
        "previewconflict": "Этот предварительный просмотр отражает текст в верхнем окне редактирования так, как он будет выглядеть, если вы решите записать его.",
-       "session_fail_preview": "'''К сожалению, сервер не смог обработать вашу правку из-за потери идентификатора сессии.\nПожалуйста, попробуйте ещё раз.\nЕсли эта ошибка повторится, попробуйте [[Special:UserLogout|завершить сеанс]] и заново представиться системе.'''",
-       "session_fail_preview_html": "'''К сожалению, сервер не смог обработать вашу правку из-за потери данных сессии.'''\n\n''Так как {{SITENAME}} разрешает использовать чистый HTML, предварительный просмотр отключён в качестве меры предотвращения JavaScript-атак.''\n\n'''Если это добросовестная попытка редактирования, пожалуйста, попробуйте ещё раз.\nЕсли не получается повторная правка, попробуйте [[Special:UserLogout|завершить сеанс]] работы и заново представиться.'''",
+       "session_fail_preview": "К сожалению, мы не смогли обработать вашу правку из-за потери данных сессии.\n\nВозможно, вы завершили сеанс работы. <strong>Пожалуйста, убедитесь, что вы всё ещё авторизованы, и попробуйте снова.</strong>\nЕсли это всё равно не помогло, попробуйте [[Special:UserLogout|выйти из системы]] и войти заново, а также проверьте, что ваш браузер позволяет принимать cookies с этого сайта.",
+       "session_fail_preview_html": "К сожалению, сервер не смог обработать вашу правку из-за потери данных сессии.\n\n<em>Так как {{SITENAME}} разрешает использовать чистый HTML, предварительный просмотр отключён в качестве меры предотвращения JavaScript-атак.</em>\n\n<strong>Если это добросовестная попытка редактирования, пожалуйста, попробуйте ещё раз.</strong>\nЕсли не получается повторная правка, попробуйте [[Special:UserLogout|завершить сеанс]] работы, заново представиться и проверить, что ваш браузер разрешает использовать cookies на этом сайте.",
        "token_suffix_mismatch": "'''Ваша правка была отклонена, так как ваша программа неправильно обрабатывает знаки пунктуации\nв окне редактирования. Правка была отменена для предотвращения искажения текста статьи.\nПодобные проблемы могут возникать при использовании анонимизирующих веб-прокси, содержащих ошибки.'''",
        "edit_form_incomplete": "'''Некоторые части формы редактирования не достигли сервера. Внимательно проверьте, что ваши правки не повреждены, и попробуйте ещё раз.'''",
        "editing": "Редактирование: $1",
        "mergehistory-empty": "Не найдены правки для объединения.",
        "mergehistory-done": "$3 {{PLURAL:$3|правка|правки|правок}} из $1 {{PLURAL:$3|была перенесена|были перенесены}} в [[:$2]].",
        "mergehistory-fail": "Не удалось произвести объединение историй страниц, пожалуйста, проверьте параметры страницы и времени.",
+       "mergehistory-fail-bad-timestamp": "Метка времени неверна.",
+       "mergehistory-fail-invalid-source": "Страница-источник неверна.",
+       "mergehistory-fail-invalid-dest": "Целевая страница неверна.",
+       "mergehistory-fail-no-change": "В процесс слияния истории не произошло слияние никаких версий. Пожалуйста, перепроверьте страницу и временные параметры.",
+       "mergehistory-fail-permission": "Недостаточно прав для выполнения объединения истории.",
+       "mergehistory-fail-self-merge": "Исходная и целевая страницы совпадают.",
+       "mergehistory-fail-timestamps-overlap": "Исходные версии перекрывают или идут после версий назначения.",
        "mergehistory-fail-toobig": "Не удаётся выполнить слияние истории, так как необходимо перенести больше допустимого лимита в $1 {{PLURAL:$1|версию|версии|версий}}.",
        "mergehistory-no-source": "Исходная страница «$1» не существует.",
        "mergehistory-no-destination": "Целевая страница «$1» не существует.",
        "uploaded-script-svg": "Найден небезопасный элемент с поддержкой сценариев «$1» в загруженном SVG-файле.",
        "uploaded-hostile-svg": "Найден небезопасный CSS-код в элементе стиля загруженного SVG-файла.",
        "uploaded-event-handler-on-svg": "Установка атрибутов обработчика событий <code>$1=\"$2\"</code> не разрешено для SVG-файлов.",
-       "uploaded-href-unsafe-target-svg": "В загруженном SVG-файле найдена ссылка на небезопасную цель <code>&lt;$1 $2=\"$3\"&gt;</code>.",
+       "uploaded-href-attribute-svg": "В SVG-файлах href-атрибуты для ссылки, найденной в <code><$1 $2=\"$3\"></code>, разрешены только цели, начинающиеся на http:// или https://.",
+       "uploaded-href-unsafe-target-svg": "В загруженном SVG-файле найдены небезопасные данные: URI <code>&lt;$1 $2=\"$3\"&gt;</code>.",
        "uploaded-animate-svg": "Найден тег «animate», который может изменять ссылку с помощью «from»-атрибута <code>&lt;$1 $2=\"$3\"&gt;</code> в загруженном SVG-файле.",
        "uploaded-setting-event-handler-svg": "Установка атрибутов обработчика событий заблокирована, в загруженном SVG-файле найден код <code>&lt;$1 $2=\"$3\"&gt;</code>.",
        "uploaded-setting-href-svg": "Использование тега «set» для добавления атрибута «href» в родительский элемент заблокировано.",
        "upload-dialog-button-done": "Готово",
        "upload-dialog-button-save": "Сохранить",
        "upload-dialog-button-upload": "Загрузить",
-       "upload-form-label-select-file": "Выбрать файл",
        "upload-form-label-infoform-title": "Подробности",
        "upload-form-label-infoform-name": "Имя",
        "upload-form-label-infoform-name-tooltip": "Уникальный описательный заголовок для файла, который будет сохранён как его название. Можете использовать простой язык и пробелы. Не указывайте расширение.",
        "foreign-structured-upload-form-label-own-work-message-shared": "Я подтверждаю, что являюсь владельцем авторских прав на этот файл, и соглашаюсь на безотзывной основе разместить этот файл на Викискладе под лицензией [https://creativecommons.org/licenses/by-sa/4.0/deed.ru Creative Commons Attribution-ShareAlike 4.0], а также соглашаюсь с [https://wikimediafoundation.org/wiki/Условия_использования Условиями использования].",
        "foreign-structured-upload-form-label-not-own-work-message-shared": "Если вы не являетесь владельцем авторских прав на этот файл, или вы хотите выпустить его под другой лицензией, рассмотрите возможность использования [https://commons.wikimedia.org/wiki/Special:UploadWizard Мастера загрузки на Викисладе].",
        "foreign-structured-upload-form-label-not-own-work-local-shared": "В том случае, если этот файл может быть загружен в соответствии с правилами сайта {{SITENAME}}, вы также можете попробовать использовать его [[Special:Upload|страницу загрузки]].",
-       "foreign-structured-upload-form-2-label-intro": "Благодарим Вас за пожертвование изображения, которое будет использовано на сайте {{SITENAME}}. Вам следует продолжить, только если оно отвечает ряду условий:",
-       "foreign-structured-upload-form-2-label-ownwork": "Оно должно быть исключительно <strong>вашей работой</strong>, а не просто картинкой, скачанной из Интернета",
-       "foreign-structured-upload-form-2-label-noderiv": "Оно не должно <strong>содержать чьей-то чужой работы</strong> или быть вдохновлено ей",
-       "foreign-structured-upload-form-2-label-useful": "Оно должно быть <strong>образовательным и полезным</strong> для обучения других",
-       "foreign-structured-upload-form-2-label-ccbysa": "Вы должны быть согласны на то, чтобы <strong>опубликовать его в Интернете навсегда</strong> под лицензией [https://creativecommons.org/licenses/by-sa/4.0/deed.ru Creative Commons Attribution-ShareAlike 4.0]",
-       "foreign-structured-upload-form-2-label-alternative": "Если не всё вышеперечисленное верно, вы все равно можете загрузить этот файл, используя кнопку [https://commons.wikimedia.org/wiki/Special:UploadWizard Мастера загрузки Викисклада], в том случае, если он доступен под свободной лицензией.",
-       "foreign-structured-upload-form-2-label-termsofuse": "Загружая данный файл, вы подтверждаете, что являетесь владельцем авторских прав на этот файл и безоговорочно согласны загрузить его на Викисклад под лицензией Creative Commons Attribution-ShareAlike 4.0, а также соглашаетесь с [https://wikimediafoundation.org/wiki/Условия_использования Условиями использования].",
-       "foreign-structured-upload-form-3-label-question-website": "Вы скачали это изображение с какого-то сайта или, может быть, нашли его через поиск изображений?",
-       "foreign-structured-upload-form-3-label-question-ownwork": "Вы создали это изображение (сделали фото, эскиз, чертёж и т. д.) сами?",
-       "foreign-structured-upload-form-3-label-question-noderiv": "Содержит ли он работу, принадлежащую кому-то другому (или вдохновлён ей), например, логотип?",
-       "foreign-structured-upload-form-3-label-yes": "Да",
-       "foreign-structured-upload-form-3-label-no": "Нет",
-       "foreign-structured-upload-form-3-label-alternative": "К сожалению, в данном случае, этот инструмент не поддерживает загрузку данного файла. Вы все равно можете загрузить этот файл, используя [https://commons.wikimedia.org/wiki/Special:UploadWizard Мастер загрузки Викисклада], в том случае, если он доступен под свободной лицензией.",
-       "foreign-structured-upload-form-4-label-good": "Используя этот инструмент, вы можете загрузить образовательную графику, которую вы создали, и фотографии, которые вы сняли, если они не содержат работ, принадлежащих кому-то другому.",
-       "foreign-structured-upload-form-4-label-bad": "Вы не можете загружать изображения, найденные в поисковой системе или скачанные с других сайтов.",
        "backend-fail-stream": "Не удалось транслировать файл $1.",
        "backend-fail-backup": "Невозможно сделать резервную копию файла $1.",
        "backend-fail-notexists": "Файл $1 не существует.",
        "querypage-disabled": "Эта спецстраница отключена для повышения производительности.",
        "apihelp": "Справка по API",
        "apihelp-no-such-module": "Модуль «$1» не найден.",
+       "apisandbox": "Песочница API",
+       "apisandbox-jsonly": "Для использования API-песочницы требуется JavaScript.",
+       "apisandbox-api-disabled": "API отключен на этом сайте.",
+       "apisandbox-intro": "Используйте эту страницу для экспериментов с <strong>MediaWiki API</strong>.\nОбратитесь к [[mw:API:Main page|документации API]] для получения дополнительной информации об использовании API. Например, о том, [//www.mediawiki.org/wiki/API#A_simple_example как получить содержание Заглавной страницы]. Выберите действие, чтобы увидеть другие примеры.\nОбратите внимание, что, хотя это и песочница, действия, выполненные на этой странице, могут внести изменения в вики.",
+       "apisandbox-fullscreen": "Развернуть панель",
+       "apisandbox-fullscreen-tooltip": "Развернуть панель песочницы, чтобы заполнить окно браузера.",
+       "apisandbox-unfullscreen": "Показать страницу",
+       "apisandbox-unfullscreen-tooltip": "Уменьшить панель песочницы, чтоб стали доступны навигационные ссылки MediaWiki.",
+       "apisandbox-submit": "Сделать запрос",
+       "apisandbox-reset": "Очистить",
+       "apisandbox-retry": "Повторить",
+       "apisandbox-loading": "Загрузка информации для API-модуля «$1»…",
+       "apisandbox-load-error": "Произошла ошибка при загрузке информации для API-модуля «$1»: $2",
+       "apisandbox-no-parameters": "У этого API-модуля нет параметров.",
+       "apisandbox-helpurls": "Ссылки на справочные материалы",
+       "apisandbox-examples": "Примеры",
+       "apisandbox-dynamic-parameters": "Дополнительные параметры",
+       "apisandbox-dynamic-parameters-add-label": "Добавить параметр:",
+       "apisandbox-dynamic-parameters-add-placeholder": "Имя параметра",
+       "apisandbox-dynamic-error-exists": "Параметр с именем «$1» уже существует.",
+       "apisandbox-deprecated-parameters": "Устаревшие параметры",
+       "apisandbox-fetch-token": "Автозаполнение токена",
+       "apisandbox-submit-invalid-fields-title": "Некоторые поля некорректны",
+       "apisandbox-submit-invalid-fields-message": "Пожалуйста, исправьте отмеченные поля и попробуйте снова.",
+       "apisandbox-results": "Результаты",
+       "apisandbox-sending-request": "Отправка API-запроса…",
+       "apisandbox-loading-results": "Получение API-результатов…",
+       "apisandbox-results-error": "Произошла ошибка при загрузке API-ответа на запрос: $1.",
+       "apisandbox-request-url-label": "URL-адрес запроса:",
+       "apisandbox-request-time": "Время запроса: {{PLURAL:$1|$1 мс}}",
+       "apisandbox-results-fixtoken": "Исправьте токен и повторите отправку",
+       "apisandbox-results-fixtoken-fail": "Не удалось вызвать токен «$1».",
+       "apisandbox-alert-page": "Поля на этой странице некорректны.",
+       "apisandbox-alert-field": "Значение этого поля является недопустимым.",
        "booksources": "Источники книг",
        "booksources-search-legend": "Поиск информации о книге",
        "booksources-isbn": "ISBN:",
        "import-nonewrevisions": "Никакие правки не были импортированы (все уже либо были обработаны, либо пропущены из-за ошибок).",
        "xml-error-string": "$1 в строке $2, позиции $3 (байт $4): $5",
        "import-upload": "Загрузить XML-данные",
-       "import-token-mismatch": "Потеряны данные сеанса. Пожалуйста, попробуйте ещё раз.",
+       "import-token-mismatch": "Потеряны данные сеанса.\n\nВозможно, вы завершили сеанс работы. <strong>Пожалуйста, убедитесь, что вы всё ещё авторизованы, и попробуйте снова.</strong>\nЕсли это всё равно не помогло, попробуйте [[Special:UserLogout|выйти из системы]] и войти заново, а также проверьте, что ваш браузер позволяет принимать cookies с этого сайта.",
        "import-invalid-interwiki": "Невозможно импортировать из указанной вики.",
        "import-error-edit": "Страница «$1» не была импортирована, так как вам не разрешено её редактировать.",
        "import-error-create": "Страница «$1» не была импортирована, так как вам не разрешено её создавать.",
        "expand_templates_generate_xml": "Показать дерево разбора XML",
        "expand_templates_generate_rawhtml": "Показать HTML",
        "expand_templates_preview": "Предпросмотр",
-       "expand_templates_preview_fail_html": "<em>Ð\9fоÑ\81колÑ\8cкÑ\83 Ð½Ð° Ñ\81айÑ\82е {{SITENAME}} Ñ\81 Ð²ÐºÐ»Ñ\8eÑ\87еннÑ\8bм Â«Ñ\81Ñ\8bÑ\80Ñ\8bм» HTML Ð¿Ñ\80оизоÑ\88ла Ð¿Ð¾Ñ\82еÑ\80Ñ\8f Ð´Ð°Ð½Ð½Ñ\8bÑ\85 Ñ\81еÑ\81Ñ\81ии, Ð¿Ñ\80едваÑ\80иÑ\82елÑ\8cнÑ\8bй Ð¿Ñ\80оÑ\81моÑ\82Ñ\80 Ñ\81кÑ\80Ñ\8bÑ\82 Ð² ÐºÐ°Ñ\87еÑ\81Ñ\82ве Ð¼ÐµÑ\80Ñ\8b Ð¿Ñ\80едоÑ\81Ñ\82оÑ\80ожноÑ\81Ñ\82и Ð¿Ñ\80оÑ\82ив JavaScript-аÑ\82ак.</em>\n\n<strong>Ð\95Ñ\81ли Ñ\8dÑ\82о Ð±Ñ\8bла Ð¿Ñ\80авомеÑ\80наÑ\8f Ð¿Ð¾Ð¿Ñ\8bÑ\82ка Ð¿Ñ\80едваÑ\80иÑ\82елÑ\8cного Ð¿Ñ\80оÑ\81моÑ\82Ñ\80а, Ð¿Ð¾Ð¶Ð°Ð»Ñ\83йÑ\81Ñ\82а, Ð¿Ð¾Ð¿Ñ\80обÑ\83йÑ\82е ÐµÑ\89Ñ\91 Ñ\80аз.</strong>\nÐ\95Ñ\81ли Ñ\83 Ð²Ð°Ñ\81 Ð¿Ð¾-пÑ\80ежнемÑ\83 Ð½Ðµ Ð¿Ð¾Ð»Ñ\83Ñ\87аеÑ\82Ñ\81Ñ\8f, Ð¿Ð¾Ð¿Ñ\80обÑ\83йÑ\82е [[Special:UserLogout|завеÑ\80Ñ\88иÑ\82Ñ\8c Ñ\81еанÑ\81 Ñ\80абоÑ\82Ñ\8b]] Ð¸ Ð°Ð²Ñ\82оÑ\80изоваÑ\82Ñ\8cÑ\81Ñ\8f ÐµÑ\89Ñ\91 Ñ\80аз.",
+       "expand_templates_preview_fail_html": "<em>Так ÐºÐ°Ðº {{SITENAME}} Ñ\80азÑ\80еÑ\88аеÑ\82 Ð¸Ñ\81полÑ\8cзоваÑ\82Ñ\8c Ñ\87иÑ\81Ñ\82Ñ\8bй HTML, Ð¿Ñ\80едваÑ\80иÑ\82елÑ\8cнÑ\8bй Ð¿Ñ\80оÑ\81моÑ\82Ñ\80 Ð¾Ñ\82клÑ\8eÑ\87Ñ\91н Ð² ÐºÐ°Ñ\87еÑ\81Ñ\82ве Ð¼ÐµÑ\80Ñ\8b Ð¿Ñ\80едоÑ\82вÑ\80аÑ\89ениÑ\8f JavaScript-аÑ\82ак.</em>\n\n<strong>Ð\95Ñ\81ли Ñ\8dÑ\82о Ð´Ð¾Ð±Ñ\80оÑ\81овеÑ\81Ñ\82наÑ\8f Ð¿Ð¾Ð¿Ñ\8bÑ\82ка Ñ\80едакÑ\82иÑ\80ованиÑ\8f, Ð¿Ð¾Ð¶Ð°Ð»Ñ\83йÑ\81Ñ\82а, Ð¿Ð¾Ð¿Ñ\80обÑ\83йÑ\82е ÐµÑ\89Ñ\91 Ñ\80аз.</strong>\nÐ\95Ñ\81ли Ð½Ðµ Ð¿Ð¾Ð»Ñ\83Ñ\87аеÑ\82Ñ\81Ñ\8f Ð¿Ð¾Ð²Ñ\82оÑ\80наÑ\8f Ð¿Ñ\80авка, Ð¿Ð¾Ð¿Ñ\80обÑ\83йÑ\82е [[Special:UserLogout|завеÑ\80Ñ\88иÑ\82Ñ\8c Ñ\81еанÑ\81]] Ñ\80абоÑ\82Ñ\8b, Ð·Ð°Ð½Ð¾Ð²Ð¾ Ð¿Ñ\80едÑ\81Ñ\82авиÑ\82Ñ\8cÑ\81Ñ\8f Ð¸ Ð¿Ñ\80овеÑ\80иÑ\82Ñ\8c, Ñ\87Ñ\82о Ð²Ð°Ñ\88 Ð±Ñ\80аÑ\83зеÑ\80 Ñ\80азÑ\80еÑ\88аеÑ\82 Ð¸Ñ\81полÑ\8cзоваÑ\82Ñ\8c cookies Ð½Ð° Ñ\8dÑ\82ом Ñ\81айÑ\82е.",
        "expand_templates_preview_fail_html_anon": "<em>Поскольку на сайте {{SITENAME}} включен «сырой» HTML, а вы не авторизовались, предварительный просмотр скрыт в качестве меры предосторожности против JavaScript-атак.</em>\n\n<strong>Если это правомерная попытка предварительного просмотра, пожалуйста, [[Special:UserLogin|войдите]] и попробуйте ещё раз.",
        "expand_templates_input_missing": "Вы должны вставить хоть какой-то текст.",
        "pagelanguage": "Изменение языка страницы",
index e0f3110..20e8e9a 100644 (file)
@@ -48,6 +48,7 @@
        "tog-watchlisthidebots": "Кэтээн көрүү тиһигэр робот уларытыытын көрдөрүмэ",
        "tog-watchlisthideminor": "Кыра уларытыылары кэтээмэ",
        "tog-watchlisthideliu": "Бэлиэтэммит кыттааччылар уларытыыларын кэтиир тиһиккэ көрдөрүмэ",
+       "tog-watchlistreloadautomatically": "Сиидэ уларыйда да кэтээһин тиһилигин саҥардан ис(JavaScript баар буолуохтаах)",
        "tog-watchlisthideanons": "Ааттарын эппэтэх кыттааччылар уларытыыларын кэтээһин тиһигэр көрдөрүмэ",
        "tog-watchlisthidepatrolled": "Ботурууллааччы көрбүт көннөрүүтүн кэтээһин испииһэгэр көрдөрүмэ",
        "tog-watchlisthidecategorization": "Сирэй категорияларын көрдөрүмэ",
        "october-date": "Алтынньы $1",
        "november-date": "Сэтинньи $1",
        "december-date": "Ахсынньы $1",
+       "period-am": "ОИ",
+       "period-pm": "ОК",
        "pagecategories": "{{PLURAL:$1|Категория|Категориялар}}",
        "category_header": "\"$1\" категория ыстатыйалара",
        "subcategories": "Субкатегориялар",
        "viewsourceold": "исходнигын көрүү",
        "editlink": "көннөрөргө",
        "viewsourcelink": "исходнигын көрүү",
-       "editsectionhint": "$1 Ñ\81екÑ\86иÑ\8fнÑ\8b уларыт",
+       "editsectionhint": "$1 Ñ\81иÑ\8dкÑ\81ийÑ\8dни уларыт",
        "toc": "Ис хоһооно",
        "showtoc": "көрдөр",
        "hidetoc": "көрдөрүмэ",
        "virus-scanfailed": "скан сыыһата (куода $1)",
        "virus-unknownscanner": "биллибэт антивирус:",
        "logouttext": "'''Эн тиһиликтэн таҕыстыҥ.'''\n\nСорох сирэйдэр өссө даҕаны эйигин урукку ааккынан көрдөрүөхтэрин сөп, ону суох гыныаххын баҕардаххына интэриниэт көрдөрөөччүҥ кээһин ыраастаа.",
+       "cannotlogoutnow-title": "Сип-билин тахсар кыаллыбат",
+       "cannotlogoutnow-text": "Маны $1 туһанар кэмҥэ тахсар кыах суох.",
        "welcomeuser": "Нөрүөн нөргүй, $1!",
        "welcomecreation-msg": "Аатыҥ бэлиэтэннэ.\n{{SITENAME}} ситим-сиргэ үлэлииргэ табыгастаах буоллун диэн [[Special:Preferences|тус туруорууларгын]] уларытыаххын сөп.",
        "yourname": "Кыттааччы аатыҥ:",
        "remembermypassword": "Миигин бу көмпүүтэргэ сигээ ($1 {{PLURAL:$1|күн|күнтэн ордуга суох}})",
        "userlogin-remembermypassword": "Тиһиликтэн тахсыма",
        "userlogin-signwithsecure": "Бигэ холбонуу",
+       "cannotloginnow-title": "Сип-билигин киирэр кыах суох",
+       "cannotloginnow-text": "Маны $1 туһанар кэмҥэ киирэр кыах суох.",
        "yourdomainname": "Эн дөмүөнүҥ:",
        "password-change-forbidden": "Бу биикигэ киирии тылы уоарытар табыллыбат.",
        "externaldberror": "Тас киирии билиитин олоҕун сыыһата буолла, эбэтэр тас киирии билииҥ олоҕун саҥардар кыаҕыҥ суох.",
        "wrongpasswordempty": "Киирии тылгын суруйбатаххын. Өссө киирэн көр.",
        "passwordtooshort": "Киирии тылыҥ наһаа кылгас.\nКырата {{PLURAL:$1|1 бэлиэлээх|$1 бэлиэлээх}} буолуохтаах.",
        "passwordtoolong": "Аһарык {{PLURAL:$1|1 бэлиэттэн|$1 бэлиэттэн}} уһун буолуо суохтаах.",
+       "passwordtoopopular": "Элбэхтэ туттуллар аһарыктары туттар сатаммат. Бука диэн атын аһарыкта тал.",
        "password-name-match": "Киирии тыл ааккыттан атын буолуохтаах.",
        "password-login-forbidden": "Маннык ааты уонна киирии тылы туһаныы бобуллар.",
        "mailmypassword": "Киирии тылы саҥардыы",
        "resetpass_submit": "Киирии тылы уларыт уонна киир",
        "changepassword-success": "Киирии тылыҥ этэҥҥэ уларыйда!",
        "changepassword-throttled": "Ааккын аһара элбэхтик билиһиннэрэ сатаатыҥ.\nБука диэн $1 буолан баран өссө киирэн көрөөр.",
+       "botpasswords": "Оруобаттар аһарыктара",
+       "botpasswords-disabled": "Оруобаттар аһарыктара араарыллыбыттар.",
+       "botpasswords-no-central-id": "Оруобат аһарыгын туһанарга кииннэммит ааккынан киириэхтээххин.",
+       "botpasswords-existing": "Билигин баар оруобат аһарыктара",
+       "botpasswords-createnew": "Оруобат саҥа аһарыгын оҥор",
+       "botpasswords-editexisting": "Оруобат аһарыгын уларыт",
+       "botpasswords-label-appid": "Оруобат аата:",
+       "botpasswords-label-create": "Оҥоруу",
+       "botpasswords-label-update": "Саҥарт",
+       "botpasswords-label-cancel": "Бигэргэтимэ",
+       "botpasswords-label-delete": "Сот",
+       "botpasswords-label-resetpassword": "Аһарыгы саҥаттан",
+       "botpasswords-label-grants": "Туттуллар көҥүллэр:",
+       "botpasswords-label-restrictions": "Туттарга хааччахтаах:",
+       "botpasswords-label-grants-column": "Көҥүллэннэ",
+       "botpasswords-bad-appid": "Маннык аат «$1» сатаммат.",
+       "botpasswords-insert-failed": "«$1» диэн ааттаах оруобаты эбэр табыллыбата. Баҕар хайыы-үйэ эбиллибитэ буолаарай?",
+       "botpasswords-created-title": "Оруобат аһарыга оҥоһулунна",
+       "botpasswords-created-body": "«$1» оруобат аһарыга бигэргэтилиннэ.",
+       "botpasswords-updated-title": "Оруобат аһарыга саҥардылынна",
+       "botpasswords-updated-body": "«$1» оруобат аһарыга уларытылынна.",
+       "botpasswords-deleted-title": "Оруобат аһарыга сотулунна",
+       "botpasswords-deleted-body": "«$1» оруобат аһарыга сотулунна.",
        "resetpass_forbidden": "Киирии тылы уларытар сатаммат",
        "resetpass-no-info": "Ааккын билиһиннэрдэххинэ эрэ бу сирэйгэ быһа тиийиэххин сөп.",
        "resetpass-submit-loggedin": "Киирии тылы уларытыы",
        "passwordreset-emailtext-ip": "Ким эрэ (баҕар эн буолуо, бу IP-ттан $1)  {{SITENAME}} ($4) бырайыакка киирии тылы уларытар туһунан ыйытык биэрбит.\nБу электрон аадырыһы кытта бу {{PLURAL:$3|аат ситимнээх|ааттар ситимнээхтэр}}:\n\n$2\n\nБу быстах кэмҥэ аналлаах {{PLURAL:$3|киирии тыл|кирии тыллар}} {{PLURAL:$5|биир күн үлэлиэҕэ|$5 күн үлэлиэхтэрэ}}.\nЭн тиһиликкэ ааккын этэн саҥа киирии тылы киллэриэхтээххин.\nӨскө бу ыйытыгы ыыппатах буоллаххына, эбэтэр урукку киирии тылгын өйдөөн кэлбит буоллаххына \nбу биллэриини ааххайыа суоххун сөп.\nОччоҕо урукку киирии тылыҥ оннунан хаалыа.",
        "passwordreset-emailtext-user": "$1 диэн кыттааччы  {{SITENAME}} ($4) бырайыакка киирии тылгын уларытар туһунан ыйытык ыыппыт.\nБу электрон аадырыһы кытта бу {{PLURAL:$3|аат ситимнээх|ааттар ситимнээхтэр}}\n\n$2\n\nБу быстах кэмҥэ аналлаах {{PLURAL:$3|киирии тыл|кирии тыллар}} {{PLURAL:$5|биир күн үлэлиэҕэ|$5 күн үлэлиэхтэрэ}}.\nЭн тиһиликкэ ааккын этэн саҥа киирии тылы киллэриэхтээххин.\nӨскө бу ыйытыгы ыыппатах буоллаххына, эбэтэр урукку киирии тылгын өйдөөн кэлбит буоллаххына \nбу биллэриини ааххайыа суоххун сөп.\nОччоҕо урукку киирии тылыҥ оннунан хаалыа.",
        "passwordreset-emailelement": "Кыттааччы: \n$1\n\nБыстах киирии тыл: \n$2",
-       "passwordreset-emailsentemail": "Өскө ааккар баайыллыбыт аадырыһы суруйбут буоллаххына, аһарык тылы уларытар туһунан сурук онно барыа.",
+       "passwordreset-emailsentemail": "Өскө бу Эн ааккар баайыллыбыт аадырыс буоллаҕына, аһарык тылы уларытар туһунан сурук барыа.",
+       "passwordreset-emailsentusername": "Өскө бу аакка баайыллыбыт аадырыс баар буоллаҕына, аһарык тылы уларытар туһунан сурук онно барыа.",
        "passwordreset-emailsent-capture": "Киирии тылы уларытар туһунан сурук аллара эмиэ көрдөрүлүннэ.",
        "passwordreset-emailerror-capture": "Манна киирии тылы уларытар туһунан сурук көрдөрүлүннэ. Ол эрэн сурук бу төрүөттэн $2 кыттааччыга сатаан барбата: $1",
        "changeemail": "Аадырыһы уларытыы уонна сотуу",
        "copyrightwarning2": "Болҕой, эн суруйбут матырыйаалгын ким баҕарар уларытар уонна суох гынар бырааптаах. Суруйбуккун уларыталларын сөбүлээбэт буоллаххына манна суруйума.<br />\nЭбиитин манна суруйдаххына, уларытыы ааптара мин буолабын, эбэтэр көҥүл туһанары уонна уларытары көҥүллүүр сиртэн ыллым диэн бигэргэтэҕин (маны көр $1).<br /> '''КИМ ЭРЭ БАС БИЛИИТИН МАННА КИНИТТЭН КӨҤҮЛЭ СУОХ УГУМА!'''",
        "editpage-cannot-use-custom-model": "Бу сирэй иһинээҕитин тутула уларыйар кыаҕа суох.",
        "longpageerror": "'''Алҕас: Суруйар кэрчиккит {{PLURAL:$1|биир килобаайт|$1 килобаайт}} ыйааһыннаах, онтуккут көҥүллэммит {{PLURAL:$2|биир килобаайты|$2 килобайты}} килобаайты куоһарар. Онон сирэй бигэргэтиллэр кыаҕа суох.'''",
-       "readonlywarning": "'''Сэрэтии: Сиэрбэргэ техническай үлэ бара турар, онон киллэрбит уларытыыларыҥ тута бигэргэнэр кыахтара суох.'''\nОнон уларытыыгын тиэкистээх билэҕэ уган баран, кэлин манна киллэриэххин сөп.\n\nХааччаҕы туруорбут дьаһабыл маннык быһаарыыны хаалларбыт: $1",
+       "readonlywarning": "<strong>Сэрэтии: Сиэрбэргэ техническай үлэ бара турар, онон киллэрбит уларытыыларыҥ тута бигэргэнэр кыахтара суох.</strong>\nОнон уларытыыгын тиэкистээх билэҕэ уган хаалларан баран, манна кэлин угуоххун сөп.\n\nХааччаҕы туруорбут дьаһабыл маннык быһаарыыны хаалларбыт: $1",
        "protectedpagewarning": "'''Сэрэтии:  Бу сирэй хатанан турар, администратор бырааптаах эрэ кыттааччылар уларытар кыахтаахтар.'''\nАллара сурунаал бүтэһик суруга көрдөрүлүннэ:",
        "semiprotectedpagewarning": "'''Биллэрии:''' Бу сирэй хатанан турар; ааттарын билиһиннэрбит эрэ кыттааччылар уларытар кыахтаахтар.\nАллара сурунаал бүтэһик суруга көрдөрүлүннэ:",
        "cascadeprotectedwarning": "<strong>Сэрэтии:</strong> Бу сирэйи дьаһабыллар эрэ уларытар кыахтаахтар, тоҕо диэтэххэ сирэй каскаднай көмүскэллээх {{PLURAL:$1|сирэй бөлөҕөр|сирэйдэр бөлөхтөрүгэр}} киирэр:",
        "permissionserrors": "Киирии алҕаһа",
        "permissionserrorstext": "Маны оҥорор кыаҕыҥ суох, {{PLURAL:$1|төрүтэ|төрүттэрэ}}:",
        "permissionserrorstext-withaction": "Бу дьайыыны ($2) оҥорор кыаҕыҥ суох.  {{PLURAL:$1|Биричиинэтэ|Биричиинэлэрэ}}:",
-       "contentmodelediterror": "Ð\91Ñ\83 Ñ\82оÑ\80Ñ\83мÑ\83 Ñ\83лаÑ\80Ñ\8bÑ\82аÑ\80 ÐºÑ\8bаÒ\95Ñ\8bÒ¥ Ñ\81Ñ\83оÑ\85 Ñ\8dбиÑ\82, Ñ\82оÒ\95о Ð´Ð¸Ñ\8dÑ\82Ñ\8dÑ\85Ñ\85Ñ\8d Ð¸Ò»Ð¸Ð½Ñ\8dÑ\8dÒ\95иÑ\82ин Ð¼Ð°Ð´Ñ\8cÑ\8bала Ð¼Ð°Ð½Ð½Ñ\8bк <code>$1</code>, Ð¾Ñ\82Ñ\82он Ñ\81иÑ\80Ñ\8dй Ð¸Ò»Ð¸Ð½Ñ\8dÑ\8dÒ\95иÑ\82ин Ð±Ð¸Ð»Ð¸Ò¥Ò¥Ð¸Ñ\82Ñ\8d Ð¼Ð°Ð½Ð½Ñ\8bк — <code>$2</code>.",
+       "contentmodelediterror": "Ð\91Ñ\83 Ñ\82оÑ\80Ñ\83мÑ\83 Ñ\83лаÑ\80Ñ\8bÑ\82аÑ\80 ÐºÑ\8bаÒ\95Ñ\8bÒ¥ Ñ\81Ñ\83оÑ\85 Ñ\8dбиÑ\82, Ñ\82оÒ\95о Ð´Ð¸Ñ\8dÑ\82Ñ\8dÑ\85Ñ\85Ñ\8d Ð¸Ò»Ð¸Ð½Ñ\8dÑ\8dÒ\95иÑ\82ин Ð¼Ð°Ð´Ñ\8cÑ\8bала Ð¼Ð°Ð½Ð½Ñ\8bк <code>$1</code>, Ð¾Ñ\82Ñ\82он Ñ\81иÑ\80Ñ\8dй Ð¸Ò»Ð¸Ð½Ñ\8dÑ\8dÒ\95иÑ\82ин Ð¼Ð°Ð´Ñ\8cÑ\8bала Ð±Ð¸Ð»Ð¸Ò¥Ò¥Ð¸Ñ\82Ñ\8d Ñ\83Ñ\80аÑ\82Ñ\8bлааÑ\85 — <code>$2</code>.",
        "recreate-moveddeleted-warn": "'''Болҕой: сотулубут сирэйи төттөрү оҥорон эрэҕин.'''\n\nТолкуйдаан көр, кырдьык бу сирэйи оҥорор туһалаах дуо.\nАллара сотуулар уонна аат уларыйыытын сурунааллара көрдөрүлүннэ.",
        "moveddeleted-notice": "Бу сирэй сотуллубут.\nАллара сотуу уонна аат уларытыытын сурунаалларыгар онно сыһыаннаах туох суруллубута көстөр.",
        "moveddeleted-notice-recent": "Бу сирэй соторутааҕыта (тиһэх 24 чаас иһигэр) сотуллубут эбит.\nАллара сотуу уонна көһөрүү сурунаалларыгар сигэлэр көстөллөр.",
        "userrights": "Кыттааччылар бырааптарын салайыы",
        "userrights-lookup-user": "Кыттаачылар бөлөхтөрүн салайыы",
        "userrights-user-editname": "Кыттааччы аата:",
-       "editusergroup": "Кыттааччылар бөлөхтөрүн уларытарга",
+       "editusergroup": "{{GENDER:$1|Кыттааччы}} бөлөхтөрүн уларытарга",
        "editinguser": "<strong>[[User:$1|$1]]</strong> кыттааччы $2 быраабын уларытыы",
        "userrights-editusergroup": "Кыттааччы бөлөхтөрүн уларытарга",
-       "saveusergroups": "Кыттааччы бөлөхтөрүн бигэргэт",
+       "saveusergroups": "{{GENDER:$1|Кыттааччы}} бөлөхтөрүн бигэргэт",
        "userrights-groupsmember": "Бу бөлөхтөргө киирэр:",
        "userrights-groupsmember-auto": "Көстүбэт чилиэн:",
        "userrights-groups-help": "Бу киһи киирэр бөлөхтөрүн уларытыаххын сөп:\n* Бөлөх аатын таһыгар бэлиэ турар буоллаҕына бу кыттааччы бу бөлөххө киирэр.\n* Бэлиэ суох буоллаҕына - кыттааччы бөлөххө киирбэт\n* Маннык бэлиэ * кыттааччы бөлөххө киирэрин/киирбэтин уларытар кыаҕыҥ суоҕун көрдөрөр.",
        "right-createpage": "Сирэйдэри оҥоруу (ырытыы сирэйдэриттэн ураты)",
        "right-createtalk": "Ырытыы сирэйдэрин оҥоруу",
        "right-createaccount": "Саҥа кыттааччыны бэлиэтээһин",
+       "right-autocreateaccount": "Тас бэлиэ-аатынан киирии",
        "right-minoredit": "Уларытыыны кыра суолталаах курдук бэлиэтээ",
        "right-move": "Сирэйдэр ааттарын уларытыы",
        "right-move-subpages": "Сирэйдэр ааттарын иһигэр киирэр сирэйдэри кытта уларытыы",
        "right-managechangetags": "[[Special:Tags|Бэлиэлэри]] билии олоҕуттан ылыы уонна сотуу",
        "right-applychangetags": "Улартыыларгын кытта [[Special:Tags|тиэктэри]] тутун",
        "right-changetags": "Ханнык баҕарар [[Special:Tags|тиэктэри]] биирдиилээн уларытыыларга уонна сурунаал суруйууларыгар эбэри уонна сотору көҥүллээ",
+       "grant-generic": "Быраап «$1» нобуора",
+       "grant-group-page-interaction": "Сирэйдиин алтыһыы",
+       "grant-group-file-interaction": "Миэдьийэлиин алтыһыы",
+       "grant-group-watchlist-interaction": "Кэтиир тиһиликкин кытта алтыһыы",
+       "grant-group-email": "Сурук ыытыы",
+       "grant-group-high-volume": "Кылгас кэм иһигэр элбэҕи оҥоруу",
+       "grant-group-customization": "Нарылааһын уонна туруоруулар",
+       "grant-group-administration": "Дьаһайыы",
+       "grant-group-other": "Эгэлгэ тэрээһиннэр",
+       "grant-blockusers": "Бэлиэ ааттары хааччахтааһын ууонна хааччаҕын устуу",
+       "grant-createaccount": "Бэлиэтэнии",
+       "grant-createeditmovepage": "Сирэй оҥоруу, тупсарыы уонна аатын уларытыы",
+       "grant-delete": "Сурунаалтан сирэйи, уларытыыны уонна суруктарын сотуу",
+       "grant-editinterface": "MediaWiki аат далыгар уонна тус CSS/JavaScript иһинэн үлэ",
+       "grant-editmycssjs": "Бэйэҥ тус CSS/JavaScript-кын уларытыы",
+       "grant-editmyoptions": "Бэйэҥ туруорууларгын уларытыы",
+       "grant-editmywatchlist": "Кэтиир тиһиликкин уларытыы",
+       "grant-editpage": "Баар сирэйдэри уларытыы",
+       "grant-editprotected": "Көмүскэммит сирэйдэри уларытыы",
+       "grant-highvolume": "Түргэнник элбэҕи уларытыы",
+       "grant-oversight": "Кыттааччылар уларытыыларын уонна сирэйдэр барылларын кистээһин",
+       "grant-patrol": "Сирэй уларыйыытын ботурууллааһын",
+       "grant-protect": "Көмүскээһин уонна көмүскэли суох гыныы",
+       "grant-rollback": "Сирэй уларыйыытын төннөрүү",
+       "grant-sendemail": "Атын кыттааччыларга эл. суругу ыытыы",
+       "grant-uploadeditmovefile": "Билэни хачайдааһын, солбуйан биэрии уонна аатын уларытыы",
+       "grant-uploadfile": "Саҥа билэни киллэрии",
+       "grant-basic": "Сүрүн быраап",
+       "grant-viewdeleted": "Сотуллубут билэлэри уонна сирэйдэри көрүү",
+       "grant-viewmywatchlist": "Кэтиир тиһиликкин көрүү",
        "newuserlogpage": "Кыттааччылары бэлиэтиир сурунаал",
        "newuserlogpagetext": "Соторутааҕыта бэлиэтэммит кыттааччылар.",
        "rightslog": "Кыттаачы бырааптарын сурунаала",
        "action-createpage": "сирэйдэри оҥоруу",
        "action-createtalk": "ырытыы сирэйдэрин оҥоруу",
        "action-createaccount": "кыттааччы бу бэлиэтэнэр аатын оҥоруу",
+       "action-autocreateaccount": "тас бэлиэ-аатынан аптамаатынан киирии",
        "action-history": "сирэй устуоруйатын көрүү",
        "action-minoredit": "бу уларытыыны суолтата суох курдук бэлиэтээ",
        "action-move": "бу сирэй аатын уларытыы",
        "recentchanges-label-plusminus": "Сирэй кээмэйэ бачча баайтынан уларыйбыт",
        "recentchanges-legend-heading": "'''Легендата:'''",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (өссө көр: [[Special:NewPages|Саҥа сирэйдэр тиһиктэрэ]])",
+       "recentchanges-submit": "Көрдөр",
        "rcnotefrom": "Манна {{PLURAL:$5|уларытыы көрдөрүлүннэ|уларытыылар көһүннүлэр}} баччаттан <strong>$3, $4</strong> (баччаттан элбэх көстүбэт <strong>$1</strong>).",
        "rclistfrom": "Бу кэм $3 $2 кэнниттэн оҥоһуллубуттары көрдөр",
        "rcshowhideminor": "$1 кыра уларыйыылары",
        "rcshowhidemine": "Мин уларытыыларбын $1",
        "rcshowhidemine-show": "Көрдөр",
        "rcshowhidemine-hide": "Кистээ",
+       "rcshowhidecategorization": "$1 сирэй категориялааһынын",
        "rcshowhidecategorization-show": "Көрдөр",
        "rcshowhidecategorization-hide": "Кистээ",
        "rclinks": "$2 күҥҥэ бүтэһик $1 уларытыыны көрдөр;<br />$3.",
        "mostrevisions": "Саамай элбэхтик уларытыллыбыт ыстатыйалар",
        "prefixindex": "Мантан саҕаланар (префикстаах) сирэйдэр барыта",
        "prefixindex-namespace": "Сирэй саҕаланыытынан наардаан көрдөрүү ($1 аат далыгар)",
+       "prefixindex-submit": "Көрдөр",
        "prefixindex-strip": "Түмүк тиһигэр префиксы көрдөрүмэ",
        "shortpages": "Кылгас ыстатыйалар",
        "longpages": "Уһун ыстатыйалар",
        "protectedpages-performer": "Кытааччы көмүскээһинэ",
        "protectedpages-params": "Көмүскээһин кээмэйдэрэ",
        "protectedpages-reason": "Төрүөтэ",
+       "protectedpages-submit": "Сирэйдэри көрдөр",
        "protectedpages-unknown-timestamp": "Биллибэт",
        "protectedpages-unknown-performer": "Биллибэт кыттааччы",
        "protectedtitles": "Көмүскэммит ааттар",
        "protectedtitles-summary": "Манна бобуллубут сирэйдэр ааттара сурулуннулар. Билигин көмүскэммит сирэйдэр тиһиктэрин манна көрүөххэ сөп: [[{{#special:ProtectedPages}}|{{int:protectedpages}}]].",
        "protectedtitlesempty": "Биир да аат бу параметрдарынан көмүскэммэт",
+       "protectedtitles-submit": "Ааттарын көрдөр",
        "listusers": "Кыттааччылар испииһэктэрэ",
        "listusers-editsonly": "Саатар биир көннөрүүнү оҥорбут кыттааччылары көрдөр",
        "listusers-creationsort": "Айыллыбыт күнүнэн наардаа",
        "usereditcount": "$1 {{PLURAL:$1|көннөрүү|көннөрүү}}",
        "usercreated": "Баччаҕа {{GENDER:$3|бэлиэтэммит}} $1,  $2",
        "newpages": "Саҥа ыстатыйалар",
+       "newpages-submit": "Көрдөр",
        "newpages-username": "Кыттааччы:",
        "ancientpages": "Бүтэһик уларытыы киирбитинэн наардаммыт ыстатыйалар",
        "move": "Аатын уларыт",
        "specialloguserlabel": "Толорооччу:",
        "speciallogtitlelabel": "Сыал (тиэкис эбэтэр {{ns:user}}:кыттааччы аата):",
        "log": "Сурунааллар",
+       "logeventslist-submit": "Көрдөрүү",
        "all-logs-page": "Көстөр сурунааллар барыта",
        "alllogstext": "{{SITENAME}} сурунаалларын уопсай испииһэгэ.\nСурунаал көрүҥүнэн, кыттааччы аатынан (улахан-кыра буукубата учуоттанар) эбэтэр сирэй аатынан (эмиэ улахана-кырата учуоттанар) наардыаххытын сөп.",
        "logempty": "Сурунаалга сөп түбэһэр элэмиэннэр суохтар.",
        "log-title-wildcard": "Бу сурук бэлиэлэриттэн (буукубалартан) саҕаланар ааттары бул",
        "showhideselectedlogentries": "Талыллыбыт суруктары кистээ/көрдөр",
        "log-edit-tags": "Сурунаалтан талбыт суругуҥ тиэгин уларыт",
+       "checkbox-select": "Талыы: $1",
+       "checkbox-all": "Бары (барыта)",
+       "checkbox-none": "Суох",
        "allpages": "Сирэйдэр барыта",
        "nextpage": "Аныгыскы сирэй ($1)",
        "prevpage": "Бу иннинээҕи сирэй ($1)",
        "cachedspecial-viewing-cached-ts": "Сирэй кээскэ киирбит барылын көрөҕүн, дьиҥнээх сирэй чыҥха атын буолуон сөп.",
        "cachedspecial-refresh-now": "Бүтэһик барылы көр.",
        "categories": "Категориялар",
+       "categories-submit": "Көрдөр",
        "categoriespagetext": "Бу {{PLURAL:$1|категория иһигэр|категориялар истэригэр}} сирэйдэр эбэтэр медиа-билэлэр бааллар.\n[[Special:UnusedCategories|Туттуллубат категориялар]] манна көстүбэттэр.\nӨссө маны көр: [[Special:WantedCategories|Баар буолуохтаах категориялар тиһиктэрэ]].",
        "categoriesfrom": "Мантан саҕаланар категориялары көрдөр:",
        "special-categories-sort-count": "ахсаанынан бэрээдэктээһин",
        "activeusers-hidebots": "Руобаттары көрдөрүмэ",
        "activeusers-hidesysops": "Дьаһабыллары көрдөрүмэ",
        "activeusers-noresult": "Кыттааччылар көстүбэтилэр.",
+       "activeusers-submit": "Көхтөөх кыттааччылары көрдөр",
        "listgrouprights": "Кыттааччылар бөлөхтөрүн бырааптара",
        "listgrouprights-summary": "Манна бу биикигэ баар бөлөхтөр уонна кинилэр киирэр бырааптара көстөллөр.\nБаҕар дьон туспа бырааптарын  туһунан [[{{MediaWiki:Listgrouprights-helppage}}|эбии информация]] баара буолуо.",
        "listgrouprights-key": "Суруга: * <span class=\"listgrouprights-granted\">Биэриллибит бырааптар</span>\n* <span class=\"listgrouprights-revoked\">Быһыллыбыт бырааптар</span>",
        "listgrouprights-namespaceprotection-header": "Аат далын хааччахтара",
        "listgrouprights-namespaceprotection-namespace": "Аат дала",
        "listgrouprights-namespaceprotection-restrictedto": "Кыттааччы көннөрөр бырааба",
+       "listgrants": "Көҥүл",
+       "listgrants-grant": "Көҥүл",
+       "listgrants-rights": "Быраап",
        "trackingcategories": "Кэтиир категориялар",
        "trackingcategories-summary": "Манна MediaWiki аптамаатынан толорор кэтиир категориялара көстөллөр.  Бу аат далыгар {{ns:8}} систиэмэ биллэриилэрин уларытан ааттарын уларытыахха сөп.",
        "trackingcategories-msg": "Кэтиир категория",
        "wlheader-showupdated": "Бүтэһик киирииҥ кэннэ уларыйбыт сирэйдэр '''модьу''' бичигинэн бэлиэтэннилэр.",
        "wlnote": "Манна кэлиҥҥи {{PLURAL:$2|чаас|<strong>$2</strong> чаас}} иһигэр оҥоһуллубут бүтэһик <strong>$1</strong> уларытыы көрдөрүлүннэ, бу кэминээҕи туругунан $3, $4.",
        "wlshowlast": "Бүтэһик $2 күҥҥэ $1 чааска көрдөр",
-       "wlshowtime": "Тиһэҕи көрдөр:",
+       "watchlist-hide": "Кистээ",
+       "watchlist-submit": "Көрдөр",
+       "wlshowtime": "Бу ыккардыгар буолбуту көрдөр:",
        "wlshowhideminor": "кыра суолталаах уларытыы",
        "wlshowhidebots": "оруобат",
        "wlshowhideliu": "бэлиэтэммит кыттааччы",
        "wlshowhideanons": "ааттарын эппэтэх кыттааччы",
        "wlshowhidepatr": "тургутуллубут уларытыы",
        "wlshowhidemine": "бэйэм уларытыым",
+       "wlshowhidecategorization": "сирэй категоризациятын",
        "watchlist-options": "Кэтээн көрүү туруоруутун уларытыы",
        "watching": "Кэтээ...",
        "unwatching": "Кэтээмэ...",
        "delete-confirm": "Маны \"$1\" соторго",
        "delete-legend": "Сотуу",
        "historywarning": "<strong>Сэрэтии</strong>: Сотоору турар сирэйиҥ $1 {{PLURAL:$1|соҕотох барыллаах|барыллаах}} устуоруйалаах:",
+       "historyaction-submit": "Көрдөр",
        "confirmdeletetext": "Эн сирэйи (ойууну) уонна кини устуоруйатын букатын сотоору гынаҕын.\nБука диэн, кырдьык инньэ гынаары гынаргын,\nбу дьайыы туох содуллаах буоларын толору билэргин\nуонна [[{{MediaWiki:Policy-url}}]] сиэрин кэспэккин бигэргэт.",
        "actioncomplete": "Дьайыы оҥоһулунна",
        "actionfailed": "Дьайыы оҥоһуллубата",
        "contributions": "{{GENDER:$1|Кыттааччы}} суруйуута (кылаата)",
        "contributions-title": "$1 кыттааччы киллэрбит уларытыылара",
        "mycontris": "Суруйуу тиһигэ",
+       "anoncontribs": "Суруйуу тиһилигэ",
        "contribsub2": "$1 ($2) суруйуута",
        "contributions-userdoesnotexist": "Маннык \"$1\" кыттааччы аата бэлиэтэниллибэтэх.",
        "nocontribs": "Эппит критерийгэр эппиэттиир уларытыылар көстүбэтилэр.",
        "whatlinkshere-hidelinks": "$1 сигэ (ыйынньык)",
        "whatlinkshere-hideimages": "$1 билэ сигэтэ",
        "whatlinkshere-filters": "Фильтрдар",
+       "whatlinkshere-submit": "Толор",
        "autoblockid": "Аптамаатынан хааччахтааһын #$1",
        "block": "Кыттааччыны хааччахтааһын",
        "unblock": "Кытааччы хааччаҕын устуу",
        "javascripttest-pagetext-frameworks": "Бука диэн, бу тургуутуу эйгэлэриттэн биирин тал: $1",
        "javascripttest-pagetext-skins": "Тургутууну ыытарга тас көрүҥүн бастаан тал:",
        "javascripttest-qunit-intro": "[$1 тургутуу документациятын] манна mediawiki.org көр.",
-       "tooltip-pt-userpage": "Кыттааччы быһыытынан тус сирэйиҥ",
+       "tooltip-pt-userpage": "{{GENDER:|Кыттааччы}} быһыытынан тус сириҥ",
        "tooltip-pt-anonuserpage": "Билигин киирбит IP-м сирэйэ",
-       "tooltip-pt-mytalk": "Кэпсэтэр-ырытар сириҥ",
+       "tooltip-pt-mytalk": "Кэпсэтэр-ырытар {{GENDER:|сириҥ}}",
        "tooltip-pt-anontalk": "Бу IP ырытыыта",
-       "tooltip-pt-preferences": "Бэйэм туруорууларым",
+       "tooltip-pt-preferences": "{{GENDER:|Бэйэҥ}} туруорууларыҥ",
        "tooltip-pt-watchlist": "Кэтээн көрөр сирэйдэрим тиһигэ",
-       "tooltip-pt-mycontris": "Суруйбут/уларыппыт Ñ\81иÑ\80Ñ\8dйдÑ\8dÑ\80иҥ Ñ\82иһикÑ\82Ñ\8dÑ\80Ñ\8d",
+       "tooltip-pt-mycontris": "Суруйбут/уларыппыт {{GENDER:|Ñ\81иÑ\80Ñ\8dйдÑ\8dÑ\80иҥ}} Ñ\82иһиликÑ\82Ñ\8dÑ\80Ñ\8d",
        "tooltip-pt-login": "Манна бэйэҕин билиһиннэриэххин сөп (булгуччута суох).",
        "tooltip-pt-logout": "Тахсыы",
        "tooltip-pt-createaccount": "Манна киирэргэ бэлиэтэнэр уонна куруук ол аатынан киирэр ордук; ол булгуччута суох",
        "tooltip-t-recentchangeslinked": "Бу сирэй сигэнэр сирэйдэригэр кэнники уларыйыылар",
        "tooltip-feed-rss": "RSS бу сирэйгэ",
        "tooltip-feed-atom": "Atom бу сирэйгэ",
-       "tooltip-t-contributions": "Бу кыттааччы уларыппыт сирэйдэрин испииһэгэ",
+       "tooltip-t-contributions": "{{GENDER:$1|Бу кыттааччы}} уларыппыт сирэйдэрин тиһилигэ",
        "tooltip-t-emailuser": "Бу киһиэхэ сурук ыытарга",
        "tooltip-t-info": "Бу сирэй туһунан сиһилии",
        "tooltip-t-upload": "Билэлэри суруттарыы",
        "watchlisttools-edit": "Кэтэбил испииһэгин көрүү/уларытыы",
        "watchlisttools-raw": "\"Сиикэй\" испииһэги уларытыы",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|ырытыы]])",
+       "timezone-local": "Олохтоох",
        "duplicate-defaultsort": "Болҕой: Наардааһын «$2» күлүүһэ урукку «$1» күлүүһү сабар (Ключ сортировки переопределяет прежний ключ).",
        "duplicate-displaytitle": "<strong>Болҕой:</strong> Көрдөрүллүбүт «$2» аат урут көрдөрүллүбүт «$1» ааты уларытар.",
        "invalid-indicator-name": "<strong>Алҕас:Сирэй туругун көрдөрөр индикатор </strong> атрибута <code>name</code> кураанах буолуо суохтаах.",
        "redirect-page": "Сирэй нүөмэрэ",
        "redirect-revision": "Сирэй барыла",
        "redirect-file": "Билэ аата",
+       "redirect-logid": "Сурунаал ID нүөмэрэ",
        "redirect-not-exists": "Суолта көстүбэтэ",
        "fileduplicatesearch": "Хос билэлэри көрдөөһүн",
        "fileduplicatesearch-summary": "Тэҥ билэлэри хэш-куодтарынан көрдөөһүн.",
        "tags-deactivate": "араар",
        "tags-hitcount": "$1 {{PLURAL:$1|уларытыы|уларытыылар}}",
        "tags-manage-no-permission": "Тиэктэри уларытар кыаҕыҥ суох эбит.",
+       "tags-manage-blocked": "Хааччахтаммыт буолаҥҥын уларытыы бэлиэлэрин уларытар кыаҕыҥ суох.",
        "tags-create-heading": "Саҥа тиэги оҥоруу",
        "tags-create-explanation": "Саҥа оҥоһуллубут тиэктэри кыттааччылар уонна буоттар уларытар кыахтаах буолуохтара.",
        "tags-create-tag-name": "Бэлиэ аата:",
index 79f99b3..4fd5ef6 100644 (file)
        "querypage-disabled": "Sta pàggina spiciali fu disattivata pi mutivi di pristazzioni.",
        "apihelp": "Guida a l'API",
        "apihelp-no-such-module": "Mòdulu «$1» nun attruvatu.",
+       "apisandbox": "Pàggina di prova API",
+       "apisandbox-submit": "Addumanna",
        "booksources": "Fonti libbrarî",
        "booksources-search-legend": "Arricerca di fonti libbrarî",
        "booksources-isbn": "Còdici ISBN:",
index 31f5eac..63b3726 100644 (file)
@@ -11,7 +11,8 @@
                        "Macofe",
                        "KWiki",
                        "Matma Rex",
-                       "Srdjan m"
+                       "Srdjan m",
+                       "Conquistador"
                ]
        },
        "tog-underline": "Podvuci linkove:",
        "thu": "чет-čet",
        "fri": "pet-пет",
        "sat": "sub-суб",
-       "january": "januar-сијечањ",
-       "february": "februar-вељача",
-       "march": "mart-ожујак",
-       "april": "april-травањ",
-       "may_long": "maj-свибањ",
-       "june": "jun-липањ",
-       "july": "jul-српањ",
-       "august": "avgust-коловоз",
-       "september": "septembar-рујан",
-       "october": "oktobar-листопад",
-       "november": "студени-novembar",
-       "december": "decembar-просинац",
-       "january-gen": "januara-сијечња",
-       "february-gen": "februara-вељаче",
-       "march-gen": "marta-ожујка",
-       "april-gen": "aprila-травња",
-       "may-gen": "маја-свибња",
-       "june-gen": "junа-липња",
-       "july-gen": "jula-српња",
-       "august-gen": "augusta-коловоза",
-       "september-gen": "septembra-рујна",
-       "october-gen": "oktobra-листопада",
-       "november-gen": "студенога-novembra",
-       "december-gen": "decembra-просинца",
-       "jan": "jan-сиј",
-       "feb": "feb-вељ",
-       "mar": "mar-ожу",
-       "apr": "apr-тра",
-       "may": "maj-сви",
-       "jun": "jun-лип",
-       "jul": "jul-срп",
-       "aug": "aug-кол",
-       "sep": "sep-руј",
-       "oct": "okt-лис",
-       "nov": "сту-nov",
-       "dec": "dec-про",
-       "january-date": "$1. januar",
-       "february-date": "$1. februar",
-       "march-date": "$1. mart",
-       "april-date": "$1. april",
-       "may-date": "$1. maj",
-       "june-date": "$1. jun",
-       "july-date": "$1. jul",
-       "august-date": "$1. august",
-       "september-date": "$1. septembar",
-       "october-date": "$1. oktobar",
-       "november-date": "$1. novembar",
-       "december-date": "$1. decembar",
+       "january": "januar",
+       "february": "februar",
+       "march": "mart",
+       "april": "april",
+       "may_long": "maj",
+       "june": "juni",
+       "july": "juli",
+       "august": "august",
+       "september": "septembar",
+       "october": "oktobar",
+       "november": "novembar",
+       "december": "decembar",
+       "january-gen": "januara",
+       "february-gen": "februara",
+       "march-gen": "marta",
+       "april-gen": "aprila",
+       "may-gen": "maja",
+       "june-gen": "juna",
+       "july-gen": "jula",
+       "august-gen": "augusta",
+       "september-gen": "septembra",
+       "october-gen": "oktobra",
+       "november-gen": "novembra",
+       "december-gen": "decembra",
+       "jan": "jan.",
+       "feb": "feb.",
+       "mar": "mar.",
+       "apr": "apr.",
+       "may": "maj",
+       "jun": "jun.",
+       "jul": "jul.",
+       "aug": "aug.",
+       "sep": "sep.",
+       "oct": "okt.",
+       "nov": "nov.",
+       "dec": "dec.",
+       "january-date": "$1. januara",
+       "february-date": "$1. februara",
+       "march-date": "$1. marta",
+       "april-date": "$1. aprila",
+       "may-date": "$1. maja",
+       "june-date": "$1. juna",
+       "july-date": "$1. jula",
+       "august-date": "$1. augusta",
+       "september-date": "$1. septembra",
+       "october-date": "$1. oktobra",
+       "november-date": "$1. novembra",
+       "december-date": "$1. decembra",
        "pagecategories": "{{PLURAL:$1|Kategorija|Kategorije}}",
        "category_header": "Stranice u kategoriji \"$1\"",
        "subcategories": "Potkategorije",
        "noindex-category": "Neindeksirane stranice",
        "broken-file-category": "Stranice sa neispravnim linkovima do datoteka",
        "about": "O...",
-       "article": "Stranica sadržaja (članak)",
+       "article": "Stranica sadržaja",
        "newwindow": "(otvara se u novom prozoru)",
-       "cancel": "Odustani - Одустани",
+       "cancel": "Otkaži",
        "moredotdotdot": "Još...",
        "morenotlisted": "Ovaj spisak nije kompletan.",
        "mypage": "Moja stranica",
-       "mytalk": "Razgovor / Разговор",
+       "mytalk": "Razgovor",
        "anontalk": "Razgovor za ovu IP adresu",
        "navigation": "Navigacija - Навигација",
        "and": "&#32;i",
        "navigation-heading": "Navigacijski meni",
        "errorpagetitle": "Greška - Грешка",
        "returnto": "Povratak na $1.",
-       "tagline": "Izvor: {{SITENAME}}",
-       "help": "Pomoć / Помоћ",
+       "tagline": "Iz {{SITENAME}}",
+       "help": "Pomoć",
        "search": "Traži / Тражи",
        "searchbutton": "Traži",
        "go": "Idi / Иди",
        "history": "Historija stranice",
        "history_short": "Historija",
        "updatedmarker": "promjene od moje zadnje posjete",
-       "printableversion": "Za štampanje / За штампање",
+       "printableversion": "Verzija za ispis",
        "permalink": "Trajni link",
        "print": "Štampa",
        "view": "Vidi",
        "talkpagelinktext": "Razgovor",
        "specialpage": "Posebna stranica",
        "personaltools": "Lični alati",
-       "articlepage": "Pogledaj stranicu sa sadržajem (članak)",
-       "talk": "Razgovor / Разговор",
+       "articlepage": "Vidi stranicu sadržaja",
+       "talk": "Razgovor",
        "views": "Pregledi",
-       "toolbox": "Alatke / Алатке",
+       "toolbox": "Alati",
        "userpage": "Pogledaj korisničku stranicu - Погледај корисничку страницу",
        "projectpage": "Pogledajte stranicu projekta",
        "imagepage": "Vidi stranicu datoteke/fajla",
        "viewhelppage": "Pogledajte stranicu za pomoć",
        "categorypage": "Pogledaj stranicu kategorije",
        "viewtalkpage": "Pogledajte raspravu",
-       "otherlanguages": "Drugi jezici / Други језици",
+       "otherlanguages": "Na drugim jezicima",
        "redirectedfrom": "(Preusmjereno sa $1)",
        "redirectpagesub": "Preusmjeri stranicu",
        "redirectto": "Preusmjerenje na:",
        "copyrightpage": "{{ns:project}}:Autorska_prava",
        "currentevents": "Trenutni događaji",
        "currentevents-url": "Project:Novosti",
-       "disclaimers": "Odricanje odgovornosti",
-       "disclaimerpage": "Project:Uslovi korištenja, pravne napomene i odricanje odgovornosti",
+       "disclaimers": "Odricanje od odgovornosti",
+       "disclaimerpage": "Project:Opće odricanje od odgovornosti",
        "edithelp": "Pomoć pri uređivanju",
        "helppage-top-gethelp": "Pomoć",
-       "mainpage": "Glavna stranica / Главна страница",
-       "mainpage-description": "Glavna stranica / Главна страница",
+       "mainpage": "Glavna stranica",
+       "mainpage-description": "Glavna stranica",
        "policy-url": "Project:Pravila",
        "portal": "Portal zajednice",
        "portal-url": "Project:Portal_zajednice",
-       "privacy": "Politika privatnosti - Политика приватности",
+       "privacy": "Politika privatnosti",
        "privacypage": "Project:Pravila o anonimnosti",
        "badaccess": "Greška pri odobrenju",
        "badaccess-group0": "Nije vam dozvoljeno izvršiti akciju koju ste zahtjevali.",
        "versionrequired": "Potrebna je verzija $1 MediaWikija",
        "versionrequiredtext": "Potrebna je verzija $1 MediaWikija da bi se koristila ova stranica. Pogledaj [[Special:Version|verziju]].",
        "ok": "da",
+       "pagetitle": "$1 - {{SITENAME}}",
+       "pagetitle-view-mainpage": "{{SITENAME}}",
        "retrievedfrom": "Dobavljeno iz \"$1\"",
        "youhavenewmessages": "Imate $1 ($2).",
        "youhavenewmessagesfromusers": "Imate $1 od {{PLURAL:$3|drugog korisnika|$3 korisnika|$3 korisnika}} ($2).",
        "red-link-title": "$1 (stranica ne postoji)",
        "sort-descending": "Poredaj opadajuće",
        "sort-ascending": "Poredaj rastuće",
-       "nstab-main": "Članak / Чланак",
-       "nstab-user": "Korisnik / Корисник",
+       "nstab-main": "Stranica",
+       "nstab-user": "Stranica korisnika",
        "nstab-media": "Mediji",
        "nstab-special": "Posebna stranica",
        "nstab-project": "Stranica projekta",
        "nstab-image": "Datoteka",
        "nstab-mediawiki": "Poruka / Порука",
        "nstab-template": "Šablon / Шаблон",
-       "nstab-help": "Pomoć / Помоћ",
+       "nstab-help": "Pomoć",
        "nstab-category": "Kategorija / Категорија",
-       "mainpage-nstab": "Glavna stranica / Главна страница",
+       "mainpage-nstab": "Glavna stranica",
        "nosuchaction": "Nema takve akcije",
        "nosuchactiontext": "Akcija navedena u URL-u nije valjana.\nMožda ste pogriješili pri unosu URL-a ili ste slijedili pokvaren link.\nMoguće je i da je ovo greška u softveru koji koristi {{SITENAME}}.",
        "nosuchspecialpage": "Nema takve posebne stranice",
        "userlogin-yourname": "Korisničko ime",
        "userlogin-yourname-ph": "Unesite svoje korisničko ime",
        "createacct-another-username-ph": "Unesi korisničko ime",
-       "yourpassword": "Vaša šifra / Ваша шифра:",
-       "userlogin-yourpassword": "Lozinka/zaporka",
-       "userlogin-yourpassword-ph": "Unesite svoju lozinku/zaporku",
+       "yourpassword": "Lozinka:",
+       "userlogin-yourpassword": "Lozinka",
+       "userlogin-yourpassword-ph": "Unesite svoju lozinku",
        "createacct-yourpassword-ph": "Unesite lozinku/zaporku",
        "yourpasswordagain": "Ponovo upišite šifru / Поново упишите шифру",
        "createacct-yourpasswordagain": "Potvrdite lozinku/zaporku",
        "createacct-yourpasswordagain-ph": "Unesite lozinku/zaporku ponovno",
        "remembermypassword": "Upamti moju lozinku na ovom kompjuteru (za maksimum od $1 {{PLURAL:$1|dan|dana}})",
-       "userlogin-remembermypassword": "Držite me ulogiranog/u",
+       "userlogin-remembermypassword": "Zapamti prijavu",
        "userlogin-signwithsecure": "Koristite sigurnu vezu",
        "yourdomainname": "Vaš domen:",
        "password-change-forbidden": "Ne možete da promenite lozinku na ovom vikiju.",
        "nav-login-createaccount": "Prijavi se / Registruj se",
        "userlogin": "Prijavi se / Пријави се",
        "userloginnocreate": "Prijavi se",
-       "logout": "Odjavi se / Одјави се",
+       "logout": "Odjava",
        "userlogout": "Odjavi se / Одјави се",
        "notloggedin": "Niste prijavljeni",
        "userlogin-noaccount": "Nemate račun?",
        "userlogin-joinproject": "Pridružite se {{SITENAME}}",
        "nologin": "Nemate korisničko ime? '''$1'''.",
-       "nologinlink": "Otvorite račun",
-       "createaccount": "Napraviti novi nalog / Направити нови налог",
+       "nologinlink": "Izradi račun",
+       "createaccount": "Izradi račun",
        "gotaccount": "Imate račun? '''$1'''.",
        "gotaccountlink": "Prijavite se / Пријавите се",
        "userlogin-resetlink": "Zaboravili ste detalje vaše prijave?",
-       "userlogin-resetpassword-link": "Zaboravili ste lozinku/zaporku?",
+       "userlogin-resetpassword-link": "Zaboravili ste lozinku?",
        "userlogin-helplink2": "Pomoć pri prijavljivanju",
        "userlogin-loggedin": "Već ste prijavljeni kao {{GENDER:$1|$1}}.\nKoristite donji obrazac da biste se prijavili kao drugi korisnik.",
-       "userlogin-createanother": "Stvori još jedan račun",
+       "userlogin-createanother": "Izradi drugi račun",
        "createacct-emailrequired": "E-mail adresa",
        "createacct-emailoptional": "E-mail adresa (opcionalno)",
        "createacct-email-ph": "Unesite svoju E-mail adresu",
        "createacct-reason": "Razlog",
        "createacct-reason-ph": "Zašto stvarate novi račun",
        "createacct-submit": "Stvorite svoj račun",
-       "createacct-another-submit": "Napravi korisnički račun",
+       "createacct-another-submit": "Izradi račun",
        "createacct-benefit-heading": "{{SITENAME}} se stvara od ljudi poput vas.",
        "createacct-benefit-body1": "$1 {{PLURAL:$1|izmjena|izmjene}}",
        "createacct-benefit-body2": "$1 {{PLURAL:$1|stranica|stranice|stranica}}",
        "loginlanguagelabel": "Jezik: $1",
        "suspicious-userlogout": "Vaš zahtjev za odjavu je odbijen jer je poslan preko pokvarenog preglednika ili keširanog proksija.",
        "createacct-another-realname-tip": "Pravo ime nije obavezno.\nAko izaberete da date ime, biće korišteno za pripisivanje za vaš rad.",
-       "pt-login": "Prijavi me / Пријави ме",
+       "pt-login": "Prijava",
        "pt-login-button": "Prijavi me / Пријави ме",
-       "pt-createaccount": "Napraviti novi nalog / Направити нови налог",
-       "pt-userlogout": "Odjavi se / Одјави се",
+       "pt-createaccount": "Izradi račun",
+       "pt-userlogout": "Odjava",
        "php-mail-error-unknown": "Nepoznata greška u PHP funkciji mail()",
        "user-mail-no-addy": "Pokušaj slanja e-maila bez e-mail adrese.",
        "user-mail-no-body": "Pokušano slanje e-maila s praznim ili nerazumno kratkim sadržajem.",
        "resetpass_submit": "Odredi lozinku i prijavi se",
        "changepassword-success": "Vaša šifra je uspiješno promjenjena! Prijava u toku...",
        "changepassword-throttled": "Previše puta ste se pokušali prijaviti.\nMolimo Vas da sačekate $1 prije nego što pokušate ponovo.",
+       "botpasswords-label-cancel": "Otkaži",
        "resetpass_forbidden": "Šifre ne mogu biti promjenjene",
        "resetpass-no-info": "Morate biti prijavljeni da bi ste pristupili ovoj stranici direktno.",
        "resetpass-submit-loggedin": "Promijeni lozinku",
-       "resetpass-submit-cancel": "Odustani",
+       "resetpass-submit-cancel": "Otkaži",
        "resetpass-wrong-oldpass": "Privremena ili trenutna lozinka nije valjana.\nMožda ste već uspješno promijenili Vašu lozinku ili ste tražili novu privremenu lozinku.",
        "resetpass-recycled": "Molimo resetirajte vašu lozinku/zaporku u nešto drugo od vaše trenutne lozinke/zaporke.",
        "resetpass-temp-emailed": "Prijavili ste se sa privremenim kodom iz e-pošte.\nDa biste završili prijavljivanje morate postaviti novu lozinku ovde:",
        "hr_tip": "Horizontalna linija (koristite rijetko)",
        "summary": "Sažetak:",
        "subject": "Tema:",
-       "minoredit": "Mala izmjena - Мала измена",
-       "watchthis": "Prati / Прати",
-       "savearticle": "Sačuvaj - Сачувај",
-       "preview": "Pretpregled / Претпреглед",
-       "showpreview": "Pretpregled - Претпреглед",
-       "showdiff": "Prikaži izmjene - Прикажи измене",
+       "minoredit": "Ovo je manje uređenje",
+       "watchthis": "Prati ovu stranicu",
+       "savearticle": "Spremi stranicu",
+       "preview": "Pregled",
+       "showpreview": "Prikaži pregled",
+       "showdiff": "Prikaži izmjene",
        "blankarticle": "<strong>Upozorenje:</strong> Napravili ste praznu stranicu.\nAko ponovno kliknete \"{{int:savearticle}}\", napravit ćete praznu stranicu bez sadržaja.",
        "anoneditwarning": "<strong>Upozorenje:</strong> Niste prijavljeni. \nVaša IP adresa će biti javno vidljiva ako napravite neku izmjenu. Ako se <strong>[$1 prijavite]</strong> ili <strong>[$2 napravite račun]</strong>, vaše izmjene će biti pripisane vašem korisničkom imenu, zajedno sa drugim pogodnostima.",
        "anonpreviewwarning": "''Niste prijavljeni. Vaša IP adresa će biti zabilježena u historiji ove stranice.''",
        "search-external": "Vanjska/spoljna pretraga",
        "searchdisabled": "Pretraga teksta na ovoj Wiki je trenutno onemogućena.\nU međuvremenu možete pretraživati preko Googlea.\nUzmite u obzir da njegovi indeksi za ovu Wiki ne moraju biti ažurirani.",
        "search-error": "Dogodila se pogreška prilikom pretraživanja: $1",
-       "preferences": "Postavke / Подешавања",
-       "mypreferences": "Postavke / Подешавања",
+       "preferences": "Postavke",
+       "mypreferences": "Postavke",
        "prefs-edits": "Broj izmjena:",
        "prefsnologintext2": "Molimo Vas prijavite se da biste promijenili postavke.",
        "prefs-skin": "Izgled (skin)",
-       "skin-preview": "Pretpregled",
+       "skin-preview": "Pregled",
        "datedefault": "Bez preferenci",
        "prefs-labs": "Eksperimentalne mogućnosti",
        "prefs-user-pages": "Korisničke stranice",
        "prefs-personal": "Korisnički profil",
        "prefs-rc": "Podešavanje nedavnih izmjena",
-       "prefs-watchlist": "Praćene stranice / Списак надгледања",
+       "prefs-watchlist": "Lista praćenja",
        "prefs-editwatchlist": "Uredi popis praćenja",
        "prefs-editwatchlist-label": "Uredi unose na popisu praćenja:",
        "prefs-editwatchlist-edit": "Vidite i uklonite naslove na vašem popisu praćenja",
        "prefs-timeoffset": "Vremenska razlika",
        "prefs-advancedediting": "Opće opcije",
        "prefs-editor": "Uređivač",
-       "prefs-preview": "Pretpregled",
+       "prefs-preview": "Pregled",
        "prefs-advancedrc": "Napredne opcije",
        "prefs-advancedrendering": "Napredne opcije",
        "prefs-advancedsearchoptions": "Napredne opcije",
        "right-createtalk": "Pravljenje stranica za razgovor",
        "right-createaccount": "Pravljenje korisničkog računa",
        "right-minoredit": "Označavanje izmjena kao malih",
-       "right-move": "Preusmjeravanje stranica",
+       "right-move": "Premještanje stranica",
        "right-move-subpages": "Preusmjeravanje stranica sa svim podstranicama",
        "right-move-rootuserpages": "Premještanje stranica osnovnih korisnika",
        "right-move-categorypages": "Pomakni stranice kategorije",
        "rc-enhanced-hide": "Sakrij detalje",
        "rc-old-title": "prvobitno kreirano kao \"$1\"",
        "recentchangeslinked": "Srodne izmjene / Сродне измене",
-       "recentchangeslinked-feed": "Srodne izmjene",
-       "recentchangeslinked-toolbox": "Srodne izmjene",
-       "recentchangeslinked-title": "Srodne promjene sa \"$1\"",
+       "recentchangeslinked-feed": "Vezane izmjene",
+       "recentchangeslinked-toolbox": "Vezane izmjene",
+       "recentchangeslinked-title": "Izmjene vezane s \"$1\"",
        "recentchangeslinked-summary": "Ova posebna stranica prikazuje promjene na povezanim stranicama.\nStranice koje su na vašem [[Special:Watchlist|spisku praćenja]] su '''podebljane'''.",
        "recentchangeslinked-page": "Naslov stranice:",
        "recentchangeslinked-to": "Pokaži promjene stranica koji su povezane sa datom stranicom",
        "upload-http-error": "Desila se HTTP greška: $1",
        "upload-copy-upload-invalid-domain": "Kopije postavljanja nisu dostupni na ovom domenu.",
        "upload-dialog-title": "Postavi datoteku",
-       "upload-dialog-button-cancel": "Odustani",
+       "upload-dialog-button-cancel": "Otkaži",
        "upload-dialog-button-done": "Urađeno",
        "upload-dialog-button-save": "Snimi",
        "upload-dialog-button-upload": "Postavi",
-       "upload-form-label-select-file": "Izaberi datoteku",
        "upload-form-label-infoform-title": "Detalji",
        "upload-form-label-infoform-name": "Ime",
        "upload-form-label-infoform-description": "Opis",
        "trackingcategories-disabled": "Kategorija je onemogućena",
        "mailnologin": "Nema adrese za slanje",
        "mailnologintext": "Morate biti [[Special:UserLogin|prijavljeni]] i imati ispravnu adresu e-pošte u vašim [[Special:Preferences|podešavanjima]] da biste slali e-poštu drugim korisnicima.",
-       "emailuser": "Pošalji E-mail ovom korisniku",
+       "emailuser": "Pošalji e-mail ovom korisniku",
        "emailuser-title-target": "Slanje e-maila {{GENDER:$1|korisniku|korisnici|korisniku}}",
        "emailuser-title-notarget": "Slanje e-maila korisniku",
        "emailpagetext": "Možete da koristite donji obrazac da pošaljete e-mail {{GENDER:$1|ovom korisniku|ovoj korisnici|ovom korisniku|}}.\nE-mail koju ste uneli u vašim [[Special:Preferences|postavkama]] će se prikazati u polju \"Od:\", tako da će primalac moći da vam odgovori direktno.",
        "emailuserfooter": "Ovu e-poruku {{GENDER:$1|poslao|poslala}} je $1 {{GENDER:$2|korisniku|korisnici}} $2 pomoću funkcije \"{{int:emailuser}}\" s projekta {{SITENAME}}.",
        "usermessage-summary": "Ostavljanje sistemske poruke.",
        "usermessage-editor": "Sistem za poruke",
-       "watchlist": "Spisak praćenja / Списак праћења",
-       "mywatchlist": "Popis praćenja / Списак надгледања",
+       "watchlist": "Lista praćenja",
+       "mywatchlist": "Lista praćenja",
        "watchlistfor2": "Za $1 $2",
        "nowatchlist": "Nemate ništa na svom spisku praćenih članaka.",
        "watchlistanontext": "Morate biti prijavljeni kako biste vidjeli ili uređivali svoj spisak praćenih članaka.",
        "removewatch": "Ukloni sa spiska praćenja",
        "removedwatchtext": "Stranica „[[:$1]]“ i njena stranica za razgovor je uklonjena s vašeg [[Special:Watchlist|spiska nadgledanja]].",
        "removedwatchtext-short": "Stranica \"$1\" je uklonjena sa vašeg spiska praćenja.",
-       "watch": "Prati / Прати",
-       "watchthispage": "Prati / Прати",
+       "watch": "Prati",
+       "watchthispage": "Prati ovu stranicu",
        "unwatch": "Prekini praćenje",
-       "unwatchthispage": "Ukinite praćenje",
-       "notanarticle": "Nije članak",
+       "unwatchthispage": "Prestani pratiti",
+       "notanarticle": "Nije stranica sadržaja",
        "notvisiblerev": "Posljednja izmjena drugog korisnika je bila izbrisana",
        "watchlist-details": "{{PLURAL:$1|$1 stranica|$1 stranice|$1 stranica }} na vašem spisku praćenja, ne računajući posebno stranice za razgovor.",
        "wlheader-enotif": "* Obavještavanje e-poštom je omogućeno.",
        "blanknamespace": "(Glavno)",
        "contributions": "Doprinosi {{GENDER:$1|korisnika|korisnice|korisnika}}",
        "contributions-title": "Korisnički doprinosi od $1",
-       "mycontris": "Doprinosi / Доприноси",
+       "mycontris": "Doprinosi",
+       "anoncontribs": "Doprinosi",
        "contribsub2": "Za {{GENDER:$3|$1}} ($2)",
        "contributions-userdoesnotexist": "Korisnički račun \"$1\" nije registrovan.",
        "nocontribs": "Nisu nađene promjene koje zadovoljavaju ove uslove.",
        "sp-contributions-toponly": "Prikaži samo izmjene koje su posljednje revizije",
        "sp-contributions-newonly": "Prikaži samo izmjene kojima su napravljene nove stranice",
        "sp-contributions-submit": "Traži",
-       "whatlinkshere": "Što vodi ovdje / Шта води овде",
+       "whatlinkshere": "Što upućuje ovamo",
        "whatlinkshere-title": "Stranice koje vode / Странице које воде до $1",
        "whatlinkshere-page": "Stranica:",
        "linkshere": "Sljedeće stranice vode na '''[[:$1]]''':",
        "lockfilenotwritable": "Datoteka zaključavanja baze je zaštićena za pisanje.\nAko želite otključati ili zaključati bazu, ova datoteka mora biti omogućena za pisanje od strane web servera.",
        "databasenotlocked": "Baza podataka nije zaključana.",
        "lockedbyandtime": "(od $1 dana $2 u $3)",
-       "move-page": "Preusmjeravanje $1",
-       "move-page-legend": "Premjestite stranicu",
+       "move-page": "Premještanje $1",
+       "move-page-legend": "Premjesti stranicu",
        "movepagetext": "Korištenjem ovog formulara možete preimenovati stranicu, premještajući cijelu historiju na novo ime.\nČlanak pod starim imenom će postati stranica koja preusmjerava na članak pod novim imenom. \nMožete automatski izmjeniti preusmjerenje do izvornog naslova.\nAko se ne odlučite na to, provjerite [[Special:DoubleRedirects|dvostruka]] ili [[Special:BrokenRedirects|neispravna preusmjeravanja]].\nDužni ste provjeriti da svi linkovi i dalje nastave voditi na prave stranice.\n\nImajte na umu da članak '''neće''' biti preusmjeren ukoliko već postoji članak pod imenom na koje namjeravate da preusmjerite osim u slučaju stranice za preusmjeravanje koja nema nikakvih starih izmjena.\nTo znači da možete vratiti stranicu na prethodno mjesto ako pogriješite, ali ne možete zamijeniti postojeću stranicu.\n\n'''Pažnja!'''\nOvo može biti drastična i neočekivana promjena kad su u pitanju popularne stranice;\nMolimo dobro razmislite prije nego što preimenujete stranicu.",
-       "movepagetext-noredirectfixer": "Koristeći obrazac ispod ćete preimenovati stranicu i premjestiti cijelu njenu historiju na novi naziv.\nStari naziv će postati preusmjerenje na novi naziv.\nMolimo provjerite da li postoje [[Special:DoubleRedirects|dvostruka]] ili [[Special:BrokenRedirects|nedovršena preusmjerenja]].\nVi ste za to odgovorni te morate provjeriti da li su linkovi ispravni i da li vode tamo gdje bi trebali.\n\nImajte na umu da stranica '''neće''' biti premještena ako već postoji stranica s tim imenom, osim ako je prazna ili je preusmjerenje ili nema ranije historije.\nOvo znali da možete preimenovati stranicu nazad gdje je ranije bila preimenovana ako ste pogriješili a ne možete ponovo preimenovati postojeću stranicu.\n\n'''Pažnja!'''\nImajte na umu da preusmjeravanje popularnog članka može biti\ndrastična i neočekivana promjena za korisnike; molimo budite sigurni da ste shvatili posljedice prije nego što nastavite.",
+       "movepagetext-noredirectfixer": "Koristeći obrazac ispod ćete preimenovati stranicu i premjestiti cijelu njenu historiju na novi naziv.\nStari naziv će postati preusmjerenje na novi naziv.\nMolimo provjerite da li postoje [[Special:DoubleRedirects|dvostruka]] ili [[Special:BrokenRedirects|nedovršena preusmjerenja]].\nVi ste za to odgovorni te morate provjeriti da li su linkovi ispravni i da li vode tamo gdje bi trebali.\n\nImajte na umu da stranica <strong>neće</strong> biti premještena ako već postoji stranica s tim imenom, osim ako je prazna ili je preusmjerenje ili nema ranije historije.\nOvo znali da možete preimenovati stranicu nazad gdje je ranije bila preimenovana ako ste pogriješili a ne možete ponovo preimenovati postojeću stranicu.\n\n<strong>Napomena:</strong>\nImajte na umu da premještanje popularnog članka može biti\ndrastična i neočekivana promjena za korisnike; molimo budite sigurni da ste shvatili posljedice prije nego što nastavite.",
        "movepagetalktext": "Ako označite ovu kutijucu, pridružena stranica za razgovor će se automatski premjestiti na novi naslov, ukoliko ne-prazna stranica razgovor sa istim imenom već postoji. U tom slučaju ćete morati, ako želite, ručno premjestiti ili spojiti stranicu.",
        "moveuserpage-warning": "'''Upozorenje:''' Premještate korisničku stranicu. Molimo da zapamtite da će se samo stranica premjestiti a korisnik se ''neće'' preimenovati.",
        "movecategorypage-warning": "<strong>Upozorenje:</strong> Premještate stranicu kategorije. Imajte na umu da će samo stranica biti premještena i da sve stranice u staroj kategoriji <em>neće</em> biti ponovo kategorirane u novu kategoriju.",
        "cant-move-category-page": "Nemate dopuštene da premještate stranice kategorija.",
        "cant-move-to-category-page": "Nemate dopuštenje da premjestite stranicu na stranicu kategorije.",
        "newtitle": "Novi naslov:",
-       "move-watch": "Prati ovu stranicu - Прати ову страницу",
-       "movepagebtn": "Premjesti stranicu – Премјести страницу",
+       "move-watch": "Prati izvornu i ciljnu stranicu",
+       "movepagebtn": "Premjesti stranicu",
        "pagemovedsub": "Premještanje uspjelo",
        "movepage-moved": "'''\"$1\" je premještena na \"$2\"'''",
        "movepage-moved-redirect": "Preusmjerenje je napravljeno.",
        "movepage-moved-noredirect": "Pravljenje preusmjerenja je onemogućeno.",
        "articleexists": "Stranica pod tim imenom već postoji, ili je ime koje ste izabrali neispravno.\nMolimo Vas da izaberete drugo ime.",
        "cantmove-titleprotected": "Ne možete premjestiti stranicu na ovu lokaciju, jer je novi naslov zaštićen od pravljenja",
-       "movetalk": "Premjesti i stranicu za diskusiju zajedno sa člankom (ako nije prazna).",
-       "move-subpages": "Premjesti sve podstranice (do $1)",
-       "move-talk-subpages": "Premjesti podstranice stranica za razgovor (do $1)",
+       "movetalk": "Premjesti pridruženu stranicu za razgovor",
+       "move-subpages": "Premjesti podstranice (sve do $1)",
+       "move-talk-subpages": "Premjesti podstranice stranice za razgovor (sve do $1)",
        "movepage-page-exists": "Stranica $1 već postoji i ne može biti automatski zamijenjena.",
        "movepage-page-moved": "Stranica $1 je premještena na $2.",
        "movepage-page-unmoved": "Stranica $1 ne može biti premještena na $2.",
        "nonfile-cannot-move-to-file": "Ne mogu se premjestiti podaci u datotečni imenski prostor",
        "imagetypemismatch": "Ekstenzija nove datoteke ne odgovara njenom tipu",
        "imageinvalidfilename": "Ciljno ime datoteke nije valjano",
-       "fix-double-redirects": "Ažuriraj sva preusmjerenja koja vode ka originalnom naslovu",
+       "fix-double-redirects": "Ažuriraj sva preusmjerenja koja vode na originalni naslov",
        "move-leave-redirect": "Ostavi preusmjerenje",
        "protectedpagemovewarning": "'''Upozorenje:''' Ova stranica je zaključana tako da je mogu premještati samo korisnici sa ovlastima administratora.\nPosljednja stavka evidencije je prikazana ispod kao referenca:",
        "semiprotectedpagemovewarning": "'''Napomena:''' Ova stranica je zaključana tako da je mogu uređivati samo registrovani korisnici.\nPosljednja stavka evidencije je prikazana ispod kao referenca:",
        "javascripttest-pagetext-frameworks": "Izaberite jedan od sledećih radnih okvira: $1",
        "javascripttest-pagetext-skins": "Izaberite s kojim skinom (interfejsom) želite da pokrenete probu:",
        "javascripttest-qunit-intro": "Pogledajte [$1 dokumentaciju za testiranje] na mediawiki.org.",
-       "tooltip-pt-userpage": "Vaša korisnička stranica",
+       "tooltip-pt-userpage": "{{GENDER:|Vaša korisnička}} stranica",
        "tooltip-pt-anonuserpage": "Korisnička stranica za ip koju Vi uređujete kao",
-       "tooltip-pt-mytalk": "Vaša stranica za razgovor",
+       "tooltip-pt-mytalk": "{{GENDER:|Vaša}} stranica za razgovor",
        "tooltip-pt-anontalk": "Razgovor o doprinosu sa ove IP adrese",
-       "tooltip-pt-preferences": "Vaše postavke",
-       "tooltip-pt-watchlist": "Spisak stranica koje pratite radi izmjena",
-       "tooltip-pt-mycontris": "Spisak vaših doprinosa",
+       "tooltip-pt-preferences": "{{GENDER:|Vaše}} postavke",
+       "tooltip-pt-watchlist": "Lista stranica čije izmjene pratite",
+       "tooltip-pt-mycontris": "Lista {{GENDER:|vaših}} doprinosa",
+       "tooltip-pt-anoncontribs": "Lista uređenja napravljenih s ove IP adrese",
        "tooltip-pt-login": "Predlažem da se prijavite; međutim, to nije obavezno",
        "tooltip-pt-logout": "Odjava sa projekta {{SITENAME}}",
        "tooltip-pt-createaccount": "Ohrabrujemo vas da otvorite račun i prijavite se; to, međutim, nije obavezno",
-       "tooltip-ca-talk": "Razgovor o sadržaju stranice",
+       "tooltip-ca-talk": "Diskusija o stranici sadržaja",
        "tooltip-ca-edit": "Uredi ovu stranicu",
        "tooltip-ca-addsection": "Započnite novu sekciju.",
        "tooltip-ca-viewsource": "Ova stranica je zaštićena.\nMožete vidjeti njen izvor",
        "tooltip-ca-delete": "Izbriši ovu stranicu",
        "tooltip-ca-undelete": "Vratite izmjene koje su načinjene prije brisanja stranice",
        "tooltip-ca-move": "Premjesti ovu stranicu",
-       "tooltip-ca-watch": "Dodajte ovu stranicu na Vaš spisak praćenja",
+       "tooltip-ca-watch": "Dodaj ovu stranicu na svoju listu praćenja",
        "tooltip-ca-unwatch": "Izbrišite ovu stranicu sa spiska praćenja",
        "tooltip-search": "Traži ovaj Wiki / Тражи овај Вики [alt-f]",
        "tooltip-search-go": "Idi na stranicu s upravo ovakvim imenom ako postoji",
        "tooltip-search-fulltext": "Pretraga stranica sa ovim tekstom",
-       "tooltip-p-logo": "Posjetite glavnu stranicu",
-       "tooltip-n-mainpage": "Posjetite glavnu stranu",
-       "tooltip-n-mainpage-description": "Posjetite glavnu stranicu",
+       "tooltip-p-logo": "Posjeti glavnu stranicu",
+       "tooltip-n-mainpage": "Posjeti glavnu stranicu",
+       "tooltip-n-mainpage-description": "Posjeti glavnu stranicu",
        "tooltip-n-portal": "O projektu, što možete učiniti, gdje možete naći stvari",
        "tooltip-n-currentevents": "Pronađi dodatne informacije o trenutnim događajima",
        "tooltip-n-recentchanges": "Spisak nedavnih izmjena na wikiju.",
        "tooltip-n-randompage": "Otvorite slučajnu stranicu",
-       "tooltip-n-help": "Mjesto gdje možete nešto da naučite",
-       "tooltip-t-whatlinkshere": "Spisak svih stranica povezanih sa ovim",
+       "tooltip-n-help": "Mjesto za saznavanje",
+       "tooltip-t-whatlinkshere": "Lista svih wiki stranica koje upućuju ovamo",
        "tooltip-t-recentchangeslinked": "Nedavne izmjene ovdje povezanih stranica",
        "tooltip-feed-rss": "RSS feed za ovu stranicu",
        "tooltip-feed-atom": "Atom feed za ovu stranicu",
-       "tooltip-t-contributions": "Pogledajte listu doprinosa ovog korisnika",
+       "tooltip-t-contributions": "Lista doprinosa {{GENDER:$1|ovog korisnika}}",
        "tooltip-t-emailuser": "Pošaljite e-mail ovom korisniku",
        "tooltip-t-upload": "Postavi datoteke",
-       "tooltip-t-specialpages": "Popis svih posebnih stranica",
-       "tooltip-t-print": "Verzija ove stranice za štampanje",
+       "tooltip-t-specialpages": "Lista svih posebnih stranica",
+       "tooltip-t-print": "Verzija ove stranice za ispis",
        "tooltip-t-permalink": "Stalni link ove verzije stranice",
-       "tooltip-ca-nstab-main": "Pogledajte sadržaj stranice",
+       "tooltip-ca-nstab-main": "Vidi stranicu sadržaja",
        "tooltip-ca-nstab-user": "Pogledajte korisničku stranicu",
        "tooltip-ca-nstab-media": "Pogledajte medijski fajl",
        "tooltip-ca-nstab-special": "Ovo je posebna stranica, te se ne može zasebno uređivati",
        "tooltip-ca-nstab-image": "Vidi stranicu datoteke/fajla",
        "tooltip-ca-nstab-mediawiki": "Pogledajte sistemsku poruku",
        "tooltip-ca-nstab-template": "Pogledajte šablon",
-       "tooltip-ca-nstab-help": "Pogledajte stranicu za pomoć",
+       "tooltip-ca-nstab-help": "Vidi stranicu pomoći",
        "tooltip-ca-nstab-category": "Pogledajte stranicu kategorije",
-       "tooltip-minoredit": "Označite ovo kao manju izmjenu",
-       "tooltip-save": "Snimi izmjene - Сними измјене [alt-s]",
-       "tooltip-preview": "Prethodni pregled stranice, molimo koristiti prije snimanja!",
+       "tooltip-minoredit": "Označi ovo kao manje uređenje",
+       "tooltip-save": "Spremite svoje izmjene",
+       "tooltip-preview": "Pregledajte svoje izmjene. Molimo vas da ovo koristite prije spremanja.",
        "tooltip-diff": "Prikaz izmjena koje ste napravili u tekstu",
        "tooltip-compareselectedversions": "Pogledajte pazlike između dvije selektovane verzije ove stranice.",
-       "tooltip-watch": "Postavite ovu stranicu na Vaš spisak praćenja / Поставите ову страницу на Ваш списак праћења [alt-w]",
+       "tooltip-watch": "Dodaj ovu stranicu na svoju listu praćenja",
        "tooltip-watchlistedit-normal-submit": "Ukloni naslove",
        "tooltip-watchlistedit-raw-submit": "Ažuriraj spisak praćenja",
        "tooltip-recreate": "Ponovno pravljenje stranice iako je već brisana",
        "exif-attributionurl": "Kada ponovno koristite ovaj rad, molimo povežite ga na",
        "exif-preferredattributionname": "Kada ponovno koristite ovaj rad, molimo pripišite ga na",
        "exif-pngfilecomment": "PNG komentar datoteke",
-       "exif-disclaimer": "Odricanje odgovornosti",
+       "exif-disclaimer": "Odricanje od odgovornosti",
        "exif-contentwarning": "Upozorenje o sadržaju",
        "exif-giffilecomment": "GIF komentar datoteke",
        "exif-intellectualgenre": "Tip predmeta",
        "fileduplicatesearch-result-1": "Datoteka \"$1\" nema identičnih dvojnika.",
        "fileduplicatesearch-result-n": "Datoteka \"$1\" ima {{PLURAL:$2|1 identičnog|$2 identična|$2 identičnih}} dvojnika.",
        "fileduplicatesearch-noresults": "Nije pronađena datoteka sa imenom \"$1\".",
-       "specialpages": "Posebno / Посебно",
+       "specialpages": "Posebne stranice",
        "specialpages-note": "* Normalne posebne stranice.\n* <span class=\"mw-specialpagerestricted\">Ograničene posebne stranice.</span>\n* <span class=\"mw-specialpagecached\">Keširane posebne stranice (mogu biti zastarjele).</span>",
        "specialpages-group-maintenance": "Izvještaji o održavanju / Извјештаји о одржавању",
        "specialpages-group-other": "Ostale posebne stranice - Остале посебне странице",
        "feedback-bugcheck": "Izvrsno! Molimo provjerite da se ne radi o nekom [$1 poznatom \"bugu\"].",
        "feedback-bugnew": "Provereno. Prijavi novu grešku",
        "feedback-bugornote": "Ako ste spremni da detaljno opišete tehnički problem, onda [$1 prijavite grešku].\nU suprotnom, poslužite se jednostavnim obrascem ispod. Vaš komentar će stajati na stranici „[$3 $2]“, zajedno s korisničkim imenom i pregledačem koji koristite.",
-       "feedback-cancel": "Odustani",
+       "feedback-cancel": "Otkaži",
        "feedback-close": "Gotovo",
        "feedback-error1": "Greška: neprepoznat rezultat od API-ja",
        "feedback-error2": "Greška: Uređivanje nije uspjelo",
        "duration-centuries": "$1 {{PLURAL:$1|vijek|vijekova}}",
        "duration-millennia": "$1 {{PLURAL:$1|milenijum|milenijuma}}",
        "rotate-comment": "Slika rotirana za $1 {{PLURAL:$1|stepeni}} u smjeru kazaljke na satu",
-       "expand_templates_input": "Unos - Унос"
+       "expand_templates_input": "Unos - Унос",
+       "expand_templates_preview": "Pregled"
 }
index 7033701..e78d108 100644 (file)
        "prefswarning-warning": "Vykonali ste zmeny v nastaveniach, ktoré zatiaľ nie sú uložené. Ak túto stránku opustíte bez kliknutia na „$1“, vaše nastavenia sa neaktualizujú.",
        "prefs-tabs-navigation-hint": "Tip: prepínať medzi záložkami môžete aj pomocou šípok vľavo a vpravo.",
        "userrights": "Spravovanie používateľských práv",
-       "userrights-lookup-user": "Spravovať skupiny používateľov",
+       "userrights-lookup-user": "Spravovať členstvo používateľa v skupinách",
        "userrights-user-editname": "Zadajte meno používateľa:",
-       "editusergroup": "Upraviť skupinu používateľa",
+       "editusergroup": "Upraviť skupiny {{GENDER:$1|používateľa|používateľky}}",
        "editinguser": "Zmena práv používateľa '''[[User:$1|$1]]''' $2",
        "userrights-editusergroup": "Upraviť skupiny používateľa",
        "saveusergroups": "Uložiť skupiny používateľa",
-       "userrights-groupsmember": "Člen skupiny:",
-       "userrights-groupsmember-auto": "Implicitným členom:",
-       "userrights-groups-help": "Môžete zmeniť, v ktorých skupinách sa používateľ nachádza.\n* Zaškrtnuté pole znamená, že používateľ je v skupine.\n* Nezaškrtnuté pole znamená, že používateľ nie je v skupine.\n* „*“ znamená, že nemôžete odstrániť skupinu, keď ste ju už pridali resp. naopak.",
+       "userrights-groupsmember": "{{GENDER:$2|Člen|Členka}} {{PLURAL:$1|skupiny|skupín}}:",
+       "userrights-groupsmember-auto": "Implicitne {{GENDER:$2|člen|členka}} {{PLURAL:$1|skupiny|skupín}}:",
+       "userrights-groups-help": "Môžete zmeniť skupiny, do ktorých je {{GENDER:$1|používateľ zaradený|používateľka zaradená}}.\n* Zaškrtnuté pole znamená, že {{GENDER:$1|používateľ|používateľka}} je v skupine.\n* Nezaškrtnuté pole znamená, že {{GENDER:$1|používateľ|používateľka}} nie je v skupine.\n* Hviezdička (*) znamená, že nemôžete odstrániť skupinu, keď ste ju už pridali resp. naopak.",
        "userrights-reason": "Dôvod:",
        "userrights-no-interwiki": "Nemáte oprávnenie upravovať práva používateľov na iných wiki.",
        "userrights-nodatabase": "Databáza $1 neexistuje alebo nie je lokálna.",
        "rcshowhidecategorization": "$1 kategorizáciu stránok",
        "rcshowhidecategorization-show": "Zobraziť",
        "rcshowhidecategorization-hide": "Skryť",
-       "rclinks": "Zobraziť posledných $1 úprav v posledných $2 dňoch<br />$3",
+       "rclinks": "Zobraziť posledných $1 úprav za posledných $2 dní<br />$3",
        "diff": "rozdiel",
        "hist": "história",
        "hide": "Skryť",
        "pager-older-n": "{{PLURAL:$1|1 starší|$1 staršie|$1 starších}}",
        "suppress": "Dozor",
        "querypage-disabled": "Táto špeciálna stránka bola zakázaná z výkonnostných dôvodov.",
+       "apisandbox": "API pieskovisko",
+       "apisandbox-api-disabled": "API je na tejto stránke vypnuté.",
+       "apisandbox-fullscreen": "Rozbaliť panel",
+       "apisandbox-unfullscreen": "Zobraziť stránku",
+       "apisandbox-submit": "Odoslať dopyt",
+       "apisandbox-reset": "Vyčistiť",
+       "apisandbox-retry": "Skúsiť znova",
+       "apisandbox-examples": "Príklad",
+       "apisandbox-results": "Výsledok",
+       "apisandbox-request-url-label": "URL požiadavky:",
        "booksources": "Knižné zdroje",
        "booksources-search-legend": "Vyhľadávať knižné zdroje",
        "booksources-search": "Hľadať",
        "log-title-wildcard": "Hľadať názvy začínajúce týmto textom",
        "showhideselectedlogentries": "Zobraziť/skryť vybraté položky záznamu",
        "log-edit-tags": "Editovať značky zvolených položiek záznamu",
+       "checkbox-select": "Zvoliť: $1",
+       "checkbox-all": "Všetky",
+       "checkbox-none": "Ždiadne",
+       "checkbox-invert": "Invertovať",
        "allpages": "Všetky stránky",
        "nextpage": "Ďalšia stránka ($1)",
        "prevpage": "Predchádzajúca stránka ($1)",
        "wlshowhideanons": "anonymov",
        "wlshowhidepatr": "preverené úpravy",
        "wlshowhidemine": "moje úpravy",
+       "wlshowhidecategorization": "kategorizáciu stránok",
        "watchlist-options": "Nastavenia zoznamu sledovaných",
        "watching": "Pridávam do zoznamu sledovaných...",
        "unwatching": "Odoberám zo zoznamu sledovaných...",
        "delete-edit-reasonlist": "Upraviť dôvody zmazania",
        "delete-toobig": "Táto stránka má veľkú históriu úprav, viac ako $1 {{PLURAL:$1|revíziu|revízie|revízií}}. Mazanie takýchto stránok bolo obmedzené, aby sa zabránilo náhodnému poškodeniu {{GRAMMAR:genitív|{{SITENAME}}}}.",
        "delete-warning-toobig": "Táto stránka má veľkú históriu úprav, viac ako $1 {{PLURAL:$1|revíziu|revízie|revízií}}. Jej zmazanie by mohlo narušiť databázové operácie {{GRAMMAR:genitív|{{SITENAME}}}}; postupujte opatrne.",
+       "deleteprotected": "Túto stránku nemôžete vymazať, pretože je zamknutá.",
+       "deleting-backlinks-warning": "'''Upozornenie:''' Stránka, ktorú sa chystáte zmazať, je odkazovaná [[Special:WhatLinksHere/{{FULLPAGENAME}}|z iných stránok]], prípadne do nich vložená.",
        "rollback": "Vrátiť späť úpravy",
        "rollbacklink": "vrátiť",
        "rollbacklinkcount": "vrátenie $1 {{PLURAL:$1|úpravy|úprav}}",
        "tooltip-t-recentchangeslinked": "Posledné úpravy v stránkach, na ktoré odkazuje táto stránka",
        "tooltip-feed-rss": "RSS feed pre túto stránku",
        "tooltip-feed-atom": "Kanál Atom pre túto stránku",
-       "tooltip-t-contributions": "Pozrieť si zoznam príspevkov od tohto používateľa",
+       "tooltip-t-contributions": "Pozrieť si zoznam príspevkov {{GENDER:$1|tohoto používateľa|tejto používateľky}}",
        "tooltip-t-emailuser": "Poslať e-mail tomuto používateľovi",
        "tooltip-t-info": "Viac informácií o tejto stránke",
        "tooltip-t-upload": "Nahranie súborov",
index 794b08b..0878a24 100644 (file)
        "previewnote": "'''Vedite, da stran le predogledujete.'''\nVaših sprememb še nismo shranili!",
        "continue-editing": "Pojdi na urejevalno območje",
        "previewconflict": "V prikazanem predogledu je v zgornjem predelu urejanja navedeno besedilo, kakor se bo prikazalo, če ga boste shranili.",
-       "session_fail_preview": "'''Oprostite! Zaradi izgube podatkov o seji nam vašega urejanja žal ni uspelo obdelati.'''\nProsimo, poskusite znova.\nČe bo spet prišlo do napake, se [[Special:UserLogout|odjavite]] in ponovno prijavite.",
-       "session_fail_preview_html": "'''Oprostite! Zaradi izgube podatkov o seji nam vašega urejanja ni uspelo obdelati.'''\n\n''Ker ima {{SITENAME}} omogočen surovi HTML, je predogled zaradi preprečevanja napadov z JavaScriptom skrit.''\n\n'''Če gre za dobronameren poskus urejanja, vas prosimo, da poskusite znova.'''\nČe bo spet prišlo do napake, se [[Special:UserLogout|odjavite]] in ponovno prijavite.",
+       "session_fail_preview": "Oprostite! Zaradi izgube podatkov o seji nam vašega urejanja žal ni uspelo obdelati.\n\nMorda ste bili odjavljeni. <strong>Prosimo, preverite, da ste še vedno prijavljeni, in poskusite znova.</strong>\nČe še vedno ne deluje, se poskusite [[Special:UserLogout|odjaviti]] in znova prijaviti; prav tako preverite, da vaš brskalnik dovoljuje piškotke s te strani.",
+       "session_fail_preview_html": "Oprostite! Zaradi izgube podatkov o seji nam vašega urejanja ni uspelo obdelati.\n\n<em>Ker ima {{SITENAME}} omogočen surov HTML, smo predogled skrili kot previdnostni ukrep pred napadi z JavaScriptom.</em>\n\n<strong>Če gre za dobronameren poskus urejanja, vas prosimo, da poskusite znova.</strong>\nČe še vedno ne deluje, se poskusite [[Special:UserLogout|odjaviti]] in znova prijaviti; prav tako preverite, da vaš brskalnik dovoljuje piškotke s te strani.",
        "token_suffix_mismatch": "'''Vaše urejanje je bilo zavrnjeno, ker je vaš odjemalec pokvaril ločila v urejevalnem zahtevku.'''\nUrejanje je bilo zavrnjeno z namenom preprečitve okvare v besedilu strani.\nNajvečkrat je razlog uporaba hroščato spletno anonimizacijsko storitev.",
        "edit_form_incomplete": "'''Nekateri deli urejevalnega obrazca niso dosegli strežnika; prepričajte se, da so vaša urejanja neokrnjena in poskusite znova.'''",
        "editing": "Urejanje $1",
        "mergehistory-empty": "Redakcij ni moč združiti.",
        "mergehistory-done": "$3 {{PLURAL:$3|redakcija|redakciji|redakcije|redakcij}} $1 {{PLURAL:$3|smo}} spojili v [[:$2]].",
        "mergehistory-fail": "Ne morem izvesti združitev zgodovine, prosimo, ponovno preverite strani in parametre časa.",
+       "mergehistory-fail-bad-timestamp": "Časovni žig je neveljaven.",
+       "mergehistory-fail-invalid-source": "Izvorna strani je neveljavna.",
+       "mergehistory-fail-invalid-dest": "Ciljna stran je neveljavna.",
+       "mergehistory-fail-no-change": "Združevanje zgodovine ni združilo nobene redakcije. Prosimo, ponovno preverite parametre strani in časa.",
+       "mergehistory-fail-permission": "Nezadostna dovoljenja za združevanje zgodovine.",
+       "mergehistory-fail-self-merge": "Izvorna in ciljna stran sta enaki.",
+       "mergehistory-fail-timestamps-overlap": "Izvorne redakcije se prekrivajo ali pridejo po ciljnh redakcijah.",
        "mergehistory-fail-toobig": "Ne morem izvesti združitve zgodovine, saj bi moral premakniti več kot $1 {{PLURAL:$1|redakcijo|redakciji|redakcije|redakcij}}, kar je omejitev.",
        "mergehistory-no-source": "Izvirna stran $1 ne obstaja.",
        "mergehistory-no-destination": "Ciljna stran $1 ne obstaja.",
        "uploaded-script-svg": "V naloženi datoteki SVG smo našli skriptni element »$1«.",
        "uploaded-hostile-svg": "V slogovnem elementu naložene datoteke SVG smo našli nevaren CSS.",
        "uploaded-event-handler-on-svg": "Določevanje atributov za dogodke <code>$1=\"$2\"</code> v datotekah SVG ni dovoljeno.",
-       "uploaded-href-unsafe-target-svg": "V naloženi datoteki SVG smo našli href z nevarnim ciljem <code>&lt;$1 $2=\"$3\"&gt;</code>.",
+       "uploaded-href-attribute-svg": "Atributi href v datotekah SVG lahko ciljajo samo na http:// ali https://, našli smo <code>&lt;$1 $2=\"$3\"&gt;</code>.",
+       "uploaded-href-unsafe-target-svg": "Našli smo href na nevaren podatek: cilj URI <code>&lt;$1 $2=\"$3\"&gt;</code> v naloženi datoteki SVG.",
        "uploaded-animate-svg": "V naloženi datoteki SVG smo našli oznako »animate«, ki lahko spreminja href z uporabo atributa »from« <code>&lt;$1 $2=\"$3\"&gt;</code>.",
        "uploaded-setting-event-handler-svg": "Določevanje atributov za dogodke je blokirano; v naloženi datoteki SVG smo našli <code>&lt;$1 $2=\"$3\"&gt;</code>.",
        "uploaded-setting-href-svg": "Uporaba oznake »set« za določevanje atributa »href« starševskega elementa je blokirano.",
        "upload-dialog-button-done": "Končano",
        "upload-dialog-button-save": "Shrani",
        "upload-dialog-button-upload": "Naloži",
-       "upload-form-label-select-file": "Izberi datoteko",
        "upload-form-label-infoform-title": "Podrobnosti",
        "upload-form-label-infoform-name": "Ime",
        "upload-form-label-infoform-name-tooltip": "Edinstven opisen naslov datoteke, ki bo služil kot ime datoteke. Uporabljate lahko navadni jezik s presledki. Ne vključujte datotečne končnice.",
        "foreign-structured-upload-form-label-own-work-message-shared": "Izjavljam, da sem lastnik avtorskih pravic te datoteke, strinjam se z nepreklicno objavo datoteke v Wikimedijini Zbirki pod dovoljenjem [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Priznanje avtorstva-Deljenje pod enakimi pogoji 4.0] in strinjam se s [https://wikimediafoundation.org/wiki/Terms_of_Use Pogoji uporabe].",
        "foreign-structured-upload-form-label-not-own-work-message-shared": "Če niste lastnik avtorskih pravic datoteke ali jo želite objaviti pod drugačnim dovoljenje, uporabite [https://commons.wikimedia.org/wiki/Special:UploadWizard Čarovnik za nalaganje v Zbirko].",
        "foreign-structured-upload-form-label-not-own-work-local-shared": "Morda želite datoteko poskusiti naložiti na [[Special:Upload|strani za nalaganje na {{SITENAME}}]], če stran dovoljuje nalaganje datoteke pod njihovimi pravili.",
-       "foreign-structured-upload-form-2-label-intro": "Zahvaljujemo se vam za prispevanje slike za uporabo na strani {{SITENAME}}. Nadaljujte samo, če slika ustreza naslednjim pogojem:",
-       "foreign-structured-upload-form-2-label-ownwork": "V celoti mora biti <strong>vaše lastn delo</strong>, ne vzeto z interneta",
-       "foreign-structured-upload-form-2-label-noderiv": "Vsebovati <strong>ne sme delo kogar koli drugega</strong> ali biti navdahnjeno z njihove strani",
-       "foreign-structured-upload-form-2-label-useful": "Mora biti <strong>izobraževalno in uporabno</strong> za učenje drugih",
-       "foreign-structured-upload-form-2-label-ccbysa": "Mora biti <strong>primerno za večno objavo</strong> na internetu pod pogoji dovoljenja [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Priznanje avtorstva-Deljenje pod enakimi pogoji 4.0]",
-       "foreign-structured-upload-form-2-label-alternative": "Če kaj od zgoraj navedenega ni res, lahko datoteko vseeno naložite z uporabo [https://commons.wikimedia.org/wiki/Special:UploadWizard Commons Čarovnika za nalaganje], dokler je na voljo pod prostim dovoljenjem.",
-       "foreign-structured-upload-form-2-label-termsofuse": "Z nalaganjem datoteke potrjujete, da si lastite avtorske pravice datoteke, da se strinjate z nepreklicno izdajo datoteke v Wikimediino Zbirko pod pogoji dovoljenja Creative Commons Priznanje avtorstva-Deljenje pod enakimi pogoji 4.0 in da se strinjate s [https://wikimediafoundation.org/wiki/Terms_of_Use Pogoji uporabe].",
-       "foreign-structured-upload-form-3-label-question-website": "Ste sliko prenesli s spletne strani ali jo našli na iskalniku slik?",
-       "foreign-structured-upload-form-3-label-question-ownwork": "Ste sliko ustvarili (fotografirali, narisali itn.) sami?",
-       "foreign-structured-upload-form-3-label-question-noderiv": "Vsebuje (ali pa je bilo navdihnjeno) delo nekoga drugega, npr. logotip?",
-       "foreign-structured-upload-form-3-label-yes": "Da",
-       "foreign-structured-upload-form-3-label-no": "Ne",
-       "foreign-structured-upload-form-3-label-alternative": "Žal, v tem primeru orodje ne podpira nalaganja te datoteke. Vseeno jo lahko naložite z uporabo [https://commons.wikimedia.org/wiki/Special:UploadWizard Commons Čarovnika za nalaganje], dokler je na voljo pod prostim dovoljenjem.",
-       "foreign-structured-upload-form-4-label-good": "Z uporabo orodja lahko naložite izobraževalne grafike in fotografije, ki ste jih ustvarili ali posneli in ki ne vsebujejo dela v lasti nekoga drugega.",
-       "foreign-structured-upload-form-4-label-bad": "Slik, ki ste jih našli na spletnem iskalniku ali prenesli z drugih spletnih strani, ne morete naložiti.",
        "backend-fail-stream": "Ne morem pretakati datoteke $1.",
        "backend-fail-backup": "Ne morem varnostno kopirati datoteke $1.",
        "backend-fail-notexists": "Datoteka $1 ne obstaja.",
        "querypage-disabled": "Ta posebna stran je onemogočena iz zmogljivostnih razlogov.",
        "apihelp": "Pomoč za API",
        "apihelp-no-such-module": "Modula »$1« nismo našli.",
+       "apisandbox": "Peskovnik API",
+       "apisandbox-jsonly": "Za uporabo peskovnika API je zahtevan JavaScript.",
+       "apisandbox-api-disabled": "API je onemogočen na tej spletni strani.",
+       "apisandbox-intro": "Uporabite to stran za preizkušanje <strong>API spletnih storitev MediaWiki</strong>.\nOglejte si [[mw:API:Main page|dokumentacijo API]] za nadaljnje podrobnosti o uporabi API. Primer: [//www.mediawiki.org/wiki/API#A_simple_example pridobi vsebino Glavne strani]. Izberite dejanje, da si ogledate več primerov.\n\nPomnite, da čeprav je to peskovnik, bodo dejanja, izvedena na tej strani, morda spremenila wiki.",
+       "apisandbox-fullscreen": "Razširi ploščo",
+       "apisandbox-fullscreen-tooltip": "Razširi ploščo peskovnika, da bo zapolnila okno obrskalnika.",
+       "apisandbox-unfullscreen": "Prikaži stran",
+       "apisandbox-unfullscreen-tooltip": "Zmanjšaš ploščo peskovnik, da postanejo navigacijske povezave MediaWiki na voljo.",
+       "apisandbox-submit": "Izvedi zahtevo",
+       "apisandbox-reset": "Počisti",
+       "apisandbox-retry": "Poskusi znova",
+       "apisandbox-loading": "Nalaganje informacij o modulu API »$1« ...",
+       "apisandbox-load-error": "Med nalaganjem informacij o modulu API »$1« je prišlo do napake: $2",
+       "apisandbox-no-parameters": "Modul API nima parametrov.",
+       "apisandbox-helpurls": "Povezave s pomočjo",
+       "apisandbox-examples": "Primeri",
+       "apisandbox-dynamic-parameters": "Dodatni parametri",
+       "apisandbox-dynamic-parameters-add-label": "Dodaj parameter:",
+       "apisandbox-dynamic-parameters-add-placeholder": "Ime parametra",
+       "apisandbox-dynamic-error-exists": "Parameter z imenom »$1« že obstaja.",
+       "apisandbox-deprecated-parameters": "Zastareli parametri",
+       "apisandbox-fetch-token": "Samodejno izpolni žeton",
+       "apisandbox-submit-invalid-fields-title": "Nekatera polja niso veljavna",
+       "apisandbox-submit-invalid-fields-message": "Prosimo, popravite označena polja in poskusite znova.",
+       "apisandbox-results": "Rezultati",
+       "apisandbox-sending-request": "Pošiljanje zahteve API ...",
+       "apisandbox-loading-results": "Prejemanje zahteve API ...",
+       "apisandbox-results-error": "Med nalaganjem odgovora poizvedbe API je prišlo do napake: $1.",
+       "apisandbox-request-url-label": "URL zahteve:",
+       "apisandbox-request-time": "Trajanje zahteve: {{PLURAL:$1|$1 ms}}",
+       "apisandbox-results-fixtoken": "Popravite žeton in ponovno pošljite",
+       "apisandbox-results-fixtoken-fail": "Pridobivanje žetona »$1« je spodletelo.",
+       "apisandbox-alert-page": "Polja na strani niso veljavna.",
+       "apisandbox-alert-field": "Vrednost polja ni veljavna.",
        "booksources": "Viri knjig",
        "booksources-search-legend": "Išči knjižne vire",
        "booksources-search": "Išči",
        "import-nonewrevisions": "Uvozil nisem nobene redakcije (vse so bile že prisotne ali pa sem jih preskočil zaradi napak).",
        "xml-error-string": "$1 v vrstici $2, znak $3 (zlog $4): $5",
        "import-upload": "Naložite podatke XML",
-       "import-token-mismatch": "Izguba podatkov o seji.\nProsimo, poskusite znova.",
+       "import-token-mismatch": "Izguba podatkov o seji.\n\nMorda ste bili odjavljeni. <strong>Prosimo, preverite, da ste še vedno prijavljeni, in poskusite znova.</strong>\nČe še vedno ne deluje, se poskusite [[Special:UserLogout|odjaviti]] in znova prijaviti; prav tako preverite, da vaš brskalnik dovoljuje piškotke s te strani.",
        "import-invalid-interwiki": "Uvoz iz navedenega wikija ni možen.",
        "import-error-edit": "Strani »$1« nismo uvozili, ker vam ni dovoljeno, da jo urejate.",
        "import-error-create": "Strani »$1« nismo uvozili, ker vam ni dovoljeno, da jo ustvarite.",
        "expand_templates_generate_xml": "Pokaži razčlenitveno drevo XML",
        "expand_templates_generate_rawhtml": "Prikaži surovi HTML",
        "expand_templates_preview": "Predogled",
-       "expand_templates_preview_fail_html": "<em>Ker ima {{SITENAME}} omogočen surov HTML in je prišlo do izgube podatkov o seji, smo predogled skrili kot previdnostni ukrep pred napadi z JavaScriptom.</em>\n\n<strong>Če je to veljaven poskus predogleda, poskusite znova.</strong>\nČe še vedno ne deluje, se poskusite [[Special:UserLogout|odjaviti]] in znova prijaviti.",
+       "expand_templates_preview_fail_html": "<em>Ker ima {{SITENAME}} omogočen surov HTML in je prišlo do izgube podatkov o seji, smo predogled skrili kot previdnostni ukrep pred napadi z JavaScriptom.</em>\n\n<strong>Če je to veljaven poskus predogleda, poskusite znova.</strong>\nČe še vedno ne deluje, se poskusite [[Special:UserLogout|odjaviti]] in znova prijaviti; prav tako preverite, da vaš brskalnik dovoljuje piškotke s te strani.",
        "expand_templates_preview_fail_html_anon": "<em>Ker ima {{SITENAME}} omogočen surov HTML in niste prijavljeni, smo predogled skrili kot previdnostni ukrep pred napadi z JavaScriptom.</em>\n\n<strong>Če je to veljaven poskus predogleda, se [[Special:UserLogin|prijavite]] in poskusite znova.</strong>",
        "expand_templates_input_missing": "Navesti boste morali vsaj nekaj vhodnega besedila.",
        "pagelanguage": "Spremeni jezik strani",
index 25e6253..28f56b2 100644 (file)
        "recentchanges-page-added-to-category-bundled": "[[:$1]] и још {{PLURAL:$2|једна страница|$2 странице}} су додате у категорију",
        "recentchanges-page-removed-from-category": "[[:$1]] је уклоњена из категорије",
        "recentchanges-page-removed-from-category-bundled": "[[:$1]] и још {{PLURAL:$2|једна страница|$2 странице}} су уклоњене из категорије",
+       "autochange-username": "Медијавики аутоматска измена",
        "upload": "Пошаљи датотеку",
        "uploadbtn": "Пошаљи датотеку",
        "reuploaddesc": "Назад на образац за отпремање",
        "upload-dialog-button-done": "Готово",
        "upload-dialog-button-save": "Сачувај",
        "upload-dialog-button-upload": "Пошаљи",
-       "upload-form-label-select-file": "Изабери датотеку",
        "upload-form-label-infoform-title": "Детаљи",
        "upload-form-label-infoform-name": "Назив",
        "upload-form-label-infoform-description": "Опис",
        "foreign-structured-upload-form-label-own-work": "Ово је моје сопствено дело",
        "foreign-structured-upload-form-label-infoform-categories": "Категорије",
        "foreign-structured-upload-form-label-infoform-date": "Датум",
-       "foreign-structured-upload-form-2-label-ownwork": "Мора бити <strong>искључиво Ваше дело</strong>, а не скинуто са интернета",
-       "foreign-structured-upload-form-2-label-noderiv": "Не сме бити <strong>туђе дело</strong> или прерада истог",
-       "foreign-structured-upload-form-2-label-useful": "Мора бити <strong>образовна и корисна</strong> за друге",
-       "foreign-structured-upload-form-2-label-ccbysa": "Мора бити <strong>у реду да се објави заувек</strong> на интернету под лиценцом [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Ауторство-Делити под истим условима 4.0]",
-       "foreign-structured-upload-form-2-label-termsofuse": "Отпремањем ове датотеке потврђујете да сте носилац ауторских права исте и непозиво је предајте Викимедијиној остави под лиценцом Creative Commons Ауторство-Делити под истим условима 4.0 и прихватате [https://wikimediafoundation.org/wiki/Terms_of_Use услове коришћења].",
-       "foreign-structured-upload-form-3-label-question-website": "Да ли сте ову слику преузели са неког сајта или претрагом слика?",
-       "foreign-structured-upload-form-3-label-question-ownwork": "Да ли сте ви направили ову слику (сликали фотоапаратом, нацртали и сл.)?",
-       "foreign-structured-upload-form-3-label-question-noderiv": "Да ли садржи логотип или је инспирисана неким туђим делом?",
-       "foreign-structured-upload-form-3-label-yes": "Да",
-       "foreign-structured-upload-form-3-label-no": "Не",
        "backend-fail-stream": "Не могу да емитујем датотеку $1.",
        "backend-fail-backup": "Не могу да направим резерву датотеке $1.",
        "backend-fail-notexists": "Датотека $1 не постоји.",
        "lockmanager-notlocked": "Не могу да откључам „$1“ јер није закључан.",
        "lockmanager-fail-closelock": "Не могу да затворим катанац за „$1“.",
        "lockmanager-fail-deletelock": "Не могу да обришем катанац за „$1“.",
-       "lockmanager-fail-acquirelock": "Не могу да добијем катанац за „$1“.",
+       "lockmanager-fail-acquirelock": "Не могу да се закључам за „$1“.",
        "lockmanager-fail-openlock": "Не могу да отворим катанац за „$1“.",
        "lockmanager-fail-releaselock": "Не могу да ослободим катанац за „$1“.",
        "lockmanager-fail-db-bucket": "Не могу да контактирам с довољно катанаца у канти $1.",
        "protectedpages-performer": "Заштитио",
        "protectedpages-params": "Ниво заштите",
        "protectedpages-reason": "Разлог",
+       "protectedpages-submit": "Прикажи странице",
        "protectedpages-unknown-timestamp": "нема",
        "protectedpages-unknown-performer": "нема",
        "protectedtitles": "Заштићени наслови",
        "protectedtitles-summary": "На овој страници се налази списак тренутно заштићених наслова. За списак тренутно заштићених страница види [[{{#special:ProtectedPages}}|{{int:protectedpages}}]].",
        "protectedtitlesempty": "Нема заштићених наслова с овим параметрима.",
+       "protectedtitles-submit": "Прикажи наслове",
        "listusers": "Списак корисника",
        "listusers-editsonly": "Прикажи само кориснике који су уређивали",
        "listusers-creationsort": "Поређај по датуму стварања",
        "querypage-disabled": "Ова посебна страница је онемогућена ради побољшања перформанси.",
        "apihelp": "API помоћ",
        "apihelp-no-such-module": "Модул „$1“ није пронађен.",
+       "apisandbox": "API песак",
+       "apisandbox-api-disabled": "АПИ је онемогућен на овом сајту.",
+       "apisandbox-submit": "Постави захтев",
+       "apisandbox-reset": "Очисти",
+       "apisandbox-results": "Резултати",
+       "apisandbox-request-url-label": "Адреса захтева:",
        "booksources": "Штампани извори",
        "booksources-search-legend": "Тражи књижевне изворе",
        "booksources-isbn": "ISBN:",
        "wlshowhideanons": "анонимне кориснике",
        "wlshowhidepatr": "патролиране измене",
        "wlshowhidemine": "моје измене",
+       "wlshowhidecategorization": "категоризацију страница",
        "watchlist-options": "Поставке списка надгледања",
        "watching": "Надгледање…",
        "unwatching": "Прекидање надгледања…",
        "svg-long-error": "Неисправна SVG датотека: $1",
        "show-big-image": "Пуна величина",
        "show-big-image-preview": "Величина овог приказа: $1.",
-       "show-big-image-preview-differ": "Величина овог $3 прегледа за ову $2 датотеку је $1.",
+       "show-big-image-preview-differ": "Величина $3 прегледа за ову $2 датотеку је $1.",
        "show-big-image-other": "{{PLURAL:$2|Друга резолуција|Друге резолуције}}: $1.",
        "show-big-image-size": "$1 × $2 пиксела",
        "file-info-gif-looped": "петља",
        "logentry-import-upload": "$1 је {{GENDER:$2|увезао|увезла}} $3 отпремањем датотеке",
        "logentry-import-upload-details": "$1 је {{GENDER:$2|увезао|увезла}} $3 отпремањем датотеке ($4 {{PLURAL:$4|измена|измене|измена}})",
        "logentry-import-interwiki": "$1 је {{GENDER:$2|увезао|увезла}} $3 с другог викија",
-       "logentry-import-interwiki-details": "$1 је {{GENDER:$2|увезао|увезла}} $3 из $5 ($4 {{PLURAL:$4|измена|измене|измена}})",
+       "logentry-import-interwiki-details": "$1 је {{GENDER:$2|увезао|увезла}} $3 из $5 ($4 {{PLURAL:$4|1=измена|измене|измена}})",
        "logentry-merge-merge": "$1 је {{GENDER:$2|спојио|спојила}} $3 у $4 (све до измене $5)",
        "logentry-move-move": "$1 је {{GENDER:$2|преместио|преместила}} страницу $3 на $4",
        "logentry-move-move-noredirect": "$1 је {{GENDER:$2|преместио|преместила}} страницу $3 на $4 без остављања преусмерења",
index 8aa46fd..065a446 100644 (file)
@@ -28,6 +28,7 @@
        "tog-hideminor": "Sakrij manje izmene u spisku skorašnjih izmena",
        "tog-hidepatrolled": "Sakrij patrolirane izmene u spisku skorašnjih izmena",
        "tog-newpageshidepatrolled": "Sakrij patrolirane stranice sa spiska novih stranica",
+       "tog-hidecategorization": "Sakrij kategorizaciju stranica",
        "tog-extendwatchlist": "Proširi spisak nadgledanja za prikaz svih izmena, ne samo skorašnjih",
        "tog-usenewrc": "Grupni prikaz izmena svake pojedinačne stranice u skorašnjim izmenama i spisku nadgledanja",
        "tog-numberheadings": "Automatski numeriši podnaslove",
        "tog-watchlisthidebots": "Sakrij izmene botova sa spiska nadgledanja",
        "tog-watchlisthideminor": "Sakrij manje izmene sa spiska nadgledanja",
        "tog-watchlisthideliu": "Sakrij izmene prijavljenih korisnika sa spiska nadgledanja",
+       "tog-watchlistreloadautomatically": "Automatski osveži spisak nadgledanja kad god se filter izmeni (potrebna JavaScript-a)",
        "tog-watchlisthideanons": "Sakrij izmene anonimnih korisnika sa spiska nadgledanja",
        "tog-watchlisthidepatrolled": "Sakrij patrolirane izmene sa spiska nadgledanja",
+       "tog-watchlisthidecategorization": "Sakrij kategorizaciju stranica",
        "tog-ccmeonemails": "Pošalji mi primerke e-poruka koje pošaljem drugim korisnicima",
        "tog-diffonly": "Ne prikazuj sadržaj stranice ispod razlika",
        "tog-showhiddencats": "Prikaži skrivene kategorije",
        "createacct-benefit-body2": "{{PLURAL:$1|stranica|stranice}}",
        "createacct-benefit-body3": "{{PLURAL:$1|aktivni korisnik|aktivnih korisnika}}",
        "badretype": "Unete lozinke se ne poklapaju.",
+       "usernameinprogress": "Nalog za ovo korisničko ime se već pravi, molimo sačekajte.",
        "userexists": "Korisničko ime je zauzeto. Izaberite drugo.",
        "loginerror": "Greška pri prijavljivanju",
        "createacct-error": "Došlo je do greške pri otvaranju naloga",
        "resetpass_submit": "Postavi lozinku i prijavi me",
        "changepassword-success": "Vaša lozinka je uspešno promenjena.",
        "changepassword-throttled": "Previše puta ste pokušali da se prijavite.\nMolimo vas da sačekate $1 pre nego što pokušate ponovo.",
+       "botpasswords-label-cancel": "Otkaži",
+       "botpasswords-label-delete": "Obriši",
        "resetpass_forbidden": "Lozinka ne može biti promenjena",
        "resetpass-no-info": "Morate biti prijavljeni da biste pristupili ovoj stranici.",
        "resetpass-submit-loggedin": "Promeni lozinku",
        "passwordreset-emailtext-user": "{{GENDER:$1|Korisnik je zatražio|Korisnica je zatražila}} podsetnik o podacima za prijavu na vikiju {{SITENAME}} ($4).\nSledeći {{PLURAL:$3|korisnički nalog je povezan|korisnički nalozi su povezani}} s ovom e-adresom:\n\n$2\n\n{{PLURAL:$3|Privremena lozinka ističe|Privremene lozinke ističu}} za {{PLURAL:$5|jedan dan|$5 dana}}.\nPrijavite se i izaberite novu lozinku. Ako je neko drugi zahtevao ovu radnju ili ste se setili lozinke i ne želite da je menjate, zanemarite ovu poruku.",
        "passwordreset-emailelement": "Korisničko ime: \n$1\n\nPrivremena lozinka: \n$2",
        "passwordreset-emailsentemail": "Podsetnik o lozinci je poslat na vašu adresu.",
+       "passwordreset-emailsentusername": "Ako ste naveli imejl adresu prilikom registracije, biće poslat imejl za resetovanje lozinke.",
        "passwordreset-emailsent-capture": "Poslat je podsetnik preko e-pošte (prikazan dole).",
        "passwordreset-emailerror-capture": "E-poruka za resetovanje lozinke, prikazana ispod je poslata, ali slanje {{GENDER:$2|korisniku|korisnici}} nije uspelo: $1",
        "changeemail": "Promeni ili ukloni e-adresu",
        "changeemail-header": "Promenite e-adresu naloga",
+       "changeemail-passwordrequired": "Morate uneti lozinku da bi potvrdili ovu izmenu.",
        "changeemail-no-info": "Morate biti prijavljeni da biste pristupili ovoj stranici.",
        "changeemail-oldemail": "Trenutna e-adresa:",
        "changeemail-newemail": "Nova e-adresa:",
        "permissionserrorstext-withaction": "Nemate dozvolu za $2 iz {{PLURAL:$1|sledećeg|sledećih}} razloga:",
        "recreate-moveddeleted-warn": "<strong>Upozorenje: ponovo pravite stranicu koja je prethodno obrisana.</strong>\n\nRazmotrite da li je prikladno da nastavite s uređivanjem ove stranice.\nOvde je navedena istorija brisanja i premeštanja s obrazloženjem:",
        "moveddeleted-notice": "Ova stranica je obrisana.\nIstorija njenog brisanja i premeštanja nalazi se ispod:",
+       "moveddeleted-notice-recent": "Žao nam je, ova stranica je nedavno obrisana (u poslednjih 24 sata).\nOvde je navedena istorija brisanja i premeštanja s obrazloženjem.",
        "log-fulllog": "Pogledaj celu istoriju",
        "edit-hook-aborted": "Izmenu je prekinula kuka.\nNije dato nikakvo obrazloženje.",
        "edit-gone-missing": "Ne mogu da ažuriram stranicu.\nIzgleda da je obrisana.",
        "rev-suppressed-unhide-diff": "Jedna od izmena ove razlike je '''sakrivena'''.\nDetalji se nalaze u [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} istoriji sakrivanja].\nIpak možete da [$1 vidite ovu razliku] ako želite da nastavite.",
        "rev-deleted-diff-view": "Jedna od izmena ove razlike je '''obrisana'''.\nIpak možete da vidite ovu razliku; više detalja možete naći u [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} istoriji brisanja].",
        "rev-suppressed-diff-view": "Jedna od izmena ove razlike je '''sakrivena'''.\nIpak možete da vidite ovu razliku; više detalja možete naći u [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} istoriji sakrivanja].",
-       "rev-delundel": "prikaži/sakrij",
+       "rev-delundel": "promeni vidljivost",
        "rev-showdeleted": "prikaži",
        "revisiondelete": "Obriši/vrati izmene",
        "revdelete-nooldid-title": "Nema tražene izmene",
        "columns": "Kolona",
        "searchresultshead": "Pretraga",
        "stub-threshold": "Prag za oblikovanje veze kao klice ($1):",
+       "stub-threshold-sample-link": "primer",
        "stub-threshold-disabled": "Onemogućeno",
        "recentchangesdays": "Broj dana u skorašnjim izmenama:",
        "recentchangesdays-max": "Najviše $1 {{PLURAL:$1|dan|dana}}",
        "prefs-help-recentchangescount": "Podrazumeva skorašnje izmene, istorije stranica i dnevnike.",
        "prefs-help-watchlist-token2": "Ovo je tajni ključ za veb-dovod Vašeg spiska nadgledanja. \nSvako ko zna ovaj ključ biće u mogućnosti da vidi Vaša nadgledanja; stoga, ključ nemojte odavati nikome. \nAko je potrebno, ključ možete [[Special:ResetTokens|resetovati]].",
        "savedprefs": "Vaša podešavanja su sačuvana.",
+       "savedrights": "Korisnička prava za {{GENDER:$1|$1}} su sačuvana.",
        "timezonelegend": "Vremenska zona:",
        "localtime": "Lokalno vreme:",
        "timezoneuseserverdefault": "podrazumevane vrednosti ($1)",
        "grant-createeditmovepage": "Pravljenje, uređivanje i premeštanje stranica",
        "grant-delete": "Brisanje stranica, izmena i unosa u dnevnicima",
        "grant-editinterface": "Uređivanje Medijaviki imenskog prostora i korisničkih CSS/JavaScript stranica",
+       "grant-editmywatchlist": "Uređivanje vašeg spiska nadgledanja",
        "grant-editpage": "Uređivanje postojećih stranica",
+       "grant-editprotected": "Uređivanje zaštićenih stranica",
        "grant-uploadeditmovefile": "Otpremanje, zamena i premeštanje datoteka",
        "grant-uploadfile": "Slanje novih datoteka",
        "newuserlogpage": "Dnevnik novih korisnika",
        "recentchanges-label-plusminus": "Promena veličine stranice u bajtovima",
        "recentchanges-legend-heading": "'''Legenda:'''",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} ([[Special:NewPages|spisak novih stranica]])",
+       "recentchanges-submit": "Prikaži",
        "rcnotefrom": "Ispod {{PLURAL:$5|je izmena|su izmene}} od <strong>$3, $4</strong> (do <strong>$1</strong> prikazano).",
        "rclistfrom": "Prikaži nove izmene počev od $2 $3",
        "rcshowhideminor": "$1 manje izmene",
        "rcshowhidemine": "$1 moje izmene",
        "rcshowhidemine-show": "Prikaži",
        "rcshowhidemine-hide": "Sakrij",
+       "rcshowhidecategorization": "$1 kategorizaciju stranica",
+       "rcshowhidecategorization-show": "Prikaži",
+       "rcshowhidecategorization-hide": "Sakrij",
        "rclinks": "Prikaži poslednjih $1 izmena {{PLURAL:$2|prethodni dan|u poslednja $2 dana|u poslednjih $2 dana}}<br />$3",
        "diff": "razl",
        "hist": "ist",
        "recentchangeslinked-summary": "Ova posebna stranica prikazuje spisak poslednjih izmena na stranicama koje su povezane (ili članovi određene kategorije).\nStranice s [[Special:Watchlist|vašeg spiska nadgledanja]] su '''podebljane'''.",
        "recentchangeslinked-page": "Naziv stranice:",
        "recentchangeslinked-to": "Prikaži izmene stranica koje su povezane s datom stranicom",
+       "recentchanges-page-added-to-category": "[[:$1]] je dodata u kategoriju",
+       "recentchanges-page-added-to-category-bundled": "[[:$1]] i još {{PLURAL:$2|jedna stranica|$2 stranice}} su dodate u kategoriju",
+       "recentchanges-page-removed-from-category": "[[:$1]] je uklonjena iz kategorije",
+       "recentchanges-page-removed-from-category-bundled": "[[:$1]] i još {{PLURAL:$2|jedna stranica|$2 stranice}} su uklonjene iz kategorije",
+       "autochange-username": "Medijaviki automatska izmena",
        "upload": "Pošalji datoteku",
        "uploadbtn": "Pošalji datoteku",
        "reuploaddesc": "Nazad na obrazac za otpremanje",
        "upload-too-many-redirects": "Adresa sadrži previše preusmerenja",
        "upload-http-error": "Došlo je do HTTP greške: $1",
        "upload-copy-upload-invalid-domain": "Primerci otpremanja nisu dostupni na ovom domenu.",
+       "upload-dialog-title": "Otpremanje datoteka",
+       "upload-dialog-button-cancel": "Otkaži",
+       "upload-dialog-button-done": "Gotovo",
+       "upload-dialog-button-save": "Sačuvaj",
+       "upload-dialog-button-upload": "Pošalji",
+       "upload-form-label-infoform-title": "Detalji",
+       "upload-form-label-infoform-name": "Ime",
+       "upload-form-label-infoform-description": "Opis",
+       "upload-form-label-usage-filename": "Naziv datoteke",
+       "foreign-structured-upload-form-label-own-work": "Ovo je moje sopstveno delo",
+       "foreign-structured-upload-form-label-infoform-categories": "Kategorije",
+       "foreign-structured-upload-form-label-infoform-date": "Datum",
        "backend-fail-stream": "Ne mogu da emitujem datoteku $1.",
        "backend-fail-backup": "Ne mogu da napravim rezervu datoteke $1.",
        "backend-fail-notexists": "Datoteka $1 ne postoji.",
        "lockmanager-notlocked": "Ne mogu da otključam „$1“ jer nije zaključan.",
        "lockmanager-fail-closelock": "Ne mogu da zatvorim katanac za „$1“.",
        "lockmanager-fail-deletelock": "Ne mogu da obrišem katanac za „$1“.",
-       "lockmanager-fail-acquirelock": "Ne mogu da dobijem katanac za „$1“.",
+       "lockmanager-fail-acquirelock": "Ne mogu da se zaključam za „$1“.",
        "lockmanager-fail-openlock": "Ne mogu da otvorim katanac za „$1“.",
        "lockmanager-fail-releaselock": "Ne mogu da oslobodim katanac za „$1“.",
        "lockmanager-fail-db-bucket": "Ne mogu da kontaktiram s dovoljno katanaca u kanti $1.",
        "mostrevisions": "Stranice s najviše izmena",
        "prefixindex": "Sve stranice s prefiksom",
        "prefixindex-namespace": "Sve stranice s predmetkom (imenski prostor $1)",
+       "prefixindex-submit": "Prikaži",
        "prefixindex-strip": "Sakrij prefiks u spisku",
        "shortpages": "Kratke stranice",
        "longpages": "Dugačke stranice",
        "protectedpages-performer": "Zaštitio",
        "protectedpages-params": "Nivo zaštite",
        "protectedpages-reason": "Razlog",
+       "protectedpages-submit": "Prikaži stranice",
        "protectedpages-unknown-timestamp": "nema",
        "protectedpages-unknown-performer": "nema",
        "protectedtitles": "Zaštićeni naslovi",
        "protectedtitles-summary": "Na ovoj stranici se nalazi spisak trenutno zaštićenih naslova. Za spisak trenutno zaštićenih stranica vidi [[{{#special:ProtectedPages}}|{{int:protectedpages}}]].",
        "protectedtitlesempty": "Nema zaštićenih naslova s ovim parametrima.",
+       "protectedtitles-submit": "Prikaži naslove",
        "listusers": "Spisak korisnika",
        "listusers-editsonly": "Prikaži samo korisnike koji su uređivali",
        "listusers-creationsort": "Poređaj po datumu stvaranja",
        "usereditcount": "$1 {{PLURAL:$1|izmena|izmene|izmena}}",
        "usercreated": "{{GENDER:$3|je napravio|je napravila|je napravio}} dana $1 u $2",
        "newpages": "Nove stranice",
+       "newpages-submit": "Prikaži",
        "newpages-username": "Korisničko ime:",
        "ancientpages": "Najstarije stranice",
        "move": "premesti",
        "querypage-disabled": "Ova posebna stranica je onemogućena radi poboljšanja performansi.",
        "apihelp": "API pomoć",
        "apihelp-no-such-module": "Modul „$1“ nije pronađen.",
+       "apisandbox": "API pesak",
+       "apisandbox-api-disabled": "API je onemogućen na ovom sajtu.",
+       "apisandbox-submit": "Postavi zahtev",
+       "apisandbox-results": "Rezultati",
+       "apisandbox-request-url-label": "Adresa zahteva:",
        "booksources": "Štampani izvori",
        "booksources-search-legend": "Traži književne izvore",
        "booksources-isbn": "ISBN:",
        "specialloguserlabel": "Izvršilac:",
        "speciallogtitlelabel": "Cilj (naslov ili {{ns:user}}:korisničko ime):",
        "log": "Dnevnici",
+       "logeventslist-submit": "Prikaži",
        "all-logs-page": "Svi javni dnevnici",
        "alllogstext": "Skupni prikaz svih dostupnih istorija ovog vikija.\nMožete suziti prikaz odabirući vrstu istorije, korisničkog imena ili tražene stranice.",
        "logempty": "Nema pronađenih unosa u dnevniku.",
        "log-title-wildcard": "Traži naslove koji počinju s ovim tekstom",
        "showhideselectedlogentries": "Prikaži/sakrij izabrane događaje",
+       "log-edit-tags": "Uredi oznake izabranih unosa u dnevnicima",
+       "checkbox-select": "Izaberi: $1",
+       "checkbox-all": "sve",
+       "checkbox-none": "ništa",
+       "checkbox-invert": "obrni",
        "allpages": "Sve stranice",
        "nextpage": "Sledeća stranica ($1)",
        "prevpage": "Prethodna stranica ($1)",
        "cachedspecial-viewing-cached-ts": "Gledate keširanu verziju ove stranice, koja može da se razlikuje od trenutne.",
        "cachedspecial-refresh-now": "Pogledaj najnoviju.",
        "categories": "Kategorije",
+       "categories-submit": "Prikaži",
        "categoriespagetext": "{{PLURAL:$1|1=Sledeća kategorija sadrži|Sledeće kategorije sadrže}} stranice ili datoteke.\n[[Special:UnusedCategories|Nekorišćene kategorije]] nisu prikazane ovde.\nPogledajte i [[Special:WantedCategories|tražene kategorije]].",
        "categoriesfrom": "Prikaži kategorije počev od:",
        "special-categories-sort-count": "poređaj po broju",
        "activeusers-hidebots": "Sakrij botove",
        "activeusers-hidesysops": "Sakrij administratore",
        "activeusers-noresult": "Korisnik nije pronađen.",
+       "activeusers-submit": "Prikaži aktivne korisnike",
        "listgrouprights": "Prava korisničkih grupa",
        "listgrouprights-summary": "Sledi spisak korisničkih grupa na ovom vikiju, zajedno s pravima pristupa.\nPogledajte [[{{MediaWiki:Listgrouprights-helppage}}|više detalja]] o pojedinačnim pravima.",
        "listgrouprights-key": "Legenda:\n* <span class=\"listgrouprights-granted\">Dodeljeno pravo</span>\n* <span class=\"listgrouprights-revoked\">Ukinuto pravo</span>",
        "listgrouprights-namespaceprotection-restrictedto": "Prava potrebna za uređivanje",
        "listgrants-rights": "Prava",
        "trackingcategories": "Medijaviki kategorije",
+       "trackingcategories-summary": "Ova posebna stranica je spisak kategorija koje su deo Medijavikija, one se automatski ažuriraju i njihovi nazivi se mogu menjanjati uređivanjem sistemskih poruka u imenskom prostoru {{ns:8}}.",
        "trackingcategories-name": "Ime poruke",
        "trackingcategories-desc": "Koje stranice se nalaze u kategoriji",
        "noindex-category-desc": "Stranice koje u sebi imaju magičnu reč <code><nowiki>__NOINDEX__</nowiki></code>.",
        "wlheader-showupdated": "Stranice koje su izmenjene otkad ste ih poslednji put posetili su '''podebljane'''.",
        "wlnote": "Ispod {{PLURAL:$1|je poslednja izmena|su poslednje <strong>$1</strong> izmene|je poslednjih <strong>$1</strong> izmena}} u {{PLURAL:$2|prethodnom satu|prethodna <strong>$2</strong> sata|prethodnih <strong>$2</strong> sati}}, zaključno sa $3, $4.",
        "wlshowlast": "Prikaži poslednjih $1 sati, $2 dana",
+       "watchlist-hide": "Sakrij",
+       "watchlist-submit": "Prikaži",
        "wlshowtime": "Period za prikaz:",
+       "wlshowhideminor": "manje izmene",
+       "wlshowhidebots": "botove",
+       "wlshowhideliu": "registrovane korisnike",
+       "wlshowhideanons": "anonimne korisnike",
+       "wlshowhidepatr": "patrolirane izmene",
+       "wlshowhidemine": "moje izmene",
+       "wlshowhidecategorization": "kategorizaciju stranica",
        "watchlist-options": "Postavke spiska nadgledanja",
        "watching": "Nadgledanje…",
        "unwatching": "Prekidanje nadgledanja…",
        "delete-confirm": "Brisanje stranice „$1“",
        "delete-legend": "Obriši",
        "historywarning": "<strong>Upozorenje:</strong> stranica koju želite da obrišete ima istoriju sa $1 {{PLURAL:$1|izmenom|izmene|izmena}}:",
+       "historyaction-submit": "Prikaži",
        "confirmdeletetext": "Upravo ćete obrisati stranicu, uključujući i njenu istoriju.\nPotvrdite svoju nameru, da razumete posledice i da ovo radite u skladu s [[{{MediaWiki:Policy-url}}|pravilima]].",
        "actioncomplete": "Radnja je završena",
        "actionfailed": "Radnja nije uspela",
        "contributions": "{{GENDER:$1|Korisnički}} doprinosi",
        "contributions-title": "Doprinosi {{GENDER:$1|korisnika|korisnice}} $1",
        "mycontris": "Doprinosi",
+       "anoncontribs": "Doprinosi",
        "contribsub2": "Za {{GENDER:$3|$1}} ($2)",
        "contributions-userdoesnotexist": "Korisnički nalog „$1“ nije registrovan.",
        "nocontribs": "Nema izmena koje odgovaraju navedenim kriterijumima.",
        "whatlinkshere-hidelinks": "$1 veze",
        "whatlinkshere-hideimages": "$1 veze do datoteke",
        "whatlinkshere-filters": "Filteri",
+       "whatlinkshere-submit": "Idi",
        "autoblockid": "Automatsko blokiranje #$1",
        "block": "Blokiraj korisnika",
        "unblock": "Deblokiraj korisnika",
        "blockipsuccesstext": "[[Special:Contributions/$1|$1]] je {{GENDER:$1|blokiran|blokirana|blokiran}}.<br />\nBlokiranja možete da pogledate [[Special:BlockList|ovde]].",
        "ipb-blockingself": "Ovom radnjom ćete blokirati sebe! Jeste li sigurni da to želite?",
        "ipb-confirmhideuser": "Upravo ćete blokirati korisnika s uključenom mogućnošću „sakrij korisnika“. Ovim će korisničko ime biti sakriveno u svim spiskovima i izveštajima. Želite li to da uradite?",
+       "ipb-confirmaction": "Ako ste sigurni da želite nastaviti označite polje „{{int:ipb-confirm}}“ na dnu stranice.",
        "ipb-edit-dropdown": "Uredi razloge blokiranja",
        "ipb-unblock-addr": "Deblokiraj $1",
        "ipb-unblock": "Deblokiraj korisničko ime ili IP adresu",
        "import-interwiki-history": "Kopiraj sve verzije istorije za ovu stranicu",
        "import-interwiki-templates": "Uključi sve šablone",
        "import-interwiki-submit": "Uvezi",
+       "import-mapping-default": "Isto kao i izvorne stranice",
        "import-mapping-namespace": "Uvezi u imenski prostor:",
+       "import-mapping-subpage": "Uvezi kao podstranice sledeće stranice:",
        "import-upload-filename": "Naziv datoteke:",
        "import-comment": "Komentar:",
        "importtext": "Izvezite datoteku s izvornog vikija koristeći [[Special:Export|izvoz]].\nSačuvajte je na računar i pošaljite ovde.",
        "tooltip-pt-preferences": "Vaša podešavanja",
        "tooltip-pt-watchlist": "Spisak stranica koje nadgledate",
        "tooltip-pt-mycontris": "Spisak vaših doprinosa",
+       "tooltip-pt-anoncontribs": "Spisak izmena napravljenih sa ove IP adrese",
        "tooltip-pt-login": "Preporučujemo vam da se prijavite, iako to nije obavezno.",
        "tooltip-pt-logout": "Odjavite se",
        "tooltip-pt-createaccount": "Ohrabrujemo vas da otvorite nalog i prijavite se ali to nije obavezno",
        "pageinfo-category-files": "Broj datoteka",
        "markaspatrolleddiff": "Označi kao patrolirano",
        "markaspatrolledtext": "Označi stranicu kao patroliranu",
+       "markaspatrolledtext-file": "Označi ovu verziju datoteke kao patroliranu",
        "markedaspatrolled": "Označeno kao patrolirano",
        "markedaspatrolledtext": "Izabrana izmena na [[:$1]] je označena kao patrolirana.",
        "rcpatroldisabled": "Patroliranje skorašnjih izmena je onemogućeno",
        "patrol-log-page": "Dnevnik patroliranja",
        "patrol-log-header": "Ovo je dnevnik patroliranih izmena.",
        "log-show-hide-patrol": "$1 dnevnik patroliranja",
+       "log-show-hide-tag": "$1 dnevnik oznaka",
        "deletedrevision": "Obrisana stara izmena $1.",
        "filedeleteerror-short": "Greška pri brisanju datoteke: $1",
        "filedeleteerror-long": "Došlo je do grešaka pri brisanju datoteke:\n\n$1",
        "svg-long-error": "Neispravna SVG datoteka: $1",
        "show-big-image": "Puna veličina",
        "show-big-image-preview": "Veličina ovog prikaza: $1.",
+       "show-big-image-preview-differ": "Veličina $3 pregleda za ovu $2 datoteku je $1.",
        "show-big-image-other": "{{PLURAL:$2|Druga rezolucija|Druge rezolucije}}: $1.",
        "show-big-image-size": "$1 × $2 piksela",
        "file-info-gif-looped": "petlja",
        "newimages-legend": "Filter",
        "newimages-label": "Naziv datoteke (ili njen deo):",
        "newimages-showbots": "Prikaži datoteke koje su poslali botovi",
+       "newimages-hidepatrolled": "Sakrij patrolirana otpremanja",
        "noimages": "Nema ništa.",
        "ilsubmit": "Pretraži",
        "bydate": "po datumu",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|razgovor]])",
        "timezone-utc": "UTC",
        "duplicate-defaultsort": "'''Upozorenje:''' podrazumevani ključ svrstavanja „$2“ menja nekadašnji ključ „$1“.",
+       "duplicate-displaytitle": "<strong>Upozorenje:</strong> naslov za prikaz „$2“ zameniće postojeći „$1“.",
        "version": "Verzija",
        "version-extensions": "Instalirana proširenja",
        "version-skins": "Instalirane teme",
        "version-libraries": "Instalirane biblioteke",
        "version-libraries-library": "Biblioteka",
        "version-libraries-version": "Verzija",
+       "version-libraries-license": "Licenca",
+       "version-libraries-description": "Opis",
+       "version-libraries-authors": "Autori",
        "redirect": "Preusmerenje na datoteku, korisnika, stranicu ili izmenu",
        "redirect-legend": "Preusmeri na datoteku ili stranicu",
        "redirect-submit": "Idi",
        "tags-actions-header": "Radnje",
        "tags-active-yes": "Da",
        "tags-active-no": "Ne",
+       "tags-source-extension": "Deo ekstenzije",
+       "tags-source-manual": "Ručno je dodaju korisnici i botovi",
        "tags-source-none": "Van upotrebe",
        "tags-edit": "uredi",
        "tags-delete": "obriši",
        "tags-hitcount": "$1 {{PLURAL:$1|izmena|izmene|izmena}}",
        "tags-manage-no-permission": "Nemate dozvolu da menjate oznake.",
        "tags-create-heading": "Nova oznaka",
+       "tags-create-explanation": "Po podrazumevanim podešavanjima nove oznake moći će da koriste korisnici i botovi.",
        "tags-create-tag-name": "Naziv oznake:",
        "tags-create-reason": "Razlog:",
        "tags-create-submit": "Napravi",
        "tags-create-warnings-below": "Pravite novu oznaku, želite li da nastavite?",
        "tags-delete-title": "Brisanje oznaka",
        "tags-delete-explanation-initial": "Brišete oznaku „$1“ iz baze podataka.",
+       "tags-delete-explanation-warning": "Ova radnja je <strong>nepovratna</strong> i <strong>ne može se poništiti</strong>, čak ni administratori baze podataka je ne mogu poništiti. Budite sigurni da je ovo oznaka koju želite obrisati.",
        "tags-delete-reason": "Razlog:",
        "tags-delete-submit": "Nepovratno obriši ovu oznaku",
        "tags-delete-not-found": "Oznaka „$1“ ne postoji.",
        "tags-deactivate-reason": "Razlog:",
        "tags-deactivate-not-allowed": "Nije moguće deaktivirati oznaku „$1“.",
        "tags-deactivate-submit": "Dekativiraj",
+       "tags-edit-title": "Uredi oznake",
+       "tags-edit-manage-link": "Upravljaj oznakama",
        "tags-edit-existing-tags": "Postojeće oznake:",
        "tags-edit-new-tags": "Nove oznake:",
        "tags-edit-reason": "Razlog:",
        "htmlform-cloner-create": "Dodaj još",
        "htmlform-cloner-delete": "Ukloni",
        "htmlform-cloner-required": "Bar jedna vrednost je potrebna.",
+       "htmlform-title-badnamespace": "[[:$1]] nije u imenskom prostoru „{{ns:$2}}“.",
+       "htmlform-title-not-exists": "$1 ne postoji.",
        "htmlform-user-not-exists": "<strong>$1</strong> ne postoji.",
        "htmlform-user-not-valid": "<strong>$1</strong> nije ispravno korisničko ime.",
        "sqlite-has-fts": "$1 s podrškom pretrage celog teksta",
        "logentry-suppress-block": "$1 je {{GENDER:$2|blokirao|blokirala}} {{GENDER:$4|$3}} u trajanju od $5 $6",
        "logentry-suppress-reblock": "$1 je {{GENDER:$2|promenio|promenila}} podešavanja za blokiranje {{GENDER:$4|korisnika|korisnice}} {{GENDER:$4|$3}} u trajanju od $5 $6",
        "logentry-import-upload": "$1 je {{GENDER:$2|uvezao|uvezla}} $3 otpremanjem datoteke",
+       "logentry-import-upload-details": "$1 je {{GENDER:$2|uvezao|uvezla}} $3 otpremanjem datoteke ($4 {{PLURAL:$4|izmena|izmene|izmena}})",
        "logentry-import-interwiki": "$1 je {{GENDER:$2|uvezao|uvezla}} $3 s drugog vikija",
+       "logentry-import-interwiki-details": "$1 је {{GENDER:$2|увезао|увезла}} $3 из $5 ($4 {{PLURAL:$4|1=измена|измене|измена}})",
        "logentry-merge-merge": "$1 je {{GENDER:$2|spojio|spojila}} $3 u $4 (sve do izmene $5)",
        "logentry-move-move": "$1 je {{GENDER:$2|premestio|premestila}} stranicu $3 na $4",
        "logentry-move-move-noredirect": "$1 je {{GENDER:$2|premestio|premestila}} stranicu $3 na $4 bez ostavljanja preusmerenja",
        "logentry-newusers-create2": "$1 je {{GENDER:$2|otvorio|otvorila}} korisnički nalog $3",
        "logentry-newusers-byemail": "$1 je {{GENDER:$2|otvorio|otvorila}} korisnički nalog $3 i lozinka je poslata na e-poštu",
        "logentry-newusers-autocreate": "Korisnički nalog $1 je automatski {{GENDER:$2|otvoren}}",
+       "logentry-protect-move_prot": "$1 je {{GENDER:$2|premestio|premestila}} postavke zaštite sa $4 na $3",
+       "logentry-protect-unprotect": "$1 je {{GENDER:$2|skinuo|skinula}} zaštitu sa stranice $3",
+       "logentry-protect-protect": "$1 je {{GENDER:$2|zaštitio|zaštitila}} $3 $4",
+       "logentry-protect-protect-cascade": "$1 je {{GENDER:$2|zaštitio|zaštitila}} $3 $4 [prenosiva zaštita]",
+       "logentry-protect-modify": "$1 je {{GENDER:$2|promenio|promenila}} stepen zaštite za $3 $4",
+       "logentry-protect-modify-cascade": "$1 je {{GENDER:$2|promenio|promenila}} stepen zaštite za $3 $4 [prenosiva zaštita]",
        "logentry-rights-rights": "$1 je {{GENDER:$2|promenio|promenila}} članstvo grupe za $3 iz $4 u $5",
        "logentry-rights-rights-legacy": "$1 je {{GENDER:$2|promenio|promenila}} čalnstvo grupe za $3",
        "logentry-rights-autopromote": "$1 je automatski {{GENDER:$1|unapređen|unapređena}} iz $4 u $5",
        "logentry-managetags-delete": "$1 je {{GENDER:$2|obrisao|obrisala}} oznaku „$4“ (uklonjena je iz $5 {{PLURAL:$5|izmene ili dnevnika|izmena i/ili dnevnika}})",
        "logentry-managetags-activate": "$1 je {{GENDER:$2|aktivirao|aktivirala}} oznaku „$4“ za upotrebu od strane korisnika i botova",
        "logentry-managetags-deactivate": "$1 je {{GENDER:$2|deaktivirao|deaktivirala}} oznaku „$4“ za upotrebu od strane korisnika i botova",
+       "log-name-tag": "Dnevnik oznaka",
+       "log-description-tag": "Ovaj dnevnik prikazuje dodavanje/uklanjanje [[Special:Tags|oznaka]] na pojedinačne izmene ili unose u dnevnicima. Ovaj dnevnik ne prikazuje označavanje kada su ona deo uređivanja, brisanja ili neke druge radnje.",
        "rightsnone": "(nema)",
        "revdelete-summary": "opis izmene",
        "feedback-adding": "Dodajem povratnu informaciju na stranicu…",
        "feedback-bugornote": "Ako ste spremni da detaljno opišete tehnički problem, onda [$1 prijavite grešku].\nU suprotnom, poslužite se jednostavnim obrascem ispod. Vaš komentar će stajati na stranici „[$3 $2]“, zajedno s korisničkim imenom i pregledačem koji koristite.",
        "feedback-cancel": "Otkaži",
        "feedback-close": "Urađeno",
+       "feedback-external-bug-report-button": "Prijavi bag",
        "feedback-error-title": "Greška",
        "feedback-error1": "Greška: neprepoznat rezultat od API-ja",
        "feedback-error2": "Greška: uređivanje nije uspelo",
        "feedback-message": "Poruka:",
        "feedback-subject": "Naslov:",
        "feedback-submit": "Pošalji",
+       "feedback-termsofuse": "Prihvatam da pošaljem povratne informacije u skladu sa uslovima korišćenja.",
        "feedback-thanks": "Hvala! Vaša povratna informacija je postavljena na stranicu „[$2 $1]“.",
        "feedback-thanks-title": "Hvala vam!",
        "searchsuggest-search": "Pretraga",
        "pagelang-name": "Stranica",
        "pagelang-language": "Jezik",
        "pagelang-select-lang": "Izaberi jezik",
+       "pagelang-submit": "Pošalji",
        "right-pagelang": "menjanje jezika stranice",
        "action-pagelang": "promenu jezika stranice",
        "logentry-pagelang-pagelang": "$1 je {{GENDER:$2|promenio|promenila}} jezik stranice $3 iz $4 u $5.",
        "mediastatistics-header-text": "Tekstualne",
        "mediastatistics-header-executable": "Izvršne",
        "mediastatistics-header-archive": "Kompresovane",
+       "mediastatistics-header-total": "Sve datoteke",
        "json-error-syntax": "Greška u sintaksi",
        "headline-anchor-title": "Veza do ovog odeljka",
        "special-characters-group-latin": "latinica",
        "special-characters-group-thai": "tajlandski",
        "special-characters-group-lao": "laoski",
        "special-characters-group-khmer": "kmerski",
+       "mw-widgets-dateinput-no-date": "Datum nije izabran",
        "mw-widgets-dateinput-placeholder-day": "GGGG-MM-DD",
        "mw-widgets-dateinput-placeholder-month": "GGGG-MM",
        "mw-widgets-titleinput-description-new-page": "stranica još uvek ne postoji",
index f50abb1..385b6cf 100644 (file)
        "showhideselectedversions": "Témbongkeun/sumputkeun révisi nu dipilih",
        "editundo": "bolaykeun",
        "diff-empty": "(taya bédana)",
+       "diff-multi-sameuser": "({{PLURAL:$1|Hiji révisi antara|$1 révisi antara}} karya pamaké nu sarua henteu ditémbongkeun)",
        "diff-multi-manyusers": "({{PLURAL:$1|Hiji révisi antara|$1 révisi antara}} karya leuwih ti {{PLURAL:$2|pamaké|pamaké}} teu ditémbongkeun)",
        "searchresults": "Hasil maluruh",
        "searchresults-title": "Hasil nyusud \"$1\"",
        "right-blockemail": "Halangan pamaké keur ngirim Surélék",
        "right-hideuser": "Peungpeuk pamaké, tong ditingalikeun ka nulain",
        "right-ipblock-exempt": "Narabas peungpeuk IP, peungpeuk-otomatis, jeung peungpeuk rentang",
-       "right-proxyunbannable": "Abaikeun pengpeuk otomatis keur proxy",
        "right-unblockself": "buka peungpeuk sorangan",
        "right-protect": "Ngarobah hambalan protéksi jeung édit kaca anu dikonci",
        "right-editprotected": "Edit kaca anu dikonci salaku \"{{int:protect-level-sysop}}\"",
        "recentchanges-label-minor": "Ieu éditan minor",
        "recentchanges-label-bot": "Ieu parobahan dijieun ku bot",
        "recentchanges-label-unpatrolled": "Ieu éditan can karoris",
+       "recentchanges-label-plusminus": "Ukuran kaca robah sajumlah ieu bit",
        "recentchanges-legend-heading": "'''Pedaran:'''",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (tempo ogé [[Special:NewPages|béréndélan kaca anyar]])",
        "rcnotefrom": "Di handap ieu parobahan saprak <b>$2</b> (nu ditémbongkeun nepi ka <b>$1</b>).",
        "upload-options": "Pilihan muat",
        "watchthisupload": "Awaskeun ieu kaca",
        "filewasdeleted": "Ngaran koropak ieu geus di hapus. Anjeun kudu ningali ka $1 sa acan muatkeun koropak deui",
-       "upload-success-subj": "Ngamuat geus hasil",
        "upload-proto-error": "Salah protokol",
        "upload-file-error": "Kasalahan internal",
        "upload-misc-error": "Kasalahan muat anu teu kanyahoan",
        "querypage-disabled": "Ieu kaca husus ditumpurkeun ku alesan kinerja.",
        "booksources": "Sumber pustaka",
        "booksources-search-legend": "Sungsi sumber buku",
+       "booksources-search": "Paluruh",
        "booksources-text": "Di handap ieu ngabéréndélkeun tumbu ka loka-loka nu ngical buku, boh nu anyar atawa loakan, nu sugan uninga kana buku anu nuju dipilari:",
        "booksources-invalid-isbn": "ISBN-na sigana henteu bener; pariksa deui bisi aya salah salin ti sumber aslina.",
        "specialloguserlabel": "Pamaké:",
        "wlheader-showupdated": "Kaca nu robah ti panungtungan anjeun sindang ditémbongkeun kalawan '''kandel'''",
        "wlnote": "Di handap ieu mangrupa $1 {{PLURAL:$1|robahan|robahan}} ahir salila '''$2''' jam.",
        "wlshowlast": "Témbongkeun $1 jam $2 poé  ahir",
-       "watchlistall2": "sadaya",
        "watchlist-options": "Pilihan awaskeuneun",
        "watching": "Ngawaskeun...",
        "unwatching": "Eureun ngawaskeun...",
        "delete-warning-toobig": "Jujutan ieu kaca panjang pisan, leuwih ti{{PLURAL:$1|révisi|révisi}}. Dihapusna ieu kaca bisa ngaruksak jalanna pangkalan data {{SITENAME}}; sing ati-ati.",
        "rollback": "Balikkeun éditan",
        "rollbacklink": "balikkeun",
+       "rollbacklinkcount": "balikkeun $1 {{PLURAL:$1|éditan}}",
        "rollbackfailed": "Gagal malikkeun",
        "cantrollback": "Éditan teu bisa dibalikkeun; kontribusi panungtung ngarupakeun hiji-hijina panulis kaca ieu.",
        "alreadyrolled": "Teu bisa mulangkeun édit ahir [[$1]] ku [[User:$2|$2]] ([[User talk:$2|Obrolan]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]); geus aya nu ngédit atawa mulangkeun kacana.\n\nÉdit ahir ku [[User:$3|$3]] ([[User talk:$3|Obrolan]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "contributions": "Kontribusi {{GENDER:$1|pamaké}}",
        "contributions-title": "Sumbangan tulisan ti $1",
        "mycontris": "Kontribusi",
+       "anoncontribs": "Kontribusi",
        "contribsub2": "Pikeun {{GENDER:$3|$1}} ($2)",
        "contributions-userdoesnotexist": "Akun pamaké \"$1\" teu aya dina daptar.",
        "nocontribs": "Taya robahan nu kapanggih cocog jeung patokan ieu.",
        "import-logentry-upload-detail": "$1 {{PLURAL:$1|vérsi heubeul}}",
        "import-logentry-interwiki-detail": "$1 {{PLURAL:$1|vérsi heubel}} ti $2",
        "javascripttest": "Nguji JavaScript",
-       "tooltip-pt-userpage": "Kaca kontributor Anjeun",
+       "tooltip-pt-userpage": "Kaca {{GENDER:|pamaké anjeun}}",
        "tooltip-pt-anonuserpage": "Kaca pamaké pikeun IP nu ku anjeun keur diédit",
-       "tooltip-pt-mytalk": "Kaca obrolan Anjeun",
+       "tooltip-pt-mytalk": "Kaca obrolan {{GENDER:|anjeun}}",
        "tooltip-pt-anontalk": "Sawala ngeunaan éditan ti alamat IP ieu",
-       "tooltip-pt-preferences": "Préferénsi anjeun",
+       "tooltip-pt-preferences": "Préferénsi {{GENDER:|anjeun}}",
        "tooltip-pt-watchlist": "Daptar kaca nu diawaskeun ku anjeun parobahanana.",
-       "tooltip-pt-mycontris": "Daptar tulisan Anjeun",
+       "tooltip-pt-mycontris": "Béréndélan kontribusi {{GENDER:|anjeun}}",
        "tooltip-pt-login": "Leuwih hadé asup log, sanajan teu wajib",
        "tooltip-pt-logout": "Kaluar log",
        "tooltip-pt-createaccount": "Najan henteu kudu, Anjeun leuwih hadé nyieun akun sarta asup log",
        "tooltip-t-recentchangeslinked": "Anu anyar robah dina kaca-kaca anu nutumbu ti dieu",
        "tooltip-feed-rss": "Asupan RSS pikeun kaca ieu",
        "tooltip-feed-atom": "Asupan atom pikeun kaca ieu",
-       "tooltip-t-contributions": "Témbongkeun béréndélan kontribusi ti ieu kontributor",
+       "tooltip-t-contributions": "Béréndélan kontribusi ti {{GENDER:$1|ieu pamaké}}",
        "tooltip-t-emailuser": "Kirim surélék ka ieu kontributor",
        "tooltip-t-upload": "Ngunggahkeun berkas",
        "tooltip-t-specialpages": "Daptar sadaya kaca husus",
        "nocredits": "Teu aya émbaran pangajén pikeun kaca ieu.",
        "spamprotectiontitle": "Saringan spam",
        "spamprotectiontext": "Kaca nu rék disimpen dipeungpeuk ku saringan spam.\nSigana mah ieu téh alatan tumbu ka loka luar.",
+       "simpleantispam-label": "Pamariksaan anti-spam.\nAnu ieu <strong>ulah</strong> dieusian!",
        "pageinfo-title": "Émbaran pikeun \"$1\"",
        "pageinfo-header-basic": "Émbaran dasar",
        "pageinfo-header-edits": "Jujutan édit",
        "logentry-delete-delete": "$1 {{GENDER:$2|ngahapus}} kaca $3",
        "revdelete-restricted": "akses geus dibatesan ukur keur kuncén",
        "revdelete-unrestricted": "Watesan akses kuncén dihapuskeun",
+       "logentry-move-move": "$1 {{GENDER:$2|mindahkeun}} kaca $3 ka $4",
        "logentry-newusers-create": "Akun pamaké $1 jeus {{GENDER:$2|dijieun}}",
+       "logentry-upload-upload": "$1 {{GENDER:$2|ngamuat}} $3",
        "rightsnone": "(euweuh)",
        "revdelete-summary": "ringkesan ngédit",
        "feedback-cancel": "Bolay",
index 161cc19..97dd59d 100644 (file)
        "previewnote": "'''Kom ihåg att detta bara är en förhandsvisning.'''\nDina ändringar har ännu inte sparats!",
        "continue-editing": "Fortsätt redigera",
        "previewconflict": "Den här förhandsvisningen är resultatet av den\nredigerbara texten ovanför,\nså som det kommer att se ut om du väljer att spara.",
-       "session_fail_preview": "'''Vi kunde inte behandla din redigering eftersom sessionsdata gått förlorad.\nVar god försök igen.\nOm det fortfarande inte fungerar, prova att [[Special:UserLogout|logga ut]] och logga in igen.'''",
-       "session_fail_preview_html": "'''Vi kunde inte behandla din redigering eftersom sessionsdata gått förlorad.'''\n\n''Eftersom {{SITENAME}} har aktiverat rå HTML, så döljs förhandsvisningen som en förebyggande säkerhetsåtgärd mot JavaScript-attacker.''\n\n'''Om detta är ett försök att göra en rättmätig redigering, så försök igen.\nOm det fortfarande inte fungerar, pröva att [[Special:UserLogout|logga ut]] och logga in igen.'''",
+       "session_fail_preview": "Vi kunde inte behandla din redigering eftersom sessionsdata gått förlorad.\nDu kanske har loggats ut. Var god se till att du fortfarande är inloggad och försök igen.\nOm det fortfarande inte fungerar, prova att [[Special:UserLogout|logga ut]] och logga in igen, samt kontrollera att din webbläsare tillåter kakor från denna webbplats.",
+       "session_fail_preview_html": "Vi kunde inte behandla din redigering eftersom sessionsdata gått förlorad.\n\n<em>Eftersom {{SITENAME}} har aktiverat rå HTML, så döljs förhandsvisningen som en förebyggande säkerhetsåtgärd mot JavaScript-attacker.</em>\n\n<strong>Om detta är ett försök att göra en rättmätig redigering, så försök igen.</strong>\nOm det fortfarande inte fungerar, pröva att [[Special:UserLogout|logga ut]] och logga in igen, samt kontrollera att din webbläsare tillåter kakor från denna webbplats.",
        "token_suffix_mismatch": "'''Din redigering har stoppats eftersom din klient har ändrat tecknen\ni redigerings-nyckeln. Redigeringen stoppades för att förhindra att sidtexten skadas.\nDetta händer ibland om du använder buggiga webbaserade anonyma proxytjänster.'''",
        "edit_form_incomplete": "'''Vissa delar av redigeringen kunde inte nå servern, dubbelkolla att dina ändringar är intakta och försök igen.'''",
        "editing": "Redigerar $1",
        "mergehistory-empty": "Inga versioner av sidorna kan sammanfogas.",
        "mergehistory-done": "$3 {{PLURAL:$3|version|versioner}} av $1 {{PLURAL:$3|har}} infogats i [[:$2]].",
        "mergehistory-fail": "Historikerna kunde inte sammanfogas, kontrollera de sidor och den sidversion som du valt.",
+       "mergehistory-fail-bad-timestamp": "Tidsstämpeln är ogiltig.",
+       "mergehistory-fail-invalid-source": "Källsidan är ogiltig.",
+       "mergehistory-fail-invalid-dest": "Målsidan är ogiltig.",
+       "mergehistory-fail-no-change": "Historiksammanfogningen kunde inte sammanfoga några versioner. Kontrollera sidan och tidsparametrarna.",
+       "mergehistory-fail-permission": "Otillräcklig behörighet för att sammanfoga historiker.",
+       "mergehistory-fail-self-merge": "Käll- och målsidorna är samma.",
+       "mergehistory-fail-timestamps-overlap": "Källversioner överlappar eller kommer efter målsversionerna.",
        "mergehistory-fail-toobig": "Kunde inte utföra historiksammanslagningen då fler än maxgränsen på $1 {{PLURAL:$1|version|versioner}} skulle ha flyttats.",
        "mergehistory-no-source": "Källsidan $1 finns inte.",
        "mergehistory-no-destination": "Målsidan $1 finns inte.",
        "uploaded-script-svg": "Hittade skriptelementet \"$1\" i den uppladdade SVG-filen.",
        "uploaded-hostile-svg": "Hittade osäker CSS i den uppladdade filens stilelement.",
        "uploaded-event-handler-on-svg": "Att ange event-handler-attribut <code>$1=\"$2\"</code> är inte tillåtet i SVG-filer.",
-       "uploaded-href-unsafe-target-svg": "Hittade href till ett osäkert mål <code>&lt;$1 $2=\"$3\"&gt;</code> i den uppladdade SVG-filen.",
+       "uploaded-href-attribute-svg": "href-attribut i SVG-filer tillåts endast att länka till http:// eller https:// som återfinns <code>&lt;$1 $2=\"$3\"&gt;</code>.",
+       "uploaded-href-unsafe-target-svg": "Hittade href till osäker data: URI-mål <code>&lt;$1 $2=\"$3\"&gt;</code> i den uppladdade SVG-filen.",
        "uploaded-animate-svg": "Hittades taggen \"animate\" som kan ändra href med hjälp av attributen \"from\" <code>&lt;$1 $2=\"$3\"&gt;</code> i den uppladdade SVG-filen.",
        "uploaded-setting-event-handler-svg": "Att ange event-handler-attribut är blockerat. Hittade <code>&lt;$1 $2=\"$3\"&gt;</code> i den uppladdade SVG-filen.",
        "uploaded-setting-href-svg": "Användning av taggen \"set\" för att lägga till attributen \"href\" till överordnade element blockeras.",
        "upload-dialog-button-done": "Klar",
        "upload-dialog-button-save": "Spara",
        "upload-dialog-button-upload": "Ladda upp",
-       "upload-form-label-select-file": "Välj fil",
        "upload-form-label-infoform-title": "Detaljer",
        "upload-form-label-infoform-name": "Namn",
        "upload-form-label-infoform-name-tooltip": "En unik beskrivande titel för filen, som kommer att fungera som ett filnamn. Du kan använda klarspråk med mellanslag. Ta inte med filändelsen.",
        "foreign-structured-upload-form-label-own-work-message-shared": "Jag intygar att jag äger upphovsrätten för denna fil och samtycker till att oåterkalleligen släppa filen på Wikimedia Commons under licensen \n[https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0] och jag accepterar [https://wikimediafoundation.org/wiki/Terms_of_Use villkoren för användning].",
        "foreign-structured-upload-form-label-not-own-work-message-shared": "Om du inte äger upphovsrätten för denna fil eller om du önskar att släppa den under en annan licens bör du överväga att använda [https://commons.wikimedia.org/wiki/Special:UploadWizard uppladdningsguiden på Commons].",
        "foreign-structured-upload-form-label-not-own-work-local-shared": "Du kanske skulle vilja prova att använda [[Special:Upload|uppladdningssidan på {{SITENAME}}]] om webbplatsens policys tillåter att denna fil laddas upp.",
-       "foreign-structured-upload-form-2-label-intro": "Tack för att du donera en bild för att användas på {{SITENAME}}. Du bör endast fortsätta om den uppfyller flera villkor:",
-       "foreign-structured-upload-form-2-label-ownwork": "Den måste vara helt och hållet <strong>din egen skapelse</strong>, inte bara tagen från Internet",
-       "foreign-structured-upload-form-2-label-noderiv": "Den får inte innehålla <strong>något verk av någon annan</strong>, eller inspirerats av dem",
-       "foreign-structured-upload-form-2-label-useful": "Den bör vara <strong>pedagogisk och användbar</strong> för att undervisa andra",
-       "foreign-structured-upload-form-2-label-ccbysa": "Den måste vara <strong>OK att publicera för evigt</strong> på Internet under [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Erkännande-DelaLika 4.0]-licensen",
-       "foreign-structured-upload-form-2-label-alternative": "Om inte alla av ovanstående är stämmer in, kan du fortfarande ha möjlighet att ladda upp denna fil med hjälp av [https://commons.wikimedia.org/wiki/Special:UploadWizard Commons Upload Wizard], så länge den är tillgänglig under en fri licens.",
-       "foreign-structured-upload-form-2-label-termsofuse": "Genom att ladda upp filen bekräftar du att du äger upphovsrätten till denna fil, samt att du samtycker till att oåterkalleligt släppa denna fil till Wikimedia Commons under Creative Commons Erkännande-DelaLika 4.0-licensen, samt att du samtycker till Wikimedias  [https://wikimediafoundation.org/wiki/Terms_of_Use användarvilkor].",
-       "foreign-structured-upload-form-3-label-question-website": "Laddade du ner den här bilden från en webbplats eller hittade du den genom en bildsökning?",
-       "foreign-structured-upload-form-3-label-question-ownwork": "Har du skapat denna bild (tagit bilden, skissat, ritat, etc.) själv?",
-       "foreign-structured-upload-form-3-label-question-noderiv": "Innehåller den, eller är den inspirerade av arbete som ägs av någon annan, som t.ex. en logotyp?",
-       "foreign-structured-upload-form-3-label-yes": "Ja",
-       "foreign-structured-upload-form-3-label-no": "Nej",
-       "foreign-structured-upload-form-3-label-alternative": "Tyvärr har detta verktyg i detta fall inte stöd för att ladda upp den här filen. Du kan fortfarande ladda upp den med hjälp av [https://commons.wikimedia.org/wiki/Special:UploadWizard Commons uppladdningsguide] så länge den är tillgänglig under en fri licens.",
-       "foreign-structured-upload-form-4-label-good": "Med detta verktyg kan du ladda upp pedagogiska bilder som du har skapat och fotografier som du har tagit, som inte innehåller verk som någon annan äger.",
-       "foreign-structured-upload-form-4-label-bad": "Du kan inte ladda upp bilder som hittats på en sökmotor eller har laddats ned från andra webbplatser.",
        "backend-fail-stream": "Kunde inte strömma filen $1.",
        "backend-fail-backup": "Kunde inte säkerhetskopiera filen ''$1''.",
        "backend-fail-notexists": "Filen $1 finns inte.",
        "querypage-disabled": "Den här specialsidan är inaktiverad av prestandaskäl.",
        "apihelp": "API-hjälp",
        "apihelp-no-such-module": "Modulen ”$1” hittades inte",
+       "apisandbox": "API-sandlåda",
+       "apisandbox-jsonly": "JavaScript krävs för att använda API-sandlådan.",
+       "apisandbox-api-disabled": "API är inaktiverat på denna webbplats.",
+       "apisandbox-intro": "Använd den här sidan för att experimentera med <strong>MediaWikis API för webbtjänster</strong>.\nSe [[mw:API:Main page|API-dokumentationen]] för ytterligare detaljer kring API-användningen. Exempel: [//www.mediawiki.org/wiki/API#A_simple_example få innehållet från en huvudsida]. Välj en handling för att se fler exempel.\n\nObservera att även om detta är en sandlåda kan handlingar du utför på denna sida påverka wikin.",
+       "apisandbox-fullscreen": "Utvidga panel",
+       "apisandbox-fullscreen-tooltip": "Utvidga sandlådspanelen för att fylla webbläsarens fönster.",
+       "apisandbox-unfullscreen": "Visa sida",
+       "apisandbox-unfullscreen-tooltip": "Förminska sandlådspanelen så MediaWikis navigeringslänkar syns.",
+       "apisandbox-submit": "Utför begäran",
+       "apisandbox-reset": "Rensa",
+       "apisandbox-retry": "Försök igen",
+       "apisandbox-loading": "Läser in information för API-modulen \"$1\"...",
+       "apisandbox-load-error": "Ett fel uppstod när information för API-modulen \"$1\" lästes in: $2",
+       "apisandbox-no-parameters": "Denna API-modul har inga parametrar.",
+       "apisandbox-helpurls": "Hjälplänkar",
+       "apisandbox-examples": "Exempel",
+       "apisandbox-dynamic-parameters": "Ytterligare parametrar",
+       "apisandbox-dynamic-parameters-add-label": "Lägg till parameter:",
+       "apisandbox-dynamic-parameters-add-placeholder": "Parameternamn",
+       "apisandbox-dynamic-error-exists": "En parameter som heter \"$1\" finns redan.",
+       "apisandbox-deprecated-parameters": "Föråldrade parametrar",
+       "apisandbox-submit-invalid-fields-title": "En del fält är ogiltiga",
+       "apisandbox-submit-invalid-fields-message": "Korrigera de markerade fälten och försök igen.",
+       "apisandbox-results": "Resultat",
+       "apisandbox-sending-request": "Skickar API-begäran...",
+       "apisandbox-loading-results": "Hämtar API-resultat...",
+       "apisandbox-request-url-label": "Begärd URL:",
+       "apisandbox-request-time": "Tid för begäran: {{PLURAL:$1|$1 ms}}",
+       "apisandbox-alert-page": "Fälten på denna sida är inte giltiga.",
+       "apisandbox-alert-field": "Värdet i detta fält är inte giltigt.",
        "booksources": "Bokkällor",
        "booksources-search-legend": "Sök efter bokkällor",
        "booksources-search": "Sök",
        "import-nonewrevisions": "Inga sidversioner importerades (alla var antingen redan där eller hoppades över p.g.a. fel).",
        "xml-error-string": "$1 på rad $2, kolumn $3 (byte $4): $5",
        "import-upload": "Ladda upp XML-data",
-       "import-token-mismatch": "Sessionsdata har förlorats. Var god pröva igen.",
+       "import-token-mismatch": "Sessionsdata har förlorats.\n\nDu kanske har loggats ut. <strong>Var god se till att du fortfarande är inloggad och försök igen.</strong>\nOm det fortfarande inte fungerar, prova att [[Special:UserLogout|logga ut]] och logga in igen, samt kontrollera att din webbläsare tillåter kakor från denna webbplats.",
        "import-invalid-interwiki": "Kan inte importera från den angivna wikin.",
        "import-error-edit": "Sidan \"$1\" blev inte importerad eftersom du inte har tillåtelse att redigera den.",
        "import-error-create": "Sidan \"$1\" blev inte importerad eftersom du inte har tillåtelse att skapa den.",
        "expand_templates_generate_xml": "Visa parseträd som XML",
        "expand_templates_generate_rawhtml": "Visa rå HTML",
        "expand_templates_preview": "Förhandsvisning",
-       "expand_templates_preview_fail_html": "<em>Eftersom {{SITENAME}} har rå HTML aktiverat och det uppstod en förlust av sessionsdata har förhandsgranskningen dolts som en försiktighetsåtgärd för att skydda mot JavaScript-attacker.</em>\n\n<strong>Om detta är ett äkta försök att förhandsgranska sidan, vänligen försök igen.</strong>\nOm det fortfarande inte fungerar, försök att [[Special:UserLogout|logga ut]] och sedan logga in igen.",
+       "expand_templates_preview_fail_html": "<em>Eftersom {{SITENAME}} har rå HTML aktiverat och det uppstod en förlust av sessionsdata har förhandsgranskningen dolts som en försiktighetsåtgärd för att skydda mot JavaScript-attacker.</em>\n\n<strong>Om detta är ett äkta försök att förhandsgranska sidan, vänligen försök igen.</strong>\nOm det fortfarande inte fungerar, försök att [[Special:UserLogout|logga ut]] och sedan logga in igen, samt kontrollera att din webbläsare tillåter kakor från denna webbplats.",
        "expand_templates_preview_fail_html_anon": "<em>Eftersom {{SITENAME}} har rå HTML aktiverat och du inte är inloggad har förhandsgranskningen dolts som en försiktighetsåtgärd för att skydda mot JavaScript-attacker.</em>\n\n<strong>Om detta är ett äkta försök att förhandsgranska sidan, vänligen [[Special:UserLogin|logga in]] och försök igen.</strong>",
        "expand_templates_input_missing": "Du måste åtminstone ange lite inmatningstext.",
        "pagelanguage": "Ändra sidans språk",
index b08fae9..f31ac1b 100644 (file)
        "print": "Drukuj",
        "view": "Podglůnd",
        "view-foreign": "Uobejrzij we {{grammar:MS.lp|$1}}",
-       "edit": "Sprowiyj",
+       "edit": "Sprowjej",
        "create": "Stwůrz",
        "create-local": "Wkludź lokalny uopis",
        "editthispage": "Sprowjej ta zajta",
        "newmessageslinkplural": "{{PLURAL:$1|jedno nowina|999=nowiny}}",
        "newmessagesdifflinkplural": "{{PLURAL:$1|uostatńe sprowjyńe|999=uostatńe sprowjyńa}}",
        "youhavenewmessagesmulti": "Mosz nowe powjadůmjyńa: $1",
-       "editsection": "Sprowiyj",
+       "editsection": "Sprowjej",
        "editold": "sprowjej",
        "viewsourceold": "pokoż zdrzůdło",
        "editlink": "sprowjej",
        "createaccountreason": "Kůmyntorz:",
        "createacct-reason": "Powůd:",
        "createacct-reason-ph": "Pojakymu tworzisz nowe kůnta",
-       "createacct-captcha": "Zicherkontrola",
-       "createacct-imgcaptcha-ph": "Wszkryflej tekst, kery widoć powyżyj",
        "createacct-submit": "Twůrz kůnto",
        "createacct-another-submit": "Twůrz inksze kůnto",
        "createacct-benefit-heading": "{{grammar:B.lp|{{SITENAME}}}} tworzům perzůny take kej Ty.",
        "passwordreset-emailtitle": "Kůnto na {{GRAMMAR:MS.lp|{{SITENAME}}}}",
        "passwordreset-emailtext-ip": "Ftoś (cheba Ty, s IP $1)\npado, aże chce informacyji lo konta do {{GRAMMAR:MS.lp{{SITENAME}}}} ($4).\nZe tym ausdrukym sům powjůnzane kůnta:\n$2\n\n{{PLURAL:$3|Tymczasowygo hasła|Tymczasowych hasył}} możno użyć we {{PLURAL:$5|jedyn dźyń|$5 dńi}}.\n\nJak chćołżeś gynał to zrobjyć, to zaloguj śe terozki a podej swoje hasło.\n\nJak ftoś inkszy chćoł nowe hasło abo jak Ci śe przipůmńoło stare a ńy chcysz nowygo, to zignoruj to a używej starygo hasła.",
        "passwordreset-emailelement": "Mjano sprowjorza: \n$1\n\nTymczasowe hasło: \n$2",
-       "passwordreset-emailsent": "E-brif posłany.",
+       "passwordreset-emailsentemail": "E-brif posłany.",
        "passwordreset-emailsent-capture": "E-brif posłony, kerego widać niżej.",
        "passwordreset-emailerror-capture": "Ńy udoło śe posłać wjadomości lo {{GENDER:$2|używocza|używoczki}}: $1",
        "changeemail": "Pomjyno ausdruka e-mail",
        "right-blockemail": "Zablokuj użytkowńikowi posyłańy e-brifůw",
        "right-hideuser": "Zablokuj mjano użytkowńika i schrůń to przed publicznym dostympym",
        "right-ipblock-exempt": "Uobejdź zawarća uod sprowjyń do IP, autozawarća i zawarća zakresůw",
-       "right-proxyunbannable": "Uobejdź autůmatyczne zawarća uod sprowjyń do proxy",
        "right-protect": "Zmjyń poźůmy zawarć i sprowjej zawarte zajty",
        "right-editprotected": "Sprowjej zawarte zajty (ze zawarćym kaskadowym)",
        "right-editinterface": "Sprowjej interfejs użytkowńika",
        "watchthisupload": "Dowej pozůr na ta zajta",
        "filewasdeleted": "Plik uo takym mjańy juž bůu sam wćepany, ale zostou wyćepńjynty. Ńim wćepńeš go zaś, sprowdź $1.",
        "filename-bad-prefix": "Mjano plika, kery wćepujesz, zaczyno śe uod '''\"$1\"''' &ndash; je to mjano nojczynśćy przipisywane autůmatyczńy bez cyfrowe fotoaparaty, a  ńy dowo uůno żodnych informacyji uo zawartośći plika. Proszymy cobyś nadoł plikowi inksze, lepij zrozůmjałe mjano.",
-       "upload-success-subj": "Wćepańe plika udouo śe",
        "upload-proto-error": "Ńyprowidłowy protokůł",
        "upload-proto-error-text": "Zdalne přesůuańy plikůw wymago podańo adresu URL kery začyno śe na <code>http://</code> abo <code>ftp://</code>.",
        "upload-file-error": "Wewnyntřny feler",
        "movelogpagetext": "Uoto lista zajtůw, kere uostatńo zostouy přećepane.",
        "movereason": "Czymu:",
        "revertmove": "cofej",
-       "delete_and_move": "Wyćep i przećep",
        "delete_and_move_text": "== Przećepańy wymogo wyćepańo inkszyj zajty ==\nZajta docelowo „[[:$1]]” już sam jest.\nCzy chcysz jům wyćepać, coby zrobić plac do przećepywanej zajty?",
        "delete_and_move_confirm": "Toć, wyćep zajta",
        "delete_and_move_reason": "Wyćepano coby zrobić plac do přećepywanyj zajty",
index 23253ef..f183f40 100644 (file)
        "pool-servererror": "ಪೂಲ್ ಕೌಂಟರ್ ಸೇವೆ ತಿಕೊಂದಿದ್ದಿ ($1).",
        "poolcounter-usage-error": "ಬಳಕೆದ ದೋಸೊ: $1",
        "aboutsite": "{{SITENAME}} ದ ಬಗೆಟ್",
-       "aboutpage": "Project:ಬಗೆಟ್",
+       "aboutpage": "Project:ಬಗೆಟ್ಟ್",
        "copyright": "ವಿಸೇಸವಾದ್ ಪಂಡ್‍ಜಂಡ ಉಂದು \"$1\" ಈ ಕಾಪಿರೈಟ್‌ಡ್ ಲಭ್ಯವುಂಡು.",
        "copyrightpage": "{{ns:project}}:ಕೃತಿ ಸ್ವಾಮ್ಯತೆಲು",
        "currentevents": "ಇತ್ತೆದ ಸಂಗತಿಲು",
        "watch": "ತೂಲೆ",
        "watchthispage": "ಈ ಪುಟೊನು ತೂಲೆ",
        "unwatch": "ವೀಕ್ಷಣಾಪಟ್ಟಿರ್ದ್ ದೆಪ್ಪು",
-       "watchlistall2": "ಪೂರ",
        "watchlist-options": "ವೀಕ್ಷಣಾಪಟ್ಟಿ ಆಯ್ಕೆಲು",
        "watching": "ವೀಕ್ಷಣಾಪಟ್ಟಿಗ್ ಸೇರ್ಪಾವೊಂದುಂಡು...",
        "unwatching": "ವೀಕ್ಷಣಾಪಟ್ಟಿರ್ದ್ ದೆತ್ತೊಂದುಂಡು...",
        "contributions": "{{$1ಸದಸ್ಯೆರ್ನ}}ಕಾಣಿಕೆಲು",
        "contributions-title": "$1 ಗ್ ಸದಸ್ಯೆರ್ನ ಕಾಣಿಕೆ",
        "mycontris": "ಎನ್ನ ಕಾನಿಕೆಲು",
+       "anoncontribs": "ಕಾನಿಕೆಲು",
        "contribsub2": "$1 ($2) ಗ್",
        "uctop": " (ಮಿತ್ತ್)",
        "month": "ಈ ತಿಂಗೊಲುರ್ದ್ (ಬೊಕ್ಕ ದುಂಬುದ):",
        "allmessagesname": "ಪುದರ್",
        "thumbnail-more": "ಮಲ್ಲೆ ಮಲ್ಪುಲೆ",
        "thumbnail_error": "ಮುನ್ನೋಟ ಚಿತ್ರೊನು ಸೃಷ್ಟಿ ಮನ್ಪುನಗ ದೋಷ: $1",
-       "tooltip-pt-userpage": "ಎನ್ನ ಸದಸ್ಯ ಪುಟೊ",
-       "tooltip-pt-mytalk": "ಎನ್ನ ಚರ್ಚೆ ಪುಟೊ",
-       "tooltip-pt-preferences": "ಎನ್ನ ಇಸ್ಟೊಲು",
+       "tooltip-pt-userpage": "{{GENDER:|ಎನ್ನ ಸದಸ್ಯ}} ಪುಟೊ",
+       "tooltip-pt-mytalk": "{{GENDER:|ಎನ್ನ}} ಚರ್ಚೆ ಪುಟೊ",
+       "tooltip-pt-preferences": "{{GENDER:|ಎನ್ನ}} ಇಸ್ಟೊಲು",
        "tooltip-pt-watchlist": "ಈರ್ ಬದಲಾವಣೆಗಾದ್ ನಿಗಾ ದೀತಿನಂಚಿನ ಪುಟೊಲೆನ ಪಟ್ಟಿ",
-       "tooltip-pt-mycontris": "ಎನ್ನ ಕಾನಿಕೆಲೆ ಪಟ್ಟಿ",
+       "tooltip-pt-mycontris": "{{GENDER:|ಎನ್ನ}} ಕಾನಿಕೆಲೆ ಪಟ್ಟಿ",
        "tooltip-pt-login": "ಈರ್ ಲಾಗಿನ್ ಆವೊಡುಂದು ಕೇನೊಂದುಲ್ಲೊ, ಆಂಡ ಉಂದು ದಾಲ ಕಡ್ಡಾಯ ಅತ್ತ್.",
        "tooltip-pt-logout": "ಲಾಗ್ ಔಟ್",
        "tooltip-pt-createaccount": "ನಿಕುಲು ಪೊಸ ಖಾತೆ ಸುರುಮಾಂತ್‍ದ್ ಲಾಗಿನ್ ಆಪುನೈನ್ ಪ್ರೋತ್ಸಾಹಿಸವೊ, ಆಂಡಲಾ ಉಂದು ಕಡ್ಡಾಯ ಅತ್ತ್.",
        "tooltip-t-recentchangeslinked": "ಈ ಪುಟೊಡ್ದ್ ಸಂಪರ್ಕೊ ಉಪ್ಪುನಂಚಿನ ಪುಟೊಡ್ ಇಂಚಿಪೊದ ಬದಲಾವಣೆಲು",
        "tooltip-feed-rss": "ಈ ಪುಟೊಗು ಆರ್.ಎಸ್.ಎಸ್ ಫೀಡ್",
        "tooltip-feed-atom": "ಈ ಪುಟೊಗು ಆಟಮ್ ಫೀಡ್ ಮಲ್ಪುಲೆ",
-       "tooltip-t-contributions": "ಈ ಸದಸ್ಯೆರ್ನ ಕಾಣಿಕೆದ ಪಟ್ಟಿನ್ ತೋಜಾವು",
+       "tooltip-t-contributions": "{{GENDER:$1|ಈ ಬಳಕೆದಾರೆರ್}}ಈ ಸದಸ್ಯೆರ್ನ ಕಾಣಿಕೆದ ಪಟ್ಟಿನ್ ತೋಜಾವು",
        "tooltip-t-emailuser": "ಈ ಸದಸ್ಯೆರೆಗ್ ಇ-ಮೇಲ್ ಕಡಪುಡ್ಲೆ",
        "tooltip-t-upload": "ಫೈಲನ್ ಅಪ್ಲೋಡ್ ಮಲ್ಪುಲೆ",
        "tooltip-t-specialpages": "ಪೂರ ಪುಟೊಲೆನ ವಿಸೇಸೊ ಪಟ್ಟಿ",
index 355e607..5620cbb 100644 (file)
        "unblock": "ปลดบล็อกผู้ใช้",
        "blockip": "บล็อกผู้ใช้",
        "blockip-legend": "บล็อกผู้ใช้",
-       "blockiptext": "à¹\83à¸\8aà¹\89à¹\81à¸\9aà¸\9aà¸\94à¹\89าà¸\99ลà¹\88าà¸\87à¹\80à¸\9eืà¹\88อà¸\9aลà¹\87อà¸\81สิà¸\97à¸\98ิà¹\80à¸\82à¹\89าà¸\96ึà¸\87à¸\81ารà¹\80à¸\82ียà¸\99à¸\82อà¸\87à¹\80ลà¸\82à¸\97ีà¹\88อยูà¹\88à¹\84อà¸\9eีหรือà¸\8aืà¹\88อà¸\9cูà¹\89à¹\83à¸\8aà¹\89à¹\82à¸\94ยà¹\80à¸\88าะà¸\88à¸\87 à¸\81ารà¸\9aลà¹\87อà¸\81à¸\99ีà¹\89à¸\84วรà¸\94ำà¹\80à¸\99ิà¸\99à¸\81ารà¹\80à¸\9eืà¹\88อà¸\9bà¹\89อà¸\87à¸\81ัà¸\99à¸\81ารà¸\81à¹\88อà¸\81วà¸\99à¹\80à¸\97à¹\88าà¸\99ัà¹\89à¸\99 à¹\81ละà¹\83หà¹\89สอà¸\94à¸\84ลà¹\89อà¸\87à¸\81ัà¸\9a[[{{MediaWiki:Policy-url}}|à¸\99à¹\82ยà¸\9aาย]]\nà¸\81รอà¸\81à¹\80หà¸\95ุà¸\9cลà¹\82à¸\94ยà¹\80à¸\88าะà¸\88à¸\87à¸\94à¹\89าà¸\99ลà¹\88าà¸\87 (à¹\80à¸\8aà¹\88à¸\99 à¸­à¹\89าà¸\87à¸\96ึà¸\87หà¸\99à¹\89าà¸\97ีà¹\88à¸\96ูà¸\81à¸\81à¹\88อà¸\81วà¸\99)",
+       "blockiptext": "à¹\83à¸\8aà¹\89à¹\81à¸\9aà¸\9aà¸\94à¹\89าà¸\99ลà¹\88าà¸\87à¹\80à¸\9eืà¹\88อà¸\9aลà¹\87อà¸\81à¸\81ารà¹\80à¸\82à¹\89าà¸\96ึà¸\87à¸\81ารà¹\80à¸\82ียà¸\99à¸\82อà¸\87à¹\80ลà¸\82à¸\97ีà¹\88อยูà¹\88à¹\84อà¸\9eีหรือà¸\8aืà¹\88อà¸\9cูà¹\89à¹\83à¸\8aà¹\89à¹\82à¸\94ยà¹\80à¸\88าะà¸\88à¸\87 à¸\81ารà¸\9aลà¹\87อà¸\81à¸\99ีà¹\89à¸\84วรà¸\94ำà¹\80à¸\99ิà¸\99à¸\81ารà¹\80à¸\9eืà¹\88อà¸\9bà¹\89อà¸\87à¸\81ัà¸\99à¸\81ารà¸\81à¹\88อà¸\81วà¸\99à¹\80à¸\97à¹\88าà¸\99ัà¹\89à¸\99 à¹\81ละà¹\83หà¹\89สอà¸\94à¸\84ลà¹\89อà¸\87à¸\81ัà¸\9a[[{{MediaWiki:Policy-url}}|à¸\99à¹\82ยà¸\9aาย]]\nà¸\81รอà¸\81à¹\80หà¸\95ุà¸\9cลà¹\82à¸\94ยà¹\80à¸\88าะà¸\88à¸\87à¸\94à¹\89าà¸\99ลà¹\88าà¸\87 (à¹\80à¸\8aà¹\88à¸\99 à¸­à¹\89าà¸\87à¸\96ึà¸\87หà¸\99à¹\89าà¸\97ีà¹\88à¸\96ูà¸\81à¸\81à¹\88อà¸\81วà¸\99)\nà¸\84ุà¸\93สามารà¸\96à¸\9aลà¹\87อà¸\81à¸\8aà¹\88วà¸\87à¹\84อà¸\9eีà¹\84à¸\94à¹\89à¹\82à¸\94ยà¹\83à¸\8aà¹\89วาà¸\81ยสัมà¸\9eัà¸\99à¸\98à¹\8c CIDR à¸\8aà¹\88วà¸\87à¹\83หà¸\8dà¹\88à¸\97ีà¹\88สุà¸\94à¸\97ีà¹\88อà¸\99ุà¸\8dาà¸\95 à¸\84ือ /$1 à¸ªà¸³à¸«à¸£à¸±à¸\9a IPv4 à¹\81ละ /$2 à¸ªà¸³à¸«à¸£à¸±à¸\9a IPv6",
        "ipaddressorusername": "เลขที่อยู่ไอพีหรือชื่อผู้ใช้:",
        "ipbexpiry": "หมดอายุ:",
        "ipbreason": "เหตุผล:",
        "block-log-flags-hiddenname": "ชื่อผู้ใช้ถูกซ่อน",
        "range_block_disabled": "การบล็อกช่วงไอพีของผู้ดูแลระบบถูกปิดใช้งาน",
        "ipb_expiry_invalid": "เวลาหมดอายุไม่ถูกต้อง",
+       "ipb_expiry_old": "เวลาหมดอายุอยู่ในอดีต",
        "ipb_expiry_temp": "การบล็อกชื่อผู้ใช้ที่ซ่อนต้องเป็นการบล็อกถาวร",
        "ipb_hide_invalid": "ไม่สามารถยับยั้งชื่อผู้ใช้นี้ได้ อาจเพราะมีการแก้ไขมากกว่า $1 การแก้ไข",
        "ipb_already_blocked": "\"$1\" ถูกบล็อกแล้ว",
        "lockedbyandtime": "(โดย {{GENDER:$1|$1}} เมื่อวันที่ $2 เวลา $3)",
        "move-page": "ย้าย $1",
        "move-page-legend": "เปลี่ยนชื่อ",
-       "movepagetext": "à¸\81ารà¹\83à¸\8aà¹\89à¹\81à¸\9aà¸\9aà¸\94à¹\89าà¸\99ลà¹\88าà¸\87à¸\88ะà¹\80à¸\9bลีà¹\88ยà¸\99à¸\8aืà¹\88อหà¸\99à¹\89า à¹\81ละยà¹\89ายà¸\9bระวัà¸\95ิà¸\97ัà¹\89à¸\87หมà¸\94à¹\84à¸\9bยัà¸\87à¸\8aืà¹\88อà¹\83หมà¹\88\nà¸\8aืà¹\88อà¹\80รืà¹\88อà¸\87à¹\80à¸\81à¹\88าà¸\88ะà¸\81ลายà¹\80à¸\9bà¹\87à¸\99หà¸\99à¹\89าà¹\80à¸\9bลีà¹\88ยà¸\99à¸\97าà¸\87à¹\84à¸\9bยัà¸\87ชื่อเรื่องใหม่\nคุณสามารถปรับการเปลี่ยนทางซึ่งชี้ไปยังชื่อเรื่องเดิมได้อัตโนมัติ\nแต่หากคุณเลือกไม่ทำเช่นนั้น ให้แน่ใจว่าตรวจสอบ[[Special:DoubleRedirects|หน้าเปลี่ยนทางซ้ำซ้อน]]หรือ[[Special:BrokenRedirects|หน้าเปลี่ยนทางเสีย]]\nคุณเป็นผู้รับผิดชอบเพื่อให้แน่ใจว่าลิงก์ต่าง ๆ ยังชี้ไปยังที่ที่สมควร\n\nโปรดทราบว่าหน้าดังกล่าวจะ<strong>ไม่</strong>ถูกย้าย ถ้ามีหน้าที่ใช้ชื่อเรื่องใหม่แล้ว เว้นแต่หน้านั้นเป็นหน้าเปลี่ยนทาง และไม่มีประวัติการแก้ไขในอดีต\nซึ่งหมายความว่า คุณสามารถเปลี่ยนชื่อหน้ากลับเป็นชื่อเดิมได้หากคุณทำผิดพลาด และคุณไม่สามารถเขียนทับหน้าที่มีอยู่แล้วได้\n\n<strong>คำเตือน!</strong>\nสิ่งนี้อาจเป็นการเปลี่ยนแปลงที่รุนแรงและไม่คาดคิดสำหรับหน้าที่เป็นที่นิยม\nโปรดให้แน่ใจว่าคุณเข้าใจผลลัพธ์นี้ก่อนดำเนินการ",
+       "movepagetext": "à¸\81ารà¹\83à¸\8aà¹\89à¹\81à¸\9aà¸\9aà¸\94à¹\89าà¸\99ลà¹\88าà¸\87à¸\88ะà¹\80à¸\9bลีà¹\88ยà¸\99à¸\8aืà¹\88อหà¸\99à¹\89า à¹\81ละยà¹\89ายà¸\9bระวัà¸\95ิà¸\97ัà¹\89à¸\87หมà¸\94à¹\84à¸\9bà¸\8aืà¹\88อà¹\83หมà¹\88\nà¸\8aืà¹\88อà¹\80รืà¹\88อà¸\87à¹\80à¸\81à¹\88าà¸\88ะà¸\81ลายà¹\80à¸\9bà¹\87à¸\99หà¸\99à¹\89าà¹\80à¸\9bลีà¹\88ยà¸\99à¸\97าà¸\87à¹\84à¸\9bชื่อเรื่องใหม่\nคุณสามารถปรับการเปลี่ยนทางซึ่งชี้ไปยังชื่อเรื่องเดิมได้อัตโนมัติ\nแต่หากคุณเลือกไม่ทำเช่นนั้น ให้แน่ใจว่าตรวจสอบ[[Special:DoubleRedirects|หน้าเปลี่ยนทางซ้ำซ้อน]]หรือ[[Special:BrokenRedirects|หน้าเปลี่ยนทางเสีย]]\nคุณเป็นผู้รับผิดชอบเพื่อให้แน่ใจว่าลิงก์ต่าง ๆ ยังชี้ไปยังที่ที่สมควร\n\nโปรดทราบว่าหน้าดังกล่าวจะ<strong>ไม่</strong>ถูกย้าย ถ้ามีหน้าที่ใช้ชื่อเรื่องใหม่แล้ว เว้นแต่หน้านั้นเป็นหน้าเปลี่ยนทาง และไม่มีประวัติการแก้ไขในอดีต\nซึ่งหมายความว่า คุณสามารถเปลี่ยนชื่อหน้ากลับเป็นชื่อเดิมได้หากคุณทำผิดพลาด และคุณไม่สามารถเขียนทับหน้าที่มีอยู่แล้วได้\n\n<strong>คำเตือน!</strong>\nสิ่งนี้อาจเป็นการเปลี่ยนแปลงที่รุนแรงและไม่คาดคิดสำหรับหน้าที่เป็นที่นิยม\nโปรดให้แน่ใจว่าคุณเข้าใจผลลัพธ์นี้ก่อนดำเนินการ",
        "movepagetext-noredirectfixer": "การใช้แบบด้านล่างจะเปลี่ยนชื่อหน้า ซึ่งจะทำให้ประวัติทั้งหมดย้ายไปยังชื่อใหม่\nชื่อเรื่องเก่าจะกลายเป็นหน้าเปลี่ยนทางไปยังชื่อเรื่องใหม่\nให้แน่ใจว่า ตรวจสอบ[[Special:DoubleRedirects|หน้าเปลี่ยนทางซ้ำซ้อน]]หรือ[[Special:BrokenRedirects|หน้าเปลี่ยนทางที่เสีย]]\nคุณจะเป็นผู้รับผิดชอบเพื่อให้แน่ใจว่าลิงก์ต่าง ๆ ยังชี้ไปยังที่ที่สมควร\n\nโปรดทราบว่าหน้าดังกล่าวจะ'''ไม่'''ถูกย้าย ถ้ามีหน้าที่ใช้ชื่อเรื่องใหม่อยู่แล้ว เว้นแต่เป็นหน้าว่างหรือหน้าเปลี่ยนทาง และไม่มีประวัติการแก้ไขในอดีต\nซึ่งหมายความว่า คุณสามารถเปลี่ยนชื่อหน้ากลับเป็นชื่อเดิมได้หากคุณทำผิดพลาด และคุณไม่สามารถเขียนทับหน้าที่มีอยู่แล้วได้\n\n'''คำเตือน!'''\nสิ่งนี้อาจเป็นการเปลี่ยนแปลงที่รุนแรงและไม่คาดคิดสำหรับหน้าที่เป็นที่นิยม\nโปรดแน่ใจว่าคุณเข้าใจถึงผลลัพธ์นี้ก่อนที่จะดำเนินการต่อไป",
-       "movepagetalktext": "หà¸\99à¹\89าà¸\9eูà¸\94à¸\84ุยà¸\82อà¸\87หà¸\99à¹\89าà¸\99ีà¹\89à¸\88ะà¸\96ูà¸\81à¹\80à¸\9bลีà¹\88ยà¸\99à¸\8aืà¹\88อà¸\95ามà¹\84à¸\9bà¹\82à¸\94ยอัà¸\95à¹\82à¸\99มัà¸\95ิ<strong>à¹\80วà¹\89à¸\99à¹\81à¸\95à¹\88:</strong>\n*มีหà¸\99à¹\89าà¸\9eูà¸\94à¸\84ุยà¸\8bึà¹\88à¸\87à¹\84มà¹\88วà¹\88าà¸\87ภายà¹\83à¸\95à¹\89à¸\8aืà¹\88อà¹\83หมà¹\88à¹\81ลà¹\89ว à¸«à¸£à¸·à¸­\n*à¸\84ุà¸\93à¹\84มà¹\88à¹\80ลือà¸\81à¸\81ลà¹\88อà¸\87à¸\94à¹\89าà¸\99ลà¹\88าà¸\87\n\nในกรณีเหล่านี้ คุณจะต้องย้ายหรือรวมหน้าเองหากต้องการ",
+       "movepagetalktext": "หาà¸\81à¸\84ุà¸\93à¹\80ลือà¸\81à¸\81ลà¹\88อà¸\87à¸\99ีà¹\89 à¸«à¸\99à¹\89าà¸\9eูà¸\94à¸\84ุยà¸\82อà¸\87หà¸\99à¹\89าà¸\99ีà¹\89à¸\88ะà¸\96ูà¸\81à¹\80à¸\9bลีà¹\88ยà¸\99à¸\8aืà¹\88อà¸\95ามà¹\84à¸\9bà¹\82à¸\94ยอัà¸\95à¹\82à¸\99มัà¸\95ิà¹\80วà¹\89à¸\99à¹\81à¸\95à¹\88à¸\9bลายà¸\97าà¸\87มีหà¸\99à¹\89าà¸\9eูà¸\94à¸\84ุยà¹\84มà¹\88วà¹\88าà¸\87à¹\81ลà¹\89ว\n\nในกรณีเหล่านี้ คุณจะต้องย้ายหรือรวมหน้าเองหากต้องการ",
        "moveuserpage-warning": "<strong>คำเตือน:</strong> คุณกำลังย้ายหน้าผู้ใช้ โปรดทราบว่าหน้าผู้ใช้เท่านั้นที่จะถูกเปลี่ยนชื่อ แต่ผู้ใช้จะ<em>ไม่</em>ถูกเปลี่ยนชื่อ",
        "movecategorypage-warning": "<strong>คำเตือน:</strong> คุณกำลังย้ายหน้าหมวดหมู่ โปรดทราบว่า จะย้ายเฉพาะหน้าและทุกหน้าในหมวดหมู่เก่าจะ<em>ไม่</em>ถูกจัดเข้าหมวดหมู่ใหม่",
        "movenologintext": "ถ้าต้องการเปลี่ยนชื่อหน้านี้ ต้องเป็นผู้ใช้ลงทะเบียนและ[[Special:UserLogin|ล็อกอิน]]",
        "movenosubpage": "หน้านี้ไม่มีหน้าย่อย",
        "movereason": "เหตุผล:",
        "revertmove": "ย้อน",
-       "delete_and_move_text": "== ต้องการลบ ==\nมีหน้าปลายทาง \"[[:$1]]\" แล้ว \nคุณต้องการลบหน้าดังกล่าวเพื่อสร้างหนทางสำหรับการย้ายหรือไม่",
+       "delete_and_move_text": "มีหน้าปลายทาง \"[[:$1]]\" แล้ว \nคุณต้องการลบหน้าดังกล่าวเพื่อสร้างทางสำหรับการย้ายหรือไม่",
        "delete_and_move_confirm": "ใช่ ลบหน้านั้น",
        "delete_and_move_reason": "ถูกลบเพื่อสร้างหนทางสำหรับการย้ายจาก \"[[$1]]\"",
        "selfmove": "ชื่อหน้าต้นทางและปลายทางเหมือนกัน\nไม่สามารถเปลี่ยนชื่อมาใช้ชื่อเดิมได้",
        "move-leave-redirect": "สร้างหน้าเปลี่ยนทางตามมา",
        "protectedpagemovewarning": "<strong>คำเตือน:</strong>  หน้านี้ถูกล็อก เฉพาะผู้ใช้ที่มีสิทธิผู้ดูแลระบบเท่านั้นที่ย้ายได้\nปูมล่าสุดแสดงไว้ด้านล่างเพื่อการอ้างอิง:",
        "semiprotectedpagemovewarning": "'''หมายเหตุ:''' หน้านี้ถูกล็อก เฉพาะผู้ใช้ลงทะเบียนเท่านั้นที่ย้ายได้\nรายการปูมล่าสุดได้ถูกแสดงไว้ด้านล่างนี้เพื่อการอ้างอิง:",
-       "move-over-sharedrepo": "== มีไฟล์เดิมปรากฏ ==\nไฟล์ [[:$1]] มีปรากฏเดิมอยู่แล้วในคลังเก็บภาพส่วนกลาง การย้ายไฟล์ที่มีชื่อเรื่องนี้อาจจะเป็นการเขียนทับไฟล์เดิมในคลังเก็บได้",
+       "move-over-sharedrepo": "มี [[:$1]] ในคลังเก็บภาพส่วนกลางแล้ว การย้ายไฟล์ไปชื่อเรื่องนี้จะเขียนทับไฟล์ที่ใช้ร่วมกัน",
        "file-exists-sharedrepo": "ชื่อไฟล์นี้มีปรากฏเดิมอยู่แล้วในคลังเก็บภาพส่วนกลาง\nกรุณาเลือกชื่ออื่น",
        "export": "ส่งออกหน้า",
        "exporttext": "คุณสามารถส่งออกข้อความและประวัติการแก้ไขของหน้าใด ๆ หรือชุดหน้าในคราวเดียว ออกมาในรูปแบบ XML ซึ่งสามารถนำไปใส่เข้าไว้ในวิกิแห่งอื่นที่ใช้ซอฟต์แวร์มีเดียวิกิได้ ผ่านคำสั่ง[[Special:Import|การนำเข้าหน้า]]\n\nการจะส่งออกหน้านั้นสามารถทำได้โดยใส่ชื่อเรื่องหน้าที่ต้องการ ลงในกล่องข้อความด้านล่าง หนึ่งชื่อต่อหนึ่งบรรทัด จากนั้นเลือกว่าต้องการทั้งรุ่นปัจจุบันและรุ่นเก่าทั้งหมดพร้อมกับประวัติของหน้านั้น หรือต้องการเพียงเนื้อหารุ่นปัจจุบันพร้อมกับสารสนเทศของการแก้ไขครั้งสุดท้ายเท่านั้น\n\nในกรณีที่ต้องการเฉพาะรุ่นปัจจุบัน คุณสามารถใช้ในรูปแบบของลิงก์ เช่น [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]] สำหรับหน้า \"[[{{MediaWiki:Mainpage}}]]\"",
index 24b1699..e713b2d 100644 (file)
                        "Ianlopez1115",
                        "Leeheonjin",
                        "Macofe",
-                       "Matma Rex"
+                       "Matma Rex",
+                       "Stranger195"
                ]
        },
        "tog-underline": "Pagsasalungguhit ng link:",
        "tog-hideminor": "Itago ang mga maliliit na pagbabago mula sa mga huling binago",
        "tog-hidepatrolled": "Ikubli ang napatrolyang mga pagbabagong nasa kamakailang mga pagbabago",
        "tog-newpageshidepatrolled": "Itago ang napatrolyang mga pahina mula talaan ng bagong pahina",
+       "tog-hidecategorization": "Itago ang kategorisasyon ng mga pahina",
        "tog-extendwatchlist": "Palawigin ang talaan ng mga binabantayan upang maipakita ang lahat ng mga pagbabago, hindi lamang ang pinakakamakailan lamang",
-       "tog-usenewrc": "Mga pagbabago ng pangkat ayon sa pahina sa kamakailang mga pagbabago at bantayan (nangangailangan ng JavaScript)",
+       "tog-usenewrc": "Mga pagbabago ng pangkat ayon sa pahina sa kamakailang mga pagbabago at bantayan",
        "tog-numberheadings": "Automatikong bilangin ang mga pamagat",
-       "tog-showtoolbar": "Ipakita ang ''toolbar'' ng pagbabago (JavaScript)",
-       "tog-editondblclick": "Magbago ng mga pahina sa dalawahang pagpindot (JavaScript)",
-       "tog-editsectiononrightclick": "Payagan ang mga pagbabagong panseksyon sa pakanang pagpindot ng mga panseksyong pamagat (JavaScript)",
+       "tog-showtoolbar": "Ipakita ang ''toolbar'' ng pagbabago",
+       "tog-editondblclick": "Magbago ng mga pahina sa dalawahang pagpindot",
+       "tog-editsectiononrightclick": "Payagan ang mga pagbabagong panseksyon sa pakanang pagpindot ng mga panseksyong pamagat",
        "tog-watchcreations": "Idagdag sa aking tala ng mga binabantayan ang mga pahinang nilikha ko at mga talaksang ikinarga kong paitaas",
        "tog-watchdefault": "Idagdag sa aking tala ng mga binabantayan ang mga pahina at mga talaksang binago ko",
        "tog-watchmoves": "Idagdag sa aking tala ng mga binabantayan ang mga pahina at mga talaksang inilipat ko",
        "tog-watchdeletion": "Idagdag sa aking tala ng mga binabantayan ang mga pahina at mga talaksang binura ko",
+       "tog-watchrollback": "Magdagdag ng mga pahina kung saan ako nag-rollback sa aking bantayan",
        "tog-minordefault": "Markahan ang lahat ng pagbabago bilang maliit nang nakatakda",
        "tog-previewontop": "Ipakita ang paunang tingin bago ang kahon ng pagbabago",
        "tog-previewonfirst": "Ipakita ang paunang tingin sa unang pagbabago",
        "tog-shownumberswatching": "Ipakita ang bilang ng mga nagbabantay na tagagamit",
        "tog-oldsig": "Umiiral na lagda:",
        "tog-fancysig": "Ituring ang lagda bilang teksto ng wiki (walang automatikong pagkawing)",
-       "tog-uselivepreview": "Gamitin ang buhay na paunang tingin (JavaScript) (Eksperimental)",
+       "tog-uselivepreview": "Gamitin ang buhay na paunang tingin",
        "tog-forceeditsummary": "Pagsabihan ako kapag nagpapasok ng walang-lamang buod ng pagbabago",
        "tog-watchlisthideown": "Itago ang aking mga pagbabago mula sa tala ng mga binabantayan",
        "tog-watchlisthidebots": "Itago ang mga pagbabago ng mga bot mula sa tala ng mga binabantayan",
        "tog-watchlisthideminor": "Itago ang mga maliliit na pagbabago mula sa tala ng mga binabantayan",
        "tog-watchlisthideliu": "Itago ang mga pagbabago ng mga nakalagdang tagagamit mula sa tala ng mga binabantayan",
+       "tog-watchlistreloadautomatically": "I-karga muli ang bantayan ng awtomatiko kung kailan man nabago ang isang filter (kinakailangan ang JavaScript)",
        "tog-watchlisthideanons": "Itago ang mga pagbabago ng hindi nakikilalang mga tagagamit mula sa tala ng mga binabantayan",
        "tog-watchlisthidepatrolled": "Itago ang napatrolyang mga pagbabago mula sa tala ng mga binabantayan",
+       "tog-watchlisthidecategorization": "Itago ang kategorisasyon ng mga pahina",
        "tog-ccmeonemails": "Padalahan ako ng mga kopya ng mga ipinadala kong e-liham sa ibang mga tagagamit",
        "tog-diffonly": "Huwag ipakita ang nilalaman ng pahinang nasa ilalim ng mga pagkakaiba",
        "tog-showhiddencats": "Ipakita ang mga nakatagong kategorya",
        "october-date": "$1 Oktubre",
        "november-date": "$1 Nobyembre",
        "december-date": "$1 Disyembre",
+       "period-am": "ng umaga",
+       "period-pm": "ng gabi",
        "pagecategories": "{{PLURAL:$1|Kategorya|Mga kategorya}}",
        "category_header": "Mga pahina sa kategoryang \"$1\"",
        "subcategories": "Mga subkategorya",
        "morenotlisted": "Hindi kumpleto ang talang ito.",
        "mypage": "Pahina ko",
        "mytalk": "Usapan",
-       "anontalk": "Usapan para sa IP na ito",
+       "anontalk": "Usapan",
        "navigation": "Paglilibot (nabigasyon)",
        "and": ",&#32;at",
        "qbfind": "Hanapin",
        "view": "Tingnan",
        "view-foreign": "Tingnan sa $1",
        "edit": "Baguhin",
+       "edit-local": "Baguhin ang lokal na paglalarawan",
        "create": "Likhain",
+       "create-local": "Magdagdag ng lokal na paglalarawan",
        "editthispage": "Baguhin ang pahinang ito",
        "create-this-page": "Likhain ang pahinang ito",
        "delete": "Burahin",
        "deletethispage": "Burahin itong pahina",
        "undeletethispage": "Ibalik mula sa pagkakabura ang pahinang ito",
        "undelete_short": "Baligtarin ang pagbura ng {{PLURAL:$1|isang pagbabago|$1 pagbabago}}",
-       "viewdeleted_short": "Tingnan ang {{PLURAL:$1|isang binurang pagbabagp|$1 binurang pagbabago}}",
+       "viewdeleted_short": "Tingnan ang {{PLURAL:$1|isang binurang pagbabago|$1 binurang pagbabago}}",
        "protect": "Ipagsanggalang",
        "protect_change": "baguhin",
        "protectthispage": "Ipagsanggalang itong pahina",
        "otherlanguages": "Sa ibang wika",
        "redirectedfrom": "(Ikinarga mula sa $1)",
        "redirectpagesub": "Pahina ng pagkarga",
+       "redirectto": "Papuntahin sa:",
        "lastmodifiedat": "Huling binago ang pahinang ito noong $2, noong $1.",
-       "viewcount": "Namataan na pahinang ito nang {{PLURAL:$1|isang|$1}} beses.",
+       "viewcount": "Namataan ang pahinang ito nang {{PLURAL:$1|isang|$1}} beses.",
        "protectedpage": "Pahinang nakasanggalang",
        "jumpto": "Tumalon sa:",
        "jumptonavigation": "paglilibot",
        "jumptosearch": "paghahanap",
        "view-pool-error": "Paumanhin, ngunit masyado pong abala ang mga serbidor sa sandaling ito.\nMasyadong maraming tagagamit ay sinusubukang tingnan ang pahinang ito.\nMangyari lamang na maghintay po nang sandali bago niyo pong subukang mataanin muli ang pahinang ito.\n\n$1",
+       "generic-pool-error": "Paumanhin, ngunit masyado pong abala ang mga serbidor sa sandaling ito.\nMasyadong maraming tagagamit ay sinusubukang tingnan ang pahinang ito.\nMangyari lamang na maghintay po nang sandali bago niyo pong subukang mataanin muli ang pahinang ito.",
        "pool-timeout": "Ang pagpapahinga ay naghihintay ng kandado",
        "pool-queuefull": "Puno na ang pisan ng mga pila",
        "pool-errorunknown": "Hindi nalalamang kamalian",
+       "pool-servererror": "Hindi magagamit ang serbisyo ng pool counter ($1).",
+       "poolcounter-usage-error": "Pagkakamali sa paggamit: $1",
        "aboutsite": "Tungkol sa {{SITENAME}}",
        "aboutpage": "Project:Patungkol",
        "copyright": "Maaaring gamitin ang nilalaman sa ilalim ng $1 maliban kung nabanggit.",
        "backlinksubtitle": "← $1",
        "retrievedfrom": "Ikinuha mula sa \"$1\"",
        "youhavenewmessages": "Mayroon kang $1 ($2).",
-       "youhavenewmessagesfromusers": "Mayroon kang $1 magmula sa {{PLURAL:$3|ibang tagagamit|$3 mga tagagamit}} ($2).",
+       "youhavenewmessagesfromusers": "{{PLURAL:$4|Mayroon kang}} $1 magmula sa {{PLURAL:$3|ibang tagagamit|$3 mga tagagamit}} ($2).",
        "youhavenewmessagesmanyusers": "Mayroon kang $1 magmula sa maraming mga tagagamit ($2).",
-       "newmessageslinkplural": "{{PLURAL:$1|isang bagong mensahe|bagong mga mensahe}}",
-       "newmessagesdifflinkplural": "huling {{PLURAL:$1|pagbabago|mga pagbabago}}",
+       "newmessageslinkplural": "{{PLURAL:$1|isang bagong mensahe|999=bagong mensahe}}",
+       "newmessagesdifflinkplural": "huling {{PLURAL:$1|pagbabago|999=mga pagbabago}}",
        "youhavenewmessagesmulti": "Mayroon kang mga bagong mensahe sa $1",
        "editsection": "baguhin",
        "editold": "baguhin",
        "hidetoc": "itago",
        "collapsible-collapse": "Ibagsak",
        "collapsible-expand": "Ibuka",
+       "confirmable-confirm": "Sigurado {{GENDER:$1|ka}} ba?",
+       "confirmable-yes": "Oo",
+       "confirmable-no": "Hindi",
        "thisisdeleted": "Tingnan o ibalik ang $1?",
        "viewdeleted": "Tingnan ang $1?",
        "restorelink": "{{PLURAL:$1|isang binurang pagbabago|$1 binurang pagbabago}}",
        "nstab-template": "Padron",
        "nstab-help": "Pahina ng tulong",
        "nstab-category": "Kategorya",
+       "mainpage-nstab": "Unang pahina",
        "nosuchaction": "Walang ganitong kilos",
        "nosuchactiontext": "Hindi tanggap ang galaw na tinukoy ng URL.\nMaaaring nagkamali ka sa pagmamakinilya ng URL, o sumunod sa isang maling kawing.\nMaaari rin itong magpahiwatig ng isang depektong nasa loob ng {{SITENAME}}.",
        "nosuchspecialpage": "Walang ganyang natatanging pahina",
        "databaseerror": "Kamalian sa kalipunan ng datos",
        "databaseerror-text": "Mayroong kamalian sa pagtanong o pag-query sa database.\nMaaring ipinapahiwatig nito ang depekto o bug sa software.",
        "databaseerror-textcl": "May nangyaring depekto sa pag-query ng database.",
+       "databaseerror-query": "Tanong: $1",
+       "databaseerror-function": "Tungkulin: $1",
        "databaseerror-error": "Depekto: $1",
        "laggedslavemode": "'''Babala:''' Maaaring hindi naglalaman ang pahina ng mga huling dagdag.",
        "readonly": "Nakakandado ang kalipunan ng datos",
        "right-blockemail": "Harangin sa pagpapadala ng e-liham ang isang tagagamit",
        "right-hideuser": "Harangin ang isang tagagamit, na itinatago mula sa publiko",
        "right-ipblock-exempt": "Laktawan ang mga pagharang/paghadlang na pang-IP, kusang pagharang/paghadlang at mga saklaw ng pagharang/paghadlang",
-       "right-proxyunbannable": "Laktawan ang mga kusang pagharang ng mga kahalili",
        "right-unblockself": "Tanggalin ang pagkakaharang ng kanilang mga sarili",
        "right-protect": "Baguhin ang mga antas ng panananggalang at baguhin ang mga pahinang nakasanggalang",
        "right-editprotected": "Baguhin ang mga pahinang nakasanggalang (walang baita-baitang na panananggalang)",
        "filewasdeleted": "Isang talaksan na may ganitong pangalan ay naikarga dati at nabura. Kailangan mong tingnan ang $1 bago magpatuloy sa pagkarga nito muli.",
        "filename-bad-prefix": "Ang talaksan na ikakarga mo ay nagsisimula sa '''\"$1\"''', na isang hindi naglalarawang pangalan na karaniwang tinatakda ng mga kamerang digital. Paki pili ang isang mas naglalarawang pangalan para sa iyong talaksan.",
        "filename-prefix-blacklist": " #<!-- leave this line exactly as it is --> <pre>\n# Ang palaugnayan ay ang sumusunod:\n#   * Ang lahat ng mga bagay mula sa isang panitik na \"#\" hanggang sa katapusan ng isang guhit ay isang puna\n#   * Bawat isang guhit na mayroong laman ay isang unlapi para sa tipikal na mga pangalan ng talaksan na kusang itinalaga ng mga kamerang dihital\nCIMG # Casio\nDSC_ # Nikon\nDSCF # Fuji\nDSCN # Nikon\nDUW # ilang mga teleponong mobilo\nIMG # heneriko\nJD # Jenoptik\nMGP # Pentax\nPICT # samu't sari\n #</pre> <!-- leave this line exactly as it is -->",
-       "upload-success-subj": "Matagumpay na pagkakarga",
-       "upload-success-msg": "Matagumpay ang ikinarga mo mula sa [$2].  Makukuha ito mula rito: [[:{{ns:file}}:$1]]",
-       "upload-failure-subj": "Problema sa pagkarga",
-       "upload-failure-msg": "May suliranin sa iyong pagkakarga mula sa [$2]:\n\n$1",
-       "upload-warning-subj": "Babala sa pagkakarga",
-       "upload-warning-msg": "May suliranin sa iyong pagkakarga mula sa [$2]. Maaari kang magbalik sa [[Special:Upload/stash/$1|upload form]] upang itama ang suliraning ito.",
        "upload-proto-error": "Maling protokolo",
        "upload-proto-error-text": "Nangangailangan ang malayong pagkarga ng mga URL na nagsisimula sa <code>http://</code> o <code>ftp://</code>.",
        "upload-file-error": "Panloob na kamalian",
        "pager-older-n": "{{PLURAL:$1|mas lumang 1|mas lumang $1}}",
        "suppress": "Tagapagingat-tago",
        "querypage-disabled": "Hindi pinagagana ang natatanging pahinang ito para sa mga dahilan ng pagganap.",
+       "apisandbox": "Kahong buhanginan ng API",
+       "apisandbox-api-disabled": "Hindi pinagagana ang API sa sityong ito.",
+       "apisandbox-intro": "Gamitin ang pahinang ito upang mag-eksperimento sa pamamagitan ng '''Paglilingkod na pangsangkasaputan ng API ng MediaWiki'''.\nSumangguni sa [//www.mediawiki.org/wiki/API:Main_page dokumentasyon ng API] para sa karagdagan pang mga detalye sa paggamit ng API. Halimbawa: [//www.mediawiki.org/wiki/API#A_simple_example kuhanin ang nilalaman ng isang Pangunahing Pahina]. Pumili ng isang galaw upang makakita ng mas marami pang mga halimbawa.",
+       "apisandbox-submit": "Gumawa ng kahilingan",
+       "apisandbox-reset": "Hawiin",
+       "apisandbox-examples": "Halimbawa",
+       "apisandbox-results": "Kinalabasan",
+       "apisandbox-request-url-label": "Hilingin ang URL:",
+       "apisandbox-request-time": "Oras ng paghiling: $1",
        "booksources": "Mga mapagkukunang aklat",
        "booksources-search-legend": "Maghanap ng mapagkukunang aklat",
        "booksources-isbn": "ISBN:",
        "wlheader-showupdated": "Ipinapakitang may '''makakapal na mga panitik''' ang nabagong/binagong mga pahina mula pa noong huli mong pagdalaw sa kanila",
        "wlnote": "Nasa ibaba ang {{PLURAL:$1|pinakahuling pagbabago|pinakahuling '''$1''' mga pagbabago}} sa loob ng huling {{PLURAL:$2|oras|'''$2''' mga oras}}, magmula noong $3 sa ganap na ika-$4.",
        "wlshowlast": "Ipakita ang huling $1 mga oras $2 mga araw",
-       "watchlistall2": "lahat",
        "watchlist-hide": "Itago",
        "wlshowhideminor": "mga maliliit na edit",
        "wlshowhidebots": "mga bot",
        "version-hook-name": "Pangalan ng pangkawit",
        "version-hook-subscribedby": "Sinuskribi ng/ni/nina",
        "version-version": "($1)",
-       "version-svn-revision": "(r$2)",
        "version-license": "Lisensiya",
        "version-poweredby-credits": "Ang wiking ito ay pinapatakbo ng '''[https://www.mediawiki.org/ MediaWiki]''', karapatang-ari © 2001-$1 $2.",
        "version-poweredby-others": "iba pa",
        "special-characters-group-khmer": "Khmer",
        "mw-widgets-dateinput-placeholder-day": "TTTT-BB-AA",
        "mw-widgets-dateinput-placeholder-month": "TTTT-BB",
-       "api-error-blacklisted": "Paki pumili ng isang naiibang mapaglarawang pamagat."
+       "api-error-blacklisted": "Paki pumili ng isang naiibang mapaglarawang pamagat.",
+       "randomrootpage": "Alin mang pinag-ugatang/pinagmulang pahina"
 }
index 0d673a5..8ae7345 100644 (file)
@@ -82,7 +82,8 @@
                        "Diyapazon",
                        "Matma Rex",
                        "HakanIST",
-                       "Imabadplayer"
+                       "Imabadplayer",
+                       "İnternion"
                ]
        },
        "tog-underline": "Bağlantıların altını çiz:",
        "may": "May",
        "jun": "Haz",
        "jul": "Tem",
-       "aug": "Ağu",
+       "aug": "Agu",
        "sep": "Eyl",
        "oct": "Eki",
        "nov": "Kas",
        "right-applychangetags": "Değişiklikleriyle beraber [[Special:Tags|etiketleri]] uygula",
        "right-changetags": "Tekil sürümler ve günlük kayıtlarına rastgele [[Special:Tags|etiket]] ekleme veya çıkarma",
        "grant-group-email": "E-posta gönder",
+       "grant-createeditmovepage": "Sayfaları oluşturma, düzenleme ve taşıma",
+       "grant-editmycssjs": "Kullanıcı CSS/JavaScript'ini düzenle",
+       "grant-editmyoptions": "Kullanıcı tercihlerini Düzenle",
+       "grant-editmywatchlist": "İzleme listeni düzenle",
+       "grant-editprotected": "Korumalı sayfaları Düzenle",
+       "grant-patrol": "Sayfadaki değişiklikleri incele",
+       "grant-uploadfile": "Dosya yükle",
        "grant-basic": "Basit haklar",
+       "grant-viewdeleted": "Silinen dosya ve sayfaları görüntüle",
+       "grant-viewmywatchlist": "İzleme listeni gör",
        "newuserlogpage": "Yeni kullanıcı kayıtları",
        "newuserlogpagetext": "En son kaydolan kullanıcı kayıtları.",
        "rightslog": "Kullanıcı hakları kayıtları",
        "rcshowhidemine": "Benim değişikliklerimi $1",
        "rcshowhidemine-show": "göster",
        "rcshowhidemine-hide": "gizle",
+       "rcshowhidecategorization": "$1 Sayfa kategorizasyonu",
        "rcshowhidecategorization-show": "Göster",
        "rcshowhidecategorization-hide": "Gizle",
        "rclinks": "Son $2 günde yapılan son $1 değişikliği göster;<br /> $3",
        "upload-too-many-redirects": "URL çok fazla yönlendirme içeriyor",
        "upload-http-error": "Bir HTTP hatası oluştu: $1",
        "upload-copy-upload-invalid-domain": "Kopya yüklemeler bu etki alanında mevcut değil.",
+       "upload-dialog-title": "Dosya Yükle",
        "upload-dialog-button-cancel": "İptal",
+       "upload-dialog-button-done": "Yapıldı",
        "upload-dialog-button-save": "Kaydet",
        "upload-dialog-button-upload": "Yükle",
-       "upload-form-label-select-file": "Dosya seç",
        "upload-form-label-infoform-title": "Ayrıntılar",
        "upload-form-label-infoform-name": "Ad",
        "upload-form-label-infoform-description": "Açıklama",
        "foreign-structured-upload-form-label-own-work": "Bu benim kendi çalışmam",
        "foreign-structured-upload-form-label-infoform-categories": "Kategoriler",
        "foreign-structured-upload-form-label-infoform-date": "Tarih",
-       "foreign-structured-upload-form-3-label-yes": "Evet",
-       "foreign-structured-upload-form-3-label-no": "Hayır",
        "backend-fail-stream": "$1 dosyası okunamadı.",
        "backend-fail-backup": "\"$1\" dosyası yedeklenemedi.",
        "backend-fail-notexists": "$1 dosyası mevcut değil.",
        "mostrevisions": "En çok değişikliğe uğramış sayfalar",
        "prefixindex": "Önek ile tüm sayfalar",
        "prefixindex-namespace": "Önek ile tüm sayfalar ($1 ad alanında)",
+       "prefixindex-submit": "Göster",
        "prefixindex-strip": "Listede öneki kırp",
        "shortpages": "Kısa sayfalar",
        "longpages": "Uzun sayfalar",
        "usereditcount": "$1 {{PLURAL:$1|değişiklik|değişiklik}}",
        "usercreated": "$1 tarihinde $2'de {{GENDER:$3|oluşturuldu}}.",
        "newpages": "Yeni sayfalar",
+       "newpages-submit": "Göster",
        "newpages-username": "Kullanıcı adı:",
        "ancientpages": "En son değişiklik tarihi en eski olan maddeler",
        "move": "Taşı",
        "querypage-disabled": "Bu özel sayfa, performansa dayalı nedenlerle devre dışı bırakılır.",
        "apihelp": "API yardımı",
        "apihelp-no-such-module": "\"$1\" modülü bulunamadı.",
+       "apisandbox-unfullscreen": "Sayfayı göster",
+       "apisandbox-submit": "İstek yap",
+       "apisandbox-reset": "Temizle",
+       "apisandbox-retry": "Tekrar dene",
+       "apisandbox-examples": "Örnekler",
+       "apisandbox-results": "Sonuç",
+       "apisandbox-request-url-label": "İstek URL:",
+       "apisandbox-request-time": "İstek zamanı: $1",
        "booksources": "Kaynak kitaplar",
        "booksources-search-legend": "Kitap kaynaklarını ara",
        "booksources-isbn": "ISBN:",
        "log-title-wildcard": "Bu metinle başlayan başlıklar ara",
        "showhideselectedlogentries": "Seçili günlük girdilerinin görünürlüğünü değiştir",
        "log-edit-tags": "Seçili kayıtların etiketlerini düzenle",
+       "checkbox-all": "Tüm",
+       "checkbox-none": "Hiçbiri",
        "allpages": "Tüm sayfalar",
        "nextpage": "Sonraki sayfa ($1)",
        "prevpage": "Önceki sayfa ($1)",
        "cachedspecial-viewing-cached-ts": "Bu sayfanın önbelleğe alınan bir sürümünü görüntülüyorsunuz, tam olarak güncel olmayabilir.",
        "cachedspecial-refresh-now": "En son görünüm.",
        "categories": "Kategoriler",
+       "categories-submit": "Göster",
        "categoriespagetext": "Aşağıdaki {{PLURAL:$1|kategori|kategoriler}} sayfa veya ortam içerir.\n[[Special:UnusedCategories|Kullanılmayan kategoriler]] burada gösterilmemektedir.\nAyrıca [[Special:WantedCategories|İstenen kategoriler]]'e bakınız.",
        "categoriesfrom": "Şununla başlayan kategorileri görüntüle:",
        "special-categories-sort-count": "sayılarına göre sırala",
        "listgrouprights-namespaceprotection-header": "Ad kısıtlamaları",
        "listgrouprights-namespaceprotection-namespace": "Ad alanı",
        "listgrouprights-namespaceprotection-restrictedto": "Kullanıcının değişiklik yapmasına izin veren hak(lar)",
+       "listgrants-rights": "Haklar",
        "trackingcategories": "Eşleşen kategoriler",
        "trackingcategories-summary": "Bu sayfa MediaWiki yazılımı tarafından otomatik olarak doldurulan takip kategorilerini listelemektedir. {{ns:8}} ad alanındaki ilgili sistem mesajları değiştirilerek isimleri düzenlenebilir.",
        "trackingcategories-msg": "İzleme kategorisi",
        "delete-confirm": "\"$1\" sil",
        "delete-legend": "sil",
        "historywarning": "<strong>Uyarı:</strong> Silmek üzere olduğunuz sayfanın yaklaşık olarak $1 sürüme sahip bir geçmişi var:",
+       "historyaction-submit": "Göster",
        "confirmdeletetext": "Bu sayfayı veya dosyayı tüm geçmişi ile birlikte veritabanından kalıcı olarak silmek üzeresiniz.\nBu işlemden kaynaklı doğabilecek sonuçların farkında iseniz ve işlemin [[{{MediaWiki:Policy-url}}|Silme kurallarına]] uygun olduğuna eminseniz, işlemi onaylayın.",
        "actioncomplete": "İşlem tamamlandı",
        "actionfailed": "İşlem başarısız oldu",
index a705263..9001177 100644 (file)
        "qbfind": "Эзләү",
        "qbbrowse": "Карау",
        "qbedit": "Үзгәртү",
-       "qbpageoptions": "Ð\90гÓ\80онан Ñ\82одаÑ\80Ñ\88",
+       "qbpageoptions": "Ð\91Ñ\83 Ð±Ð¸Ñ\82",
        "qbmyoptions": "Битләрем",
        "faq": "ЕБС",
        "faqpage": "Project:ЕБС",
        "retypenew": "Яңа серсүзне кабатлагыз:",
        "resetpass_submit": "Серсүз куеп керү",
        "changepassword-success": "Серсүзегез уңышлы үзгәртелде!",
+       "botpasswords": "Ботларның серсүзләре",
        "botpasswords-label-appid": "Бот исеме:",
        "botpasswords-label-create": "Төзү",
        "botpasswords-label-update": "Яңарту",
        "resettokens": "Токеннарны ташлау",
        "resettokens-tokens": "Токеннар:",
        "resettokens-token-label": "$1 (агымдагы мәгънә: $2)",
+       "resettokens-done": "Токеннар ташланды.",
+       "resettokens-resetbutton": "Сайланган токеннарны ташлау",
        "bold_sample": "Калын язылыш",
        "bold_tip": "Калын язылыш",
        "italic_sample": "Курсив язылыш",
        "diff-multi-manyusers": "($2 күбрәк {{PLURAL:$2|кулланучының|кулланучының}} {{PLURAL:$1|Бер арадаш юрамасы|$1 арадаш юрамасы}} күрсәтелмәгән)",
        "searchresults": "Эзләү нәтиҗәләре",
        "searchresults-title": "«$1» өчен эзләү нәтиҗәләре",
+       "titlematches": "Бит исемнәрендә тиңдәшлек",
+       "textmatches": "Бит эчтәлегендә тиңдәшлек",
        "notextmatches": "Тиңдәш текстлы битләр юк",
        "prevn": "алдагы {{PLURAL:$1|$1}}",
        "nextn": "чираттагы {{PLURAL:$1|$1}}",
        "right-reupload": "Булган файллар өстеннән язарга",
        "right-writeapi": "Язма өчен API куллану",
        "right-delete": "битләрне бетерү",
+       "right-browsearchive": "Бетерелгән битләрне эзләү",
+       "right-undelete": "Битләрне торгызу",
        "right-editinterface": "Кулланучы интерфейсын үзгәртү",
        "grant-group-email": "Хатлар җибәрү",
        "grant-uploadfile": "Яңа файллар йөкләү",
        "rcshowhidemine": "минем үзгәртүләремне $1",
        "rcshowhidemine-show": "Күрсәтү",
        "rcshowhidemine-hide": "Яшер",
+       "rcshowhidecategorization": "битләрне төркемләүне $1",
        "rcshowhidecategorization-show": "Күрсәт",
        "rcshowhidecategorization-hide": "Яшер",
        "rclinks": "Соңгы $2 көн эчендә ясалган $1 үзгәртүне күрсәт<br />$3",
        "upload-dialog-button-done": "Әзер",
        "upload-dialog-button-save": "Саклау",
        "upload-dialog-button-upload": "Йөкләү",
-       "upload-form-label-select-file": "Файлны сайлагыз",
        "upload-form-label-infoform-title": "Тулырак",
        "upload-form-label-infoform-name": "Исем",
        "upload-form-label-infoform-description": "Тасвир",
        "foreign-structured-upload-form-label-own-work": "Бу минем үз эшем",
        "foreign-structured-upload-form-label-infoform-categories": "Төркемнәр",
        "foreign-structured-upload-form-label-infoform-date": "Дата",
-       "foreign-structured-upload-form-3-label-yes": "Әйе",
-       "foreign-structured-upload-form-3-label-no": "Юк",
        "uploadstash": "Яшерен йөкләү",
        "uploadstash-summary": "Әлеге бит йөкләнгән (яисә йукләү барышында булган), әмма викида әлегә күрсәтелмәгән файлларны карау мөмкинлеген бирә. Бу файлларны йөкләгән кулланучыдан башка беркемдә күрә алмый.",
        "uploadstash-clear": "Яшерен файлларны бетерү",
        "imagelinks": "Файлны куллану",
        "linkstoimage": "{{PLURAL:$1|Киләсе $1 бит|Киләсе $1 битләр|}} әлеге файлга сылтама ясый:",
        "nolinkstoimage": "Бу файлга сылтаган битләр юк.",
+       "linkstoimage-redirect": "$1 (файл юнәлтүе) $2",
        "duplicatesoffile": "{{PLURAL:$1|Әлеге $1 файл }} астагы файлның күчерелмәсе булып тора ([[Special:FileDuplicateSearch/$2|тулырак]]):",
        "sharedupload": "Бу файл $1 проектыннан һәм башка проектларда кулланырга мөмкин",
        "sharedupload-desc-here": "Бу файл $1 проектыннан һәм башка проектларда кулланырга мөмкин. \nФайл турында [$2 тулырак мәгълүмат] түбәндәрәк күрсәтелгән.",
        "filedelete-comment": "Сәбәп:",
        "filedelete-submit": "Бетерү",
        "filedelete-nofile": "<strong>$1</strong> файлы юк.",
+       "filedelete-otherreason": "Башка сәбәп:",
        "filedelete-reason-otherlist": "Башка сәбәп",
+       "filedelete-reason-dropdown": "*Киң таралган бетерү сәбәпләре \n** авторлык хокукларны бозу\n** кабатланган файл",
+       "filedelete-edit-reasonlist": "Сәбәпләр исемлеген үзгәртү",
        "mimesearch": "MIME эзләү",
        "mimetype": "MIME-тип:",
        "download": "йөкләү",
        "pageswithprop-prop": "Үзенчәлекнең атамасы:",
        "pageswithprop-submit": "Табу",
        "doubleredirects": "Икеләтә юнәлтүләр",
+       "double-redirect-fixer": "Юнәлтүләрне төзәтүче",
        "brokenredirects": "Бәйләнешсез юнәлтүләр",
        "brokenredirectstext": "Бу юнәлтүләр булмаган битләргә сылтыйлар:",
        "brokenredirects-edit": "үзгәртү",
        "longpages": "Озын битләр",
        "deadendpages": "Тупик битләре",
        "protectedpages": "Якланган битләр",
+       "protectedpages-timestamp": "Дата/вакыт",
        "protectedpages-page": "Бит",
        "protectedpages-expiry": "Тәмамлана",
        "protectedpages-performer": "Кулланучыны яклау",
        "protectedpages-unknown-timestamp": "Билгесез",
        "protectedpages-unknown-performer": "Билгесез кулланучы",
        "protectedtitles": "Тыелган исемнәр",
+       "protectedtitles-submit": "Башлыкларны күрсәтү",
        "listusers": "Кулланучылар исемлеге",
        "usercreated": "$3 $1 көнне $2 вакытта {{GENDER:$3|теркәлде}}",
        "newpages": "Яңа битләр",
        "ancientpages": "Иң иске битләр",
        "move": "Күчерү",
        "movethispage": "Бу битне күчерү",
+       "notargettitle": "Максатсыз",
        "nopagetitle": "Мондый бит юк",
        "nopagetext": "Күрсәтелгән бит юк.",
        "pager-newer-n": "{{PLURAL:$1|$1 яңарак}}",
        "pager-older-n": "$1 {{PLURAL:$1|искерәк}}",
        "suppress": "Яшерү",
+       "apihelp": "API ярдәм",
+       "apihelp-no-such-module": "«$1» модуле табылмады.",
        "booksources": "Китап чыганаклары",
        "booksources-search-legend": "Китап чыганакларыны эзләү",
        "booksources-search": "Эзләү",
        "allinnamespace": "«$1» исемнәр мәйданындагы барлык битләр",
        "allpagessubmit": "Башкару",
        "allpagesprefix": "Алкушымчалы битләрне күрсәтү:",
+       "allpages-bad-ns": "{{SITENAME}} проектында «$1» исемнәр мәйданы юк.",
        "allpages-hide-redirects": "Юнәлтүләрне яшер",
+       "cachedspecial-refresh-now": "Соңгы юраманы карау.",
        "categories": "Төркемнәр",
        "categories-submit": "Күрсәт",
        "categoriespagetext": "{{PLURAL:$1|1=Әлеге төркем үз өченә|Әлеге төркемнәр  үз өченә}}   битләрне һәм медиа-файлларны ала.\nАста [[Special:UnusedCategories|кулланылмаган төркемнәр]] кәрсәтелгән.\nШулай ук  [[Special:WantedCategories|кирәкле төркемнәр исемлегендә]] карагыз.",
        "special-categories-sort-count": "исәп буенча тәртипләү",
        "special-categories-sort-abc": "әлифба буенча тәртипләү",
+       "deletedcontributions": "Кулланучының бетерелгән кертеме",
+       "deletedcontributions-title": "Бетерелгән кертем",
        "sp-deletedcontributions-contribs": "кертем",
        "linksearch": "Тышкы сылтамаларны эзләү",
        "linksearch-pat": "Эзләү өчен үрнәк:",
        "wlshowhideanons": "аноним кулланучыларныкын",
        "wlshowhidepatr": "тикшерелгән үзгәртүләр",
        "wlshowhidemine": "үзгәртүләрем",
+       "wlshowhidecategorization": "битләрне төркемләүне",
        "watchlist-options": "Күзәтү исемлеге көйләүләре",
        "watching": "Күзәтү исемлегемә өстәүе…",
        "unwatching": "Күзәтү исемлегемнән чыгаруы…",
        "movedarticleprotection": "яклау көйләнмәләрен «[[$2]]» битеннән «[[$1]]» битенә күчерде",
        "protect-title": "«$1» өчен яклау дәрәҗәсен билгеләү",
        "prot_1movedto2": "«[[$1]]» бите «[[$2]]» битенә күчерелде",
+       "protect-norestrictiontypes-title": "Сакланмаган бит",
        "protect-legend": "Битне яклау турында раслагыз",
        "protectcomment": "Сәбәп:",
        "protectexpiry": "Бетә:",
        "protect-level-autoconfirmed": "Автоматик рәвештә расланган кулланучыларга гына рөхсәт ителә",
        "protect-level-sysop": "Идарәчеләргә генә рөхсәт ителә",
        "protect-summary-cascade": "каскадлы",
-       "protect-expiring": "$1 үтә (UTC)",
+       "protect-expiring": "$1 тәмамлана (UTC)",
+       "protect-expiring-local": "$1 тәмамлана",
        "protect-expiry-indefinite": "Вакыт чикләнмәгән",
        "protect-cascade": "Бу биткә кергән битләрне яклау (каскадлы яклау)",
        "protect-cantedit": "Сез бу битнең яклау дәрәҗәсене үзгәрә алмыйсыз, чөнки сездә аны үзгәртергә рөхсәтегез юк.",
        "protect-othertime": "Башка вакыт:",
        "protect-othertime-op": "башка вакыт",
+       "protect-existing-expiry": "Хәзерге тәмамлану вакыты: $2 $3",
+       "protect-existing-expiry-infinity": "Хәзерге тәмамлану вакыты: чикләнмәгән",
+       "protect-otherreason": "Башка/өстәмә сәбәп:",
        "protect-otherreason-op": "Башка сәбәп",
        "protect-dropdown": "* Гади яклау сәбәпләре\n** вандаллык\n** зур спам\n** кирәксез үзгәртүләр саны\n** популяр бит",
        "protect-edit-reasonlist": "Сәбәпләр исемлеген үзгәртү",
        "blocklist-reason": "Сәбәп",
        "ipblocklist-submit": "Эзләү",
        "ipblocklist-localblock": "Локаль тыюлык",
+       "ipblocklist-otherblocks": "Башка {{PLURAL:$1|1=тыю|тыюлар}}",
        "infiniteblock": "билгеле бер вакытсыз",
        "expiringblock": "$1 $2 тәмамлана",
        "anononlyblock": "анонимнар гына",
        "block-log-flags-nocreate": "яңа хисап язмасы теркәү тыелган",
        "block-log-flags-noemail": "хат җибәрү тыелган",
        "block-log-flags-hiddenname": "кулланучының исеме яшерелгән",
+       "ipb-otherblocks-header": "Башка {{PLURAL:$1|1=тыю|тыюлар}}",
        "proxyblocker": "Прокси тыю",
        "sorbsreason": "Сезнең IP адресыгыз DNSBLда ачык прокси дип санала.",
+       "lockdb": "Мәгълүматлар базасын чикләү",
+       "unlockdb": "Мәгълүматлар базасына язу мөмкинлеген кайтару",
+       "lockbtn": "Мәгълүматлар базасын чикләү",
        "unlockbtn": "Мәгълүматлар базасына язу мөмкинлеген кайтару",
+       "locknoconfirm": "Сез раслау юлында билге куймагансыз.",
        "move-page": "$1 — исемен алмаштыру",
        "move-page-legend": "Битне күчерү",
        "movepagetext": "Астагы форманы куллану битнең исемен алыштырып, аның барлык тарихын яңа исемле биткә күчерер.\nИске исемле бит яңа исемле биткә юнәлтү булып калыр.\nСез иске исемгә юнәлтүләрне автоматик рәвештә яңа исемгә күчерә аласыз.\nӘгәр моны эшләмәсәгез, [[Special:DoubleRedirects|икеле]] һәм [[Special:BrokenRedirects|өзелгән юнәлтүләрне]] тикшерегез.\nСез барлык сылтамаларның кирәкле җиргә сылтавына җаваплы.\n\nКүздә тотыгыз: әгәр яңа исем урынында бит булса инде, һәм ул буш яки юнәлтү түгел исә, бит <strong>күчерелмәячәк</strong>.\nБу шуны аңлата: сез ялгышып күчерсәгез, битне кайтара аласыз, әмма инде булган битне бетерә алмыйсыз.\n\n<strong>Искәрмә:</strong>\nПопуляр битләрне күчерү зур һәм көтелмәгән нәтиҗәләргә китерә ала.\nДәвам иткәнче, барлык нәтиҗәләрне аңлавыгызны тагын бер кат уйлагыз.",
        "movelogpage": "Күчерү көндәлеге",
        "movereason": "Сәбәп:",
        "revertmove": "кире кайту",
+       "delete_and_move_confirm": "Әйе, битне бетерү",
        "delete_and_move_reason": "Күчерүне мөмкин итәр өчен бетерелде «[[$1]]»",
        "move-leave-redirect": "Юнәлтү калдырылсын",
        "export": "Битләрне чыгаруы",
        "allmessages-filter-modified": "Үзгәртелгән",
        "allmessages-language": "Тел:",
        "allmessages-filter-submit": "Күчү",
+       "allmessages-filter-translate": "Тәрҗемә итү",
        "thumbnail-more": "Зурайту",
        "filemissing": "Файл табылмады",
        "thumbnail_error": "Кечкенә сүрәт төзүе хатасы: $1",
        "import": "Битләр кертү",
        "importinterwiki": "Башка викидан кертү",
        "import-interwiki-text": "Викины һәм кертелүче битнең исемен языгыз.\nҮзгәртүләр вакыты һәм аның авторлары сакланачак.\nБөтен викиара күчерүләр [[Special:Log/import|махсус журналда]] сакланачак.",
+       "import-interwiki-sourcewiki": "Чыганак вики:",
+       "import-interwiki-sourcepage": "Чыганак бит:",
        "import-interwiki-history": "Бу битнең барлык үзгәртү тарихын күчермәләү",
        "import-interwiki-templates": "Барлык үрнәкләрне кертү",
        "import-interwiki-submit": "Импортлау",
        "import-revision-count": "$1 {{PLURAL:$1|юрама}}",
        "importnopages": "Импортлау өчен битләр юк.",
        "importlogpage": "Кертү көндәлеге",
+       "javascripttest": "JavaScript тикшерү",
+       "javascripttest-pagetext-noframework": "Әлеге бит JavaScript тестларын ачу өчен ясалган.",
        "tooltip-pt-userpage": "{{GENDER:|Кулланучы}} битегез",
        "tooltip-pt-mytalk": "Бәхәс {{GENDER:|битегез}}",
        "tooltip-pt-preferences": "{{GENDER:|Көйләнмәләрегез}}",
        "anonymous": "{{grammar:genitive|{{SITENAME}}}} {{PLURAL:$1|1=Аноним кулланучысы|Аноним кулланучылары}}",
        "siteuser": "{{SITENAME}} кулланучысы $1",
        "othercontribs": "Төзүдә катнаштылар: $1.",
+       "others": "башкалар",
        "siteusers": "{{{{SITENAME}}}} {{PLURAL:$2|1=кулланучы|кулланучылары}} $1",
        "creditspage": "Рәхмәтләр",
        "spamprotectiontitle": "Спам фильтры",
        "simpleantispam-label": "Анти-спам тикшерә.\nМоны <strong>ТУТЫРМАГЫЗ!</strong>",
+       "pageinfo-header-basic": "Төп мәгълүмат",
+       "pageinfo-header-edits": "Үзгәртүләр тарихы",
+       "pageinfo-header-restrictions": "Битне яклау",
+       "pageinfo-header-properties": "Битнең үзенчәлекләре",
+       "pageinfo-display-title": "Күренмә башлык",
+       "pageinfo-default-sort": "Гадәти сайлау ачкычы",
+       "pageinfo-length": "Бит озынлыгы (байтларда)",
+       "pageinfo-article-id": "Бит идентификаторы",
+       "pageinfo-language": "Битнең теле",
+       "pageinfo-robot-index": "Рөхсәт",
+       "pageinfo-robot-noindex": "Рөхсәтсез",
+       "pageinfo-firstuser": "Битне төзүче",
+       "pageinfo-firsttime": "Битне төзү датасы",
+       "pageinfo-lastuser": "Соңгы мөхәррирләүче",
+       "pageinfo-lasttime": "Соңгы үзгәртү датасы",
+       "pageinfo-edits": "Гомуми төзәтүләр саны",
+       "pageinfo-authors": "Гомуми авторлар саны",
        "pageinfo-toolboxlink": "Бит турында мәгълүмат",
+       "pageinfo-redirectsto-info": "мәгълүмат",
+       "pageinfo-contentpage-yes": "Әйе",
+       "pageinfo-protect-cascading-yes": "Әйе",
        "markaspatrolledtext": "Бу мәкаләне тикшерелгән дип тамгалау",
        "markedaspatrolled": "Тикшерелгән дип тамгаланды",
        "markedaspatrolledtext": "Сайланган [[:$1]] мәкаләсенең әлеге юрамасы тикшерелгән дип тамгаланды.",
        "show-big-image-preview": "Алдан карауның зурлыгы: $1.",
        "show-big-image-other": "{{PLURAL:$2|1=Башка зурлык|Башка зурлыклар}}: $1.",
        "show-big-image-size": "$1 × $2 пиксель",
+       "file-info-gif-looped": "әйләнешле",
+       "file-info-gif-frames": "$1 {{PLURAL:$1|фрейм}}",
+       "file-info-png-looped": "әйләнешле",
        "newimages": "Яңа сүрәтләр җыелмасы",
        "newimages-legend": "Фильтр",
        "ilsubmit": "Эзләү",
+       "bydate": "дата буенча",
+       "seconds": "{{PLURAL:$1|$1 секунд}}",
+       "minutes": "{{PLURAL:$1|$1 минут}}",
        "hours": "{{PLURAL:$1|$1 cәгать}}",
        "days": "{{PLURAL:$1|$1 көн}}",
+       "weeks": "{{PLURAL:$1|$1 атна}}",
+       "months": "{{PLURAL:$1|$1 ай}}",
+       "years": "{{PLURAL:$1|$1 ел}}",
        "ago": "$1 элек",
+       "just-now": "яңа гына",
        "hours-ago": "$1 cәгать элек",
        "minutes-ago": "$1 минут элек",
+       "seconds-ago": "$1 {{PLURAL:$1|секунд}} элек",
+       "monday-at": "дүшәмбе $1",
+       "tuesday-at": "сишәмбе $1",
+       "wednesday-at": "чәршәмбе $1",
+       "thursday-at": "пәнҗешәмбе $1",
+       "friday-at": "җомга $1",
+       "saturday-at": "шимбә $1",
+       "sunday-at": "якшәмбе $1",
+       "yesterday-at": "Кичә $1",
        "bad_image_list": "Киләчәк рәвеш кирәк:\n\nИсемлек кисәкләре генә (* символыннан башланучы юллар) саналырлар.\nЮлның беренче сылтамасы куйма өчен тыелган рәсемгә сылтама булырга тиеш.\nШул ук юлның киләчәк сылтамалары чыгармалар, рәсемгә тыелмаган битләре, саналырлар.",
        "metadata": "Мета мәгълүматлар",
        "metadata-help": "Бу файлда гадәттә санлы камера яки сканер тарафыннан өстәлгән мәгълүмат бар. Әгәр бу файл төзү вакытыннан соң үзгәртелгән булса, аның кайбер параметрлары дөрес булмаска мөмкин.",
        "metadata-fields": "Бу исемлеккә кергән метабирелмәләр кырлары рәсем битендә күрсәтелер, калганнары исә килешү буенча яшерелер.\n* make\n* model\n* datetimeoriginal\n* exposuretime\n* fnumber\n* isospeedratings\n* focallength\n* artist\n* copyright\n* imagedescription\n* gpslatitude\n* gpslongitude\n* gpsaltitude",
        "exif-imagewidth": "Киңлек",
        "exif-imagelength": "Биеклек",
+       "exif-bitspersample": "Төс тирәнлеге",
+       "exif-compression": "Кысу ысулы",
+       "exif-photometricinterpretation": "Төс моделе",
        "exif-orientation": "Кадр куелышы",
+       "exif-samplesperpixel": "Төс өлешләре саны",
        "exif-xresolution": "Горизонталь зурлык",
        "exif-yresolution": "Вертикаль зурлык",
        "exif-datetime": "Файл үзгәртүләр датасы һәм вакыты",
        "exif-model": "Камераның төре",
        "exif-software": "Программалы тәэмин ителеш",
        "exif-artist": "Автор",
-       "exif-copyright": "Автор хокуклары хуҗасы",
+       "exif-copyright": "Автор хокуклары иясе",
        "exif-exifversion": "Exif юрамасы",
        "exif-flashpixversion": "FlashPix юрамасын тәэмин итү",
        "exif-colorspace": "Төсләр тирәлеге",
        "exif-gpsspeedref": "Тизлекне исәпләү берәмлеге",
        "exif-gpsspeed": "Хәрәкәт тизлеге",
        "exif-gpsdatestamp": "Дата",
+       "exif-keywords": "Иң мөһиме",
+       "exif-source": "Чыганак",
+       "exif-writer": "Язучы",
+       "exif-languagecode": "Тел",
+       "exif-iimversion": "IIM юрамасы",
+       "exif-iimcategory": "Төркем",
+       "exif-identifier": "Идентификатор",
+       "exif-label": "Билгеләү",
+       "exif-copyrighted": "Автор хокуклары халәте:",
+       "exif-copyrightowner": "Автор хокуклары иясе",
+       "exif-usageterms": "Куллану шартлары",
        "exif-orientation-1": "Нормаль",
        "exif-orientation-3": "180° ка борылган",
        "exif-meteringmode-0": "Билгесез",
index 57ac62a..7d6e783 100644 (file)
@@ -9,7 +9,8 @@
                        "friends at tyvawiki.org",
                        "לערי ריינהארט",
                        "아라",
-                       "Монгуш Салим"
+                       "Монгуш Салим",
+                       "Көпек"
                ]
        },
        "tog-underline": "Холбааны шыяры:",
        "oct": "октябрь",
        "nov": "ноябрь",
        "dec": "декабрь",
-       "pagecategories": "{{PLURAL:$1|Ð\9aаÑ\82егоÑ\80иÑ\8f\9aаÑ\82егоÑ\80иÑ\8fлар}}",
-       "category_header": "«$1» ÐºÐ°Ñ\82егоÑ\80иÑ\8fның арыннары",
+       "pagecategories": "{{PLURAL:$1|Ð\90ңгÑ\8bлал|Ð\90ңгÑ\8bлалдар}}",
+       "category_header": "«$1» Ð°Ò£Ð³Ñ\8bлалдың арыннары",
        "subcategories": "Адаккы бөлүктер",
        "category-media-header": "«$1» деп бөлүкте файлдар",
        "category-empty": "''Амгы бо бөлүкте медиа база арыннар чок.''",
-       "hidden-categories": "{{PLURAL:$1|1=Ð\9aөзүлбеÑ\81 ÐºÐ°Ñ\82егоÑ\80иÑ\8f\9aөзүлбеÑ\81 ÐºÐ°Ñ\82егоÑ\80иÑ\8fлар}}",
+       "hidden-categories": "{{PLURAL:$1|1=Ð\9aөзүлбеÑ\81 Ð°Ò£Ð³Ñ\8bлал|Ð\9aөзүлбеÑ\81 Ð°Ò£Ð³Ñ\8bлалдар}}",
        "hidden-category-category": "Чажыт бөлүктер",
-       "category-subcat-count": "{{PLURAL:$2|1=Ð\91о ÐºÐ°Ñ\82егоÑ\80иÑ\8f Ñ\87үгле Ð´Ð°Ñ\80аазÑ\8bнда Ð¸Ñ\88Ñ\82ики ÐºÐ°Ñ\82егоÑ\80иÑ\8fлÑ\8bг.|Ð\91о ÐºÐ°Ñ\82егоÑ\80иÑ\8fда Ð±Ð°Ñ\80-ла $2 Ð¸Ñ\88Ñ\82ики ÐºÐ°Ñ\82егоÑ\80иÑ\8fлаÑ\80нÑ\8bÒ£ $1 Ð´ÐµÐ¿ Ð¸Ñ\88Ñ\82ики ÐºÐ°Ñ\82егоÑ\80иÑ\8fзы көстүп турар.}}",
-       "category-subcat-count-limited": "Ð\91о Ð°Ò£Ð³Ñ\8bлал {{PLURAL:$1|1=биÑ\80|$1}} Ð°Ò£Ð³Ñ\8bламнÑ\8bг.",
-       "category-article-count": "{{PLURAL:$2|1=Ð\91о ÐºÐ°Ñ\82егоÑ\80иÑ\8fда Ñ\87үгле Ñ\87аңгÑ\8bÑ\81 Ð°Ñ\80Ñ\8bн Ð±Ð°Ñ\80.|Ук ÐºÐ°Ñ\82егоÑ\80иÑ\8fда бар $2 арыннарының аразындан}} |{{PLURAL:$1 арынны көргүскен| $1 арыннарны көргүскен.}}",
-       "category-file-count": "{{PLURAL:$2|1=Ð\91о ÐºÐ°Ñ\82егоÑ\80иÑ\8f Ñ\87үгле Ñ\87аңгÑ\8bÑ\81 Ñ\84айлдÑ\8bг.|Ð\91о ÐºÐ°Ñ\82егоÑ\80иÑ\8fнÑ\8bÒ£ Ñ\88Ñ\83пÑ\82Ñ\83 $2 Ñ\84айлÑ\8bнÑ\8bÒ£ $1 файлын көргүскен.}}",
+       "category-subcat-count": "{{PLURAL:$2|1=Ук Ð°Ò£Ð³Ñ\8bлал Ñ\87үгле Ð´Ð°Ñ\80аазÑ\8bнда Ð¸Ñ\88Ñ\82ики Ð°Ò£Ð³Ñ\8bлалдÑ\8bг.|Ук Ð°Ò£Ð³Ñ\8bлалда Ð±Ð°Ñ\80-ла $2 Ð¸Ñ\88Ñ\82ики Ð°Ò£Ð³Ñ\8bлалдаÑ\80нÑ\8bÒ£ $1 Ð¸Ñ\88Ñ\82ики Ð°Ò£Ð³Ñ\8bлалы көстүп турар.}}",
+       "category-subcat-count-limited": "Ук Ð°Ò£Ð³Ñ\8bлалда {{PLURAL:$1|1=биÑ\80|$1}} Ð¸Ñ\88Ñ\82ики Ð°Ò£Ð³Ñ\8bлал Ð±Ð°Ñ\80.",
+       "category-article-count": "{{PLURAL:$2|1=Ук Ð°Ò£Ð³Ñ\8bлалда Ñ\87үгле Ñ\87аңгÑ\8bÑ\81 Ð°Ñ\80Ñ\8bн Ð±Ð°Ñ\80.|Ук Ð°Ò£Ð³Ñ\8bлалда бар $2 арыннарының аразындан}} |{{PLURAL:$1 арынны көргүскен| $1 арыннарны көргүскен.}}",
+       "category-file-count": "{{PLURAL:$2|1=Ук Ð°Ò£Ð³Ñ\8bлал Ñ\87үгле Ñ\87аңгÑ\8bÑ\81 Ñ\84айлдÑ\8bг.|Ук Ð°Ò£Ð³Ñ\8bлалдÑ\8bÒ£ Ñ\88Ñ\83пÑ\82Ñ\83 $2 Ñ\84айлдаÑ\80Ñ\8bнÑ\8bÒ£ Ð°Ñ\80азÑ\8bндан $1 файлын көргүскен.}}",
        "listingcontinuesabbrev": "(уланчы)",
        "noindex-category": "Индекстелбес арынар",
        "broken-file-category": "Ажылдавайн турар файл-шөлүлгелиг арыннар",
        "mainpage": "Кол арын",
        "mainpage-description": "Кол арын",
        "policy-url": "Project:Чурум",
-       "portal": "Ниитилелдиң порталы",
-       "portal-url": "Project:Ниитилелдиң порталы",
-       "privacy": "Ð\91үзүÑ\80ел Ð´Ñ\83гÑ\83Ñ\80жÑ\83лгазÑ\8b",
+       "portal": "Ниитилел хаалгазы",
+       "portal-url": "Project:Ниитилел хаалгазы",
+       "privacy": "Ð\90кÑ\82Ñ\8bг Ð´Ñ\83Ñ\80жÑ\83лга",
        "privacypage": "Project:Бүзүрел дугуржулгазы",
        "badaccess": "Алдаг:Эргеңер чок.",
        "versionrequired": "МедиаВикиниң $1 үндүреризи херек",
        "nstab-mediawiki": "Чагаа",
        "nstab-template": "Майык",
        "nstab-help": "Дуза",
-       "nstab-category": "Ð\9aаÑ\82егоÑ\80иÑ\8f",
+       "nstab-category": "Ð\90ңгÑ\8bлал",
        "mainpage-nstab": "Кол арын",
        "nosuchaction": "Ындыг кылыг чок",
        "nosuchspecialpage": "Ындыг тускай арын чок",
        "watchthis": "Бо арынны хайгаараар",
        "savearticle": "Арынны шыгжаар",
        "preview": "Чижеглей көөрү",
-       "showpreview": "Чижек ÐºÓ©Ñ\80үлде",
+       "showpreview": "Ð¥Ñ\8bнап ÐºÓ©Ñ\80",
        "showdiff": "Кииртинген эдилгелер",
        "anoneditwarning": "<strong> Кичээңгейлиг! </strong> Сайтта бүрүткеттинмээн-дир силер. Кандыг-даа бол эдилгелер киирер болзуңарза, IP-адрезиңер хөйге көскү болур. Сайтче <strong>[$1 кире бээр азы]</strong> азы <strong>[$2 бүрүткеттинип алыр] болзуңарза, эдилгелер силерниң адыңар-биле холбаалыг апаар, силерге өске-даа эптиг аргаларлыг тыптып кээр.",
        "missingcommenttext": "Тайылбырни адаанда чогаадыңар.",
        "templatesused": "Бо арында ажыглап турар{{PLURAL:$1|1=майык|майыктар}}:",
        "template-protected": "(камгалаан)",
        "template-semiprotected": "(чартык-чамдыызы камгалалдыг)",
-       "hiddencategories": "Бо арын{{PLURAL:$1|$1 көзүлбес категорияга|$1 көзүлбес категорияларга|1=бир көзүлбес категорияга}} хамааржыр:",
+       "hiddencategories": "Бо арын {{PLURAL:$1|$1 көзүлбес аңгылалга|$1 көзүлбес аңгылалдарга|1=чаңгыс көзүлбес аңгылалга}} хамааржыр:",
        "permissionserrorstext-withaction": "{{PLURAL:$1|1=дараазында чылдагаан-биле|дараазында чылдагааннар-биле}} $2-ни ажыглаар эрге силерде чок:",
        "recreate-moveddeleted-warn": "'''Кичээңейлиг. Ооң мурнунда казыттынган арынны катап тургузар деп тур Силер.'''\n\nОл арынны катап тургузары шынап-ла чугула бе, боданыңар.\nБо адаанда ол арынның казыышкыннар болгаш өскээр адалгалар журналдарын көргүскен.",
-       "moveddeleted-notice": "Ð\91о Ð°Ñ\80Ñ\8bн Ð°Ð¿ ÐºÐ°Ð°Ð²Ñ\8bÑ\82кан.\nÐ\90даанда Ð°Ð¿ ÐºÐ°Ð°Ð²Ñ\8bÑ\82кан Ð±Ð¸Ð»Ðµ өскээр адаан бижиктер шынзылгазын көргүскен.",
+       "moveddeleted-notice": "Ук Ð°Ñ\80Ñ\8bн ÐºÐ°Ð·Ñ\8bÑ\82Ñ\8bнган.\nÐ\90даанда ÐºÐ°Ð·Ñ\8bлда Ð±Ð¾Ð»Ð³Ð°Ñ\88 өскээр адаан бижиктер шынзылгазын көргүскен.",
        "post-expand-template-inclusion-warning": "Сагындырыг: Кошкан майыктарның ниити хемчээли дендии улуг.\nЧамдык майыктар коштунмаан боор.",
        "post-expand-template-inclusion-category": "Кожар майыктарга чөшпээрээн хемчээлин ашкан арыннар",
        "post-expand-template-argument-warning": "'''Кичээнгейлиг:''' бо арында тоң дора дээрге (по крайней мере) чаңгыс майыктыг, а ооң аргументизи эмин эрттир улуг калбаяр хемчээлдиг.\nЫндыг чергелиг аргументилерни эрттирип каан.",
        "filehist-filesize": "Файл хемчээли",
        "filehist-comment": "Тайылбыр",
        "imagelinks": "Файлдың ажыглаашкыны",
-       "linkstoimage": "Дараазында {{PLURAL:$1|1=арын|$1 арыннарның шөлүлгези файл}}:",
+       "linkstoimage": "Дараазында {{PLURAL:$1|1=арын|$1 ажыг арын ук файлче айтып турар}}:",
        "nolinkstoimage": "Бердинген файлче шөлүп турар арыннар чок.",
        "sharedupload-desc-here": "Моон $1 алган файл өске төлевилелдерге база ажыглаттынып болур.\nОоң [$2 тайылбыр арнындан] медээни адаанда киирген.",
        "upload-disallowed-here": "Бо файлды эде бижидип шыдавас силер.",
        "movelogpage": "Шимчээринге журнал",
        "movereason": "Чылдагаан:",
        "revertmove": "эгидип тургузары",
-       "delete_and_move": "Ырадыры болгаш шимчээри",
        "export": "Арынар үндүр дамчыдары",
        "allmessages": "Системниң дыңнадыглары",
        "allmessagesname": "Ат",
        "tooltip-p-logo": "Кол арынче кирер",
        "tooltip-n-mainpage": "Кол арынче шилчиир",
        "tooltip-n-mainpage-description": "Кол арынче кирер",
-       "tooltip-n-portal": "Төлевилел дугайында, маңаа  чүнү кылып болурул, ол ышкаш кайда чүү барыл",
+       "tooltip-n-portal": "Төлевилел дугайында, маңаа чүнү кылып болурул, ол ышкаш кайда чүү барыл",
        "tooltip-n-currentevents": "Амгы болуушкуннар дугайында медээ",
        "tooltip-n-recentchanges": "Викиниң сөөлгү өскерлиишкиннер даңзызы",
        "tooltip-n-randompage": "Дужа-келби таварышкан арынны көөр",
        "tooltip-ca-nstab-image": "Файлдың арны",
        "tooltip-ca-nstab-template": "Майыкты көөрү",
        "tooltip-ca-nstab-help": "Дуза арынын көөрү",
-       "tooltip-ca-nstab-category": "Ð\9aаÑ\82егоÑ\80иÑ\8f арны",
+       "tooltip-ca-nstab-category": "Ð\90ңгÑ\8bлал арны",
        "tooltip-minoredit": "Бо өскертилгени \"биче\" деп демдеглээр",
        "tooltip-save": "Эдилгелериңерни шыгжап арттырар",
-       "tooltip-preview": "Арынның чижек көрүлдези: шыгжаар бетинде ону ажыглаар силер!",
+       "tooltip-preview": "Арынны шыгжаарының бетинде: бижээн чүүлүң хынап көр.",
        "tooltip-diff": "Үндезин сөзүглелге хамаарыштыр кылдынган өскерлиишкиннерни көргүзер.",
        "tooltip-compareselectedversions": "Бо арынның шилиттинген ийи хевиринниң ылгалын көөр.",
        "tooltip-watch": "Силерниң хайгаарал даңзызынга бо арынны немерелээри",
index 54dfe40..fd19301 100644 (file)
        "createacct-reason-ph": "Чому ви створюєте інший обліковий запис",
        "createacct-submit": "Створіть ваш обліковий запис",
        "createacct-another-submit": "Створити обліковий запис",
-       "createacct-benefit-heading": "{{SITENAME}} Ñ\81Ñ\82воÑ\80Ñ\8eÑ\94Ñ\82Ñ\8cÑ\81Ñ\8f Ñ\82акими Ñ\81амими Ð»Ñ\8eдÑ\8cми, Ñ\8fк Ñ\96 Ð²и.",
+       "createacct-benefit-heading": "{{SITENAME}} Ñ\81Ñ\82воÑ\80Ñ\8eÑ\94Ñ\82Ñ\8cÑ\81Ñ\8f Ñ\82акими Ñ\81амими Ð»Ñ\8eдÑ\8cми, Ñ\8fк Ñ\96 Ð\92и.",
        "createacct-benefit-body1": "{{PLURAL:$1|редагування|редагування|редагувань}}",
        "createacct-benefit-body2": "{{PLURAL:$1|сторінка|сторінки|сторінок}}",
        "createacct-benefit-body3": "{{PLURAL:$1|дописувач|дописувачі|дописувачів}} цього місяця",
        "botpasswords-deleted-body": "Пароль бота «$1» було видалено",
        "botpasswords-newpassword": "Новий пароль для входу під <strong>$1</strong> — <strong>$2</strong>. <em>Запишіть його для подальшого використання.</em>",
        "botpasswords-no-provider": "BotPasswordsSessionProvider не доступний.",
+       "botpasswords-restriction-failed": "Вхід не було здійснено через обмеження для паролю бота.",
+       "botpasswords-invalid-name": "Вказане ім'я користувача не містить роздільник для пароля бота («$1»).",
+       "botpasswords-not-exist": "У користувача «$1» нема пароля для бота «$2».",
        "resetpass_forbidden": "Пароль не можна змінити",
        "resetpass-no-info": "Щоб звертатися безпосередньо до цієї сторінки, вам слід увійти до системи.",
        "resetpass-submit-loggedin": "Змінити пароль",
        "previewnote": "'''Це лише попередній перегляд.'''\nВаші зміни ще не збережено!",
        "continue-editing": "Продовжити редагування",
        "previewconflict": "Цей попередній перегляд відображає текст з верхнього вікна редагування так, як він буде виглядати, якщо ви вирішите зберегти його.",
-       "session_fail_preview": "'''Система не може зберегти ваші редагування, оскільки втрачені дані сеансу. Будь ласка, повторіть вашу спробу.\nЯкщо помилка буде повторюватись, спробуйте [[Special:UserLogout|вийти з системи]] і увійти знову.'''",
-       "session_fail_preview_html": "<strong>Вибачте! Неможливо зберегти ваші зміни через втрату даних HTML-сесії.</strong>\n\n''Оскільки {{SITENAME}} дозволяє використовувати чистий HTML, попередній перегляд відключено, щоб попередити JavaScript-атаки.''\n\n<strong>Якщо це легітимна спроба редагування, будь ласка, спробуйте ще раз. Якщо не вийде знову, — спробуйте [[Special:UserLogout|завершити сеанс роботи]] й ще раз ввійти до системи.</strong>",
+       "session_fail_preview": "Вибачте! Ваше редагування не вдалося зберегти, оскільки втрачені дані сеансу. \n\nВи могли вийти з системи. <strong>Будь ласка, переконайтеся, що досі залогінені, і спробуйте ще раз</strong>.\nЯкщо це не спрацює, спробуйте [[Special:UserLogout|вийти з системи]] і увійти знову, та перевірте, чи Ваш браузер дозволяє куки з цього сайту.",
+       "session_fail_preview_html": "Вибачте! Неможливо зберегти Ваші зміни через втрату даних HTML-сесії.\n\n<em>Оскільки {{SITENAME}} дозволяє використовувати чистий HTML, попередній перегляд відключено, щоб попередити JavaScript-атаки.</em>\n\n<strong>Якщо це легітимна спроба редагування, будь ласка, спробуйте ще раз.</strong>\nЯкщо не вийде знову, спробуйте [[Special:UserLogout|завершити сеанс роботи]] і ще раз ввійти до системи, та перевірте, чи Ваш браузер дозволяє куки з цього сайту.",
        "token_suffix_mismatch": "'''Ваше редагування було відхилене, оскільки ваша програма неправильно обробляє знаки пунктуації у вікні редагування. Редагування було скасоване для запобігання спотворенню тексту статті.\nПодібні проблеми можуть виникати при використанні анонімізуючих веб-проксі, що містять помилки.'''",
        "edit_form_incomplete": "<strong>Частина даних із форми редагування не досягла сервера. Уважно перевірте, чи не пошкоджені Ваші редагування і спробуйте ще раз.</strong>",
        "editing": "Редагування $1",
        "mergehistory-empty": "Не знайдено версій для об'єднання.",
        "mergehistory-done": "$3 {{PLURAL:$3|редагування|редагування|редагувань}} з $1 успішно {{PLURAL:$3|перенесене|перенесені}} до [[:$2]].",
        "mergehistory-fail": "Не вдалося здійснити об'єднання історій сторінок, будь ласка, перевірте параметри сторінки й часу.",
+       "mergehistory-fail-bad-timestamp": "Недійсна мітка часу.",
+       "mergehistory-fail-invalid-source": "Вихідна сторінка недійсна.",
+       "mergehistory-fail-invalid-dest": "Сторінка призначення недійсна.",
+       "mergehistory-fail-no-change": "Під час об'єднання історій не відбулося об'єднання жодних версій. Будь ласка, перевірте сторінку і параметри часу.",
+       "mergehistory-fail-permission": "Недостатньо прав для об'єднання історії.",
+       "mergehistory-fail-self-merge": "Початкова і цільова сторінки однакові.",
+       "mergehistory-fail-timestamps-overlap": "Вихідні версії перекривають або йдуть після версій призначення.",
        "mergehistory-fail-toobig": "Не вдалося виконати злиття історії оскільки буде перейменовано більше, ніж ліміт у $1 {{PLURAL:$1|версію|версії|версій}}.",
        "mergehistory-no-source": "Вихідна сторінка «$1» не існує.",
        "mergehistory-no-destination": "Цільова сторінка «$1» не існує.",
        "userrights": "Керування правами користувачів",
        "userrights-lookup-user": "Керування групами користувача",
        "userrights-user-editname": "Введіть ім'я користувача:",
-       "editusergroup": "Редагувати групи користувачів",
+       "editusergroup": "Редагувати групи {{GENDER:$1|користувачів}}",
        "editinguser": "Зміна прав {{GENDER:$1|користувача}} <strong>[[User:$1|$1]]</strong> $2",
        "userrights-editusergroup": "Змінити групи користувачів",
-       "saveusergroups": "Зберегти групи користувача",
+       "saveusergroups": "Зберегти групи {{GENDER:$1|користувачів}}",
        "userrights-groupsmember": "Член груп:",
        "userrights-groupsmember-auto": "Неявний член:",
        "userrights-groups-help": "Ви можете змінити групи, до яких належить цей користувач:\n* Якщо біля назви групи стоїть позначка, то користувач належить до цієї групи.\n* Якщо позначка не стоїть — користувач не належить до відповідної групи.\n* Зірочка означає, що ви не можете вилучити користувача з групи, якщо додасте його до неї, і навпаки.",
        "right-createpage": "створення сторінок (але не обговорень)",
        "right-createtalk": "створення обговорень сторінок",
        "right-createaccount": "створення нових облікових записів",
+       "right-autocreateaccount": "Автоматичний вхід в систему із зовнішнього облікового запису користувача",
        "right-minoredit": "позначення редагувань як незначні",
        "right-move": "перейменування сторінок",
        "right-move-subpages": "перейменування сторінок і їх підсторінок",
        "right-managechangetags": "створення та вилучення [[Special:Tags|міток]] з бази даних",
        "right-applychangetags": "додавання [[Special:Tags|міток]] разом зі змінами",
        "right-changetags": "додавання або вилучення будь-яких [[Special:Tags|міток]] для певних версій сторінок або записів журналів",
-       "grant-generic": "Ð\9fÑ\83Ñ\87ок Ð¿Ñ\80ав \"$1\"",
-       "grant-group-page-interaction": "Взаємодіяти з сторінками",
+       "grant-generic": "Ð\9dабÑ\96Ñ\80 Ð¿Ñ\80ав Â«$1»",
+       "grant-group-page-interaction": "Взаємодіяти зі сторінками",
        "grant-group-file-interaction": "Взаємодіяти з медіа",
-       "grant-group-watchlist-interaction": "Ð\92заÑ\94модÑ\96Ñ\8fÑ\82и Ð· Ð²ашим списком спостереження",
+       "grant-group-watchlist-interaction": "Ð\92заÑ\94модÑ\96Ñ\8fÑ\82и Ð· Ð\92ашим списком спостереження",
        "grant-group-email": "Надіслати листа",
-       "grant-group-high-volume": "Виконати великий обсяг діяльності",
+       "grant-group-high-volume": "Виконувати великий обсяг діяльності",
        "grant-group-customization": "Налаштування і переваги",
-       "grant-group-administration": "Виконати адміністративні дії",
+       "grant-group-administration": "Виконувати адміністративні дії",
        "grant-group-other": "Різна діяльність",
-       "grant-blockusers": "Блокування і розблокування користувачів",
-       "grant-createaccount": "Створити облікові записи",
+       "grant-blockusers": "Блокувати і розблокувати користувачів",
+       "grant-createaccount": "Створювати облікові записи",
        "grant-createeditmovepage": "Створювати, редагувати та перейменовувати сторінки",
-       "grant-delete": "Ð\92идаленнÑ\8f Ñ\81Ñ\82оÑ\80Ñ\96нок, Ð²ÐµÑ\80Ñ\81Ñ\96й Ñ\96 Ð·Ð°Ð¿Ð¸Ñ\81Ñ\96в Ð¶Ñ\83Ñ\80налÑ\83",
-       "grant-editinterface": "Редагування простору назв MediaWiki та CSS/JavaScript користувача",
-       "grant-editmycssjs": "Редагування Вашого користувацького CSS/JavaScript",
-       "grant-editmyoptions": "Редагування Ваших налаштувань користувача",
-       "grant-editmywatchlist": "Редагувати список спостереження",
+       "grant-delete": "Ð\92илÑ\83Ñ\87аÑ\82и Ñ\81Ñ\82оÑ\80Ñ\96нки, Ð²ÐµÑ\80Ñ\81Ñ\96Ñ\97 Ñ\96 Ð·Ð°Ð¿Ð¸Ñ\81и Ð¶Ñ\83Ñ\80налÑ\96в",
+       "grant-editinterface": "Редагувати простір назв MediaWiki та CSS/JavaScript користувача",
+       "grant-editmycssjs": "Редагувати Ваш користувацький CSS/JavaScript",
+       "grant-editmyoptions": "Редагувати Ваші налаштування користувача",
+       "grant-editmywatchlist": "Редагувати Ваш список спостереження",
        "grant-editpage": "Редагувати наявні сторінки",
        "grant-editprotected": "Редагувати захищені сторінки",
-       "grant-highvolume": "Редагування великих обсягів",
-       "grant-oversight": "Приховати користувачів і подавити версії",
+       "grant-highvolume": "Редагування у великих обсягах",
+       "grant-oversight": "Приховувати користувачів і версії",
        "grant-patrol": "Патрулювати зміни на сторінках",
-       "grant-protect": "Захистити і зняти захист сторінок",
-       "grant-rollback": "Відкат змін на сторінках",
-       "grant-sendemail": "Відправляти пошту іншим користувачам",
-       "grant-uploadeditmovefile": "Завантажити, замінити та перемістити файли",
-       "grant-uploadfile": "Завантажити нові файли",
+       "grant-protect": "Захищати і знімати захист сторінок",
+       "grant-rollback": "Відкочувати зміни на сторінках",
+       "grant-sendemail": "Надсилати пошту іншим користувачам",
+       "grant-uploadeditmovefile": "Завантажувати, замінювати та перейменовувати файли",
+       "grant-uploadfile": "Завантажувати нові файли",
+       "grant-basic": "Основні права",
        "grant-viewdeleted": "Перегляд видалених файлів і сторінок",
        "grant-viewmywatchlist": "Перегляд списку спостереження",
        "newuserlogpage": "Журнал нових користувачів",
        "action-createpage": "створення сторінок",
        "action-createtalk": "створення сторінок обговорень",
        "action-createaccount": "створення цього облікового запису",
+       "action-autocreateaccount": "автоматичне створення цього зовнішнього облікового запису користувача",
        "action-history": "переглядати історію цієї сторінки",
        "action-minoredit": "позначення цього редагування незначним",
        "action-move": "перейменування цієї сторінки",
        "uploaded-script-svg": " \t\t\nЗнайдений небезпечний елемент з підтримкою сценаріїв «$1» в завантаженому файлі SVG.",
        "uploaded-hostile-svg": " \t\nЗнайдений небезпечний CSS-код в елементі стилю завантаженого файлу SVG.",
        "uploaded-event-handler-on-svg": " \t\nУстановка атрибутів обробника подій <code>$1=\"$2\"</code> не дозволено для SVG-файлів.",
-       "uploaded-href-unsafe-target-svg": "У завантаженому SVG-файлі знайдено href до цілі <code>&lt;$1 $2=\"$3\"&gt;</code>, що не є безпечною.",
+       "uploaded-href-attribute-svg": "href-атрибути в SVG-файлах дозволені лише для посилання на цілі http:// або https://, знайдено <code>&lt;$1 $2=\"$3\"&gt;</code>.",
+       "uploaded-href-unsafe-target-svg": "У завантаженому SVG-файлі знайдено href на небезпечні дані: ціль URI <code>&lt;$1 $2=\"$3\"&gt;</code>.",
        "uploaded-animate-svg": "У завантаженому SVG-файлі знайдено теґ «animate», який може змінювати href, використовуючи атрибут «from» <code>&lt;$1 $2=\"$3\"&gt;</code>.",
        "uploaded-setting-event-handler-svg": "Встановлення  атрибутів обробника подій заблоковане, у завантаженому файлі SVG знайдено <code>&lt;$1 $2=\"$3\"&gt;</code>.",
        "uploaded-setting-href-svg": "Використання теґа «set» для додавання атрибуту «href» у батьківський елемент заблоковано.",
        "upload-too-many-redirects": "URL містить надто багато перенаправлень",
        "upload-http-error": "Відбулася помилка HTTP: $1",
        "upload-copy-upload-invalid-domain": "З цього домену завантаження неможливе.",
+       "upload-foreign-cant-upload": "Ця вікі не налаштована на завантаження файлів у запитаний сторонній файловий репозиторій.",
        "upload-dialog-title": "Завантажити файл",
        "upload-dialog-button-cancel": "Скасувати",
        "upload-dialog-button-done": "Готово",
        "upload-dialog-button-save": "Зберегти",
        "upload-dialog-button-upload": "Завантажити",
-       "upload-form-label-select-file": "Обрати файл",
        "upload-form-label-infoform-title": "Деталі",
        "upload-form-label-infoform-name": "Назва",
        "upload-form-label-infoform-name-tooltip": "Унікальна описова назва файлу. Ви можете використовувати простий текст з пробілами. Не вказуйте розширення файла.",
        "foreign-structured-upload-form-label-own-work-message-shared": "Я підтверджую, що я є власником авторських прав на цей файл та погоджуюся беззастережно поширити його у Вікісховищі на умовах ліцензії [https://creativecommons.org/licenses/by-sa/4.0/deed.uk Creative CommonsAttribution-ShareAlike 4.0], і я згоден з [https://wikimediafoundation.org/wiki/Terms_of_Use умовами використання].",
        "foreign-structured-upload-form-label-not-own-work-message-shared": "Якщо ви не є власником авторських прав на цей файл або ви хочете розповсюджувати його на умовах іншої ліцензії, рекомендуємо використати [https://commons.wikimedia.org/wiki/Special:UploadWizard Майстер завантажень на Вікісховищі].",
        "foreign-structured-upload-form-label-not-own-work-local-shared": "Ви також можете спробувати використати [[Special:Upload|сторінку завантаження на {{GRAMMAR:locative|{{SITENAME}}}}]], якщо цей файл може бути завантажений на цей сайт згідно з його правилами.",
-       "foreign-structured-upload-form-2-label-intro": "Дякуємо Вам за пожертвування зображення, яке буде використано на сайті {{SITENAME}}. Вам слід продовжити, тільки якщо воно відповідає ряду умов:",
-       "foreign-structured-upload-form-2-label-ownwork": "Воно повинно бути виключно <strong>вашою роботою</strong>, а не просто картинкою, завантаженою з Інтернету",
-       "foreign-structured-upload-form-2-label-noderiv": "Воно не повинно <strong>містити чиєїсь чужої роботи</strong> чи бути натхненно нею.",
-       "foreign-structured-upload-form-2-label-useful": "Воно повинно бути <strong>освітнім і корисним</strong> для навчання інших.",
-       "foreign-structured-upload-form-2-label-ccbysa": "Ви маєте бути згодні на те, щоб <strong>опублікувати його в Інтернеті назавжди</strong> під ліцензією [https://creativecommons.org/licenses/by-sa/4.0/deed.uk Creative Commons Attribution-ShareAlike 4.0]",
-       "foreign-structured-upload-form-2-label-alternative": "Якщо не все перераховане вище вірно, ви все одно можете продовжити завантажувати цей файл, використовуючи [https://commons.wikimedia.org/wiki/Special:UploadWizard Майстер завантажень файлів Вікісховища], якщо цей файл доступний під вільною ліцензією.",
-       "foreign-structured-upload-form-2-label-termsofuse": "Завантажуючи цей файл, ви підтверджуєте, що ви є власником авторських прав на нього, і погоджуєтеся беззастережно передати цей файл на Вікісховище під ліцензією Creative Commons Attribution-ShareAlike 4.0, та погоджуєтесь з [https://wikimediafoundation.org/wiki/Terms_of_Use Умовами використання].",
-       "foreign-structured-upload-form-3-label-question-website": "Ви завантажили це зображення з якогось сайту, чи, можливо, знайшли його через пошук зображень?",
-       "foreign-structured-upload-form-3-label-question-ownwork": "Ви створили це зображення (зробили фото, ескіз, креслення тощо) самі?",
-       "foreign-structured-upload-form-3-label-question-noderiv": "Чи є ця робота похідною від іншої роботи, що належить комусь іншому, як, до прикладу, логотип?",
-       "foreign-structured-upload-form-3-label-yes": "Так",
-       "foreign-structured-upload-form-3-label-no": "Ні",
-       "foreign-structured-upload-form-3-label-alternative": "На жаль, в даному випадку, цей інструмент не підтримує завантаження даного файла. Ви все одно можете завантажити його, використовуючи [https://commons.wikimedia.org/wiki/Special:UploadWizard Майстер завантажень файлів Вікісховища], доки він доступний під вільною ліцензією.",
-       "foreign-structured-upload-form-4-label-good": "Використовуючи цей інструмент, ви можете завантажити освітню графіку, яку ви створили, і фотографії, які ви сфотографували, якщо вони не містять робіт, що належать комусь іншому.",
-       "foreign-structured-upload-form-4-label-bad": "Ви не можете завантажувати зображення, знайдені в пошукових системах або скачані з інших сайтів.",
        "backend-fail-stream": "Не вдалося транслювати файл $1.",
        "backend-fail-backup": "Не вдалося створити резервну копію файлу $1.",
        "backend-fail-notexists": "Файл $1 не існує.",
        "querypage-disabled": "Цю спеціальну сторінку вимкнуто для покращення продуктивності.",
        "apihelp": "Довідка з API",
        "apihelp-no-such-module": "Додаток \"$1\" не знайдено.",
+       "apisandbox": "Майданчик для тестування API",
+       "apisandbox-jsonly": "Для використання API-пісочниці потрібен JavaScript.",
+       "apisandbox-api-disabled": "API вимкнуто на цьому сайті.",
+       "apisandbox-intro": "Ця сторінка служить для експериментування з <strong>MediaWiki веб-API</strong>.\nЗверніться до [[mw:API:Main page|документації]] для докладнішої інформації про використання API. Приклад: [//www.mediawiki.org/wiki/API#A_simple_example як отримати вміст головної сторінки]. Виберіть дію, щоб побачити більше прикладів.\n\nЗверніть увагу, що, хоча це пісочниця, дії, виконані вами, на цій сторінці можуть змінити вікі.",
+       "apisandbox-fullscreen": "Розгорнути панель",
+       "apisandbox-fullscreen-tooltip": "Розгорнути панель пісочниці, щоб заповнити вікно браузера.",
+       "apisandbox-unfullscreen": "Показати сторінку",
+       "apisandbox-unfullscreen-tooltip": "Зменшити панель пісочниці, щоб були доступні навігаційні посилання MediaWiki.",
+       "apisandbox-submit": "Зробити запит",
+       "apisandbox-reset": "Очистити",
+       "apisandbox-retry": "Повторити",
+       "apisandbox-loading": "Завантаження інформації для API-модуля «$1»…",
+       "apisandbox-load-error": "Сталася помилка при завантаженні інформації для API-модуля «$1»: $2",
+       "apisandbox-no-parameters": "Цей модуль API не має параметрів.",
+       "apisandbox-helpurls": "Посилання на довідку",
+       "apisandbox-examples": "Приклади",
+       "apisandbox-dynamic-parameters": "Додаткові параметри",
+       "apisandbox-dynamic-parameters-add-label": "Додати параметр:",
+       "apisandbox-dynamic-parameters-add-placeholder": "Назва параметра",
+       "apisandbox-dynamic-error-exists": "Параметр з назвою «$1» вже існує.",
+       "apisandbox-deprecated-parameters": "Застарілі параметри",
+       "apisandbox-fetch-token": "Автозаповнення токена",
+       "apisandbox-submit-invalid-fields-title": "Деякі поля недійсні",
+       "apisandbox-submit-invalid-fields-message": "Будь ласка, виправте відмічені поля і спробуйте знову.",
+       "apisandbox-results": "Результати",
+       "apisandbox-sending-request": "Надсилання запиту API…",
+       "apisandbox-loading-results": "Отримання результатів API…",
+       "apisandbox-results-error": "Сталася помилка при завантаженні відповіді на запит API: $1.",
+       "apisandbox-request-url-label": "URL-адреса запиту:",
+       "apisandbox-request-time": "Час запиту: {{PLURAL:$1|$1 мс}}",
+       "apisandbox-results-fixtoken": "Виправте токен і надішліть ще раз",
+       "apisandbox-results-fixtoken-fail": "Не вдалося викликати токен «$1».",
+       "apisandbox-alert-page": "Поля на цій сторінці не є дійсними.",
+       "apisandbox-alert-field": "Значення цього поля не є допустимим.",
        "booksources": "Джерела книг",
        "booksources-search-legend": "Пошук інформації про книгу",
        "booksources-isbn": "ISBN:",
        "log-title-wildcard": "Знайти заголовки, що починаються з цих символів",
        "showhideselectedlogentries": "Показати/приховати виділені записи журналу",
        "log-edit-tags": "Змінити мітки для вибраних записів журналів",
+       "checkbox-select": "Виберіть: $1",
+       "checkbox-all": "Всі",
+       "checkbox-none": "Нічого",
+       "checkbox-invert": "Інвертувати",
        "allpages": "Усі сторінки",
        "nextpage": "Наступна сторінка ($1)",
        "prevpage": "Попередня сторінка ($1)",
        "listgrouprights-namespaceprotection-header": "Обмеження простору назв",
        "listgrouprights-namespaceprotection-namespace": "Простір назв",
        "listgrouprights-namespaceprotection-restrictedto": "Права, що дозволяють користувачу редагувати",
-       "listgrants-summary": "Нижче наведено список OAuth грантів, з їхнім пов'язаним доступом до прав користувача. Користувачі можуть дозволити програмам використовувати свій обліковий запис, але з обмеженими правами на основі грантів користувача наданих до заяви. Програма діє від імені користувача, однак насправді не може використовувати права, які користувач не має.\nТам може бути [[{{MediaWiki:Listgrouprights-helppage}}|додаткова інформація]] про індивідуальні права.",
+       "listgrants": "Дозвіл",
+       "listgrants-summary": "Нижче наведено список дозволів, з їхнім пов'язаним доступом до прав користувача. Користувачі можуть дозволити програмам використовувати свій обліковий запис, але з обмеженими правами на основі грантів користувача наданих до заяви. Програма діє від імені користувача, однак насправді не може використовувати права, які користувач не має.\nМоже бути [[{{MediaWiki:Listgrouprights-helppage}}|додаткова інформація]] про індивідуальні права.",
        "listgrants-grant": "Грант",
        "listgrants-rights": "Права",
        "trackingcategories": "Відстежувані категорії",
        "block-log-flags-hiddenname": "ім'я користувача приховане",
        "range_block_disabled": "Адміністраторам заборонено блокувати діапазони.",
        "ipb_expiry_invalid": "Невірно вказано термін.",
+       "ipb_expiry_old": "Час закінчення — в минулому.",
        "ipb_expiry_temp": "Блокування із приховуванням імені користувача мають бути безстроковими.",
        "ipb_hide_invalid": "Неможливо приховати обліковий запис; з нього зроблено понад{{PLURAL:$1|одне редагування|$1 редагування|$1 редагувань}}.",
        "ipb_already_blocked": "«$1» уже заблоковано. Для того, щоб призначити новий термін блокування, спочатку розблокуйте його.",
        "lockedbyandtime": "($1 $2 $3)",
        "move-page": "Перейменування сторінки «$1»",
        "move-page-legend": "Перейменування сторінки",
-       "movepagetext": "СкоÑ\80иÑ\81Ñ\82авÑ\88иÑ\81Ñ\8c Ñ\84оÑ\80моÑ\8e Ð½Ð¸Ð¶Ñ\87е, Ð²Ð¸ Ð¼Ð¾Ð¶ÐµÑ\82е Ð¿ÐµÑ\80ейменÑ\83ваÑ\82и Ñ\81Ñ\82оÑ\80Ñ\96нкÑ\83, Ð¾Ð´Ð½Ð¾Ñ\87аÑ\81но Ð¿ÐµÑ\80емÑ\96Ñ\81Ñ\82ивÑ\88и Ð½Ð° Ð½Ð¾Ð²Ðµ Ð¼Ñ\96Ñ\81Ñ\86е Ñ\96 Ð¶Ñ\83Ñ\80нал Ñ\97Ñ\97 Ñ\80едагÑ\83ванÑ\8c.\nСÑ\82аÑ\80а Ð½Ð°Ð·Ð²Ð° Ñ\81Ñ\82ане Ð¿ÐµÑ\80енапÑ\80авленнÑ\8fм Ð½Ð° Ð½Ð¾Ð²Ñ\83 Ð½Ð°Ð·Ð²Ñ\83.\nÐ\92и Ð¼Ð¾Ð¶ÐµÑ\82е Ð°Ð²Ñ\82омаÑ\82иÑ\87но Ð¾Ð½Ð¾Ð²Ð¸Ñ\82и Ð¿ÐµÑ\80енапÑ\80авленнÑ\8f Ð½Ð° Ñ\81Ñ\82аÑ\80Ñ\83 Ð½Ð°Ð·Ð²Ñ\83.\nЯкÑ\89о Ð²Ð¸ Ñ\86Ñ\8cого Ð½Ðµ Ð·Ñ\80обиÑ\82е, Ð±Ñ\83дÑ\8c Ð»Ð°Ñ\81ка, Ð¿ÐµÑ\80евÑ\96Ñ\80Ñ\82е Ð½Ð°Ñ\8fвнÑ\96Ñ\81Ñ\82Ñ\8c [[Special:DoubleRedirects|подвÑ\96йниÑ\85]] Ñ\87и [[Special:BrokenRedirects|Ñ\80озÑ\96Ñ\80ваниÑ\85]] Ð¿ÐµÑ\80енапÑ\80авленÑ\8c.\nÐ\92и Ð²Ñ\96дповÑ\96даÑ\94Ñ\82е Ð·Ð° Ñ\82е, Ñ\89об Ð¿Ð¾Ñ\81иланнÑ\8f Ñ\96 Ð½Ð°Ð´Ð°Ð»Ñ\96 Ð²ÐºÐ°Ð·Ñ\83вали Ñ\82Ñ\83ди, ÐºÑ\83ди Ð¿Ñ\80ипÑ\83Ñ\81калоÑ\81Ñ\8f.\n\nÐ\97веÑ\80нÑ\96Ñ\82Ñ\8c Ñ\83вагÑ\83, Ñ\89о Ñ\81Ñ\82оÑ\80Ñ\96нка '''не''' Ð±Ñ\83де Ð¿ÐµÑ\80ейменована, Ñ\8fкÑ\89о Ñ\81Ñ\82оÑ\80Ñ\96нка Ð· Ð½Ð¾Ð²Ð¾Ñ\8e Ð½Ð°Ð·Ð²Ð¾Ñ\8e Ð²Ð¶Ðµ Ñ\96Ñ\81нÑ\83Ñ\94, Ð¾ÐºÑ\80Ñ\96м Ð²Ð¸Ð¿Ð°Ð´ÐºÑ\96в, ÐºÐ¾Ð»Ð¸ Ð¾Ñ\81Ñ\82аннÑ\8f Ð¿Ð¾Ñ\80ожнÑ\8f Ð°Ð±Ð¾ Ñ\94 Ð¿ÐµÑ\80енапÑ\80авленнÑ\8fм, Ð° Ð¶Ñ\83Ñ\80нал Ñ\97Ñ\97 Ñ\80едагÑ\83ванÑ\8c Ð¿Ð¾Ñ\80ожнÑ\96й.\nЦе Ð¾Ð·Ð½Ð°Ñ\87аÑ\94, Ñ\89о Ð²Ð¸ Ð¼Ð¾Ð¶ÐµÑ\82е Ð¿Ð¾Ð²ÐµÑ\80нÑ\83Ñ\82и Ñ\81Ñ\82оÑ\80Ñ\96нÑ\86Ñ\96 Ñ\81Ñ\82аÑ\80Ñ\83 Ð½Ð°Ð·Ð²Ñ\83, Ñ\8fкÑ\89о Ð²Ð¸ Ð¿ÐµÑ\80ейменÑ\83вали Ñ\97Ñ\97 Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ¾Ð²Ð¾, Ð°Ð»Ðµ Ð²Ð¸ Ð½Ðµ Ð¼Ð¾Ð¶ÐµÑ\82е Ð·Ð°Ñ\82еÑ\80Ñ\82и Ñ\96Ñ\81нÑ\83Ñ\8eÑ\87Ñ\83 Ñ\81Ñ\82оÑ\80Ñ\96нкÑ\83.\n\n'''Ð\9fÐ\9eÐ\9fÐ\95РÐ\95Ð\94Ð\96Ð\95Ð\9dÐ\9dЯ!'''\nЦÑ\8f Ð´Ñ\96Ñ\8f Ð¼Ð¾Ð¶Ðµ Ñ\81Ñ\82аÑ\82и Ð¿Ñ\80иÑ\87иноÑ\8e Ñ\81еÑ\80йозниÑ\85 Ñ\82а Ð½ÐµÐ¾Ñ\87Ñ\96кÑ\83ваниÑ\85 Ð·Ð¼Ñ\96н Ð¿Ð¾Ð¿Ñ\83лÑ\8fÑ\80ниÑ\85 Ñ\81Ñ\82оÑ\80Ñ\96нок.\nÐ\91Ñ\83дÑ\8c Ð»Ð°Ñ\81ка, Ð¿ÐµÑ\80ед Ð¿Ñ\80одовженнÑ\8fм Ð¿ÐµÑ\80еконайÑ\82еÑ\81Ñ\8c, Ñ\89о Ð²и розумієте всі можливі наслідки.",
-       "movepagetext-noredirectfixer": "Ця форма дозволяє перейменувати сторінку з одночасним переміщенням її журналу змін.\nСтара назва стане перенаправленням на нову.\nБудь ласка, не забудьте виправити [[Special:DoubleRedirects|подвійні]] та [[Special:BrokenRedirects|розірвані перенаправлення]].\nВи відповідаєте за те, щоб посилання і далі вказували туди, куди треба.\n\nЗверніть увагу, що сторінка '''не буде''' перейменована, якщо сторінка з новою назвою вже існує, крім випадків, коли вона є перенаправленням або порожня та не має історії редагувань.\nЦе означає, що Ви можете перейменувати сторінку назад, якщо Ви допустилися помилки, і при цьому не зможете випадково перезаписати наявну сторінку.\n\n'''Попередження!'''\nПерейменування може призвести до масштабних і несподіваних змін для ''популярних'' сторінок.\nТому перед перейменуванням упевніться, що Ви оцінили можливі наслідки.",
+       "movepagetext": "СкоÑ\80иÑ\81Ñ\82авÑ\88иÑ\81Ñ\8c Ñ\84оÑ\80моÑ\8e Ð½Ð¸Ð¶Ñ\87е, Ð\92и Ð¼Ð¾Ð¶ÐµÑ\82е Ð¿ÐµÑ\80ейменÑ\83ваÑ\82и Ñ\81Ñ\82оÑ\80Ñ\96нкÑ\83, Ð¾Ð´Ð½Ð¾Ñ\87аÑ\81но Ð¿ÐµÑ\80емÑ\96Ñ\81Ñ\82ивÑ\88и Ð½Ð° Ð½Ð¾Ð²Ðµ Ð¼Ñ\96Ñ\81Ñ\86е Ñ\96 Ð¶Ñ\83Ñ\80нал Ñ\97Ñ\97 Ñ\80едагÑ\83ванÑ\8c.\nСÑ\82аÑ\80а Ð½Ð°Ð·Ð²Ð° Ñ\81Ñ\82ане Ð¿ÐµÑ\80енапÑ\80авленнÑ\8fм Ð½Ð° Ð½Ð¾Ð²Ñ\83 Ð½Ð°Ð·Ð²Ñ\83.\nÐ\92и Ð¼Ð¾Ð¶ÐµÑ\82е Ð°Ð²Ñ\82омаÑ\82иÑ\87но Ð¾Ð½Ð¾Ð²Ð¸Ñ\82и Ð¿ÐµÑ\80енапÑ\80авленнÑ\8f Ð½Ð° Ñ\81Ñ\82аÑ\80Ñ\83 Ð½Ð°Ð·Ð²Ñ\83.\nЯкÑ\89о Ð\92и Ñ\86Ñ\8cого Ð½Ðµ Ð·Ñ\80обиÑ\82е, Ð±Ñ\83дÑ\8c Ð»Ð°Ñ\81ка, Ð¿ÐµÑ\80евÑ\96Ñ\80Ñ\82е Ð½Ð°Ñ\8fвнÑ\96Ñ\81Ñ\82Ñ\8c [[Special:DoubleRedirects|подвÑ\96йниÑ\85]] Ñ\87и [[Special:BrokenRedirects|Ñ\80озÑ\96Ñ\80ваниÑ\85]] Ð¿ÐµÑ\80енапÑ\80авленÑ\8c.\nÐ\92и Ð²Ñ\96дповÑ\96даÑ\94Ñ\82е Ð·Ð° Ñ\82е, Ñ\89об Ð¿Ð¾Ñ\81иланнÑ\8f Ñ\96 Ð½Ð°Ð´Ð°Ð»Ñ\96 Ð²ÐºÐ°Ð·Ñ\83вали Ñ\82Ñ\83ди, ÐºÑ\83ди Ð¿Ñ\80ипÑ\83Ñ\81калоÑ\81Ñ\8f.\n\nÐ\97веÑ\80нÑ\96Ñ\82Ñ\8c Ñ\83вагÑ\83, Ñ\89о Ñ\81Ñ\82оÑ\80Ñ\96нка <strong>не</strong> Ð±Ñ\83де Ð¿ÐµÑ\80ейменована, Ñ\8fкÑ\89о Ñ\81Ñ\82оÑ\80Ñ\96нка Ð· Ð½Ð¾Ð²Ð¾Ñ\8e Ð½Ð°Ð·Ð²Ð¾Ñ\8e Ð²Ð¶Ðµ Ñ\96Ñ\81нÑ\83Ñ\94, Ð¾ÐºÑ\80Ñ\96м Ð²Ð¸Ð¿Ð°Ð´ÐºÑ\96в, ÐºÐ¾Ð»Ð¸ Ð¾Ñ\81Ñ\82аннÑ\8f Ð¿Ð¾Ñ\80ожнÑ\8f Ð°Ð±Ð¾ Ñ\94 Ð¿ÐµÑ\80енапÑ\80авленнÑ\8fм, Ð° Ð¶Ñ\83Ñ\80нал Ñ\97Ñ\97 Ñ\80едагÑ\83ванÑ\8c Ð¿Ð¾Ñ\80ожнÑ\96й.\nЦе Ð¾Ð·Ð½Ð°Ñ\87аÑ\94, Ñ\89о Ð\92и Ð¼Ð¾Ð¶ÐµÑ\82е Ð¿Ð¾Ð²ÐµÑ\80нÑ\83Ñ\82и Ñ\81Ñ\82оÑ\80Ñ\96нÑ\86Ñ\96 Ñ\81Ñ\82аÑ\80Ñ\83 Ð½Ð°Ð·Ð²Ñ\83, Ñ\8fкÑ\89о Ð¿ÐµÑ\80ейменÑ\83вали Ñ\97Ñ\97 Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ¾Ð²Ð¾, Ð°Ð»Ðµ Ð\92и Ð½Ðµ Ð¼Ð¾Ð¶ÐµÑ\82е Ð·Ð°Ñ\82еÑ\80Ñ\82и Ñ\96Ñ\81нÑ\83Ñ\8eÑ\87Ñ\83 Ñ\81Ñ\82оÑ\80Ñ\96нкÑ\83.\n\n<strong>Ð\9fÑ\80имÑ\96Ñ\82ка</strong>\nЦÑ\8f Ð´Ñ\96Ñ\8f Ð¼Ð¾Ð¶Ðµ Ñ\81Ñ\82аÑ\82и Ð¿Ñ\80иÑ\87иноÑ\8e Ñ\81еÑ\80йозниÑ\85 Ñ\82а Ð½ÐµÐ¾Ñ\87Ñ\96кÑ\83ваниÑ\85 Ð·Ð¼Ñ\96н Ð¿Ð¾Ð¿Ñ\83лÑ\8fÑ\80ниÑ\85 Ñ\81Ñ\82оÑ\80Ñ\96нок.\nÐ\91Ñ\83дÑ\8c Ð»Ð°Ñ\81ка, Ð¿ÐµÑ\80ед Ð¿Ñ\80одовженнÑ\8fм Ð¿ÐµÑ\80еконайÑ\82еÑ\81Ñ\8f, Ñ\89о Ð\92и розумієте всі можливі наслідки.",
+       "movepagetext-noredirectfixer": "Ця форма дозволяє перейменувати сторінку з одночасним переміщенням її журналу змін.\nСтара назва стане перенаправленням на нову.\nБудь ласка, не забудьте виправити [[Special:DoubleRedirects|подвійні]] та [[Special:BrokenRedirects|розірвані перенаправлення]].\nВи відповідаєте за те, щоб посилання і далі вказували туди, куди треба.\n\nЗверніть увагу, що сторінка <strong>не буде</strong> перейменована, якщо сторінка з новою назвою вже існує, крім випадків, коли вона є перенаправленням або порожня та не має історії редагувань.\nЦе означає, що Ви можете перейменувати сторінку назад, якщо Ви допустилися помилки, і при цьому не зможете випадково перезаписати наявну сторінку.\n\n<strong>Примітка</strong>\nПерейменування може призвести до масштабних і несподіваних змін для популярних сторінок.\nТому перед перейменуванням упевніться, що Ви оцінили можливі наслідки.",
        "movepagetalktext": "Якщо ви встановите тут прапорець, приєднана сторінка обговорення також буде автоматично перейменована, окрім випадку, коли непорожня сторінка обговорення з такою назвою вже існує.\n\nУ цьому разі ви будете змушені перейменувати чи об'єднати сторінки вручну в разі необхідності.",
        "moveuserpage-warning": "'''Увага:''' Ви збираєтеся перейменувати сторінку користувача. Будь ласка, зверніть увагу, що  буде перейменовано тільки сторінку, але користувача '''не''' буде перейменовано.",
        "movecategorypage-warning": "<strong>Увага:</strong> Ви збираєтесь перейменувати сторінку категорії. Будь ласка, зауважте, що це перейменує лише цю сторінку, <em>не</em> перемістивши сторінки, що входять до категорії до категорії з новою назвою.",
        "movenosubpage": "Ця сторінка не має підсторінок.",
        "movereason": "Причина:",
        "revertmove": "відкинути",
-       "delete_and_move_text": "== Потрібне вилучення ==\nСторінка з назвою [[:$1|«$1»]] вже існує.\nБажаєте вилучити її для можливості перейменування?",
+       "delete_and_move_text": "Сторінка з назвою [[:$1|«$1»]] вже існує.\nБажаєте вилучити її для можливості перейменування?",
        "delete_and_move_confirm": "Так, вилучити для перейменування",
        "delete_and_move_reason": "Вилучена для можливості перейменування сторінки «[[$1]]»",
        "selfmove": "Неможливо перейменувати сторінку: поточна й нова назви сторінки співпадають.",
        "move-leave-redirect": "Залишити перенаправлення",
        "protectedpagemovewarning": "'''Попередження:''' Ця сторінка захищена так, що її можуть перейменовувати тільки адміністратори.\nОстанній запис журналу наведений нижче для довідки:",
        "semiprotectedpagemovewarning": "'''Зауваження:''' Ця сторінка захищена так, що перейменовувати її можуть тільки зареєстровані користувачі.\nОстанній запис журналу наведений нижче для довідки:",
-       "move-over-sharedrepo": "== Файл існує ==\nУ спільному сховищі існує [[:$1]]. Перейменування файлу на цю назву призведе до перекриття файлу зі спільного сховища.",
+       "move-over-sharedrepo": "У спільному сховищі існує [[:$1]]. Перейменування файлу на цю назву призведе до перекриття файлу зі спільного сховища.",
        "file-exists-sharedrepo": "Обрана назва файлу вже використовується у спільному сховищі.\nБудь ласка, оберіть іншу назву.",
        "export": "Експорт статей",
        "exporttext": "Ви можете експортувати текст та журнал змін конкретної сторінки чи кількох сторінок у XML, який пізніше можна [[Special:Import|імпортувати]] в іншу вікі, що використовує програмне забезпечення MediaWiki.\n\nЩоб експортувати сторінки, введіть їх назви в поле редагування, одну назву на рядок і оберіть, бажаєте ви експортувати всю історію змін сторінок чи тільки останні версії статей.\n\nВи також можете використовувати спеціальну адресу для експорту тільки останньої версії. Наприклад, для сторінки «[[{{MediaWiki:Mainpage}}]]» ця адреса така: [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]].",
        "import-nonewrevisions": "Ніякі поправки не були імпортовані (всі вже були оброблені, або пропущені через помилки).",
        "xml-error-string": "$1 в рядку $2, позиції $3 (байт $4): $5",
        "import-upload": "Завантажити XML-дані",
-       "import-token-mismatch": "УÑ\82Ñ\80аÑ\87енÑ\96 Ð´Ð°Ð½Ñ\96 Ñ\81еанÑ\81Ñ\83. Ð\91Ñ\83дÑ\8c Ð»Ð°Ñ\81ка, Ñ\81пÑ\80обÑ\83йÑ\82е Ñ\89е Ñ\80аз.",
+       "import-token-mismatch": "Ð\92Ñ\82Ñ\80аÑ\87енÑ\96 Ð´Ð°Ð½Ñ\96 Ñ\81еанÑ\81Ñ\83. \n\nÐ\92и Ð¼Ð¾Ð³Ð»Ð¸ Ð²Ð¸Ð¹Ñ\82и Ð· Ñ\81иÑ\81Ñ\82еми. <strong>Ð\91Ñ\83дÑ\8c Ð»Ð°Ñ\81ка, Ð¿ÐµÑ\80еконайÑ\82еÑ\81Ñ\8f, Ñ\89о Ð´Ð¾Ñ\81Ñ\96 Ð·Ð°Ð»Ð¾Ð³Ñ\96ненÑ\96, Ñ\96 Ñ\81пÑ\80обÑ\83йÑ\82е Ñ\89е Ñ\80аз</strong>.\nЯкÑ\89о Ñ\86е Ð½Ðµ Ñ\81пÑ\80аÑ\86Ñ\8eÑ\94, Ñ\81пÑ\80обÑ\83йÑ\82е [[Special:UserLogout|вийÑ\82и Ð· Ñ\81иÑ\81Ñ\82еми]] Ñ\96 Ñ\83вÑ\96йÑ\82и Ð·Ð½Ð¾Ð²Ñ\83, Ñ\82а Ð¿ÐµÑ\80евÑ\96Ñ\80Ñ\82е, Ñ\87и Ð\92аÑ\88 Ð±Ñ\80аÑ\83зеÑ\80 Ð´Ð¾Ð·Ð²Ð¾Ð»Ñ\8fÑ\94 ÐºÑ\83ки Ð· Ñ\86Ñ\8cого Ñ\81айÑ\82Ñ\83.",
        "import-invalid-interwiki": "Неможливо імпортувати із зазначеної вікі.",
        "import-error-edit": "Сторінку «$1» не було імпортовано, оскільки Вам не дозволено її редагувати.",
        "import-error-create": "Сторінку «$1» не було імпортовано, оскільки Вам не дозволено її створювати.",
        "javascripttest-pagetext-frameworks": "Будь ласка, оберіть одне з наступних середовищ тестування: $1",
        "javascripttest-pagetext-skins": "Виберіть оформлення сторінки запуску тесту:",
        "javascripttest-qunit-intro": "Переглянути [ $1  тестування документації] на mediawiki.org.",
-       "tooltip-pt-userpage": "Ваша сторінка користувача",
+       "tooltip-pt-userpage": "{{GENDER:|Ваша}} сторінка користувача",
        "tooltip-pt-anonuserpage": "Сторінка користувача для вашої IP-адреси",
-       "tooltip-pt-mytalk": "Ваша сторінка обговорення",
+       "tooltip-pt-mytalk": "{{GENDER:|Ваша}} сторінка обговорення",
        "tooltip-pt-anontalk": "Обговорення редагувань з цієї IP-адреси",
-       "tooltip-pt-preferences": "Ваші налаштування",
+       "tooltip-pt-preferences": "{{GENDER:|Ваші}} налаштування",
        "tooltip-pt-watchlist": "Список сторінок, за змінами в яких Ви спостерігаєте",
-       "tooltip-pt-mycontris": "Ваш внесок",
+       "tooltip-pt-mycontris": "{{GENDER:|Ваш}} внесок",
        "tooltip-pt-anoncontribs": "Список редагувань, зроблених з цієї IP-адреси",
        "tooltip-pt-login": "Тут можна зареєструватися в системі, але це не обов'язково.",
        "tooltip-pt-logout": "Вихід із системи",
        "tooltip-t-recentchangeslinked": "Останні зміни на сторінках, на які посилається ця сторінка",
        "tooltip-feed-rss": "Трансляція в RSS для цієї сторінки",
        "tooltip-feed-atom": "Трансляція в Atom для цієї сторінки",
-       "tooltip-t-contributions": "Ð\9fеÑ\80еглÑ\8fд Ð²Ð½ÐµÑ\81кÑ\83 Ñ\86Ñ\8cого ÐºÐ¾Ñ\80иÑ\81Ñ\82Ñ\83ваÑ\87а",
-       "tooltip-t-emailuser": "Надіслати листа цьому користувачеві",
+       "tooltip-t-contributions": "Ð\92неÑ\81ок {{GENDER:$1|Ñ\86Ñ\8cого ÐºÐ¾Ñ\80иÑ\81Ñ\82Ñ\83ваÑ\87а|Ñ\86Ñ\96Ñ\94Ñ\97 ÐºÐ¾Ñ\80иÑ\81Ñ\82Ñ\83ваÑ\87ки}}",
+       "tooltip-t-emailuser": "Надіслати листа {{GENDER:$1|цьому користувачеві|цій користувачці}}",
        "tooltip-t-info": "Додаткові відомості про цю сторінку",
        "tooltip-t-upload": "Завантажити файли",
        "tooltip-t-specialpages": "Перелік спеціальних сторінок",
        "lastmodifiedatby": "Остання зміна $2, $1 користувачем $3.",
        "othercontribs": "Базується на праці $1.",
        "others": "інші",
-       "siteusers": "{{PLURAL:$2|1=Користувач|Користувачі}} {{grammar:genitive|{{SITENAME}}}} $1",
+       "siteusers": "{{PLURAL:$2|1={{GENDER:$1|Користувач|Користувачка}}|Користувачі}} {{grammar:genitive|{{SITENAME}}}} $1",
        "anonusers": "{{PLURAL:$2|1=анонімний користувач|анонімні користувачі}} {{grammar:genitive|{{SITENAME}}}} $1",
        "creditspage": "Подяки",
        "nocredits": "Відсутній список користувачів для цієї статті",
        "scarytranscludefailed-httpstatus": "[Не вдалось завантажити шаблон для $1: HTTP $2]",
        "scarytranscludetoolong": "[URL дуже довгий]",
        "deletedwhileediting": "'''Увага:''' ця сторінка була вилучена після того, як ви розпочали редагування!",
-       "confirmrecreate": "Користувач [[User:$1|$1]] ([[User talk:$1|обговорення]]) вилучив цю сторінку після того, як ви почали редагування і зазначив причиною:\n: ''$2''\nБудь ласка, підтвердьте, що ви дійсно бажаєте створити цю сторінку заново.",
-       "confirmrecreate-noreason": "Користувач [[User:$1|$1]] ([[User talk:$1|обг]]) вилучив цю сторінку після того, як ви її почали редагувати. Будь-ласка, підтвердіть, що ви дійсно хочете її відновити.",
+       "confirmrecreate": "{{GENDER:$1|Користувач|Користувачка}} [[User:$1|$1]] ([[User talk:$1|обговорення]]) {{GENDER:$1|вилучив|вилучила}} цю сторінку після того, як ви почали редагування і зазначив причиною:\n: <em>$2</em>\nБудь ласка, підтвердіть, що Ви дійсно бажаєте створити цю сторінку заново.",
+       "confirmrecreate-noreason": "{{GENDER:$1|Користувач|Користувачка}} [[User:$1|$1]] ([[User talk:$1|обговорення]]) {{GENDER:$1|вилучив|вилучила}} цю сторінку після того, як Ви її почали редагувати. Будь ласка, підтвердіть, що Ви дійсно хочете її відновити.",
        "recreate": "Повторно створити",
        "unit-pixel": " пікс.",
        "confirm_purge_button": "Гаразд",
        "watchlistedit-clear-legend": "Очистити список спостереження",
        "watchlistedit-clear-explain": "Усі сторінки буде вилучено з Вашого списку спостереження",
        "watchlistedit-clear-titles": "Сторінки:",
-       "watchlistedit-clear-submit": "Ð\9eÑ\87иÑ\81Ñ\82иÑ\82и Ñ\81пиÑ\81ок Ñ\81поÑ\81Ñ\82еÑ\80еженнÑ\8f (Ñ\86е Ð±ÐµÐ·Ð¿Ð¾Ð²Ð¾Ñ\80оÑ\82нÑ\8cо!)",
+       "watchlistedit-clear-submit": "Ð\9eÑ\87иÑ\81Ñ\82иÑ\82и Ñ\81пиÑ\81ок Ñ\81поÑ\81Ñ\82еÑ\80еженнÑ\8f (Ñ\86е Ð½ÐµÐ·Ð²Ð¾Ñ\80оÑ\82но!)",
        "watchlistedit-clear-done": "Ваш список спостереження було очищено.",
        "watchlistedit-clear-removed": "Було видалено {{PLURAL:$1|1 запис|$1 записів|$1 записи}}:",
        "watchlistedit-too-many": "Забагато сторінок для відображення тут.",
        "version-libraries-license": "Ліцензія",
        "version-libraries-description": "Опис",
        "version-libraries-authors": "Автори",
-       "redirect": "Перенаправлення за файлом, користувачем, сторінкою або ID версії",
+       "redirect": "Перенаправлення за файлом, користувачем, сторінкою, версією або ID журналу",
        "redirect-legend": "Перенаправити на файл чи сторінку",
-       "redirect-summary": "Ця спеціальна сторінка перенаправляє на файл (за поданою назвою файлу), сторінку (за поданим ID версії або сторінки) або сторінку користувача (за поданим числовим ID користувача). Використання: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]],[[{{#Special:Redirect}}/revision/328429]] або [[{{#Special:Redirect}}/user/101]].",
+       "redirect-summary": "Ця спеціальна сторінка перенаправляє на файл (за поданою назвою файлу), сторінку (за поданим ID версії або сторінки), сторінку користувача (за поданим числовим ID користувача) або запис журлану (за поданим ID журналу). Використання: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]],[[{{#Special:Redirect}}/revision/328429]], [[{{#Special:Redirect}}/user/101]] або [[{{#Special:Redirect}}/logid/186]].",
        "redirect-submit": "Перейти",
        "redirect-lookup": "Шукати:",
        "redirect-value": "Значення:",
        "redirect-page": "ID сторінки",
        "redirect-revision": "Версія сторінки",
        "redirect-file": "Назва файлу",
+       "redirect-logid": "ID журналу",
        "redirect-not-exists": "Значення не знайдено",
        "fileduplicatesearch": "Пошук файлів-дублікатів",
        "fileduplicatesearch-summary": "Пошук дублікатів файлів на основі хеш-значень.",
        "expand_templates_generate_xml": "Показати дерево аналізу XML",
        "expand_templates_generate_rawhtml": "Показати сирий HTML",
        "expand_templates_preview": "Попередній перегляд",
-       "expand_templates_preview_fail_html": "<em>Ð\9eÑ\81кÑ\96лÑ\8cки {{SITENAME}} Ð¼Ð°Ñ\94 Ð²Ð²Ñ\96мкненим Ñ\81иÑ\80ий HTML Ñ\96 Ð²Ñ\96дбÑ\83лаÑ\81Ñ\8c Ð²Ñ\82Ñ\80аÑ\82а Ð´Ð°Ð½Ð¸Ñ\85 Ñ\81еÑ\81Ñ\96Ñ\97, Ð¿Ð¾Ð¿ÐµÑ\80еднÑ\96й Ð¿ÐµÑ\80еглÑ\8fд Ð¿Ñ\80иÑ\85ований Ñ\8fк Ð·Ð°Ñ\85Ñ\96д Ð±ÐµÐ·Ð¿ÐµÐºÐ¸ Ð²Ñ\96д JavaScript-аÑ\82ак.</em>\n\n<strong>ЯкÑ\89о Ñ\86е Ð¿Ñ\80авомÑ\96Ñ\80на Ñ\81пÑ\80оба Ð¿Ð¾Ð¿ÐµÑ\80еднÑ\8cого Ð¿ÐµÑ\80еглÑ\8fдÑ\83, Ð±Ñ\83дÑ\8c Ð»Ð°Ñ\81ка, Ñ\81пÑ\80обÑ\83йÑ\82е Ð·Ð½Ð¾Ð²Ñ\83.</strong>\nЯкÑ\89о Ñ\86е Ð´Ð¾Ñ\81Ñ\96 Ð½Ðµ Ð¿Ñ\80аÑ\86Ñ\8eÑ\94, Ñ\81пÑ\80обÑ\83йÑ\82е [[Special:UserLogout|вийÑ\82и Ñ\96з Ñ\81иÑ\81Ñ\82еми]] Ñ\82а Ð·Ð½Ð¾Ð²Ñ\83 Ð²Ð²Ñ\96йÑ\82и Ð´Ð¾ Ð½ÐµÑ\97.",
+       "expand_templates_preview_fail_html": "<em>Ð\9eÑ\81кÑ\96лÑ\8cки {{SITENAME}} Ð¼Ð°Ñ\94 Ð²Ð²Ñ\96мкнений Ñ\81иÑ\80ий HTML Ñ\96 Ð²Ñ\96дбÑ\83лаÑ\81Ñ\8f Ð²Ñ\82Ñ\80аÑ\82а Ð´Ð°Ð½Ð¸Ñ\85 Ñ\81еÑ\81Ñ\96Ñ\97, Ð¿Ð¾Ð¿ÐµÑ\80еднÑ\96й Ð¿ÐµÑ\80еглÑ\8fд Ð¿Ñ\80иÑ\85ований Ñ\8fк Ð·Ð°Ñ\85Ñ\96д Ð±ÐµÐ·Ð¿ÐµÐºÐ¸ Ð²Ñ\96д JavaScript-аÑ\82ак.</em>\n\n<strong>ЯкÑ\89о Ñ\86е Ð¿Ñ\80авомÑ\96Ñ\80на Ñ\81пÑ\80оба Ð¿Ð¾Ð¿ÐµÑ\80еднÑ\8cого Ð¿ÐµÑ\80еглÑ\8fдÑ\83, Ð±Ñ\83дÑ\8c Ð»Ð°Ñ\81ка, Ñ\81пÑ\80обÑ\83йÑ\82е Ð·Ð½Ð¾Ð²Ñ\83.</strong>\nЯкÑ\89о Ñ\86е Ð´Ð°Ð»Ñ\96 Ð½Ðµ Ð¿Ñ\80аÑ\86Ñ\8eÑ\94, Ñ\81пÑ\80обÑ\83йÑ\82е [[Special:UserLogout|вийÑ\82и Ñ\96з Ñ\81иÑ\81Ñ\82еми]] Ñ\96 Ð·Ð½Ð¾Ð²Ñ\83 Ð²Ð²Ñ\96йÑ\82и, Ñ\82а Ð¿ÐµÑ\80евÑ\96Ñ\80Ñ\82е, Ñ\87и Ð\92аÑ\88 Ð±Ñ\80аÑ\83зеÑ\80 Ð´Ð¾Ð·Ð²Ð¾Ð»Ñ\8fÑ\94 ÐºÑ\83ки Ð· Ñ\86Ñ\8cого Ñ\81айÑ\82Ñ\83.",
        "expand_templates_preview_fail_html_anon": "<em>Оскільки {{SITENAME}} має ввімкненим сирий HTML, а Ви не ввійшли до системи, попередній перегляд прихований як захід безпеки від JavaScript-атак.</em>\n\n<strong>Якщо це правомірна спроба попереднього перегляду, будь ласка, [[Special:UserLogin|увійдіть до системи]] та спробуйте знову.</strong>",
        "expand_templates_input_missing": "Ви повинні надати принаймні деякий вхідний текст.",
-       "pagelanguage": "Ð\92ибÑ\96Ñ\80 Ð¼Ð¾Ð²Ð¸ сторінки",
+       "pagelanguage": "Ð\97мÑ\96ниÑ\82и Ð¼Ð¾Ð²Ñ\83 сторінки",
        "pagelang-name": "Сторінка",
        "pagelang-language": "Мова",
        "pagelang-use-default": "Мова за замовчуванням",
        "action-pagelang": "змінити мову сторінки",
        "log-name-pagelang": "Журнал змін мови",
        "log-description-pagelang": "Це журнал змін мови сторінок.",
-       "logentry-pagelang-pagelang": "$1 {{GENDER:$2|змінив|змінила}} мову сторінки для $3 з $4 на $5.",
+       "logentry-pagelang-pagelang": "$1 {{GENDER:$2|змінив|змінила}} мову для $3 з $4 на $5",
        "default-skin-not-found": "Ой! Типова тема оформлення для вашої вікі <code dir=\"ltr\">$wgDefaultSkin</code>, <code>$1</code> недоступна.\n\nСхоже, що Ваша установка містить {{PLURAL:$4|наступну тему оформлення|наступні теми оформлення}}. Див. [https://www.mediawiki.org/wiki/Manual:Skin_configuration Manual:Skin configuration] для отримання інформації про те, як увімкнути {{PLURAL:$4|її|їх та обрати тему за замовчуванням}}.\n\n\n$2\n\n\n; Якщо Ви щойно встановили MediaWiki:\n: Ви, мабуть, зробили це з Git або безпосередньо з вихідного коду, використовуючи деякий інший спосіб. Тоді можливе наступне. Спробуйте встановити деякі теми з [https://www.mediawiki.org/wiki/Category:All_skins каталогу тем оформлення сайту mediawiki.org]:\n:* Завантаживши [https://www.mediawiki.org/wiki/Download архів файлів], який містить декілька тем оформлення й розширень. Ви можете скопіювати теку <code>skins/</code> з нього.\n:* Завантаживши архіви окремих тем оформлення з [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org].\n:* [https://www.mediawiki.org/wiki/Download_from_Git#Using_Git_to_download_MediaWiki_skins Використавши Git, щоб завантажити теми оформлення].\n: Це не повинно зашкодити вашому сховищу, якщо Ви MediaWiki-розробник.\n\n; Якщо Ви щойно оновили MediaWiki:\n: MediaWiki версії 1.24 й новіші більше не вмикають встановлені теми автоматично (див. [https://www.mediawiki.org/wiki/Manual:Skin_autodiscovery Manual: Skin autodiscovery]). Ви можете вставити {{PLURAL:$5|наступний рядок|наступні рядки}} в <code>LocalSettings.php</code>, щоб увімкнути {{PLURAL:$5|встановлену тему|всі встановлені теми}} оформлення: \n\n\n<pre dir=\"ltr\">$3</pre>\n\n\n; Якщо Ви щойно змінили <code>LocalSettings.php</code>:\n: Повторно перевірте назви тем на наявність помилок.",
        "default-skin-not-found-no-skins": "Ой! Тема оформлення для Вашої вікі за замовчуванням, визначена у <code>$wgDefaultSkin</code> як <code>$1</code> недоступна.\n\n\nУ Вас немає встановлених тем оформлення.\n\n\n; Якщо Ви щойно встановили або оновили MediaWiki:\n: Ви, мабуть, зробили це з Git або безпосередньо з вихідного коду, використовуючи інший спосіб. Тоді це можливо. MediaWiki версії 1.24 або новіша не містить теми оформлення в основному репозиторії. Спробуйте встановити деякі теми з [https://www.mediawiki.org/wiki/Category:All_skins каталогу тем оформлення сайту mediawiki.org]:\n:* Завантаживши [https://www.mediawiki.org/wiki/Download архів файлів], який містить декілька тем оформлення і розширень. Ви можете скопіювати теку <code>skins/</code> з нього.\n:* Завантаживши окремі архіви тем з [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org].\n:* [https://www.mediawiki.org/wiki/Download_from_Git#Using_Git_to_download_MediaWiki_skins Використавши Git, щоб завантажити теми оформлення].\n: Це не повинно зашкодити Вашому сховищу, якщо Ви MediaWiki-розробник. Див. [https://www.mediawiki.org/wiki/Manual:Skin_configuration Manual:Skin configuration] для отримання інформації про те, як включити теми оформлення і вибрати тему за замовчуванням.",
        "default-skin-not-found-row-enabled": "* <code>$1</code> / $2 (увімкнено)",
        "mw-widgets-titleinput-description-new-page": "сторінка ще не існує",
        "mw-widgets-titleinput-description-redirect": "перенаправлення на $1",
        "api-error-blacklisted": "Будь ласка, виберіть іншу, більш зрозумілу назву.",
+       "sessionmanager-tie": "Не можна поєднувати кілька типів автентифікації запиту: $1.",
+       "sessionprovider-generic": "сесій $1",
+       "sessionprovider-mediawiki-session-cookiesessionprovider": "сесій на основі кук",
+       "sessionprovider-nocookies": "Куки можуть бути відключені. Переконайтеся, що у Вас включені cookies і почніть знову.",
        "randomrootpage": "Випадкова коренева сторінка"
 }
index 56a55de..ba19298 100644 (file)
        "pager-older-n": "{{PLURAL:$1|پُرانا 1|پُرانے $1}}",
        "apihelp": "معاونت اے پی آئی",
        "apihelp-no-such-module": "ماڈیول \"$1\" نہیں ملا",
+       "apisandbox-submit": "بنانے کی درخواست",
+       "apisandbox-reset": "واضح",
+       "apisandbox-examples": "مثال کے طور پر",
+       "apisandbox-results": "نتیجہ",
        "booksources": "کتابی وسائل",
        "booksources-search-legend": "تلاش برائے مآخذاتِ کتاب",
        "booksources-search": "تلاش",
index fbcc83e..c1b783d 100644 (file)
@@ -33,7 +33,8 @@
                        "Darcy Le",
                        "Quenhitran",
                        "Matma Rex",
-                       "Xð"
+                       "Xð",
+                       "Nguyên Lê"
                ]
        },
        "tog-underline": "Gạch chân liên kết:",
        "virus-scanfailed": "quét thất bại (mã $1)",
        "virus-unknownscanner": "không nhận ra phần mềm diệt virus:",
        "logouttext": "'''Bạn đã đăng xuất.'''\n\nXin lưu ý rằng một vài trang có thể vẫn hiển thị như khi bạn còn đăng nhập, cho đến khi bạn xóa vùng nhớ đệm (''cache'') của trình duyệt.",
+       "cannotlogoutnow-title": "Không thể đăng xuất lúc này",
+       "cannotlogoutnow-text": "Không thể đăng xuất khi đang dùng $1.",
        "welcomeuser": "Hoan nghênh, $1!",
        "welcomecreation-msg": "Tài khoản của bạn đã được mở.\nHãy nhớ thay đổi [[Special:Preferences|tùy chọn cá nhân {{SITENAME}}]] của bạn.",
        "yourname": "Tên người dùng:",
        "remembermypassword": "Nhớ thông tin đăng nhập của tôi trên máy tính này (cho đến $1 ngày)",
        "userlogin-remembermypassword": "Giữ trạng thái đăng nhập",
        "userlogin-signwithsecure": "Sử dụng kết nối an toàn",
+       "cannotloginnow-title": "Không thể đăng nhập lúc này",
+       "cannotloginnow-text": "Không thể đăng nhập khi đang dùng $1.",
        "yourdomainname": "Tên miền của bạn:",
        "password-change-forbidden": "Bạn không thể đổi mật khẩu trên wiki này.",
        "externaldberror": "Có lỗi khi xác nhận cơ sở dữ liệu bên ngoài hoặc bạn không được phép cập nhật tài khoản bên ngoài.",
        "resetpass_submit": "Chọn mật khẩu và đăng nhập",
        "changepassword-success": "Đã đổi mật khẩu thành công!",
        "changepassword-throttled": "Bạn đã thử đăng nhập gần đây nhiều lần quá. Xin chờ $1 trước khi bạn thử lần nữa.",
+       "botpasswords-label-appid": "Tên bot:",
+       "botpasswords-label-create": "Tạo",
+       "botpasswords-label-update": "Cập nhật",
+       "botpasswords-label-cancel": "Huỷ bỏ",
+       "botpasswords-label-delete": "Xoá",
        "resetpass_forbidden": "Không được đổi mật khẩu",
        "resetpass-no-info": "Bạn phải đăng nhập mới có thể truy cập trực tiếp trang này.",
        "resetpass-submit-loggedin": "Thay đổi mật khẩu",
        "upload-dialog-button-done": "Xong",
        "upload-dialog-button-save": "Lưu",
        "upload-dialog-button-upload": "Tải lên",
-       "upload-form-label-select-file": "Chọn tập tin",
        "upload-form-label-infoform-title": "Chi tiết",
        "upload-form-label-infoform-name": "Tên",
        "upload-form-label-infoform-description": "Miêu tả",
        "foreign-structured-upload-form-label-own-work-message-shared": "Tôi khẳng định rằng tôi giữ quyền tác giả của tập tin này và đồng ý phát hành, một cách không thể hủy bỏ, tập tin này cho Wikimedia Commons theo giấy phép [https://creativecommons.org/licenses/by-sa/4.0/deed.vi Creative Commons Ghi công–Chia sẻ tương tự 4.0], và tôi chấp nhận các [https://wikimediafoundation.org/wiki/Terms_of_Use/vi?uselang=vi Điều khoản Sử dụng].",
        "foreign-structured-upload-form-label-not-own-work-message-shared": "Nếu bạn không giữ quyền tác giả của tập tin hoặc muốn phát hành nó theo một giấy phép khác, xin nghĩ đến việc sử dụng [https://commons.wikimedia.org/wiki/Special:UploadWizard?uselang=vi Trình thuật sĩ tải lên Commons].",
        "foreign-structured-upload-form-label-not-own-work-local-shared": "Bạn cũng có thể muốn thử [[Special:Upload|trang tải lên tại {{SITENAME}}]] nếu trang đó cho phép tải lên tập tin này theo các quy định của họ.",
-       "foreign-structured-upload-form-2-label-intro": "Cảm ơn bạn đóng góp hình ảnh để sử dụng tại {{SITENAME}}. Hãy chỉ tiếp tục nếu hình này thỏa mãn vài điều khoản:",
-       "foreign-structured-upload-form-2-label-ownwork": "Nó phải hoàn toàn <strong>do bạn tạo ra</strong>, chứ không chỉ lấy từ Internet",
-       "foreign-structured-upload-form-2-label-noderiv": "Nó <strong>không được chứa tác phẩm của người khác</strong> hoặc phỏng theo tác phẩm của người khác",
-       "foreign-structured-upload-form-2-label-useful": "Nó phải <strong>có tính giáo dục và có ích</strong> trong việc chia sẻ kiến thức",
-       "foreign-structured-upload-form-2-label-ccbysa": "Nó phải <strong>được phép xuất bản mãi mãi</strong> trên Internet theo giấy phép [https://creativecommons.org/licenses/by-sa/4.0/deed.vi Creative Commons Ghi công–Chia sẻ tương tự 4.0]",
-       "foreign-structured-upload-form-2-label-alternative": "Nếu bạn đã trả lời “Không” bên trên, có thể là [https://commons.wikimedia.org/wiki/Special:UploadWizard?setlang=vi Trình thuật sĩ tải lên tại Commons] vẫn cho phép tải nó lên, miễn là nó được phát hành theo một giấy phép mở.",
-       "foreign-structured-upload-form-2-label-termsofuse": "Với việc tải lên tập tin này, bạn xác nhận rằng bạn giữ quyền tác giả của tập tin này và đồng ý phát hành tập tin này, một cách không thể hủy bỏ, dưới giấy phép Creative Commons Ghi công–Chia sẻ tương tự 4.0, và bạn chấp nhận [https://wikimediafoundation.org/wiki/Terms_of_Use/vi?uselang=vi các Điều khoản Sử dụng].",
-       "foreign-structured-upload-form-3-label-question-website": "Bạn có tải về hình này từ trang Web nào hoặc lấy nó từ máy tìm kiếm hình ảnh nào?",
-       "foreign-structured-upload-form-3-label-question-ownwork": "Bạn có tự mình tạo hình này (chụp hình, vẽ bức ảnh, v.v.)?",
-       "foreign-structured-upload-form-3-label-question-noderiv": "Nó có chứa hoặc phỏng theo tác phẩm nào do người khác sở hữu, chẳng hạn một biểu trưng?",
-       "foreign-structured-upload-form-3-label-yes": "Có",
-       "foreign-structured-upload-form-3-label-no": "Không",
-       "foreign-structured-upload-form-3-label-alternative": "Đáng tiếc, trong trường hợp này, công cụ này không hỗ trợ việc tải lên tập tin này. Có thể là [https://commons.wikimedia.org/wiki/Special:UploadWizard?setlang=vi Trình thuật sĩ tải lên tại Commons] vẫn cho phép tải nó lên, miễn là nó được phát hành theo một giấy phép mở.",
-       "foreign-structured-upload-form-4-label-good": "Với công cụ này, bạn có thể tải lên các trang minh học có tính giáo dục do bạn tạo ra và các hình ảnh do bạn chụp lấy, miễn là nó không chứa tác phẩm của người khác.",
-       "foreign-structured-upload-form-4-label-bad": "Bạn không được phép tải lên hình ảnh được lấy từ máy tìm kiếm hoặc tải về từ trang Web khác.",
        "backend-fail-stream": "Không thể gửi luồng tập tin $1.",
        "backend-fail-backup": "Không thể sao lưu tập tin $1.",
        "backend-fail-notexists": "Tập tin $1 không tồn tại.",
        "querypage-disabled": "Trang đặc biệt này bị tắt vì lý do hiệu suất.",
        "apihelp": "Trợ giúp API",
        "apihelp-no-such-module": "Không tìm thấy mô đun “$1”",
+       "apisandbox": "Chỗ thử API",
+       "apisandbox-api-disabled": "API đã bị vô hiệu hóa trên trang web này.",
+       "apisandbox-intro": "Trang này dùng để thử nghiệm với '''API dịch vụ Web của MediaWiki'''.\nHãy tra cứu [//www.mediawiki.org/wiki/API:Main_page tài liệu API] để biết chi tiết về cách sử dụng API. Ví dụ: [//www.mediawiki.org/wiki/API#A_simple_example lấy nội dung của Trang Chính]. Chọn một tác vụ để xem thêm ví dụ.\n\nLưu ý rằng, mặc dù đây là một chỗ thử, nhưng các tác vụ của bạn tại trang này có thể thực hiện các thay đổi trên wiki.",
+       "apisandbox-submit": "Yêu cầu",
+       "apisandbox-reset": "Tẩy trống",
+       "apisandbox-examples": "Các ví dụ",
+       "apisandbox-results": "Kết quả",
+       "apisandbox-request-url-label": "URL của yêu cầu:",
+       "apisandbox-request-time": "Thời gian xử lý: $1",
        "booksources": "Nguồn sách",
        "booksources-search-legend": "Tìm kiếm nguồn sách",
        "booksources-search": "Tìm kiếm",
        "tooltip-t-recentchangeslinked": "Thay đổi gần đây của các trang liên kết đến đây",
        "tooltip-feed-rss": "Nguồn cấp RSS của trang này",
        "tooltip-feed-atom": "Nguồn cấp Atom của trang này",
-       "tooltip-t-contributions": "Xem đóng góp của người này",
+       "tooltip-t-contributions": "Danh sách đóng góp của {{GENDER:$1|người này}}",
        "tooltip-t-emailuser": "Gửi thư cho người này",
        "tooltip-t-info": "Thêm chi tiết về trang này",
        "tooltip-t-upload": "Tải hình ảnh hoặc tập tin lên",
index 0bbeced..3dda0f6 100644 (file)
        "pager-newer-n": "{{PLURAL:$1|nulikum 1|nulikum $1}}",
        "pager-older-n": "{{PLURAL:$1|büikum 1|büikum $1}}",
        "suppress": "Lovelogam",
+       "apisandbox-examples": "Sam",
        "booksources": "Bukafons",
        "booksources-search-legend": "Sukön bukafonis:",
        "booksources-search": "Sukön",
index 13edba7..10b3a58 100644 (file)
        "virus-scanfailed": "Pakyas an pag-scan (kodigo $1)",
        "virus-unknownscanner": "diri-nasasabtan nga antivirus:",
        "logouttext": "'''Nakalog-out kana.'''\n\nGinpapasabot ka la nga an iba nga pakli in magpapadayon nga magpakita komo nga ikaw naka-log-in pa, tubtob imo ginlimpyo an imo browser cache.",
+       "cannotlogoutnow-title": "Diri nakakalog-out yana",
+       "cannotlogoutnow-text": "Iton pag-log-out in imposible samtang nagamit hin $1.",
        "welcomeuser": "¡Uswag ngan Dayon, $1!",
        "welcomecreation-msg": "An im akawnt in nahimo na.\nAyaw kalimti pagbalyo han imo [[Special:Preferences|{{SITENAME}} preperensya]].",
        "yourname": "Agnay hit gumaramit:",
        "remembermypassword": "Hinumdumi an akon pan-sakob dinhi nga panngaykay ''(browser)'' (para ha pinakamaiha $1 {{PLURAL:$1|ka adlaw|ka mga adlaw}})",
        "userlogin-remembermypassword": "I-log-in la ako",
        "userlogin-signwithsecure": "Gamit hin koneksyon nga nakakasegurado",
+       "cannotloginnow-title": "Diri nakakalog-in yana",
+       "cannotloginnow-text": "Iton paglog-in in diri posible samtang nagamit hin $1.",
        "yourdomainname": "Imo dominyo:",
        "password-change-forbidden": "Diri ka makakabalyo hin pulong-pagsulod ha dinhi nga wiki.",
        "externaldberror": "Mayda authenticaton database error o diri ka tinutugotan pag-update an imo akwant ha gawas.",
        "wrongpasswordempty": "An tigaman-pagsulod nga ginbutang in waray sulod.\nAlayon pagutro pagbutang.",
        "passwordtooshort": "An tigaman-pagsulod dapat diri maubos hit {{PLURAL:$1|1 nga agi|$1 nga agi}}.",
        "passwordtoolong": "It mga password in diri puydi mas huruhilaba hin {{PLURAL:$1|1 ka karakter|$1 ka mga karakter}}.",
+       "passwordtoopopular": "Iton agsob pinipili nga mga password in diri puydi gamiton. Alayon pagpili hin mas kakaiba nga password.",
        "password-name-match": "An imo tigaman-pagsulod in kinahanglan iba ha imo agnay-hiton-gumaramit.",
        "password-login-forbidden": "An paggamit hini nga agnay-hit-gumaramit ngan tigaman-pagsulod in diri gintutugotan.",
        "mailmypassword": "Ig-reset an tigaman-pagsulod",
        "resetpass_submit": "Igbutang an password ngan log in",
        "changepassword-success": "Malinamposon an pagbal-iw hit imo tigaman-panakob!",
        "changepassword-throttled": "Damo na nga mga paningkamot hin pagsakob an imo ginhimò.\nAlayon paghulat hin $1 san-o ka umutro.",
+       "botpasswords": "Mga bot password",
+       "botpasswords-disabled": "Ginparong an mga bot password.",
+       "botpasswords-no-central-id": "Para han paggamit hin mga bot password, kinahanglan ka maglog-in ha centralized account.",
+       "botpasswords-existing": "Aada nga mga bot password.",
+       "botpasswords-createnew": "Pahimo hin bag-o nga bot password",
+       "botpasswords-editexisting": "Igliwat an aada nga bot password",
+       "botpasswords-label-appid": "Ngaran han bot:",
+       "botpasswords-label-create": "Paghimo",
+       "botpasswords-label-update": "Ig-update",
+       "botpasswords-label-cancel": "Pasagda",
+       "botpasswords-label-delete": "Paraa",
+       "botpasswords-label-resetpassword": "Igreset an password",
+       "botpasswords-label-grants": "Mga applicable grant",
+       "botpasswords-label-restrictions": "Mga gindidiri ha paggamit:",
+       "botpasswords-label-grants-column": "Ginhatag",
+       "botpasswords-bad-appid": "An ngaran han bot nga \"$1\" in diri puydi gamiton.",
+       "botpasswords-insert-failed": "Pakyas han pagdugang han ngaran han bot nga \"$1\". Naidugang na ini?",
+       "botpasswords-update-failed": "Pakyas han pag-update han bot nga ngaran nga \"$1\". Ginpara na ini?",
+       "botpasswords-created-title": "Nahimo an bot password",
+       "botpasswords-created-body": "An bot password nga \"$1\" in malinamposon nga nahimo.",
+       "botpasswords-updated-title": "Gin-update an bot password",
+       "botpasswords-updated-body": "An bot password nga \"$1\" in malinamposon nga na-update.",
+       "botpasswords-deleted-title": "Ginpara an bot password",
+       "botpasswords-deleted-body": "An bot password nga \"$1\" in ginpara.",
+       "botpasswords-newpassword": "An bag-o nga password para han pag log-in han <strong>$1</strong> in <strong>$2</strong>. <em>Alayon igrecord ini para han future reference.</em>",
+       "botpasswords-no-provider": "BotPasswordsSessionProvider in waray dinhi.",
+       "botpasswords-restriction-failed": "An mga restriction han bot password in nagpupugong han pag-login hinin.",
+       "botpasswords-not-exist": "An gumaramit nga \"$1\" in waray bot password nga nakangaran hin \"$2\".",
        "resetpass_forbidden": "Diri mababalyoan an mga tigaman-pagsulod",
        "resetpass-no-info": "Kinahanglan mo paglog-in para direkta ka makasakob dinhi nga pakli.",
        "resetpass-submit-loggedin": "Igbal-iw an tigaman-pagsulod",
        "resetpass-temp-password": "Temporaryo nga tigaman-pagsakob:",
        "resetpass-abort-generic": "Ginpugong an pagbal-iw hin tigaman-panakob hin uska ekstensyon.",
        "resetpass-expired": "Naubosan na hin panahon an im tigaman-pansakob.  Alayon paghimo hin bag-o nga tigaman-pansakob basi ka makasakob.",
+       "resetpass-expired-soft": "An imo password in nag-expire ngan kinahanglan ig-reset. Alayon pagpili hin bag-o nga password yana, kun diri pidlita an \"{{int:resetpass-submit-cancel}}\" para ig-reset nuruniyan.",
+       "resetpass-validity-soft": "Diri puydi gamiton an imo password nga: $1\n\nAlayon pagpili hin bag-o nga password yana, kun diri pidlita an \"{{int:resetpass-submit-cancel}}\" para ig-reset nuruniyan.",
        "passwordreset": "igreset an tigaman-hit-pagsulod",
        "passwordreset-text-one": "Kompletoha ini nga porma paramakareset hin imo tigaman-panakob.",
        "passwordreset-text-many": "{{PLURAL:$1|Butanga it usa nga mga surodlan basi makakarawat ko hin temporaryo nga tigaman-pansulod pinaagi ha email.}}",
        "changeemail-password": "An imo {{SITENAME}} password:",
        "changeemail-submit": "Igbalyo an e-mail",
        "changeemail-throttled": "Nakadamo kada pag-log-in. Alayon paghulat hin $1 ugsa ka umutro.",
+       "changeemail-nochange": "Alayon pagbutang hin lain nga bag-o nga email address.",
        "resettokens": "Igrest an mga token",
        "resettokens-text": "Puydi nimo mareset an mga token para makahatag hin pipira nga pribado nga datos nga may pakahisumpay ha imo akawnt dinhi.\nKinahanglan mo ini buhaton kun aksidenti nim nasaro hira ha iba nga tawo o an imo akawnt in nakompromiso.",
        "resettokens-no-tokens": "Waray token nga marereset.",
        "sig_tip": "Imo pirma nga may-ada marka hin oras",
        "hr_tip": "Patumba nga bagis (hinay-hinay la it paggamit)",
        "summary": "Halipotay nga masisiring:",
-       "subject": "Katukiban:",
+       "subject": "Himangrawan:",
        "minoredit": "Gutiay ini nga pagliwat",
        "watchthis": "Bantayi ini nga pakli",
        "savearticle": "Igtipig an pakli",
        "missingcommenttext": "Alayon pagbutang hin komento ha ilarom.",
        "missingcommentheader": "'''Pahinumdom:''' Waray ka humatag hin subject/headline para hini nga komento.  Kun pinduton mo an \"{{int:savearticle}}\" utro, an imo pagliwat in matitipig bisan waray hini.",
        "summary-preview": "Pahiuna nga pagawas han dalikyat nga pulong:",
-       "subject-preview": "Pahiuna nga pagawas hit himangrawon:",
+       "subject-preview": "Pahiuna nga pagawas hit himangrawan:",
        "blockedtitle": "Ginpugngan ini nga gumaramit",
        "blockedtext": "'''An imo agnay-gumaramit o IP address in ginpugngan.'''\n\nAn pagpugong in ginhimo ni $1.\nAn rason nga ginhatag in ''$2''.\n\n* Pagtikang han pagpugong: $8\n* Paghuman han pagpugong: $6\n* Ginpupugngan: $7\n\nPuydi nimo bilngon hi $1 o iba liwat nga [[{{MediaWiki:Grouppage-sysop}}|magdudumara]] para makipaghimangraw hiunong hini nga pagpugong.\nDiri nimo magagamit an \"ig-email ini nga gumaramit\" nga feature antes may-ada balido nga email address nga nakabutang ha imo  [[Special:Preferences|mga preperensya han akawnt]] ngan waray ka pugngi paggamit hini.\nAn imo IP address yana in $3, ngan an imo pagpugong nga ID in #$5.  Alayon la paglakip han ngatanan nga aada ha igbaw nga mga detalye ha bisan ano nga mga pakiana nga karuyag mo buhaton.",
        "autoblockedtext": "An imo IP address in automatiko nga ginpugngan mahitungod nga ini in gingamit hin iba nga gumaramit, nga ginpugngan ni $1.\n\nAn rason nga ginhatag in ''$2''.\n\n* Pagtikang han pagpugong: $8\n* Paghuman han pagpugong: $6\n* Ginpupugngan: $7\n\nPuydi nimo bilngon hi $1 o iba liwat nga [[{{MediaWiki:Grouppage-sysop}}|magdudumara]] para makipaghimangraw hiunong hini nga pagpugong.\n\nGinpapasabot ka nga diri nimo magagamitan an \"ig-email ini nga gumaramit\" nga feature antes may-ada nimo balido nga email address nga nakarehistro ha imo  [[Special:Preferences|mga preperensya han gumaramit]] ngan waray ka pugngi hit paggamit hini.\n\nAn imo IP address yana in $3, ngan an imo pagpugong nga ID in #$5.  Alayon la paglakip han ngatanan nga aada ha igbaw nga mga detalye ha bisan ano nga mga pakiana nga karuyag mo buhaton.",
        "content-model-text": "yano nga teksto",
        "content-model-javascript": "JavaScript",
        "content-model-css": "CSS",
+       "content-json-empty-object": "Empty object",
+       "content-json-empty-array": "Empty array",
        "post-expand-template-inclusion-warning": "'''Pahimatngon:''' An batakan nga ginlakip in sobra kadako.\nAn iba nga mga batakan in diri mauupod.",
        "post-expand-template-inclusion-category": "Mga pakli kun diin an mga nahilalakip nga kadako han batakan in nalabaw.",
        "post-expand-template-argument-warning": "'''Pahimatngaon:''' Ini nga pakli in nagsusulod hin pinakaguti usa nga argumento hin batakan nga may-ada sobra nga dako it padako nga kadako.\nIni nga mga argumento in ginlaktawan.",
        "history-feed-empty": "An imo ginpaalayon nga pakli in waray dida.\nBangin ini napara tikang ha wiki, o ginngaranan hin iba.\n\n[[Special:Search|pamilnga ha wiki]] para han may pagkahisumpay nga bag-o nga pakli.",
        "rev-deleted-comment": "(gintanggal an halipotay nga masisiring hiton pagliwat)",
        "rev-deleted-user": "(gintanggal an agnay hiton gumaramit)",
-       "rev-deleted-event": "(gintanggal an talaan han mga buhat)",
+       "rev-deleted-event": "(gintanggal an mga detalye han log)",
        "rev-deleted-user-contribs": "[gintanggal an agnay-hit-gumaramit o IP address - an pagliwat in gintago tikang han mga amot]",
        "rev-deleted-text-permission": "Ini nga rebisyon han pakli in '''ginpara'''.\nAn mga detalye in mabibilngan ha [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} deletion log].",
        "rev-suppressed-no-diff": "Diri mo makikita ini nga kaibhan tungod nga usa ha mga rebisyon in '''ginpara'''.",
        "revdelete-show-file-submit": "Oo",
        "revdelete-hide-text": "Rebisyon nga sinurat",
        "revdelete-hide-image": "Tagoon an sulod han paypay",
-       "revdelete-hide-name": "Tagoon an buhat ngan kakadtoan",
+       "revdelete-hide-name": "Tagoon an target ngan mga parameter",
        "revdelete-hide-comment": "Halipotay nga masisiring hiton pagliwat",
+       "revdelete-hide-user": "An kanan magliliwat ngaran-gumaramit/IP address",
        "revdelete-radio-same": "(ayaw balyu-e)",
        "revdelete-radio-set": "Tinago",
        "revdelete-radio-unset": "Nakikit-an",
        "rows": "Mga rumbay pahigda:",
        "columns": "Mga rumbay patindog:",
        "searchresultshead": "Bilnga",
+       "stub-threshold-sample-link": "pananglitan",
        "stub-threshold-disabled": "Waray ginpagana",
        "recentchangesdays": "Kadamo hin adlaw nga igpapakita an mga kabag-ohan:",
        "recentchangesdays-max": "Pinakadamo $1 {{PLURAL:$1|ka adlaw|ka mga adlaw}}",
        "prefs-i18n": "Internasyonalisasyon",
        "prefs-signature": "Pirma",
        "prefs-dateformat": "Batakan han petsa",
+       "prefs-timeoffset": "Time offset",
        "prefs-advancedediting": "Mga kasahiran nga pagpipilian",
+       "prefs-editor": "Editor",
        "prefs-preview": "Pahiuna nga pakita",
        "prefs-advancedrc": "Abansado nga mga pagpipilian",
        "prefs-advancedrendering": "Abansado nga mga pagpipilian",
        "right-userrights-interwiki": "Igliwat an mga katungod han gumaramit han mga gumaramit ha iba nga mga wiki",
        "right-siteadmin": "Igtrangka ngan igrangka an database",
        "right-sendemail": "Padad-i hin e-mail ngada ha iba nga mga gumaramit",
+       "grant-group-email": "Padangat hin email",
+       "grant-createaccount": "Pahimo hin mga account",
+       "grant-createeditmovepage": "Paghimo, pagliwat, ngan pagbalhin hin mga pakli",
+       "grant-delete": "Pagpara hin mga pakli, mga rebisyon, ngan mga iginsulod ha log",
        "newuserlogpage": "Talaan han paghimo hin gumaramit",
        "newuserlogpagetext": "Ini an talaan han mga nagkahihimo nga mga gumaramit.",
        "rightslog": "Talaan hin mga katungod han gumaramit",
        "uploadbtn": "Igkarga an file",
        "reuploaddesc": "Undanga an pagkarga-pasaka ngan balik ngadto ha porma han pagkarga-pasaka",
        "uploadnologin": "Diri nakalog-in",
+       "uploadnologintext": "Alayon $1 para han pag-upload han mga file.",
        "uploaderror": "Sayop hit pagkarga-pasaka",
        "upload-recreate-warning": "'''Pahimatngon:  An fayl nga may-ada hiton nga ngaran in ginpara o ginbalhin.'''\n\nAn taramdan han pagpara ngan pagbalhin para hini nga pakli in ginhahatag para han imo kamurayaw:",
        "upload-permitted": "Gintutugotan nga mga klase han paypay: $1.",
        "upload-description": "Pangilal-an han paypay",
        "upload-options": "Mga pirilion han pagkarga paigbaw",
        "watchthisupload": "Bantayi ini nga paypay",
-       "upload-success-subj": "Malinamposan an imo pagkarga-paigbaw.",
-       "upload-success-msg": "An imo pagkarga-paigbaw tikang ha [$2] in malinamposon.  Ini in aada dinhi: [[:{{ns:file}}:$1]]",
-       "upload-failure-subj": "May-ada problema an pagkarga-paigbaw",
-       "upload-failure-msg": "May-ada problema an imo pagkarga-paigbaw tikang ha [$2]:\n\n$1",
-       "upload-warning-subj": "Pahimatngon han pagkarga paigbaw",
        "upload-proto-error": "Sayop nga protocol",
        "upload-file-error": "Sayop ha sulod",
        "upload-misc-error": "Waray kasasabti nga sayop hin pagkarga-paigbaw",
        "upload-dialog-button-done": "Tima na",
        "upload-dialog-button-save": "Igtipig",
        "upload-dialog-button-upload": "Upload",
-       "upload-form-label-select-file": "Pagpili hin file",
        "upload-form-label-infoform-title": "Mga detalye",
        "upload-form-label-infoform-name": "Ngaran",
        "upload-form-label-usage-title": "Paggamit",
        "license": "Palilisensya:",
        "license-header": "Palilisensya:",
        "nolicense": "Waray napili",
+       "listfiles-delete": "igpara",
+       "listfiles-summary": "Ini nga pinaurog nga pakli in nagpapakita han ngatanan nga pinan-upload nga mga file.",
+       "listfiles_search_for": "Pamiling hin media nga ngaran:",
        "imgfile": "paypay",
        "listfiles": "Listahan han fayl",
        "listfiles_date": "Pitsa",
        "listfiles_user": "Nagamit",
        "listfiles_size": "Kadako",
        "listfiles_count": "Mga bersyon",
+       "listfiles-latestversion": "Bersyon yana",
        "listfiles-latestversion-yes": "Oo",
        "listfiles-latestversion-no": "Diri",
        "file-anchor-link": "Paypay",
        "unusedtemplates": "Waray kagamiti nga mga batakan",
        "unusedtemplateswlh": "iba nga mga sumpay",
        "randompage": "Bisan ano nga pakli",
+       "randomincategory-submit": "Kadto-a",
        "randomredirect": "Bisan ano la nga redirect",
        "randomredirect-nopages": "Waray mga redirecta ha ngaran-lat'ang nga \"$1\".",
        "statistics": "Mga estadistika",
        "usereditcount": "$1 {{PLURAL:$1|ka pagliwat|ka mga pagliwat}}",
        "usercreated": "{{GENDER:$3|Ginhimo}} han $1 ha $2",
        "newpages": "Bag-o nga mga pakli",
+       "newpages-submit": "Igpakita",
        "newpages-username": "Agnay hiton gumaramit:",
        "ancientpages": "Mga gidaani nga pakli",
        "move": "Balhina",
        "nopagetitle": "Waray sugad hito nga kakadtoan nga pakli",
        "pager-newer-n": "{{PLURAL:$1|burubag-o 1|burubag-o $1}}",
        "pager-older-n": "{{PLURAL:$1|durudaan 1|durudaan $1}}",
+       "apisandbox-unfullscreen": "Igpakita an pakli",
+       "apisandbox-submit": "Paghimo hin request",
+       "apisandbox-reset": "Hawana",
+       "apisandbox-retry": "Utroha",
+       "apisandbox-helpurls": "Mga sumpay hit pabulig",
+       "apisandbox-examples": "Mga pananglitan",
+       "apisandbox-dynamic-parameters": "Dugang nga mga parameter",
+       "apisandbox-dynamic-parameters-add-label": "Dugngi hin parameter:",
+       "apisandbox-dynamic-parameters-add-placeholder": "Ngaran hit parameter",
+       "apisandbox-dynamic-error-exists": "May-ada na nakangaran nga \"$1\" nga parameter.",
+       "apisandbox-results": "Mga resulta",
        "booksources": "Mga libro nga tinikangan",
        "booksources-search-legend": "Pamilnga an mga libro nga gintikangan",
        "booksources-search": "Bilnga",
        "notvisiblerev": "An urhi nga pagliwat han iba nga gumaramit in ginpara",
        "watchlist-details": "{{PLURAL:$1|$1 nga pakli|$1 nga mga pakli}} nga aada ha imo talaan nga binabantayan, diri bulag nga paglakip han mga hiruhimangraw-nga-pakli.",
        "wlshowlast": "Igpakita an katapusan nga $1 nga mga oras $2 nga mga adlaw",
-       "watchlistall2": "ngatanan",
+       "watchlist-hide": "Tago-a",
+       "watchlist-submit": "Pakit-a",
+       "wlshowhideminor": "gudti nga mga pagliwat",
+       "wlshowhidebots": "Mga bot",
+       "wlshowhideliu": "Mga nakarehistro nga gumaramit",
+       "wlshowhideanons": "Mga waray magpakilala nga gumaramit",
+       "wlshowhidepatr": "Nakapatrolya na nga mga pagliwat",
+       "wlshowhidemine": "ako mga pagliwat",
        "watchlist-options": "Mga pirilian han talaan han binabantayan",
        "watching": "Ginbabantay...",
        "unwatching": "Diri na ginbabantay...",
        "protect-othertime-op": "lain nga oras",
        "protect-otherreason": "Lain/dugang nga katadongan:",
        "protect-otherreason-op": "Lain nga katadongan",
+       "protect-expiry-options": "1 ka oras:1 hour,1 ka adlaw:1 day,1 ka semana:1 week,2 ka mga semana:2 weeks,1 ka bulan:1 month,3 ka mga bulan:3 months,6 ka mga bulan:6 months,1 ka tuig:1 year, waray kataposan:infinite",
        "restriction-type": "Pagtugot:",
        "minimum-size": "Pinakaguti nga kadako",
        "maximum-size": "Pinakadako nga kadako:",
        "whatlinkshere-hidelinks": "$1 an mga sumpay",
        "whatlinkshere-hideimages": "$1 an mga sumpay han paypay",
        "whatlinkshere-filters": "Mga panara",
+       "whatlinkshere-submit": "Kadto-a",
        "block": "Pugngi an gumaramit",
        "blockip": "Pugngi an gumaramit",
        "blockip-legend": "Pugngi an gumaramit",
        "allmessages-filter-all": "Ngatanan",
        "allmessages-language": "Yinaknan:",
        "allmessages-filter-submit": "Kadto-a",
+       "allmessages-filter-translate": "Ighubad",
        "thumbnail-more": "Padako-a",
        "filemissing": "Nawawara an fayl",
        "thumbnail_error": "Sayo han paghihimo hin thumbnail: $1",
+       "thumbnail_error_remote": "Sayop nga mensahe tikang $1:\n$2",
        "thumbnail_image-type": "An klase han hulagway in diri suportado",
        "import": "Naangbit hit mga pakli",
        "import-interwiki-templates": "Lakip an ngatanan nga mga batakan",
        "siteusers": "{{SITENAME}} {{PLURAL:$2|gumaramit|mga gumaramit}} $1",
        "simpleantispam-label": "Anti-spam check.\n<strong>Ayaw</strong> pagbinutangi dinhi!",
        "pageinfo-title": "Impormasyon para \"$1\"",
+       "pageinfo-not-current": "Pasaylo-a, imposible makahatag hin impormasyon hiunong han mga daan nga rebisyon.",
        "pageinfo-header-basic": "Panguna nga pananabotan",
        "pageinfo-header-edits": "Kaagi han pagliwat",
        "pageinfo-header-restrictions": "Panalipod han pakli",
        "pageinfo-edits": "Ngatanan nga ihap han mga pakli",
        "pageinfo-toolboxlink": "Impormasyon han pakli",
        "pageinfo-redirectsto": "Igredirect ngadto ha",
+       "pageinfo-redirectsto-info": "info",
        "pageinfo-contentpage": "Ginlakip komo uska unod nga pakli",
        "pageinfo-contentpage-yes": "Oo",
        "pageinfo-protect-cascading-yes": "Oo",
        "pageinfo-category-info": "Impormasyon han kaarangay",
+       "pageinfo-category-total": "Ngatanan nga mga naka-api.",
        "pageinfo-category-pages": "Ihap han mga pakli",
        "pageinfo-category-subcats": "Ihap han mga ubos-kaarangay",
        "pageinfo-category-files": "Ihap han mga paypay",
        "bydate": "pinaagi han petsa",
        "ago": "$1 an nakalabay",
        "just-now": "yana pala",
+       "monday-at": "Lunes ha $1",
+       "tuesday-at": "Martes ha $1",
+       "wednesday-at": "Miyerkules ha $1",
+       "thursday-at": "Huybes ha $1",
+       "friday-at": "Biyernes ha $1",
+       "saturday-at": "Sabado ha $1",
+       "sunday-at": "Dominggo ha $1",
+       "yesterday-at": "Kakulop ha $1",
        "bad_image_list": "An kabutangan in masunod:\n\nAn nakatalala la nga mga butang (mga bagis nga nagtitikang hin *) in mahiuupod paglabot.\nAn syahan nga sumpay ha uska bagis in dapat may-ada sumpay ngadto ha maraot nga fayl.\nAn bisan ano nga masunod nga mga sumpay ha kapareho nga bagis in igtratrato nga eksepsyon, sugad hin, mga pakli kun diin an mga fayl in puydi mabubutang ha sulod han bagis.",
        "metadata": "Metadata",
        "metadata-help": "Iní nga paypay mayda dugang nga pagpasabot, nga bangin gindugáng tikang han digital nga camera o iskaner nga gin-gamit paghimo o pag-digitar hini.\nKon an paypay ginliwat tikang han orihinal nga kamutangan, mayda mga detalye nga bangin diri magpakita han ginliwat nga paypay",
        "exif-model": "Modelo han kamera",
        "exif-software": "Software nga gingamit",
        "exif-artist": "Tag-iya",
+       "exif-copyright": "May katungod han copyright",
        "exif-exifversion": "Version han Exif",
        "exif-colorspace": "Kolor lat-ang",
        "exif-datetimeoriginal": "Petsa ngan oras han data generation",
        "exif-datetimereleased": "Ginpagawas han",
        "exif-lens": "Mga lente nga gingamit",
        "exif-cameraownername": "Tag-iya han kamera",
+       "exif-copyrighted": "Kahimtang han copyright",
+       "exif-copyrightowner": "Tag-iya han copyright",
        "exif-usageterms": "Mga termino hit paggamit",
        "exif-copyrighted-false": "Status hin katungod-hin-panag-iya waray mahabutang",
        "exif-unknowndate": "Waray kasabti an petsa",
index 39465f6..e8034ba 100644 (file)
        "tog-previewontop": "編寫框頭前顯示先望",
        "tog-previewonfirst": "頭垡編寫顯示先望",
        "tog-enotifwatchlistpages": "我關注表裏個頁要弗文件變脫到用電子信通知我",
-       "tog-enotifusertalkpages": "我用戶討論頁變脫到用電子信通知我",
+       "tog-enotifusertalkpages": "我个讨论页有改动个辰光发邮件畀我",
        "tog-enotifminoredits": "頁搭文件細編也用電子信通知我",
        "tog-enotifrevealaddr": "電子信通知單裏顯示我個電子信地址",
        "tog-shownumberswatching": "顯示關注人數",
        "tog-oldsig": "本生个签名:",
-       "tog-fancysig": "畀簽名當wiki文本(弗自動鏈接)",
+       "tog-fancysig": "拿签名当成维基文本(弗自动链接)",
        "tog-uselivepreview": "使用实时预览",
        "tog-forceeditsummary": "編要空白到提醒我",
        "tog-watchlisthideown": "關注表裏囥脫我所編",
@@ -60,7 +60,7 @@
        "tog-watchlisthidepatrolled": "關注表裏囥脫巡脫編",
        "tog-watchlisthidecategorization": "囥脱对页面个分类",
        "tog-ccmeonemails": "拿我发畀其他用户个电子邮件也发只副本畀我自家",
-       "tog-diffonly": "æ¯\94è¼\83å\85©ç\89\88å¼\97樣å\88°å¼\97顯示é \81å\85§容",
+       "tog-diffonly": "æ¯\94è¾\83两å\8fªä¿®è®¢ç\89\88æ\9c¬ä¸¤æ ·ä¸ªè¾°å\85\89å¼\97æ\98¾ç¤ºé¡µé\9d¢å\86\85容",
        "tog-showhiddencats": "顯示囥脫分類",
        "tog-norollbackdiff": "执行退回之后弗显示两样",
        "tog-useeditwarning": "離開編頁朆保存到提醒我",
        "october-date": "10月$1号",
        "november-date": "11月$1号",
        "december-date": "12月$1号",
+       "period-am": "AM",
+       "period-pm": "PM",
        "pagecategories": "{{PLURAL:$1|分类}}",
        "category_header": "“$1”分類裏個頁",
        "subcategories": "子分类",
        "searchbutton": "搜寻",
        "go": "去",
        "searcharticle": "去",
-       "history": "é \81史",
+       "history": "页é\9d¢å\8e\86史",
        "history_short": "历史",
        "updatedmarker": "從上趟訪問起個更新",
        "printableversion": "打印版",
        "mainpage-nstab": "封面",
        "nosuchaction": "嘸能操作",
        "nosuchactiontext": "URL指定個命令無效。爾嘸數畀URL打錯哉,要勿点击仔出錯個鏈接。也嘸數{{SITENAME}}用個軟件本身出錯緣故。",
-       "nosuchspecialpage": "å\98¸è\83½å\80\8bç\89¹å\88¥é \81",
+       "nosuchspecialpage": "å\91\92ä¸\8då®\9eæ¢\97个ç\89¹å\88«é¡µé\9d¢",
        "nospecialpagetext": "<strong>侬请求个特殊页面无效。</strong>\n\n参考特殊页面列表[[Special:SpecialPages| {{int:specialpages}}]]。",
        "error": "错误",
        "databaseerror": "数据库错误",
        "badarticleerror": "呒处垃拉箇只页面进行箇只操作。",
        "cannotdelete": "无处删除页面或图像 \"$1\"。\n渠作兴已经拨别人家删除脱哉。",
        "cannotdelete-title": "\"$1\"箇页删弗爻",
-       "delete-hook-aborted": "删除畀钩子取消。\n渠弗曾畀出解释。",
+       "delete-hook-aborted": "删除畀扩展钩子中止。渠弗曾畀出解释。",
        "no-null-revision": "\"$1\"页呒处建新个修改",
        "badtitle": "坏标题",
        "badtitletext": "所请求页面个标题是无效个、弗存在,跨语言或跨wiki链接个标题错误。渠作兴包含一只或多只弗好用拉标题里向字符。",
        "viewyourtext": "你侬好望也好畀'''你侬编个'''复制到箇页:",
        "protectedinterface": "箇页为箇维基个软件提供界面文本,锁牢定防乱用。\n加改全部维基个译文,用[//translatewiki.net/ translatewiki.net],MediaWiki软件个本地化计划。",
        "editinginterface": "<strong>警告:</strong>侬来里编写个页面是畀软件用个界面文本。箇页变化会影响各许人个界面样子。",
-       "translateinterface": "要加入或着更改所有个wiki个翻译,请侬访问MediaWiki本地化项目个网站[//translatewiki.net/ translatewiki.net]。",
+       "translateinterface": "要添加或者更改所有wiki个翻译,请侬访问MediaWiki本地化项目个网站[//translatewiki.net/ translatewiki.net]。",
        "cascadeprotected": "箇只页面畀保护拉许,因为渠已嵌入到下底已经标注“级联保护”个{{PLURAL:$1|一只|多只}}畀保护页面:\n$2",
        "namespaceprotected": "侬无没编辑'''$1'''名字空间里向页面个权限。",
        "customcssprotected": "箇CSS页你呒处编,箇页有各许用户个私人设置。",
        "virus-badscanner": "设置问题:未知个反病毒扫描器:''$1''",
        "virus-scanfailed": "扫描失败(代码 $1)",
        "virus-unknownscanner": "未知个反病毒扫描器:",
-       "logouttext": "'''你侬登出哉。'''\n\n部份页面呒数还会显示你侬还登勒里,到你侬畀浏览器慢存清爻止。",
+       "logouttext": "<strong>侬已经登出哉。</strong>\n\n请注意有星页面作兴还是会得搭侬登出前头一样显示,一脚到侬个浏览器缓存清脱为止。",
        "welcomeuser": "走来赞,$1!",
-       "welcomecreation-msg": "你个账号建起来哉。\n覅忘记哉走去改你个[[Special:Preferences|{{SITENAME}}个私人偏好]]。",
+       "welcomecreation-msg": "倷个账号建立好哉。倷可以更改自家个{{SITENAME}}[[Special:Preferences|偏好设定]]。",
        "yourname": "用户名:",
        "userlogin-yourname": "用户名",
        "userlogin-yourname-ph": "打进侬个用户名",
        "yourpasswordagain": "密码再打一遍:",
        "createacct-yourpasswordagain": "确认密码",
        "createacct-yourpasswordagain-ph": "再打一遍密码",
-       "remembermypassword": "徕箇浏览器里畀我登进去个记牢(记$1{{PLURAL:$1|日|日}})",
+       "remembermypassword": "来箇只浏览器上记牢我个登录状态(顶长$1天)",
        "userlogin-remembermypassword": "记牢我个登录状态",
        "userlogin-signwithsecure": "用保险链接",
        "yourdomainname": "侬个域名:",
        "noemailcreate": "侬要提供只有效个电子邮件地址",
        "passwordsent": "用户\"$1\"个新密码已经寄往登记个电子邮件地址。\n请收着仔再登录。",
        "blocked-mailpassword": "侬个IP地址处于查封状态,弗允许编辑,为仔安全起见,密码恢复功能已经禁用。",
-       "eauthentsent": "一封确认信已经发送到指定个电子邮箱地址。\nå\9e\83æ\8b\89å\85¶å®\83é\82®ä»¶å\8f\91é\80\81å\88°ç®\87å\8fªè´¦æ\88·ä¹\8bå\89\8dï¼\8c侬å¿\85é¡»é¦\96å\85\88æ\8c\89ç\85§ç®\87å°\81ä¿¡é\87\8cå\90\91个æ\8c\87示ï¼\8c确认ç®\87å\8fªé\82®ç®±ç\9c\9få®\9eæ\9c\89æ\95\88ã\80\82",
+       "eauthentsent": "一封确认信已经发送到指定个电子邮箱地址。å\9e\83æ\8b\89å\85¶ä»\96é\82®ä»¶å\8f\91é\80\81å\88°æ\9c¬è´¦å\8f·ä¹\8bå\89\8dï¼\8c侬å¿\85é¡»é¦\96å\85\88æ\8c\89ç\85§ç®\87å°\81ä¿¡é\87\8cå\90\91个æ\8c\87示ï¼\8c确认ç®\87å\8fªé\82®ç®±ç\9c\9få®\9eæ\9c\89æ\95\88ã\80\82",
        "throttled-mailpassword": "密码转设电子信徕最近$1个钟头里发畀你侬哉。保险点,密码转设电子信$1个钟头只一垡好发。",
        "mailerror": "发送邮件错误:$1",
        "acct_creation_throttle_hit": "弗好意思,使用箇只IP个访客已经创建仔$1只账号,迭个是箇段辰光里向所允许个最大值。箇咾使用箇只IP个地址个访客暂时弗好再创建账户。",
        "accmailtitle": "密码已发送哉。",
        "accmailtext": "已经为[[User talk:$1|$1]]产生只随机密码,并且已经发送到$2。登录之后,侬可以垃拉<em>[[Special:ChangePassword|箇只页面]]</em>更改密码。",
        "newarticle": "(新)",
-       "newarticletext": "倷跟仔链接来着一个还弗勒里个页面。\n要创建该页面呢,就勒下底个框框里向开始写([$1 帮助页面]浪有更加多个信息)。\n要是倷是弗用心到该搭个说话,只要点击倷浏览器个'''返回'''揿钮。",
+       "newarticletext": "倷跟著链接来着一个还弗勒里个页面。要创建该页面呢,就勒下底个框里向开始写([$1 帮助页面]浪有更加多个信息)。要是倷是弗用心到该𡍲个说话,请点击浏览器个<strong>返回</strong>揿钮。",
        "anontalkpagetext": "---- ''箇是一个还弗曾建立账户个匿名用户个讨论页, 箇咾我伲只好用IP地址来搭渠联络。该IP地址可能由几名用户共享。如果侬是一名匿名用户并认为箇只页面高头个评语搭侬弗搭界,请 [[Special:UserLogin/signup|创建新账户]]或[[Special:UserLogin|登录]]来避免垃拉将来搭其他匿名用户混淆。''",
-       "noarticletext": "箇页目前呒有文本。\n你侬好来别个页[[Special:Search/{{PAGENAME}}|搜寻箇页标题]]、<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} 搜寻相关日志]要勿[{{fullurl:{{FULLPAGENAME}}|action=edit}} 编箇页]。</span>",
+       "noarticletext": "箇只页面目前呒没文本。侬可以垃拉其他页面高头[[Special:Search/{{PAGENAME}}|寻该只标题]]、<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} 寻相关日志]或[{{fullurl:{{FULLPAGENAME}}|action=edit}} 编辑此页]</span>。",
        "noarticletext-nopermission": "箇只页面目前还呒不文本。侬好来别个页面[[Special:Search/{{PAGENAME}}|寻箇页标题]],或者<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} 寻相关日志]</span>,但必过侬呒不权限建立箇只页面。",
        "userpage-userdoesnotexist": "用户账户“<nowiki>$1</nowiki>”弗曾创建。请垃拉创建/编辑迭个页面前头先检查一记。",
        "userpage-userdoesnotexist-view": "用户账户“$1”弗曾创建。",
        "previewconflict": "箇个预览显示了上头文字编辑区里向个内容。渠会得垃拉侬保存之后出现。",
        "session_fail_preview": "'''弗好意思!由于会话数据落失,我伲弗好处理侬个编辑。'''请重试。如果再次失败,请尝试[[Special:UserLogout|登出]]之后重新登录。",
        "session_fail_preview_html": "'''弗好意思!我伲弗好处理侬垃拉进程数据落失辰光个编辑。'''\n\n''由于{{SITENAME}}允许使用原始个 HTML,为著防范 JavaScript 攻击,预览已畀隐藏。''\n\n'''如果这是一次合法的编辑,请重新进行尝试。'''如果还不行,请 [[Special:UserLogout|退出]]并重新登录。",
-       "token_suffix_mismatch": "'''由于侬用户端里向个编辑令牌毁损仔一些标点符号字元,为防止编辑个文字损坏,侬个编辑已经畀回头。'''\n箇种情况通常出现垃拉使用含有交关bug、以网络为主个匿名代理服务个辰光。",
+       "token_suffix_mismatch": "<strong>由于侬用户端里向个编辑令牌毁损仔一些标点符号字元,为防止编辑个文字损坏,侬个编辑已经畀回头。</strong>箇种情况通常出现垃拉使用含有交关bug、以网络为主个匿名代理服务个辰光。",
        "editing": "来里编写$1",
        "creating": "创建“$1”",
        "editingsection": "来里编辑$1(段落)",
        "edit-no-change": "侬个编辑畀忽略,因为文本弗曾有改动。",
        "postedit-confirmation-created": "页面已创建。",
        "postedit-confirmation-restored": "页面已恢复。",
-       "postedit-confirmation-saved": "个编辑已保存。",
+       "postedit-confirmation-saved": "个编辑已保存。",
        "edit-already-exists": "弗好创建新页面。\n已经有垃许。",
        "defaultmessagetext": "默认消息文本",
        "invalid-content-data": "无效内容数据",
        "gender-male": "男",
        "gender-female": "女",
        "email": "电子邮件",
-       "prefs-help-email": "电子信由你侬填弗填,转设密码用得着。",
+       "prefs-help-email": "电子邮箱是选填个,来侬忘脱密码之后好拿新密码寄畀侬。",
        "prefs-help-email-others": "你侬也好来你侬个用户|讨论页里添加自己个电子信连接畀别人联系你用。\n别人联系你是弗晓得你侬个电子信地址个。",
        "prefs-help-email-required": "需要电子邮件地址。",
        "prefs-info": "基本信息",
        "grant-createaccount": "建立账号",
        "grant-createeditmovepage": "建立、编辑搭著捅荡页面",
        "grant-rollback": "畀修改擂轉到頁面",
-       "grant-sendemail": "發電子信畀各許用戶",
+       "grant-sendemail": "发电子邮件畀其他用户",
        "newuserlogpage": "用户创建日志",
        "newuserlogpagetext": "箇是用户创建个记录。",
        "rightslog": "用户权限日志",
        "rcshowhidemine": "$1我个编辑",
        "rcshowhidemine-show": "显示",
        "rcshowhidemine-hide": "囥脱",
+       "rcshowhidecategorization-hide": "囥脱",
        "rclinks": "显示来拉上个 $2 日里向个最近 $1 趟改动<br />$3",
        "diff": "两样",
        "hist": "历史",
        "filestatus": "版权状态:",
        "filesource": "来源:",
        "ignorewarning": "弗管警告,随便哪亨要保存文件。",
-       "ignorewarnings": "任何警告都弗管",
+       "ignorewarnings": "忽略所有警告",
        "minlength1": "文件名至少一個字。",
        "illegalfilename": "“$1”文件名裏有嘸處當頁題目個字。文件名轉改再傳上來試試相。",
        "filename-toolong": "文件名嘸處比240字節長。",
        "filehist-comment": "备注",
        "imagelinks": "文件用法",
        "linkstoimage": "下头$1个页面链到箇文件:",
-       "nolinkstoimage": "呒有页链到箇文件。",
+       "nolinkstoimage": "呒不页面链接到该只文件。",
        "linkstoimage-redirect": "$1(文件轉戳到)$2",
        "sharedupload": "箇只文件来源于$1,渠作兴垃拉其它项目当中拨应用。",
-       "sharedupload-desc-here": "箇文件$1里个,作兴会畀别个项目使用。\n渠个[$2 描述页]里个说明显示如下。",
+       "sharedupload-desc-here": "箇文件$1里个,作兴会畀别个项目使用。渠个[$2 描述页]里个说明显示如下。",
        "uploadnewversion-linktext": "上载该文件个新版",
        "upload-disallowed-here": "你弗可以覆盖伊只文件。",
        "filerevert": "恢复$1",
        "newpages": "新页",
        "newpages-username": "用户名:",
        "ancientpages": "顶顶老个页面",
-       "move": "移å\88°",
+       "move": "移å\8a¨",
        "movethispage": "捅该只页面",
        "pager-newer-n": "新$1次",
        "pager-older-n": "旧$1次",
        "emailsenttext": "倷个电子邮件讯息已经拨发送哉。",
        "watchlist": "關注表",
        "mywatchlist": "我个关注表",
-       "nowatchlist": "倷个监控列表是空个。",
+       "nowatchlist": "倷个关注表是空个。",
        "watchnologin": "朆登录",
-       "addedwatchtext": "“[[:$1]]”及其讨论页已经加进侬个[[Special:Watchlist|关注表]]哉。",
+       "addedwatchtext": "“[[:$1]]”连牢著讨论页已经加进侬个[[Special:Watchlist|关注表]]哉。",
        "removewatch": "從關注表移爻",
-       "removedwatchtext": "“[[:$1]]”及其讨论页已经从侬个[[Special:Watchlist|关注表]]去脱哉。",
+       "removedwatchtext": "“[[:$1]]”连牢著讨论页已经从侬个[[Special:Watchlist|关注表]]去脱哉。",
        "watch": "关注",
        "watchthispage": "监控该只页面",
        "unwatch": "弗关注",
        "unwatchthispage": "停止监控",
        "notanarticle": "弗是內容頁",
        "watchlist-details": "有$1页垃拉侬关注表高头,弗包括讨论页。",
-       "wlheader-showupdated": "勒侬上趟查看之后畀修改个页面<strong>加粗</strong>显示。",
+       "wlheader-showupdated": "勒侬上趟查看之后修改过个页面<strong>加粗</strong>显示。",
        "wlnote": "下底是{{PLURAL:$2|过去<strong>$2</strong>个钟头}}个{{PLURAL:$1|最后<strong>$1</strong>届更改}},截至$3 $4。",
        "wlshowlast": "显示上$1个钟头$2日天",
        "watchlist-hide": "囥脱",
        "wlshowhidemine": "我个编辑",
-       "watchlist-options": "监控列表选项",
+       "watchlist-options": "关注表选项",
        "watching": "监控……",
        "unwatching": "解除监控……",
        "created": "建立哉",
        "rollbackfailed": "恢复失败",
        "cantrollback": "弗好恢复编辑;阿末个贡献人是本页唯一个作者。",
        "alreadyrolled": "恢复弗落[[User:$2|$2]]([[User talk:$2|讲张]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]])对[[:$1]]个编辑,其他人已经编辑歇或恢复过该个页面。\n\n最后编辑者是[[User:$3|$3]]([[User talk:$3|讲张]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]])。",
-       "revertpage": "取消[[Special:Contributions/$2|$2]]([[User talk:$2|讲张]])个改动;恢复到[[Special:Contributions/$1|$1]]个阿末只版本",
+       "revertpage": "取消[[Special:Contributions/$2|$2]]([[User talk:$2|讲张]])个改动;恢复到[[User:$1|$1]]个阿末只版本",
        "protectlogpage": "保护日志",
        "protectedarticle": "保护“[[$1]]”",
        "modifiedarticleprotection": "“[[$1]]”个保护等级改好哉",
        "sp-contributions-blocklog": "查封记录",
        "sp-contributions-deleted": "删脱个用户贡献",
        "sp-contributions-talk": "讲张",
-       "sp-contributions-search": "寻贡献记录",
+       "sp-contributions-search": "寻贡献记录",
        "sp-contributions-username": "IP地址要勿用户名:",
        "sp-contributions-submit": "搜寻",
        "whatlinkshere": "有啥链到箇里",
        "unlockbtn": "數據庫開鎖",
        "databasenotlocked": "數據庫朆鎖牢。",
        "move-page-legend": "页面捅荡",
-       "movepagetext": "用下底个表会重命名一只页面,全部历史侪移到新名字里。老个名字会变成戳到新名字个重定向页。注意检查[[Special:DoubleRedirects|双重重定向]]或者[[Special:BrokenRedirects|坏脱个重定向]]。倷有实概个责任,让链接仍旧链到俚笃应该链到个场化去。\n\n注意,如果新名字归面搭已经有页面个说话,老名字个页面'''弗'''会畀移动,除非归个是只空页面或者是只重定向并且呒不编辑历史。箇也就是讲,假使倷犯错误个说话,倷好拿一只重命名过个页面还原到原来个名字,但倷弗好覆盖一只已经来上个页面。\n\n<strong>警告!</strong>箇呒数会引起对一只热门页面剧烈个、想弗着个改变。来操作前头请倷确定倷已经充分了解行为个后果。",
+       "movepagetext": "用下底个表会重命名一只页面,全部历史侪移到新名字里。老个名字会变成戳到新名字个重定向页。注意检查[[Special:DoubleRedirects|双重重定向]]或者[[Special:BrokenRedirects|坏脱个重定向]]。倷有实概个责任,让链接仍旧链到俚笃应该链到个场化去。\n\n注意,如果新名字归面𡍲已经有页面个说话,老名字个页面<strong>弗会</strong>畀移动,除非归个是只空页面或者是只重定向并且呒不编辑历史。箇也就是讲,假使倷犯错误个说话,倷好拿一只重命名过个页面还原到原来个名字,但倷弗好覆盖一只已经来上个页面。\n\n<strong>注意:</strong>箇呒数会引起对一只热门页面剧烈个、想弗着个改变。来操作前头请倷确定倷已经充分了解行为个后果。",
        "movepagetalktext": "如果侬勾选此框,相关讨论页会自动移动到新标题,除非箇𡍲已经有著一只非空个讨论页。\n\n来箇种情况下底,如果有需要,侬必须手工移动或合并页面。",
        "movenologintext": "倷板定要是注册用户并且[[Special:UserLogin|登录]]著才好拿页面捅荡。",
        "newtitle": "新标题:",
        "movereason": "理由:",
        "revertmove": "恢复",
        "delete_and_move_confirm": "对哉,删脱该只页面",
+       "semiprotectedpagemovewarning": "<strong>注意:</strong>箇只页面畀保护来许,只有注册用户才好移动渠。下底提供最近个日志畀侬参考:",
        "export": "页导出",
        "allmessages": "系统讯息",
        "allmessagesname": "名字",
        "allmessagesdefault": "默认文本",
        "allmessagescurrent": "当前文本",
-       "allmessagestext": "该个是MediaWiki名字空间里可用个系统消息列表。如果想MediaWiki个本地化贡献翻译,请访问[https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation MediaWiki本地化]搭[//translatewiki.net translatewiki.net]。",
+       "allmessagestext": "该个是MediaWiki名字空间里可用个系统消息列表。如果想MediaWiki个本地化贡献翻译,请访问[https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation MediaWiki本地化]搭[//translatewiki.net translatewiki.net]。",
        "allmessagesnotsupportedDB": "'''{{ns:special}}:Allmessages''' 呒处显示,因为 '''$wgUseDatabaseMessages''' 关勒浪。",
        "thumbnail-more": "放大",
        "filemissing": "文件寻弗着哉",
        "tooltip-pt-userpage": "{{GENDER:|侬个用户}}页",
        "tooltip-pt-mytalk": "{{GENDER:|侬}}个讨论页",
+       "tooltip-pt-anontalk": "有关箇只IP地址编辑个讨论",
        "tooltip-pt-preferences": "{{GENDER:|侬}}个设置",
        "tooltip-pt-watchlist": "监控修改页面列表",
        "tooltip-pt-mycontris": "{{GENDER:|侬}}个贡献列表",
+       "tooltip-pt-anoncontribs": "箇只IP地址个编辑清单",
        "tooltip-pt-login": "鼓励大家登录进来,不过也弗是板定要求",
        "tooltip-pt-logout": "登出",
        "tooltip-pt-createaccount": "建议你建立一个账号并登录,但必过箇弗是板要个",
        "tooltip-ca-watch": "拿箇只页面加到侬个关注表里向",
        "tooltip-ca-unwatch": "拿箇只页面从侬个关注表里删脱",
        "tooltip-search": "搜寻{{SITENAME}}",
-       "tooltip-search-go": "如果存在相同标题,箇么直接去该页面",
+       "tooltip-search-go": "如果存在一样个标题就直接到箇只页面𡍲去",
        "tooltip-search-fulltext": "搜寻包含箇星文本个页面",
        "tooltip-p-logo": "翻到封面",
        "tooltip-n-mainpage": "翻到封面",
        "tooltip-t-whatlinkshere": "列出全部搭箇页链个页",
        "tooltip-t-recentchangeslinked": "箇页链出去个全部页箇阶段变化",
        "tooltip-feed-rss": "订阅本页",
-       "tooltip-feed-atom": "此页个Atom 订阅",
+       "tooltip-feed-atom": "此页个Atom订阅",
        "tooltip-t-contributions": "{{GENDER:$1|箇位用户}}个贡献列表",
        "tooltip-t-emailuser": "发电子信畀{{GENDER:$1|箇位用户}}",
        "tooltip-t-upload": "上传文件",
        "tooltip-ca-nstab-category": "望分类页",
        "tooltip-minoredit": "标作小编写",
        "tooltip-save": "保存侬个修改",
-       "tooltip-preview": "望望相你侬个修改,保存之前用!",
+       "tooltip-preview": "预览倷个更改。请勒拉保存前头用用俚。",
        "tooltip-diff": "显示侬对文本个修改",
        "tooltip-compareselectedversions": "查看本页面两只选定个修订版个差异。",
        "tooltip-watch": "拿箇页加到侬个关注表里",
        "tooltip-summary": "打进短摘要",
        "interlanguage-link-title": "̩$1 - $2",
        "anonymous": "{{SITENAME}}上个匿名{{PLURAL:$1|用户}}",
-       "simpleantispam-label": "反垃圾检查。\n<strong>覅</strong>加进伊个!",
+       "simpleantispam-label": "反垃圾检查。<strong>弗要</strong>填伊个!",
        "pageinfo-toolboxlink": "页面信息",
        "deletedrevision": "拨删脱个旧修订 $1",
        "previousdiff": "←老版",
        "autoredircomment": "重定向页面至[[$1]]",
        "autosumm-new": "新页面:“$1”",
        "watchlistedit-normal-title": "编辑监视列表",
+       "watchlistedit-normal-done": "{{PLURAL:$1|$1个}}标题已经从倷个关注表里向拿脱哉:",
+       "watchlistedit-raw-done": "侬个关注表已经更新。",
        "watchlistedit-raw-added": "$1个标题已经加进去哉:",
        "watchlistedit-raw-removed": "$1个标题已经拿脱哉:",
+       "watchlistedit-clear-title": "清空关注表",
+       "watchlistedit-clear-legend": "清空关注表",
+       "watchlistedit-clear-explain": "所有标题会从侬个关注表里向拿脱",
+       "watchlistedit-clear-submit": "清空关注表(永远!)",
+       "watchlisttools-clear": "清空关注表",
        "watchlisttools-view": "望相关修改",
        "watchlisttools-edit": "查看并编辑关注表",
        "watchlisttools-raw": "编写原始关注表",
        "logentry-newusers-create": "用户账号$1畀{{GENDER:$2|创建}}",
        "logentry-newusers-create2": "用户账号$3畀$1{{GENDER:$2|创建}}",
        "logentry-newusers-autocreate": "用户账号$1畀自动{{GENDER:$2|创建}}",
+       "logentry-rights-rights": "$1{{GENDER:$2|更改}}$3个用户组从$4到$5",
        "logentry-upload-upload": "$1{{GENDER:$2|上传}}$3",
        "rightsnone": "(呒)",
        "revdelete-summary": "编辑摘要",
index 9d13582..e2373e0 100644 (file)
        "databaseerror-function": "函数:$1",
        "databaseerror-error": "错误:$1",
        "transaction-duration-limit-exceeded": "因为写入时间($1)超过了$2{{PLURAL:$2|秒}}的限制,为防止创建大量复制延迟,此次处理已被中止。如果您正在同时更改很多项目,请尝试进行多次小规模操作。",
-       "laggedslavemode": "'''警告:'''页面中可能没有包含最近的更新。",
+       "laggedslavemode": "<strong>警告:</strong>页面中可能没有包含最近的更新。",
        "readonly": "数据库被锁定",
        "enterlockreason": "请输入锁定的原因,这包括预计解除锁定的时间",
        "readonlytext": "数据库当前被锁定,不能添加新条目或进行其他修改,锁定可能是因为例行的数据库维护,完成后即可恢复正常。\n\n锁定数据库的系统管理员做出如下解释:$1",
        "editinginterface": "<strong>警告:</strong>您正在编辑用于提供软件的界面文字的页面。改变此页将影响其他在此wiki上其他用户的用户界面外观。",
        "translateinterface": "要加入或更改所有wiki的翻译,请访问MediaWiki本地化项目网站[//translatewiki.net/ translatewiki.net]。",
        "cascadeprotected": "本页面已经受到保护,不能编辑,因为它被嵌入于以下被“连锁保护”的{{PLURAL:$1|页面}}:\n$2",
-       "namespaceprotected": "您没有权限编辑'''$1'''名字空间内的页面。",
+       "namespaceprotected": "您没有权限编辑<strong>$1</strong>名字空间内的页面。",
        "customcssprotected": "您没有权限编辑此CSS页面,因为它包含另一位用户的个人设置。",
        "customjsprotected": "您没有权限编辑此JavaScript页面,因为它包含另一位用户的个人设置。",
        "mycustomcssprotected": "您没有权限编辑这个 CSS 页面。",
        "mycustomjsprotected": "您没有权限编辑这个 JavaScript 页面。",
-       "myprivateinfoprotected": "你没有权限编辑你的私人信息。",
+       "myprivateinfoprotected": "您没有权限编辑您的私人信息。",
        "mypreferencesprotected": "您没有权限来编辑您的个人设置。",
        "ns-specialprotected": "特殊页面不可编辑。",
-       "titleprotected": "此标题已被[[User:$1|$1]]保护以防止创建。理由是“$2”。",
+       "titleprotected": "此标题已被[[User:$1|$1]]保护以防止创建。理由是“<em>$2</em>”。",
        "filereadonlyerror": "因为媒体库“$2”处于只读模式而无法修改文件“$1”。\n\n锁定数据库的系统管理员做出如下解释:“$3”。",
        "invalidtitle-knownnamespace": "使用名字空间“$2”和文本“$3”的无效标题",
        "invalidtitle-unknownnamespace": "使用未知名字空间编号$1和文本“$2”的无效标题",
        "exception-nologin": "未登录",
        "exception-nologin-text": "请登录以访问此页面或进行操作。",
        "exception-nologin-text-manual": "查看该页面或进行此操作需要您$1。",
-       "virus-badscanner": "错误的配置:未知的病毒扫描程序:''$1''",
+       "virus-badscanner": "错误的配置:未知的病毒扫描程序:<em>$1</em>",
        "virus-scanfailed": "扫描失败(代码 $1)",
        "virus-unknownscanner": "未知的反病毒软件:",
        "logouttext": "<strong>您现在已经退出登录。</strong>\n\n请注意一些页面可能仍然显示您处于登录状态,直到您清空浏览器缓存为止。",
        "cannotlogoutnow-title": "现在不能退出",
        "cannotlogoutnow-text": "当使用$1时无法退出。",
        "welcomeuser": "欢迎,$1!",
-       "welcomecreation-msg": "你的账户已创建。请不要忘记更改你的[[Special:Preferences|{{SITENAME}}设置]]。",
+       "welcomecreation-msg": "您的账户已创建。\n如果需要,您可以更改您在{{SITENAME}}的[[Special:Preferences|参数设置]]。",
        "yourname": "用户名:",
        "userlogin-yourname": "用户名",
        "userlogin-yourname-ph": "请输入你的用户名",
        "showpreview": "显示预览",
        "showdiff": "显示更改",
        "blankarticle": "<strong>警告</strong>:您创建的页面是空白的。如果您再次点击“{{int:savearticle}}”,您将真的创建没有任何内容的页面。",
-       "anoneditwarning": "<strong>警告:</strong>您没有登录。如果您做出任意编辑,您的IP地址将会公开可见。如果您<strong>[$1 登]</strong>或<strong>[$2 创建]</strong>一个账户,您的编辑将归属于您的用户名,且将享受其他好处。",
+       "anoneditwarning": "<strong>警告:</strong>您没有登录。如果您做出任意编辑,您的IP地址将会公开可见。如果您<strong>[$1 登]</strong>或<strong>[$2 创建]</strong>一个账户,您的编辑将归属于您的用户名,且将享受其他好处。",
        "anonpreviewwarning": "<em>您没有登录。保存将您的IP地址记录至此页面的编辑历史中。</em>",
-       "missingsummary": "'''提示:'''你没有提供编辑摘要。如果你再次点击“{{int:savearticle}}”,你的编辑将不带编辑摘要保存。",
+       "missingsummary": "<strong>提示:</strong>您没有提供编辑摘要。如果您再次点击“{{int:savearticle}}”,您的编辑将不带摘要保存。",
        "selfredirect": "<strong>警告:</strong>您正在将此页面重定向至它自己。\n您可能指定了错误的重定向目标,或者您正在编辑错误的页面。\n如果您再次点击“{{int:savearticle}}”,重定向将无论如何被创建。",
        "missingcommenttext": "请在下面输入评论。",
        "missingcommentheader": "<strong>提示:</strong>您还没有为此评论提供一个标题。如果您再次点击“{{int:savearticle}}”,您的编辑将不带标题保存。",
        "subject-preview": "主题预览:",
        "previewerrortext": "尝试预览您的更改时发生未知错误。",
        "blockedtitle": "用户被封禁",
-       "blockedtext": "<strong>你的用户名或IP地址已被封禁。</strong>\n\n执行封禁的管理员是$1。封禁原因是<em>$2</em>。\n\n* 开始时间:$8\n* 到期时间:$6\n* 目标用户:$7\n\n你可以联系$1或其他[[{{MediaWiki:Grouppage-sysop}}|管理员]]讨论该封禁。只有当你在[[Special:Preferences|系统设置]]确认了电子邮件地址且未被禁止使用“电邮联系”功能时,才可以使用它。你当前的IP地址是$3,该封禁ID是#$5。请在你的询问中包含上面的所有信息。",
+       "blockedtext": "<strong>您的用户名或IP地址已被封禁。</strong>\n\n执行封禁的管理员是$1。封禁原因是<em>$2</em>。\n\n* 开始时间:$8\n* 到期时间:$6\n* 目标用户:$7\n\n您可以联络$1或其他[[{{MediaWiki:Grouppage-sysop}}|管理员]]讨论该封禁。只有当您在[[Special:Preferences|系统设置]]确认了电子邮件地址且未被禁止使用“电邮联系”功能时,才可以使用它。您当前的IP地址是$3,该封禁ID是#$5。请在您的询问中包含上面的所有信息。",
        "autoblockedtext": "您的IP地址因曾被一位被$1封禁的用户使用而被自动封禁。封禁原因:\n\n:<em>$2</em>\n\n* 开始时间:$8\n* 到期时间:$6\n* 目标用户:$7\n\n您可以联系$1或其他[[{{MediaWiki:Grouppage-sysop}}|管理员]]申诉该封禁。\n\n请注意,只有当您已在[[Special:Preferences|系统设置]]确认了电子邮件地址且未被禁止使用“电邮联系”功能时,才能发送电子邮件联系管理员。\n\n您当前的IP地址为$3,该封禁ID为#$5。\n请您在申诉内容中说明以上所有信息。",
        "blockednoreason": "未给出原因",
        "whitelistedittext": "请$1以编辑页面。",
        "accmailtext": "为[[User talk:$1|$1]]随机生成的密码已送至$2。登录后可以在<em>[[Special:ChangePassword|更改密码]]</em>页面中修改。",
        "newarticle": "(新页面)",
        "newarticletext": "您点击了一个尚不存在的页面的链接。要创建该页面,请在下面的编辑框中输入内容(更多信息请见[$1 帮助页面])。如果您是错误地进入了此页面,请点击您的浏览器的<strong>返回</strong>按钮。",
-       "anontalkpagetext": "---- ''这是一个还未建立账户的匿名用户的讨论页, 因此我们只能用IP地址来与他或她联络。该IP地址可能由几名用户共享。如果您是一名匿名用户并认为此页上的评语与您无关,请[[Special:UserLogin/signup|创建新账户]]或[[Special:UserLogin|登录]]以避免在未来与其他匿名用户混淆。''",
-       "noarticletext": "本页面目前没有内容。可以在其他页面中[[Special:Search/{{PAGENAME}}|搜索本页标题]]、<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} 搜索相关日志]或[{{fullurl:{{FULLPAGENAME}}|action=edit}} 编辑本页面]。</span>",
-       "noarticletext-nopermission": "本页面目前没有内容。你可以在其他页面中[[Special:Search/{{PAGENAME}}|搜索本页标题]]或<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} 搜索相关日志]</span>,但你没有权限创建本页面。",
+       "anontalkpagetext": "----\n<em>这是一个还未建立账户的匿名用户的讨论页, 因此我们只能用IP地址来与他或她联络。</em>该IP地址可能由几名用户共享。如果您是一名匿名用户并认为此页上的评语与您无关,请[[Special:UserLogin/signup|创建新账户]]或[[Special:UserLogin|登录]]以避免在未来与其他匿名用户混淆。",
+       "noarticletext": "本页面目前没有内容。可以在其他页面中[[Special:Search/{{PAGENAME}}|搜索本页标题]]、<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} 搜索相关日志]或[{{fullurl:{{FULLPAGENAME}}|action=edit}} 编辑本页面]。</span>",
+       "noarticletext-nopermission": "本页面目前没有内容。您可以在其他页面中[[Special:Search/{{PAGENAME}}|搜索本页标题]]或<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} 搜索相关日志]</span>,但您没有权限创建本页面。",
        "missing-revision": "“{{FULLPAGENAME}}”的版本#$1不存在。\n\n这通常是因为进入了一个已被删除的页面的历史链接。\n详细信息可以在[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} 删除日志]中找到。",
        "userpage-userdoesnotexist": "用户账户“$1”没有注册。请在创建/编辑本页前检查。",
        "userpage-userdoesnotexist-view": "用户账户“$1”没有被注册。",
        "clearyourcache": "<strong>注意:</strong>在保存之后,您可能需要清除浏览器缓存才能看到所作出的变更的影响。\n* <strong>Firefox或Safari:</strong>按住“Shift”的同时单击“刷新”,或按“Ctrl-F5”或“Ctrl-R”(Mac为“⌘-R”)\n* <strong>Google Chrome:</strong>按“Ctrl-Shift-R”(Mac为“⌘-Shift-R”)\n* <strong>Internet Explorer:</strong>按住“Ctrl”的同时单击“刷新”,或按“Ctrl-F5”\n* <strong>Opera:</strong>在“工具→首选项”中清除缓存",
        "usercssyoucanpreview": "<strong>提示:</strong>在保存前请用“{{int:showpreview}}”按钮来测试您新的 CSS 。",
        "userjsyoucanpreview": "<strong>提示:</strong>在保存前请用“{{int:showpreview}}”按钮来测试您新的 JavaScript 。",
-       "usercsspreview": "'''请记住你现在只是在预览你的用户CSS。它尚未保存!'''",
-       "userjspreview": "'''请记住你现在只是在测试/预览你的用户JavaScript。它尚未保存!'''",
-       "sitecsspreview": "'''请记住你现在只是在预览该CSS。它尚未保存!'''",
-       "sitejspreview": "'''请记住你现在只是在预览该JavaScript代码。它尚未保存!'''",
+       "usercsspreview": "<strong>请记住您现在只是在预览你的用户CSS。它尚未保存!</strong>",
+       "userjspreview": "<strong>请记住你现在只是在测试/预览你的用户JavaScript。它尚未保存!</strong>",
+       "sitecsspreview": "<strong>请记住你现在只是在预览该CSS。它尚未保存!</strong>",
+       "sitejspreview": "<strong>请记住你现在只是在预览该JavaScript代码。它尚未保存!</strong>",
        "userinvalidcssjstitle": "<strong>警告:</strong>不存在皮肤“$1”。注意自定义的 .css 和 .js 页要使用小写标题,例如,{{ns:user}}:Foo/vector.css 不同于 {{ns:user}}:Foo/Vector.css。",
        "updated": "(已更新)",
-       "note": "'''注意:'''",
-       "previewnote": "'''请记住这只是预览。'''你的更改还没有保存!",
+       "note": "<strong>注意:</strong>",
+       "previewnote": "<strong>请记住这只是预览。</strong>\n您的更改还没有保存!",
        "continue-editing": "前往编辑区",
        "previewconflict": "该预览反映了上面文字编辑区中的文字在你保存后的显示状况。",
-       "session_fail_preview": "'''对不起!由于会话数据丢失,我们无法处理你的编辑。'''请重试。如果仍然失败,请尝试[[Special:UserLogout|退出登录]]后重新登录。",
-       "session_fail_preview_html": "'''对不起!由于会话数据丢失,我们无法处理你的编辑。'''\n\n''因为{{SITENAME}}已启用原始HTML,为了预防JavaScript攻击,预览被隐藏。''\n\n'''如果该编辑尝试合法,请重试。'''如果仍然失败,请尝试[[Special:UserLogout|退出登录]]后重新登录。",
+       "session_fail_preview": "对不起!由于会话数据丢失,我们无法处理您的编辑。\n\n您可能已经退出。<strong>请核实您是否仍在登录,并重试</strong>。\n如果仍然不能工作,尝试[[Special:UserLogout|退出]]并重新登录,并检查您的浏览器是否允许来自该网站的cookie。",
+       "session_fail_preview_html": "对不起!由于会话数据丢失,我们无法处理您的编辑,\n\n<em>因为{{SITENAME}}已启用原始HTML,为了预防JavaScript攻击,预览被隐藏。</em>\n\n<strong>如果该编辑尝试合法,请重试。</strong>\n如果仍然不能工作,尝试[[Special:UserLogout|退出]]并重新登录,并检查您的浏览器是否允许来自该网站的cookie。",
        "token_suffix_mismatch": "<strong>由于您客户端中的编辑令牌毁损了一些标点符号字符,您的编辑已经被拒绝。</strong>\n此次编辑被拒绝以防止页面文本损坏。\n这种情况通常在您使用含有故障的网页式匿名代理服务的时候出现。",
-       "edit_form_incomplete": "'''编辑表格的某些部分没有到达服务器,请检查你的编辑是否完整并重试。'''",
+       "edit_form_incomplete": "<strong>编辑表格的某些部分没有到达服务器,请检查您的编辑是否完整并重试。</strong>",
        "editing": "编辑“$1”",
        "creating": "创建“$1”",
        "editingsection": "编辑“$1(段落)”",
        "explainconflict": "其他用户在你开始编辑后更改了该页面。上面的文字区含有该页面当前的文字。下面的文字区显示你的更改。你必须把你的更改合并至现有文字。'''只有'''当你单击“{{int:savearticle}}”后,上面的文字区中的文字才会被保存。",
        "yourtext": "您的文字",
        "storedversion": "已保存的版本",
-       "nonunicodebrowser": "'''警告:您的浏览器不兼容Unicode编码。'''这里有一个工作区将使您能安全地编辑页面:非ASCII字符将以十六进制编码方式出现在编辑框中。",
-       "editingold": "'''警告:你正在编辑的是本页面的旧版本。'''如果你保存该编辑,该版本后的所有更改都会丢失。",
+       "nonunicodebrowser": "<strong>警告:您的浏览器不兼容Unicode编码。</strong>这里有一个工作区将使您能安全地编辑页面:非ASCII字符将以十六进制编码方式出现在编辑框中。",
+       "editingold": "<strong>警告:您正在编辑的是本页面的旧版本。</strong>如果您保存该编辑,该版本后的所有更改都会丢失。",
        "yourdiff": "差异",
        "copyrightwarning": "请注意您对{{SITENAME}}的所有贡献都被认为是在$2下发布,请查看在$1的细节。\n如果您不希望您的文字被任意修改和再散布,请不要提交。<br />\n您同时也要向我们保证您所提交的内容是您自己所作,或得自一个不受版权保护或相似自由的来源。\n'''不要在未获授权的情况下发表!'''<br />",
-       "copyrightwarning2": "请注意,您对{{SITENAME}}的所有贡献都可能被其他贡献者编辑,修改或删除。如果您不希望您的文字被任意修改和再散布,请不要提交。<br />\n您同时也要向我们保证您所提交的内容是您自己所作,或得自一个不受版权保护或相似自由的来源(参阅$1的细节)。'''不要在未获授权的情况下发表!'''",
+       "copyrightwarning2": "请注意,您对{{SITENAME}}的所有贡献都可能被其他贡献者编辑,修改或删除。如果您不希望您的文字被任意修改和再散布,请不要提交。<br />\n您同时也要向我们保证您所提交的内容是您自己所作,或得自一个不受版权保护或相似自由的来源(参阅$1的细节)。<strong>不要在未获授权的情况下发表!</strong>",
        "editpage-cannot-use-custom-model": "此页面的内容模型不能被更改。",
-       "longpageerror": "'''错误:您所提交的文本长度有{{PLURAL:$1|1|$1}}KB,这大于{{PLURAL:$2|1|$2}}KB的最大值。'''\n因此,该文本无法保存。",
+       "longpageerror": "<strong>错误:您所提交的文本长度有{{PLURAL:$1|1|$1}}KB,这大于{{PLURAL:$2|1|$2}}KB的最大值。</strong>\n因此,该文本无法保存。",
        "readonlywarning": "<strong>警告:数据库被锁定以进行维护,所以您目前将无法保存您的编辑。</strong>您可以将您的文本复制粘贴到一个文本文档并保存它,以便稍后更改。\n\n锁定数据库的系统管理员做出如下解释:$1",
-       "protectedpagewarning": "'''警告:本页面已被保护,只有拥有管理员权限的用户可以编辑。'''下面提供最后的日志条目以供参考:",
-       "semiprotectedpagewarning": "'''注意:'''本页面已被保护,只有注册用户可以编辑。下面提供最后的日志条目以供参考:",
+       "protectedpagewarning": "<strong>警告:本页面已被保护,只有拥有管理员权限的用户可以编辑。</strong>下面提供最后的日志条目以供参考:",
+       "semiprotectedpagewarning": "<strong>注意:</strong>本页面已被保护,只有注册用户可以编辑。下面提供最后的日志条目以供参考:",
        "cascadeprotectedwarning": "<strong>警告:</strong>本页面已经被保护,只有拥有管理员权限的用户可以编辑,因为它被嵌入于以下启用连锁保护的{{PLURAL:$1|页面}}中:",
-       "titleprotectedwarning": "'''警告:本页面已被保护,创建本页面需要[[Special:ListGroupRights|特定权限]]。'''下面提供最后的日志条目以供参考:",
+       "titleprotectedwarning": "<strong>警告:本页面已被保护,创建本页面需要[[Special:ListGroupRights|特定权限]]。</strong>下面提供最后的日志条目以供参考:",
        "templatesused": "该页面使用的{{PLURAL:$1|模板}}:",
        "templatesusedpreview": "本预览使用的{{PLURAL:$1|模板}}:",
        "templatesusedsection": "该段落使用的{{PLURAL:$1|模板}}:",
        "template-semiprotected": "(受半保护)",
        "hiddencategories": "该页面属于$1个隐藏分类:",
        "edittools": "<!-- 这里的文字将显示在编辑和上传表格下面。 -->",
-       "nocreatetext": "{{SITENAME}}已经限制创建新页面功能。可以返回编辑现有页面或[[Special:UserLogin|登录或创建账户]]。",
-       "nocreate-loggedin": "没有权限创建新页面。",
+       "nocreatetext": "{{SITENAME}}已经限制创建新页面功能。可以返回编辑现有页面或[[Special:UserLogin|登录或创建账户]]。",
+       "nocreate-loggedin": "没有权限创建新页面。",
        "sectioneditnotsupported-title": "段落编辑不支持",
        "sectioneditnotsupported-text": "本页面不支持段落编辑。",
        "permissionserrors": "权限错误",
-       "permissionserrorstext": "因为以下{{PLURAL:$1|原因}},你没有权限进行该操作:",
-       "permissionserrorstext-withaction": "因为以下{{PLURAL:$1|原因}},没有权限$2:",
+       "permissionserrorstext": "因为以下{{PLURAL:$1|原因}},您没有权限这样做:",
+       "permissionserrorstext-withaction": "因为以下{{PLURAL:$1|原因}},没有权限$2:",
        "contentmodelediterror": "您不能编辑此修订版本,因为它的内容模型是<code>$1</code>,这与当前页面<code>$2</code>的内容模型不同。",
-       "recreate-moveddeleted-warn": "'''警告:你正在重新创建曾经被删除的页面。'''\n\n你应该考虑继续编辑本页是否合适。这里提供本页的删除和移动日志以供参考:",
+       "recreate-moveddeleted-warn": "<strong>警告:您正在重新创建曾经被删除的页面。</strong>\n\n您应该考虑继续编辑本页是否合适。这里提供本页的删除和移动日志以供参考:",
        "moveddeleted-notice": "本页面已被删除。下面提供本页的删除和移动日志以供参考。",
        "moveddeleted-notice-recent": "抱歉,此页面刚刚被删除(在最近24小时内)。\n页面的删除和移动日志在下方提供以供参考。",
        "log-fulllog": "查看完整日志",
        "edit-hook-aborted": "编辑被hook指令取消。\n无解释。",
        "edit-gone-missing": "不能更新页面。\n它可能刚刚被删除。",
        "edit-conflict": "编辑冲突。",
-       "edit-no-change": "因为没有文字更改,的编辑已被忽略。",
+       "edit-no-change": "因为没有文字更改,的编辑已被忽略。",
        "postedit-confirmation-created": "页面已创建。",
        "postedit-confirmation-restored": "页面已恢复。",
-       "postedit-confirmation-saved": "的编辑已保存。",
+       "postedit-confirmation-saved": "的编辑已保存。",
        "edit-already-exists": "不可以建立一个新页面。\n它已经存在。",
        "defaultmessagetext": "默认消息文本",
        "content-failed-to-parse": "未能将 $2 内容转换为 $1:$3",
        "invalid-content-data": "无效的内容数据",
        "content-not-allowed-here": "[[$2]]页面上不允许“$1”内容",
-       "editwarning-warning": "离开本页面可能导致你失去任何你已经作出的更改。如果你处于登录状态,你可以在你的设置的“{{int:prefs-editing}}”部分停用该警告。",
+       "editwarning-warning": "离开本页面可能导致您失去任何你已经作出的更改。如果您处于登录状态,您可以在您的设置的“{{int:prefs-editing}}”部分停用该警告。",
        "editpage-notsupportedcontentformat-title": "内容格式尚不支持",
        "editpage-notsupportedcontentformat-text": "内容模型$2尚不支持内容格式$1。",
        "content-model-wikitext": "维基文字",
        "duplicate-args-warning": "<strong>警告:</strong>[[:$1]]正在调用超过一个[[:$2]]中“$3”参数的值。只有最后提供的值会被使用。",
        "duplicate-args-category": "调用重复模板参数的页面",
        "duplicate-args-category-desc": "页面包含调用了重复参数的模板,例如<code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code>或<code><nowiki>{{foo|bar|1=baz}}</nowiki></code>。",
-       "expensive-parserfunction-warning": "<strong>警告:</strong>这个页面有太多高昂的语法功能调用。\n\n它应该少过$2次呼叫,现在有$1次呼叫。",
+       "expensive-parserfunction-warning": "<strong>警告:</strong>这个页面有太多高开销解析器函数调用。\n\n它应少于$2次{{PLURAL:$2|调用}},而目前有{{PLURAL:$1|$1次调用}}。",
        "expensive-parserfunction-category": "有过多高开销解析器函数调用的页面",
-       "post-expand-template-inclusion-warning": "'''警告:'''包含模板大小过大。\n一些模板将不会包含。",
+       "post-expand-template-inclusion-warning": "<strong>警告:</strong>包含模板大小过大。\n一些模板将不会包含。",
        "post-expand-template-inclusion-category": "模板包含上限已经超过的页面",
        "post-expand-template-argument-warning": "<strong>警告:</strong>本页面包含至少一个模板参数有过大扩展大小。这些参数会被略过。",
        "post-expand-template-argument-category": "含有略过模板参数的页面",
        "expansion-depth-exceeded-category": "扩展深度超出限制的页面",
        "expansion-depth-exceeded-category-desc": "页面超出最大展开深度限制。",
        "expansion-depth-exceeded-warning": "页面超出展开深度限制",
-       "parser-unstrip-loop-warning": "检测到回圈",
-       "parser-unstrip-recursion-limit": "递归超过限制 ($1)",
+       "parser-unstrip-loop-warning": "检测到Unstrip循环",
+       "parser-unstrip-recursion-limit": "已超出Unstrip递归限制($1)",
        "converter-manual-rule-error": "在手动语言转换规则中检测到错误",
-       "undo-success": "该编辑可以被撤销。请检查下面的对比以核实想要撤销的内容,然后保存下面的更改以完成撤销。",
+       "undo-success": "该编辑可以被撤销。请检查下面的对比以核实想要撤销的内容,然后保存下面的更改以完成撤销。",
        "undo-failure": "因存在冲突的中间编辑,本编辑不能撤销。",
        "undo-norev": "该编辑无法撤消,因为它不存在或已被删除。",
        "undo-nochange": "这次编辑似乎已被撤销。",
        "rev-deleted-user": "(用户名被删除)",
        "rev-deleted-event": "(日志详情已移除)",
        "rev-deleted-user-contribs": "[用户名或IP地址被删除 - 编辑在贡献中隐藏]",
-       "rev-deleted-text-permission": "本页面版本已被'''删除'''。详情请见[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} 删除日志]。",
+       "rev-deleted-text-permission": "本页面版本已被<strong>删除</strong>。详情请见[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} 删除日志]。",
        "rev-suppressed-text-permission": "此页面修订已经被<strong>监督隐藏</strong>。详细信息可在[{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} 监督日志]中找到。",
-       "rev-deleted-text-unhide": "本页面版本已被'''删除'''。详情请见[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} 删除日志]。如果你想继续操作,你仍然可以[$1 查看本版本]。",
-       "rev-suppressed-text-unhide": "该页面版本已经被'''监督隐藏'''。在[{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} 监督日志]中可以找到详细的信息。如果您想继续的话,您可以仍然[$1 去查看这次版本]。",
-       "rev-deleted-text-view": "本页面版本已被'''删除'''。你可以查看它,详情请见[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} 删除日志]。",
-       "rev-suppressed-text-view": "该页面版本已经被'''监督隐藏'''。您可以查看它。在[{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} 监督日志]中可以找到详细的信息。",
-       "rev-deleted-no-diff": "你不能查看该差异,因为其中一个版本已被'''删除'''。详情请见[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} 删除日志]。",
+       "rev-deleted-text-unhide": "本页面版本已被<strong>删除</strong>。详情请见[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} 删除日志]。如果您想继续操作,您仍然可以[$1 查看本版本]。",
+       "rev-suppressed-text-unhide": "该页面版本已经被<strong>监督隐藏</strong>。在[{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} 监督日志]中可以找到详细的信息。如果您想继续的话,您可以仍然[$1 去查看这次版本]。",
+       "rev-deleted-text-view": "本页面版本已被<strong>删除</strong>。您可以查看它,详情请见[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} 删除日志]。",
+       "rev-suppressed-text-view": "该页面版本已经被<strong>监督隐藏</strong>。您可以查看它。在[{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} 监督日志]中可以找到详细的信息。",
+       "rev-deleted-no-diff": "您不能查看该差异,因为其中一个版本已被<strong>删除</strong>。详情请见[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} 删除日志]。",
        "rev-suppressed-no-diff": "无法查看该差异,因为其中一个版本已被<strong>删除<strong>。",
        "rev-deleted-unhide-diff": "该差异对比的其中的一个版本已经被<strong>删除</strong>。在[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} 删除日志]中可以找到更多的信息。如果您想继续的话,您仍然可以[$1 查看此版本]。",
        "rev-suppressed-unhide-diff": "该页面的其中一次版本已经被<strong>监督隐藏</strong>。\n在[{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} 监督日志]中可以找到更多的资料。如果您想继续的话,您可以仍然[$1 去查看这版本]。",
        "logdelete-text": "已删除日志事件仍将在日志中显示,但涉及部分的内容将对公众不可见。",
        "revdelete-text-others": "其他管理员仍将可以访问隐藏内容并删除它,除非附加条件被设定。",
        "revdelete-confirm": "请确认该操作,明白其后果,并确保该操作符合[[{{MediaWiki:Policy-url}}|方针]]。",
-       "revdelete-suppress-text": "阻止应'''仅'''用于以下情况:\n* 潜在的诽谤信息\n* 不合适的个人信息\n*: ''家庭地址、电话号码和社保号码等。''",
+       "revdelete-suppress-text": "阻止应<strong>仅</strong>用于以下情况:\n* 潜在的诽谤信息\n* 不合适的个人信息\n*: <em>家庭地址、电话号码和社保号码等。</em>",
        "revdelete-legend": "设置可见性之限制",
        "revdelete-hide-text": "版本文字",
        "revdelete-hide-image": "隐藏文件内容",
        "revdelete-submit": "应用于选中的{{PLURAL:$1|版本}}",
        "revdelete-success": "版本可见性更新成功。",
        "revdelete-failure": "版本可见性无法更新:\n$1",
-       "logdelete-success": "'''事件的可见性已经成功设置。'''",
-       "logdelete-failure": "'''事件的可见性无法设置:'''\n$1",
+       "logdelete-success": "事件的可见性已经成功设置。",
+       "logdelete-failure": "事件的可见性无法设置:\n$1",
        "revdel-restore": "更改可见性",
        "pagehist": "页面历史",
        "deletedhist": "已删除历史",
        "revdelete-show-no-access": "正在显示于$1 $2之项目错误:这个项目已经标示为\"已限制\",您对它并无通行权。",
        "revdelete-modify-no-access": "正在更改于$1 $2之项目错误:这个项目已经标示为\"已限制\",您对它并无通行权。",
        "revdelete-modify-missing": "正在更改项目ID $1错误:它在资料库中遗失!",
-       "revdelete-no-change": "警告:于$1 $2之项目已经请求了可见性的设置。",
+       "revdelete-no-change": "<strong>警告:</strong>于$1 $2之项目已经请求了可见性的设置。",
        "revdelete-concurrent-change": "正在更改于$1 $2之项目错误:当我们尝试更改它的设置时,已经被另一些人更改过。请检查纪录。",
        "revdelete-only-restricted": "在隐藏$1 $2的项目时发生错误:您不能在选择了另一可见性选项后废止管理员查看该项目。",
        "revdelete-reason-dropdown": "*常用删除理由\n** 侵犯版权\n** 不适当的评论或个人信息\n** 不适当的用户名\n** 潜在毁谤性信息",
        "mergehistory-empty": "没有可以合并的版本。",
        "mergehistory-done": "$1的$3个{{PLURAL:$3|版本}}{{PLURAL:$3|已}}成功合并至[[:$2]]。",
        "mergehistory-fail": "不可以进行历史合并,请重新检查该页面以及时间参数。",
+       "mergehistory-fail-bad-timestamp": "时间戳无效。",
+       "mergehistory-fail-invalid-source": "来源页面无效。",
+       "mergehistory-fail-invalid-dest": "目标页面无效。",
+       "mergehistory-fail-no-change": "历史合并未合并任何修订版本。请重新检查页面和时间参数。",
+       "mergehistory-fail-permission": "没有足够权限合并历史。",
+       "mergehistory-fail-self-merge": "来源页面与目标页面相同。",
+       "mergehistory-fail-timestamps-overlap": "来源修订版本重复,或在目标修订版本之后出现。",
        "mergehistory-fail-toobig": "由于超出$1的限制而无法执行历史合并,$1个版本将被移动。",
        "mergehistory-no-source": "来源页面$1不存在。",
        "mergehistory-no-destination": "目的页面$1不存在。",
        "nextn-title": "后$1个结果",
        "shown-title": "每页显示$1项结果",
        "viewprevnext": "查看($1{{int:pipe-separator}}$2)($3)",
-       "searchmenu-exists": "'''本wiki上有名为“[[:$1]]”的页面。'''",
+       "searchmenu-exists": "<strong>本wiki上有名为“[[:$1]]”的页面。</strong>{{PLURAL:$2|0=|另请查看找到的其他搜索结果。}}",
        "searchmenu-new": "<strong>在本Wiki上新建名为“[[:$1]]”的页面!</strong>{{PLURAL:$2|0=|另请查看您的搜索找的结果。|另请查看搜索结果。}}",
        "searchprofile-articles": "内容页面",
        "searchprofile-images": "多媒体",
        "search-relatedarticle": "相关",
        "searchrelated": "相关",
        "searchall": "所有",
-       "showingresults": "下面显示从第'''$2'''条结果开始的'''$1'''条结果。",
+       "showingresults": "下面显示从第<strong>$2</strong>条结果开始的<strong>$1</strong>条结果。",
        "showingresultsinrange": "下面显示区间#<strong>$2</strong>至#<strong>$3</strong>的<strong>$1</strong>条结果。",
        "search-showingresults": "{{PLURAL:$4|<strong>$3</strong>条结果中的<strong>$1</strong>条|<strong>$3</strong>条结果中的<strong>$1~$2</strong>条}}",
        "search-nonefound": "找不到和查询相匹配的结果。",
        "saveusergroups": "保存{{GENDER:$1|用户}}组",
        "userrights-groupsmember": "用户组:",
        "userrights-groupsmember-auto": "自动用户组:",
-       "userrights-groups-help": "可以更改该用户的用户组:\n* 选中的选项框表示该用户属于该用户组。\n* 未选中的选项框表示该用户不属于该用户组。\n* 星号(*)表示一旦添加该用户组后不能删除,反之亦然。",
+       "userrights-groups-help": "可以更改该用户的用户组:\n* 选中的选项框表示该用户属于该用户组。\n* 未选中的选项框表示该用户不属于该用户组。\n* 星号(*)表示一旦添加该用户组后不能删除,反之亦然。",
        "userrights-reason": "原因:",
        "userrights-no-interwiki": "您并没有权限去编辑在其它wiki上的用户权限。",
        "userrights-nodatabase": "数据库$1不存在或并非为本地的。",
        "userrights-nologin": "您必须要以管理员帐户[[Special:UserLogin|登录]]之后才可以指定用户权限。",
-       "userrights-notallowed": "你没有权限添加或删除用户权限。",
-       "userrights-changeable-col": "可以更改的用户组",
-       "userrights-unchangeable-col": "不能更改的用户组",
+       "userrights-notallowed": "您没有权限添加或移除用户权限。",
+       "userrights-changeable-col": "可以更改的用户组",
+       "userrights-unchangeable-col": "不能更改的用户组",
        "userrights-conflict": "用户权限的更改存在冲突!请检查并确认您的更改。",
        "userrights-removed-self": "您已成功删除您自己的权利。因此,您不再能够访问此页。",
        "group": "用户组:",
        "group-suppress": "Flow监督员",
        "group-all": "(所有)",
        "group-user-member": "{{GENDER:$1|用户}}",
-       "group-autoconfirmed-member": "自动确认用户",
-       "group-bot-member": "机器人",
+       "group-autoconfirmed-member": "{{GENDER:$1|自动确认用户}}",
+       "group-bot-member": "{{GENDER:$1|机器人}}",
        "group-sysop-member": "{{GENDER:$1|管理员}}",
-       "group-bureaucrat-member": "行政员",
+       "group-bureaucrat-member": "{{GENDER:$1|行政员}}",
        "group-suppress-member": "{{GENDER:$1|Flow监督员}}",
        "grouppage-user": "{{ns:project}}:用户",
        "grouppage-autoconfirmed": "{{ns:project}}:自动确认用户",
        "upload_directory_missing": "上传目录($1)遗失,不能由网页服务器建立。",
        "upload_directory_read_only": "上传目录($1)不存在或无写权限。",
        "uploaderror": "上传出错",
-       "upload-recreate-warning": "'''警告:一个相同名字的文件曾经被删除或者移动至别处。'''\n\n这个页面的删除和移动日志在这里提供以便参考:",
+       "upload-recreate-warning": "<strong>警告:一个相同名字的文件曾经被删除或者移动至别处。</strong>\n\n这个页面的删除和移动日志在这里提供以便参考:",
        "uploadtext": "请使用下面的表格上传文件。要查看或搜索以往上传的文件,请前往[[Special:FileList|上传的文件的列表]],(重新)上传也将记录在[[Special:Log/upload|上传日志]]中,删除将记录在[[Special:Log/delete|删除日志]]中。\n\n要在页面中包含文件,请使用一种以下形式的链接:\n* <strong><code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:File.jpg]]</nowiki></code></strong>使用文件的完整版本\n* <strong><code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:File.png|200px|thumb|left|替代文字]]</nowiki></code></strong>使用位于页面左边的框内的200像素宽的图片,以“替代文字”作为说明\n* <strong><code><nowiki>[[</nowiki>{{ns:media}}<nowiki>:File.ogg]]</nowiki></code></strong>直接链接到文件而不显示文件",
        "upload-permitted": "允许的文件{{PLURAL:$2|类型}}:$1。",
        "upload-preferred": "建议的文件{{PLURAL:$2|类型}}:$1。",
        "filetype-mime-mismatch": "文件扩展名“.$1”与检测到的文件MIME类型($2)不匹配。",
        "filetype-badmime": "“$1”类型的文件已被禁止上传。",
        "filetype-bad-ie-mime": "无法上传该文件,因为Internet Explorer会将它检测为“$1”,这是一种禁止且带有潜在危险的文件类型。",
-       "filetype-unwanted-type": "'''“.$1”'''是一种不需要的文件类型。\n建议的{{PLURAL:$3|一种|多种}}文件类型有$2。",
-       "filetype-banned-type": "'''\".$1\"'''{{PLURAL:$4|不是一个允许的文件类型|不是一个允许的文件类型}}。\n允许 {{PLURAL:$3|文件类型是}} $2。",
+       "filetype-unwanted-type": "<strong>“.$1”</strong>是一种不需要的文件类型。\n建议的{{PLURAL:$3|一种|多种}}文件类型有$2。",
+       "filetype-banned-type": "<strong>“.$1”</strong>{{PLURAL:$4|不是允许的文件类型}}。\n允许的{{PLURAL:$3|文件类型是}}$2。",
        "filetype-missing": "该文件名称并没有扩展名(例如“.jpg”)。",
        "empty-file": "您所提交的文件为空文件。",
        "file-too-large": "您所提交的文件过大。",
        "filename-tooshort": "文件名过短。",
        "filetype-banned": "此类文件被禁止。",
        "verification-error": "文件未通过验证。",
-       "hookaborted": "尝试的修改被扩展程序中止。",
+       "hookaborted": "尝试的修改被扩展程序中止。",
        "illegal-filename": "文件名非法。",
        "overwrite": "不允许覆盖现有文件。",
        "unknown-error": "发生未知错误。",
        "largefileserver": "这个文件的大小比服务器配置允许的大小还要大。",
        "emptyfile": "您所上传的文件不存在。这可能是由于文件名键入错误。请检查您是否真的要上传此文件。",
        "windows-nonascii-filename": "本wiki不支持在文件名中使用特殊字符。",
-       "fileexists": "已存在相同名称的文件,如果您无法确定您是否要改变它,请检查<strong><strong>[[:$1]]</strong></strong>。 [[$1|thumb]]",
-       "filepageexists": "该文件的说明页面已经创建于<strong>[[:$1]]</strong>,但是目前没有名称为此的文件存在。你输入的摘要不会显示在说明页面上。要使你的摘要在那里显示,你需要手工编辑它。[[$1|thumb]]",
+       "fileexists": "已存在相同名称的文件,如果您无法确定您是否要改变它,请检查<strong>[[:$1]]</strong>。 [[$1|thumb]]",
+       "filepageexists": "该文件的说明页面已经创建于<strong>[[:$1]]</strong>,但是目前没有名称为此的文件存在。您输入的摘要不会显示在说明页面上。要使你的摘要在那里显示,您需要手工编辑它。[[$1|thumb]]",
        "fileexists-extension": "一个相似名称的文件已经存在: [[$2|thumb]]\n* 上传文件的文件名:<strong>[[:$1]]</strong>\n* 现有文件的文件名:<strong>[[:$2]]</strong>\n请选择一个不同的名字。",
-       "fileexists-thumbnail-yes": "此文件可能是另一幅图像的缩小版本''(缩略图)''。 [[$1|thumb]]\n请仔细检查该文件<strong>[[:$1]]</strong>。\n如果被检查文件与原始大小的图像是同一幅图像,您无需上传多余的缩略图。",
-       "file-thumbnail-no": "文件名以<strong>$1</strong>开始。它似乎是缩小的图像''(缩略图)''。如果你有完整分辨率的该图像,请上传它,否则请更改文件名。",
+       "fileexists-thumbnail-yes": "此文件可能是另一幅图像的缩小版本<em>(缩略图)</em>。 [[$1|thumb]]\n请仔细检查该文件<strong>[[:$1]]</strong>。\n如果被检查文件与原始大小的图像是同一幅图像,您无需上传多余的缩略图。",
+       "file-thumbnail-no": "文件名以<strong>$1</strong>开始。它似乎是缩小的图像<em>(缩略图)</em>。如果您有完整分辨率的该图像,请上传它,否则请更改文件名。",
        "fileexists-forbidden": "已存在相同名称的文件,且不能覆盖;请返回并用一个新的名称来上传此文件。[[File:$1|thumb|center|$1]]",
-       "fileexists-shared-forbidden": "共享文件库中存在该名称的文件。如果仍想上传你的文件,请返回使用其他名称。[[File:$1|thumb|center|$1]]",
+       "fileexists-shared-forbidden": "共享文件库中存在该名称的文件。如果仍想上传你的文件,请返回使用其他名称。[[File:$1|thumb|center|$1]]",
        "file-exists-duplicate": "本文件是以下{{PLURAL:$1|文件}}的副本:",
        "file-deleted-duplicate": "一个相同名称的文件 ([[:$1]]) 在先前删除过。您应该在重新上传之前检查一下该文件之删除纪录。",
        "file-deleted-duplicate-notitle": "之前有与此相同的文件被删除和取消标题。您应该询问查看过改文件数据的任何人以复查重新上传时的诸多问题。",
        "uploaded-script-svg": "在上传的SVG文件中找到可编写脚本的元素“$1”。",
        "uploaded-hostile-svg": "在上传的SVG文件中的样式元素中找到不安全CSS。",
        "uploaded-event-handler-on-svg": "在SVG文件中不允许设置event-handler属性<code>$1=\"$2\"</code>。",
-       "uploaded-href-unsafe-target-svg": "在上传的SVG文件中找到href至不安全目标<code>&lt;$1 $2=\"$3\"&gt;</code>。",
+       "uploaded-href-attribute-svg": "SVG文件中的href属性只允许链接至http://或https://目标,已找到<code>&lt;$1 $2=\"$3\"&gt;</code>。",
+       "uploaded-href-unsafe-target-svg": "在上传的SVG文件中找到了至不安全数据的href:URI目标<code>&lt;$1 $2=\"$3\"&gt;</code>。",
        "uploaded-animate-svg": "在上传的SVG文件找到“animate”标签,它可能会更改href,使用“from”属性<code>&lt;$1 $2=\"$3\"&gt;</code>。",
        "uploaded-setting-event-handler-svg": "设置event-handler属性时受阻,在上传的SVG文件中找到<code>&lt;$1 $2=\"$3\"&gt;</code>。",
        "uploaded-setting-href-svg": "使用“set”标签加入“href”属性至父元素时受阻。",
        "uploaded-setting-handler-svg": "通过远程/数据/脚本设置“handler”属性的SVG时受阻。在上传的SVG文件中找到<code>$1=\"$2\"</code>。",
        "uploaded-remote-url-svg": "通过远程URL设置任意样式属性的SVG时受阻。在上传的SVG文件中找到<code>$1=\"$2\"</code>。",
        "uploaded-image-filter-svg": "在上传的SVG文件中找到图片过滤器带URL:<code>&lt;$1 $2=\"$3\"&gt;</code>。",
-       "uploadscriptednamespace": "此SVG文件包含非法名字空间“$1”",
+       "uploadscriptednamespace": "此SVG文件包含非法名字空间“$1”",
        "uploadinvalidxml": "上传文件中的XML无法解析。",
        "uploadvirus": "该文件包含病毒!\n详情:$1",
        "uploadjava": "该文件是 ZIP 文件,其中包含 Java 的.class 文件。上传Java文件不被允许,因为它们可能绕过限制,从而引起安全问题。",
        "upload-dialog-button-done": "完成",
        "upload-dialog-button-save": "保存",
        "upload-dialog-button-upload": "上传",
-       "upload-form-label-select-file": "选择文件",
        "upload-form-label-infoform-title": "详情",
        "upload-form-label-infoform-name": "名称",
        "upload-form-label-infoform-name-tooltip": "用于文件的唯一描述性标题,它将用作文件名。您可以使用带空格的普通语言。不要包含文件扩展名。",
        "foreign-structured-upload-form-label-own-work-message-shared": "我证明我拥有此文件的版权,并不可撤销地同意采用[https://creativecommons.org/licenses/by-sa/4.0/ 知识共享 署名-相同方式共享 4.0]许可协议将此文件发布至维基共享资源,并且我同意[https://wikimediafoundation.org/wiki/Terms_of_Use 使用条款]。",
        "foreign-structured-upload-form-label-not-own-work-message-shared": "如果您并不拥有此文件的版权,或者您希望将其以其他许可协议发布,请考虑使用[https://commons.wikimedia.org/wiki/Special:UploadWizard 共享资源的上传向导]。",
        "foreign-structured-upload-form-label-not-own-work-local-shared": "如果网站允许依据他们的方针上传此文件的话,您也可以尝试使用[[Special:Upload|{{SITENAME}}上的上传页面]]。",
-       "foreign-structured-upload-form-2-label-intro": "感谢您贡献图片以用于{{SITENAME}}。您只应在其满足以下条件时贡献:",
-       "foreign-structured-upload-form-2-label-ownwork": "它必须完全<strong>由您创作</strong>,而不只是从Internet上获取",
-       "foreign-structured-upload-form-2-label-noderiv": "它不包含<strong>其他任何人</strong>的成果,或者来自他们的灵感",
-       "foreign-structured-upload-form-2-label-useful": "它应当有<strong>教育意义</strong>并对教育他人有用",
-       "foreign-structured-upload-form-2-label-ccbysa": "它必须<strong>允许</strong>依据[https://creativecommons.org/licenses/by-sa/4.0/ 知识共享 署名-相同方式共享4.0]许可协议在网络上再发布",
-       "foreign-structured-upload-form-2-label-alternative": "如果以上条件并不完全满足的话,您仍应当使用[https://commons.wikimedia.org/wiki/Special:UploadWizard 共享资源上传向导]上传此文件,只要它依据自由许可协议可用于该网站的话。",
-       "foreign-structured-upload-form-2-label-termsofuse": "通过上传文件,您证明您拥有此文件的著作权,并不可撤销地同意采用知识共享 署名-相同方式共享4.0许可协议将此文件发布至维基共享资源,并且您同意[https://wikimediafoundation.org/wiki/Terms_of_Use 使用条款]。",
-       "foreign-structured-upload-form-3-label-question-website": "您是否从一个网站下载,或通过图片搜索获得到的它?",
-       "foreign-structured-upload-form-3-label-question-ownwork": "您是自行创建的这幅图片么(例如拍摄照片,素描等)?",
-       "foreign-structured-upload-form-3-label-question-noderiv": "它是否包含别人的作品,或受别人作品启发,例如标志?",
-       "foreign-structured-upload-form-3-label-yes": "是",
-       "foreign-structured-upload-form-3-label-no": "否",
-       "foreign-structured-upload-form-3-label-alternative": "很遗憾,这种情况下,此工具不支持上传此文件。您仍应当使用[https://commons.wikimedia.org/wiki/Special:UploadWizard 共享资源上传向导]上传此文件,只要它依据自由许可协议可用于该网站的话。",
-       "foreign-structured-upload-form-4-label-good": "使用此工具,您可以上传您创建的教育性图片和您拍摄的照片,不能包含其他任何人的作品。",
-       "foreign-structured-upload-form-4-label-bad": "您不能上传来自搜索引擎或从其他网站上下载的图片。",
        "backend-fail-stream": "无法流传送文件$1。",
        "backend-fail-backup": "无法备份文件$1。",
        "backend-fail-notexists": "条目$1不存在。",
        "backend-fail-closetemp": "无法创建临时文件。",
        "backend-fail-read": "找不到文件“$1”。",
        "backend-fail-create": "无法写入文件 $1 。",
-       "backend-fail-maxsize": "无法写入文件 $1,因为它大于$2字节。",
-       "backend-fail-readonly": "“$1”存储后端目前在只读模式,因为:“$2”",
+       "backend-fail-maxsize": "无法写入文件“$1”,因为它大于$2字节。",
+       "backend-fail-readonly": "“$1”存储后端目前在只读模式,因为:“<em>$2</em>”",
        "backend-fail-synced": "文件\"$1\"在内部存储后端之中处于不一致状态",
        "backend-fail-connect": "无法连接到存储后端“$1。",
        "backend-fail-internal": "存储后端“$1”发生了一个未知错误。",
        "uploadstash": "上传隐藏",
        "uploadstash-summary": "这个页面提供已经上传(或者上传中)但未发布到wiki之文件存取。这些文件除了上传的用户之外不会被其他人可见。",
        "uploadstash-clear": "清除贮藏文件",
-       "uploadstash-nofiles": "没有被隐藏的文件。",
+       "uploadstash-nofiles": "没有被隐藏的文件。",
        "uploadstash-badtoken": "该操作执行失败,可能是因为你的编辑凭证已过期。请重试。",
        "uploadstash-errclear": "清除文件不成功。",
        "uploadstash-refresh": "更新文件列表",
        "upload-disallowed-here": "您不可以覆盖此文件。",
        "filerevert": "恢复$1",
        "filerevert-legend": "恢复文件",
-       "filerevert-intro": "你将要恢复文件'''[[Media:$1|$1]]'''至[$4 $2 $3的版本]。",
+       "filerevert-intro": "您将要恢复文件<strong>[[Media:$1|$1]]</strong>至[$4 $2 $3的版本]。",
        "filerevert-comment": "原因:",
        "filerevert-defaultcomment": "回退至$1 $2($3)的版本",
        "filerevert-submit": "恢复",
        "filerevert-badversion": "文件并无所请求时间戳下的早期本地版本。",
        "filedelete": "删除$1",
        "filedelete-legend": "删除文件",
-       "filedelete-intro": "你将要删除文件'''[[Media:$1|$1]]'''及其全部历史。",
-       "filedelete-intro-old": "你正在删除'''[[Media:$1|$1]]'''[$4 $2$3]的版本。",
+       "filedelete-intro": "您将要删除文件<strong>[[Media:$1|$1]]</strong>及其全部历史。",
+       "filedelete-intro-old": "你正在删除<strong>[[Media:$1|$1]]</strong>[$4 $2$3]的版本。",
        "filedelete-comment": "原因:",
        "filedelete-submit": "删除",
-       "filedelete-success": "'''$1'''已经删除。",
-       "filedelete-success-old": "'''[[Media:$1|$1]]'''于 $2 $3 的版本已经删除。",
-       "filedelete-nofile": "'''$1'''不存在。",
-       "filedelete-nofile-old": "在已指定属性的情况下,这里没有'''$1'''的保存版本。",
+       "filedelete-success": "<strong>$1</strong>已经删除。",
+       "filedelete-success-old": "<strong>[[Media:$1|$1]]</strong> 于 $2 $3 的版本已经删除。",
+       "filedelete-nofile": "<strong>$1</strong>不存在。",
+       "filedelete-nofile-old": "在已指定属性的情况下,这里没有<strong>$1</strong>的保存版本。",
        "filedelete-otherreason": "其他/附加原因:",
        "filedelete-reason-otherlist": "其他原因",
-       "filedelete-reason-dropdown": "\n*常用删除理由\n** 侵犯版权\n** 重复文件",
+       "filedelete-reason-dropdown": "*常用删除理由\n** 侵犯版权\n** 重复文件",
        "filedelete-edit-reasonlist": "编辑删除原因",
        "filedelete-maintenance": "维护期间文件删除和恢复暂时停用。",
        "filedelete-maintenance-title": "无法删除文件",
        "randompage-nopages": "在以下{{PLURAL:$2|名字空间}}中没有页面:$1。",
        "randomincategory": "分类中随机页面",
        "randomincategory-invalidcategory": "“$1”不是一个有效的分类名称。",
-       "randomincategory-nopages": "[[:Category:$1]]中没有页面。",
+       "randomincategory-nopages": "[[:Category:$1|$1]]分类中没有页面。",
        "randomincategory-category": "分类:",
-       "randomincategory-legend": "分中随机页面",
+       "randomincategory-legend": "分中随机页面",
        "randomincategory-submit": "提交",
        "randomredirect": "随机重定向",
        "randomredirect-nopages": "“$1”名字空间中没有重定向。",
        "querypage-disabled": "本特殊页面因性能问题而停用。",
        "apihelp": "API 帮助",
        "apihelp-no-such-module": "找不到模块“$1”。",
+       "apisandbox": "API 沙盒",
+       "apisandbox-jsonly": "需要JavaScript以使用API沙盒。",
+       "apisandbox-api-disabled": "API在该网站停用。",
+       "apisandbox-intro": "使用这个页面来试验<strong>MediaWiki Web 服务应用程序接口(API)</strong>。\n欲知API使用详情,请参阅[[mw:API:Main page|API文档]]。\n例如:[//www.mediawiki.org/wiki/API#A_simple_example 取得某个主页的内容],然后选择一个操作来看更多范例。\n\n请注意,虽然这是一个沙盒,但是你在这个页面上的改动可能会修改维基。",
+       "apisandbox-fullscreen": "展开面板",
+       "apisandbox-fullscreen-tooltip": "展开沙盒面板以填充浏览器窗口。",
+       "apisandbox-unfullscreen": "显示页面",
+       "apisandbox-unfullscreen-tooltip": "缩小沙盒面板,这样就可以使用MediaWiki导航链接。",
+       "apisandbox-submit": "提交请求",
+       "apisandbox-reset": "清除",
+       "apisandbox-retry": "重试",
+       "apisandbox-loading": "正在加载用于API模块“$1”的信息...",
+       "apisandbox-load-error": "加载用于API模块“$1”的信息时出错:$2",
+       "apisandbox-no-parameters": "此API模块没有参数。",
+       "apisandbox-helpurls": "帮助链接",
+       "apisandbox-examples": "示例",
+       "apisandbox-dynamic-parameters": "额外参数",
+       "apisandbox-dynamic-parameters-add-label": "添加参数:",
+       "apisandbox-dynamic-parameters-add-placeholder": "参数名称",
+       "apisandbox-dynamic-error-exists": "已存在名为“$1”的参数。",
+       "apisandbox-deprecated-parameters": "弃用参数",
+       "apisandbox-fetch-token": "自动填充令牌",
+       "apisandbox-submit-invalid-fields-title": "一些字段无效",
+       "apisandbox-submit-invalid-fields-message": "请改正标记的字段并重试。",
+       "apisandbox-results": "结果",
+       "apisandbox-sending-request": "正在发送API请求...",
+       "apisandbox-loading-results": "正在接收API请求...",
+       "apisandbox-results-error": "加载API查询响应时出错:$1。",
+       "apisandbox-request-url-label": "请求的URL:",
+       "apisandbox-request-time": "请求时间:{{PLURAL:$1|$1毫秒}}",
+       "apisandbox-results-fixtoken": "改正令牌并重新提交",
+       "apisandbox-results-fixtoken-fail": "检索“$1”令牌失败。",
+       "apisandbox-alert-page": "此页面上的字段无效。",
+       "apisandbox-alert-field": "此字段的值无效。",
        "booksources": "网络书源",
        "booksources-search-legend": "搜索图书来源",
        "booksources-isbn": "ISBN:",
        "trackingcategories-nodesc": "没有可用说明。",
        "trackingcategories-disabled": "分类被禁用",
        "mailnologin": "无电子邮件地址",
-       "mailnologintext": "你必须[[Special:UserLogin|登录]]并在你的[[Special:Preferences|系统设置]]中拥有有效的电子邮件地址才能向其他用户发送电子邮件。",
+       "mailnologintext": "您必须[[Special:UserLogin|登录]]并在您的[[Special:Preferences|系统设置]]中拥有有效的电子邮件地址才能向其他用户发送电子邮件。",
        "emailuser": "电邮联系",
        "emailuser-title-target": "电邮联系该{{GENDER:$1|用户}}",
        "emailuser-title-notarget": "电邮联系",
-       "emailpagetext": "你可以使用下面的表格发送电子邮件信息至该{{GENDER:$1|用户}}。你在[[Special:Preferences|系统设置]]中输入的电子邮件地址将显示为邮件的“发件人”地址,所以该用户将可以直接回复你。",
+       "emailpagetext": "您可以使用下面的表格发送电子邮件信息至该{{GENDER:$1|用户}}。您在[[Special:Preferences|系统设置]]中输入的电子邮件地址将显示为邮件的“发件人”地址,所以该用户将可以直接回复您。",
        "defemailsubject": "来自{{SITENAME}}用户“$1”的电子邮件",
        "usermaildisabled": "用户电子邮件停用",
        "usermaildisabledtext": "你不能发送电子邮件至本wiki的其他用户",
        "watchlist": "监视列表",
        "mywatchlist": "监视列表",
        "watchlistfor2": "$1的监视列表$2",
-       "nowatchlist": "的监视列表为空。",
+       "nowatchlist": "的监视列表为空。",
        "watchlistanontext": "请登录以查看或编辑您的监视列表。",
        "watchnologin": "未登录",
        "addwatch": "添加至监视列表",
        "moveuserpage-warning": "'''警告:'''你将移动一个用户页面。请注意,只有该页面会被移动,该用户''不''会被更名。",
        "movecategorypage-warning": "<strong>警告:</strong>您将移动分类页面。请注意只有此页面将会移动,旧有分类的任何页面将<em>不会</em>同步移动。",
        "movenologintext": "您必须是一名登记用户并且[[Special:UserLogin|登录]]\n后才可移动一个页面。",
-       "movenotallowed": "没有权限移动页面。",
-       "movenotallowedfile": "没有权限移动文件。",
-       "cant-move-user-page": "没有权限移动用户页面(子页面除外)。",
-       "cant-move-to-user-page": "没有权限移动页面至用户页面(用户子页面除外)。",
+       "movenotallowed": "没有权限移动页面。",
+       "movenotallowedfile": "没有权限移动文件。",
+       "cant-move-user-page": "没有权限移动用户页面(子页面除外)。",
+       "cant-move-to-user-page": "没有权限移动页面至用户页面(用户子页面除外)。",
        "cant-move-category-page": "您没有权限移动分类页面。",
        "cant-move-to-category-page": "您没有权限移动页面至分类页面。",
        "newtitle": "新标题:",
        "imageinvalidfilename": "目标文件名称无效",
        "fix-double-redirects": "更新所有指向原始标题的重定向",
        "move-leave-redirect": "保留重定向",
-       "protectedpagemovewarning": "'''警告:'''本页面已被保护,只有拥有管理员权限的用户可以移动。下面提供最后的日志条目以供参考:",
-       "semiprotectedpagemovewarning": "'''注意:'''本页面已被保护,只有注册用户可以移动。下面提供最后的日志条目以供参考:",
+       "protectedpagemovewarning": "<strong>警告:</strong>本页面已被保护,只有拥有管理员权限的用户可以移动。下面提供最后的日志条目以供参考:",
+       "semiprotectedpagemovewarning": "<strong>注意:</strong>本页面已被保护,只有注册用户可以移动。下面提供最后的日志条目以供参考:",
        "move-over-sharedrepo": "[[:$1]]已在一个共享的存储库存在。将文件移动到此标题将覆盖共享的文件。",
        "file-exists-sharedrepo": "同名文件已于共享资源存在。\n请选择另一个文件名。",
        "export": "导出页面",
        "import-nonewrevisions": "没有导入版本(所有都已存在或因错误跳过)。",
        "xml-error-string": "$1于行$2,列$3($4字节):$5",
        "import-upload": "上传XML数据",
-       "import-token-mismatch": "会话数据遗失。请重试。",
+       "import-token-mismatch": "会话数据丢失。\n\n您可能已经退出。<strong>请核实您是否仍在登录,并重试</strong>。\n如果仍然不能工作,尝试[[Special:UserLogout|退出]]并重新登录,并检查您的浏览器是否允许来自该网站的cookie。",
        "import-invalid-interwiki": "不能从指定的wiki导入。",
        "import-error-edit": "页面“$1”未导入,因为您不被允许编辑它。",
        "import-error-create": "页面“$1”未导入,因为您不被允许创建它。",
        "logentry-newusers-byemail": "$1创建用户$3,并且密码已通过电子邮件发送",
        "logentry-newusers-autocreate": "用户账户$1被自动{{GENDER:$2|创建}}",
        "logentry-protect-move_prot": "$1将保护设置从$4{{GENDER:$2|移动}}到了$3",
-       "logentry-protect-unprotect": "$1{{GENDER:$2|移除了}}来自$3的保护",
+       "logentry-protect-unprotect": "$1{{GENDER:$2|移除了}}$3的保护",
        "logentry-protect-protect": "$1{{GENDER:$2|保护了}}$3 $4",
        "logentry-protect-protect-cascade": "$1{{GENDER:$2|保护了}}$3 $4[级联]",
        "logentry-protect-modify": "$1{{GENDER:$2|更改了}}$3的保护等级$4",
        "expand_templates_generate_xml": "显示XML语法树",
        "expand_templates_generate_rawhtml": "显示原始HTML",
        "expand_templates_preview": "预览",
-       "expand_templates_preview_fail_html": "<em>因为{{SITENAME}}启用了Raw HTML并且丢失了会话数据,预览被隐藏以防止JavaScript攻击。</em>\n\n<strong>如果这是合法的预览尝试,请再次重试。</strong>\n如果仍然不能工作,尝试[[Special:UserLogout|退出]]并重新登录。",
+       "expand_templates_preview_fail_html": "<em>因为{{SITENAME}}启用了Raw HTML并且丢失了会话数据,预览被隐藏以防止JavaScript攻击。</em>\n\n<strong>如果这是合法的预览尝试,请再次重试。</strong>\n如果仍然不能工作,尝试[[Special:UserLogout|退出]]并重新登录,并检查您的浏览器是否允许来自该网站的cookie。",
        "expand_templates_preview_fail_html_anon": "<em>因为{{SITENAME}}启用了Raw HTML并且丢失了会话数据,预览被隐藏以防止JavaScript攻击。</em>\n\n<strong>如果这是合法的预览尝试,请尝试[[Special:UserLogin|登录]]并重试。</strong>",
        "expand_templates_input_missing": "您需要提供至少一些输入文本。",
        "pagelanguage": "更改页面语言",
index c7ba1f0..077892e 100644 (file)
@@ -67,7 +67,8 @@
                        "Matma Rex",
                        "范",
                        "Jasonzhuocn",
-                       "Bowleerin"
+                       "Bowleerin",
+                       "飞舞回堂前"
                ]
        },
        "tog-underline": "底線標示連結:",
        "badaccess": "權限錯誤",
        "badaccess-group0": "系統不允許您執行這項操作。",
        "badaccess-groups": "您請求的操作只有{{PLURAL:$2|這個|這些}}群組的使用者能使用:$1",
-       "versionrequired": "需使用 MediaWiki $1 版",
+       "versionrequired": "需要 $1 版本的 MediaWiki",
        "versionrequiredtext": "需使用 $1 版本的 MediaWiki 才能使用此頁面。\n請參考 [[Special:Version|版本]]。",
        "ok": "確定",
        "retrievedfrom": "取自 \"$1\"",
        "missingarticle-rev": "(修訂#:$1)",
        "missingarticle-diff": "(差異:$1, $2)",
        "readonly_lag": "資料庫已自動鎖定,正在等候次要資料庫同步資料到主要資料庫",
+       "nonwrite-api-promise-error": "非寫入 API 動作 'Promise-Non-Write-API-Action' HTTP 標頭已送出但請求被送至 API 寫入模組。",
        "internalerror": "內部錯誤",
        "internalerror_info": "內部錯誤:$1",
        "internalerror-fatal-exception": "嚴重例外類型 \"$1\"",
        "botpasswords-label-delete": "刪除",
        "botpasswords-label-resetpassword": "重設密碼",
        "botpasswords-label-grants": "適用的權限:",
+       "botpasswords-help-grants": "每個授權會給予擁有該授權的使用者帳號列於該授權清單的使用者權限。 請參考 [[Special:ListGrants|授權表]] 取得更多資訊。",
        "botpasswords-label-restrictions": "使用限制:",
        "botpasswords-label-grants-column": "已授權",
        "botpasswords-bad-appid": "機器人名稱 \"$1\" 無效。",
        "botpasswords-updated-body": "機器人密碼 \"$1\" 已修改成功。",
        "botpasswords-deleted-title": "已刪除機器人密碼",
        "botpasswords-deleted-body": "機器人密碼 \"$1\" 已刪除。",
+       "botpasswords-newpassword": "用來登入 <strong>$1</strong> 的新密碼為 <strong>$2</strong>。 <em>請記錄此密碼以供未來參考使用。</em>",
        "botpasswords-no-provider": "BotPasswordsSessionProvider 無法使用。",
        "botpasswords-restriction-failed": "機器人密碼限制已拒絕此次登入。",
        "botpasswords-invalid-name": "指定的使用者名稱未包含機器人密碼分隔字元 (\"$1\")。",
        "undo-summary": "取消由 [[Special:Contributions/$2|$2]] ([[User talk:$2|對話]]) 所作出的修訂 $1",
        "undo-summary-username-hidden": "還原隱藏使用者的修訂 $1",
        "cantcreateaccounttitle": "無法建立帳號",
-       "cantcreateaccount-text": "自這個 IP 位址(<strong>$1</strong>)建立帳號已經被 [[User:$3|$3]] 封鎖。\n\n$3 封鎖的原因是 <em>$2</em>",
+       "cantcreateaccount-text": "自這個 IP 位址 (<strong>$1</strong>) 建立帳號已經被 [[User:$3|$3]] 封鎖。\n\n$3 封鎖的原因是 <em>$2</em>",
        "cantcreateaccount-range-text": "來自 IP 位址範圍 '''$1''',包含您的 IP 位址 ('''$4''') 所建立的帳號已經被 [[User:$3|$3]] 封鎖。\n\n$3 封鎖的原因是 ''$2''",
        "viewpagelogs": "檢視此頁面的日誌",
        "nohistory": "此頁沒有任何的修訂記錄。",
        "rev-suppressed-unhide-diff": "檢視差異的其中一個修訂已被 <strong>禁止顯示</strong>。\n可至 [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} 禁止顯示日誌] 取得詳細資訊。\n若您要繼續,您仍可以 [$1 檢視此差異]。",
        "rev-deleted-diff-view": "檢視差異的其中一個修訂已被 <strong>刪除</strong>。\n您可繼續檢視差異,可至 [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} 刪除日誌] 取得詳細資訊。",
        "rev-suppressed-diff-view": "檢視差異的其中一個修訂已被 <strong>禁止顯示</strong>。\n您可繼續檢視差異,可至 [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} 禁止顯示日誌] 取得詳細資訊。",
-       "rev-delundel": "變更可見",
+       "rev-delundel": "變更可見",
        "rev-showdeleted": "顯示",
        "revisiondelete": "刪除/取消刪除修訂",
        "revdelete-nooldid-title": "無效的目標修訂",
        "uploaded-script-svg": "於已上傳的 SVG 檔案中找到可程式的腳本標籤 \"$1\"。",
        "uploaded-hostile-svg": "於已上傳的 SVG 檔案的樣式標籤中找到不安全的 CSS。",
        "uploaded-event-handler-on-svg": "不允許在 SVG 檔案設定 event-handler 屬性 <code>$1=\"$2\"</code>。",
-       "uploaded-href-unsafe-target-svg": "於已上傳的 SVG 檔案中找到 href 連結至不安全的目標 <code>&lt;$1 $2=\"$3\"&gt;</code>。",
+       "uploaded-href-attribute-svg": "發現 SVG 檔案中的 href 屬性為 <code>&lt;$1 $2=\"$3\"&gt;</code>,僅允許連結至 http:// 或 https:// 的目標。",
+       "uploaded-href-unsafe-target-svg": "於已上傳的 SVG 檔案中找到 href 連結至不安全的資料:URI 目標為 <code>&lt;$1 $2=\"$3\"&gt;</code>。",
        "uploaded-animate-svg": "於已上傳的 SVG 檔案中找到 \"animate\" 標籤可能會使用 \"from\" 屬性 <code>&lt;$1 $2=\"$3\"&gt;</code> 更改 href。",
        "uploaded-setting-event-handler-svg": "於已上傳的 SVG 檔案中找到 <code>&lt;$1 $2=\"$3\"&gt;</code>,已禁止設定 event-handler 屬性。",
        "uploaded-setting-href-svg": "已禁止使用 \"set\" 標籤來加入 \"href\" 屬性至父元素。",
        "upload-too-many-redirects": "該 URL 重新導向至太多其他位址",
        "upload-http-error": "發生 HTTP 錯誤:$1",
        "upload-copy-upload-invalid-domain": "此網域不允許複製上傳的檔案。",
+       "upload-foreign-cant-upload": "此 wiki 未設定可上傳來自遠端檔案庫的請求的檔案。",
        "upload-dialog-title": "上傳檔案",
        "upload-dialog-button-cancel": "取消",
        "upload-dialog-button-done": "完成",
        "upload-dialog-button-save": "儲存",
        "upload-dialog-button-upload": "上傳",
-       "upload-form-label-select-file": "選擇檔案",
        "upload-form-label-infoform-title": "詳細資料",
        "upload-form-label-infoform-name": "名稱",
+       "upload-form-label-infoform-name-tooltip": "具獨特描述性的檔案標題,將會用來做為檔名。 您可以使用您的語系及空白做為檔名,請勿包含檔案副檔名。",
        "upload-form-label-infoform-description": "描述",
+       "upload-form-label-infoform-description-tooltip": "簡短描述該作品任何值得說明的事項。\n以照片為例,可提及照片中的事物、情境及地點。",
        "upload-form-label-usage-title": "用法",
        "upload-form-label-usage-filename": "檔案名稱",
        "foreign-structured-upload-form-label-own-work": "這是我的作品",
        "foreign-structured-upload-form-label-own-work-message-shared": "我保証我擁有此檔案的版權,並且不反悔同意使用 [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0] 授權條款發佈此檔案到維基媒體共享資源,並且我同意 [https://wikimediafoundation.org/wiki/Terms_of_Use 使用條款]。",
        "foreign-structured-upload-form-label-not-own-work-message-shared": "若您並未擁有此檔案的版權,或者您希望使用其他的授權條款發佈此檔案,請考慮使用[https://commons.wikimedia.org/wiki/Special:UploadWizard 通用上傳精靈]。",
        "foreign-structured-upload-form-label-not-own-work-local-shared": "若該站的授權政策允許上傳此檔案,您可能會希望直接嘗試使用 [[Special:Upload|{{SITENAME}} 的上傳頁面]]。",
-       "foreign-structured-upload-form-2-label-intro": "感謝您貢獻影像給 {{SITENAME}},您應確認您的影像符合以下條件下繼續:",
-       "foreign-structured-upload-form-2-label-ownwork": "影像必須完全由<strong>您所創作</strong>,並非取自網際網路",
-       "foreign-structured-upload-form-2-label-noderiv": "影像並未包含<strong>他人的成果</strong>,或是來自他人的靈感",
-       "foreign-structured-upload-form-2-label-useful": "影像應具有<strong>教育意義</strong>並對教學他人有幫助",
-       "foreign-structured-upload-form-2-label-ccbysa": "影像必須採用 [https://creativecommons.org/licenses/by-sa/4.0/ 創用CC 姓名標示─相同方式分享 4.0] 授權條款,<strong>可以永久發布r</strong>於網際網路上",
-       "foreign-structured-upload-form-3-label-question-website": "您是否自其他網站下載此影像,或取自影像搜尋的結果?",
-       "foreign-structured-upload-form-3-label-yes": "是",
-       "foreign-structured-upload-form-3-label-no": "否",
        "backend-fail-stream": "無法傳輸檔案 \"$1\"。",
        "backend-fail-backup": "無法備份檔案 \"$1\"。",
        "backend-fail-notexists": "檔案 $1 不存在。",
        "querypage-disabled": "此特殊頁面因考量效能問題已被停用。",
        "apihelp": "API 說明",
        "apihelp-no-such-module": "查無模組 \"$1\"。",
+       "apisandbox": "API 沙盒",
+       "apisandbox-api-disabled": "此網站已關閉 API 使用。",
+       "apisandbox-intro": "使用此頁面可測試 '''MediaWiki Web Service API'''。\n請參考 [//www.mediawiki.org/wiki/API:Main_page API 說明文件] 以取得詳細資訊。例:[//www.mediawiki.org/wiki/API#A_simple_example 取得主頁的內容]。 請選擇動作以取得更多範例。\n\n請注意,雖然此為沙盒,您在此頁所執行的動作仍有可能會修改到 Wiki。",
+       "apisandbox-fullscreen": "展開面板",
+       "apisandbox-fullscreen-tooltip": "展開沙盒面板來填滿瀏覽器視窗。",
+       "apisandbox-unfullscreen": "顯示頁面",
+       "apisandbox-unfullscreen-tooltip": "減少沙盒面板大小,以讓 MediaWiki 導覽連結可使用。",
+       "apisandbox-submit": "發出請求",
+       "apisandbox-reset": "清除",
+       "apisandbox-retry": "重試",
+       "apisandbox-loading": "讀取 API 模組 \"$1\" 的資訊...",
+       "apisandbox-load-error": "在讀取 API 模組 \"$1\" 的資訊時發生錯誤:$2",
+       "apisandbox-no-parameters": "此 API 模組沒有參數。",
+       "apisandbox-helpurls": "說明連結",
+       "apisandbox-examples": "範例",
+       "apisandbox-dynamic-parameters": "其他參數",
+       "apisandbox-dynamic-parameters-add-label": "加入參數:",
+       "apisandbox-dynamic-parameters-add-placeholder": "參數名稱",
+       "apisandbox-dynamic-error-exists": "命名的參數 \"$1\" 已經存在。",
+       "apisandbox-deprecated-parameters": "停用的參數",
+       "apisandbox-fetch-token": "自動填寫密鑰",
+       "apisandbox-submit-invalid-fields-title": "部份欄位無效",
+       "apisandbox-submit-invalid-fields-message": "請更正已標註的欄位,然後再試一次。",
+       "apisandbox-results": "結果",
+       "apisandbox-sending-request": "傳送 API 請求中...",
+       "apisandbox-loading-results": "接收 API 結果中...",
+       "apisandbox-request-url-label": "請求 URL:",
+       "apisandbox-request-time": "請求時間:{{PLURAL:$1|$1 ms}}",
+       "apisandbox-results-fixtoken": "更正密鑰並重新送出",
        "booksources": "圖書資源",
        "booksources-search-legend": "尋找圖書資源",
        "booksources-isbn": "國際標準書號:",
        "lockedbyandtime": "(由 {{GENDER:$1|$1}} 於 $2 的 $3)",
        "move-page": "移動 $1",
        "move-page-legend": "移動頁面",
-       "movepagetext": "以下表單可以用來重新命名一個頁面,並將該頁面的所有歷史記錄一併移至擁有新名稱的頁面。\n舊標題的頁面將會變成重新導向頁面,指向使用新標題的頁面。\n您可以選擇自動更新所有指向舊頁面的重新導向,讓它們改為指向新頁面。\n若您選擇不自動更新,請檢查有沒有[[Special:DoubleRedirects|雙重重新導向]]或[[Special:BrokenRedirects|損壞的重新導向]]需要修正。\n您有責任讓連結繼續指向正確的地方。\n\n請注意,若新的頁面名稱已經被使用,則此頁面將<strong>不會</strong>移動至該處,除非新名稱下是個重新導向頁面而且沒有任何編輯歷史。\n即是說,您可以將錯誤移動至其他名稱的頁面還原到原有名稱,但不能覆蓋任何現有的頁面。\n\n<strong>注音:</strong>\n這個動作對受歡迎的頁面來說可能是重大而唐突的變更;\n在行動前請先確認您了解移動可能帶來的後果。",
+       "movepagetext": "以下表單可以用來重新命名一個頁面,並將該頁面的所有歷史記錄一併移至擁有新名稱的頁面。舊標題的頁面將會變成重新導向頁面,指向使用新標題的頁面。您可以選擇自動更新所有指向舊頁面的重新導向,讓它們改為指向新頁面。若您選擇不自動更新,請檢查有沒有[[Special:DoubleRedirects|雙重重新導向]]或[[Special:BrokenRedirects|損壞的重新導向]]需要修正。您有責任讓連結繼續指向正確的地方。\n\n請注意,若新的頁面名稱已經被使用,則此頁面將<strong>不會</strong>移動至該處,除非新名稱下是個重新導向頁面而且沒有任何編輯歷史。即是說,您可以將錯誤移動至其他名稱的頁面還原到原有名稱,但不能覆蓋任何現有的頁面。\n\n<strong>注意:</strong>這個動作對受歡迎的頁面來說可能是重大而唐突的變更;在行動前請先確認您了解移動可能帶來的後果。",
        "movepagetext-noredirectfixer": "以下表單可以用來重新命名一個頁面,並將該頁面的所有歷史記錄一併移至擁有新名稱的頁面。\n舊標題的頁面將會變成重新導向頁面,指向使用新標題的頁面。\n請檢查有沒有[[Special:DoubleRedirects|雙重重新導向]]或[[Special:BrokenRedirects|損壞的重新導向]]需要修正。\n您有責任讓連結繼續指向正確的地方。\n\n請注意,若新的頁面名稱已經被使用,則此頁面將<strong>不會</strong>移動至該處,除非新名稱下是個重新導向頁面而且沒有任何編輯歷史。\n即是說,您可以將錯誤移動至其他名稱的頁面還原到原有名稱,但不能覆蓋任何現有的頁面。\n\n<strong>注意:</strong>\n這個動作對受歡迎的頁面來說可能是重大而唐突的變更;\n在行動前請先確認您了解移動可能帶來的後果。",
        "movepagetalktext": "若勾選此方塊,相關的對話頁面會自動與此頁面一起移動至新的位置,除非新的名稱已有一個存在的對話頁面。\n在此情況下,若有必要您必須手動移動或合併已存在的頁面。",
        "moveuserpage-warning": "<strong>警告:</strong>您正要移動使用者頁面,請注意只有使用者頁面會變更名稱,並<em>不會</em>重新命名使用者。",
        "movenosubpage": "此頁面沒有任何子頁面。",
        "movereason": "原因",
        "revertmove": "還原",
-       "delete_and_move_text": "== 需要刪除 ==\n目標頁面 \"[[:$1]]\" 已存在。\n您是否要刪除該頁面以完成移動?",
+       "delete_and_move_text": "目標頁面 \"[[:$1]]\" 已存在。\n您是否要刪除該頁面以完成移動?",
        "delete_and_move_confirm": "是的,刪除該頁面",
        "delete_and_move_reason": "已刪除讓來自 [[$1]] 頁面可移動",
        "selfmove": "原始標題與目標標題相同,無法移動至自身頁面。",
        "move-leave-redirect": "留下重新導向頁面",
        "protectedpagemovewarning": "<strong>警告:</strong>本頁已經被保護,只有擁有管理員權限的使用者才可移動。\n以下提供最近的日誌以便參考:",
        "semiprotectedpagemovewarning": "<strong>注意:</strong>本頁已經被保護,只有已註冊的使用者才可移動。\n以下提供最近的日誌以便參考:",
-       "move-over-sharedrepo": "== 檔案已存在 ==\n[[:$1]] 已存在於共用檔案庫,將檔案移動到此標題會覆蓋該共用檔案。",
+       "move-over-sharedrepo": "[[:$1]] 已存在於共用檔案庫,將檔案移動到此標題會覆蓋該共用檔案。",
        "file-exists-sharedrepo": "選擇的檔案名稱於共用檔案庫已有其他檔案使用。\n請改選擇其他名稱。",
        "export": "匯出頁面",
        "exporttext": "您可以匯出指定頁面或多頁的文字與編輯歷史,使用 XML 格式包裝。\n這些檔案可以匯入至其他使用 MediaWiki 的 Wiki,透過 [[Special:Import|匯入頁面]]。\n\n要匯出頁面,請在下方文字方塊中輸入頁面標題,一個標題使用一行,並選擇是否要匯出目前的修訂含所有的歷史修訂記錄,或者只匯出目前的修訂與最後編輯的資訊。\n\n在文字方塊中您也可使用連結,如:[[{{#Special:Export}}/{{MediaWiki:Mainpage}}]] 代表匯出頁面 \"[[{{MediaWiki:Mainpage}}]]\"。",
        "version-libraries-license": "授權條款",
        "version-libraries-description": "描述",
        "version-libraries-authors": "作者",
-       "redirect": "重定向(按文件、用戶、頁面、修訂版本ID或日誌ID)",
+       "redirect": "依檔案、使用者、頁面、修訂或日誌 ID 來重新導向",
        "redirect-legend": "重新導向至檔案或頁面",
-       "redirect-summary": "此特殊頁面可用來重新導向至檔案 (指定檔案名稱)、頁面 (指定修訂 ID 或頁面 ID) 或使用者頁面 (指定使用者 ID)。用法:[[{{#Special:Redirect}}/file/Example.jpg]]、[[{{#Special:Redirect}}/page/64308]]、[[{{#Special:Redirect}}/revision/328429]] 或 [[{{#Special:Redirect}}/user/101]]。",
+       "redirect-summary": "此特殊頁面可用來重新導向至檔案 (指定檔案名稱)、頁面 (指定修訂 ID 或頁面 ID)、使用者頁面 (指定使用者 ID)、或者日誌項目 (指定日誌 ID)。用法:[[{{#Special:Redirect}}/file/Example.jpg]]、[[{{#Special:Redirect}}/page/64308]]、[[{{#Special:Redirect}}/revision/328429]]、[[{{#Special:Redirect}}/user/101]] 或 [[{{#Special:Redirect}}/logid/186]]。",
        "redirect-submit": "執行",
        "redirect-lookup": "查詢:",
        "redirect-value": "值:",
        "redirect-page": "頁面 ID",
        "redirect-revision": "頁面修訂 ID",
        "redirect-file": "檔案名稱",
+       "redirect-logid": "日誌 ID",
        "redirect-not-exists": "查無值",
        "fileduplicatesearch": "搜尋重複檔案",
        "fileduplicatesearch-summary": "依據雜湊值 (Hash) 來搜尋重複的檔案。",
        "expand_templates_preview": "預覽",
        "expand_templates_preview_fail_html": "<em>因連線階段的資料遺失且 {{SITENAME}} 已開啟顯示原始 HTML 功能,為預防 JavaScript 攻擊已隱藏預覽內容。</em>\n\n<strong>若您目前的預覽動作並無非法,請再試一次。</strong>\n若仍然無效,請嘗試[[Special:UserLogout|登出]]並再登入一次。",
        "expand_templates_preview_fail_html_anon": "<em>因您尚未登入且 {{SITENAME}} 已開啟顯示原始 HTML 功能,為預防 JavaScript 攻擊已隱藏預覽內容。</em>\n\n<strong>若您目前的預覽動作並無非法,請[[Special:UserLogin|登入]]後再試一次。</strong>",
-       "pagelanguage": "頁面語言選擇器",
+       "pagelanguage": "變更頁面語言",
        "pagelang-name": "頁面",
        "pagelang-language": "語言",
        "pagelang-use-default": "使用預設語言",
        "pagelang-submit": "送出",
        "right-pagelang": "變更頁面語言",
        "action-pagelang": "變更頁面語言",
-       "log-name-pagelang": "è®\8aæ\9b´èª\9eè¨\80日誌",
+       "log-name-pagelang": "èª\9eè¨\80è®\8aæ\9b´日誌",
        "log-description-pagelang": "此頁為頁面語言的變更日誌。",
-       "logentry-pagelang-pagelang": "$1 {{GENDER:$2|已更改}}頁面 $3 的語言從 $4 到 $5",
+       "logentry-pagelang-pagelang": "$1 {{GENDER:$2|已更改}}頁面 $3 的語言從 $4 到 $5",
        "default-skin-not-found": "哎呀!您於 <code dir=\"ltr\">$wgDefaultSkin</code> 設定的 Wiki 預設外觀 <code>$1</code> 無法使用。\n\n您的安裝程序應包含以下{{PLURAL:$4|外觀}}。請參考 [https://www.mediawiki.org/wiki/Manual:Skin_configuration 操作手冊:外觀設定] 以取得如何{{PLURAL:$4|開啟外觀並設為預設值}}的資訊。\n\n$2\n\n; 若您才剛安裝完 MediaWiki:\n: 您大概是使用 git 或直接透過原始碼使用其他方法安裝,這種情況是正常的。請嘗試安裝 [https://www.mediawiki.org/wiki/Category:All_skins mediawiki.org 的外觀目錄] 中的部份外觀使用以下方式:\n:* 下載 [https://www.mediawiki.org/wiki/Special:MyLanguage/Download tarball 安裝程式],該程式包含數個外觀與擴充套件。您可以複製並貼上至 <code>skins/</code> 目錄。\n:* 自 [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org] 下載個別外觀 tarball。\n:* [https://www.mediawiki.org/wiki/Download_from_Git#Using_Git_to_download_MediaWiki_skins 使用 Git 下載外觀]。\n: 若您是 MediaWiki 的開發人員,這麼做應該不會影響到您的 git 儲存庫。\n\n; 若您才剛升級 MediaWiki:\n: MediaWiki 1.24 與較新的版本不再自動開啟已安裝的外觀 (請參考 [https://www.mediawiki.org/wiki/Manual:Skin_autodiscovery 操作手冊:外觀自動搜尋])。您可以將下列{{PLURAL:$5|行}}貼上至 <code>LocalSettings.php</code> 來開啟{{PLURAL:$5|所有}}目前已經安裝的{{PLURAL:$5|外觀}}:\n\n<pre dir=\"ltr\">$3</pre>\n\n; 若您才剛修改 <code>LocalSettings.php</code>:\n: 請再次確認您輸入的外觀名稱是否有誤。",
        "default-skin-not-found-no-skins": "哎呀!您於 <code>$wgDefaultSkin</code> 設定的 Wiki 預設外觀 <code>$1</code> 無法使用。\n\n您未安裝任何的外觀。\n\n; 若您才剛安裝完或升級完 MediaWiki:\n: 您大概是使用 git 或直接透過原始碼使用其他方法安裝,這種情況是正常的。 MediaWiki 1.24 或較新的版本在主要儲存庫中不再包含任何的外觀。 請嘗試安裝 [https://www.mediawiki.org/wiki/Category:All_skins mediawiki.org 的外觀目錄] 中的部份外觀使用以下方式:\n:* 下載 [https://www.mediawiki.org/wiki/Special:MyLanguage/Download tarball 安裝程式],該程式包含數個外觀與擴充套件。 您可以複製並貼上至 <code>skins/</code> 目錄。\n:* 自 [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org] 下載個別外觀 tarball。\n:* [https://www.mediawiki.org/wiki/Download_from_Git#Using_Git_to_download_MediaWiki_skins 使用 Git 下載外觀]。\n: 若您是 MediaWiki 的開發人員,這麼做應該不會影響到您的 git 儲存庫。 請參考 [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Skin_configuration 操作手冊:外觀設定] 以取得如何開啟外觀並設為預設值的資訊。",
        "default-skin-not-found-row-enabled": "* <code>$1</code> / $2 (已開啟)",
index 2a1f85b..bd2ac04 100644 (file)
@@ -390,6 +390,7 @@ $specialPageAliases = array(
        'AllMyUploads'              => array( 'AllMyUploads', 'AllMyFiles' ),
        'Allpages'                  => array( 'AllPages' ),
        'ApiHelp'                   => array( 'ApiHelp' ),
+       'ApiSandbox'                => array( 'ApiSandbox' ),
        'Ancientpages'              => array( 'AncientPages' ),
        'Badtitle'                  => array( 'Badtitle' ),
        'Blankpage'                 => array( 'BlankPage' ),
index 4adf154..fb26675 100644 (file)
@@ -1,5 +1,5 @@
 <?php
-/** Goan Konkani (à¤\97à¥\8bवा à¤\95à¥\8bà¤\82à¤\95णà¥\80 / Gova Konknni)
+/** Goan Konkani (à¤\97à¥\8bà¤\82यà¤\9aà¥\80 à¤\95à¥\8bà¤\82à¤\95णà¥\80 / Gõychi Konknni)
  *
  * To improve a translation please visit https://translatewiki.net
  *
index b5cc343..190bc4d 100644 (file)
@@ -1,5 +1,5 @@
 <?php
-/** Goan Konkani - Devanagari script (à¤\97à¥\8bवा कोंकणी)
+/** Goan Konkani - Devanagari script (à¤\97à¥\8bà¤\82यà¤\9aà¥\80 कोंकणी)
  *
  * To improve a translation please visit https://translatewiki.net
  *
diff --git a/languages/messages/MessagesLki.php b/languages/messages/MessagesLki.php
new file mode 100644 (file)
index 0000000..10d06e5
--- /dev/null
@@ -0,0 +1,13 @@
+<?php
+/** Laki
+ *
+ * To improve a translation please visit https://translatewiki.net
+ *
+ * @ingroup Language
+ * @file
+ *
+ */
+
+$fallback = 'fa';
+
+$rtl = true;
index 9af9604..1306ae6 100644 (file)
@@ -138,7 +138,7 @@ class BackupDumper extends Maintenance {
                        require_once $file;
                }
                $register = array( $class, 'register' );
-               call_user_func_array( $register, array( &$this ) );
+               call_user_func_array( $register, array( $this ) );
        }
 
        function execute() {
index 574d5bd..1277e83 100644 (file)
@@ -90,7 +90,7 @@ class TitleCleanup extends TableCleanup {
        protected function moveIllegalPage( $row ) {
                $legal = 'A-Za-z0-9_/\\\\-';
                $legalized = preg_replace_callback( "!([^$legal])!",
-                       array( &$this, 'hexChar' ),
+                       array( $this, 'hexChar' ),
                        $row->page_title );
                if ( $legalized == '.' ) {
                        $legalized = '(dot)';
index eef535a..0f39513 100644 (file)
@@ -76,7 +76,7 @@ abstract class DumpIterator extends Maintenance {
                $importer = new WikiImporter( $source, $this->getConfig() );
 
                $importer->setRevisionCallback(
-                       array( &$this, 'handleRevision' ) );
+                       array( $this, 'handleRevision' ) );
 
                $this->from = $this->getOption( 'from', null );
                $this->count = 0;
index 8169ef5..12ccd0e 100644 (file)
@@ -426,10 +426,10 @@ TEXT
 
                xml_set_element_handler(
                        $parser,
-                       array( &$this, 'startElement' ),
-                       array( &$this, 'endElement' )
+                       array( $this, 'startElement' ),
+                       array( $this, 'endElement' )
                );
-               xml_set_character_data_handler( $parser, array( &$this, 'characterData' ) );
+               xml_set_character_data_handler( $parser, array( $this, 'characterData' ) );
 
                $offset = 0; // for context extraction on error reporting
                do {
index 1cf818e..9879b10 100644 (file)
@@ -82,12 +82,14 @@ class FindHooks extends Maintenance {
                        $IP . '/includes/deferred/',
                        $IP . '/includes/diff/',
                        $IP . '/includes/exception/',
+                       $IP . '/includes/export/',
                        $IP . '/includes/externalstore/',
                        $IP . '/includes/filebackend/',
                        $IP . '/includes/filerepo/',
                        $IP . '/includes/filerepo/file/',
                        $IP . '/includes/gallery/',
                        $IP . '/includes/htmlform/',
+                       $IP . '/includes/import/',
                        $IP . '/includes/installer/',
                        $IP . '/includes/interwiki/',
                        $IP . '/includes/jobqueue/',
@@ -102,11 +104,13 @@ class FindHooks extends Maintenance {
                        $IP . '/includes/resourceloader/',
                        $IP . '/includes/revisiondelete/',
                        $IP . '/includes/search/',
+                       $IP . '/includes/session/',
                        $IP . '/includes/site/',
                        $IP . '/includes/skins/',
                        $IP . '/includes/specialpage/',
                        $IP . '/includes/specials/',
                        $IP . '/includes/upload/',
+                       $IP . '/includes/user/',
                        $IP . '/includes/utils/',
                        $IP . '/languages/',
                        $IP . '/maintenance/',
index 7c452a6..3bf8b27 100644 (file)
@@ -297,13 +297,13 @@ TEXT
                                return false;
                        }
                }
-               $importer->setPageCallback( array( &$this, 'reportPage' ) );
+               $importer->setPageCallback( array( $this, 'reportPage' ) );
                $this->importCallback = $importer->setRevisionCallback(
-                       array( &$this, 'handleRevision' ) );
+                       array( $this, 'handleRevision' ) );
                $this->uploadCallback = $importer->setUploadCallback(
-                       array( &$this, 'handleUpload' ) );
+                       array( $this, 'handleUpload' ) );
                $this->logItemCallback = $importer->setLogItemCallback(
-                       array( &$this, 'handleLogItem' ) );
+                       array( $this, 'handleLogItem' ) );
                if ( $this->uploads ) {
                        $importer->setImportUploads( true );
                }
index cc8069d..33008d1 100644 (file)
@@ -1,6 +1,5 @@
 # Custom tags for JSDuck 5.x
 # See also:
-# - https://github.com/senchalabs/jsduck/wiki/Tags
 # - https://github.com/senchalabs/jsduck/wiki/Custom-tags
 # - https://github.com/senchalabs/jsduck/wiki/Custom-tags/7f5c32e568eab9edc8e3365e935bcb836cb11f1d
 require 'jsduck/tag/tag'
index b59f4a9..21e31ed 100644 (file)
@@ -484,19 +484,19 @@ class NamespaceConflictChecker extends Maintenance {
         * Get an alternative title to move a page to. This is used if the
         * preferred destination title already exists.
         *
-        * @param Title $title
+        * @param LinkTarget $linkTarget
         * @param array $options Associative array of validated command-line options
         * @return Title|bool
         */
-       private function getAlternateTitle( $title, $options ) {
+       private function getAlternateTitle( LinkTarget $linkTarget, $options ) {
                $prefix = $options['add-prefix'];
                $suffix = $options['add-suffix'];
                if ( $prefix == '' && $suffix == '' ) {
                        return false;
                }
                while ( true ) {
-                       $dbk = $prefix . $title->getDBkey() . $suffix;
-                       $title = Title::makeTitleSafe( $title->getNamespace(), $dbk );
+                       $dbk = $prefix . $linkTarget->getDBkey() . $suffix;
+                       $title = Title::makeTitleSafe( $linkTarget->getNamespace(), $dbk );
                        if ( !$title ) {
                                return false;
                        }
@@ -510,14 +510,14 @@ class NamespaceConflictChecker extends Maintenance {
         * Move a page
         *
         * @param integer $id The page_id
-        * @param Title $newTitle The new title
+        * @param LinkTarget $newLinkTarget The new title link target
         * @return bool
         */
-       private function movePage( $id, Title $newTitle ) {
+       private function movePage( $id, LinkTarget $newLinkTarget ) {
                $this->db->update( 'page',
                        array(
-                               "page_namespace" => $newTitle->getNamespace(),
-                               "page_title" => $newTitle->getDBkey(),
+                               "page_namespace" => $newLinkTarget->getNamespace(),
+                               "page_title" => $newLinkTarget->getDBkey(),
                        ),
                        array(
                                "page_id" => $id,
@@ -533,7 +533,7 @@ class NamespaceConflictChecker extends Maintenance {
                        list( $table, $fieldPrefix ) = $tableInfo;
                        $this->db->update( $table,
                                // SET
-                               array( "{$fieldPrefix}_from_namespace" => $newTitle->getNamespace() ),
+                               array( "{$fieldPrefix}_from_namespace" => $newLinkTarget->getNamespace() ),
                                // WHERE
                                array( "{$fieldPrefix}_from" => $id ),
                                __METHOD__ );
@@ -550,12 +550,12 @@ class NamespaceConflictChecker extends Maintenance {
         * recentchanges review, etc.
         *
         * @param integer $id The page_id
-        * @param Title $newTitle The new title
+        * @param LinkTarget $linkTarget The new link target
         * @param string $logStatus This is set to the log status message on failure
         * @return bool
         */
-       private function canMerge( $id, Title $newTitle, &$logStatus ) {
-               $latestDest = Revision::newFromTitle( $newTitle, 0, Revision::READ_LATEST );
+       private function canMerge( $id, LinkTarget $linkTarget, &$logStatus ) {
+               $latestDest = Revision::newFromTitle( $linkTarget, 0, Revision::READ_LATEST );
                $latestSource = Revision::newFromPageId( $id, 0, Revision::READ_LATEST );
                if ( $latestSource->getTimestamp() > $latestDest->getTimestamp() ) {
                        $logStatus = 'cannot merge since source is later';
index 09b1b1c..33d6e50 100644 (file)
@@ -65,7 +65,7 @@ class DumpRenderer extends Maintenance {
                $importer = new WikiImporter( $source, $this->getConfig() );
 
                $importer->setRevisionCallback(
-                       array( &$this, 'handleRevision' ) );
+                       array( $this, 'handleRevision' ) );
 
                $importer->doImport();
 
index 831e2dc..48d9705 100755 (executable)
@@ -44,9 +44,15 @@ mkdir -p "$REPO_DIR/$TARGET_DIR/i18n"
 mkdir -p "$REPO_DIR/$TARGET_DIR/images"
 mkdir -p "$REPO_DIR/$TARGET_DIR/themes/mediawiki/images"
 mkdir -p "$REPO_DIR/$TARGET_DIR/themes/apex/images"
-cp ./node_modules/oojs-ui/dist/oojs-ui.js "$REPO_DIR/$TARGET_DIR"
-cp ./node_modules/oojs-ui/dist/{oojs-ui-mediawiki-noimages.css,oojs-ui-mediawiki.js} "$REPO_DIR/$TARGET_DIR"
-cp ./node_modules/oojs-ui/dist/{oojs-ui-apex-noimages.css,oojs-ui-apex.js} "$REPO_DIR/$TARGET_DIR"
+cp ./node_modules/oojs-ui/dist/oojs-ui-core.js "$REPO_DIR/$TARGET_DIR"
+cp ./node_modules/oojs-ui/dist/oojs-ui-core-{mediawiki,apex}.css "$REPO_DIR/$TARGET_DIR"
+cp ./node_modules/oojs-ui/dist/oojs-ui-widgets.js "$REPO_DIR/$TARGET_DIR"
+cp ./node_modules/oojs-ui/dist/oojs-ui-widgets-{mediawiki,apex}.css "$REPO_DIR/$TARGET_DIR"
+cp ./node_modules/oojs-ui/dist/oojs-ui-toolbars.js "$REPO_DIR/$TARGET_DIR"
+cp ./node_modules/oojs-ui/dist/oojs-ui-toolbars-{mediawiki,apex}.css "$REPO_DIR/$TARGET_DIR"
+cp ./node_modules/oojs-ui/dist/oojs-ui-windows.js "$REPO_DIR/$TARGET_DIR"
+cp ./node_modules/oojs-ui/dist/oojs-ui-windows-{mediawiki,apex}.css "$REPO_DIR/$TARGET_DIR"
+cp ./node_modules/oojs-ui/dist/oojs-ui-{mediawiki,apex}.js "$REPO_DIR/$TARGET_DIR"
 cp -R ./node_modules/oojs-ui/dist/i18n "$REPO_DIR/$TARGET_DIR"
 cp -R ./node_modules/oojs-ui/dist/images "$REPO_DIR/$TARGET_DIR"
 cp -R ./node_modules/oojs-ui/dist/themes/mediawiki/images "$REPO_DIR/$TARGET_DIR/themes/mediawiki"
index c0f6c7b..95e3ca7 100644 (file)
@@ -471,7 +471,7 @@ class CheckStorage {
                        $source,
                        ConfigFactory::getDefaultInstance()->makeConfig( 'main' )
                );
-               $importer->setRevisionCallback( array( &$this, 'importRevision' ) );
+               $importer->setRevisionCallback( array( $this, 'importRevision' ) );
                $importer->doImport();
        }
 
index b2f2577..756de27 100644 (file)
@@ -39,7 +39,7 @@ class UpdateSearchIndex extends Maintenance {
 
        public function __construct() {
                parent::__construct();
-               $this->setDescription( 'Script for periodic off-peak updating of the search index' );
+               $this->addDescription( 'Script for periodic off-peak updating of the search index' );
                $this->addOption( 's', 'starting timestamp', false, true );
                $this->addOption( 'e', 'Ending timestamp', false, true );
                $this->addOption(
index 31b201c..75a93a3 100644 (file)
@@ -71,6 +71,7 @@ function wfInstallerMain() {
                $langCode = 'en';
        }
        $wgLang = Language::factory( $langCode );
+       RequestContext::getMain()->setLanguage( $wgLang );
 
        $installer->setParserLanguage( $wgLang );
 
index 5a8257c..33a2039 100644 (file)
@@ -9,16 +9,16 @@
     "grunt": "0.4.5",
     "grunt-cli": "0.1.13",
     "grunt-banana-checker": "0.4.0",
-    "grunt-contrib-copy": "0.8.1",
-    "grunt-contrib-jshint": "0.11.3",
+    "grunt-contrib-copy": "0.8.2",
+    "grunt-contrib-jshint": "0.12.0",
     "grunt-contrib-watch": "0.6.1",
-    "grunt-jscs": "2.6.0",
+    "grunt-jscs": "2.7.0",
     "grunt-jsonlint": "1.0.7",
     "grunt-karma": "0.12.1",
     "karma": "0.13.19",
     "karma-chrome-launcher": "0.2.2",
     "karma-firefox-launcher": "0.1.7",
-    "karma-qunit": "0.1.5",
+    "karma-qunit": "0.1.9",
     "qunitjs": "1.18.0"
   }
 }
index 458d5f1..b9dd3fa 100644 (file)
@@ -1182,15 +1182,19 @@ return array(
                'scripts' => array(
                        'resources/src/mediawiki/mediawiki.Upload.BookletLayout.js',
                ),
+               'styles' => array(
+                       'resources/src/mediawiki/mediawiki.Upload.BookletLayout.css',
+               ),
                'dependencies' => array(
                        'oojs-ui',
+                       'oojs-ui.styles.icons-content',
+                       'oojs-ui.styles.icons-editing-advanced',
                        'mediawiki.Title',
                        'mediawiki.user',
                        'mediawiki.Upload',
                        'mediawiki.jqueryMsg',
                ),
                'messages' => array(
-                       'upload-form-label-select-file',
                        'upload-form-label-infoform-title',
                        'upload-form-label-infoform-name',
                        'upload-form-label-infoform-name-tooltip',
@@ -1277,24 +1281,6 @@ return array(
                        'foreign-structured-upload-form-label-own-work-message-local',
                        'foreign-structured-upload-form-label-not-own-work-message-local',
                        'foreign-structured-upload-form-label-not-own-work-local-local',
-                       'foreign-structured-upload-form-2-label-intro',
-                       'foreign-structured-upload-form-2-label-ownwork',
-                       'foreign-structured-upload-form-2-label-noderiv',
-                       'foreign-structured-upload-form-2-label-useful',
-                       'foreign-structured-upload-form-2-label-ccbysa',
-                       'foreign-structured-upload-form-2-label-alternative',
-                       'foreign-structured-upload-form-2-label-termsofuse',
-                       'foreign-structured-upload-form-3-label-question-website',
-                       'foreign-structured-upload-form-3-label-question-ownwork',
-                       'foreign-structured-upload-form-3-label-question-noderiv',
-                       'foreign-structured-upload-form-3-label-yes',
-                       'foreign-structured-upload-form-3-label-no',
-                       'foreign-structured-upload-form-3-label-alternative',
-                       'foreign-structured-upload-form-4-label-good',
-                       'foreign-structured-upload-form-4-label-bad',
-               ),
-               'templates' => array(
-                       'guide.html' => 'resources/src/mediawiki/bookletlayout/option4/guide.html',
                ),
        ),
        'mediawiki.toc' => array(
@@ -1695,6 +1681,61 @@ return array(
                'scripts' => 'resources/src/mediawiki.special/mediawiki.special.js',
                'styles' => 'resources/src/mediawiki.special/mediawiki.special.css',
        ),
+       'mediawiki.special.apisandbox.styles' => array(
+               'styles' => 'resources/src/mediawiki.special/mediawiki.special.apisandbox.top.css',
+       ),
+       'mediawiki.special.apisandbox' => array(
+               'styles' => 'resources/src/mediawiki.special/mediawiki.special.apisandbox.css',
+               'scripts' => 'resources/src/mediawiki.special/mediawiki.special.apisandbox.js',
+               'dependencies' => array(
+                       'mediawiki.special',
+                       'mediawiki.api',
+                       'mediawiki.jqueryMsg',
+                       'oojs-ui',
+                       'mediawiki.widgets.datetime',
+               ),
+               'messages' => array(
+                       'apisandbox-intro',
+                       'apisandbox-submit',
+                       'apisandbox-reset',
+                       'apisandbox-fullscreen',
+                       'apisandbox-fullscreen-tooltip',
+                       'apisandbox-unfullscreen',
+                       'apisandbox-unfullscreen-tooltip',
+                       'apisandbox-retry',
+                       'apisandbox-loading',
+                       'apisandbox-load-error',
+                       'apisandbox-fetch-token',
+                       'apisandbox-helpurls',
+                       'apisandbox-examples',
+                       'apisandbox-dynamic-parameters',
+                       'apisandbox-dynamic-parameters-add-label',
+                       'apisandbox-dynamic-parameters-add-placeholder',
+                       'apisandbox-dynamic-error-exists',
+                       'apisandbox-deprecated-parameters',
+                       'apisandbox-no-parameters',
+                       'api-help-param-limit',
+                       'api-help-param-limit2',
+                       'api-help-param-integer-min',
+                       'api-help-param-integer-max',
+                       'api-help-param-integer-minmax',
+                       'api-help-param-multi-separate',
+                       'api-help-param-multi-max',
+                       'apisandbox-submit-invalid-fields-title',
+                       'apisandbox-submit-invalid-fields-message',
+                       'apisandbox-results',
+                       'apisandbox-sending-request',
+                       'apisandbox-loading-results',
+                       'apisandbox-results-error',
+                       'apisandbox-request-url-label',
+                       'apisandbox-request-time',
+                       'apisandbox-results-fixtoken',
+                       'apisandbox-results-fixtoken-fail',
+                       'apisandbox-alert-page',
+                       'apisandbox-alert-field',
+                       'blanknamespace',
+               ),
+       ),
        'mediawiki.special.block' => array(
                'scripts' => 'resources/src/mediawiki.special/mediawiki.special.block.js',
                'styles' => 'resources/src/mediawiki.special/mediawiki.special.block.css',
@@ -2036,8 +2077,6 @@ return array(
                        'jquery.byteLimit',
                        // TitleOptionWidget
                        'jquery.autoEllipsis',
-                       // FIXME: Kept for bc
-                       'mediawiki.widgets.CategorySelector',
                ),
                'messages' => array(
                        // NamespaceInputWidget
index d3b74f2..d1b5111 100644 (file)
@@ -32,35 +32,71 @@ return call_user_func( function () {
        $themes = array_map( 'strtolower', $themes );
        $themes['default'] = 'mediawiki';
 
+       // Helper function to generate paths to files used in 'skinStyles' and 'skinScripts'.
+       $getSkinSpecific = function ( $module, $ext = 'css' ) use ( $themes ) {
+               return array_combine(
+                       array_keys( $themes ),
+                       array_map( function ( $theme ) use ( $module, $ext ) {
+                               $module = $module ? "$module-" : '';
+                               // TODO Allow extensions to specify this path somehow
+                               return "resources/lib/oojs-ui/oojs-ui-$module$theme.$ext";
+                       }, array_values( $themes ) )
+               );
+       };
+
        $modules = array();
+
+       // Omnibus module.
        $modules['oojs-ui'] = array(
+               'dependencies' => array(
+                       'oojs-ui-core',
+                       'oojs-ui-widgets',
+                       'oojs-ui-toolbars',
+                       'oojs-ui-windows',
+               ),
+               'targets' => array( 'desktop', 'mobile' ),
+       );
+
+       // The core JavaScript library.
+       $modules['oojs-ui-core'] = array(
                'scripts' => array(
-                       'resources/lib/oojs-ui/oojs-ui.js',
+                       'resources/lib/oojs-ui/oojs-ui-core.js',
                        'resources/src/oojs-ui-local.js',
                ),
-               'skinScripts' => array_combine(
-                       array_keys( $themes ),
-                       array_map( function ( $theme ) {
-                               // TODO Allow extensions to specify this path somehow
-                               return "resources/lib/oojs-ui/oojs-ui-$theme.js";
-                       }, array_values( $themes ) )
-               ),
+               'skinScripts' => $getSkinSpecific( null, 'js' ),
                'dependencies' => array(
                        'es5-shim',
                        'oojs',
-                       'oojs-ui.styles',
+                       'oojs-ui-core.styles',
+                       'mediawiki.language',
+               ),
+               'targets' => array( 'desktop', 'mobile' ),
+       );
+       // This contains only the styles required by core widgets.
+       $modules['oojs-ui-core.styles'] = array(
+               'position' => 'top',
+               'styles' => 'resources/src/oojs-ui-local.css', // HACK, see inside the file
+               'skinStyles' => $getSkinSpecific( 'core' ),
+               'targets' => array( 'desktop', 'mobile' ),
+               // ResourceLoaderImageModule doesn't support 'skipFunction', so instead we set this up so that
+               // this module is skipped together with its dependencies. Nothing else depends on these modules.
+               'dependencies' => array(
                        'oojs-ui.styles.icons',
                        'oojs-ui.styles.indicators',
                        'oojs-ui.styles.textures',
-                       'mediawiki.language',
                ),
+               'skipFunction' => 'resources/src/oojs-ui-styles-skip.js',
+       );
+
+       // Deprecated old name for the module 'oojs-ui-core.styles'.
+       $modules['oojs-ui.styles'] = $modules['oojs-ui-core.styles'];
+
+       // Additional widgets and layouts module.
+       $modules['oojs-ui-widgets'] = array(
+               'scripts' => 'resources/lib/oojs-ui/oojs-ui-widgets.js',
+               'skinStyles' => $getSkinSpecific( 'widgets' ),
+               'dependencies' => 'oojs-ui-core',
                'messages' => array(
-                       'ooui-dialog-message-accept',
-                       'ooui-dialog-message-reject',
-                       'ooui-dialog-process-continue',
-                       'ooui-dialog-process-dismiss',
-                       'ooui-dialog-process-error',
-                       'ooui-dialog-process-retry',
                        'ooui-outline-control-move-down',
                        'ooui-outline-control-move-up',
                        'ooui-outline-control-remove',
@@ -68,21 +104,33 @@ return call_user_func( function () {
                        'ooui-selectfile-dragdrop-placeholder',
                        'ooui-selectfile-not-supported',
                        'ooui-selectfile-placeholder',
+               ),
+               'targets' => array( 'desktop', 'mobile' ),
+       );
+       // Toolbar and tools module.
+       $modules['oojs-ui-toolbars'] = array(
+               'scripts' => 'resources/lib/oojs-ui/oojs-ui-toolbars.js',
+               'skinStyles' => $getSkinSpecific( 'toolbars' ),
+               'dependencies' => 'oojs-ui-core',
+               'messages' => array(
                        'ooui-toolbar-more',
                        'ooui-toolgroup-collapse',
                        'ooui-toolgroup-expand',
                ),
                'targets' => array( 'desktop', 'mobile' ),
        );
-       $modules['oojs-ui.styles'] = array(
-               'position' => 'top',
-               'styles' => 'resources/src/oojs-ui-local.css', // HACK, see inside the file
-               'skinStyles' => array_combine(
-                       array_keys( $themes ),
-                       array_map( function ( $theme ) {
-                               // TODO Allow extensions to specify this path somehow
-                               return "resources/lib/oojs-ui/oojs-ui-$theme-noimages.css";
-                       }, array_values( $themes ) )
+       // Windows and dialogs module.
+       $modules['oojs-ui-windows'] = array(
+               'scripts' => 'resources/lib/oojs-ui/oojs-ui-windows.js',
+               'skinStyles' => $getSkinSpecific( 'windows' ),
+               'dependencies' => 'oojs-ui-core',
+               'messages' => array(
+                       'ooui-dialog-message-accept',
+                       'ooui-dialog-message-reject',
+                       'ooui-dialog-process-continue',
+                       'ooui-dialog-process-dismiss',
+                       'ooui-dialog-process-error',
+                       'ooui-dialog-process-retry',
                ),
                'targets' => array( 'desktop', 'mobile' ),
        );
diff --git a/resources/lib/oojs-ui/i18n/cdo.json b/resources/lib/oojs-ui/i18n/cdo.json
new file mode 100644 (file)
index 0000000..cb46b43
--- /dev/null
@@ -0,0 +1,23 @@
+{
+       "@metadata": {
+               "authors": [
+                       "Yejianfei"
+               ]
+       },
+       "ooui-outline-control-move-down": "下移項目",
+       "ooui-outline-control-move-up": "上移項目",
+       "ooui-outline-control-remove": "移除項目",
+       "ooui-toolbar-more": "更価",
+       "ooui-toolgroup-expand": "更価",
+       "ooui-toolgroup-collapse": "更少",
+       "ooui-dialog-message-accept": "確定",
+       "ooui-dialog-message-reject": "取消",
+       "ooui-dialog-process-error": "什乇出毛病了",
+       "ooui-dialog-process-dismiss": "關閉",
+       "ooui-dialog-process-retry": "重試",
+       "ooui-dialog-process-continue": "繼續",
+       "ooui-selectfile-button-select": "選擇蜀萆文件",
+       "ooui-selectfile-not-supported": "𣍐支持選擇其文件",
+       "ooui-selectfile-placeholder": "未選文件",
+       "ooui-selectfile-dragdrop-placeholder": "共文件拖遘嚽塊"
+}
index e789565..ff2a2b3 100644 (file)
@@ -5,7 +5,8 @@
                        "KuboF",
                        "Shirayuki",
                        "Yekrats",
-                       "Kvardek du"
+                       "Kvardek du",
+                       "Psychoslave"
                ]
        },
        "ooui-outline-control-move-down": "Movi eron suben",
@@ -22,5 +23,6 @@
        "ooui-dialog-process-continue": "Daŭrigi",
        "ooui-selectfile-button-select": "Elekti dosieron",
        "ooui-selectfile-not-supported": "Dosieroselekto ne estas subtenata.",
-       "ooui-selectfile-placeholder": "Vi ne selektis dosieron"
+       "ooui-selectfile-placeholder": "Vi ne selektis dosieron",
+       "ooui-selectfile-dragdrop-placeholder": "Ĵetu dosieron ĉi tie."
 }
index 2cd688e..341d0ff 100644 (file)
@@ -7,7 +7,7 @@
                        "Vahe Gharakhanyan"
                ]
        },
-       "ooui-outline-control-move-down": "Ô»Õ»Õ¥Ö\81Õ¶Õ¥Õ¬ Õ¯Õ¥Õ¿Õ¨",
+       "ooui-outline-control-move-down": "Ô»Õ»Õ¥Ö\81Õ¶Õ¥Õ¬ Õ¶Õ¥Ö\80Ö\84Ö\87",
        "ooui-outline-control-move-up": "Բարձրացնել կետը",
        "ooui-outline-control-remove": "Հեռացնել տարրը",
        "ooui-toolbar-more": "Ավելին",
index 68a25b5..4b95ffc 100644 (file)
@@ -15,7 +15,8 @@
                        "Ontsed",
                        "Alexmar983",
                        "Nemo bis",
-                       "Jdforrester"
+                       "Jdforrester",
+                       "Fringio"
                ]
        },
        "ooui-outline-control-move-down": "Sposta in basso",
@@ -33,5 +34,5 @@
        "ooui-selectfile-button-select": "Seleziona un file",
        "ooui-selectfile-not-supported": "La selezione del file non è supportata",
        "ooui-selectfile-placeholder": "Nessun file è selezionato",
-       "ooui-selectfile-dragdrop-placeholder": "Posiziona i files qui"
+       "ooui-selectfile-dragdrop-placeholder": "Posiziona i file qui"
 }
index a61083b..31344be 100644 (file)
@@ -17,6 +17,8 @@
        "ooui-dialog-process-dismiss": "Didi",
        "ooui-dialog-process-retry": "Itti deebi'ii yaali",
        "ooui-dialog-process-continue": "Itti fufi",
+       "ooui-selectfile-button-select": "Faayilii filadhu",
        "ooui-selectfile-not-supported": "Faayilii filachuun hin danda'amu.",
-       "ooui-selectfile-placeholder": "Faayiliin wayiiyyuu hin filatamne"
+       "ooui-selectfile-placeholder": "Faayiliin wayiiyyuu hin filatamne",
+       "ooui-selectfile-dragdrop-placeholder": "Faayilii as kaa'i"
 }
index 12c77e0..dc14339 100644 (file)
@@ -17,6 +17,7 @@
        "ooui-dialog-process-retry": "ٻيهر ڪوشش ڪريو",
        "ooui-dialog-process-continue": "جاري رکو",
        "ooui-selectfile-button-select": "ڪو فائيل چونڊِو",
+       "ooui-selectfile-not-supported": "فائيل جي چونڊ سپورٽ نٿي ڪئي وڃي",
        "ooui-selectfile-placeholder": "ڪوبه فائيل چونڊيو نه ويو آهي",
        "ooui-selectfile-dragdrop-placeholder": "فائيل کي هتي ڪيرايو"
 }
index db6fa3c..532ba3f 100644 (file)
@@ -1,7 +1,8 @@
 {
        "@metadata": {
                "authors": [
-                       "OC Ripper"
+                       "OC Ripper",
+                       "Sf"
                ]
        },
        "ooui-outline-control-move-down": "Pomakni stavku dolje",
@@ -16,6 +17,8 @@
        "ooui-dialog-process-dismiss": "Odbaci",
        "ooui-dialog-process-retry": "Pokušajte ponovo",
        "ooui-dialog-process-continue": "Nastavi",
+       "ooui-selectfile-button-select": "Izaberi datoteku",
        "ooui-selectfile-not-supported": "Izbor datoteke nije podržan",
-       "ooui-selectfile-placeholder": "Nijedna datoteka nije odabrana"
+       "ooui-selectfile-placeholder": "Nijedna datoteka nije odabrana",
+       "ooui-selectfile-dragdrop-placeholder": "Prevuci datoteku ovdje"
 }
index 704a186..bdf6a64 100644 (file)
@@ -15,5 +15,7 @@
        "ooui-dialog-process-error": "Nešto je pošlo naopako",
        "ooui-dialog-process-dismiss": "Odbaci",
        "ooui-dialog-process-retry": "Pokušaj ponovo",
-       "ooui-dialog-process-continue": "Nastavi"
+       "ooui-dialog-process-continue": "Nastavi",
+       "ooui-selectfile-button-select": "Izaberi datoteku",
+       "ooui-selectfile-placeholder": "Nije izabrana nijedna datoteka"
 }
index f5bfa2c..4109c36 100644 (file)
@@ -1,7 +1,8 @@
 {
        "@metadata": {
                "authors": [
-                       "David1010"
+                       "David1010",
+                       "Silovan"
                ]
        },
        "ooui-outline-control-move-down": "ელემენტის ქვემოთ გადატანა",
@@ -15,5 +16,9 @@
        "ooui-dialog-process-error": "მოხდა რაღაც შეცდომა",
        "ooui-dialog-process-dismiss": "დამალვა",
        "ooui-dialog-process-retry": "კიდევ სცადეთ",
-       "ooui-dialog-process-continue": "გაგრძელება"
+       "ooui-dialog-process-continue": "გაგრძელება",
+       "ooui-selectfile-button-select": "გეგშაგორით ფაილი",
+       "ooui-selectfile-not-supported": "ფაილიშ აშაგორუა ვა რე ხენწყილი",
+       "ooui-selectfile-placeholder": "ფაილი ვა რე გიშაგორილი",
+       "ooui-selectfile-dragdrop-placeholder": "ქინაჸათით ფაილი ათაქ"
 }
index 9934d9d..132c32d 100644 (file)
@@ -17,7 +17,8 @@
                        "Zhangjintao",
                        "乌拉跨氪",
                        "Great Brightstar",
-                       "Nbdd0121"
+                       "Nbdd0121",
+                       "Yejianfei"
                ]
        },
        "ooui-outline-control-move-down": "向下移动一项",
@@ -33,7 +34,7 @@
        "ooui-dialog-process-retry": "重试",
        "ooui-dialog-process-continue": "继续",
        "ooui-selectfile-button-select": "选择一个文件",
-       "ooui-selectfile-not-supported": "文件选择不受支持",
+       "ooui-selectfile-not-supported": "不支持文件选择器",
        "ooui-selectfile-placeholder": "没有选定文件",
        "ooui-selectfile-dragdrop-placeholder": "将文件拖动至此"
 }
diff --git a/resources/lib/oojs-ui/oojs-ui-apex-noimages.css b/resources/lib/oojs-ui/oojs-ui-apex-noimages.css
deleted file mode 100644 (file)
index a616f4d..0000000
+++ /dev/null
@@ -1,2938 +0,0 @@
-/*!
- * OOjs UI v0.15.1
- * https://www.mediawiki.org/wiki/OOjs_UI
- *
- * Copyright 2011–2016 OOjs UI Team and other contributors.
- * Released under the MIT license
- * http://oojs.mit-license.org
- *
- * Date: 2016-01-26T21:14:25Z
- */
-@-webkit-keyframes oo-ui-progressBarWidget-slide {
-       from {
-               margin-left: -40%;
-       }
-       to {
-               margin-left: 100%;
-       }
-}
-@-moz-keyframes oo-ui-progressBarWidget-slide {
-       from {
-               margin-left: -40%;
-       }
-       to {
-               margin-left: 100%;
-       }
-}
-@-ms-keyframes oo-ui-progressBarWidget-slide {
-       from {
-               margin-left: -40%;
-       }
-       to {
-               margin-left: 100%;
-       }
-}
-@-o-keyframes oo-ui-progressBarWidget-slide {
-       from {
-               margin-left: -40%;
-       }
-       to {
-               margin-left: 100%;
-       }
-}
-@keyframes oo-ui-progressBarWidget-slide {
-       from {
-               margin-left: -40%;
-       }
-       to {
-               margin-left: 100%;
-       }
-}
-/* @noflip */
-.oo-ui-rtl {
-       direction: rtl;
-}
-/* @noflip */
-.oo-ui-ltr {
-       direction: ltr;
-}
-.oo-ui-element-hidden {
-       display: none !important;
-}
-.oo-ui-buttonElement > .oo-ui-buttonElement-button {
-       cursor: pointer;
-       display: inline-block;
-       vertical-align: middle;
-       font: inherit;
-       white-space: nowrap;
-       -webkit-touch-callout: none;
-       -webkit-user-select: none;
-          -moz-user-select: none;
-           -ms-user-select: none;
-               user-select: none;
-}
-.oo-ui-buttonElement > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon,
-.oo-ui-buttonElement > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator {
-       display: none;
-}
-.oo-ui-buttonElement.oo-ui-widget-disabled > .oo-ui-buttonElement-button {
-       cursor: default;
-}
-.oo-ui-buttonElement.oo-ui-indicatorElement > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator,
-.oo-ui-buttonElement.oo-ui-iconElement > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon {
-       display: inline-block;
-       vertical-align: middle;
-}
-.oo-ui-buttonElement-frameless {
-       display: inline-block;
-       position: relative;
-}
-.oo-ui-buttonElement-frameless.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
-       display: inline-block;
-       vertical-align: middle;
-}
-.oo-ui-buttonElement-framed > .oo-ui-buttonElement-button {
-       display: inline-block;
-       vertical-align: top;
-       text-align: center;
-}
-.oo-ui-buttonElement-framed.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
-       display: inline-block;
-       vertical-align: middle;
-}
-.oo-ui-buttonElement-framed.oo-ui-widget-disabled > .oo-ui-buttonElement-button,
-.oo-ui-buttonElement-framed.oo-ui-widget-disabled.oo-ui-buttonElement-active > .oo-ui-buttonElement-button,
-.oo-ui-buttonElement-framed.oo-ui-widget-disabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
-       cursor: default;
-}
-.oo-ui-buttonElement > .oo-ui-buttonElement-button {
-       color: #333333;
-}
-.oo-ui-buttonElement.oo-ui-iconElement > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon {
-       margin-left: 0;
-}
-.oo-ui-buttonElement.oo-ui-indicatorElement > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator {
-       width: 0.9375em;
-       height: 0.9375em;
-       margin: 0.46875em;
-}
-.oo-ui-buttonElement.oo-ui-iconElement > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator {
-       margin-left: 0.46875em;
-}
-.oo-ui-buttonElement.oo-ui-iconElement > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon {
-       width: 1.875em;
-       height: 1.875em;
-}
-.oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button:hover,
-.oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button:focus {
-       outline: none;
-}
-.oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button:hover > .oo-ui-iconElement-icon,
-.oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button:focus > .oo-ui-iconElement-icon {
-       opacity: 1;
-}
-.oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button:hover > .oo-ui-labelElement-label,
-.oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button:focus > .oo-ui-labelElement-label {
-       color: #000000;
-}
-.oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
-       color: #333333;
-}
-.oo-ui-buttonElement-frameless.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
-       margin-left: 0.25em;
-}
-.oo-ui-buttonElement-frameless > input.oo-ui-buttonElement-button {
-       padding-left: 0.25em;
-       color: #333333;
-}
-.oo-ui-buttonElement-frameless > input.oo-ui-buttonElement-button:hover,
-.oo-ui-buttonElement-frameless > input.oo-ui-buttonElement-button:focus {
-       color: #000000;
-}
-.oo-ui-buttonElement-frameless.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
-       color: #087ecc;
-}
-.oo-ui-buttonElement-frameless.oo-ui-flaggedElement-constructive > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
-       color: #76ab36;
-}
-.oo-ui-buttonElement-frameless.oo-ui-flaggedElement-destructive > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
-       color: #d45353;
-}
-.oo-ui-buttonElement-frameless.oo-ui-widget-disabled > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon {
-       opacity: 0.2;
-}
-.oo-ui-buttonElement-frameless.oo-ui-widget-disabled > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
-       color: #cccccc;
-}
-.oo-ui-buttonElement-framed > .oo-ui-buttonElement-button {
-       padding: 0.2em 0.8em;
-       border-radius: 0.3em;
-       text-shadow: 0 1px 1px rgba(255, 255, 255, 0.5);
-       border: 1px #c9c9c9 solid;
-       -webkit-transition: border-color 100ms ease;
-          -moz-transition: border-color 100ms ease;
-               transition: border-color 100ms ease;
-       background: #eeeeee;
-       filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#fff', endColorstr='#ddd');
-       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0%, #ffffff), color-stop(100%, #dddddd));
-       background-image: -webkit-linear-gradient(top, #ffffff 0%, #dddddd 100%);
-       background-image:    -moz-linear-gradient(top, #ffffff 0%, #dddddd 100%);
-       background-image:      -o-linear-gradient(top, #ffffff 0%, #dddddd 100%);
-       background-image:         linear-gradient(to bottom, #ffffff 0%, #dddddd 100%);
-}
-.oo-ui-buttonElement-framed > .oo-ui-buttonElement-button:hover,
-.oo-ui-buttonElement-framed > .oo-ui-buttonElement-button:focus {
-       border-color: #aaaaaa;
-       outline: none;
-}
-.oo-ui-buttonElement-framed > input.oo-ui-buttonElement-button,
-.oo-ui-buttonElement-framed.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
-       line-height: 1.875em;
-}
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active,
-.oo-ui-buttonElement-framed.oo-ui-buttonElement-active > .oo-ui-buttonElement-button,
-.oo-ui-buttonElement-framed.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
-       box-shadow: inset 0 1px 4px 0 rgba(0, 0, 0, 0.07);
-       color: black;
-       border-color: #c9c9c9;
-       background: #eeeeee;
-       filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#ddd', endColorstr='#fff');
-       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0%, #dddddd), color-stop(100%, #ffffff));
-       background-image: -webkit-linear-gradient(top, #dddddd 0%, #ffffff 100%);
-       background-image:    -moz-linear-gradient(top, #dddddd 0%, #ffffff 100%);
-       background-image:      -o-linear-gradient(top, #dddddd 0%, #ffffff 100%);
-       background-image:         linear-gradient(to bottom, #dddddd 0%, #ffffff 100%);
-}
-.oo-ui-buttonElement-framed.oo-ui-iconElement > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon {
-       margin-left: -0.5em;
-       margin-right: -0.5em;
-}
-.oo-ui-buttonElement-framed.oo-ui-iconElement.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon {
-       margin-right: 0.3em;
-}
-.oo-ui-buttonElement-framed.oo-ui-indicatorElement > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator {
-       margin-left: -0.005em;
-       margin-right: -0.005em;
-}
-.oo-ui-buttonElement-framed.oo-ui-indicatorElement.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator,
-.oo-ui-buttonElement-framed.oo-ui-indicatorElement.oo-ui-iconElement:not( .oo-ui-labelElement ) > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator {
-       margin-left: 0.46875em;
-       margin-right: -0.275em;
-}
-.oo-ui-buttonElement-framed.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button {
-       border: 1px solid #a6cee1;
-       background: #cde7f4;
-       filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#eaf4fa', endColorstr='#b0d9ee');
-       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0%, #eaf4fa), color-stop(100%, #b0d9ee));
-       background-image: -webkit-linear-gradient(top, #eaf4fa 0%, #b0d9ee 100%);
-       background-image:    -moz-linear-gradient(top, #eaf4fa 0%, #b0d9ee 100%);
-       background-image:      -o-linear-gradient(top, #eaf4fa 0%, #b0d9ee 100%);
-       background-image:         linear-gradient(to bottom, #eaf4fa 0%, #b0d9ee 100%);
-}
-.oo-ui-buttonElement-framed.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button:hover,
-.oo-ui-buttonElement-framed.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button:focus {
-       border-color: #9dc2d4;
-}
-.oo-ui-buttonElement-framed.oo-ui-flaggedElement-progressive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active,
-.oo-ui-buttonElement-framed.oo-ui-flaggedElement-progressive.oo-ui-buttonElement-active > .oo-ui-buttonElement-button,
-.oo-ui-buttonElement-framed.oo-ui-flaggedElement-progressive.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
-       border: 1px solid #a6cee1;
-       background: #cde7f4;
-       filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#b0d9ee', endColorstr='#eaf4fa');
-       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0%, #b0d9ee), color-stop(100%, #eaf4fa));
-       background-image: -webkit-linear-gradient(top, #b0d9ee 0%, #eaf4fa 100%);
-       background-image:    -moz-linear-gradient(top, #b0d9ee 0%, #eaf4fa 100%);
-       background-image:      -o-linear-gradient(top, #b0d9ee 0%, #eaf4fa 100%);
-       background-image:         linear-gradient(to bottom, #b0d9ee 0%, #eaf4fa 100%);
-}
-.oo-ui-buttonElement-framed.oo-ui-flaggedElement-constructive > .oo-ui-buttonElement-button {
-       border: 1px solid #b8d892;
-       background: #daf0be;
-       filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#f0fbe1', endColorstr='#c3e59a');
-       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0%, #f0fbe1), color-stop(100%, #c3e59a));
-       background-image: -webkit-linear-gradient(top, #f0fbe1 0%, #c3e59a 100%);
-       background-image:    -moz-linear-gradient(top, #f0fbe1 0%, #c3e59a 100%);
-       background-image:      -o-linear-gradient(top, #f0fbe1 0%, #c3e59a 100%);
-       background-image:         linear-gradient(to bottom, #f0fbe1 0%, #c3e59a 100%);
-}
-.oo-ui-buttonElement-framed.oo-ui-flaggedElement-constructive > .oo-ui-buttonElement-button:hover,
-.oo-ui-buttonElement-framed.oo-ui-flaggedElement-constructive > .oo-ui-buttonElement-button:focus {
-       border-color: #adcb89;
-}
-.oo-ui-buttonElement-framed.oo-ui-flaggedElement-constructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active,
-.oo-ui-buttonElement-framed.oo-ui-flaggedElement-constructive.oo-ui-buttonElement-active > .oo-ui-buttonElement-button,
-.oo-ui-buttonElement-framed.oo-ui-flaggedElement-constructive.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
-       border: 1px solid #b8d892;
-       background: #daf0be;
-       filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#c3e59a', endColorstr='#f0fbe1');
-       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0%, #c3e59a), color-stop(100%, #f0fbe1));
-       background-image: -webkit-linear-gradient(top, #c3e59a 0%, #f0fbe1 100%);
-       background-image:    -moz-linear-gradient(top, #c3e59a 0%, #f0fbe1 100%);
-       background-image:      -o-linear-gradient(top, #c3e59a 0%, #f0fbe1 100%);
-       background-image:         linear-gradient(to bottom, #c3e59a 0%, #f0fbe1 100%);
-}
-.oo-ui-buttonElement-framed.oo-ui-flaggedElement-destructive > .oo-ui-buttonElement-button {
-       color: #d45353;
-}
-.oo-ui-buttonElement-framed.oo-ui-widget-disabled > .oo-ui-buttonElement-button,
-.oo-ui-buttonElement-framed.oo-ui-widget-disabled.oo-ui-buttonElement-active > .oo-ui-buttonElement-button,
-.oo-ui-buttonElement-framed.oo-ui-widget-disabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
-       opacity: 0.5;
-       -webkit-transform: translate3d(0, 0, 0);
-       box-shadow: none;
-       color: #333333;
-       background: #eeeeee;
-       border-color: #cccccc;
-}
-.oo-ui-buttonElement-framed.oo-ui-widget-disabled > .oo-ui-buttonElement-button:hover,
-.oo-ui-buttonElement-framed.oo-ui-widget-disabled.oo-ui-buttonElement-active > .oo-ui-buttonElement-button:hover,
-.oo-ui-buttonElement-framed.oo-ui-widget-disabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button:hover,
-.oo-ui-buttonElement-framed.oo-ui-widget-disabled > .oo-ui-buttonElement-button:focus,
-.oo-ui-buttonElement-framed.oo-ui-widget-disabled.oo-ui-buttonElement-active > .oo-ui-buttonElement-button:focus,
-.oo-ui-buttonElement-framed.oo-ui-widget-disabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button:focus {
-       border-color: #cccccc;
-       box-shadow: none;
-}
-.oo-ui-clippableElement-clippable {
-       -webkit-box-sizing: border-box;
-          -moz-box-sizing: border-box;
-               box-sizing: border-box;
-}
-.oo-ui-draggableElement {
-       cursor: -webkit-grab -moz-grab, url(images/grab.cur), move;
-}
-.oo-ui-draggableElement-dragging {
-       cursor: -webkit-grabbing -moz-grabbing, url(images/grabbing.cur), move;
-       background: rgba(0, 0, 0, 0.2);
-       opacity: 0.4;
-}
-.oo-ui-draggableGroupElement-horizontal .oo-ui-draggableElement.oo-ui-optionWidget {
-       display: inline-block;
-}
-.oo-ui-draggableGroupElement-placeholder {
-       position: absolute;
-       display: block;
-       background: rgba(0, 0, 0, 0.4);
-}
-.oo-ui-iconElement .oo-ui-iconElement-icon,
-.oo-ui-iconElement.oo-ui-iconElement-icon {
-       background-size: contain;
-       background-position: center center;
-       background-repeat: no-repeat;
-}
-.oo-ui-iconElement .oo-ui-iconElement-icon,
-.oo-ui-iconElement.oo-ui-iconElement-icon {
-       opacity: 0.8;
-}
-.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator,
-.oo-ui-indicatorElement.oo-ui-indicatorElement-indicator {
-       background-size: contain;
-       background-position: center center;
-       background-repeat: no-repeat;
-}
-.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator,
-.oo-ui-indicatorElement.oo-ui-indicatorElement-indicator {
-       opacity: 0.8;
-}
-.oo-ui-lookupElement > .oo-ui-menuSelectWidget {
-       z-index: 1;
-       width: 100%;
-}
-.oo-ui-pendingElement-pending {
-       background-image: /* @embed */ url(themes/apex/images/textures/pending.gif);
-}
-.oo-ui-bookletLayout-stackLayout.oo-ui-stackLayout-continuous > .oo-ui-panelLayout-scrollable {
-       overflow-y: hidden;
-}
-.oo-ui-bookletLayout-stackLayout > .oo-ui-panelLayout {
-       width: 100%;
-       -webkit-box-sizing: border-box;
-          -moz-box-sizing: border-box;
-               box-sizing: border-box;
-}
-.oo-ui-bookletLayout-stackLayout > .oo-ui-panelLayout-scrollable {
-       overflow-y: auto;
-}
-.oo-ui-bookletLayout-stackLayout > .oo-ui-panelLayout-padded {
-       padding: 2em;
-}
-.oo-ui-bookletLayout-outlinePanel-editable > .oo-ui-outlineSelectWidget {
-       position: absolute;
-       top: 0;
-       left: 0;
-       right: 0;
-       bottom: 3em;
-       overflow-y: auto;
-}
-.oo-ui-bookletLayout-outlinePanel > .oo-ui-outlineControlsWidget {
-       position: absolute;
-       bottom: 0;
-       left: 0;
-       right: 0;
-}
-.oo-ui-bookletLayout-stackLayout > .oo-ui-panelLayout {
-       padding: 1.5em;
-}
-.oo-ui-bookletLayout-outlinePanel {
-       border-right: 1px solid #dddddd;
-}
-.oo-ui-bookletLayout-outlinePanel > .oo-ui-outlineControlsWidget {
-       box-shadow: 0 0 0.25em rgba(0, 0, 0, 0.25);
-}
-.oo-ui-indexLayout > .oo-ui-menuLayout-menu {
-       height: 3em;
-}
-.oo-ui-indexLayout > .oo-ui-menuLayout-content {
-       top: 3em;
-}
-.oo-ui-indexLayout-stackLayout > .oo-ui-panelLayout {
-       padding: 1.5em;
-}
-.oo-ui-fieldLayout {
-       display: block;
-       margin-bottom: 1em;
-}
-.oo-ui-fieldLayout:before,
-.oo-ui-fieldLayout:after {
-       content: " ";
-       display: table;
-}
-.oo-ui-fieldLayout:after {
-       clear: both;
-}
-.oo-ui-fieldLayout.oo-ui-fieldLayout-align-left > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label,
-.oo-ui-fieldLayout.oo-ui-fieldLayout-align-right > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label,
-.oo-ui-fieldLayout.oo-ui-fieldLayout-align-left > .oo-ui-fieldLayout-body > .oo-ui-fieldLayout-field,
-.oo-ui-fieldLayout.oo-ui-fieldLayout-align-right > .oo-ui-fieldLayout-body > .oo-ui-fieldLayout-field {
-       display: block;
-       float: left;
-}
-.oo-ui-fieldLayout.oo-ui-fieldLayout-align-right > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label {
-       text-align: right;
-}
-.oo-ui-fieldLayout.oo-ui-fieldLayout-align-inline > .oo-ui-fieldLayout-body {
-       display: table;
-}
-.oo-ui-fieldLayout.oo-ui-fieldLayout-align-inline > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label,
-.oo-ui-fieldLayout.oo-ui-fieldLayout-align-inline > .oo-ui-fieldLayout-body > .oo-ui-fieldLayout-field {
-       display: table-cell;
-       vertical-align: middle;
-}
-.oo-ui-fieldLayout.oo-ui-labelElement.oo-ui-fieldLayout-align-top > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label {
-       display: inline-block;
-}
-.oo-ui-fieldLayout > .oo-ui-fieldLayout-help {
-       float: right;
-}
-.oo-ui-fieldLayout > .oo-ui-fieldLayout-help > .oo-ui-popupWidget > .oo-ui-popupWidget-popup {
-       z-index: 1;
-}
-.oo-ui-fieldLayout > .oo-ui-fieldLayout-help .oo-ui-fieldLayout-help-content {
-       padding: 0.5em 0.75em;
-       line-height: 1.5em;
-}
-.oo-ui-fieldLayout:last-child {
-       margin-bottom: 0;
-}
-.oo-ui-fieldLayout.oo-ui-fieldLayout-align-left.oo-ui-labelElement > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label,
-.oo-ui-fieldLayout.oo-ui-fieldLayout-align-right.oo-ui-labelElement > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label {
-       padding-top: 0.5em;
-       margin-right: 5%;
-       width: 35%;
-}
-.oo-ui-fieldLayout.oo-ui-fieldLayout-align-left > .oo-ui-fieldLayout-body > .oo-ui-fieldLayout-field,
-.oo-ui-fieldLayout.oo-ui-fieldLayout-align-right > .oo-ui-fieldLayout-body > .oo-ui-fieldLayout-field {
-       width: 60%;
-}
-.oo-ui-fieldLayout.oo-ui-fieldLayout-align-inline {
-       margin-bottom: 1.25em;
-}
-.oo-ui-fieldLayout.oo-ui-fieldLayout-align-inline.oo-ui-labelElement > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label {
-       padding: 0.25em 0.25em 0.25em 0.5em;
-}
-.oo-ui-fieldLayout.oo-ui-fieldLayout-align-top.oo-ui-labelElement > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label {
-       padding: 0.5em 0;
-}
-.oo-ui-fieldLayout > .oo-ui-popupButtonWidget {
-       margin-right: 0;
-       margin-top: 0.25em;
-}
-.oo-ui-fieldLayout > .oo-ui-popupButtonWidget:last-child {
-       margin-right: 0;
-}
-.oo-ui-fieldLayout-disabled > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label {
-       color: #cccccc;
-}
-.oo-ui-fieldLayout-messages {
-       list-style: none none;
-       margin: 0;
-       padding: 0;
-       margin-top: 0.25em;
-       margin-left: 0.25em;
-}
-.oo-ui-fieldLayout-messages > li {
-       margin: 0;
-       padding: 0;
-}
-.oo-ui-fieldLayout-messages .oo-ui-iconWidget {
-       display: none;
-}
-.oo-ui-fieldLayout-messages .oo-ui-fieldLayout-messages-error {
-       color: #d45353;
-}
-.oo-ui-fieldLayout-messages .oo-ui-labelWidget {
-       padding: 0;
-       line-height: 1.875em;
-       vertical-align: middle;
-}
-.oo-ui-actionFieldLayout-input,
-.oo-ui-actionFieldLayout-button {
-       display: table-cell;
-       vertical-align: middle;
-}
-.oo-ui-actionFieldLayout-input {
-       padding-right: 1em;
-}
-.oo-ui-actionFieldLayout-button {
-       width: 1%;
-       white-space: nowrap;
-}
-.oo-ui-fieldsetLayout {
-       position: relative;
-       margin: 0;
-       padding: 0;
-       border: none;
-}
-.oo-ui-fieldsetLayout.oo-ui-iconElement > .oo-ui-iconElement-icon {
-       display: block;
-       position: absolute;
-}
-.oo-ui-fieldsetLayout.oo-ui-labelElement > .oo-ui-labelElement-label {
-       display: inline-block;
-}
-.oo-ui-fieldsetLayout > .oo-ui-fieldsetLayout-help {
-       float: right;
-}
-.oo-ui-fieldsetLayout > .oo-ui-fieldsetLayout-help > .oo-ui-popupWidget > .oo-ui-popupWidget-popup {
-       z-index: 1;
-}
-.oo-ui-fieldsetLayout > .oo-ui-fieldsetLayout-help .oo-ui-fieldsetLayout-help-content {
-       padding: 0.5em 0.75em;
-       line-height: 1.5em;
-}
-.oo-ui-fieldsetLayout + .oo-ui-fieldsetLayout,
-.oo-ui-fieldsetLayout + .oo-ui-formLayout {
-       margin-top: 2em;
-}
-.oo-ui-fieldsetLayout > .oo-ui-labelElement-label {
-       font-size: 1.1em;
-       margin-bottom: 0.5em;
-       padding: 0.25em 0;
-       font-weight: bold;
-}
-.oo-ui-fieldsetLayout.oo-ui-iconElement > .oo-ui-labelElement-label {
-       padding-left: 2em;
-       line-height: 1.8em;
-}
-.oo-ui-fieldsetLayout.oo-ui-iconElement > .oo-ui-iconElement-icon {
-       left: 0;
-       top: 0.25em;
-       width: 1.875em;
-       height: 1.875em;
-}
-.oo-ui-fieldsetLayout > .oo-ui-popupButtonWidget {
-       margin-right: 0;
-}
-.oo-ui-fieldsetLayout > .oo-ui-popupButtonWidget:last-child {
-       margin-right: 0;
-}
-.oo-ui-formLayout + .oo-ui-fieldsetLayout,
-.oo-ui-formLayout + .oo-ui-formLayout {
-       margin-top: 2em;
-}
-.oo-ui-menuLayout {
-       position: absolute;
-       top: 0;
-       left: 0;
-       right: 0;
-       bottom: 0;
-}
-.oo-ui-menuLayout-menu,
-.oo-ui-menuLayout-content {
-       position: absolute;
-       -webkit-transition: all 200ms ease;
-          -moz-transition: all 200ms ease;
-               transition: all 200ms ease;
-}
-.oo-ui-menuLayout-menu {
-       height: 18em;
-       width: 18em;
-}
-.oo-ui-menuLayout-content {
-       top: 18em;
-       left: 18em;
-       right: 18em;
-       bottom: 18em;
-}
-.oo-ui-menuLayout.oo-ui-menuLayout-hideMenu > .oo-ui-menuLayout-menu {
-       width: 0 !important;
-       height: 0 !important;
-       overflow: hidden;
-}
-.oo-ui-menuLayout.oo-ui-menuLayout-hideMenu > .oo-ui-menuLayout-content {
-       top: 0 !important;
-       left: 0 !important;
-       right: 0 !important;
-       bottom: 0 !important;
-}
-.oo-ui-menuLayout.oo-ui-menuLayout-showMenu.oo-ui-menuLayout-top > .oo-ui-menuLayout-menu {
-       width: auto !important;
-       left: 0;
-       top: 0;
-       right: 0;
-}
-.oo-ui-menuLayout.oo-ui-menuLayout-showMenu.oo-ui-menuLayout-top > .oo-ui-menuLayout-content {
-       right: 0 !important;
-       bottom: 0 !important;
-       left: 0 !important;
-}
-.oo-ui-menuLayout.oo-ui-menuLayout-showMenu.oo-ui-menuLayout-after > .oo-ui-menuLayout-menu {
-       height: auto !important;
-       top: 0;
-       right: 0;
-       bottom: 0;
-}
-.oo-ui-menuLayout.oo-ui-menuLayout-showMenu.oo-ui-menuLayout-after > .oo-ui-menuLayout-content {
-       bottom: 0 !important;
-       left: 0 !important;
-       top: 0 !important;
-}
-.oo-ui-menuLayout.oo-ui-menuLayout-showMenu.oo-ui-menuLayout-bottom > .oo-ui-menuLayout-menu {
-       width: auto !important;
-       right: 0;
-       bottom: 0;
-       left: 0;
-}
-.oo-ui-menuLayout.oo-ui-menuLayout-showMenu.oo-ui-menuLayout-bottom > .oo-ui-menuLayout-content {
-       left: 0 !important;
-       top: 0 !important;
-       right: 0 !important;
-}
-.oo-ui-menuLayout.oo-ui-menuLayout-showMenu.oo-ui-menuLayout-before > .oo-ui-menuLayout-menu {
-       height: auto !important;
-       bottom: 0;
-       left: 0;
-       top: 0;
-}
-.oo-ui-menuLayout.oo-ui-menuLayout-showMenu.oo-ui-menuLayout-before > .oo-ui-menuLayout-content {
-       top: 0 !important;
-       right: 0 !important;
-       bottom: 0 !important;
-}
-.oo-ui-panelLayout {
-       position: relative;
-}
-.oo-ui-panelLayout-scrollable {
-       overflow-y: auto;
-}
-.oo-ui-panelLayout-expanded {
-       position: absolute;
-       top: 0;
-       left: 0;
-       right: 0;
-       bottom: 0;
-}
-.oo-ui-panelLayout-padded {
-       padding: 1.25em;
-}
-.oo-ui-panelLayout-framed {
-       border-radius: 0.5em;
-       box-shadow: 0 0.25em 1em rgba(0, 0, 0, 0.25);
-}
-.oo-ui-panelLayout-padded.oo-ui-panelLayout-framed {
-       margin: 1em 0;
-}
-.oo-ui-stackLayout-continuous > .oo-ui-panelLayout {
-       display: block;
-       position: relative;
-}
-.oo-ui-horizontalLayout > .oo-ui-widget {
-       display: inline-block;
-       vertical-align: middle;
-}
-.oo-ui-horizontalLayout > .oo-ui-layout {
-       display: inline-block;
-}
-.oo-ui-horizontalLayout > .oo-ui-layout,
-.oo-ui-horizontalLayout > .oo-ui-widget {
-       margin-right: 0.5em;
-}
-.oo-ui-horizontalLayout > .oo-ui-layout:last-child,
-.oo-ui-horizontalLayout > .oo-ui-widget:last-child {
-       margin-right: 0;
-}
-.oo-ui-horizontalLayout > .oo-ui-layout {
-       margin-bottom: 0;
-}
-.oo-ui-popupTool .oo-ui-popupWidget-popup,
-.oo-ui-popupTool .oo-ui-popupWidget-anchor {
-       z-index: 4;
-}
-.oo-ui-popupTool .oo-ui-popupWidget {
-       /* @noflip */
-       margin-left: 1.25em;
-}
-.oo-ui-toolGroupTool > .oo-ui-popupToolGroup {
-       border: 0;
-       border-radius: 0;
-       margin: 0;
-}
-.oo-ui-toolGroupTool:first-child > .oo-ui-popupToolGroup {
-       border-top-left-radius: 0.3125em;
-       border-bottom-left-radius: 0.3125em;
-}
-.oo-ui-toolGroupTool:last-child > .oo-ui-popupToolGroup {
-       border-top-right-radius: 0.3125em;
-       border-bottom-right-radius: 0.3125em;
-}
-.oo-ui-toolGroupTool > .oo-ui-popupToolGroup > .oo-ui-popupToolGroup-handle {
-       height: 1.875em;
-       padding: 0.3125em;
-}
-.oo-ui-toolGroupTool > .oo-ui-popupToolGroup > .oo-ui-popupToolGroup-handle .oo-ui-iconElement-icon {
-       height: 1.875em;
-       width: 1.875em;
-}
-.oo-ui-toolGroupTool > .oo-ui-popupToolGroup.oo-ui-labelElement > .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
-       line-height: 2.1em;
-}
-.oo-ui-toolGroup {
-       display: inline-block;
-       vertical-align: middle;
-       margin: 0.375em;
-       border-radius: 0.3125em;
-       border: 1px solid transparent;
-       -webkit-transition: border-color 250ms ease;
-          -moz-transition: border-color 250ms ease;
-               transition: border-color 250ms ease;
-}
-.oo-ui-toolGroup-empty {
-       display: none;
-}
-.oo-ui-toolGroup .oo-ui-tool-link {
-       text-decoration: none;
-}
-.oo-ui-toolbar-narrow .oo-ui-toolGroup + .oo-ui-toolGroup {
-       margin-left: 0;
-}
-.oo-ui-toolGroup.oo-ui-widget-enabled:hover {
-       border-color: rgba(0, 0, 0, 0.1);
-}
-.oo-ui-toolGroup.oo-ui-widget-enabled .oo-ui-tool-link .oo-ui-tool-title {
-       color: #000000;
-}
-.oo-ui-barToolGroup > .oo-ui-iconElement-icon,
-.oo-ui-barToolGroup > .oo-ui-labelElement-label {
-       display: none;
-}
-.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool > .oo-ui-tool-link {
-       cursor: pointer;
-}
-.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool {
-       display: inline-block;
-       position: relative;
-       vertical-align: top;
-}
-.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool > .oo-ui-tool-link {
-       display: block;
-}
-.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool > .oo-ui-tool-link .oo-ui-tool-accel {
-       display: none;
-}
-.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-iconElement > .oo-ui-tool-link .oo-ui-iconElement-icon {
-       display: inline-block;
-       vertical-align: top;
-}
-.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-iconElement > .oo-ui-tool-link .oo-ui-tool-title {
-       display: none;
-}
-.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-iconElement.oo-ui-tool-with-label > .oo-ui-tool-link .oo-ui-tool-title {
-       display: inline;
-}
-.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-disabled > .oo-ui-tool-link {
-       outline: 0;
-       cursor: default;
-}
-.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool {
-       margin: -1px 0 -1px -1px;
-       border: 1px solid transparent;
-}
-.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool:first-child {
-       border-top-left-radius: 0.3125em;
-       border-bottom-left-radius: 0.3125em;
-}
-.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool:last-child {
-       margin-right: -1px;
-       border-top-right-radius: 0.3125em;
-       border-bottom-right-radius: 0.3125em;
-}
-.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool > .oo-ui-tool-link {
-       height: 1.875em;
-       padding: 0.3125em;
-}
-.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool > .oo-ui-tool-link .oo-ui-iconElement-icon {
-       height: 1.875em;
-       width: 1.875em;
-}
-.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool > .oo-ui-tool-link .oo-ui-tool-title {
-       line-height: 2.1em;
-}
-.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-enabled:hover {
-       border-color: rgba(0, 0, 0, 0.2);
-}
-.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-tool-active.oo-ui-widget-enabled {
-       border-color: rgba(0, 0, 0, 0.2);
-       box-shadow: inset 0 0.0875em 0.0875em 0 rgba(0, 0, 0, 0.07);
-       background: #f8fbfd;
-       filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#F1F7FB', endColorstr='#fff');
-       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0%, #f1f7fb), color-stop(100%, #ffffff));
-       background-image: -webkit-linear-gradient(top, #f1f7fb 0%, #ffffff 100%);
-       background-image:    -moz-linear-gradient(top, #f1f7fb 0%, #ffffff 100%);
-       background-image:      -o-linear-gradient(top, #f1f7fb 0%, #ffffff 100%);
-       background-image:         linear-gradient(to bottom, #f1f7fb 0%, #ffffff 100%);
-}
-.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-tool-active.oo-ui-widget-enabled + .oo-ui-tool-active.oo-ui-widget-enabled {
-       border-left-color: rgba(0, 0, 0, 0.1);
-}
-.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-disabled > .oo-ui-tool-link:focus {
-       outline: 0;
-}
-.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-disabled > .oo-ui-tool-link .oo-ui-tool-title {
-       color: #cccccc;
-}
-.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-disabled > .oo-ui-tool-link .oo-ui-iconElement-icon {
-       opacity: 0.2;
-}
-.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-enabled:hover > .oo-ui-tool-link .oo-ui-iconElement-icon {
-       opacity: 1;
-}
-.oo-ui-barToolGroup.oo-ui-widget-disabled > .oo-ui-toolGroup-tools > .oo-ui-tool:focus {
-       outline: 0;
-}
-.oo-ui-barToolGroup.oo-ui-widget-disabled > .oo-ui-toolGroup-tools > .oo-ui-tool > .oo-ui-tool-link:focus {
-       outline: 0;
-}
-.oo-ui-barToolGroup.oo-ui-widget-disabled > .oo-ui-toolGroup-tools > .oo-ui-tool > .oo-ui-tool-link .oo-ui-tool-title {
-       color: #cccccc;
-}
-.oo-ui-barToolGroup.oo-ui-widget-disabled > .oo-ui-toolGroup-tools > .oo-ui-tool > .oo-ui-tool-link .oo-ui-iconElement-icon {
-       opacity: 0.2;
-}
-.oo-ui-popupToolGroup {
-       position: relative;
-       height: 2.5em;
-       min-width: 2.5em;
-}
-.oo-ui-popupToolGroup-handle {
-       display: block;
-       cursor: pointer;
-}
-.oo-ui-popupToolGroup-handle .oo-ui-indicatorElement-indicator,
-.oo-ui-popupToolGroup-handle .oo-ui-iconElement-icon {
-       position: absolute;
-}
-.oo-ui-popupToolGroup.oo-ui-widget-disabled .oo-ui-popupToolGroup-handle {
-       outline: 0;
-       cursor: default;
-}
-.oo-ui-popupToolGroup .oo-ui-toolGroup-tools {
-       display: none;
-       position: absolute;
-       z-index: 4;
-}
-.oo-ui-popupToolGroup-active.oo-ui-widget-enabled > .oo-ui-toolGroup-tools {
-       display: block;
-}
-.oo-ui-popupToolGroup-left > .oo-ui-toolGroup-tools {
-       left: 0;
-}
-.oo-ui-popupToolGroup-right > .oo-ui-toolGroup-tools {
-       right: 0;
-}
-.oo-ui-popupToolGroup .oo-ui-tool-link {
-       display: table;
-       width: 100%;
-       vertical-align: middle;
-       white-space: nowrap;
-}
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-iconElement-icon,
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel,
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title {
-       display: table-cell;
-       vertical-align: middle;
-}
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel {
-       text-align: right;
-}
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel:not(:empty) {
-       padding-left: 3em;
-}
-.oo-ui-toolbar-narrow .oo-ui-popupToolGroup {
-       min-width: 1.875em;
-}
-.oo-ui-popupToolGroup.oo-ui-iconElement {
-       min-width: 3.125em;
-}
-.oo-ui-toolbar-narrow .oo-ui-popupToolGroup.oo-ui-iconElement {
-       min-width: 2.5em;
-}
-.oo-ui-popupToolGroup.oo-ui-indicatorElement.oo-ui-iconElement {
-       min-width: 4.375em;
-}
-.oo-ui-toolbar-narrow .oo-ui-popupToolGroup.oo-ui-indicatorElement.oo-ui-iconElement {
-       min-width: 3.75em;
-}
-.oo-ui-popupToolGroup.oo-ui-labelElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
-       line-height: 2.6em;
-       margin: 0 1em;
-}
-.oo-ui-toolbar-narrow .oo-ui-popupToolGroup.oo-ui-labelElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
-       margin: 0 0.5em;
-}
-.oo-ui-popupToolGroup.oo-ui-labelElement.oo-ui-iconElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
-       margin-left: 3em;
-}
-.oo-ui-toolbar-narrow .oo-ui-popupToolGroup.oo-ui-labelElement.oo-ui-iconElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
-       margin-left: 2.5em;
-}
-.oo-ui-popupToolGroup.oo-ui-labelElement.oo-ui-indicatorElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
-       margin-right: 2.25em;
-}
-.oo-ui-toolbar-narrow .oo-ui-popupToolGroup.oo-ui-labelElement.oo-ui-indicatorElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
-       margin-right: 1.75em;
-}
-.oo-ui-popupToolGroup-handle .oo-ui-indicatorElement-indicator {
-       width: 0.9375em;
-       height: 0.9375em;
-       margin: 0.78125em;
-       top: 0;
-       right: 0;
-}
-.oo-ui-toolbar-narrow .oo-ui-popupToolGroup-handle .oo-ui-indicatorElement-indicator {
-       right: -0.3125em;
-}
-.oo-ui-popupToolGroup-handle .oo-ui-iconElement-icon {
-       width: 1.875em;
-       height: 1.875em;
-       margin: 0.3125em;
-       top: 0;
-       left: 0.3125em;
-}
-.oo-ui-toolbar-narrow .oo-ui-popupToolGroup-handle .oo-ui-iconElement-icon {
-       left: 0;
-}
-.oo-ui-popupToolGroup-header {
-       line-height: 2.6em;
-       margin: 0 0.6em;
-       font-weight: bold;
-}
-.oo-ui-popupToolGroup-active.oo-ui-widget-enabled {
-       border-bottom-left-radius: 0;
-       border-bottom-right-radius: 0;
-       box-shadow: inset 0 0.0875em 0.0875em 0 rgba(0, 0, 0, 0.07);
-       background: #f8fbfd;
-       filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#F1F7FB', endColorstr='#fff');
-       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0%, #f1f7fb), color-stop(100%, #ffffff));
-       background-image: -webkit-linear-gradient(top, #f1f7fb 0%, #ffffff 100%);
-       background-image:    -moz-linear-gradient(top, #f1f7fb 0%, #ffffff 100%);
-       background-image:      -o-linear-gradient(top, #f1f7fb 0%, #ffffff 100%);
-       background-image:         linear-gradient(to bottom, #f1f7fb 0%, #ffffff 100%);
-}
-.oo-ui-popupToolGroup .oo-ui-toolGroup-tools {
-       top: 2.5em;
-       margin: 0 -1px;
-       border: 1px solid #cccccc;
-       background-color: white;
-       box-shadow: 0 0.3125em 1.25em rgba(0, 0, 0, 0.25);
-}
-.oo-ui-popupToolGroup .oo-ui-tool-link {
-       padding: 0.3125em 0 0.3125em 0.3125em;
-}
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-iconElement-icon {
-       height: 1.875em;
-       width: 1.875em;
-       min-width: 1.875em;
-}
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title {
-       padding-left: 0.5em;
-}
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel,
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title {
-       line-height: 2em;
-}
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel {
-       color: #888888;
-}
-.oo-ui-listToolGroup .oo-ui-tool {
-       display: block;
-       -webkit-box-sizing: border-box;
-          -moz-box-sizing: border-box;
-               box-sizing: border-box;
-}
-.oo-ui-listToolGroup .oo-ui-tool-link {
-       cursor: pointer;
-}
-.oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link {
-       cursor: default;
-}
-.oo-ui-listToolGroup .oo-ui-toolGroup-tools {
-       padding: 0.3125em;
-}
-.oo-ui-listToolGroup.oo-ui-popupToolGroup-active {
-       border-color: rgba(0, 0, 0, 0.2);
-}
-.oo-ui-listToolGroup .oo-ui-tool {
-       border: 1px solid transparent;
-       margin: -1px 0;
-       padding: 0 0.625em 0 0;
-}
-.oo-ui-listToolGroup .oo-ui-tool-active.oo-ui-widget-enabled {
-       border-color: rgba(0, 0, 0, 0.1);
-       box-shadow: inset 0 0.0875em 0.0875em 0 rgba(0, 0, 0, 0.07);
-       background: #f8fbfd;
-       filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#F1F7FB', endColorstr='#fff');
-       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0%, #f1f7fb), color-stop(100%, #ffffff));
-       background-image: -webkit-linear-gradient(top, #f1f7fb 0%, #ffffff 100%);
-       background-image:    -moz-linear-gradient(top, #f1f7fb 0%, #ffffff 100%);
-       background-image:      -o-linear-gradient(top, #f1f7fb 0%, #ffffff 100%);
-       background-image:         linear-gradient(to bottom, #f1f7fb 0%, #ffffff 100%);
-}
-.oo-ui-listToolGroup .oo-ui-tool-active.oo-ui-widget-enabled + .oo-ui-tool-active.oo-ui-widget-enabled {
-       border-top-color: rgba(0, 0, 0, 0.1);
-}
-.oo-ui-listToolGroup .oo-ui-tool-active.oo-ui-widget-enabled:hover {
-       border-color: rgba(0, 0, 0, 0.2);
-}
-.oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-enabled:hover {
-       border-color: rgba(0, 0, 0, 0.2);
-}
-.oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-enabled:hover .oo-ui-tool-link .oo-ui-iconElement-icon {
-       opacity: 1;
-}
-.oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-tool-title {
-       color: #cccccc;
-}
-.oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-tool-accel {
-       color: #dddddd;
-}
-.oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-iconElement-icon {
-       opacity: 0.2;
-}
-.oo-ui-listToolGroup.oo-ui-widget-disabled {
-       color: #cccccc;
-}
-.oo-ui-listToolGroup.oo-ui-widget-disabled .oo-ui-indicatorElement-indicator,
-.oo-ui-listToolGroup.oo-ui-widget-disabled .oo-ui-iconElement-icon {
-       opacity: 0.2;
-}
-.oo-ui-menuToolGroup {
-       border-color: rgba(0, 0, 0, 0.1);
-}
-.oo-ui-menuToolGroup .oo-ui-tool {
-       display: block;
-}
-.oo-ui-menuToolGroup .oo-ui-tool-link {
-       cursor: pointer;
-}
-.oo-ui-menuToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link {
-       cursor: default;
-}
-.oo-ui-menuToolGroup .oo-ui-popupToolGroup-handle {
-       min-width: 10em;
-}
-.oo-ui-toolbar-narrow .oo-ui-menuToolGroup .oo-ui-popupToolGroup-handle {
-       min-width: 8.125em;
-}
-.oo-ui-menuToolGroup .oo-ui-toolGroup-tools {
-       padding: 0.3125em 0 0.3125em 0;
-}
-.oo-ui-menuToolGroup.oo-ui-widget-enabled:hover {
-       border-color: rgba(0, 0, 0, 0.2);
-}
-.oo-ui-menuToolGroup.oo-ui-popupToolGroup-active {
-       border-color: rgba(0, 0, 0, 0.25);
-}
-.oo-ui-menuToolGroup .oo-ui-tool {
-       padding: 0 1.25em 0 0.3125em;
-}
-.oo-ui-menuToolGroup .oo-ui-tool-link .oo-ui-iconElement-icon {
-       background-image: none;
-}
-.oo-ui-menuToolGroup .oo-ui-tool-active .oo-ui-tool-link .oo-ui-iconElement-icon {
-       background-image: url("themes/apex/images/icons/check.png");
-       background-image: -webkit-linear-gradient(transparent, transparent), /* @embed */ url("themes/apex/images/icons/check.svg");
-       background-image:         linear-gradient(transparent, transparent), /* @embed */ url("themes/apex/images/icons/check.svg");
-       background-image:      -o-linear-gradient(transparent, transparent), url("themes/apex/images/icons/check.png");
-       background-size: contain;
-       background-position: center center;
-       background-repeat: no-repeat;
-}
-.oo-ui-menuToolGroup .oo-ui-tool.oo-ui-widget-enabled:hover {
-       background-color: #e1f3ff;
-}
-.oo-ui-menuToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-tool-title {
-       color: #cccccc;
-}
-.oo-ui-menuToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-iconElement-icon {
-       opacity: 0.2;
-}
-.oo-ui-menuToolGroup.oo-ui-widget-disabled {
-       color: #cccccc;
-       border-color: rgba(0, 0, 0, 0.05);
-}
-.oo-ui-menuToolGroup.oo-ui-widget-disabled .oo-ui-indicatorElement-indicator,
-.oo-ui-menuToolGroup.oo-ui-widget-disabled .oo-ui-iconElement-icon {
-       opacity: 0.2;
-}
-.oo-ui-toolbar {
-       clear: both;
-}
-.oo-ui-toolbar-bar {
-       line-height: 1em;
-       position: relative;
-}
-.oo-ui-toolbar-actions {
-       float: right;
-}
-.oo-ui-toolbar-actions .oo-ui-toolbar {
-       display: inline-block;
-}
-.oo-ui-toolbar-tools {
-       display: inline;
-       white-space: nowrap;
-}
-.oo-ui-toolbar-narrow .oo-ui-toolbar-tools {
-       white-space: normal;
-}
-.oo-ui-toolbar-tools .oo-ui-tool {
-       white-space: normal;
-}
-.oo-ui-toolbar-tools,
-.oo-ui-toolbar-actions,
-.oo-ui-toolbar-shadow {
-       -webkit-touch-callout: none;
-       -webkit-user-select: none;
-          -moz-user-select: none;
-           -ms-user-select: none;
-               user-select: none;
-}
-.oo-ui-toolbar-actions .oo-ui-popupWidget {
-       -webkit-touch-callout: default;
-       -webkit-user-select: all;
-          -moz-user-select: all;
-           -ms-user-select: all;
-               user-select: all;
-}
-.oo-ui-toolbar-shadow {
-       background-position: left top;
-       background-repeat: repeat-x;
-       position: absolute;
-       width: 100%;
-       pointer-events: none;
-}
-.oo-ui-toolbar-bar {
-       border-bottom: 1px solid #cccccc;
-       background: #f8fbfd;
-       filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#fff', endColorstr='#F1F7FB');
-       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0%, #ffffff), color-stop(100%, #f1f7fb));
-       background-image: -webkit-linear-gradient(top, #ffffff 0%, #f1f7fb 100%);
-       background-image:    -moz-linear-gradient(top, #ffffff 0%, #f1f7fb 100%);
-       background-image:      -o-linear-gradient(top, #ffffff 0%, #f1f7fb 100%);
-       background-image:         linear-gradient(to bottom, #ffffff 0%, #f1f7fb 100%);
-}
-.oo-ui-toolbar-bar .oo-ui-toolbar-bar {
-       border: none;
-       background: none;
-}
-.oo-ui-toolbar-actions > .oo-ui-buttonElement-framed,
-.oo-ui-toolbar-actions > .oo-ui-buttonElement-framed:last-child {
-       margin-top: 0.4em;
-       margin-bottom: 0.4em;
-       margin-right: 0.5em;
-}
-.oo-ui-toolbar-actions > .oo-ui-buttonElement-frameless.oo-ui-labelElement,
-.oo-ui-toolbar-actions > .oo-ui-buttonElement-frameless:last-child.oo-ui-labelElement {
-       margin: 0;
-}
-.oo-ui-toolbar-actions > .oo-ui-buttonElement-frameless.oo-ui-labelElement > .oo-ui-buttonElement-button,
-.oo-ui-toolbar-actions > .oo-ui-buttonElement-frameless:last-child.oo-ui-labelElement > .oo-ui-buttonElement-button {
-       margin: 0;
-       padding: 0 0.3125em;
-}
-.oo-ui-toolbar-actions > .oo-ui-buttonElement-frameless.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label,
-.oo-ui-toolbar-actions > .oo-ui-buttonElement-frameless:last-child.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
-       margin: 0 1em;
-       line-height: 3.40625em;
-}
-.oo-ui-toolbar-shadow {
-       background-image: /* @embed */ url(themes/apex/images/toolbar-shadow.png);
-       bottom: -9px;
-       height: 9px;
-       opacity: 0.5;
-       -webkit-transition: opacity 500ms ease;
-          -moz-transition: opacity 500ms ease;
-               transition: opacity 500ms ease;
-}
-.oo-ui-optionWidget {
-       position: relative;
-       display: block;
-       padding: 0.25em 0.5em;
-       border: none;
-}
-.oo-ui-optionWidget.oo-ui-widget-enabled {
-       cursor: pointer;
-}
-.oo-ui-optionWidget.oo-ui-labelElement .oo-ui-labelElement-label {
-       display: block;
-       white-space: nowrap;
-       text-overflow: ellipsis;
-       overflow: hidden;
-}
-.oo-ui-optionWidget-highlighted {
-       background-color: #e1f3ff;
-}
-.oo-ui-optionWidget .oo-ui-labelElement-label {
-       line-height: 1.5em;
-}
-.oo-ui-selectWidget-depressed .oo-ui-optionWidget-selected {
-       background-color: #a7dcff;
-}
-.oo-ui-selectWidget-pressed .oo-ui-optionWidget-pressed,
-.oo-ui-selectWidget-pressed .oo-ui-optionWidget-pressed.oo-ui-optionWidget-highlighted,
-.oo-ui-selectWidget-pressed .oo-ui-optionWidget-pressed.oo-ui-optionWidget-highlighted.oo-ui-optionWidget-selected {
-       background-color: #a7dcff;
-}
-.oo-ui-optionWidget.oo-ui-widget-disabled {
-       color: #cccccc;
-}
-.oo-ui-decoratedOptionWidget {
-       padding: 0.5em 2em 0.5em 3em;
-}
-.oo-ui-decoratedOptionWidget .oo-ui-iconElement-icon,
-.oo-ui-decoratedOptionWidget .oo-ui-indicatorElement-indicator {
-       position: absolute;
-}
-.oo-ui-decoratedOptionWidget.oo-ui-iconElement .oo-ui-iconElement-icon,
-.oo-ui-decoratedOptionWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
-       top: 0;
-       height: 100%;
-}
-.oo-ui-decoratedOptionWidget.oo-ui-iconElement .oo-ui-iconElement-icon {
-       width: 1.875em;
-       left: 0.5em;
-}
-.oo-ui-decoratedOptionWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
-       width: 0.9375em;
-       right: 0.5em;
-}
-.oo-ui-decoratedOptionWidget.oo-ui-widget-disabled .oo-ui-iconElement-icon,
-.oo-ui-decoratedOptionWidget.oo-ui-widget-disabled .oo-ui-indicatorElement-indicator {
-       opacity: 0.2;
-}
-.oo-ui-buttonSelectWidget {
-       display: inline-block;
-       white-space: nowrap;
-       border-radius: 0.3em;
-       margin-right: 0.5em;
-}
-.oo-ui-buttonSelectWidget:last-child {
-       margin-right: 0;
-}
-.oo-ui-buttonSelectWidget .oo-ui-buttonOptionWidget .oo-ui-buttonElement-button {
-       border-radius: 0;
-       margin-left: -1px;
-}
-.oo-ui-buttonSelectWidget .oo-ui-buttonOptionWidget:first-child .oo-ui-buttonElement-button {
-       border-bottom-left-radius: 0.3em;
-       border-top-left-radius: 0.3em;
-       margin-left: 0;
-}
-.oo-ui-buttonSelectWidget .oo-ui-buttonOptionWidget:last-child .oo-ui-buttonElement-button {
-       border-bottom-right-radius: 0.3em;
-       border-top-right-radius: 0.3em;
-}
-.oo-ui-radioSelectWidget {
-       padding: 0.75em 0 0.5em 0;
-}
-.oo-ui-buttonOptionWidget {
-       display: inline-block;
-       padding: 0;
-       background-color: transparent;
-}
-.oo-ui-buttonOptionWidget .oo-ui-buttonElement-button {
-       position: relative;
-}
-.oo-ui-buttonOptionWidget.oo-ui-iconElement .oo-ui-iconElement-icon,
-.oo-ui-buttonOptionWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
-       position: static;
-       display: inline-block;
-       vertical-align: middle;
-}
-.oo-ui-buttonOptionWidget .oo-ui-buttonElement-button {
-       height: 1.875em;
-}
-.oo-ui-buttonOptionWidget.oo-ui-iconElement .oo-ui-iconElement-icon {
-       margin-top: 0;
-}
-.oo-ui-buttonOptionWidget.oo-ui-optionWidget-selected,
-.oo-ui-buttonOptionWidget.oo-ui-optionWidget-pressed,
-.oo-ui-buttonOptionWidget.oo-ui-optionWidget-highlighted {
-       background-color: transparent;
-}
-.oo-ui-radioOptionWidget {
-       cursor: default;
-       padding: 0;
-       background-color: transparent;
-}
-.oo-ui-radioOptionWidget .oo-ui-radioInputWidget,
-.oo-ui-radioOptionWidget.oo-ui-labelElement .oo-ui-labelElement-label {
-       display: inline-block;
-       vertical-align: middle;
-}
-.oo-ui-radioOptionWidget.oo-ui-optionWidget-selected,
-.oo-ui-radioOptionWidget.oo-ui-optionWidget-pressed,
-.oo-ui-radioOptionWidget.oo-ui-optionWidget-highlighted {
-       background-color: transparent;
-}
-.oo-ui-radioOptionWidget.oo-ui-labelElement .oo-ui-labelElement-label {
-       padding-left: 0.5em;
-}
-.oo-ui-radioOptionWidget .oo-ui-radioInputWidget {
-       margin-right: 0;
-}
-.oo-ui-labelWidget {
-       display: inline-block;
-       padding: 0.5em 0;
-}
-.oo-ui-iconWidget {
-       display: inline-block;
-       vertical-align: middle;
-       line-height: 2.5em;
-       height: 1.875em;
-       width: 1.875em;
-}
-.oo-ui-iconWidget.oo-ui-widget-disabled {
-       opacity: 0.2;
-}
-.oo-ui-indicatorWidget {
-       display: inline-block;
-       vertical-align: middle;
-       line-height: 2.5em;
-       height: 0.9375em;
-       width: 0.9375em;
-       margin: 0.46875em;
-}
-.oo-ui-indicatorWidget.oo-ui-widget-disabled {
-       opacity: 0.2;
-}
-.oo-ui-buttonWidget {
-       display: inline-block;
-       vertical-align: middle;
-       margin-right: 0.5em;
-}
-.oo-ui-buttonWidget:last-child {
-       margin-right: 0;
-}
-.oo-ui-buttonGroupWidget {
-       display: inline-block;
-       white-space: nowrap;
-       border-radius: 0.3em;
-       margin-right: 0.5em;
-}
-.oo-ui-buttonGroupWidget:last-child {
-       margin-right: 0;
-}
-.oo-ui-buttonGroupWidget .oo-ui-buttonElement {
-       margin-right: 0;
-}
-.oo-ui-buttonGroupWidget .oo-ui-buttonElement:last-child {
-       margin-right: 0;
-}
-.oo-ui-buttonGroupWidget .oo-ui-buttonElement-framed .oo-ui-buttonElement-button {
-       border-radius: 0;
-       margin-left: -1px;
-}
-.oo-ui-buttonGroupWidget .oo-ui-buttonElement-framed:first-child .oo-ui-buttonElement-button {
-       border-bottom-left-radius: 0.3em;
-       border-top-left-radius: 0.3em;
-       margin-left: 0;
-}
-.oo-ui-buttonGroupWidget .oo-ui-buttonElement-framed:last-child .oo-ui-buttonElement-button {
-       border-bottom-right-radius: 0.3em;
-       border-top-right-radius: 0.3em;
-}
-.oo-ui-toggleButtonWidget {
-       display: inline-block;
-       vertical-align: middle;
-       margin-right: 0.5em;
-}
-.oo-ui-toggleButtonWidget:last-child {
-       margin-right: 0;
-}
-.oo-ui-toggleSwitchWidget {
-       position: relative;
-       display: inline-block;
-       vertical-align: middle;
-       overflow: hidden;
-       -webkit-box-sizing: border-box;
-          -moz-box-sizing: border-box;
-               box-sizing: border-box;
-       -webkit-transform: translateZ(0);
-          -moz-transform: translateZ(0);
-           -ms-transform: translateZ(0);
-               transform: translateZ(0);
-       height: 2em;
-       width: 4em;
-       border-radius: 1em;
-       box-shadow: 0 0 0 white, inset 0 0.1em 0.2em #dddddd;
-       border: 1px solid #cccccc;
-       margin-right: 0.5em;
-       background: #eeeeee;
-       filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#ddd', endColorstr='#fff');
-       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0%, #dddddd), color-stop(100%, #ffffff));
-       background-image: -webkit-linear-gradient(top, #dddddd 0%, #ffffff 100%);
-       background-image:    -moz-linear-gradient(top, #dddddd 0%, #ffffff 100%);
-       background-image:      -o-linear-gradient(top, #dddddd 0%, #ffffff 100%);
-       background-image:         linear-gradient(to bottom, #dddddd 0%, #ffffff 100%);
-}
-.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled {
-       cursor: pointer;
-}
-.oo-ui-toggleSwitchWidget-grip {
-       position: absolute;
-       display: block;
-       -webkit-box-sizing: border-box;
-          -moz-box-sizing: border-box;
-               box-sizing: border-box;
-}
-.oo-ui-toggleSwitchWidget .oo-ui-toggleSwitchWidget-glow {
-       position: absolute;
-       top: 0;
-       bottom: 0;
-       right: 0;
-       left: 0;
-       -webkit-touch-callout: none;
-       -webkit-user-select: none;
-          -moz-user-select: none;
-           -ms-user-select: none;
-               user-select: none;
-}
-.oo-ui-toggleWidget-off .oo-ui-toggleSwitchWidget-glow {
-       display: none;
-}
-.oo-ui-toggleSwitchWidget:last-child {
-       margin-right: 0;
-}
-.oo-ui-toggleSwitchWidget.oo-ui-widget-disabled {
-       opacity: 0.5;
-}
-.oo-ui-toggleSwitchWidget-grip {
-       top: 0.25em;
-       left: 0.25em;
-       width: 1.5em;
-       height: 1.5em;
-       margin-top: -1px;
-       border-radius: 1em;
-       box-shadow: 0 0.1em 0.25em rgba(0, 0, 0, 0.1);
-       border: 1px #c9c9c9 solid;
-       -webkit-transition: left 250ms ease, margin-left 250ms ease;
-          -moz-transition: left 250ms ease, margin-left 250ms ease;
-               transition: left 250ms ease, margin-left 250ms ease;
-       background: #eeeeee;
-       filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#fff', endColorstr='#ddd');
-       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0%, #ffffff), color-stop(100%, #dddddd));
-       background-image: -webkit-linear-gradient(top, #ffffff 0%, #dddddd 100%);
-       background-image:    -moz-linear-gradient(top, #ffffff 0%, #dddddd 100%);
-       background-image:      -o-linear-gradient(top, #ffffff 0%, #dddddd 100%);
-       background-image:         linear-gradient(to bottom, #ffffff 0%, #dddddd 100%);
-}
-.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:hover,
-.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:hover .oo-ui-toggleSwitchWidget-grip {
-       border-color: #aaaaaa;
-}
-.oo-ui-toggleSwitchWidget .oo-ui-toggleSwitchWidget-glow {
-       border-radius: 1em;
-       box-shadow: inset 0 1px 4px 0 rgba(0, 0, 0, 0.07);
-       -webkit-transition: opacity 250ms ease;
-          -moz-transition: opacity 250ms ease;
-               transition: opacity 250ms ease;
-       background: #cde7f4;
-       filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#b0d9ee', endColorstr='#eaf4fa');
-       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0%, #b0d9ee), color-stop(100%, #eaf4fa));
-       background-image: -webkit-linear-gradient(top, #b0d9ee 0%, #eaf4fa 100%);
-       background-image:    -moz-linear-gradient(top, #b0d9ee 0%, #eaf4fa 100%);
-       background-image:      -o-linear-gradient(top, #b0d9ee 0%, #eaf4fa 100%);
-       background-image:         linear-gradient(to bottom, #b0d9ee 0%, #eaf4fa 100%);
-}
-.oo-ui-toggleWidget-on .oo-ui-toggleSwitchWidget-glow {
-       opacity: 1;
-}
-.oo-ui-toggleWidget-on .oo-ui-toggleSwitchWidget-grip {
-       left: 2.25em;
-       margin-left: -2px;
-}
-.oo-ui-toggleWidget-off .oo-ui-toggleSwitchWidget-glow {
-       display: block;
-       opacity: 0;
-}
-.oo-ui-toggleWidget-off .oo-ui-toggleSwitchWidget-grip {
-       left: 0.25em;
-       margin-left: 0;
-}
-.oo-ui-progressBarWidget {
-       max-width: 50em;
-       background-color: #ffffff;
-       border: 1px solid #cccccc;
-       border-radius: 0.25em;
-       overflow: hidden;
-}
-.oo-ui-progressBarWidget-bar {
-       height: 1em;
-       border-right: 1px solid #cccccc;
-       -webkit-transition: width 250ms ease, margin-left 250ms ease;
-          -moz-transition: width 250ms ease, margin-left 250ms ease;
-               transition: width 250ms ease, margin-left 250ms ease;
-       background: #cde7f4;
-       filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#eaf4fa', endColorstr='#b0d9ee');
-       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0%, #eaf4fa), color-stop(100%, #b0d9ee));
-       background-image: -webkit-linear-gradient(top, #eaf4fa 0%, #b0d9ee 100%);
-       background-image:    -moz-linear-gradient(top, #eaf4fa 0%, #b0d9ee 100%);
-       background-image:      -o-linear-gradient(top, #eaf4fa 0%, #b0d9ee 100%);
-       background-image:         linear-gradient(to bottom, #eaf4fa 0%, #b0d9ee 100%);
-}
-.oo-ui-progressBarWidget-indeterminate .oo-ui-progressBarWidget-bar {
-       -webkit-animation: oo-ui-progressBarWidget-slide 2s infinite linear;
-          -moz-animation: oo-ui-progressBarWidget-slide 2s infinite linear;
-               animation: oo-ui-progressBarWidget-slide 2s infinite linear;
-       width: 40%;
-       margin-left: -10%;
-       border-left: 1px solid #a6cee1;
-}
-.oo-ui-progressBarWidget.oo-ui-widget-disabled {
-       opacity: 0.6;
-}
-.oo-ui-actionWidget.oo-ui-pendingElement-pending {
-       background-image: /* @embed */ url(themes/apex/images/textures/pending.gif);
-}
-.oo-ui-popupWidget {
-       position: absolute;
-       /* @noflip */
-       left: 0;
-}
-.oo-ui-popupWidget-popup {
-       position: relative;
-       overflow: hidden;
-       z-index: 1;
-}
-.oo-ui-popupWidget-anchor {
-       display: none;
-       z-index: 1;
-}
-.oo-ui-popupWidget-anchored .oo-ui-popupWidget-anchor {
-       display: block;
-       position: absolute;
-       top: 0;
-       /* @noflip */
-       left: 0;
-       background-repeat: no-repeat;
-}
-.oo-ui-popupWidget-head {
-       -webkit-touch-callout: none;
-       -webkit-user-select: none;
-          -moz-user-select: none;
-           -ms-user-select: none;
-               user-select: none;
-}
-.oo-ui-popupWidget-head > .oo-ui-buttonWidget {
-       float: right;
-}
-.oo-ui-popupWidget-head > .oo-ui-labelElement-label {
-       float: left;
-       cursor: default;
-}
-.oo-ui-popupWidget-body {
-       clear: both;
-       overflow: hidden;
-}
-.oo-ui-popupWidget-popup {
-       background-color: #ffffff;
-       border: 1px solid #cccccc;
-       border-radius: 0.25em;
-       box-shadow: 0 0.15em 0.5em 0 rgba(0, 0, 0, 0.2);
-}
-.oo-ui-popupWidget-anchored .oo-ui-popupWidget-popup {
-       margin-top: 6px;
-}
-.oo-ui-popupWidget-anchored .oo-ui-popupWidget-anchor:before,
-.oo-ui-popupWidget-anchored .oo-ui-popupWidget-anchor:after {
-       content: "";
-       position: absolute;
-       width: 0;
-       height: 0;
-       border-style: solid;
-       border-color: transparent;
-       border-top: 0;
-}
-.oo-ui-popupWidget-anchored .oo-ui-popupWidget-anchor:before {
-       bottom: -7px;
-       left: -6px;
-       border-bottom-color: #aaaaaa;
-       border-width: 7px;
-}
-.oo-ui-popupWidget-anchored .oo-ui-popupWidget-anchor:after {
-       bottom: -7px;
-       left: -5px;
-       border-bottom-color: #ffffff;
-       border-width: 6px;
-}
-.oo-ui-popupWidget-transitioning .oo-ui-popupWidget-popup {
-       -webkit-transition: width 100ms ease, height 100ms ease, left 100ms ease;
-          -moz-transition: width 100ms ease, height 100ms ease, left 100ms ease;
-               transition: width 100ms ease, height 100ms ease, left 100ms ease;
-}
-.oo-ui-popupWidget-head {
-       height: 2.5em;
-}
-.oo-ui-popupWidget-head > .oo-ui-buttonWidget {
-       margin: 0.25em;
-}
-.oo-ui-popupWidget-head > .oo-ui-labelElement-label {
-       margin: 0.75em 1em;
-}
-.oo-ui-popupWidget-body-padded {
-       padding: 0 1em;
-}
-.oo-ui-popupButtonWidget {
-       position: relative;
-}
-.oo-ui-popupButtonWidget .oo-ui-popupWidget {
-       position: absolute;
-       cursor: auto;
-}
-.oo-ui-popupButtonWidget.oo-ui-buttonElement-frameless > .oo-ui-popupWidget {
-       /* @noflip */
-       left: 1em;
-}
-.oo-ui-popupButtonWidget.oo-ui-buttonElement-framed > .oo-ui-popupWidget {
-       /* @noflip */
-       left: 1.25em;
-}
-.oo-ui-inputWidget {
-       margin-right: 0.5em;
-}
-.oo-ui-inputWidget:last-child {
-       margin-right: 0;
-}
-.oo-ui-buttonInputWidget {
-       display: inline-block;
-       vertical-align: middle;
-}
-.oo-ui-buttonInputWidget > button,
-.oo-ui-buttonInputWidget > input {
-       border: 0;
-       padding: 0;
-       background-color: transparent;
-}
-.oo-ui-dropdownInputWidget {
-       position: relative;
-       vertical-align: middle;
-       -webkit-box-sizing: border-box;
-          -moz-box-sizing: border-box;
-               box-sizing: border-box;
-       width: 100%;
-       max-width: 50em;
-}
-.oo-ui-dropdownInputWidget select {
-       display: inline-block;
-       width: 100%;
-       resize: none;
-       -webkit-box-sizing: border-box;
-          -moz-box-sizing: border-box;
-               box-sizing: border-box;
-}
-.oo-ui-dropdownInputWidget select {
-       background-color: #ffffff;
-       height: 2.5em;
-       padding: 0.5em;
-       font-size: inherit;
-       font-family: inherit;
-       border: 1px solid rgba(0, 0, 0, 0.1);
-       border-radius: 0.25em;
-}
-.oo-ui-dropdownInputWidget.oo-ui-widget-enabled select:hover,
-.oo-ui-dropdownInputWidget.oo-ui-widget-enabled select:focus {
-       border-color: rgba(0, 0, 0, 0.2);
-       outline: none;
-}
-.oo-ui-dropdownInputWidget.oo-ui-widget-disabled select {
-       color: #cccccc;
-       border-color: #dddddd;
-       background-color: #f3f3f3;
-}
-.oo-ui-radioSelectInputWidget .oo-ui-fieldLayout {
-       margin-bottom: 0;
-}
-.oo-ui-textInputWidget {
-       position: relative;
-       vertical-align: middle;
-       -webkit-box-sizing: border-box;
-          -moz-box-sizing: border-box;
-               box-sizing: border-box;
-       width: 100%;
-       max-width: 50em;
-}
-.oo-ui-textInputWidget input,
-.oo-ui-textInputWidget textarea {
-       display: inline-block;
-       width: 100%;
-       resize: none;
-       -webkit-box-sizing: border-box;
-          -moz-box-sizing: border-box;
-               box-sizing: border-box;
-}
-.oo-ui-textInputWidget textarea {
-       overflow: auto;
-}
-.oo-ui-textInputWidget input[type="search"] {
-       -webkit-appearance: none;
-}
-.oo-ui-textInputWidget input[type="search"]::-ms-clear {
-       display: none;
-}
-.oo-ui-textInputWidget input[type="search"]::-ms-reveal {
-       display: none;
-}
-.oo-ui-textInputWidget input[type="search"]::-webkit-search-decoration,
-.oo-ui-textInputWidget input[type="search"]::-webkit-search-cancel-button,
-.oo-ui-textInputWidget input[type="search"]::-webkit-search-results-button,
-.oo-ui-textInputWidget input[type="search"]::-webkit-search-results-decoration {
-       display: none;
-}
-.oo-ui-textInputWidget > .oo-ui-iconElement-icon,
-.oo-ui-textInputWidget > .oo-ui-indicatorElement-indicator,
-.oo-ui-textInputWidget > .oo-ui-labelElement-label {
-       display: none;
-}
-.oo-ui-textInputWidget.oo-ui-iconElement > .oo-ui-iconElement-icon,
-.oo-ui-textInputWidget.oo-ui-indicatorElement > .oo-ui-indicatorElement-indicator {
-       display: block;
-       position: absolute;
-       top: 0;
-       height: 100%;
-       -webkit-touch-callout: none;
-       -webkit-user-select: none;
-          -moz-user-select: none;
-           -ms-user-select: none;
-               user-select: none;
-}
-.oo-ui-textInputWidget.oo-ui-widget-enabled > .oo-ui-iconElement-icon,
-.oo-ui-textInputWidget.oo-ui-widget-enabled > .oo-ui-indicatorElement-indicator {
-       cursor: text;
-}
-.oo-ui-textInputWidget.oo-ui-widget-enabled.oo-ui-textInputWidget-type-search > .oo-ui-indicatorElement-indicator {
-       cursor: pointer;
-}
-.oo-ui-textInputWidget.oo-ui-labelElement > .oo-ui-labelElement-label {
-       display: block;
-}
-.oo-ui-textInputWidget > .oo-ui-iconElement-icon {
-       left: 0;
-}
-.oo-ui-textInputWidget > .oo-ui-indicatorElement-indicator {
-       right: 0;
-}
-.oo-ui-textInputWidget > .oo-ui-labelElement-label {
-       position: absolute;
-       top: 0;
-}
-.oo-ui-textInputWidget-labelPosition-after > .oo-ui-labelElement-label {
-       right: 0;
-}
-.oo-ui-textInputWidget-labelPosition-before > .oo-ui-labelElement-label {
-       left: 0;
-}
-.oo-ui-textInputWidget input,
-.oo-ui-textInputWidget textarea {
-       padding: 0.5em;
-       line-height: 1.275em;
-       font-size: inherit;
-       font-family: inherit;
-       background-color: #ffffff;
-       color: black;
-       border: 1px solid #cccccc;
-       box-shadow: 0 0 0 white, inset 0 0.1em 0.2em #dddddd;
-       border-radius: 0.25em;
-       -webkit-transition: border-color 250ms ease, box-shadow 250ms ease;
-          -moz-transition: border-color 250ms ease, box-shadow 250ms ease;
-               transition: border-color 250ms ease, box-shadow 250ms ease;
-}
-.oo-ui-textInputWidget input.oo-ui-pendingElement-pending,
-.oo-ui-textInputWidget textarea.oo-ui-pendingElement-pending {
-       background-color: transparent;
-}
-.oo-ui-textInputWidget.oo-ui-widget-enabled input:focus,
-.oo-ui-textInputWidget.oo-ui-widget-enabled textarea:focus {
-       outline: none;
-       border-color: #a7dcff;
-       box-shadow: 0 0 0.3em #a7dcff, 0 0 0 white;
-}
-.oo-ui-textInputWidget.oo-ui-widget-enabled input[readonly],
-.oo-ui-textInputWidget.oo-ui-widget-enabled textarea[readonly] {
-       color: #777777;
-}
-.oo-ui-textInputWidget.oo-ui-widget-enabled.oo-ui-flaggedElement-invalid input,
-.oo-ui-textInputWidget.oo-ui-widget-enabled.oo-ui-flaggedElement-invalid textarea {
-       background-color: #ffdddd;
-}
-.oo-ui-textInputWidget.oo-ui-widget-disabled input,
-.oo-ui-textInputWidget.oo-ui-widget-disabled textarea {
-       color: #cccccc;
-       text-shadow: 0 1px 1px #ffffff;
-       border-color: #dddddd;
-       background-color: #f3f3f3;
-}
-.oo-ui-textInputWidget.oo-ui-widget-disabled .oo-ui-iconElement-icon,
-.oo-ui-textInputWidget.oo-ui-widget-disabled .oo-ui-indicatorElement-indicator {
-       opacity: 0.2;
-}
-.oo-ui-textInputWidget.oo-ui-widget-disabled .oo-ui-labelElement-label {
-       color: #dddddd;
-       text-shadow: 0 1px 1px #ffffff;
-}
-.oo-ui-textInputWidget.oo-ui-iconElement input,
-.oo-ui-textInputWidget.oo-ui-iconElement textarea {
-       padding-left: 2.475em;
-}
-.oo-ui-textInputWidget.oo-ui-iconElement .oo-ui-iconElement-icon {
-       width: 1.875em;
-       max-height: 2.375em;
-       margin-left: 0.3em;
-}
-.oo-ui-textInputWidget.oo-ui-indicatorElement input,
-.oo-ui-textInputWidget.oo-ui-indicatorElement textarea {
-       padding-right: 2.4875em;
-}
-.oo-ui-textInputWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
-       width: 0.9375em;
-       max-height: 2.375em;
-       margin-right: 0.775em;
-}
-.oo-ui-textInputWidget > .oo-ui-labelElement-label {
-       padding: 0.4em;
-       line-height: 1.5em;
-       color: #888888;
-}
-.oo-ui-textInputWidget-labelPosition-after.oo-ui-indicatorElement > .oo-ui-labelElement-label {
-       margin-right: 2.0875em;
-}
-.oo-ui-textInputWidget-labelPosition-before.oo-ui-iconElement > .oo-ui-labelElement-label {
-       margin-left: 2.075em;
-}
-.oo-ui-menuSelectWidget {
-       position: absolute;
-       background-color: #ffffff;
-       margin-top: -1px;
-       border: 1px solid #cccccc;
-       border-radius: 0 0 0.25em 0.25em;
-       box-shadow: 0 0.15em 1em 0 rgba(0, 0, 0, 0.2);
-}
-.oo-ui-menuSelectWidget input {
-       position: absolute;
-       width: 0;
-       height: 0;
-       overflow: hidden;
-       opacity: 0;
-}
-.oo-ui-menuOptionWidget {
-       position: relative;
-}
-.oo-ui-menuOptionWidget .oo-ui-iconElement-icon {
-       display: none;
-}
-.oo-ui-menuOptionWidget.oo-ui-optionWidget-selected {
-       background-color: transparent;
-}
-.oo-ui-menuOptionWidget.oo-ui-optionWidget-selected .oo-ui-iconElement-icon {
-       display: block;
-}
-.oo-ui-menuOptionWidget.oo-ui-optionWidget-selected {
-       background-color: transparent;
-}
-.oo-ui-menuOptionWidget.oo-ui-optionWidget-highlighted,
-.oo-ui-menuOptionWidget.oo-ui-optionWidget-highlighted.oo-ui-optionWidget-selected {
-       background-color: #e1f3ff;
-}
-.oo-ui-menuSectionOptionWidget {
-       cursor: default;
-       padding: 0.33em 0.75em;
-       color: #888888;
-}
-.oo-ui-dropdownWidget {
-       display: inline-block;
-       position: relative;
-       width: 100%;
-       max-width: 50em;
-       background-color: #ffffff;
-       margin-right: 0.5em;
-}
-.oo-ui-dropdownWidget-handle {
-       width: 100%;
-       display: inline-block;
-       -webkit-touch-callout: none;
-       -webkit-user-select: none;
-          -moz-user-select: none;
-           -ms-user-select: none;
-               user-select: none;
-       -webkit-box-sizing: border-box;
-          -moz-box-sizing: border-box;
-               box-sizing: border-box;
-}
-.oo-ui-dropdownWidget-handle .oo-ui-indicatorElement-indicator,
-.oo-ui-dropdownWidget-handle .oo-ui-iconElement-icon {
-       position: absolute;
-}
-.oo-ui-dropdownWidget > .oo-ui-menuSelectWidget {
-       z-index: 1;
-       width: 100%;
-}
-.oo-ui-dropdownWidget.oo-ui-widget-enabled .oo-ui-dropdownWidget-handle {
-       cursor: pointer;
-}
-.oo-ui-dropdownWidget:last-child {
-       margin-right: 0;
-}
-.oo-ui-dropdownWidget-handle {
-       height: 2.5em;
-       border: 1px solid rgba(0, 0, 0, 0.1);
-       border-radius: 0.25em;
-}
-.oo-ui-dropdownWidget-handle:hover {
-       border-color: rgba(0, 0, 0, 0.2);
-}
-.oo-ui-dropdownWidget-handle .oo-ui-indicatorElement-indicator {
-       right: 0;
-}
-.oo-ui-dropdownWidget-handle .oo-ui-iconElement-icon {
-       left: 0.25em;
-}
-.oo-ui-dropdownWidget-handle .oo-ui-labelElement-label {
-       line-height: 2.5em;
-       margin: 0 0.5em;
-}
-.oo-ui-dropdownWidget-handle .oo-ui-indicatorElement-indicator {
-       top: 0;
-       width: 0.9375em;
-       height: 0.9375em;
-       margin: 0.775em;
-}
-.oo-ui-dropdownWidget-handle .oo-ui-iconElement-icon {
-       top: 0;
-       width: 1.875em;
-       height: 1.875em;
-       margin: 0.3em;
-}
-.oo-ui-dropdownWidget.oo-ui-widget-disabled .oo-ui-dropdownWidget-handle {
-       color: #cccccc;
-       text-shadow: 0 1px 1px #ffffff;
-       border-color: #dddddd;
-       background-color: #f3f3f3;
-}
-.oo-ui-dropdownWidget.oo-ui-widget-disabled .oo-ui-dropdownWidget-handle:focus {
-       outline: 0;
-}
-.oo-ui-dropdownWidget.oo-ui-widget-disabled .oo-ui-indicatorElement-indicator {
-       opacity: 0.2;
-}
-.oo-ui-dropdownWidget.oo-ui-iconElement .oo-ui-dropdownWidget-handle .oo-ui-labelElement-label {
-       margin-left: 3em;
-}
-.oo-ui-dropdownWidget.oo-ui-indicatorElement .oo-ui-dropdownWidget-handle .oo-ui-labelElement-label {
-       margin-right: 2em;
-}
-.oo-ui-selectFileWidget {
-       display: inline-block;
-       vertical-align: middle;
-       width: 100%;
-       max-width: 50em;
-       margin-right: 0.5em;
-}
-.oo-ui-selectFileWidget-selectButton {
-       display: table-cell;
-       vertical-align: middle;
-}
-.oo-ui-selectFileWidget-selectButton > .oo-ui-buttonElement-button {
-       position: relative;
-       overflow: hidden;
-}
-.oo-ui-selectFileWidget-selectButton > .oo-ui-buttonElement-button > input[type="file"] {
-       position: absolute;
-       margin: 0;
-       top: 0;
-       bottom: 0;
-       left: 0;
-       right: 0;
-       width: 100%;
-       height: 100%;
-       opacity: 0;
-       z-index: 1;
-       cursor: pointer;
-       padding-top: 100px;
-}
-.oo-ui-selectFileWidget-selectButton.oo-ui-widget-disabled > .oo-ui-buttonElement-button > input[type="file"] {
-       display: none;
-}
-.oo-ui-selectFileWidget-info {
-       width: 100%;
-       display: table-cell;
-       vertical-align: middle;
-       position: relative;
-       overflow: hidden;
-       -webkit-box-sizing: border-box;
-          -moz-box-sizing: border-box;
-               box-sizing: border-box;
-}
-.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-label {
-       position: absolute;
-       top: 0;
-       bottom: 0;
-       left: 0;
-       right: 0;
-       text-overflow: ellipsis;
-}
-.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-label > .oo-ui-selectFileWidget-fileName {
-       float: left;
-}
-.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-label > .oo-ui-selectFileWidget-fileType {
-       float: right;
-}
-.oo-ui-selectFileWidget-info > .oo-ui-indicatorElement-indicator,
-.oo-ui-selectFileWidget-info > .oo-ui-iconElement-icon,
-.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-clearButton {
-       position: absolute;
-}
-.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-clearButton {
-       z-index: 2;
-}
-.oo-ui-selectFileWidget-dropTarget {
-       cursor: default;
-}
-.oo-ui-selectFileWidget-supported.oo-ui-widget-enabled .oo-ui-selectFileWidget-dropTarget {
-       cursor: pointer;
-}
-.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-clearButton,
-.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-clearButton {
-       display: none;
-}
-.oo-ui-selectFileWidget:last-child {
-       margin-right: 0;
-}
-.oo-ui-selectFileWidget-selectButton > .oo-ui-buttonElement-button {
-       margin-left: 0.5em;
-}
-.oo-ui-selectFileWidget-info {
-       height: 2.4em;
-       background-color: #ffffff;
-       border: 1px solid rgba(0, 0, 0, 0.1);
-       border-radius: 0.25em;
-}
-.oo-ui-selectFileWidget-info > .oo-ui-indicatorElement-indicator {
-       right: 0;
-}
-.oo-ui-selectFileWidget-info > .oo-ui-iconElement-icon {
-       left: 0;
-}
-.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-label {
-       line-height: 2.3em;
-       margin: 0;
-       overflow: hidden;
-       white-space: nowrap;
-       -webkit-box-sizing: border-box;
-          -moz-box-sizing: border-box;
-               box-sizing: border-box;
-       text-overflow: ellipsis;
-       left: 0.5em;
-       right: 0.5em;
-}
-.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-label > .oo-ui-selectFileWidget-fileType {
-       color: #888888;
-}
-.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-clearButton {
-       top: 0;
-       width: 1.875em;
-       margin-right: 0;
-}
-.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-clearButton .oo-ui-buttonElement-button > .oo-ui-iconElement-icon {
-       height: 2.3em;
-}
-.oo-ui-selectFileWidget-info > .oo-ui-indicatorElement-indicator {
-       top: 0;
-       width: 0.9375em;
-       height: 2.3em;
-       margin-right: 0.775em;
-}
-.oo-ui-selectFileWidget-info > .oo-ui-iconElement-icon {
-       top: 0;
-       width: 1.875em;
-       height: 2.3em;
-       margin-left: 0.3em;
-}
-.oo-ui-selectFileWidget.oo-ui-widget-disabled .oo-ui-selectFileWidget-info {
-       color: #cccccc;
-       text-shadow: 0 1px 1px #ffffff;
-       border-color: #dddddd;
-       background-color: #f3f3f3;
-}
-.oo-ui-selectFileWidget.oo-ui-widget-disabled .oo-ui-selectFileWidget-info > .oo-ui-iconElement-icon,
-.oo-ui-selectFileWidget.oo-ui-widget-disabled .oo-ui-selectFileWidget-info > .oo-ui-indicatorElement-indicator {
-       opacity: 0.2;
-}
-.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-label {
-       color: #cccccc;
-}
-.oo-ui-selectFileWidget.oo-ui-iconElement .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-label {
-       left: 2.475em;
-}
-.oo-ui-selectFileWidget .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-label {
-       right: 2.175em;
-}
-.oo-ui-selectFileWidget .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-clearButton {
-       right: 0;
-}
-.oo-ui-selectFileWidget.oo-ui-indicatorElement .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-label {
-       right: 4.2625em;
-}
-.oo-ui-selectFileWidget.oo-ui-indicatorElement .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-clearButton {
-       right: 2.0875em;
-}
-.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-label,
-.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-label {
-       right: 0.5em;
-}
-.oo-ui-selectFileWidget-empty.oo-ui-indicatorElement .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-label,
-.oo-ui-selectFileWidget-notsupported.oo-ui-indicatorElement .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-label {
-       right: 2em;
-}
-.oo-ui-selectFileWidget-dropTarget {
-       line-height: 3.5em;
-       background-color: #ffffff;
-       border: 1px dashed #aaaaaa;
-       padding: 0.5em 1em;
-       margin-bottom: 0.5em;
-       text-align: center;
-       vertical-align: middle;
-}
-.oo-ui-selectFileWidget-supported.oo-ui-widget-enabled .oo-ui-selectFileWidget-dropTarget:hover,
-.oo-ui-selectFileWidget-supported.oo-ui-widget-enabled.oo-ui-selectFileWidget-canDrop oo-ui-selectfilewidget-droptarget {
-       background-color: #e1f3ff;
-}
-.oo-ui-selectFileWidget.oo-ui-widget-disabled .oo-ui-selectFileWidget-dropTarget,
-.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-dropTarget {
-       color: #cccccc;
-       text-shadow: 0 1px 1px #ffffff;
-       border-color: #dddddd;
-       background-color: #f3f3f3;
-}
-.oo-ui-outlineOptionWidget {
-       position: relative;
-       cursor: pointer;
-       -webkit-touch-callout: none;
-       -webkit-user-select: none;
-          -moz-user-select: none;
-           -ms-user-select: none;
-               user-select: none;
-       font-size: 1.1em;
-       padding: 0.75em;
-}
-.oo-ui-outlineOptionWidget.oo-ui-indicatorElement .oo-ui-labelElement-label {
-       padding-right: 1.5em;
-}
-.oo-ui-outlineOptionWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
-       opacity: 0.5;
-}
-.oo-ui-outlineOptionWidget-level-0 {
-       padding-left: 3.5em;
-}
-.oo-ui-outlineOptionWidget-level-0 .oo-ui-iconElement-icon {
-       left: 1em;
-}
-.oo-ui-outlineOptionWidget-level-1 {
-       padding-left: 5em;
-}
-.oo-ui-outlineOptionWidget-level-1 .oo-ui-iconElement-icon {
-       left: 2.5em;
-}
-.oo-ui-outlineOptionWidget-level-2 {
-       padding-left: 6.5em;
-}
-.oo-ui-outlineOptionWidget-level-2 .oo-ui-iconElement-icon {
-       left: 4em;
-}
-.oo-ui-selectWidget-depressed .oo-ui-outlineOptionWidget.oo-ui-optionWidget-selected {
-       background-color: #a7dcff;
-       text-shadow: 0 1px 1px rgba(255, 255, 255, 0.5);
-}
-.oo-ui-outlineOptionWidget.oo-ui-flaggedElement-important {
-       font-weight: bold;
-}
-.oo-ui-outlineOptionWidget.oo-ui-flaggedElement-placeholder {
-       font-style: italic;
-}
-.oo-ui-outlineOptionWidget.oo-ui-flaggedElement-empty .oo-ui-iconElement-icon {
-       opacity: 0.5;
-}
-.oo-ui-outlineOptionWidget.oo-ui-flaggedElement-empty .oo-ui-labelElement-label {
-       color: #777777;
-}
-.oo-ui-outlineControlsWidget {
-       height: 3em;
-       background-color: #ffffff;
-}
-.oo-ui-outlineControlsWidget-items,
-.oo-ui-outlineControlsWidget-movers {
-       float: left;
-       -webkit-box-sizing: border-box;
-          -moz-box-sizing: border-box;
-               box-sizing: border-box;
-}
-.oo-ui-outlineControlsWidget > .oo-ui-iconElement-icon {
-       float: left;
-       background-position: right center;
-}
-.oo-ui-outlineControlsWidget-items {
-       float: left;
-}
-.oo-ui-outlineControlsWidget-items .oo-ui-buttonWidget {
-       float: left;
-}
-.oo-ui-outlineControlsWidget-movers {
-       float: right;
-}
-.oo-ui-outlineControlsWidget-movers .oo-ui-buttonWidget {
-       float: right;
-}
-.oo-ui-outlineControlsWidget-items,
-.oo-ui-outlineControlsWidget-movers {
-       height: 2em;
-       margin: 0.5em 0.5em 0.5em 0;
-       padding: 0;
-}
-.oo-ui-outlineControlsWidget > .oo-ui-iconElement-icon {
-       width: 1.5em;
-       height: 2em;
-       margin: 0.5em 0 0.5em 0.5em;
-       opacity: 0.2;
-}
-.oo-ui-tabSelectWidget {
-       text-align: left;
-       white-space: nowrap;
-       overflow: hidden;
-       background-color: #eeeeee;
-       box-shadow: inset 0 -0.015em 0.1em rgba(0, 0, 0, 0.1);
-}
-.oo-ui-tabOptionWidget {
-       display: inline-block;
-       vertical-align: bottom;
-       padding: 0.5em 1em;
-       margin: 0.5em 0 0 0.75em;
-       border: 1px solid transparent;
-       border-bottom: none;
-       border-top-left-radius: 0.5em;
-       border-top-right-radius: 0.5em;
-}
-.oo-ui-tabOptionWidget.oo-ui-indicatorElement .oo-ui-labelElement-label {
-       padding-right: 1.5em;
-}
-.oo-ui-tabOptionWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
-       opacity: 0.5;
-}
-.oo-ui-selectWidget-pressed .oo-ui-tabOptionWidget.oo-ui-optionWidget-pressed {
-       background-color: transparent;
-}
-.oo-ui-tabOptionWidget.oo-ui-widget-enabled:hover {
-       background-color: rgba(255, 255, 255, 0.2);
-       border-color: #dddddd;
-}
-.oo-ui-tabOptionWidget.oo-ui-widget-enabled:active {
-       background-color: #ffffff;
-       border-color: #dddddd;
-}
-.oo-ui-selectWidget-pressed .oo-ui-tabOptionWidget.oo-ui-optionWidget-selected,
-.oo-ui-selectWidget-depressed .oo-ui-tabOptionWidget.oo-ui-optionWidget-selected,
-.oo-ui-tabOptionWidget.oo-ui-optionWidget-selected:hover {
-       background-color: #ffffff;
-       border-color: #dddddd;
-}
-.oo-ui-capsuleMultiSelectWidget {
-       display: inline-block;
-       position: relative;
-       width: 100%;
-       max-width: 50em;
-}
-.oo-ui-capsuleMultiSelectWidget-handle {
-       width: 100%;
-       display: inline-block;
-       position: relative;
-}
-.oo-ui-capsuleMultiSelectWidget-content {
-       position: relative;
-}
-.oo-ui-capsuleMultiSelectWidget.oo-ui-widget-disabled .oo-ui-capsuleMultiSelectWidget-content > input {
-       display: none;
-}
-.oo-ui-capsuleMultiSelectWidget-group {
-       display: inline;
-}
-.oo-ui-capsuleMultiSelectWidget > .oo-ui-menuSelectWidget {
-       z-index: 1;
-       width: 100%;
-}
-.oo-ui-capsuleMultiSelectWidget-handle {
-       background-color: #ffffff;
-       cursor: text;
-       min-height: 2.4em;
-       margin-right: 0.5em;
-       padding: 0.15em 0.25em;
-       border: 1px solid rgba(0, 0, 0, 0.1);
-       border-radius: 0.25em;
-       -webkit-box-sizing: border-box;
-          -moz-box-sizing: border-box;
-               box-sizing: border-box;
-}
-.oo-ui-capsuleMultiSelectWidget-handle:last-child {
-       margin-right: 0;
-}
-.oo-ui-capsuleMultiSelectWidget-handle > .oo-ui-indicatorElement-indicator,
-.oo-ui-capsuleMultiSelectWidget-handle > .oo-ui-iconElement-icon {
-       position: absolute;
-       background-position: center center;
-       background-repeat: no-repeat;
-}
-.oo-ui-capsuleMultiSelectWidget-handle > .oo-ui-capsuleMultiSelectWidget-content > input {
-       border: none;
-       line-height: 1.675em;
-       margin: 0;
-       margin-left: 0.2em;
-       padding: 0;
-       font-size: inherit;
-       font-family: inherit;
-       background-color: transparent;
-       color: black;
-       vertical-align: middle;
-}
-.oo-ui-capsuleMultiSelectWidget-handle > .oo-ui-capsuleMultiSelectWidget-content > input:focus {
-       outline: none;
-}
-.oo-ui-capsuleMultiSelectWidget.oo-ui-indicatorElement .oo-ui-capsuleMultiSelectWidget-handle {
-       padding-right: 2.4875em;
-}
-.oo-ui-capsuleMultiSelectWidget.oo-ui-indicatorElement .oo-ui-capsuleMultiSelectWidget-handle > .oo-ui-indicatorElement-indicator {
-       right: 0;
-       top: 0;
-       width: 0.9375em;
-       height: 0.9375em;
-       margin: 0.775em;
-}
-.oo-ui-capsuleMultiSelectWidget.oo-ui-iconElement .oo-ui-capsuleMultiSelectWidget-handle {
-       padding-left: 2.475em;
-}
-.oo-ui-capsuleMultiSelectWidget.oo-ui-iconElement .oo-ui-capsuleMultiSelectWidget-handle > .oo-ui-iconElement-icon {
-       left: 0;
-       top: 0;
-       width: 1.875em;
-       height: 1.875em;
-       margin: 0.3em;
-}
-.oo-ui-capsuleMultiSelectWidget:hover .oo-ui-capsuleMultiSelectWidget-handle {
-       border-color: rgba(0, 0, 0, 0.2);
-}
-.oo-ui-capsuleMultiSelectWidget.oo-ui-widget-disabled .oo-ui-capsuleMultiSelectWidget-handle {
-       color: #cccccc;
-       text-shadow: 0 1px 1px #ffffff;
-       border-color: #dddddd;
-       background-color: #f3f3f3;
-       cursor: default;
-}
-.oo-ui-capsuleMultiSelectWidget.oo-ui-widget-disabled .oo-ui-capsuleMultiSelectWidget-handle > .oo-ui-iconElement-icon,
-.oo-ui-capsuleMultiSelectWidget.oo-ui-widget-disabled .oo-ui-capsuleMultiSelectWidget-handle > .oo-ui-indicatorElement-indicator {
-       opacity: 0.2;
-}
-.oo-ui-capsuleMultiSelectWidget .oo-ui-selectWidget {
-       border-top-color: #ffffff;
-}
-.oo-ui-capsuleItemWidget {
-       position: relative;
-       display: inline-block;
-       cursor: default;
-       white-space: nowrap;
-       width: auto;
-       max-width: 100%;
-       -webkit-box-sizing: border-box;
-          -moz-box-sizing: border-box;
-               box-sizing: border-box;
-       vertical-align: middle;
-       padding: 0 0.4em;
-       margin: 0.1em;
-       height: 1.7em;
-       line-height: 1.7em;
-       background: #eeeeee;
-       filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#fff', endColorstr='#ddd');
-       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0%, #ffffff), color-stop(100%, #dddddd));
-       background-image: -webkit-linear-gradient(top, #ffffff 0%, #dddddd 100%);
-       background-image:    -moz-linear-gradient(top, #ffffff 0%, #dddddd 100%);
-       background-image:      -o-linear-gradient(top, #ffffff 0%, #dddddd 100%);
-       background-image:         linear-gradient(to bottom, #ffffff 0%, #dddddd 100%);
-       border: 1px solid #cccccc;
-       color: #555555;
-       border-radius: 0.25em;
-}
-.oo-ui-capsuleItemWidget > .oo-ui-iconElement-icon {
-       cursor: pointer;
-}
-.oo-ui-capsuleItemWidget.oo-ui-widget-disabled > .oo-ui-iconElement-icon {
-       cursor: default;
-}
-.oo-ui-capsuleItemWidget.oo-ui-labelElement .oo-ui-labelElement-label {
-       display: block;
-       text-overflow: ellipsis;
-       overflow: hidden;
-}
-.oo-ui-capsuleItemWidget.oo-ui-indicatorElement > .oo-ui-labelElement-label {
-       padding-right: 1.3375em;
-}
-.oo-ui-capsuleItemWidget.oo-ui-indicatorElement > .oo-ui-indicatorElement-indicator {
-       position: absolute;
-       right: 0.4em;
-       top: 0;
-       width: 0.9375em;
-       height: 100%;
-       background-repeat: no-repeat;
-}
-.oo-ui-capsuleItemWidget.oo-ui-indicatorElement > .oo-ui-indicator-clear {
-       cursor: pointer;
-}
-.oo-ui-capsuleItemWidget.oo-ui-widget-disabled {
-       opacity: 0.5;
-       -webkit-transform: translate3d(0, 0, 0);
-       box-shadow: none;
-       color: #333333;
-       background: #eeeeee;
-       border-color: #cccccc;
-}
-.oo-ui-capsuleItemWidget.oo-ui-widget-disabled > .oo-ui-indicatorElement-indicator {
-       opacity: 0.2;
-}
-.oo-ui-comboBoxInputWidget {
-       display: inline-block;
-       position: relative;
-       width: 100%;
-       max-width: 50em;
-       margin-right: 0.5em;
-}
-.oo-ui-comboBoxInputWidget > .oo-ui-menuSelectWidget {
-       z-index: 1;
-       width: 100%;
-}
-.oo-ui-comboBoxInputWidget.oo-ui-widget-enabled > .oo-ui-indicatorElement-indicator {
-       cursor: pointer;
-}
-.oo-ui-comboBoxInputWidget-php input::-webkit-calendar-picker-indicator {
-       opacity: 0 !important;
-       position: absolute;
-       right: 0;
-       top: 0;
-       height: 2.5em;
-       width: 2.5em;
-       padding: 0;
-}
-.oo-ui-comboBoxInputWidget-php > .oo-ui-indicatorElement-indicator {
-       pointer-events: none;
-}
-.oo-ui-comboBoxInputWidget:last-child {
-       margin-right: 0;
-}
-.oo-ui-comboBoxInputWidget.oo-ui-widget-disabled .oo-ui-textInputWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator,
-.oo-ui-comboBoxInputWidget-empty .oo-ui-textInputWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
-       cursor: default;
-       opacity: 0.2;
-}
-.oo-ui-comboBoxInputWidget > .oo-ui-selectWidget {
-       margin-top: -3px;
-}
-.oo-ui-searchWidget-query {
-       position: absolute;
-       top: 0;
-       left: 0;
-       right: 0;
-}
-.oo-ui-searchWidget-query .oo-ui-textInputWidget {
-       width: 100%;
-}
-.oo-ui-searchWidget-results {
-       position: absolute;
-       bottom: 0;
-       left: 0;
-       right: 0;
-       overflow-x: hidden;
-       overflow-y: auto;
-}
-.oo-ui-searchWidget-query {
-       height: 4em;
-       padding: 0 1em;
-       box-shadow: 0 0 0.5em rgba(0, 0, 0, 0.2);
-}
-.oo-ui-searchWidget-query .oo-ui-textInputWidget {
-       margin: 0.75em 0;
-}
-.oo-ui-searchWidget-results {
-       top: 4em;
-       padding: 1em;
-       line-height: 0;
-}
-.oo-ui-numberInputWidget {
-       display: inline-block;
-       position: relative;
-       max-width: 50em;
-}
-.oo-ui-numberInputWidget-field {
-       display: table;
-       table-layout: fixed;
-       width: 100%;
-}
-.oo-ui-numberInputWidget-field > .oo-ui-buttonWidget,
-.oo-ui-numberInputWidget-field > .oo-ui-textInputWidget {
-       display: table-cell;
-       vertical-align: middle;
-}
-.oo-ui-numberInputWidget-field > .oo-ui-textInputWidget {
-       width: 100%;
-}
-.oo-ui-numberInputWidget-field > .oo-ui-buttonWidget {
-       white-space: nowrap;
-}
-.oo-ui-numberInputWidget-field > .oo-ui-buttonWidget > .oo-ui-buttonElement-button {
-       -webkit-box-sizing: border-box;
-          -moz-box-sizing: border-box;
-               box-sizing: border-box;
-}
-.oo-ui-numberInputWidget-field > .oo-ui-buttonWidget {
-       width: 2.25em;
-}
-.oo-ui-numberInputWidget-minusButton.oo-ui-buttonElement-framed.oo-ui-widget-enabled > .oo-ui-buttonElement-button {
-       border-top-right-radius: 0;
-       border-bottom-right-radius: 0;
-       border-right-width: 0;
-}
-.oo-ui-numberInputWidget-plusButton.oo-ui-buttonElement-framed.oo-ui-widget-enabled > .oo-ui-buttonElement-button {
-       border-top-left-radius: 0;
-       border-bottom-left-radius: 0;
-       border-left-width: 0;
-}
-.oo-ui-numberInputWidget .oo-ui-textInputWidget input {
-       border-radius: 0;
-}
-.oo-ui-window {
-       background-color: transparent;
-       background-image: none;
-}
-.oo-ui-window-frame {
-       -webkit-box-sizing: border-box;
-          -moz-box-sizing: border-box;
-               box-sizing: border-box;
-}
-.oo-ui-window-content:focus {
-       outline: none;
-}
-.oo-ui-window-head,
-.oo-ui-window-foot {
-       -webkit-touch-callout: none;
-       -webkit-user-select: none;
-          -moz-user-select: none;
-           -ms-user-select: none;
-               user-select: none;
-}
-.oo-ui-window-body {
-       margin: 0;
-       padding: 0;
-       background: none;
-}
-.oo-ui-window-overlay {
-       position: absolute;
-       top: 0;
-       /* @noflip */
-       left: 0;
-}
-.oo-ui-dialog-content > .oo-ui-window-head,
-.oo-ui-dialog-content > .oo-ui-window-body,
-.oo-ui-dialog-content > .oo-ui-window-foot {
-       position: absolute;
-       left: 0;
-       right: 0;
-       -webkit-box-sizing: border-box;
-          -moz-box-sizing: border-box;
-               box-sizing: border-box;
-}
-.oo-ui-dialog-content > .oo-ui-window-head {
-       overflow: hidden;
-       z-index: 1;
-       top: 0;
-}
-.oo-ui-dialog-content > .oo-ui-window-body {
-       overflow: auto;
-       z-index: 2;
-       top: 0;
-       bottom: 0;
-}
-.oo-ui-dialog-content > .oo-ui-window-foot {
-       overflow: hidden;
-       z-index: 1;
-       bottom: 0;
-}
-.oo-ui-dialog-content > .oo-ui-window-body {
-       box-shadow: 0 0 0.66em rgba(0, 0, 0, 0.25);
-}
-.oo-ui-messageDialog-actions-horizontal {
-       display: table;
-       table-layout: fixed;
-       width: 100%;
-}
-.oo-ui-messageDialog-actions-horizontal .oo-ui-actionWidget {
-       display: table-cell;
-       width: 1%;
-}
-.oo-ui-messageDialog-actions-vertical {
-       display: block;
-}
-.oo-ui-messageDialog-actions-vertical .oo-ui-actionWidget {
-       display: block;
-       overflow: hidden;
-       text-overflow: ellipsis;
-}
-.oo-ui-messageDialog-actions .oo-ui-actionWidget {
-       position: relative;
-       text-align: center;
-}
-.oo-ui-messageDialog-actions .oo-ui-actionWidget .oo-ui-buttonElement-button {
-       display: block;
-}
-.oo-ui-messageDialog-actions .oo-ui-actionWidget .oo-ui-labelElement-label {
-       position: relative;
-       top: auto;
-       bottom: auto;
-       display: inline;
-       white-space: nowrap;
-}
-.oo-ui-messageDialog-content .oo-ui-window-body {
-       box-shadow: 0 0 0.33em rgba(0, 0, 0, 0.33);
-}
-.oo-ui-messageDialog-title,
-.oo-ui-messageDialog-message {
-       display: block;
-       text-align: center;
-}
-.oo-ui-messageDialog-title.oo-ui-labelElement,
-.oo-ui-messageDialog-message.oo-ui-labelElement {
-       padding-top: 0.5em;
-}
-.oo-ui-messageDialog-title {
-       font-size: 1.5em;
-       line-height: 1em;
-       color: #000000;
-}
-.oo-ui-messageDialog-message {
-       font-size: 0.9em;
-       line-height: 1.25em;
-       color: #666666;
-}
-.oo-ui-messageDialog-message-verbose {
-       font-size: 1.1em;
-       line-height: 1.5em;
-       text-align: left;
-}
-.oo-ui-messageDialog-actions-horizontal .oo-ui-actionWidget {
-       border-right: 1px solid #e5e5e5;
-       margin: 0;
-}
-.oo-ui-messageDialog-actions-horizontal .oo-ui-actionWidget:last-child {
-       border-right-width: 0;
-}
-.oo-ui-messageDialog-actions-vertical .oo-ui-actionWidget {
-       border-bottom: 1px solid #e5e5e5;
-       margin: 0;
-}
-.oo-ui-messageDialog-actions-vertical .oo-ui-actionWidget:last-child {
-       border-bottom-width: 0;
-}
-.oo-ui-messageDialog-actions .oo-ui-actionWidget {
-       height: 3.4em;
-       margin-right: 0;
-}
-.oo-ui-messageDialog-actions .oo-ui-actionWidget:last-child {
-       margin-right: 0;
-}
-.oo-ui-messageDialog-actions .oo-ui-actionWidget.oo-ui-labelElement .oo-ui-labelElement-label {
-       text-align: center;
-       line-height: 3.4em;
-       padding: 0 2em;
-}
-.oo-ui-messageDialog-actions .oo-ui-actionWidget:hover {
-       background-color: rgba(0, 0, 0, 0.05);
-}
-.oo-ui-messageDialog-actions .oo-ui-actionWidget:active {
-       background-color: rgba(0, 0, 0, 0.1);
-}
-.oo-ui-messageDialog-actions .oo-ui-actionWidget.oo-ui-flaggedElement-progressive:hover {
-       background-color: rgba(8, 126, 204, 0.05);
-}
-.oo-ui-messageDialog-actions .oo-ui-actionWidget.oo-ui-flaggedElement-progressive:active {
-       background-color: rgba(8, 126, 204, 0.1);
-}
-.oo-ui-messageDialog-actions .oo-ui-actionWidget.oo-ui-flaggedElement-progressive .oo-ui-labelElement-label {
-       font-weight: bold;
-}
-.oo-ui-messageDialog-actions .oo-ui-actionWidget.oo-ui-flaggedElement-constructive:hover {
-       background-color: rgba(118, 171, 54, 0.05);
-}
-.oo-ui-messageDialog-actions .oo-ui-actionWidget.oo-ui-flaggedElement-constructive:active {
-       background-color: rgba(118, 171, 54, 0.1);
-}
-.oo-ui-messageDialog-actions .oo-ui-actionWidget.oo-ui-flaggedElement-destructive:hover {
-       background-color: rgba(212, 83, 83, 0.05);
-}
-.oo-ui-messageDialog-actions .oo-ui-actionWidget.oo-ui-flaggedElement-destructive:active {
-       background-color: rgba(212, 83, 83, 0.1);
-}
-.oo-ui-processDialog-location {
-       overflow: hidden;
-       text-overflow: ellipsis;
-       white-space: nowrap;
-}
-.oo-ui-processDialog-title {
-       display: inline;
-       padding: 0;
-}
-.oo-ui-processDialog-actions-safe .oo-ui-actionWidget,
-.oo-ui-processDialog-actions-primary .oo-ui-actionWidget,
-.oo-ui-processDialog-actions-other .oo-ui-actionWidget {
-       white-space: nowrap;
-}
-.oo-ui-processDialog-actions-safe,
-.oo-ui-processDialog-actions-primary {
-       position: absolute;
-       top: 0;
-       bottom: 0;
-}
-.oo-ui-processDialog-actions-safe {
-       left: 0;
-}
-.oo-ui-processDialog-actions-primary {
-       right: 0;
-}
-.oo-ui-processDialog-errors {
-       position: absolute;
-       top: 0;
-       left: 0;
-       right: 0;
-       bottom: 0;
-       z-index: 2;
-       overflow-x: hidden;
-       overflow-y: auto;
-}
-.oo-ui-processDialog-content .oo-ui-window-head {
-       height: 3.4em;
-}
-.oo-ui-processDialog-content .oo-ui-window-body {
-       top: 3.4em;
-       box-shadow: 0 0 0.33em rgba(0, 0, 0, 0.33);
-}
-.oo-ui-processDialog-navigation {
-       position: relative;
-       height: 3.4em;
-       padding: 0 1em;
-}
-.oo-ui-processDialog-location {
-       padding: 0.75em 0;
-       height: 1.875em;
-       cursor: default;
-       text-align: center;
-}
-.oo-ui-processDialog-title {
-       font-weight: bold;
-       line-height: 1.875em;
-}
-.oo-ui-processDialog-actions-safe .oo-ui-actionWidget .oo-ui-buttonElement-button,
-.oo-ui-processDialog-actions-primary .oo-ui-actionWidget .oo-ui-buttonElement-button,
-.oo-ui-processDialog-actions-other .oo-ui-actionWidget .oo-ui-buttonElement-button {
-       min-width: 1.875em;
-       min-height: 1.875em;
-}
-.oo-ui-processDialog-actions-safe .oo-ui-actionWidget .oo-ui-labelElement-label,
-.oo-ui-processDialog-actions-primary .oo-ui-actionWidget .oo-ui-labelElement-label,
-.oo-ui-processDialog-actions-other .oo-ui-actionWidget .oo-ui-labelElement-label {
-       line-height: 1.875em;
-}
-.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-iconElement .oo-ui-iconElement-icon,
-.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-iconElement .oo-ui-iconElement-icon,
-.oo-ui-processDialog-actions-other .oo-ui-actionWidget.oo-ui-iconElement .oo-ui-iconElement-icon {
-       margin-top: -0.125em;
-}
-.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-buttonElement-framed,
-.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-buttonElement-framed,
-.oo-ui-processDialog-actions-other .oo-ui-actionWidget.oo-ui-buttonElement-framed {
-       margin: 0.75em;
-}
-.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-buttonElement-framed .oo-ui-buttonElement-button,
-.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-buttonElement-framed .oo-ui-buttonElement-button,
-.oo-ui-processDialog-actions-other .oo-ui-actionWidget.oo-ui-buttonElement-framed .oo-ui-buttonElement-button {
-       padding: 0 1em;
-       vertical-align: middle;
-       margin: -1px;
-}
-.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-buttonElement-frameless,
-.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-buttonElement-frameless,
-.oo-ui-processDialog-actions-other .oo-ui-actionWidget.oo-ui-buttonElement-frameless {
-       margin: 0;
-}
-.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-buttonElement-frameless .oo-ui-buttonElement-button,
-.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-buttonElement-frameless .oo-ui-buttonElement-button,
-.oo-ui-processDialog-actions-other .oo-ui-actionWidget.oo-ui-buttonElement-frameless .oo-ui-buttonElement-button {
-       padding: 0.75em 1em;
-       vertical-align: middle;
-}
-.oo-ui-processDialog-actions-safe .oo-ui-actionWidget:hover,
-.oo-ui-processDialog-actions-primary .oo-ui-actionWidget:hover {
-       background-color: rgba(0, 0, 0, 0.05);
-}
-.oo-ui-processDialog-actions-safe .oo-ui-actionWidget:active,
-.oo-ui-processDialog-actions-primary .oo-ui-actionWidget:active {
-       background-color: rgba(0, 0, 0, 0.1);
-}
-.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-flaggedElement-progressive:hover,
-.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-flaggedElement-progressive:hover {
-       background-color: rgba(8, 126, 204, 0.05);
-}
-.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-flaggedElement-progressive:active,
-.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-flaggedElement-progressive:active {
-       background-color: rgba(8, 126, 204, 0.1);
-}
-.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-flaggedElement-progressive .oo-ui-labelElement-label,
-.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-flaggedElement-progressive .oo-ui-labelElement-label {
-       font-weight: bold;
-}
-.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-flaggedElement-constructive:hover,
-.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-flaggedElement-constructive:hover {
-       background-color: rgba(118, 171, 54, 0.05);
-}
-.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-flaggedElement-constructive:active,
-.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-flaggedElement-constructive:active {
-       background-color: rgba(118, 171, 54, 0.1);
-}
-.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-flaggedElement-destructive:hover,
-.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-flaggedElement-destructive:hover {
-       background-color: rgba(212, 83, 83, 0.05);
-}
-.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-flaggedElement-destructive:active,
-.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-flaggedElement-destructive:active {
-       background-color: rgba(212, 83, 83, 0.1);
-}
-.oo-ui-processDialog-actions-other .oo-ui-actionWidget.oo-ui-buttonElement {
-       margin-right: 0;
-}
-.oo-ui-processDialog > .oo-ui-window-frame {
-       min-height: 5em;
-}
-.oo-ui-processDialog-errors {
-       background-color: rgba(255, 255, 255, 0.9);
-       padding: 3em 3em 1.5em 3em;
-       text-align: center;
-}
-.oo-ui-processDialog-errors .oo-ui-buttonWidget {
-       margin: 2em 1em 2em 1em;
-}
-.oo-ui-processDialog-errors-title {
-       font-size: 1.5em;
-       color: #000000;
-       margin-bottom: 2em;
-}
-.oo-ui-processDialog-error {
-       text-align: left;
-       margin: 1em;
-       padding: 1em;
-       border: 1px solid #ff9e9e;
-       background-color: #fff7f7;
-       border-radius: 0.25em;
-}
-.oo-ui-windowManager-modal > .oo-ui-dialog {
-       position: fixed;
-       width: 0;
-       height: 0;
-       overflow: hidden;
-}
-.oo-ui-windowManager-modal > .oo-ui-dialog.oo-ui-window-active {
-       width: auto;
-       height: auto;
-       top: 0;
-       right: 0;
-       bottom: 0;
-       left: 0;
-       padding: 1em;
-}
-.oo-ui-windowManager-modal > .oo-ui-dialog.oo-ui-window-setup > .oo-ui-window-frame {
-       position: absolute;
-       right: 0;
-       left: 0;
-       margin: auto;
-       overflow: hidden;
-       max-width: 100%;
-       max-height: 100%;
-}
-.oo-ui-windowManager-fullscreen > .oo-ui-dialog > .oo-ui-window-frame {
-       width: 100%;
-       height: 100%;
-       top: 0;
-       bottom: 0;
-}
-.oo-ui-windowManager-modal > .oo-ui-dialog {
-       background-color: rgba(255, 255, 255, 0.5);
-       opacity: 0;
-       -webkit-transition: opacity 250ms ease;
-          -moz-transition: opacity 250ms ease;
-               transition: opacity 250ms ease;
-}
-.oo-ui-windowManager-modal > .oo-ui-dialog > .oo-ui-window-frame {
-       background-color: #ffffff;
-       opacity: 0;
-       -webkit-transform: scale(0.5);
-          -moz-transform: scale(0.5);
-           -ms-transform: scale(0.5);
-               transform: scale(0.5);
-       -webkit-transition: all 250ms ease;
-          -moz-transition: all 250ms ease;
-               transition: all 250ms ease;
-}
-.oo-ui-windowManager-modal > .oo-ui-dialog.oo-ui-window-setup {
-       opacity: 1;
-}
-.oo-ui-windowManager-modal > .oo-ui-dialog.oo-ui-window-ready > .oo-ui-window-frame {
-       opacity: 1;
-       -webkit-transform: scale(1);
-          -moz-transform: scale(1);
-           -ms-transform: scale(1);
-               transform: scale(1);
-}
-.oo-ui-windowManager-modal.oo-ui-windowManager-floating > .oo-ui-dialog > .oo-ui-window-frame {
-       top: 1em;
-       bottom: 1em;
-       border: 1px solid #cccccc;
-       border-radius: 0.5em;
-       box-shadow: 0 0.2em 1em rgba(0, 0, 0, 0.3);
-}
index 5f9c93c..3637818 100644 (file)
@@ -1,13 +1,17 @@
 /*!
- * OOjs UI v0.15.1
+ * OOjs UI v0.15.3
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-01-26T21:14:23Z
+ * Date: 2016-02-09T21:21:16Z
  */
+( function ( OO ) {
+
+'use strict';
+
 /**
  * @class
  * @extends OO.ui.Theme
@@ -26,3 +30,5 @@ OO.inheritClass( OO.ui.ApexTheme, OO.ui.Theme );
 /* Instantiation */
 
 OO.ui.theme = new OO.ui.ApexTheme();
+
+}( OO ) );
diff --git a/resources/lib/oojs-ui/oojs-ui-core-apex.css b/resources/lib/oojs-ui/oojs-ui-core-apex.css
new file mode 100644 (file)
index 0000000..734ff71
--- /dev/null
@@ -0,0 +1,1088 @@
+/*!
+ * OOjs UI v0.15.3
+ * https://www.mediawiki.org/wiki/OOjs_UI
+ *
+ * Copyright 2011–2016 OOjs UI Team and other contributors.
+ * Released under the MIT license
+ * http://oojs.mit-license.org
+ *
+ * Date: 2016-02-09T21:21:21Z
+ */
+.oo-ui-element-hidden {
+       display: none !important;
+}
+.oo-ui-buttonElement > .oo-ui-buttonElement-button {
+       cursor: pointer;
+       display: inline-block;
+       vertical-align: middle;
+       font: inherit;
+       white-space: nowrap;
+       -webkit-touch-callout: none;
+       -webkit-user-select: none;
+          -moz-user-select: none;
+           -ms-user-select: none;
+               user-select: none;
+}
+.oo-ui-buttonElement > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon,
+.oo-ui-buttonElement > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator {
+       display: none;
+}
+.oo-ui-buttonElement.oo-ui-widget-disabled > .oo-ui-buttonElement-button {
+       cursor: default;
+}
+.oo-ui-buttonElement.oo-ui-indicatorElement > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator,
+.oo-ui-buttonElement.oo-ui-iconElement > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon {
+       display: inline-block;
+       vertical-align: middle;
+}
+.oo-ui-buttonElement-frameless {
+       display: inline-block;
+       position: relative;
+}
+.oo-ui-buttonElement-frameless.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
+       display: inline-block;
+       vertical-align: middle;
+}
+.oo-ui-buttonElement-framed > .oo-ui-buttonElement-button {
+       display: inline-block;
+       vertical-align: top;
+       text-align: center;
+}
+.oo-ui-buttonElement-framed.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
+       display: inline-block;
+       vertical-align: middle;
+}
+.oo-ui-buttonElement-framed.oo-ui-widget-disabled > .oo-ui-buttonElement-button,
+.oo-ui-buttonElement-framed.oo-ui-widget-disabled.oo-ui-buttonElement-active > .oo-ui-buttonElement-button,
+.oo-ui-buttonElement-framed.oo-ui-widget-disabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
+       cursor: default;
+}
+.oo-ui-buttonElement > .oo-ui-buttonElement-button {
+       color: #333333;
+}
+.oo-ui-buttonElement.oo-ui-iconElement > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon {
+       margin-left: 0;
+}
+.oo-ui-buttonElement.oo-ui-indicatorElement > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator {
+       width: 0.9375em;
+       height: 0.9375em;
+       margin: 0.46875em;
+}
+.oo-ui-buttonElement.oo-ui-iconElement > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator {
+       margin-left: 0.46875em;
+}
+.oo-ui-buttonElement.oo-ui-iconElement > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon {
+       width: 1.875em;
+       height: 1.875em;
+}
+.oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button:hover,
+.oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button:focus {
+       outline: none;
+}
+.oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button:hover > .oo-ui-iconElement-icon,
+.oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button:focus > .oo-ui-iconElement-icon {
+       opacity: 1;
+}
+.oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button:hover > .oo-ui-labelElement-label,
+.oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button:focus > .oo-ui-labelElement-label {
+       color: #000000;
+}
+.oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
+       color: #333333;
+}
+.oo-ui-buttonElement-frameless.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
+       margin-left: 0.25em;
+}
+.oo-ui-buttonElement-frameless > input.oo-ui-buttonElement-button {
+       padding-left: 0.25em;
+       color: #333333;
+}
+.oo-ui-buttonElement-frameless > input.oo-ui-buttonElement-button:hover,
+.oo-ui-buttonElement-frameless > input.oo-ui-buttonElement-button:focus {
+       color: #000000;
+}
+.oo-ui-buttonElement-frameless.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
+       color: #087ecc;
+}
+.oo-ui-buttonElement-frameless.oo-ui-flaggedElement-constructive > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
+       color: #76ab36;
+}
+.oo-ui-buttonElement-frameless.oo-ui-flaggedElement-destructive > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
+       color: #d45353;
+}
+.oo-ui-buttonElement-frameless.oo-ui-widget-disabled > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon {
+       opacity: 0.2;
+}
+.oo-ui-buttonElement-frameless.oo-ui-widget-disabled > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
+       color: #cccccc;
+}
+.oo-ui-buttonElement-framed > .oo-ui-buttonElement-button {
+       padding: 0.2em 0.8em;
+       border-radius: 0.3em;
+       text-shadow: 0 1px 1px rgba(255, 255, 255, 0.5);
+       border: 1px #c9c9c9 solid;
+       -webkit-transition: border-color 100ms ease;
+          -moz-transition: border-color 100ms ease;
+               transition: border-color 100ms ease;
+       background-color: #eeeeee;
+       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0, #ffffff), color-stop(100%, #dddddd));
+       background-image: -webkit-linear-gradient(top, #ffffff 0, #dddddd 100%);
+       background-image:    -moz-linear-gradient(top, #ffffff 0, #dddddd 100%);
+       background-image:         linear-gradient(to bottom, #ffffff 0, #dddddd 100%);
+       -ms-filter: "progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffffff', endColorstr='#ffdddddd' )";
+}
+.oo-ui-buttonElement-framed > .oo-ui-buttonElement-button:hover,
+.oo-ui-buttonElement-framed > .oo-ui-buttonElement-button:focus {
+       border-color: #aaaaaa;
+       outline: none;
+}
+.oo-ui-buttonElement-framed > input.oo-ui-buttonElement-button,
+.oo-ui-buttonElement-framed.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
+       line-height: 1.875em;
+}
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active,
+.oo-ui-buttonElement-framed.oo-ui-buttonElement-active > .oo-ui-buttonElement-button,
+.oo-ui-buttonElement-framed.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
+       box-shadow: inset 0 1px 4px 0 rgba(0, 0, 0, 0.07);
+       color: black;
+       border-color: #c9c9c9;
+       background-color: #eeeeee;
+       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0, #dddddd), color-stop(100%, #ffffff));
+       background-image: -webkit-linear-gradient(top, #dddddd 0, #ffffff 100%);
+       background-image:    -moz-linear-gradient(top, #dddddd 0, #ffffff 100%);
+       background-image:         linear-gradient(to bottom, #dddddd 0, #ffffff 100%);
+       -ms-filter: "progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffdddddd', endColorstr='#ffffffff' )";
+}
+.oo-ui-buttonElement-framed.oo-ui-iconElement > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon {
+       margin-left: -0.5em;
+       margin-right: -0.5em;
+}
+.oo-ui-buttonElement-framed.oo-ui-iconElement.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon {
+       margin-right: 0.3em;
+}
+.oo-ui-buttonElement-framed.oo-ui-indicatorElement > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator {
+       margin-left: -0.005em;
+       margin-right: -0.005em;
+}
+.oo-ui-buttonElement-framed.oo-ui-indicatorElement.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator,
+.oo-ui-buttonElement-framed.oo-ui-indicatorElement.oo-ui-iconElement:not( .oo-ui-labelElement ) > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator {
+       margin-left: 0.46875em;
+       margin-right: -0.275em;
+}
+.oo-ui-buttonElement-framed.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button {
+       border: 1px solid #a6cee1;
+       background-color: #cde7f4;
+       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0, #eaf4fa), color-stop(100%, #b0d9ee));
+       background-image: -webkit-linear-gradient(top, #eaf4fa 0, #b0d9ee 100%);
+       background-image:    -moz-linear-gradient(top, #eaf4fa 0, #b0d9ee 100%);
+       background-image:         linear-gradient(to bottom, #eaf4fa 0, #b0d9ee 100%);
+       -ms-filter: "progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffeaf4fa', endColorstr='#ffb0d9ee' )";
+}
+.oo-ui-buttonElement-framed.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button:hover,
+.oo-ui-buttonElement-framed.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button:focus {
+       border-color: #9dc2d4;
+}
+.oo-ui-buttonElement-framed.oo-ui-flaggedElement-progressive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active,
+.oo-ui-buttonElement-framed.oo-ui-flaggedElement-progressive.oo-ui-buttonElement-active > .oo-ui-buttonElement-button,
+.oo-ui-buttonElement-framed.oo-ui-flaggedElement-progressive.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
+       border: 1px solid #a6cee1;
+       background-color: #cde7f4;
+       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0, #b0d9ee), color-stop(100%, #eaf4fa));
+       background-image: -webkit-linear-gradient(top, #b0d9ee 0, #eaf4fa 100%);
+       background-image:    -moz-linear-gradient(top, #b0d9ee 0, #eaf4fa 100%);
+       background-image:         linear-gradient(to bottom, #b0d9ee 0, #eaf4fa 100%);
+       -ms-filter: "progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffb0d9ee', endColorstr='#ffeaf4fa' )";
+}
+.oo-ui-buttonElement-framed.oo-ui-flaggedElement-constructive > .oo-ui-buttonElement-button {
+       border: 1px solid #b8d892;
+       background-color: #daf0bd;
+       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0, #f0fbe1), color-stop(100%, #c3e59a));
+       background-image: -webkit-linear-gradient(top, #f0fbe1 0, #c3e59a 100%);
+       background-image:    -moz-linear-gradient(top, #f0fbe1 0, #c3e59a 100%);
+       background-image:         linear-gradient(to bottom, #f0fbe1 0, #c3e59a 100%);
+       -ms-filter: "progid:DXImageTransform.Microsoft.gradient( startColorstr='#fff0fbe1', endColorstr='#ffc3e59a' )";
+}
+.oo-ui-buttonElement-framed.oo-ui-flaggedElement-constructive > .oo-ui-buttonElement-button:hover,
+.oo-ui-buttonElement-framed.oo-ui-flaggedElement-constructive > .oo-ui-buttonElement-button:focus {
+       border-color: #adcb89;
+}
+.oo-ui-buttonElement-framed.oo-ui-flaggedElement-constructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active,
+.oo-ui-buttonElement-framed.oo-ui-flaggedElement-constructive.oo-ui-buttonElement-active > .oo-ui-buttonElement-button,
+.oo-ui-buttonElement-framed.oo-ui-flaggedElement-constructive.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
+       border: 1px solid #b8d892;
+       background-color: #daf0bd;
+       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0, #c3e59a), color-stop(100%, #f0fbe1));
+       background-image: -webkit-linear-gradient(top, #c3e59a 0, #f0fbe1 100%);
+       background-image:    -moz-linear-gradient(top, #c3e59a 0, #f0fbe1 100%);
+       background-image:         linear-gradient(to bottom, #c3e59a 0, #f0fbe1 100%);
+       -ms-filter: "progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffc3e59a', endColorstr='#fff0fbe1' )";
+}
+.oo-ui-buttonElement-framed.oo-ui-flaggedElement-destructive > .oo-ui-buttonElement-button {
+       color: #d45353;
+}
+.oo-ui-buttonElement-framed.oo-ui-widget-disabled > .oo-ui-buttonElement-button,
+.oo-ui-buttonElement-framed.oo-ui-widget-disabled.oo-ui-buttonElement-active > .oo-ui-buttonElement-button,
+.oo-ui-buttonElement-framed.oo-ui-widget-disabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
+       opacity: 0.5;
+       -webkit-transform: translate3d(0, 0, 0);
+       box-shadow: none;
+       color: #333333;
+       background: #eeeeee;
+       border-color: #cccccc;
+}
+.oo-ui-buttonElement-framed.oo-ui-widget-disabled > .oo-ui-buttonElement-button:hover,
+.oo-ui-buttonElement-framed.oo-ui-widget-disabled.oo-ui-buttonElement-active > .oo-ui-buttonElement-button:hover,
+.oo-ui-buttonElement-framed.oo-ui-widget-disabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button:hover,
+.oo-ui-buttonElement-framed.oo-ui-widget-disabled > .oo-ui-buttonElement-button:focus,
+.oo-ui-buttonElement-framed.oo-ui-widget-disabled.oo-ui-buttonElement-active > .oo-ui-buttonElement-button:focus,
+.oo-ui-buttonElement-framed.oo-ui-widget-disabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button:focus {
+       border-color: #cccccc;
+       box-shadow: none;
+}
+.oo-ui-clippableElement-clippable {
+       -webkit-box-sizing: border-box;
+          -moz-box-sizing: border-box;
+               box-sizing: border-box;
+}
+.oo-ui-iconElement .oo-ui-iconElement-icon,
+.oo-ui-iconElement.oo-ui-iconElement-icon {
+       background-size: contain;
+       background-position: center center;
+       background-repeat: no-repeat;
+}
+.oo-ui-iconElement .oo-ui-iconElement-icon,
+.oo-ui-iconElement.oo-ui-iconElement-icon {
+       opacity: 0.8;
+}
+.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator,
+.oo-ui-indicatorElement.oo-ui-indicatorElement-indicator {
+       background-size: contain;
+       background-position: center center;
+       background-repeat: no-repeat;
+}
+.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator,
+.oo-ui-indicatorElement.oo-ui-indicatorElement-indicator {
+       opacity: 0.8;
+}
+.oo-ui-pendingElement-pending {
+       background-image: /* @embed */ url(themes/apex/images/textures/pending.gif);
+}
+.oo-ui-fieldLayout {
+       display: block;
+       margin-bottom: 1em;
+}
+.oo-ui-fieldLayout:before,
+.oo-ui-fieldLayout:after {
+       content: " ";
+       display: table;
+}
+.oo-ui-fieldLayout:after {
+       clear: both;
+}
+.oo-ui-fieldLayout.oo-ui-fieldLayout-align-left > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label,
+.oo-ui-fieldLayout.oo-ui-fieldLayout-align-right > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label,
+.oo-ui-fieldLayout.oo-ui-fieldLayout-align-left > .oo-ui-fieldLayout-body > .oo-ui-fieldLayout-field,
+.oo-ui-fieldLayout.oo-ui-fieldLayout-align-right > .oo-ui-fieldLayout-body > .oo-ui-fieldLayout-field {
+       display: block;
+       float: left;
+}
+.oo-ui-fieldLayout.oo-ui-fieldLayout-align-right > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label {
+       text-align: right;
+}
+.oo-ui-fieldLayout.oo-ui-fieldLayout-align-inline > .oo-ui-fieldLayout-body {
+       display: table;
+}
+.oo-ui-fieldLayout.oo-ui-fieldLayout-align-inline > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label,
+.oo-ui-fieldLayout.oo-ui-fieldLayout-align-inline > .oo-ui-fieldLayout-body > .oo-ui-fieldLayout-field {
+       display: table-cell;
+       vertical-align: middle;
+}
+.oo-ui-fieldLayout.oo-ui-labelElement.oo-ui-fieldLayout-align-top > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label {
+       display: inline-block;
+}
+.oo-ui-fieldLayout > .oo-ui-fieldLayout-help {
+       float: right;
+}
+.oo-ui-fieldLayout > .oo-ui-fieldLayout-help > .oo-ui-popupWidget > .oo-ui-popupWidget-popup {
+       z-index: 1;
+}
+.oo-ui-fieldLayout > .oo-ui-fieldLayout-help .oo-ui-fieldLayout-help-content {
+       padding: 0.5em 0.75em;
+       line-height: 1.5em;
+}
+.oo-ui-fieldLayout:last-child {
+       margin-bottom: 0;
+}
+.oo-ui-fieldLayout.oo-ui-fieldLayout-align-left.oo-ui-labelElement > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label,
+.oo-ui-fieldLayout.oo-ui-fieldLayout-align-right.oo-ui-labelElement > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label {
+       padding-top: 0.5em;
+       margin-right: 5%;
+       width: 35%;
+}
+.oo-ui-fieldLayout.oo-ui-fieldLayout-align-left > .oo-ui-fieldLayout-body > .oo-ui-fieldLayout-field,
+.oo-ui-fieldLayout.oo-ui-fieldLayout-align-right > .oo-ui-fieldLayout-body > .oo-ui-fieldLayout-field {
+       width: 60%;
+}
+.oo-ui-fieldLayout.oo-ui-fieldLayout-align-inline {
+       margin-bottom: 1.25em;
+}
+.oo-ui-fieldLayout.oo-ui-fieldLayout-align-inline.oo-ui-labelElement > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label {
+       padding: 0.25em 0.25em 0.25em 0.5em;
+}
+.oo-ui-fieldLayout.oo-ui-fieldLayout-align-top.oo-ui-labelElement > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label {
+       padding: 0.5em 0;
+}
+.oo-ui-fieldLayout > .oo-ui-popupButtonWidget {
+       margin-right: 0;
+       margin-top: 0.25em;
+}
+.oo-ui-fieldLayout > .oo-ui-popupButtonWidget:last-child {
+       margin-right: 0;
+}
+.oo-ui-fieldLayout-disabled > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label {
+       color: #cccccc;
+}
+.oo-ui-fieldLayout-messages {
+       list-style: none none;
+       margin: 0;
+       padding: 0;
+       margin-top: 0.25em;
+       margin-left: 0.25em;
+}
+.oo-ui-fieldLayout-messages > li {
+       margin: 0;
+       padding: 0;
+}
+.oo-ui-fieldLayout-messages .oo-ui-iconWidget {
+       display: none;
+}
+.oo-ui-fieldLayout-messages .oo-ui-fieldLayout-messages-error {
+       color: #d45353;
+}
+.oo-ui-fieldLayout-messages .oo-ui-labelWidget {
+       padding: 0;
+       line-height: 1.875em;
+       vertical-align: middle;
+}
+.oo-ui-actionFieldLayout-input,
+.oo-ui-actionFieldLayout-button {
+       display: table-cell;
+       vertical-align: middle;
+}
+.oo-ui-actionFieldLayout-input {
+       padding-right: 1em;
+}
+.oo-ui-actionFieldLayout-button {
+       width: 1%;
+       white-space: nowrap;
+}
+.oo-ui-fieldsetLayout {
+       position: relative;
+       margin: 0;
+       padding: 0;
+       border: none;
+}
+.oo-ui-fieldsetLayout.oo-ui-iconElement > .oo-ui-iconElement-icon {
+       display: block;
+       position: absolute;
+}
+.oo-ui-fieldsetLayout.oo-ui-labelElement > .oo-ui-labelElement-label {
+       display: inline-block;
+}
+.oo-ui-fieldsetLayout > .oo-ui-fieldsetLayout-help {
+       float: right;
+}
+.oo-ui-fieldsetLayout > .oo-ui-fieldsetLayout-help > .oo-ui-popupWidget > .oo-ui-popupWidget-popup {
+       z-index: 1;
+}
+.oo-ui-fieldsetLayout > .oo-ui-fieldsetLayout-help .oo-ui-fieldsetLayout-help-content {
+       padding: 0.5em 0.75em;
+       line-height: 1.5em;
+}
+.oo-ui-fieldsetLayout + .oo-ui-fieldsetLayout,
+.oo-ui-fieldsetLayout + .oo-ui-formLayout {
+       margin-top: 2em;
+}
+.oo-ui-fieldsetLayout > .oo-ui-labelElement-label {
+       font-size: 1.1em;
+       margin-bottom: 0.5em;
+       padding: 0.25em 0;
+       font-weight: bold;
+}
+.oo-ui-fieldsetLayout.oo-ui-iconElement > .oo-ui-labelElement-label {
+       padding-left: 2em;
+       line-height: 1.8em;
+}
+.oo-ui-fieldsetLayout.oo-ui-iconElement > .oo-ui-iconElement-icon {
+       left: 0;
+       top: 0.25em;
+       width: 1.875em;
+       height: 1.875em;
+}
+.oo-ui-fieldsetLayout > .oo-ui-popupButtonWidget {
+       margin-right: 0;
+}
+.oo-ui-fieldsetLayout > .oo-ui-popupButtonWidget:last-child {
+       margin-right: 0;
+}
+.oo-ui-formLayout + .oo-ui-fieldsetLayout,
+.oo-ui-formLayout + .oo-ui-formLayout {
+       margin-top: 2em;
+}
+.oo-ui-panelLayout {
+       position: relative;
+}
+.oo-ui-panelLayout-scrollable {
+       overflow-y: auto;
+}
+.oo-ui-panelLayout-expanded {
+       position: absolute;
+       top: 0;
+       left: 0;
+       right: 0;
+       bottom: 0;
+}
+.oo-ui-panelLayout-padded {
+       padding: 1.25em;
+}
+.oo-ui-panelLayout-framed {
+       border-radius: 0.5em;
+       box-shadow: 0 0.25em 1em rgba(0, 0, 0, 0.25);
+}
+.oo-ui-panelLayout-padded.oo-ui-panelLayout-framed {
+       margin: 1em 0;
+}
+.oo-ui-horizontalLayout > .oo-ui-widget {
+       display: inline-block;
+       vertical-align: middle;
+}
+.oo-ui-horizontalLayout > .oo-ui-layout {
+       display: inline-block;
+}
+.oo-ui-horizontalLayout > .oo-ui-layout,
+.oo-ui-horizontalLayout > .oo-ui-widget {
+       margin-right: 0.5em;
+}
+.oo-ui-horizontalLayout > .oo-ui-layout:last-child,
+.oo-ui-horizontalLayout > .oo-ui-widget:last-child {
+       margin-right: 0;
+}
+.oo-ui-horizontalLayout > .oo-ui-layout {
+       margin-bottom: 0;
+}
+.oo-ui-optionWidget {
+       position: relative;
+       display: block;
+       padding: 0.25em 0.5em;
+       border: none;
+}
+.oo-ui-optionWidget.oo-ui-widget-enabled {
+       cursor: pointer;
+}
+.oo-ui-optionWidget.oo-ui-labelElement .oo-ui-labelElement-label {
+       display: block;
+       white-space: nowrap;
+       text-overflow: ellipsis;
+       overflow: hidden;
+}
+.oo-ui-optionWidget-highlighted {
+       background-color: #e1f3ff;
+}
+.oo-ui-optionWidget .oo-ui-labelElement-label {
+       line-height: 1.5em;
+}
+.oo-ui-selectWidget-depressed .oo-ui-optionWidget-selected {
+       background-color: #a7dcff;
+}
+.oo-ui-selectWidget-pressed .oo-ui-optionWidget-pressed,
+.oo-ui-selectWidget-pressed .oo-ui-optionWidget-pressed.oo-ui-optionWidget-highlighted,
+.oo-ui-selectWidget-pressed .oo-ui-optionWidget-pressed.oo-ui-optionWidget-highlighted.oo-ui-optionWidget-selected {
+       background-color: #a7dcff;
+}
+.oo-ui-optionWidget.oo-ui-widget-disabled {
+       color: #cccccc;
+}
+.oo-ui-decoratedOptionWidget {
+       padding: 0.5em 2em 0.5em 3em;
+}
+.oo-ui-decoratedOptionWidget .oo-ui-iconElement-icon,
+.oo-ui-decoratedOptionWidget .oo-ui-indicatorElement-indicator {
+       position: absolute;
+}
+.oo-ui-decoratedOptionWidget.oo-ui-iconElement .oo-ui-iconElement-icon,
+.oo-ui-decoratedOptionWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
+       top: 0;
+       height: 100%;
+}
+.oo-ui-decoratedOptionWidget.oo-ui-iconElement .oo-ui-iconElement-icon {
+       width: 1.875em;
+       left: 0.5em;
+}
+.oo-ui-decoratedOptionWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
+       width: 0.9375em;
+       right: 0.5em;
+}
+.oo-ui-decoratedOptionWidget.oo-ui-widget-disabled .oo-ui-iconElement-icon,
+.oo-ui-decoratedOptionWidget.oo-ui-widget-disabled .oo-ui-indicatorElement-indicator {
+       opacity: 0.2;
+}
+.oo-ui-radioSelectWidget {
+       padding: 0.75em 0 0.5em 0;
+}
+.oo-ui-radioOptionWidget {
+       cursor: default;
+       padding: 0;
+       background-color: transparent;
+}
+.oo-ui-radioOptionWidget .oo-ui-radioInputWidget,
+.oo-ui-radioOptionWidget.oo-ui-labelElement .oo-ui-labelElement-label {
+       display: inline-block;
+       vertical-align: middle;
+}
+.oo-ui-radioOptionWidget.oo-ui-optionWidget-selected,
+.oo-ui-radioOptionWidget.oo-ui-optionWidget-pressed,
+.oo-ui-radioOptionWidget.oo-ui-optionWidget-highlighted {
+       background-color: transparent;
+}
+.oo-ui-radioOptionWidget.oo-ui-labelElement .oo-ui-labelElement-label {
+       padding-left: 0.5em;
+}
+.oo-ui-radioOptionWidget .oo-ui-radioInputWidget {
+       margin-right: 0;
+}
+.oo-ui-labelWidget {
+       display: inline-block;
+       padding: 0.5em 0;
+}
+.oo-ui-iconWidget {
+       display: inline-block;
+       vertical-align: middle;
+       line-height: 2.5em;
+       height: 1.875em;
+       width: 1.875em;
+}
+.oo-ui-iconWidget.oo-ui-widget-disabled {
+       opacity: 0.2;
+}
+.oo-ui-indicatorWidget {
+       display: inline-block;
+       vertical-align: middle;
+       line-height: 2.5em;
+       height: 0.9375em;
+       width: 0.9375em;
+       margin: 0.46875em;
+}
+.oo-ui-indicatorWidget.oo-ui-widget-disabled {
+       opacity: 0.2;
+}
+.oo-ui-buttonWidget {
+       display: inline-block;
+       vertical-align: middle;
+       margin-right: 0.5em;
+}
+.oo-ui-buttonWidget:last-child {
+       margin-right: 0;
+}
+.oo-ui-buttonGroupWidget {
+       display: inline-block;
+       white-space: nowrap;
+       border-radius: 0.3em;
+       margin-right: 0.5em;
+}
+.oo-ui-buttonGroupWidget:last-child {
+       margin-right: 0;
+}
+.oo-ui-buttonGroupWidget .oo-ui-buttonElement {
+       margin-right: 0;
+}
+.oo-ui-buttonGroupWidget .oo-ui-buttonElement:last-child {
+       margin-right: 0;
+}
+.oo-ui-buttonGroupWidget .oo-ui-buttonElement-framed .oo-ui-buttonElement-button {
+       border-radius: 0;
+       margin-left: -1px;
+}
+.oo-ui-buttonGroupWidget .oo-ui-buttonElement-framed:first-child .oo-ui-buttonElement-button {
+       border-bottom-left-radius: 0.3em;
+       border-top-left-radius: 0.3em;
+       margin-left: 0;
+}
+.oo-ui-buttonGroupWidget .oo-ui-buttonElement-framed:last-child .oo-ui-buttonElement-button {
+       border-bottom-right-radius: 0.3em;
+       border-top-right-radius: 0.3em;
+}
+.oo-ui-popupWidget {
+       position: absolute;
+       /* @noflip */
+       left: 0;
+}
+.oo-ui-popupWidget-popup {
+       position: relative;
+       overflow: hidden;
+       z-index: 1;
+}
+.oo-ui-popupWidget-anchor {
+       display: none;
+       z-index: 1;
+}
+.oo-ui-popupWidget-anchored .oo-ui-popupWidget-anchor {
+       display: block;
+       position: absolute;
+       top: 0;
+       /* @noflip */
+       left: 0;
+       background-repeat: no-repeat;
+}
+.oo-ui-popupWidget-head {
+       -webkit-touch-callout: none;
+       -webkit-user-select: none;
+          -moz-user-select: none;
+           -ms-user-select: none;
+               user-select: none;
+}
+.oo-ui-popupWidget-head > .oo-ui-buttonWidget {
+       float: right;
+}
+.oo-ui-popupWidget-head > .oo-ui-labelElement-label {
+       float: left;
+       cursor: default;
+}
+.oo-ui-popupWidget-body {
+       clear: both;
+       overflow: hidden;
+}
+.oo-ui-popupWidget-popup {
+       background-color: #ffffff;
+       border: 1px solid #cccccc;
+       border-radius: 0.25em;
+       box-shadow: 0 0.15em 0.5em 0 rgba(0, 0, 0, 0.2);
+}
+.oo-ui-popupWidget-anchored .oo-ui-popupWidget-popup {
+       margin-top: 6px;
+}
+.oo-ui-popupWidget-anchored .oo-ui-popupWidget-anchor:before,
+.oo-ui-popupWidget-anchored .oo-ui-popupWidget-anchor:after {
+       content: "";
+       position: absolute;
+       width: 0;
+       height: 0;
+       border-style: solid;
+       border-color: transparent;
+       border-top: 0;
+}
+.oo-ui-popupWidget-anchored .oo-ui-popupWidget-anchor:before {
+       bottom: -7px;
+       left: -6px;
+       border-bottom-color: #aaaaaa;
+       border-width: 7px;
+}
+.oo-ui-popupWidget-anchored .oo-ui-popupWidget-anchor:after {
+       bottom: -7px;
+       left: -5px;
+       border-bottom-color: #ffffff;
+       border-width: 6px;
+}
+.oo-ui-popupWidget-transitioning .oo-ui-popupWidget-popup {
+       -webkit-transition: width 100ms ease, height 100ms ease, left 100ms ease;
+          -moz-transition: width 100ms ease, height 100ms ease, left 100ms ease;
+               transition: width 100ms ease, height 100ms ease, left 100ms ease;
+}
+.oo-ui-popupWidget-head {
+       height: 2.5em;
+}
+.oo-ui-popupWidget-head > .oo-ui-buttonWidget {
+       margin: 0.25em;
+}
+.oo-ui-popupWidget-head > .oo-ui-labelElement-label {
+       margin: 0.75em 1em;
+}
+.oo-ui-popupWidget-body-padded {
+       padding: 0 1em;
+}
+.oo-ui-popupButtonWidget {
+       position: relative;
+}
+.oo-ui-popupButtonWidget .oo-ui-popupWidget {
+       position: absolute;
+       cursor: auto;
+}
+.oo-ui-popupButtonWidget.oo-ui-buttonElement-frameless > .oo-ui-popupWidget {
+       /* @noflip */
+       left: 1em;
+}
+.oo-ui-popupButtonWidget.oo-ui-buttonElement-framed > .oo-ui-popupWidget {
+       /* @noflip */
+       left: 1.25em;
+}
+.oo-ui-inputWidget {
+       margin-right: 0.5em;
+}
+.oo-ui-inputWidget:last-child {
+       margin-right: 0;
+}
+.oo-ui-buttonInputWidget {
+       display: inline-block;
+       vertical-align: middle;
+}
+.oo-ui-buttonInputWidget > button,
+.oo-ui-buttonInputWidget > input {
+       border: 0;
+       padding: 0;
+       background-color: transparent;
+}
+.oo-ui-dropdownInputWidget {
+       position: relative;
+       vertical-align: middle;
+       -webkit-box-sizing: border-box;
+          -moz-box-sizing: border-box;
+               box-sizing: border-box;
+       width: 100%;
+       max-width: 50em;
+}
+.oo-ui-dropdownInputWidget select {
+       display: inline-block;
+       width: 100%;
+       resize: none;
+       -webkit-box-sizing: border-box;
+          -moz-box-sizing: border-box;
+               box-sizing: border-box;
+}
+.oo-ui-dropdownInputWidget select {
+       background-color: #ffffff;
+       height: 2.5em;
+       padding: 0.5em;
+       font-size: inherit;
+       font-family: inherit;
+       border: 1px solid rgba(0, 0, 0, 0.1);
+       border-radius: 0.25em;
+}
+.oo-ui-dropdownInputWidget.oo-ui-widget-enabled select:hover,
+.oo-ui-dropdownInputWidget.oo-ui-widget-enabled select:focus {
+       border-color: rgba(0, 0, 0, 0.2);
+       outline: none;
+}
+.oo-ui-dropdownInputWidget.oo-ui-widget-disabled select {
+       color: #cccccc;
+       border-color: #dddddd;
+       background-color: #f3f3f3;
+}
+.oo-ui-radioSelectInputWidget .oo-ui-fieldLayout {
+       margin-bottom: 0;
+}
+.oo-ui-textInputWidget {
+       position: relative;
+       vertical-align: middle;
+       -webkit-box-sizing: border-box;
+          -moz-box-sizing: border-box;
+               box-sizing: border-box;
+       width: 100%;
+       max-width: 50em;
+}
+.oo-ui-textInputWidget input,
+.oo-ui-textInputWidget textarea {
+       display: inline-block;
+       width: 100%;
+       resize: none;
+       -webkit-box-sizing: border-box;
+          -moz-box-sizing: border-box;
+               box-sizing: border-box;
+}
+.oo-ui-textInputWidget textarea {
+       overflow: auto;
+}
+.oo-ui-textInputWidget input[type="search"] {
+       -webkit-appearance: none;
+}
+.oo-ui-textInputWidget input[type="search"]::-ms-clear {
+       display: none;
+}
+.oo-ui-textInputWidget input[type="search"]::-ms-reveal {
+       display: none;
+}
+.oo-ui-textInputWidget input[type="search"]::-webkit-search-decoration,
+.oo-ui-textInputWidget input[type="search"]::-webkit-search-cancel-button,
+.oo-ui-textInputWidget input[type="search"]::-webkit-search-results-button,
+.oo-ui-textInputWidget input[type="search"]::-webkit-search-results-decoration {
+       display: none;
+}
+.oo-ui-textInputWidget > .oo-ui-iconElement-icon,
+.oo-ui-textInputWidget > .oo-ui-indicatorElement-indicator,
+.oo-ui-textInputWidget > .oo-ui-labelElement-label {
+       display: none;
+}
+.oo-ui-textInputWidget.oo-ui-iconElement > .oo-ui-iconElement-icon,
+.oo-ui-textInputWidget.oo-ui-indicatorElement > .oo-ui-indicatorElement-indicator {
+       display: block;
+       position: absolute;
+       top: 0;
+       height: 100%;
+       -webkit-touch-callout: none;
+       -webkit-user-select: none;
+          -moz-user-select: none;
+           -ms-user-select: none;
+               user-select: none;
+}
+.oo-ui-textInputWidget.oo-ui-widget-enabled > .oo-ui-iconElement-icon,
+.oo-ui-textInputWidget.oo-ui-widget-enabled > .oo-ui-indicatorElement-indicator {
+       cursor: text;
+}
+.oo-ui-textInputWidget.oo-ui-widget-enabled.oo-ui-textInputWidget-type-search > .oo-ui-indicatorElement-indicator {
+       cursor: pointer;
+}
+.oo-ui-textInputWidget.oo-ui-labelElement > .oo-ui-labelElement-label {
+       display: block;
+}
+.oo-ui-textInputWidget > .oo-ui-iconElement-icon {
+       left: 0;
+}
+.oo-ui-textInputWidget > .oo-ui-indicatorElement-indicator {
+       right: 0;
+}
+.oo-ui-textInputWidget > .oo-ui-labelElement-label {
+       position: absolute;
+       top: 0;
+}
+.oo-ui-textInputWidget-labelPosition-after > .oo-ui-labelElement-label {
+       right: 0;
+}
+.oo-ui-textInputWidget-labelPosition-before > .oo-ui-labelElement-label {
+       left: 0;
+}
+.oo-ui-textInputWidget input,
+.oo-ui-textInputWidget textarea {
+       padding: 0.5em;
+       line-height: 1.275em;
+       font-size: inherit;
+       font-family: inherit;
+       background-color: #ffffff;
+       color: black;
+       border: 1px solid #cccccc;
+       box-shadow: 0 0 0 white, inset 0 0.1em 0.2em #dddddd;
+       border-radius: 0.25em;
+       -webkit-transition: border-color 250ms ease, box-shadow 250ms ease;
+          -moz-transition: border-color 250ms ease, box-shadow 250ms ease;
+               transition: border-color 250ms ease, box-shadow 250ms ease;
+}
+.oo-ui-textInputWidget input.oo-ui-pendingElement-pending,
+.oo-ui-textInputWidget textarea.oo-ui-pendingElement-pending {
+       background-color: transparent;
+}
+.oo-ui-textInputWidget.oo-ui-widget-enabled input:focus,
+.oo-ui-textInputWidget.oo-ui-widget-enabled textarea:focus {
+       outline: none;
+       border-color: #a7dcff;
+       box-shadow: 0 0 0.3em #a7dcff, 0 0 0 white;
+}
+.oo-ui-textInputWidget.oo-ui-widget-enabled input[readonly],
+.oo-ui-textInputWidget.oo-ui-widget-enabled textarea[readonly] {
+       color: #777777;
+}
+.oo-ui-textInputWidget.oo-ui-widget-enabled.oo-ui-flaggedElement-invalid input,
+.oo-ui-textInputWidget.oo-ui-widget-enabled.oo-ui-flaggedElement-invalid textarea {
+       background-color: #ffdddd;
+}
+.oo-ui-textInputWidget.oo-ui-widget-disabled input,
+.oo-ui-textInputWidget.oo-ui-widget-disabled textarea {
+       color: #cccccc;
+       text-shadow: 0 1px 1px #ffffff;
+       border-color: #dddddd;
+       background-color: #f3f3f3;
+}
+.oo-ui-textInputWidget.oo-ui-widget-disabled .oo-ui-iconElement-icon,
+.oo-ui-textInputWidget.oo-ui-widget-disabled .oo-ui-indicatorElement-indicator {
+       opacity: 0.2;
+}
+.oo-ui-textInputWidget.oo-ui-widget-disabled .oo-ui-labelElement-label {
+       color: #dddddd;
+       text-shadow: 0 1px 1px #ffffff;
+}
+.oo-ui-textInputWidget.oo-ui-iconElement input,
+.oo-ui-textInputWidget.oo-ui-iconElement textarea {
+       padding-left: 2.475em;
+}
+.oo-ui-textInputWidget.oo-ui-iconElement .oo-ui-iconElement-icon {
+       width: 1.875em;
+       max-height: 2.375em;
+       margin-left: 0.3em;
+}
+.oo-ui-textInputWidget.oo-ui-indicatorElement input,
+.oo-ui-textInputWidget.oo-ui-indicatorElement textarea {
+       padding-right: 2.4875em;
+}
+.oo-ui-textInputWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
+       width: 0.9375em;
+       max-height: 2.375em;
+       margin-right: 0.775em;
+}
+.oo-ui-textInputWidget > .oo-ui-labelElement-label {
+       padding: 0.4em;
+       line-height: 1.5em;
+       color: #888888;
+}
+.oo-ui-textInputWidget-labelPosition-after.oo-ui-indicatorElement > .oo-ui-labelElement-label {
+       margin-right: 2.0875em;
+}
+.oo-ui-textInputWidget-labelPosition-before.oo-ui-iconElement > .oo-ui-labelElement-label {
+       margin-left: 2.075em;
+}
+.oo-ui-menuSelectWidget {
+       position: absolute;
+       background-color: #ffffff;
+       margin-top: -1px;
+       border: 1px solid #cccccc;
+       border-radius: 0 0 0.25em 0.25em;
+       box-shadow: 0 0.15em 1em 0 rgba(0, 0, 0, 0.2);
+}
+.oo-ui-menuSelectWidget input {
+       position: absolute;
+       width: 0;
+       height: 0;
+       overflow: hidden;
+       opacity: 0;
+}
+.oo-ui-menuOptionWidget {
+       position: relative;
+}
+.oo-ui-menuOptionWidget .oo-ui-iconElement-icon {
+       display: none;
+}
+.oo-ui-menuOptionWidget.oo-ui-optionWidget-selected {
+       background-color: transparent;
+}
+.oo-ui-menuOptionWidget.oo-ui-optionWidget-selected .oo-ui-iconElement-icon {
+       display: block;
+}
+.oo-ui-menuOptionWidget.oo-ui-optionWidget-selected {
+       background-color: transparent;
+}
+.oo-ui-menuOptionWidget.oo-ui-optionWidget-highlighted,
+.oo-ui-menuOptionWidget.oo-ui-optionWidget-highlighted.oo-ui-optionWidget-selected {
+       background-color: #e1f3ff;
+}
+.oo-ui-menuSectionOptionWidget {
+       cursor: default;
+       padding: 0.33em 0.75em;
+       color: #888888;
+}
+.oo-ui-dropdownWidget {
+       display: inline-block;
+       position: relative;
+       width: 100%;
+       max-width: 50em;
+       background-color: #ffffff;
+       margin-right: 0.5em;
+}
+.oo-ui-dropdownWidget-handle {
+       width: 100%;
+       display: inline-block;
+       white-space: nowrap;
+       overflow: hidden;
+       text-overflow: ellipsis;
+       -webkit-touch-callout: none;
+       -webkit-user-select: none;
+          -moz-user-select: none;
+           -ms-user-select: none;
+               user-select: none;
+       -webkit-box-sizing: border-box;
+          -moz-box-sizing: border-box;
+               box-sizing: border-box;
+}
+.oo-ui-dropdownWidget-handle .oo-ui-indicatorElement-indicator,
+.oo-ui-dropdownWidget-handle .oo-ui-iconElement-icon {
+       position: absolute;
+}
+.oo-ui-dropdownWidget > .oo-ui-menuSelectWidget {
+       z-index: 1;
+       width: 100%;
+}
+.oo-ui-dropdownWidget.oo-ui-widget-enabled .oo-ui-dropdownWidget-handle {
+       cursor: pointer;
+}
+.oo-ui-dropdownWidget:last-child {
+       margin-right: 0;
+}
+.oo-ui-dropdownWidget-handle {
+       height: 2.5em;
+       border: 1px solid rgba(0, 0, 0, 0.1);
+       border-radius: 0.25em;
+}
+.oo-ui-dropdownWidget-handle:hover {
+       border-color: rgba(0, 0, 0, 0.2);
+}
+.oo-ui-dropdownWidget-handle .oo-ui-indicatorElement-indicator {
+       right: 0;
+}
+.oo-ui-dropdownWidget-handle .oo-ui-iconElement-icon {
+       left: 0.25em;
+}
+.oo-ui-dropdownWidget-handle .oo-ui-labelElement-label {
+       line-height: 2.5em;
+       margin: 0 0.5em;
+}
+.oo-ui-dropdownWidget-handle .oo-ui-indicatorElement-indicator {
+       top: 0;
+       width: 0.9375em;
+       height: 0.9375em;
+       margin: 0.775em;
+}
+.oo-ui-dropdownWidget-handle .oo-ui-iconElement-icon {
+       top: 0;
+       width: 1.875em;
+       height: 1.875em;
+       margin: 0.3em;
+}
+.oo-ui-dropdownWidget.oo-ui-widget-disabled .oo-ui-dropdownWidget-handle {
+       color: #cccccc;
+       text-shadow: 0 1px 1px #ffffff;
+       border-color: #dddddd;
+       background-color: #f3f3f3;
+}
+.oo-ui-dropdownWidget.oo-ui-widget-disabled .oo-ui-dropdownWidget-handle:focus {
+       outline: 0;
+}
+.oo-ui-dropdownWidget.oo-ui-widget-disabled .oo-ui-indicatorElement-indicator {
+       opacity: 0.2;
+}
+.oo-ui-dropdownWidget.oo-ui-iconElement .oo-ui-dropdownWidget-handle .oo-ui-labelElement-label {
+       margin-left: 3em;
+}
+.oo-ui-dropdownWidget.oo-ui-indicatorElement .oo-ui-dropdownWidget-handle .oo-ui-labelElement-label {
+       margin-right: 2em;
+}
+.oo-ui-comboBoxInputWidget {
+       display: inline-block;
+       position: relative;
+       width: 100%;
+       max-width: 50em;
+       margin-right: 0.5em;
+}
+.oo-ui-comboBoxInputWidget > .oo-ui-menuSelectWidget {
+       z-index: 1;
+       width: 100%;
+}
+.oo-ui-comboBoxInputWidget.oo-ui-widget-enabled > .oo-ui-indicatorElement-indicator {
+       cursor: pointer;
+}
+.oo-ui-comboBoxInputWidget-php input::-webkit-calendar-picker-indicator {
+       opacity: 0 !important;
+       position: absolute;
+       right: 0;
+       top: 0;
+       height: 2.5em;
+       width: 2.5em;
+       padding: 0;
+}
+.oo-ui-comboBoxInputWidget-php > .oo-ui-indicatorElement-indicator {
+       pointer-events: none;
+}
+.oo-ui-comboBoxInputWidget:last-child {
+       margin-right: 0;
+}
+.oo-ui-comboBoxInputWidget.oo-ui-widget-disabled .oo-ui-textInputWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator,
+.oo-ui-comboBoxInputWidget-empty .oo-ui-textInputWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
+       cursor: default;
+       opacity: 0.2;
+}
+.oo-ui-comboBoxInputWidget > .oo-ui-selectWidget {
+       margin-top: -3px;
+}
diff --git a/resources/lib/oojs-ui/oojs-ui-core-mediawiki.css b/resources/lib/oojs-ui/oojs-ui-core-mediawiki.css
new file mode 100644 (file)
index 0000000..51abc2d
--- /dev/null
@@ -0,0 +1,1356 @@
+/*!
+ * OOjs UI v0.15.3
+ * https://www.mediawiki.org/wiki/OOjs_UI
+ *
+ * Copyright 2011–2016 OOjs UI Team and other contributors.
+ * Released under the MIT license
+ * http://oojs.mit-license.org
+ *
+ * Date: 2016-02-09T21:21:21Z
+ */
+.oo-ui-element-hidden {
+       display: none !important;
+}
+.oo-ui-buttonElement > .oo-ui-buttonElement-button {
+       cursor: pointer;
+       display: inline-block;
+       vertical-align: middle;
+       font: inherit;
+       white-space: nowrap;
+       -webkit-touch-callout: none;
+       -webkit-user-select: none;
+          -moz-user-select: none;
+           -ms-user-select: none;
+               user-select: none;
+}
+.oo-ui-buttonElement > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon,
+.oo-ui-buttonElement > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator {
+       display: none;
+}
+.oo-ui-buttonElement.oo-ui-widget-disabled > .oo-ui-buttonElement-button {
+       cursor: default;
+}
+.oo-ui-buttonElement.oo-ui-indicatorElement > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator,
+.oo-ui-buttonElement.oo-ui-iconElement > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon {
+       display: inline-block;
+       vertical-align: middle;
+}
+.oo-ui-buttonElement-frameless {
+       display: inline-block;
+       position: relative;
+}
+.oo-ui-buttonElement-frameless.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
+       display: inline-block;
+       vertical-align: middle;
+}
+.oo-ui-buttonElement-framed > .oo-ui-buttonElement-button {
+       display: inline-block;
+       vertical-align: top;
+       text-align: center;
+}
+.oo-ui-buttonElement-framed.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
+       display: inline-block;
+       vertical-align: middle;
+}
+.oo-ui-buttonElement-framed.oo-ui-widget-disabled > .oo-ui-buttonElement-button,
+.oo-ui-buttonElement-framed.oo-ui-widget-disabled.oo-ui-buttonElement-active > .oo-ui-buttonElement-button,
+.oo-ui-buttonElement-framed.oo-ui-widget-disabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
+       cursor: default;
+}
+.oo-ui-buttonElement > .oo-ui-buttonElement-button {
+       font-weight: bold;
+       text-decoration: none;
+}
+.oo-ui-buttonElement.oo-ui-iconElement > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon {
+       margin-left: 0;
+}
+.oo-ui-buttonElement.oo-ui-indicatorElement > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator {
+       width: 0.9375em;
+       height: 0.9375em;
+}
+.oo-ui-buttonElement.oo-ui-iconElement > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator {
+       margin-left: 0.46875em;
+}
+.oo-ui-buttonElement.oo-ui-iconElement > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon {
+       width: 1.875em;
+       height: 1.875em;
+}
+.oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button:focus {
+       box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.2), 0 0 0 1px rgba(0, 0, 0, 0.2);
+       outline: none;
+}
+.oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button .oo-ui-indicatorElement-indicator {
+       margin-right: 0;
+}
+.oo-ui-buttonElement-frameless.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
+       margin-left: 0.25em;
+       margin-right: 0.25em;
+}
+.oo-ui-buttonElement-frameless > input.oo-ui-buttonElement-button {
+       padding-left: 0.25em;
+       padding-right: 0.25em;
+       color: #333333;
+}
+.oo-ui-buttonElement-frameless.oo-ui-widget-enabled > input.oo-ui-buttonElement-button,
+.oo-ui-buttonElement-frameless.oo-ui-widget-enabled > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
+       color: #555555;
+}
+.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > input.oo-ui-buttonElement-button,
+.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
+       color: #444444;
+}
+.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button:hover > .oo-ui-labelElement-label,
+.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button:focus > .oo-ui-labelElement-label {
+       color: #2962cc;
+}
+.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
+       color: #347bff;
+}
+.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active > .oo-ui-labelElement-label,
+.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
+       color: #1f4999;
+       box-shadow: none;
+}
+.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive > .oo-ui-buttonElement-button:hover > .oo-ui-labelElement-label,
+.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive > .oo-ui-buttonElement-button:focus > .oo-ui-labelElement-label {
+       color: #008064;
+}
+.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
+       color: #00af89;
+}
+.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active > .oo-ui-labelElement-label,
+.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
+       color: #005946;
+       box-shadow: none;
+}
+.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive > .oo-ui-buttonElement-button:hover > .oo-ui-labelElement-label,
+.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive > .oo-ui-buttonElement-button:focus > .oo-ui-labelElement-label {
+       color: #8c130d;
+}
+.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
+       color: #d11d13;
+}
+.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active > .oo-ui-labelElement-label,
+.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
+       color: #73100a;
+       box-shadow: none;
+}
+.oo-ui-buttonElement-frameless.oo-ui-widget-disabled > .oo-ui-buttonElement-button {
+       color: #cccccc;
+}
+.oo-ui-buttonElement-frameless.oo-ui-widget-disabled > .oo-ui-buttonElement-button:focus {
+       box-shadow: none;
+}
+.oo-ui-buttonElement-frameless.oo-ui-widget-disabled > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon,
+.oo-ui-buttonElement-frameless.oo-ui-widget-disabled > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator {
+       opacity: 0.2;
+}
+.oo-ui-buttonElement-framed.oo-ui-iconElement.oo-ui-labelElement > .oo-ui-buttonElement-button,
+.oo-ui-buttonElement-framed.oo-ui-iconElement.oo-ui-indicatorElement > .oo-ui-buttonElement-button {
+       padding-left: 2.4em;
+}
+.oo-ui-buttonElement-framed > .oo-ui-buttonElement-button {
+       padding: 0.5em 1em;
+       min-height: 1.2em;
+       min-width: 1em;
+       border-radius: 2px;
+       position: relative;
+       -webkit-transition: background 100ms ease, color 100ms ease, border-color 100ms ease, box-shadow 100ms ease;
+          -moz-transition: background 100ms ease, color 100ms ease, border-color 100ms ease, box-shadow 100ms ease;
+               transition: background 100ms ease, color 100ms ease, border-color 100ms ease, box-shadow 100ms ease;
+}
+.oo-ui-buttonElement-framed > .oo-ui-buttonElement-button:hover,
+.oo-ui-buttonElement-framed > .oo-ui-buttonElement-button:focus {
+       outline: none;
+}
+.oo-ui-buttonElement-framed > input.oo-ui-buttonElement-button,
+.oo-ui-buttonElement-framed.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
+       line-height: 1.2em;
+       display: inline-block;
+}
+.oo-ui-buttonElement-framed.oo-ui-iconElement > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon {
+       position: absolute;
+       top: 0.2em;
+       left: 0.5625em;
+}
+.oo-ui-buttonElement-framed.oo-ui-iconElement.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
+       margin-left: 0.3em;
+}
+.oo-ui-buttonElement-framed.oo-ui-indicatorElement > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator {
+       display: inline-block;
+}
+.oo-ui-buttonElement-framed.oo-ui-indicatorElement.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator,
+.oo-ui-buttonElement-framed.oo-ui-indicatorElement.oo-ui-iconElement:not( .oo-ui-labelElement ) > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator {
+       margin-left: 0.46875em;
+       margin-right: -0.275em;
+}
+.oo-ui-buttonElement-framed.oo-ui-indicatorElement.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator {
+       position: relative;
+       left: 0.2em;
+}
+.oo-ui-buttonElement-framed.oo-ui-widget-disabled > .oo-ui-buttonElement-button {
+       background-color: #dddddd;
+       color: #ffffff;
+       border: 1px solid #dddddd;
+}
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled > .oo-ui-buttonElement-button {
+       color: #555555;
+       background-color: #ffffff;
+       border: 1px solid #cccccc;
+}
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled > .oo-ui-buttonElement-button:hover {
+       background-color: #ebebeb;
+}
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled > .oo-ui-buttonElement-button:focus {
+       box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.2);
+}
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active,
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
+       background-color: #d9d9d9;
+       border-color: #d9d9d9;
+       box-shadow: none;
+}
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-buttonElement-active > .oo-ui-buttonElement-button {
+       background-color: #999999;
+       color: #ffffff;
+}
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button {
+       color: #347bff;
+}
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button:hover {
+       background-color: rgba(52, 123, 255, 0.1);
+       border-color: rgba(31, 73, 153, 0.5);
+}
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button:focus {
+       box-shadow: inset 0 0 0 1px #1f4999;
+       border-color: #1f4999;
+}
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active,
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
+       color: #1f4999;
+       border-color: #1f4999;
+       box-shadow: none;
+}
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive.oo-ui-widget-enabled.oo-ui-buttonElement-active > .oo-ui-buttonElement-button {
+       background-color: #999999;
+       color: #ffffff;
+}
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive > .oo-ui-buttonElement-button {
+       color: #00af89;
+}
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive > .oo-ui-buttonElement-button:hover {
+       background-color: rgba(0, 171, 137, 0.1);
+       border-color: rgba(0, 89, 70, 0.5);
+}
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive > .oo-ui-buttonElement-button:focus {
+       box-shadow: inset 0 0 0 1px #005946;
+       border-color: #005946;
+}
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active,
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
+       color: #005946;
+       border-color: #005946;
+       box-shadow: none;
+}
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive.oo-ui-widget-enabled.oo-ui-buttonElement-active > .oo-ui-buttonElement-button {
+       background-color: #999999;
+       color: #ffffff;
+}
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive > .oo-ui-buttonElement-button {
+       color: #d11d13;
+}
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive > .oo-ui-buttonElement-button:hover {
+       background-color: rgba(209, 29, 19, 0.1);
+       border-color: rgba(115, 16, 10, 0.5);
+}
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive > .oo-ui-buttonElement-button:focus {
+       box-shadow: inset 0 0 0 1px #73100a;
+       border-color: #73100a;
+}
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active,
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
+       color: #73100a;
+       border-color: #73100a;
+       box-shadow: none;
+}
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive.oo-ui-widget-enabled.oo-ui-buttonElement-active > .oo-ui-buttonElement-button {
+       background-color: #999999;
+       color: #ffffff;
+}
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button {
+       color: #ffffff;
+       background-color: #347bff;
+       border-color: #347bff;
+}
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button:hover {
+       background: #2962cc;
+       border-color: #2962cc;
+}
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button:focus {
+       box-shadow: inset 0 0 0 1px #ffffff;
+       border-color: #347bff;
+}
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-progressive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active,
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-progressive.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
+       color: #ffffff;
+       background-color: #1f4999;
+       border-color: #1f4999;
+       box-shadow: none;
+}
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-progressive.oo-ui-widget-enabled.oo-ui-buttonElement-active > .oo-ui-buttonElement-button {
+       background-color: #999999;
+       color: #ffffff;
+}
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-constructive > .oo-ui-buttonElement-button {
+       color: #ffffff;
+       background-color: #00af89;
+       border-color: #00af89;
+}
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-constructive > .oo-ui-buttonElement-button:hover {
+       background: #008064;
+       border-color: #008064;
+}
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-constructive > .oo-ui-buttonElement-button:focus {
+       box-shadow: inset 0 0 0 1px #ffffff;
+       border-color: #00af89;
+}
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-constructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active,
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-constructive.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
+       color: #ffffff;
+       background-color: #005946;
+       border-color: #005946;
+       box-shadow: none;
+}
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-constructive.oo-ui-widget-enabled.oo-ui-buttonElement-active > .oo-ui-buttonElement-button {
+       background-color: #999999;
+       color: #ffffff;
+}
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-destructive > .oo-ui-buttonElement-button {
+       color: #ffffff;
+       background-color: #d11d13;
+       border-color: #d11d13;
+}
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-destructive > .oo-ui-buttonElement-button:hover {
+       background: #8c130d;
+       border-color: #8c130d;
+}
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-destructive > .oo-ui-buttonElement-button:focus {
+       box-shadow: inset 0 0 0 1px #ffffff;
+       border-color: #d11d13;
+}
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-destructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active,
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-destructive.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
+       color: #ffffff;
+       background-color: #73100a;
+       border-color: #73100a;
+       box-shadow: none;
+}
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-destructive.oo-ui-widget-enabled.oo-ui-buttonElement-active > .oo-ui-buttonElement-button {
+       background-color: #999999;
+       color: #ffffff;
+}
+.oo-ui-clippableElement-clippable {
+       -webkit-box-sizing: border-box;
+          -moz-box-sizing: border-box;
+               box-sizing: border-box;
+}
+.oo-ui-iconElement .oo-ui-iconElement-icon,
+.oo-ui-iconElement.oo-ui-iconElement-icon {
+       background-size: contain;
+       background-position: center center;
+       background-repeat: no-repeat;
+}
+.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator,
+.oo-ui-indicatorElement.oo-ui-indicatorElement-indicator {
+       background-size: contain;
+       background-position: center center;
+       background-repeat: no-repeat;
+}
+.oo-ui-pendingElement-pending {
+       background-image: /* @embed */ url(themes/mediawiki/images/textures/pending.gif);
+}
+.oo-ui-fieldLayout {
+       display: block;
+       margin-bottom: 1em;
+}
+.oo-ui-fieldLayout:before,
+.oo-ui-fieldLayout:after {
+       content: " ";
+       display: table;
+}
+.oo-ui-fieldLayout:after {
+       clear: both;
+}
+.oo-ui-fieldLayout.oo-ui-fieldLayout-align-left > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label,
+.oo-ui-fieldLayout.oo-ui-fieldLayout-align-right > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label,
+.oo-ui-fieldLayout.oo-ui-fieldLayout-align-left > .oo-ui-fieldLayout-body > .oo-ui-fieldLayout-field,
+.oo-ui-fieldLayout.oo-ui-fieldLayout-align-right > .oo-ui-fieldLayout-body > .oo-ui-fieldLayout-field {
+       display: block;
+       float: left;
+}
+.oo-ui-fieldLayout.oo-ui-fieldLayout-align-right > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label {
+       text-align: right;
+}
+.oo-ui-fieldLayout.oo-ui-fieldLayout-align-inline > .oo-ui-fieldLayout-body {
+       display: table;
+}
+.oo-ui-fieldLayout.oo-ui-fieldLayout-align-inline > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label,
+.oo-ui-fieldLayout.oo-ui-fieldLayout-align-inline > .oo-ui-fieldLayout-body > .oo-ui-fieldLayout-field {
+       display: table-cell;
+       vertical-align: middle;
+}
+.oo-ui-fieldLayout.oo-ui-labelElement.oo-ui-fieldLayout-align-top > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label {
+       display: inline-block;
+}
+.oo-ui-fieldLayout > .oo-ui-fieldLayout-help {
+       float: right;
+}
+.oo-ui-fieldLayout > .oo-ui-fieldLayout-help > .oo-ui-popupWidget > .oo-ui-popupWidget-popup {
+       z-index: 1;
+}
+.oo-ui-fieldLayout > .oo-ui-fieldLayout-help .oo-ui-fieldLayout-help-content {
+       padding: 0.5em 0.75em;
+       line-height: 1.5em;
+}
+.oo-ui-fieldLayout:last-child {
+       margin-bottom: 0;
+}
+.oo-ui-fieldLayout.oo-ui-fieldLayout-align-left.oo-ui-labelElement > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label,
+.oo-ui-fieldLayout.oo-ui-fieldLayout-align-right.oo-ui-labelElement > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label {
+       padding-top: 0.5em;
+       margin-right: 5%;
+       width: 35%;
+}
+.oo-ui-fieldLayout.oo-ui-fieldLayout-align-left > .oo-ui-fieldLayout-body > .oo-ui-fieldLayout-field,
+.oo-ui-fieldLayout.oo-ui-fieldLayout-align-right > .oo-ui-fieldLayout-body > .oo-ui-fieldLayout-field {
+       width: 60%;
+}
+.oo-ui-fieldLayout.oo-ui-fieldLayout-align-inline {
+       margin-bottom: 1.25em;
+}
+.oo-ui-fieldLayout.oo-ui-fieldLayout-align-inline.oo-ui-labelElement > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label {
+       padding: 0.25em 0.25em 0.25em 1em;
+}
+.oo-ui-fieldLayout.oo-ui-fieldLayout-align-top.oo-ui-labelElement > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label {
+       padding-top: 0.25em;
+       padding-bottom: 0.5em;
+}
+.oo-ui-fieldLayout > .oo-ui-popupButtonWidget {
+       margin-right: 0;
+}
+.oo-ui-fieldLayout > .oo-ui-popupButtonWidget:last-child {
+       margin-right: 0;
+}
+.oo-ui-fieldLayout-disabled > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label {
+       color: #cccccc;
+}
+.oo-ui-fieldLayout-messages {
+       list-style: none none;
+       margin: 0.25em 0 0 0.25em;
+       padding: 0;
+}
+.oo-ui-fieldLayout-messages > li {
+       margin: 0;
+       padding: 0;
+       display: table;
+}
+.oo-ui-fieldLayout-messages .oo-ui-iconWidget {
+       display: table-cell;
+       border-right: 0.5em solid transparent;
+}
+.oo-ui-fieldLayout-messages .oo-ui-labelWidget {
+       display: table-cell;
+       padding: 0;
+       line-height: 1.875em;
+       vertical-align: middle;
+}
+.oo-ui-actionFieldLayout-input,
+.oo-ui-actionFieldLayout-button {
+       display: table-cell;
+       vertical-align: middle;
+}
+.oo-ui-actionFieldLayout-input {
+       padding-right: 1em;
+}
+.oo-ui-actionFieldLayout-button {
+       width: 1%;
+       white-space: nowrap;
+}
+.oo-ui-fieldsetLayout {
+       position: relative;
+       margin: 0;
+       padding: 0;
+       border: 0;
+}
+.oo-ui-fieldsetLayout.oo-ui-iconElement > .oo-ui-iconElement-icon {
+       display: block;
+       position: absolute;
+}
+.oo-ui-fieldsetLayout.oo-ui-labelElement > .oo-ui-labelElement-label {
+       display: inline-block;
+}
+.oo-ui-fieldsetLayout > .oo-ui-fieldsetLayout-help {
+       float: right;
+}
+.oo-ui-fieldsetLayout > .oo-ui-fieldsetLayout-help > .oo-ui-popupWidget > .oo-ui-popupWidget-popup {
+       z-index: 1;
+}
+.oo-ui-fieldsetLayout > .oo-ui-fieldsetLayout-help .oo-ui-fieldsetLayout-help-content {
+       padding: 0.5em 0.75em;
+       line-height: 1.5em;
+}
+.oo-ui-fieldsetLayout + .oo-ui-fieldsetLayout,
+.oo-ui-fieldsetLayout + .oo-ui-formLayout {
+       margin-top: 2em;
+}
+.oo-ui-fieldsetLayout > .oo-ui-labelElement-label {
+       font-size: 1.1em;
+       margin-bottom: 0.5em;
+       padding: 0.25em 0;
+       font-weight: bold;
+}
+.oo-ui-fieldsetLayout.oo-ui-iconElement > .oo-ui-labelElement-label {
+       padding-left: 2em;
+       line-height: 1.8em;
+}
+.oo-ui-fieldsetLayout.oo-ui-iconElement > .oo-ui-iconElement-icon {
+       left: 0;
+       top: 0.25em;
+       width: 1.875em;
+       height: 1.875em;
+}
+.oo-ui-fieldsetLayout > .oo-ui-popupButtonWidget {
+       margin-right: 0;
+}
+.oo-ui-fieldsetLayout > .oo-ui-popupButtonWidget:last-child {
+       margin-right: 0;
+}
+.oo-ui-formLayout + .oo-ui-fieldsetLayout,
+.oo-ui-formLayout + .oo-ui-formLayout {
+       margin-top: 2em;
+}
+.oo-ui-panelLayout {
+       position: relative;
+}
+.oo-ui-panelLayout-scrollable {
+       overflow-y: auto;
+}
+.oo-ui-panelLayout-expanded {
+       position: absolute;
+       top: 0;
+       left: 0;
+       right: 0;
+       bottom: 0;
+}
+.oo-ui-panelLayout-padded {
+       padding: 1.25em;
+}
+.oo-ui-panelLayout-framed {
+       border: 1px solid #aaaaaa;
+       border-radius: 2px;
+       box-shadow: 0 0.15em 0 0 rgba(0, 0, 0, 0.15);
+}
+.oo-ui-panelLayout-padded.oo-ui-panelLayout-framed {
+       margin: 1em 0;
+}
+.oo-ui-horizontalLayout > .oo-ui-widget {
+       display: inline-block;
+       vertical-align: middle;
+}
+.oo-ui-horizontalLayout > .oo-ui-layout {
+       display: inline-block;
+}
+.oo-ui-horizontalLayout > .oo-ui-layout,
+.oo-ui-horizontalLayout > .oo-ui-widget {
+       margin-right: 0.5em;
+}
+.oo-ui-horizontalLayout > .oo-ui-layout:last-child,
+.oo-ui-horizontalLayout > .oo-ui-widget:last-child {
+       margin-right: 0;
+}
+.oo-ui-horizontalLayout > .oo-ui-layout {
+       margin-bottom: 0;
+}
+.oo-ui-optionWidget {
+       position: relative;
+       display: block;
+       padding: 0.25em 0.5em;
+       border: 0;
+}
+.oo-ui-optionWidget.oo-ui-widget-enabled {
+       cursor: pointer;
+}
+.oo-ui-optionWidget.oo-ui-labelElement .oo-ui-labelElement-label {
+       display: block;
+       white-space: nowrap;
+       text-overflow: ellipsis;
+       overflow: hidden;
+}
+.oo-ui-optionWidget-highlighted {
+       background-color: #eeeeee;
+}
+.oo-ui-optionWidget .oo-ui-labelElement-label {
+       line-height: 1.5em;
+}
+.oo-ui-selectWidget-depressed .oo-ui-optionWidget-selected,
+.oo-ui-selectWidget-pressed .oo-ui-optionWidget-pressed,
+.oo-ui-selectWidget-pressed .oo-ui-optionWidget-pressed.oo-ui-optionWidget-highlighted,
+.oo-ui-selectWidget-pressed .oo-ui-optionWidget-pressed.oo-ui-optionWidget-highlighted.oo-ui-optionWidget-selected {
+       background-color: #d0d0d0;
+}
+.oo-ui-optionWidget.oo-ui-widget-disabled {
+       color: #cccccc;
+}
+.oo-ui-decoratedOptionWidget {
+       padding: 0.5em 2em 0.5em 3em;
+}
+.oo-ui-decoratedOptionWidget .oo-ui-iconElement-icon,
+.oo-ui-decoratedOptionWidget .oo-ui-indicatorElement-indicator {
+       position: absolute;
+}
+.oo-ui-decoratedOptionWidget.oo-ui-iconElement .oo-ui-iconElement-icon,
+.oo-ui-decoratedOptionWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
+       top: 0;
+       height: 100%;
+}
+.oo-ui-decoratedOptionWidget.oo-ui-iconElement .oo-ui-iconElement-icon {
+       width: 1.875em;
+       left: 0.5em;
+}
+.oo-ui-decoratedOptionWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
+       width: 0.9375em;
+       right: 0.5em;
+}
+.oo-ui-decoratedOptionWidget.oo-ui-widget-disabled .oo-ui-iconElement-icon,
+.oo-ui-decoratedOptionWidget.oo-ui-widget-disabled .oo-ui-indicatorElement-indicator {
+       opacity: 0.2;
+}
+.oo-ui-radioOptionWidget {
+       cursor: default;
+       padding: 0.25em 0;
+       background-color: transparent;
+}
+.oo-ui-radioOptionWidget .oo-ui-radioInputWidget,
+.oo-ui-radioOptionWidget.oo-ui-labelElement .oo-ui-labelElement-label {
+       display: inline-block;
+       vertical-align: middle;
+}
+.oo-ui-radioOptionWidget.oo-ui-optionWidget-selected,
+.oo-ui-radioOptionWidget.oo-ui-optionWidget-pressed,
+.oo-ui-radioOptionWidget.oo-ui-optionWidget-highlighted {
+       background-color: transparent;
+}
+.oo-ui-radioOptionWidget.oo-ui-labelElement .oo-ui-labelElement-label {
+       padding: 0.25em 0.25em 0.25em 1em;
+}
+.oo-ui-radioOptionWidget .oo-ui-radioInputWidget {
+       margin-right: 0;
+}
+.oo-ui-labelWidget {
+       display: inline-block;
+}
+.oo-ui-iconWidget {
+       display: inline-block;
+       vertical-align: middle;
+       line-height: 2.5em;
+       width: 1.875em;
+       height: 1.875em;
+}
+.oo-ui-iconWidget.oo-ui-widget-disabled {
+       opacity: 0.2;
+}
+.oo-ui-indicatorWidget {
+       display: inline-block;
+       vertical-align: middle;
+       line-height: 2.5em;
+       width: 0.9375em;
+       height: 0.9375em;
+       margin: 0.46875em;
+}
+.oo-ui-indicatorWidget.oo-ui-widget-disabled {
+       opacity: 0.2;
+}
+.oo-ui-buttonWidget {
+       display: inline-block;
+       vertical-align: middle;
+       margin-right: 0.5em;
+}
+.oo-ui-buttonWidget:last-child {
+       margin-right: 0;
+}
+.oo-ui-buttonGroupWidget {
+       display: inline-block;
+       white-space: nowrap;
+       border-radius: 2px;
+       margin-right: 0.5em;
+}
+.oo-ui-buttonGroupWidget:last-child {
+       margin-right: 0;
+}
+.oo-ui-buttonGroupWidget .oo-ui-buttonElement {
+       margin-right: 0;
+}
+.oo-ui-buttonGroupWidget .oo-ui-buttonElement:last-child {
+       margin-right: 0;
+}
+.oo-ui-buttonGroupWidget .oo-ui-buttonElement-framed .oo-ui-buttonElement-button {
+       border-radius: 0;
+       margin-left: -1px;
+}
+.oo-ui-buttonGroupWidget .oo-ui-buttonElement-framed:first-child .oo-ui-buttonElement-button {
+       border-bottom-left-radius: 2px;
+       border-top-left-radius: 2px;
+       margin-left: 0;
+}
+.oo-ui-buttonGroupWidget .oo-ui-buttonElement-framed:last-child .oo-ui-buttonElement-button {
+       border-bottom-right-radius: 2px;
+       border-top-right-radius: 2px;
+}
+.oo-ui-popupWidget {
+       position: absolute;
+       /* @noflip */
+       left: 0;
+}
+.oo-ui-popupWidget-popup {
+       position: relative;
+       overflow: hidden;
+       z-index: 1;
+}
+.oo-ui-popupWidget-anchor {
+       display: none;
+       z-index: 1;
+}
+.oo-ui-popupWidget-anchored .oo-ui-popupWidget-anchor {
+       display: block;
+       position: absolute;
+       top: 0;
+       /* @noflip */
+       left: 0;
+       background-repeat: no-repeat;
+}
+.oo-ui-popupWidget-head {
+       -webkit-touch-callout: none;
+       -webkit-user-select: none;
+          -moz-user-select: none;
+           -ms-user-select: none;
+               user-select: none;
+}
+.oo-ui-popupWidget-head > .oo-ui-buttonWidget {
+       float: right;
+}
+.oo-ui-popupWidget-head > .oo-ui-labelElement-label {
+       float: left;
+       cursor: default;
+}
+.oo-ui-popupWidget-body {
+       clear: both;
+       overflow: hidden;
+}
+.oo-ui-popupWidget-popup {
+       background-color: #ffffff;
+       border: 1px solid #aaaaaa;
+       border-radius: 2px;
+       box-shadow: 0 0.15em 0 0 rgba(0, 0, 0, 0.15);
+}
+.oo-ui-popupWidget-anchored .oo-ui-popupWidget-popup {
+       margin-top: 9px;
+}
+.oo-ui-popupWidget-anchored .oo-ui-popupWidget-anchor:before,
+.oo-ui-popupWidget-anchored .oo-ui-popupWidget-anchor:after {
+       content: "";
+       position: absolute;
+       width: 0;
+       height: 0;
+       border-style: solid;
+       border-color: transparent;
+       border-top: 0;
+}
+.oo-ui-popupWidget-anchored .oo-ui-popupWidget-anchor:before {
+       bottom: -10px;
+       left: -9px;
+       border-bottom-color: #888888;
+       border-width: 10px;
+}
+.oo-ui-popupWidget-anchored .oo-ui-popupWidget-anchor:after {
+       bottom: -10px;
+       left: -8px;
+       border-bottom-color: #ffffff;
+       border-width: 9px;
+}
+.oo-ui-popupWidget-transitioning .oo-ui-popupWidget-popup {
+       -webkit-transition: width 100ms ease, height 100ms ease, left 100ms ease;
+          -moz-transition: width 100ms ease, height 100ms ease, left 100ms ease;
+               transition: width 100ms ease, height 100ms ease, left 100ms ease;
+}
+.oo-ui-popupWidget-head {
+       height: 2.5em;
+}
+.oo-ui-popupWidget-head > .oo-ui-buttonWidget {
+       margin: 0.25em;
+}
+.oo-ui-popupWidget-head > .oo-ui-labelElement-label {
+       margin: 0.75em 1em;
+}
+.oo-ui-popupWidget-body-padded {
+       padding: 0 1em;
+}
+.oo-ui-popupButtonWidget {
+       position: relative;
+}
+.oo-ui-popupButtonWidget .oo-ui-popupWidget {
+       position: absolute;
+       cursor: auto;
+}
+.oo-ui-popupButtonWidget.oo-ui-buttonElement-frameless > .oo-ui-popupWidget {
+       /* @noflip */
+       left: 1em;
+}
+.oo-ui-popupButtonWidget.oo-ui-buttonElement-framed > .oo-ui-popupWidget {
+       /* @noflip */
+       left: 1.75em;
+}
+.oo-ui-inputWidget {
+       margin-right: 0.5em;
+}
+.oo-ui-inputWidget:last-child {
+       margin-right: 0;
+}
+.oo-ui-buttonInputWidget {
+       display: inline-block;
+       vertical-align: middle;
+}
+.oo-ui-buttonInputWidget > button,
+.oo-ui-buttonInputWidget > input {
+       border: 0;
+       padding: 0;
+       background-color: transparent;
+}
+.oo-ui-checkboxInputWidget {
+       position: relative;
+       line-height: 1.6em;
+       white-space: nowrap;
+}
+.oo-ui-checkboxInputWidget * {
+       font: inherit;
+       vertical-align: middle;
+}
+.oo-ui-checkboxInputWidget input[type="checkbox"] {
+       opacity: 0;
+       z-index: 1;
+       position: relative;
+       cursor: pointer;
+       margin: 0;
+       width: 1.6em;
+       height: 1.6em;
+       max-width: none;
+}
+.oo-ui-checkboxInputWidget input[type="checkbox"] + span {
+       -webkit-transition: background-size 200ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
+          -moz-transition: background-size 200ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
+               transition: background-size 200ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
+       -webkit-box-sizing: border-box;
+          -moz-box-sizing: border-box;
+               box-sizing: border-box;
+       position: absolute;
+       left: 0;
+       border-radius: 2px;
+       width: 1.6em;
+       height: 1.6em;
+       background-color: white;
+       border: 1px solid #777777;
+       background-image: url("themes/mediawiki/images/icons/check-constructive.png");
+       background-image: -webkit-linear-gradient(transparent, transparent), /* @embed */ url("themes/mediawiki/images/icons/check-constructive.svg");
+       background-image:         linear-gradient(transparent, transparent), /* @embed */ url("themes/mediawiki/images/icons/check-constructive.svg");
+       background-image:      -o-linear-gradient(transparent, transparent), url("themes/mediawiki/images/icons/check-constructive.png");
+       background-repeat: no-repeat;
+       background-position: center center;
+       background-origin: border-box;
+       background-size: 0 0;
+}
+.oo-ui-checkboxInputWidget input[type="checkbox"]:checked + span {
+       background-size: 100% 100%;
+}
+.oo-ui-checkboxInputWidget input[type="checkbox"]:active + span {
+       background-color: #cccccc;
+       border-color: #cccccc;
+}
+.oo-ui-checkboxInputWidget input[type="checkbox"]:focus + span {
+       border-width: 2px;
+}
+.oo-ui-checkboxInputWidget input[type="checkbox"]:focus:hover + span,
+.oo-ui-checkboxInputWidget input[type="checkbox"]:hover + span {
+       border-bottom-width: 3px;
+}
+.oo-ui-checkboxInputWidget input[type="checkbox"]:disabled {
+       cursor: default;
+}
+.oo-ui-checkboxInputWidget input[type="checkbox"]:disabled + span {
+       background-color: #dddddd;
+       border-color: #dddddd;
+}
+.oo-ui-checkboxInputWidget input[type="checkbox"]:disabled:checked + span {
+       background-image: url("themes/mediawiki/images/icons/check-invert.png");
+       background-image: -webkit-linear-gradient(transparent, transparent), /* @embed */ url("themes/mediawiki/images/icons/check-invert.svg");
+       background-image:         linear-gradient(transparent, transparent), /* @embed */ url("themes/mediawiki/images/icons/check-invert.svg");
+       background-image:      -o-linear-gradient(transparent, transparent), url("themes/mediawiki/images/icons/check-invert.png");
+}
+.oo-ui-dropdownInputWidget {
+       position: relative;
+       vertical-align: middle;
+       -webkit-box-sizing: border-box;
+          -moz-box-sizing: border-box;
+               box-sizing: border-box;
+       width: 100%;
+       max-width: 50em;
+}
+.oo-ui-dropdownInputWidget select {
+       display: inline-block;
+       width: 100%;
+       resize: none;
+       -webkit-box-sizing: border-box;
+          -moz-box-sizing: border-box;
+               box-sizing: border-box;
+}
+.oo-ui-dropdownInputWidget select {
+       background-color: #ffffff;
+       height: 2.275em;
+       font-size: inherit;
+       font-family: inherit;
+       -webkit-box-sizing: border-box;
+          -moz-box-sizing: border-box;
+               box-sizing: border-box;
+       border: 1px solid #cccccc;
+       border-radius: 2px;
+       padding-left: 1em;
+       vertical-align: middle;
+}
+.oo-ui-dropdownInputWidget.oo-ui-widget-enabled select:hover,
+.oo-ui-dropdownInputWidget.oo-ui-widget-enabled select:focus {
+       border-color: #aaaaaa;
+       outline: none;
+}
+.oo-ui-dropdownInputWidget.oo-ui-widget-disabled select {
+       color: #cccccc;
+       border-color: #dddddd;
+       background-color: #f3f3f3;
+}
+.oo-ui-radioInputWidget {
+       position: relative;
+       line-height: 1.6em;
+       white-space: nowrap;
+}
+.oo-ui-radioInputWidget * {
+       font: inherit;
+       vertical-align: middle;
+}
+.oo-ui-radioInputWidget input[type="radio"] {
+       opacity: 0;
+       z-index: 1;
+       position: relative;
+       cursor: pointer;
+       margin: 0;
+       width: 1.6em;
+       height: 1.6em;
+       max-width: none;
+}
+.oo-ui-radioInputWidget input[type="radio"] + span {
+       -webkit-transition: background-size 200ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
+          -moz-transition: background-size 200ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
+               transition: background-size 200ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
+       -webkit-box-sizing: border-box;
+          -moz-box-sizing: border-box;
+               box-sizing: border-box;
+       position: absolute;
+       left: 0;
+       border-radius: 100%;
+       width: 1.6em;
+       height: 1.6em;
+       background: white;
+       border: 1px solid #777777;
+       background-image: url("themes/mediawiki/images/icons/circle-constructive.png");
+       background-image: -webkit-linear-gradient(transparent, transparent), /* @embed */ url("themes/mediawiki/images/icons/circle-constructive.svg");
+       background-image:         linear-gradient(transparent, transparent), /* @embed */ url("themes/mediawiki/images/icons/circle-constructive.svg");
+       background-image:      -o-linear-gradient(transparent, transparent), url("themes/mediawiki/images/icons/circle-constructive.png");
+       background-repeat: no-repeat;
+       background-position: center center;
+       background-origin: border-box;
+       background-size: 0 0;
+}
+.oo-ui-radioInputWidget input[type="radio"]:checked + span {
+       background-size: 100% 100%;
+}
+.oo-ui-radioInputWidget input[type="radio"]:active + span {
+       background-color: #cccccc;
+       border-color: #cccccc;
+}
+.oo-ui-radioInputWidget input[type="radio"]:focus + span {
+       border-width: 2px;
+}
+.oo-ui-radioInputWidget input[type="radio"]:focus:hover + span,
+.oo-ui-radioInputWidget input[type="radio"]:hover + span {
+       border-bottom-width: 3px;
+}
+.oo-ui-radioInputWidget input[type="radio"]:disabled {
+       cursor: default;
+}
+.oo-ui-radioInputWidget input[type="radio"]:disabled + span {
+       background-color: #dddddd;
+       border-color: #dddddd;
+}
+.oo-ui-radioInputWidget input[type="radio"]:disabled:checked + span {
+       background-image: url("themes/mediawiki/images/icons/circle-invert.png");
+       background-image: -webkit-linear-gradient(transparent, transparent), /* @embed */ url("themes/mediawiki/images/icons/circle-invert.svg");
+       background-image:         linear-gradient(transparent, transparent), /* @embed */ url("themes/mediawiki/images/icons/circle-invert.svg");
+       background-image:      -o-linear-gradient(transparent, transparent), url("themes/mediawiki/images/icons/circle-invert.png");
+}
+.oo-ui-radioSelectInputWidget .oo-ui-fieldLayout {
+       margin-bottom: 0;
+}
+.oo-ui-textInputWidget {
+       position: relative;
+       vertical-align: middle;
+       -webkit-box-sizing: border-box;
+          -moz-box-sizing: border-box;
+               box-sizing: border-box;
+       width: 100%;
+       max-width: 50em;
+}
+.oo-ui-textInputWidget input,
+.oo-ui-textInputWidget textarea {
+       display: inline-block;
+       width: 100%;
+       resize: none;
+       -webkit-box-sizing: border-box;
+          -moz-box-sizing: border-box;
+               box-sizing: border-box;
+}
+.oo-ui-textInputWidget textarea {
+       overflow: auto;
+}
+.oo-ui-textInputWidget input[type="search"] {
+       -webkit-appearance: none;
+}
+.oo-ui-textInputWidget input[type="search"]::-ms-clear {
+       display: none;
+}
+.oo-ui-textInputWidget input[type="search"]::-ms-reveal {
+       display: none;
+}
+.oo-ui-textInputWidget input[type="search"]::-webkit-search-decoration,
+.oo-ui-textInputWidget input[type="search"]::-webkit-search-cancel-button,
+.oo-ui-textInputWidget input[type="search"]::-webkit-search-results-button,
+.oo-ui-textInputWidget input[type="search"]::-webkit-search-results-decoration {
+       display: none;
+}
+.oo-ui-textInputWidget > .oo-ui-iconElement-icon,
+.oo-ui-textInputWidget > .oo-ui-indicatorElement-indicator,
+.oo-ui-textInputWidget > .oo-ui-labelElement-label {
+       display: none;
+}
+.oo-ui-textInputWidget.oo-ui-iconElement > .oo-ui-iconElement-icon,
+.oo-ui-textInputWidget.oo-ui-indicatorElement > .oo-ui-indicatorElement-indicator {
+       display: block;
+       position: absolute;
+       top: 0;
+       height: 100%;
+       -webkit-touch-callout: none;
+       -webkit-user-select: none;
+          -moz-user-select: none;
+           -ms-user-select: none;
+               user-select: none;
+}
+.oo-ui-textInputWidget.oo-ui-widget-enabled > .oo-ui-iconElement-icon,
+.oo-ui-textInputWidget.oo-ui-widget-enabled > .oo-ui-indicatorElement-indicator {
+       cursor: text;
+}
+.oo-ui-textInputWidget.oo-ui-widget-enabled.oo-ui-textInputWidget-type-search > .oo-ui-indicatorElement-indicator {
+       cursor: pointer;
+}
+.oo-ui-textInputWidget.oo-ui-labelElement > .oo-ui-labelElement-label {
+       display: block;
+}
+.oo-ui-textInputWidget > .oo-ui-iconElement-icon {
+       left: 0;
+}
+.oo-ui-textInputWidget > .oo-ui-indicatorElement-indicator {
+       right: 0;
+}
+.oo-ui-textInputWidget > .oo-ui-labelElement-label {
+       position: absolute;
+       top: 0;
+}
+.oo-ui-textInputWidget-labelPosition-after > .oo-ui-labelElement-label {
+       right: 0;
+}
+.oo-ui-textInputWidget-labelPosition-before > .oo-ui-labelElement-label {
+       left: 0;
+}
+.oo-ui-textInputWidget input,
+.oo-ui-textInputWidget textarea {
+       padding: 0.5em;
+       line-height: 1.275em;
+       margin: 0;
+       font-size: inherit;
+       font-family: inherit;
+       background-color: #ffffff;
+       color: #000000;
+       border: 1px solid #cccccc;
+       border-radius: 2px;
+       -webkit-box-sizing: border-box;
+          -moz-box-sizing: border-box;
+               box-sizing: border-box;
+}
+.oo-ui-textInputWidget input.oo-ui-pendingElement-pending,
+.oo-ui-textInputWidget textarea.oo-ui-pendingElement-pending {
+       background-color: transparent;
+}
+.oo-ui-textInputWidget.oo-ui-widget-enabled input,
+.oo-ui-textInputWidget.oo-ui-widget-enabled textarea {
+       box-shadow: inset 0 0 0 0.1em #ffffff;
+       -webkit-transition: border 200ms cubic-bezier(0.39, 0.575, 0.565, 1), box-shadow 200ms cubic-bezier(0.39, 0.575, 0.565, 1);
+          -moz-transition: border 200ms cubic-bezier(0.39, 0.575, 0.565, 1), box-shadow 200ms cubic-bezier(0.39, 0.575, 0.565, 1);
+               transition: border 200ms cubic-bezier(0.39, 0.575, 0.565, 1), box-shadow 200ms cubic-bezier(0.39, 0.575, 0.565, 1);
+}
+.oo-ui-textInputWidget.oo-ui-widget-enabled input:focus,
+.oo-ui-textInputWidget.oo-ui-widget-enabled textarea:focus {
+       outline: none;
+       border-color: #347bff;
+       box-shadow: inset 0 0 0 0.1em #347bff;
+}
+.oo-ui-textInputWidget.oo-ui-widget-enabled input[readonly],
+.oo-ui-textInputWidget.oo-ui-widget-enabled textarea[readonly] {
+       color: #777777;
+       text-shadow: 0 1px 1px #ffffff;
+}
+.oo-ui-textInputWidget.oo-ui-widget-enabled input[readonly]:focus,
+.oo-ui-textInputWidget.oo-ui-widget-enabled textarea[readonly]:focus {
+       border-color: #cccccc;
+       box-shadow: inset 0 0 0 0.1em #cccccc;
+}
+.oo-ui-textInputWidget.oo-ui-widget-enabled.oo-ui-flaggedElement-invalid input,
+.oo-ui-textInputWidget.oo-ui-widget-enabled.oo-ui-flaggedElement-invalid textarea {
+       border-color: #ff0000;
+}
+.oo-ui-textInputWidget.oo-ui-widget-enabled.oo-ui-flaggedElement-invalid input:focus,
+.oo-ui-textInputWidget.oo-ui-widget-enabled.oo-ui-flaggedElement-invalid textarea:focus {
+       border-color: #ff0000;
+       box-shadow: inset 0 0 0 0.1em #ff0000;
+}
+.oo-ui-textInputWidget.oo-ui-widget-disabled input,
+.oo-ui-textInputWidget.oo-ui-widget-disabled textarea {
+       color: #cccccc;
+       text-shadow: 0 1px 1px #ffffff;
+       border-color: #dddddd;
+       background-color: #f3f3f3;
+}
+.oo-ui-textInputWidget.oo-ui-widget-disabled .oo-ui-iconElement-icon,
+.oo-ui-textInputWidget.oo-ui-widget-disabled .oo-ui-indicatorElement-indicator {
+       opacity: 0.2;
+}
+.oo-ui-textInputWidget.oo-ui-widget-disabled .oo-ui-labelElement-label {
+       color: #dddddd;
+       text-shadow: 0 1px 1px #ffffff;
+}
+.oo-ui-textInputWidget.oo-ui-iconElement input,
+.oo-ui-textInputWidget.oo-ui-iconElement textarea {
+       padding-left: 2.875em;
+}
+.oo-ui-textInputWidget.oo-ui-iconElement .oo-ui-iconElement-icon {
+       left: 0;
+       width: 1.875em;
+       max-height: 2.375em;
+       margin-left: 0.5em;
+       height: 100%;
+       background-position: right center;
+}
+.oo-ui-textInputWidget.oo-ui-indicatorElement input,
+.oo-ui-textInputWidget.oo-ui-indicatorElement textarea {
+       padding-right: 2.4875em;
+}
+.oo-ui-textInputWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
+       width: 0.9375em;
+       max-height: 2.375em;
+       margin: 0 0.775em;
+       height: 100%;
+}
+.oo-ui-textInputWidget > .oo-ui-labelElement-label {
+       padding: 0.4em;
+       line-height: 1.5em;
+       color: #888888;
+}
+.oo-ui-textInputWidget-labelPosition-after.oo-ui-indicatorElement > .oo-ui-labelElement-label {
+       margin-right: 2.0875em;
+}
+.oo-ui-textInputWidget-labelPosition-before.oo-ui-iconElement > .oo-ui-labelElement-label {
+       margin-left: 2.475em;
+}
+.oo-ui-menuSelectWidget {
+       position: absolute;
+       background-color: #ffffff;
+       margin-top: -1px;
+       border: 1px solid #aaaaaa;
+       border-radius: 0 0 2px 2px;
+       box-shadow: 0 0.15em 0 0 rgba(0, 0, 0, 0.15);
+}
+.oo-ui-menuSelectWidget input {
+       position: absolute;
+       width: 0;
+       height: 0;
+       overflow: hidden;
+       opacity: 0;
+}
+.oo-ui-menuOptionWidget {
+       position: relative;
+       padding: 0.5em 1em;
+}
+.oo-ui-menuOptionWidget .oo-ui-iconElement-icon {
+       display: none;
+}
+.oo-ui-menuOptionWidget.oo-ui-optionWidget-selected {
+       background-color: transparent;
+}
+.oo-ui-menuOptionWidget.oo-ui-optionWidget-selected .oo-ui-iconElement-icon {
+       display: block;
+}
+.oo-ui-menuOptionWidget.oo-ui-optionWidget-selected {
+       background-color: #d8e6fe;
+       color: rgba(0, 0, 0, 0.8);
+}
+.oo-ui-menuOptionWidget.oo-ui-optionWidget-selected .oo-ui-iconElement-icon {
+       display: none;
+}
+.oo-ui-menuOptionWidget.oo-ui-optionWidget-highlighted {
+       background-color: #eeeeee;
+       color: #000000;
+}
+.oo-ui-menuOptionWidget.oo-ui-optionWidget-selected.oo-ui-menuOptionWidget.oo-ui-optionWidget-highlighted {
+       background-color: #d8e6fe;
+}
+.oo-ui-menuSectionOptionWidget {
+       cursor: default;
+       padding: 0.33em 0.75em;
+       color: #888888;
+}
+.oo-ui-dropdownWidget {
+       display: inline-block;
+       position: relative;
+       width: 100%;
+       max-width: 50em;
+       background-color: #ffffff;
+       margin-right: 0.5em;
+}
+.oo-ui-dropdownWidget-handle {
+       width: 100%;
+       display: inline-block;
+       white-space: nowrap;
+       overflow: hidden;
+       text-overflow: ellipsis;
+       -webkit-touch-callout: none;
+       -webkit-user-select: none;
+          -moz-user-select: none;
+           -ms-user-select: none;
+               user-select: none;
+       -webkit-box-sizing: border-box;
+          -moz-box-sizing: border-box;
+               box-sizing: border-box;
+}
+.oo-ui-dropdownWidget-handle .oo-ui-indicatorElement-indicator,
+.oo-ui-dropdownWidget-handle .oo-ui-iconElement-icon {
+       position: absolute;
+}
+.oo-ui-dropdownWidget > .oo-ui-menuSelectWidget {
+       z-index: 1;
+       width: 100%;
+}
+.oo-ui-dropdownWidget.oo-ui-widget-enabled .oo-ui-dropdownWidget-handle {
+       cursor: pointer;
+}
+.oo-ui-dropdownWidget:last-child {
+       margin-right: 0;
+}
+.oo-ui-dropdownWidget-handle {
+       padding: 0.5em 0;
+       height: 2.275em;
+       line-height: 1.275;
+       border: 1px solid #cccccc;
+       border-radius: 2px;
+}
+.oo-ui-dropdownWidget-handle .oo-ui-indicatorElement-indicator {
+       right: 0;
+}
+.oo-ui-dropdownWidget-handle .oo-ui-iconElement-icon {
+       left: 0.25em;
+}
+.oo-ui-dropdownWidget-handle .oo-ui-labelElement-label {
+       line-height: 1.275em;
+       margin: 0 1em;
+}
+.oo-ui-dropdownWidget-handle .oo-ui-indicatorElement-indicator {
+       top: 0;
+       width: 0.9375em;
+       height: 0.9375em;
+       margin: 0.775em;
+}
+.oo-ui-dropdownWidget-handle .oo-ui-iconElement-icon {
+       top: 0;
+       width: 1.875em;
+       height: 1.875em;
+       margin: 0.3em;
+}
+.oo-ui-dropdownWidget:hover .oo-ui-dropdownWidget-handle {
+       border-color: #aaaaaa;
+}
+.oo-ui-dropdownWidget.oo-ui-widget-disabled .oo-ui-dropdownWidget-handle {
+       color: #cccccc;
+       text-shadow: 0 1px 1px #ffffff;
+       border-color: #dddddd;
+       background-color: #f3f3f3;
+}
+.oo-ui-dropdownWidget.oo-ui-widget-disabled .oo-ui-dropdownWidget-handle:focus {
+       outline: 0;
+}
+.oo-ui-dropdownWidget.oo-ui-widget-disabled .oo-ui-indicatorElement-indicator {
+       opacity: 0.2;
+}
+.oo-ui-dropdownWidget.oo-ui-iconElement .oo-ui-dropdownWidget-handle .oo-ui-labelElement-label {
+       margin-left: 3em;
+}
+.oo-ui-dropdownWidget.oo-ui-indicatorElement .oo-ui-dropdownWidget-handle .oo-ui-labelElement-label {
+       margin-right: 2em;
+}
+.oo-ui-dropdownWidget .oo-ui-selectWidget {
+       border-top-color: #ffffff;
+}
+.oo-ui-comboBoxInputWidget {
+       display: inline-block;
+       position: relative;
+       width: 100%;
+       max-width: 50em;
+       margin-right: 0.5em;
+}
+.oo-ui-comboBoxInputWidget > .oo-ui-menuSelectWidget {
+       z-index: 1;
+       width: 100%;
+}
+.oo-ui-comboBoxInputWidget.oo-ui-widget-enabled > .oo-ui-indicatorElement-indicator {
+       cursor: pointer;
+}
+.oo-ui-comboBoxInputWidget-php input::-webkit-calendar-picker-indicator {
+       opacity: 0 !important;
+       position: absolute;
+       right: 0;
+       top: 0;
+       height: 2.5em;
+       width: 2.5em;
+       padding: 0;
+}
+.oo-ui-comboBoxInputWidget-php > .oo-ui-indicatorElement-indicator {
+       pointer-events: none;
+}
+.oo-ui-comboBoxInputWidget:last-child {
+       margin-right: 0;
+}
+.oo-ui-comboBoxInputWidget input,
+.oo-ui-comboBoxInputWidget textarea {
+       height: 2.35em;
+}
diff --git a/resources/lib/oojs-ui/oojs-ui-core.js b/resources/lib/oojs-ui/oojs-ui-core.js
new file mode 100644 (file)
index 0000000..5a43228
--- /dev/null
@@ -0,0 +1,9381 @@
+/*!
+ * OOjs UI v0.15.3
+ * https://www.mediawiki.org/wiki/OOjs_UI
+ *
+ * Copyright 2011–2016 OOjs UI Team and other contributors.
+ * Released under the MIT license
+ * http://oojs.mit-license.org
+ *
+ * Date: 2016-02-09T21:21:16Z
+ */
+( function ( OO ) {
+
+'use strict';
+
+/**
+ * Namespace for all classes, static methods and static properties.
+ *
+ * @class
+ * @singleton
+ */
+OO.ui = {};
+
+OO.ui.bind = $.proxy;
+
+/**
+ * @property {Object}
+ */
+OO.ui.Keys = {
+       UNDEFINED: 0,
+       BACKSPACE: 8,
+       DELETE: 46,
+       LEFT: 37,
+       RIGHT: 39,
+       UP: 38,
+       DOWN: 40,
+       ENTER: 13,
+       END: 35,
+       HOME: 36,
+       TAB: 9,
+       PAGEUP: 33,
+       PAGEDOWN: 34,
+       ESCAPE: 27,
+       SHIFT: 16,
+       SPACE: 32
+};
+
+/**
+ * Constants for MouseEvent.which
+ *
+ * @property {Object}
+ */
+OO.ui.MouseButtons = {
+       LEFT: 1,
+       MIDDLE: 2,
+       RIGHT: 3
+};
+
+/**
+ * @property {Number}
+ */
+OO.ui.elementId = 0;
+
+/**
+ * Generate a unique ID for element
+ *
+ * @return {String} [id]
+ */
+OO.ui.generateElementId = function () {
+       OO.ui.elementId += 1;
+       return 'oojsui-' + OO.ui.elementId;
+};
+
+/**
+ * Check if an element is focusable.
+ * Inspired from :focusable in jQueryUI v1.11.4 - 2015-04-14
+ *
+ * @param {jQuery} element Element to test
+ * @return {boolean}
+ */
+OO.ui.isFocusableElement = function ( $element ) {
+       var nodeName,
+               element = $element[ 0 ];
+
+       // Anything disabled is not focusable
+       if ( element.disabled ) {
+               return false;
+       }
+
+       // Check if the element is visible
+       if ( !(
+               // This is quicker than calling $element.is( ':visible' )
+               $.expr.filters.visible( element ) &&
+               // Check that all parents are visible
+               !$element.parents().addBack().filter( function () {
+                       return $.css( this, 'visibility' ) === 'hidden';
+               } ).length
+       ) ) {
+               return false;
+       }
+
+       // Check if the element is ContentEditable, which is the string 'true'
+       if ( element.contentEditable === 'true' ) {
+               return true;
+       }
+
+       // Anything with a non-negative numeric tabIndex is focusable.
+       // Use .prop to avoid browser bugs
+       if ( $element.prop( 'tabIndex' ) >= 0 ) {
+               return true;
+       }
+
+       // Some element types are naturally focusable
+       // (indexOf is much faster than regex in Chrome and about the
+       // same in FF: https://jsperf.com/regex-vs-indexof-array2)
+       nodeName = element.nodeName.toLowerCase();
+       if ( [ 'input', 'select', 'textarea', 'button', 'object' ].indexOf( nodeName ) !== -1 ) {
+               return true;
+       }
+
+       // Links and areas are focusable if they have an href
+       if ( ( nodeName === 'a' || nodeName === 'area' ) && $element.attr( 'href' ) !== undefined ) {
+               return true;
+       }
+
+       return false;
+};
+
+/**
+ * Find a focusable child
+ *
+ * @param {jQuery} $container Container to search in
+ * @param {boolean} [backwards] Search backwards
+ * @return {jQuery} Focusable child, an empty jQuery object if none found
+ */
+OO.ui.findFocusable = function ( $container, backwards ) {
+       var $focusable = $( [] ),
+               // $focusableCandidates is a superset of things that
+               // could get matched by isFocusableElement
+               $focusableCandidates = $container
+                       .find( 'input, select, textarea, button, object, a, area, [contenteditable], [tabindex]' );
+
+       if ( backwards ) {
+               $focusableCandidates = Array.prototype.reverse.call( $focusableCandidates );
+       }
+
+       $focusableCandidates.each( function () {
+               var $this = $( this );
+               if ( OO.ui.isFocusableElement( $this ) ) {
+                       $focusable = $this;
+                       return false;
+               }
+       } );
+       return $focusable;
+};
+
+/**
+ * Get the user's language and any fallback languages.
+ *
+ * These language codes are used to localize user interface elements in the user's language.
+ *
+ * In environments that provide a localization system, this function should be overridden to
+ * return the user's language(s). The default implementation returns English (en) only.
+ *
+ * @return {string[]} Language codes, in descending order of priority
+ */
+OO.ui.getUserLanguages = function () {
+       return [ 'en' ];
+};
+
+/**
+ * Get a value in an object keyed by language code.
+ *
+ * @param {Object.<string,Mixed>} obj Object keyed by language code
+ * @param {string|null} [lang] Language code, if omitted or null defaults to any user language
+ * @param {string} [fallback] Fallback code, used if no matching language can be found
+ * @return {Mixed} Local value
+ */
+OO.ui.getLocalValue = function ( obj, lang, fallback ) {
+       var i, len, langs;
+
+       // Requested language
+       if ( obj[ lang ] ) {
+               return obj[ lang ];
+       }
+       // Known user language
+       langs = OO.ui.getUserLanguages();
+       for ( i = 0, len = langs.length; i < len; i++ ) {
+               lang = langs[ i ];
+               if ( obj[ lang ] ) {
+                       return obj[ lang ];
+               }
+       }
+       // Fallback language
+       if ( obj[ fallback ] ) {
+               return obj[ fallback ];
+       }
+       // First existing language
+       for ( lang in obj ) {
+               return obj[ lang ];
+       }
+
+       return undefined;
+};
+
+/**
+ * Check if a node is contained within another node
+ *
+ * Similar to jQuery#contains except a list of containers can be supplied
+ * and a boolean argument allows you to include the container in the match list
+ *
+ * @param {HTMLElement|HTMLElement[]} containers Container node(s) to search in
+ * @param {HTMLElement} contained Node to find
+ * @param {boolean} [matchContainers] Include the container(s) in the list of nodes to match, otherwise only match descendants
+ * @return {boolean} The node is in the list of target nodes
+ */
+OO.ui.contains = function ( containers, contained, matchContainers ) {
+       var i;
+       if ( !Array.isArray( containers ) ) {
+               containers = [ containers ];
+       }
+       for ( i = containers.length - 1; i >= 0; i-- ) {
+               if ( ( matchContainers && contained === containers[ i ] ) || $.contains( containers[ i ], contained ) ) {
+                       return true;
+               }
+       }
+       return false;
+};
+
+/**
+ * Return a function, that, as long as it continues to be invoked, will not
+ * be triggered. The function will be called after it stops being called for
+ * N milliseconds. If `immediate` is passed, trigger the function on the
+ * leading edge, instead of the trailing.
+ *
+ * Ported from: http://underscorejs.org/underscore.js
+ *
+ * @param {Function} func
+ * @param {number} wait
+ * @param {boolean} immediate
+ * @return {Function}
+ */
+OO.ui.debounce = function ( func, wait, immediate ) {
+       var timeout;
+       return function () {
+               var context = this,
+                       args = arguments,
+                       later = function () {
+                               timeout = null;
+                               if ( !immediate ) {
+                                       func.apply( context, args );
+                               }
+                       };
+               if ( immediate && !timeout ) {
+                       func.apply( context, args );
+               }
+               clearTimeout( timeout );
+               timeout = setTimeout( later, wait );
+       };
+};
+
+/**
+ * Proxy for `node.addEventListener( eventName, handler, true )`.
+ *
+ * @param {HTMLElement} node
+ * @param {string} eventName
+ * @param {Function} handler
+ * @deprecated
+ */
+OO.ui.addCaptureEventListener = function ( node, eventName, handler ) {
+       node.addEventListener( eventName, handler, true );
+};
+
+/**
+ * Proxy for `node.removeEventListener( eventName, handler, true )`.
+ *
+ * @param {HTMLElement} node
+ * @param {string} eventName
+ * @param {Function} handler
+ * @deprecated
+ */
+OO.ui.removeCaptureEventListener = function ( node, eventName, handler ) {
+       node.removeEventListener( eventName, handler, true );
+};
+
+/**
+ * Reconstitute a JavaScript object corresponding to a widget created by
+ * the PHP implementation.
+ *
+ * This is an alias for `OO.ui.Element.static.infuse()`.
+ *
+ * @param {string|HTMLElement|jQuery} idOrNode
+ *   A DOM id (if a string) or node for the widget to infuse.
+ * @return {OO.ui.Element}
+ *   The `OO.ui.Element` corresponding to this (infusable) document node.
+ */
+OO.ui.infuse = function ( idOrNode ) {
+       return OO.ui.Element.static.infuse( idOrNode );
+};
+
+( function () {
+       /**
+        * Message store for the default implementation of OO.ui.msg
+        *
+        * Environments that provide a localization system should not use this, but should override
+        * OO.ui.msg altogether.
+        *
+        * @private
+        */
+       var messages = {
+               // Tool tip for a button that moves items in a list down one place
+               'ooui-outline-control-move-down': 'Move item down',
+               // Tool tip for a button that moves items in a list up one place
+               'ooui-outline-control-move-up': 'Move item up',
+               // Tool tip for a button that removes items from a list
+               'ooui-outline-control-remove': 'Remove item',
+               // Label for the toolbar group that contains a list of all other available tools
+               'ooui-toolbar-more': 'More',
+               // Label for the fake tool that expands the full list of tools in a toolbar group
+               'ooui-toolgroup-expand': 'More',
+               // Label for the fake tool that collapses the full list of tools in a toolbar group
+               'ooui-toolgroup-collapse': 'Fewer',
+               // Default label for the accept button of a confirmation dialog
+               'ooui-dialog-message-accept': 'OK',
+               // Default label for the reject button of a confirmation dialog
+               'ooui-dialog-message-reject': 'Cancel',
+               // Title for process dialog error description
+               'ooui-dialog-process-error': 'Something went wrong',
+               // Label for process dialog dismiss error button, visible when describing errors
+               'ooui-dialog-process-dismiss': 'Dismiss',
+               // Label for process dialog retry action button, visible when describing only recoverable errors
+               'ooui-dialog-process-retry': 'Try again',
+               // Label for process dialog retry action button, visible when describing only warnings
+               'ooui-dialog-process-continue': 'Continue',
+               // Label for the file selection widget's select file button
+               'ooui-selectfile-button-select': 'Select a file',
+               // Label for the file selection widget if file selection is not supported
+               'ooui-selectfile-not-supported': 'File selection is not supported',
+               // Label for the file selection widget when no file is currently selected
+               'ooui-selectfile-placeholder': 'No file is selected',
+               // Label for the file selection widget's drop target
+               'ooui-selectfile-dragdrop-placeholder': 'Drop file here'
+       };
+
+       /**
+        * Get a localized message.
+        *
+        * In environments that provide a localization system, this function should be overridden to
+        * return the message translated in the user's language. The default implementation always returns
+        * English messages.
+        *
+        * After the message key, message parameters may optionally be passed. In the default implementation,
+        * any occurrences of $1 are replaced with the first parameter, $2 with the second parameter, etc.
+        * Alternative implementations of OO.ui.msg may use any substitution system they like, as long as
+        * they support unnamed, ordered message parameters.
+        *
+        * @param {string} key Message key
+        * @param {Mixed...} [params] Message parameters
+        * @return {string} Translated message with parameters substituted
+        */
+       OO.ui.msg = function ( key ) {
+               var message = messages[ key ],
+                       params = Array.prototype.slice.call( arguments, 1 );
+               if ( typeof message === 'string' ) {
+                       // Perform $1 substitution
+                       message = message.replace( /\$(\d+)/g, function ( unused, n ) {
+                               var i = parseInt( n, 10 );
+                               return params[ i - 1 ] !== undefined ? params[ i - 1 ] : '$' + n;
+                       } );
+               } else {
+                       // Return placeholder if message not found
+                       message = '[' + key + ']';
+               }
+               return message;
+       };
+} )();
+
+/**
+ * Package a message and arguments for deferred resolution.
+ *
+ * Use this when you are statically specifying a message and the message may not yet be present.
+ *
+ * @param {string} key Message key
+ * @param {Mixed...} [params] Message parameters
+ * @return {Function} Function that returns the resolved message when executed
+ */
+OO.ui.deferMsg = function () {
+       var args = arguments;
+       return function () {
+               return OO.ui.msg.apply( OO.ui, args );
+       };
+};
+
+/**
+ * Resolve a message.
+ *
+ * If the message is a function it will be executed, otherwise it will pass through directly.
+ *
+ * @param {Function|string} msg Deferred message, or message text
+ * @return {string} Resolved message
+ */
+OO.ui.resolveMsg = function ( msg ) {
+       if ( $.isFunction( msg ) ) {
+               return msg();
+       }
+       return msg;
+};
+
+/**
+ * @param {string} url
+ * @return {boolean}
+ */
+OO.ui.isSafeUrl = function ( url ) {
+       // Keep this function in sync with php/Tag.php
+       var i, protocolWhitelist;
+
+       function stringStartsWith( haystack, needle ) {
+               return haystack.substr( 0, needle.length ) === needle;
+       }
+
+       protocolWhitelist = [
+               'bitcoin', 'ftp', 'ftps', 'geo', 'git', 'gopher', 'http', 'https', 'irc', 'ircs',
+               'magnet', 'mailto', 'mms', 'news', 'nntp', 'redis', 'sftp', 'sip', 'sips', 'sms', 'ssh',
+               'svn', 'tel', 'telnet', 'urn', 'worldwind', 'xmpp'
+       ];
+
+       if ( url === '' ) {
+               return true;
+       }
+
+       for ( i = 0; i < protocolWhitelist.length; i++ ) {
+               if ( stringStartsWith( url, protocolWhitelist[ i ] + ':' ) ) {
+                       return true;
+               }
+       }
+
+       // This matches '//' too
+       if ( stringStartsWith( url, '/' ) || stringStartsWith( url, './' ) ) {
+               return true;
+       }
+       if ( stringStartsWith( url, '?' ) || stringStartsWith( url, '#' ) ) {
+               return true;
+       }
+
+       return false;
+};
+
+/*!
+ * Mixin namespace.
+ */
+
+/**
+ * Namespace for OOjs UI mixins.
+ *
+ * Mixins are named according to the type of object they are intended to
+ * be mixed in to.  For example, OO.ui.mixin.GroupElement is intended to be
+ * mixed in to an instance of OO.ui.Element, and OO.ui.mixin.GroupWidget
+ * is intended to be mixed in to an instance of OO.ui.Widget.
+ *
+ * @class
+ * @singleton
+ */
+OO.ui.mixin = {};
+
+/**
+ * Each Element represents a rendering in the DOM—a button or an icon, for example, or anything
+ * that is visible to a user. Unlike {@link OO.ui.Widget widgets}, plain elements usually do not have events
+ * connected to them and can't be interacted with.
+ *
+ * @abstract
+ * @class
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {string[]} [classes] The names of the CSS classes to apply to the element. CSS styles are added
+ *  to the top level (e.g., the outermost div) of the element. See the [OOjs UI documentation on MediaWiki][2]
+ *  for an example.
+ *  [2]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Buttons_and_Switches#cssExample
+ * @cfg {string} [id] The HTML id attribute used in the rendered tag.
+ * @cfg {string} [text] Text to insert
+ * @cfg {Array} [content] An array of content elements to append (after #text).
+ *  Strings will be html-escaped; use an OO.ui.HtmlSnippet to append raw HTML.
+ *  Instances of OO.ui.Element will have their $element appended.
+ * @cfg {jQuery} [$content] Content elements to append (after #text).
+ * @cfg {jQuery} [$element] Wrapper element. Defaults to a new element with #getTagName.
+ * @cfg {Mixed} [data] Custom data of any type or combination of types (e.g., string, number, array, object).
+ *  Data can also be specified with the #setData method.
+ */
+OO.ui.Element = function OoUiElement( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Properties
+       this.$ = $;
+       this.visible = true;
+       this.data = config.data;
+       this.$element = config.$element ||
+               $( document.createElement( this.getTagName() ) );
+       this.elementGroup = null;
+       this.debouncedUpdateThemeClassesHandler = OO.ui.debounce( this.debouncedUpdateThemeClasses );
+
+       // Initialization
+       if ( Array.isArray( config.classes ) ) {
+               this.$element.addClass( config.classes.join( ' ' ) );
+       }
+       if ( config.id ) {
+               this.$element.attr( 'id', config.id );
+       }
+       if ( config.text ) {
+               this.$element.text( config.text );
+       }
+       if ( config.content ) {
+               // The `content` property treats plain strings as text; use an
+               // HtmlSnippet to append HTML content.  `OO.ui.Element`s get their
+               // appropriate $element appended.
+               this.$element.append( config.content.map( function ( v ) {
+                       if ( typeof v === 'string' ) {
+                               // Escape string so it is properly represented in HTML.
+                               return document.createTextNode( v );
+                       } else if ( v instanceof OO.ui.HtmlSnippet ) {
+                               // Bypass escaping.
+                               return v.toString();
+                       } else if ( v instanceof OO.ui.Element ) {
+                               return v.$element;
+                       }
+                       return v;
+               } ) );
+       }
+       if ( config.$content ) {
+               // The `$content` property treats plain strings as HTML.
+               this.$element.append( config.$content );
+       }
+};
+
+/* Setup */
+
+OO.initClass( OO.ui.Element );
+
+/* Static Properties */
+
+/**
+ * The name of the HTML tag used by the element.
+ *
+ * The static value may be ignored if the #getTagName method is overridden.
+ *
+ * @static
+ * @inheritable
+ * @property {string}
+ */
+OO.ui.Element.static.tagName = 'div';
+
+/* Static Methods */
+
+/**
+ * Reconstitute a JavaScript object corresponding to a widget created
+ * by the PHP implementation.
+ *
+ * @param {string|HTMLElement|jQuery} idOrNode
+ *   A DOM id (if a string) or node for the widget to infuse.
+ * @return {OO.ui.Element}
+ *   The `OO.ui.Element` corresponding to this (infusable) document node.
+ *   For `Tag` objects emitted on the HTML side (used occasionally for content)
+ *   the value returned is a newly-created Element wrapping around the existing
+ *   DOM node.
+ */
+OO.ui.Element.static.infuse = function ( idOrNode ) {
+       var obj = OO.ui.Element.static.unsafeInfuse( idOrNode, false );
+       // Verify that the type matches up.
+       // FIXME: uncomment after T89721 is fixed (see T90929)
+       /*
+       if ( !( obj instanceof this['class'] ) ) {
+               throw new Error( 'Infusion type mismatch!' );
+       }
+       */
+       return obj;
+};
+
+/**
+ * Implementation helper for `infuse`; skips the type check and has an
+ * extra property so that only the top-level invocation touches the DOM.
+ * @private
+ * @param {string|HTMLElement|jQuery} idOrNode
+ * @param {jQuery.Promise|boolean} domPromise A promise that will be resolved
+ *     when the top-level widget of this infusion is inserted into DOM,
+ *     replacing the original node; or false for top-level invocation.
+ * @return {OO.ui.Element}
+ */
+OO.ui.Element.static.unsafeInfuse = function ( idOrNode, domPromise ) {
+       // look for a cached result of a previous infusion.
+       var id, $elem, data, cls, parts, parent, obj, top, state, infusedChildren;
+       if ( typeof idOrNode === 'string' ) {
+               id = idOrNode;
+               $elem = $( document.getElementById( id ) );
+       } else {
+               $elem = $( idOrNode );
+               id = $elem.attr( 'id' );
+       }
+       if ( !$elem.length ) {
+               throw new Error( 'Widget not found: ' + id );
+       }
+       if ( $elem[ 0 ].oouiInfused ) {
+               $elem = $elem[ 0 ].oouiInfused;
+       }
+       data = $elem.data( 'ooui-infused' );
+       if ( data ) {
+               // cached!
+               if ( data === true ) {
+                       throw new Error( 'Circular dependency! ' + id );
+               }
+               if ( domPromise ) {
+                       // pick up dynamic state, like focus, value of form inputs, scroll position, etc.
+                       state = data.gatherPreInfuseState( $elem );
+                       // restore dynamic state after the new element is re-inserted into DOM under infused parent
+                       domPromise.done( data.restorePreInfuseState.bind( data, state ) );
+                       infusedChildren = $elem.data( 'ooui-infused-children' );
+                       if ( infusedChildren && infusedChildren.length ) {
+                               infusedChildren.forEach( function ( data ) {
+                                       var state = data.gatherPreInfuseState( $elem );
+                                       domPromise.done( data.restorePreInfuseState.bind( data, state ) );
+                               } );
+                       }
+               }
+               return data;
+       }
+       data = $elem.attr( 'data-ooui' );
+       if ( !data ) {
+               throw new Error( 'No infusion data found: ' + id );
+       }
+       try {
+               data = $.parseJSON( data );
+       } catch ( _ ) {
+               data = null;
+       }
+       if ( !( data && data._ ) ) {
+               throw new Error( 'No valid infusion data found: ' + id );
+       }
+       if ( data._ === 'Tag' ) {
+               // Special case: this is a raw Tag; wrap existing node, don't rebuild.
+               return new OO.ui.Element( { $element: $elem } );
+       }
+       parts = data._.split( '.' );
+       cls = OO.getProp.apply( OO, [ window ].concat( parts ) );
+       if ( cls === undefined ) {
+               // The PHP output might be old and not including the "OO.ui" prefix
+               // TODO: Remove this back-compat after next major release
+               cls = OO.getProp.apply( OO, [ OO.ui ].concat( parts ) );
+               if ( cls === undefined ) {
+                       throw new Error( 'Unknown widget type: id: ' + id + ', class: ' + data._ );
+               }
+       }
+
+       // Verify that we're creating an OO.ui.Element instance
+       parent = cls.parent;
+
+       while ( parent !== undefined ) {
+               if ( parent === OO.ui.Element ) {
+                       // Safe
+                       break;
+               }
+
+               parent = parent.parent;
+       }
+
+       if ( parent !== OO.ui.Element ) {
+               throw new Error( 'Unknown widget type: id: ' + id + ', class: ' + data._ );
+       }
+
+       if ( domPromise === false ) {
+               top = $.Deferred();
+               domPromise = top.promise();
+       }
+       $elem.data( 'ooui-infused', true ); // prevent loops
+       data.id = id; // implicit
+       infusedChildren = [];
+       data = OO.copy( data, null, function deserialize( value ) {
+               var infused;
+               if ( OO.isPlainObject( value ) ) {
+                       if ( value.tag ) {
+                               infused = OO.ui.Element.static.unsafeInfuse( value.tag, domPromise );
+                               infusedChildren.push( infused );
+                               // Flatten the structure
+                               infusedChildren.push.apply( infusedChildren, infused.$element.data( 'ooui-infused-children' ) || [] );
+                               infused.$element.removeData( 'ooui-infused-children' );
+                               return infused;
+                       }
+                       if ( value.html ) {
+                               return new OO.ui.HtmlSnippet( value.html );
+                       }
+               }
+       } );
+       // allow widgets to reuse parts of the DOM
+       data = cls.static.reusePreInfuseDOM( $elem[ 0 ], data );
+       // pick up dynamic state, like focus, value of form inputs, scroll position, etc.
+       state = cls.static.gatherPreInfuseState( $elem[ 0 ], data );
+       // rebuild widget
+       // jscs:disable requireCapitalizedConstructors
+       obj = new cls( data );
+       // jscs:enable requireCapitalizedConstructors
+       // now replace old DOM with this new DOM.
+       if ( top ) {
+               // An efficient constructor might be able to reuse the entire DOM tree of the original element,
+               // so only mutate the DOM if we need to.
+               if ( $elem[ 0 ] !== obj.$element[ 0 ] ) {
+                       $elem.replaceWith( obj.$element );
+                       // This element is now gone from the DOM, but if anyone is holding a reference to it,
+                       // let's allow them to OO.ui.infuse() it and do what they expect (T105828).
+                       // Do not use jQuery.data(), as using it on detached nodes leaks memory in 1.x line by design.
+                       $elem[ 0 ].oouiInfused = obj.$element;
+               }
+               top.resolve();
+       }
+       obj.$element.data( 'ooui-infused', obj );
+       obj.$element.data( 'ooui-infused-children', infusedChildren );
+       // set the 'data-ooui' attribute so we can identify infused widgets
+       obj.$element.attr( 'data-ooui', '' );
+       // restore dynamic state after the new element is inserted into DOM
+       domPromise.done( obj.restorePreInfuseState.bind( obj, state ) );
+       return obj;
+};
+
+/**
+ * Pick out parts of `node`'s DOM to be reused when infusing a widget.
+ *
+ * This method **must not** make any changes to the DOM, only find interesting pieces and add them
+ * to `config` (which should then be returned). Actual DOM juggling should then be done by the
+ * constructor, which will be given the enhanced config.
+ *
+ * @protected
+ * @param {HTMLElement} node
+ * @param {Object} config
+ * @return {Object}
+ */
+OO.ui.Element.static.reusePreInfuseDOM = function ( node, config ) {
+       return config;
+};
+
+/**
+ * Gather the dynamic state (focus, value of form inputs, scroll position, etc.) of a HTML DOM node
+ * (and its children) that represent an Element of the same class and the given configuration,
+ * generated by the PHP implementation.
+ *
+ * This method is called just before `node` is detached from the DOM. The return value of this
+ * function will be passed to #restorePreInfuseState after the newly created widget's #$element
+ * is inserted into DOM to replace `node`.
+ *
+ * @protected
+ * @param {HTMLElement} node
+ * @param {Object} config
+ * @return {Object}
+ */
+OO.ui.Element.static.gatherPreInfuseState = function () {
+       return {};
+};
+
+/**
+ * Get a jQuery function within a specific document.
+ *
+ * @static
+ * @param {jQuery|HTMLElement|HTMLDocument|Window} context Context to bind the function to
+ * @param {jQuery} [$iframe] HTML iframe element that contains the document, omit if document is
+ *   not in an iframe
+ * @return {Function} Bound jQuery function
+ */
+OO.ui.Element.static.getJQuery = function ( context, $iframe ) {
+       function wrapper( selector ) {
+               return $( selector, wrapper.context );
+       }
+
+       wrapper.context = this.getDocument( context );
+
+       if ( $iframe ) {
+               wrapper.$iframe = $iframe;
+       }
+
+       return wrapper;
+};
+
+/**
+ * Get the document of an element.
+ *
+ * @static
+ * @param {jQuery|HTMLElement|HTMLDocument|Window} obj Object to get the document for
+ * @return {HTMLDocument|null} Document object
+ */
+OO.ui.Element.static.getDocument = function ( obj ) {
+       // jQuery - selections created "offscreen" won't have a context, so .context isn't reliable
+       return ( obj[ 0 ] && obj[ 0 ].ownerDocument ) ||
+               // Empty jQuery selections might have a context
+               obj.context ||
+               // HTMLElement
+               obj.ownerDocument ||
+               // Window
+               obj.document ||
+               // HTMLDocument
+               ( obj.nodeType === 9 && obj ) ||
+               null;
+};
+
+/**
+ * Get the window of an element or document.
+ *
+ * @static
+ * @param {jQuery|HTMLElement|HTMLDocument|Window} obj Context to get the window for
+ * @return {Window} Window object
+ */
+OO.ui.Element.static.getWindow = function ( obj ) {
+       var doc = this.getDocument( obj );
+       return doc.defaultView;
+};
+
+/**
+ * Get the direction of an element or document.
+ *
+ * @static
+ * @param {jQuery|HTMLElement|HTMLDocument|Window} obj Context to get the direction for
+ * @return {string} Text direction, either 'ltr' or 'rtl'
+ */
+OO.ui.Element.static.getDir = function ( obj ) {
+       var isDoc, isWin;
+
+       if ( obj instanceof jQuery ) {
+               obj = obj[ 0 ];
+       }
+       isDoc = obj.nodeType === 9;
+       isWin = obj.document !== undefined;
+       if ( isDoc || isWin ) {
+               if ( isWin ) {
+                       obj = obj.document;
+               }
+               obj = obj.body;
+       }
+       return $( obj ).css( 'direction' );
+};
+
+/**
+ * Get the offset between two frames.
+ *
+ * TODO: Make this function not use recursion.
+ *
+ * @static
+ * @param {Window} from Window of the child frame
+ * @param {Window} [to=window] Window of the parent frame
+ * @param {Object} [offset] Offset to start with, used internally
+ * @return {Object} Offset object, containing left and top properties
+ */
+OO.ui.Element.static.getFrameOffset = function ( from, to, offset ) {
+       var i, len, frames, frame, rect;
+
+       if ( !to ) {
+               to = window;
+       }
+       if ( !offset ) {
+               offset = { top: 0, left: 0 };
+       }
+       if ( from.parent === from ) {
+               return offset;
+       }
+
+       // Get iframe element
+       frames = from.parent.document.getElementsByTagName( 'iframe' );
+       for ( i = 0, len = frames.length; i < len; i++ ) {
+               if ( frames[ i ].contentWindow === from ) {
+                       frame = frames[ i ];
+                       break;
+               }
+       }
+
+       // Recursively accumulate offset values
+       if ( frame ) {
+               rect = frame.getBoundingClientRect();
+               offset.left += rect.left;
+               offset.top += rect.top;
+               if ( from !== to ) {
+                       this.getFrameOffset( from.parent, offset );
+               }
+       }
+       return offset;
+};
+
+/**
+ * Get the offset between two elements.
+ *
+ * The two elements may be in a different frame, but in that case the frame $element is in must
+ * be contained in the frame $anchor is in.
+ *
+ * @static
+ * @param {jQuery} $element Element whose position to get
+ * @param {jQuery} $anchor Element to get $element's position relative to
+ * @return {Object} Translated position coordinates, containing top and left properties
+ */
+OO.ui.Element.static.getRelativePosition = function ( $element, $anchor ) {
+       var iframe, iframePos,
+               pos = $element.offset(),
+               anchorPos = $anchor.offset(),
+               elementDocument = this.getDocument( $element ),
+               anchorDocument = this.getDocument( $anchor );
+
+       // If $element isn't in the same document as $anchor, traverse up
+       while ( elementDocument !== anchorDocument ) {
+               iframe = elementDocument.defaultView.frameElement;
+               if ( !iframe ) {
+                       throw new Error( '$element frame is not contained in $anchor frame' );
+               }
+               iframePos = $( iframe ).offset();
+               pos.left += iframePos.left;
+               pos.top += iframePos.top;
+               elementDocument = iframe.ownerDocument;
+       }
+       pos.left -= anchorPos.left;
+       pos.top -= anchorPos.top;
+       return pos;
+};
+
+/**
+ * Get element border sizes.
+ *
+ * @static
+ * @param {HTMLElement} el Element to measure
+ * @return {Object} Dimensions object with `top`, `left`, `bottom` and `right` properties
+ */
+OO.ui.Element.static.getBorders = function ( el ) {
+       var doc = el.ownerDocument,
+               win = doc.defaultView,
+               style = win.getComputedStyle( el, null ),
+               $el = $( el ),
+               top = parseFloat( style ? style.borderTopWidth : $el.css( 'borderTopWidth' ) ) || 0,
+               left = parseFloat( style ? style.borderLeftWidth : $el.css( 'borderLeftWidth' ) ) || 0,
+               bottom = parseFloat( style ? style.borderBottomWidth : $el.css( 'borderBottomWidth' ) ) || 0,
+               right = parseFloat( style ? style.borderRightWidth : $el.css( 'borderRightWidth' ) ) || 0;
+
+       return {
+               top: top,
+               left: left,
+               bottom: bottom,
+               right: right
+       };
+};
+
+/**
+ * Get dimensions of an element or window.
+ *
+ * @static
+ * @param {HTMLElement|Window} el Element to measure
+ * @return {Object} Dimensions object with `borders`, `scroll`, `scrollbar` and `rect` properties
+ */
+OO.ui.Element.static.getDimensions = function ( el ) {
+       var $el, $win,
+               doc = el.ownerDocument || el.document,
+               win = doc.defaultView;
+
+       if ( win === el || el === doc.documentElement ) {
+               $win = $( win );
+               return {
+                       borders: { top: 0, left: 0, bottom: 0, right: 0 },
+                       scroll: {
+                               top: $win.scrollTop(),
+                               left: $win.scrollLeft()
+                       },
+                       scrollbar: { right: 0, bottom: 0 },
+                       rect: {
+                               top: 0,
+                               left: 0,
+                               bottom: $win.innerHeight(),
+                               right: $win.innerWidth()
+                       }
+               };
+       } else {
+               $el = $( el );
+               return {
+                       borders: this.getBorders( el ),
+                       scroll: {
+                               top: $el.scrollTop(),
+                               left: $el.scrollLeft()
+                       },
+                       scrollbar: {
+                               right: $el.innerWidth() - el.clientWidth,
+                               bottom: $el.innerHeight() - el.clientHeight
+                       },
+                       rect: el.getBoundingClientRect()
+               };
+       }
+};
+
+/**
+ * Get scrollable object parent
+ *
+ * documentElement can't be used to get or set the scrollTop
+ * property on Blink. Changing and testing its value lets us
+ * use 'body' or 'documentElement' based on what is working.
+ *
+ * https://code.google.com/p/chromium/issues/detail?id=303131
+ *
+ * @static
+ * @param {HTMLElement} el Element to find scrollable parent for
+ * @return {HTMLElement} Scrollable parent
+ */
+OO.ui.Element.static.getRootScrollableElement = function ( el ) {
+       var scrollTop, body;
+
+       if ( OO.ui.scrollableElement === undefined ) {
+               body = el.ownerDocument.body;
+               scrollTop = body.scrollTop;
+               body.scrollTop = 1;
+
+               if ( body.scrollTop === 1 ) {
+                       body.scrollTop = scrollTop;
+                       OO.ui.scrollableElement = 'body';
+               } else {
+                       OO.ui.scrollableElement = 'documentElement';
+               }
+       }
+
+       return el.ownerDocument[ OO.ui.scrollableElement ];
+};
+
+/**
+ * Get closest scrollable container.
+ *
+ * Traverses up until either a scrollable element or the root is reached, in which case the window
+ * will be returned.
+ *
+ * @static
+ * @param {HTMLElement} el Element to find scrollable container for
+ * @param {string} [dimension] Dimension of scrolling to look for; `x`, `y` or omit for either
+ * @return {HTMLElement} Closest scrollable container
+ */
+OO.ui.Element.static.getClosestScrollableContainer = function ( el, dimension ) {
+       var i, val,
+               // props = [ 'overflow' ] doesn't work due to https://bugzilla.mozilla.org/show_bug.cgi?id=889091
+               props = [ 'overflow-x', 'overflow-y' ],
+               $parent = $( el ).parent();
+
+       if ( dimension === 'x' || dimension === 'y' ) {
+               props = [ 'overflow-' + dimension ];
+       }
+
+       while ( $parent.length ) {
+               if ( $parent[ 0 ] === this.getRootScrollableElement( el ) ) {
+                       return $parent[ 0 ];
+               }
+               i = props.length;
+               while ( i-- ) {
+                       val = $parent.css( props[ i ] );
+                       if ( val === 'auto' || val === 'scroll' ) {
+                               return $parent[ 0 ];
+                       }
+               }
+               $parent = $parent.parent();
+       }
+       return this.getDocument( el ).body;
+};
+
+/**
+ * Scroll element into view.
+ *
+ * @static
+ * @param {HTMLElement} el Element to scroll into view
+ * @param {Object} [config] Configuration options
+ * @param {string} [config.duration] jQuery animation duration value
+ * @param {string} [config.direction] Scroll in only one direction, e.g. 'x' or 'y', omit
+ *  to scroll in both directions
+ * @param {Function} [config.complete] Function to call when scrolling completes
+ */
+OO.ui.Element.static.scrollIntoView = function ( el, config ) {
+       var rel, anim, callback, sc, $sc, eld, scd, $win;
+
+       // Configuration initialization
+       config = config || {};
+
+       anim = {};
+       callback = typeof config.complete === 'function' && config.complete;
+       sc = this.getClosestScrollableContainer( el, config.direction );
+       $sc = $( sc );
+       eld = this.getDimensions( el );
+       scd = this.getDimensions( sc );
+       $win = $( this.getWindow( el ) );
+
+       // Compute the distances between the edges of el and the edges of the scroll viewport
+       if ( $sc.is( 'html, body' ) ) {
+               // If the scrollable container is the root, this is easy
+               rel = {
+                       top: eld.rect.top,
+                       bottom: $win.innerHeight() - eld.rect.bottom,
+                       left: eld.rect.left,
+                       right: $win.innerWidth() - eld.rect.right
+               };
+       } else {
+               // Otherwise, we have to subtract el's coordinates from sc's coordinates
+               rel = {
+                       top: eld.rect.top - ( scd.rect.top + scd.borders.top ),
+                       bottom: scd.rect.bottom - scd.borders.bottom - scd.scrollbar.bottom - eld.rect.bottom,
+                       left: eld.rect.left - ( scd.rect.left + scd.borders.left ),
+                       right: scd.rect.right - scd.borders.right - scd.scrollbar.right - eld.rect.right
+               };
+       }
+
+       if ( !config.direction || config.direction === 'y' ) {
+               if ( rel.top < 0 ) {
+                       anim.scrollTop = scd.scroll.top + rel.top;
+               } else if ( rel.top > 0 && rel.bottom < 0 ) {
+                       anim.scrollTop = scd.scroll.top + Math.min( rel.top, -rel.bottom );
+               }
+       }
+       if ( !config.direction || config.direction === 'x' ) {
+               if ( rel.left < 0 ) {
+                       anim.scrollLeft = scd.scroll.left + rel.left;
+               } else if ( rel.left > 0 && rel.right < 0 ) {
+                       anim.scrollLeft = scd.scroll.left + Math.min( rel.left, -rel.right );
+               }
+       }
+       if ( !$.isEmptyObject( anim ) ) {
+               $sc.stop( true ).animate( anim, config.duration === undefined ? 'fast' : config.duration );
+               if ( callback ) {
+                       $sc.queue( function ( next ) {
+                               callback();
+                               next();
+                       } );
+               }
+       } else {
+               if ( callback ) {
+                       callback();
+               }
+       }
+};
+
+/**
+ * Force the browser to reconsider whether it really needs to render scrollbars inside the element
+ * and reserve space for them, because it probably doesn't.
+ *
+ * Workaround primarily for <https://code.google.com/p/chromium/issues/detail?id=387290>, but also
+ * similar bugs in other browsers. "Just" forcing a reflow is not sufficient in all cases, we need
+ * to first actually detach (or hide, but detaching is simpler) all children, *then* force a reflow,
+ * and then reattach (or show) them back.
+ *
+ * @static
+ * @param {HTMLElement} el Element to reconsider the scrollbars on
+ */
+OO.ui.Element.static.reconsiderScrollbars = function ( el ) {
+       var i, len, scrollLeft, scrollTop, nodes = [];
+       // Save scroll position
+       scrollLeft = el.scrollLeft;
+       scrollTop = el.scrollTop;
+       // Detach all children
+       while ( el.firstChild ) {
+               nodes.push( el.firstChild );
+               el.removeChild( el.firstChild );
+       }
+       // Force reflow
+       void el.offsetHeight;
+       // Reattach all children
+       for ( i = 0, len = nodes.length; i < len; i++ ) {
+               el.appendChild( nodes[ i ] );
+       }
+       // Restore scroll position (no-op if scrollbars disappeared)
+       el.scrollLeft = scrollLeft;
+       el.scrollTop = scrollTop;
+};
+
+/* Methods */
+
+/**
+ * Toggle visibility of an element.
+ *
+ * @param {boolean} [show] Make element visible, omit to toggle visibility
+ * @fires visible
+ * @chainable
+ */
+OO.ui.Element.prototype.toggle = function ( show ) {
+       show = show === undefined ? !this.visible : !!show;
+
+       if ( show !== this.isVisible() ) {
+               this.visible = show;
+               this.$element.toggleClass( 'oo-ui-element-hidden', !this.visible );
+               this.emit( 'toggle', show );
+       }
+
+       return this;
+};
+
+/**
+ * Check if element is visible.
+ *
+ * @return {boolean} element is visible
+ */
+OO.ui.Element.prototype.isVisible = function () {
+       return this.visible;
+};
+
+/**
+ * Get element data.
+ *
+ * @return {Mixed} Element data
+ */
+OO.ui.Element.prototype.getData = function () {
+       return this.data;
+};
+
+/**
+ * Set element data.
+ *
+ * @param {Mixed} Element data
+ * @chainable
+ */
+OO.ui.Element.prototype.setData = function ( data ) {
+       this.data = data;
+       return this;
+};
+
+/**
+ * Check if element supports one or more methods.
+ *
+ * @param {string|string[]} methods Method or list of methods to check
+ * @return {boolean} All methods are supported
+ */
+OO.ui.Element.prototype.supports = function ( methods ) {
+       var i, len,
+               support = 0;
+
+       methods = Array.isArray( methods ) ? methods : [ methods ];
+       for ( i = 0, len = methods.length; i < len; i++ ) {
+               if ( $.isFunction( this[ methods[ i ] ] ) ) {
+                       support++;
+               }
+       }
+
+       return methods.length === support;
+};
+
+/**
+ * Update the theme-provided classes.
+ *
+ * @localdoc This is called in element mixins and widget classes any time state changes.
+ *   Updating is debounced, minimizing overhead of changing multiple attributes and
+ *   guaranteeing that theme updates do not occur within an element's constructor
+ */
+OO.ui.Element.prototype.updateThemeClasses = function () {
+       this.debouncedUpdateThemeClassesHandler();
+};
+
+/**
+ * @private
+ * @localdoc This method is called directly from the QUnit tests instead of #updateThemeClasses, to
+ *   make them synchronous.
+ */
+OO.ui.Element.prototype.debouncedUpdateThemeClasses = function () {
+       OO.ui.theme.updateElementClasses( this );
+};
+
+/**
+ * Get the HTML tag name.
+ *
+ * Override this method to base the result on instance information.
+ *
+ * @return {string} HTML tag name
+ */
+OO.ui.Element.prototype.getTagName = function () {
+       return this.constructor.static.tagName;
+};
+
+/**
+ * Check if the element is attached to the DOM
+ * @return {boolean} The element is attached to the DOM
+ */
+OO.ui.Element.prototype.isElementAttached = function () {
+       return $.contains( this.getElementDocument(), this.$element[ 0 ] );
+};
+
+/**
+ * Get the DOM document.
+ *
+ * @return {HTMLDocument} Document object
+ */
+OO.ui.Element.prototype.getElementDocument = function () {
+       // Don't cache this in other ways either because subclasses could can change this.$element
+       return OO.ui.Element.static.getDocument( this.$element );
+};
+
+/**
+ * Get the DOM window.
+ *
+ * @return {Window} Window object
+ */
+OO.ui.Element.prototype.getElementWindow = function () {
+       return OO.ui.Element.static.getWindow( this.$element );
+};
+
+/**
+ * Get closest scrollable container.
+ */
+OO.ui.Element.prototype.getClosestScrollableElementContainer = function () {
+       return OO.ui.Element.static.getClosestScrollableContainer( this.$element[ 0 ] );
+};
+
+/**
+ * Get group element is in.
+ *
+ * @return {OO.ui.mixin.GroupElement|null} Group element, null if none
+ */
+OO.ui.Element.prototype.getElementGroup = function () {
+       return this.elementGroup;
+};
+
+/**
+ * Set group element is in.
+ *
+ * @param {OO.ui.mixin.GroupElement|null} group Group element, null if none
+ * @chainable
+ */
+OO.ui.Element.prototype.setElementGroup = function ( group ) {
+       this.elementGroup = group;
+       return this;
+};
+
+/**
+ * Scroll element into view.
+ *
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.Element.prototype.scrollElementIntoView = function ( config ) {
+       return OO.ui.Element.static.scrollIntoView( this.$element[ 0 ], config );
+};
+
+/**
+ * Restore the pre-infusion dynamic state for this widget.
+ *
+ * This method is called after #$element has been inserted into DOM. The parameter is the return
+ * value of #gatherPreInfuseState.
+ *
+ * @protected
+ * @param {Object} state
+ */
+OO.ui.Element.prototype.restorePreInfuseState = function () {
+};
+
+/**
+ * Wraps an HTML snippet for use with configuration values which default
+ * to strings.  This bypasses the default html-escaping done to string
+ * values.
+ *
+ * @class
+ *
+ * @constructor
+ * @param {string} [content] HTML content
+ */
+OO.ui.HtmlSnippet = function OoUiHtmlSnippet( content ) {
+       // Properties
+       this.content = content;
+};
+
+/* Setup */
+
+OO.initClass( OO.ui.HtmlSnippet );
+
+/* Methods */
+
+/**
+ * Render into HTML.
+ *
+ * @return {string} Unchanged HTML snippet.
+ */
+OO.ui.HtmlSnippet.prototype.toString = function () {
+       return this.content;
+};
+
+/**
+ * Layouts are containers for elements and are used to arrange other widgets of arbitrary type in a way
+ * that is centrally controlled and can be updated dynamically. Layouts can be, and usually are, combined.
+ * See {@link OO.ui.FieldsetLayout FieldsetLayout}, {@link OO.ui.FieldLayout FieldLayout}, {@link OO.ui.FormLayout FormLayout},
+ * {@link OO.ui.PanelLayout PanelLayout}, {@link OO.ui.StackLayout StackLayout}, {@link OO.ui.PageLayout PageLayout},
+ * {@link OO.ui.HorizontalLayout HorizontalLayout}, and {@link OO.ui.BookletLayout BookletLayout} for more information and examples.
+ *
+ * @abstract
+ * @class
+ * @extends OO.ui.Element
+ * @mixins OO.EventEmitter
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.Layout = function OoUiLayout( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Parent constructor
+       OO.ui.Layout.parent.call( this, config );
+
+       // Mixin constructors
+       OO.EventEmitter.call( this );
+
+       // Initialization
+       this.$element.addClass( 'oo-ui-layout' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.Layout, OO.ui.Element );
+OO.mixinClass( OO.ui.Layout, OO.EventEmitter );
+
+/**
+ * Widgets are compositions of one or more OOjs UI elements that users can both view
+ * and interact with. All widgets can be configured and modified via a standard API,
+ * and their state can change dynamically according to a model.
+ *
+ * @abstract
+ * @class
+ * @extends OO.ui.Element
+ * @mixins OO.EventEmitter
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {boolean} [disabled=false] Disable the widget. Disabled widgets cannot be used and their
+ *  appearance reflects this state.
+ */
+OO.ui.Widget = function OoUiWidget( config ) {
+       // Initialize config
+       config = $.extend( { disabled: false }, config );
+
+       // Parent constructor
+       OO.ui.Widget.parent.call( this, config );
+
+       // Mixin constructors
+       OO.EventEmitter.call( this );
+
+       // Properties
+       this.disabled = null;
+       this.wasDisabled = null;
+
+       // Initialization
+       this.$element.addClass( 'oo-ui-widget' );
+       this.setDisabled( !!config.disabled );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.Widget, OO.ui.Element );
+OO.mixinClass( OO.ui.Widget, OO.EventEmitter );
+
+/* Static Properties */
+
+/**
+ * Whether this widget will behave reasonably when wrapped in a HTML `<label>`. If this is true,
+ * wrappers such as OO.ui.FieldLayout may use a `<label>` instead of implementing own label click
+ * handling.
+ *
+ * @static
+ * @inheritable
+ * @property {boolean}
+ */
+OO.ui.Widget.static.supportsSimpleLabel = false;
+
+/* Events */
+
+/**
+ * @event disable
+ *
+ * A 'disable' event is emitted when the disabled state of the widget changes
+ * (i.e. on disable **and** enable).
+ *
+ * @param {boolean} disabled Widget is disabled
+ */
+
+/**
+ * @event toggle
+ *
+ * A 'toggle' event is emitted when the visibility of the widget changes.
+ *
+ * @param {boolean} visible Widget is visible
+ */
+
+/* Methods */
+
+/**
+ * Check if the widget is disabled.
+ *
+ * @return {boolean} Widget is disabled
+ */
+OO.ui.Widget.prototype.isDisabled = function () {
+       return this.disabled;
+};
+
+/**
+ * Set the 'disabled' state of the widget.
+ *
+ * When a widget is disabled, it cannot be used and its appearance is updated to reflect this state.
+ *
+ * @param {boolean} disabled Disable widget
+ * @chainable
+ */
+OO.ui.Widget.prototype.setDisabled = function ( disabled ) {
+       var isDisabled;
+
+       this.disabled = !!disabled;
+       isDisabled = this.isDisabled();
+       if ( isDisabled !== this.wasDisabled ) {
+               this.$element.toggleClass( 'oo-ui-widget-disabled', isDisabled );
+               this.$element.toggleClass( 'oo-ui-widget-enabled', !isDisabled );
+               this.$element.attr( 'aria-disabled', isDisabled.toString() );
+               this.emit( 'disable', isDisabled );
+               this.updateThemeClasses();
+       }
+       this.wasDisabled = isDisabled;
+
+       return this;
+};
+
+/**
+ * Update the disabled state, in case of changes in parent widget.
+ *
+ * @chainable
+ */
+OO.ui.Widget.prototype.updateDisabled = function () {
+       this.setDisabled( this.disabled );
+       return this;
+};
+
+/**
+ * Theme logic.
+ *
+ * @abstract
+ * @class
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.Theme = function OoUiTheme( config ) {
+       // Configuration initialization
+       config = config || {};
+};
+
+/* Setup */
+
+OO.initClass( OO.ui.Theme );
+
+/* Methods */
+
+/**
+ * Get a list of classes to be applied to a widget.
+ *
+ * The 'on' and 'off' lists combined MUST contain keys for all classes the theme adds or removes,
+ * otherwise state transitions will not work properly.
+ *
+ * @param {OO.ui.Element} element Element for which to get classes
+ * @return {Object.<string,string[]>} Categorized class names with `on` and `off` lists
+ */
+OO.ui.Theme.prototype.getElementClasses = function () {
+       return { on: [], off: [] };
+};
+
+/**
+ * Update CSS classes provided by the theme.
+ *
+ * For elements with theme logic hooks, this should be called any time there's a state change.
+ *
+ * @param {OO.ui.Element} element Element for which to update classes
+ * @return {Object.<string,string[]>} Categorized class names with `on` and `off` lists
+ */
+OO.ui.Theme.prototype.updateElementClasses = function ( element ) {
+       var $elements = $( [] ),
+               classes = this.getElementClasses( element );
+
+       if ( element.$icon ) {
+               $elements = $elements.add( element.$icon );
+       }
+       if ( element.$indicator ) {
+               $elements = $elements.add( element.$indicator );
+       }
+
+       $elements
+               .removeClass( classes.off.join( ' ' ) )
+               .addClass( classes.on.join( ' ' ) );
+};
+
+/**
+ * The TabIndexedElement class is an attribute mixin used to add additional functionality to an
+ * element created by another class. The mixin provides a ‘tabIndex’ property, which specifies the
+ * order in which users will navigate through the focusable elements via the "tab" key.
+ *
+ *     @example
+ *     // TabIndexedElement is mixed into the ButtonWidget class
+ *     // to provide a tabIndex property.
+ *     var button1 = new OO.ui.ButtonWidget( {
+ *         label: 'fourth',
+ *         tabIndex: 4
+ *     } );
+ *     var button2 = new OO.ui.ButtonWidget( {
+ *         label: 'second',
+ *         tabIndex: 2
+ *     } );
+ *     var button3 = new OO.ui.ButtonWidget( {
+ *         label: 'third',
+ *         tabIndex: 3
+ *     } );
+ *     var button4 = new OO.ui.ButtonWidget( {
+ *         label: 'first',
+ *         tabIndex: 1
+ *     } );
+ *     $( 'body' ).append( button1.$element, button2.$element, button3.$element, button4.$element );
+ *
+ * @abstract
+ * @class
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {jQuery} [$tabIndexed] The element that should use the tabindex functionality. By default,
+ *  the functionality is applied to the element created by the class ($element). If a different element is specified, the tabindex
+ *  functionality will be applied to it instead.
+ * @cfg {number|null} [tabIndex=0] Number that specifies the element’s position in the tab-navigation
+ *  order (e.g., 1 for the first focusable element). Use 0 to use the default navigation order; use -1
+ *  to remove the element from the tab-navigation flow.
+ */
+OO.ui.mixin.TabIndexedElement = function OoUiMixinTabIndexedElement( config ) {
+       // Configuration initialization
+       config = $.extend( { tabIndex: 0 }, config );
+
+       // Properties
+       this.$tabIndexed = null;
+       this.tabIndex = null;
+
+       // Events
+       this.connect( this, { disable: 'onTabIndexedElementDisable' } );
+
+       // Initialization
+       this.setTabIndex( config.tabIndex );
+       this.setTabIndexedElement( config.$tabIndexed || this.$element );
+};
+
+/* Setup */
+
+OO.initClass( OO.ui.mixin.TabIndexedElement );
+
+/* Methods */
+
+/**
+ * Set the element that should use the tabindex functionality.
+ *
+ * This method is used to retarget a tabindex mixin so that its functionality applies
+ * to the specified element. If an element is currently using the functionality, the mixin’s
+ * effect on that element is removed before the new element is set up.
+ *
+ * @param {jQuery} $tabIndexed Element that should use the tabindex functionality
+ * @chainable
+ */
+OO.ui.mixin.TabIndexedElement.prototype.setTabIndexedElement = function ( $tabIndexed ) {
+       var tabIndex = this.tabIndex;
+       // Remove attributes from old $tabIndexed
+       this.setTabIndex( null );
+       // Force update of new $tabIndexed
+       this.$tabIndexed = $tabIndexed;
+       this.tabIndex = tabIndex;
+       return this.updateTabIndex();
+};
+
+/**
+ * Set the value of the tabindex.
+ *
+ * @param {number|null} tabIndex Tabindex value, or `null` for no tabindex
+ * @chainable
+ */
+OO.ui.mixin.TabIndexedElement.prototype.setTabIndex = function ( tabIndex ) {
+       tabIndex = typeof tabIndex === 'number' ? tabIndex : null;
+
+       if ( this.tabIndex !== tabIndex ) {
+               this.tabIndex = tabIndex;
+               this.updateTabIndex();
+       }
+
+       return this;
+};
+
+/**
+ * Update the `tabindex` attribute, in case of changes to tab index or
+ * disabled state.
+ *
+ * @private
+ * @chainable
+ */
+OO.ui.mixin.TabIndexedElement.prototype.updateTabIndex = function () {
+       if ( this.$tabIndexed ) {
+               if ( this.tabIndex !== null ) {
+                       // Do not index over disabled elements
+                       this.$tabIndexed.attr( {
+                               tabindex: this.isDisabled() ? -1 : this.tabIndex,
+                               // Support: ChromeVox and NVDA
+                               // These do not seem to inherit aria-disabled from parent elements
+                               'aria-disabled': this.isDisabled().toString()
+                       } );
+               } else {
+                       this.$tabIndexed.removeAttr( 'tabindex aria-disabled' );
+               }
+       }
+       return this;
+};
+
+/**
+ * Handle disable events.
+ *
+ * @private
+ * @param {boolean} disabled Element is disabled
+ */
+OO.ui.mixin.TabIndexedElement.prototype.onTabIndexedElementDisable = function () {
+       this.updateTabIndex();
+};
+
+/**
+ * Get the value of the tabindex.
+ *
+ * @return {number|null} Tabindex value
+ */
+OO.ui.mixin.TabIndexedElement.prototype.getTabIndex = function () {
+       return this.tabIndex;
+};
+
+/**
+ * ButtonElement is often mixed into other classes to generate a button, which is a clickable
+ * interface element that can be configured with access keys for accessibility.
+ * See the [OOjs UI documentation on MediaWiki] [1] for examples.
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Buttons_and_Switches#Buttons
+ * @abstract
+ * @class
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {jQuery} [$button] The button element created by the class.
+ *  If this configuration is omitted, the button element will use a generated `<a>`.
+ * @cfg {boolean} [framed=true] Render the button with a frame
+ */
+OO.ui.mixin.ButtonElement = function OoUiMixinButtonElement( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Properties
+       this.$button = null;
+       this.framed = null;
+       this.active = false;
+       this.onMouseUpHandler = this.onMouseUp.bind( this );
+       this.onMouseDownHandler = this.onMouseDown.bind( this );
+       this.onKeyDownHandler = this.onKeyDown.bind( this );
+       this.onKeyUpHandler = this.onKeyUp.bind( this );
+       this.onClickHandler = this.onClick.bind( this );
+       this.onKeyPressHandler = this.onKeyPress.bind( this );
+
+       // Initialization
+       this.$element.addClass( 'oo-ui-buttonElement' );
+       this.toggleFramed( config.framed === undefined || config.framed );
+       this.setButtonElement( config.$button || $( '<a>' ) );
+};
+
+/* Setup */
+
+OO.initClass( OO.ui.mixin.ButtonElement );
+
+/* Static Properties */
+
+/**
+ * Cancel mouse down events.
+ *
+ * This property is usually set to `true` to prevent the focus from changing when the button is clicked.
+ * Classes such as {@link OO.ui.mixin.DraggableElement DraggableElement} and {@link OO.ui.ButtonOptionWidget ButtonOptionWidget}
+ * use a value of `false` so that dragging behavior is possible and mousedown events can be handled by a
+ * parent widget.
+ *
+ * @static
+ * @inheritable
+ * @property {boolean}
+ */
+OO.ui.mixin.ButtonElement.static.cancelButtonMouseDownEvents = true;
+
+/* Events */
+
+/**
+ * A 'click' event is emitted when the button element is clicked.
+ *
+ * @event click
+ */
+
+/* Methods */
+
+/**
+ * Set the button element.
+ *
+ * This method is used to retarget a button mixin so that its functionality applies to
+ * the specified button element instead of the one created by the class. If a button element
+ * is already set, the method will remove the mixin’s effect on that element.
+ *
+ * @param {jQuery} $button Element to use as button
+ */
+OO.ui.mixin.ButtonElement.prototype.setButtonElement = function ( $button ) {
+       if ( this.$button ) {
+               this.$button
+                       .removeClass( 'oo-ui-buttonElement-button' )
+                       .removeAttr( 'role accesskey' )
+                       .off( {
+                               mousedown: this.onMouseDownHandler,
+                               keydown: this.onKeyDownHandler,
+                               click: this.onClickHandler,
+                               keypress: this.onKeyPressHandler
+                       } );
+       }
+
+       this.$button = $button
+               .addClass( 'oo-ui-buttonElement-button' )
+               .attr( { role: 'button' } )
+               .on( {
+                       mousedown: this.onMouseDownHandler,
+                       keydown: this.onKeyDownHandler,
+                       click: this.onClickHandler,
+                       keypress: this.onKeyPressHandler
+               } );
+};
+
+/**
+ * Handles mouse down events.
+ *
+ * @protected
+ * @param {jQuery.Event} e Mouse down event
+ */
+OO.ui.mixin.ButtonElement.prototype.onMouseDown = function ( e ) {
+       if ( this.isDisabled() || e.which !== OO.ui.MouseButtons.LEFT ) {
+               return;
+       }
+       this.$element.addClass( 'oo-ui-buttonElement-pressed' );
+       // Run the mouseup handler no matter where the mouse is when the button is let go, so we can
+       // reliably remove the pressed class
+       this.getElementDocument().addEventListener( 'mouseup', this.onMouseUpHandler, true );
+       // Prevent change of focus unless specifically configured otherwise
+       if ( this.constructor.static.cancelButtonMouseDownEvents ) {
+               return false;
+       }
+};
+
+/**
+ * Handles mouse up events.
+ *
+ * @protected
+ * @param {MouseEvent} e Mouse up event
+ */
+OO.ui.mixin.ButtonElement.prototype.onMouseUp = function ( e ) {
+       if ( this.isDisabled() || e.which !== OO.ui.MouseButtons.LEFT ) {
+               return;
+       }
+       this.$element.removeClass( 'oo-ui-buttonElement-pressed' );
+       // Stop listening for mouseup, since we only needed this once
+       this.getElementDocument().removeEventListener( 'mouseup', this.onMouseUpHandler, true );
+};
+
+/**
+ * Handles mouse click events.
+ *
+ * @protected
+ * @param {jQuery.Event} e Mouse click event
+ * @fires click
+ */
+OO.ui.mixin.ButtonElement.prototype.onClick = function ( e ) {
+       if ( !this.isDisabled() && e.which === OO.ui.MouseButtons.LEFT ) {
+               if ( this.emit( 'click' ) ) {
+                       return false;
+               }
+       }
+};
+
+/**
+ * Handles key down events.
+ *
+ * @protected
+ * @param {jQuery.Event} e Key down event
+ */
+OO.ui.mixin.ButtonElement.prototype.onKeyDown = function ( e ) {
+       if ( this.isDisabled() || ( e.which !== OO.ui.Keys.SPACE && e.which !== OO.ui.Keys.ENTER ) ) {
+               return;
+       }
+       this.$element.addClass( 'oo-ui-buttonElement-pressed' );
+       // Run the keyup handler no matter where the key is when the button is let go, so we can
+       // reliably remove the pressed class
+       this.getElementDocument().addEventListener( 'keyup', this.onKeyUpHandler, true );
+};
+
+/**
+ * Handles key up events.
+ *
+ * @protected
+ * @param {KeyboardEvent} e Key up event
+ */
+OO.ui.mixin.ButtonElement.prototype.onKeyUp = function ( e ) {
+       if ( this.isDisabled() || ( e.which !== OO.ui.Keys.SPACE && e.which !== OO.ui.Keys.ENTER ) ) {
+               return;
+       }
+       this.$element.removeClass( 'oo-ui-buttonElement-pressed' );
+       // Stop listening for keyup, since we only needed this once
+       this.getElementDocument().removeEventListener( 'keyup', this.onKeyUpHandler, true );
+};
+
+/**
+ * Handles key press events.
+ *
+ * @protected
+ * @param {jQuery.Event} e Key press event
+ * @fires click
+ */
+OO.ui.mixin.ButtonElement.prototype.onKeyPress = function ( e ) {
+       if ( !this.isDisabled() && ( e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER ) ) {
+               if ( this.emit( 'click' ) ) {
+                       return false;
+               }
+       }
+};
+
+/**
+ * Check if button has a frame.
+ *
+ * @return {boolean} Button is framed
+ */
+OO.ui.mixin.ButtonElement.prototype.isFramed = function () {
+       return this.framed;
+};
+
+/**
+ * Render the button with or without a frame. Omit the `framed` parameter to toggle the button frame on and off.
+ *
+ * @param {boolean} [framed] Make button framed, omit to toggle
+ * @chainable
+ */
+OO.ui.mixin.ButtonElement.prototype.toggleFramed = function ( framed ) {
+       framed = framed === undefined ? !this.framed : !!framed;
+       if ( framed !== this.framed ) {
+               this.framed = framed;
+               this.$element
+                       .toggleClass( 'oo-ui-buttonElement-frameless', !framed )
+                       .toggleClass( 'oo-ui-buttonElement-framed', framed );
+               this.updateThemeClasses();
+       }
+
+       return this;
+};
+
+/**
+ * Set the button's active state.
+ *
+ * The active state occurs when a {@link OO.ui.ButtonOptionWidget ButtonOptionWidget} or
+ * a {@link OO.ui.ToggleButtonWidget ToggleButtonWidget} is pressed. This method does nothing
+ * for other button types.
+ *
+ * @param {boolean} value Make button active
+ * @chainable
+ */
+OO.ui.mixin.ButtonElement.prototype.setActive = function ( value ) {
+       this.active = !!value;
+       this.$element.toggleClass( 'oo-ui-buttonElement-active', this.active );
+       return this;
+};
+
+/**
+ * Check if the button is active
+ *
+ * @return {boolean} The button is active
+ */
+OO.ui.mixin.ButtonElement.prototype.isActive = function () {
+       return this.active;
+};
+
+/**
+ * Any OOjs UI widget that contains other widgets (such as {@link OO.ui.ButtonWidget buttons} or
+ * {@link OO.ui.OptionWidget options}) mixes in GroupElement. Adding, removing, and clearing
+ * items from the group is done through the interface the class provides.
+ * For more information, please see the [OOjs UI documentation on MediaWiki] [1].
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Elements/Groups
+ *
+ * @abstract
+ * @class
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {jQuery} [$group] The container element created by the class. If this configuration
+ *  is omitted, the group element will use a generated `<div>`.
+ */
+OO.ui.mixin.GroupElement = function OoUiMixinGroupElement( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Properties
+       this.$group = null;
+       this.items = [];
+       this.aggregateItemEvents = {};
+
+       // Initialization
+       this.setGroupElement( config.$group || $( '<div>' ) );
+};
+
+/* Methods */
+
+/**
+ * Set the group element.
+ *
+ * If an element is already set, items will be moved to the new element.
+ *
+ * @param {jQuery} $group Element to use as group
+ */
+OO.ui.mixin.GroupElement.prototype.setGroupElement = function ( $group ) {
+       var i, len;
+
+       this.$group = $group;
+       for ( i = 0, len = this.items.length; i < len; i++ ) {
+               this.$group.append( this.items[ i ].$element );
+       }
+};
+
+/**
+ * Check if a group contains no items.
+ *
+ * @return {boolean} Group is empty
+ */
+OO.ui.mixin.GroupElement.prototype.isEmpty = function () {
+       return !this.items.length;
+};
+
+/**
+ * Get all items in the group.
+ *
+ * The method returns an array of item references (e.g., [button1, button2, button3]) and is useful
+ * when synchronizing groups of items, or whenever the references are required (e.g., when removing items
+ * from a group).
+ *
+ * @return {OO.ui.Element[]} An array of items.
+ */
+OO.ui.mixin.GroupElement.prototype.getItems = function () {
+       return this.items.slice( 0 );
+};
+
+/**
+ * Get an item by its data.
+ *
+ * Only the first item with matching data will be returned. To return all matching items,
+ * use the #getItemsFromData method.
+ *
+ * @param {Object} data Item data to search for
+ * @return {OO.ui.Element|null} Item with equivalent data, `null` if none exists
+ */
+OO.ui.mixin.GroupElement.prototype.getItemFromData = function ( data ) {
+       var i, len, item,
+               hash = OO.getHash( data );
+
+       for ( i = 0, len = this.items.length; i < len; i++ ) {
+               item = this.items[ i ];
+               if ( hash === OO.getHash( item.getData() ) ) {
+                       return item;
+               }
+       }
+
+       return null;
+};
+
+/**
+ * Get items by their data.
+ *
+ * All items with matching data will be returned. To return only the first match, use the #getItemFromData method instead.
+ *
+ * @param {Object} data Item data to search for
+ * @return {OO.ui.Element[]} Items with equivalent data
+ */
+OO.ui.mixin.GroupElement.prototype.getItemsFromData = function ( data ) {
+       var i, len, item,
+               hash = OO.getHash( data ),
+               items = [];
+
+       for ( i = 0, len = this.items.length; i < len; i++ ) {
+               item = this.items[ i ];
+               if ( hash === OO.getHash( item.getData() ) ) {
+                       items.push( item );
+               }
+       }
+
+       return items;
+};
+
+/**
+ * Aggregate the events emitted by the group.
+ *
+ * When events are aggregated, the group will listen to all contained items for the event,
+ * and then emit the event under a new name. The new event will contain an additional leading
+ * parameter containing the item that emitted the original event. Other arguments emitted from
+ * the original event are passed through.
+ *
+ * @param {Object.<string,string|null>} events An object keyed by the name of the event that should be
+ *  aggregated  (e.g., ‘click’) and the value of the new name to use (e.g., ‘groupClick’).
+ *  A `null` value will remove aggregated events.
+
+ * @throws {Error} An error is thrown if aggregation already exists.
+ */
+OO.ui.mixin.GroupElement.prototype.aggregate = function ( events ) {
+       var i, len, item, add, remove, itemEvent, groupEvent;
+
+       for ( itemEvent in events ) {
+               groupEvent = events[ itemEvent ];
+
+               // Remove existing aggregated event
+               if ( Object.prototype.hasOwnProperty.call( this.aggregateItemEvents, itemEvent ) ) {
+                       // Don't allow duplicate aggregations
+                       if ( groupEvent ) {
+                               throw new Error( 'Duplicate item event aggregation for ' + itemEvent );
+                       }
+                       // Remove event aggregation from existing items
+                       for ( i = 0, len = this.items.length; i < len; i++ ) {
+                               item = this.items[ i ];
+                               if ( item.connect && item.disconnect ) {
+                                       remove = {};
+                                       remove[ itemEvent ] = [ 'emit', this.aggregateItemEvents[ itemEvent ], item ];
+                                       item.disconnect( this, remove );
+                               }
+                       }
+                       // Prevent future items from aggregating event
+                       delete this.aggregateItemEvents[ itemEvent ];
+               }
+
+               // Add new aggregate event
+               if ( groupEvent ) {
+                       // Make future items aggregate event
+                       this.aggregateItemEvents[ itemEvent ] = groupEvent;
+                       // Add event aggregation to existing items
+                       for ( i = 0, len = this.items.length; i < len; i++ ) {
+                               item = this.items[ i ];
+                               if ( item.connect && item.disconnect ) {
+                                       add = {};
+                                       add[ itemEvent ] = [ 'emit', groupEvent, item ];
+                                       item.connect( this, add );
+                               }
+                       }
+               }
+       }
+};
+
+/**
+ * Add items to the group.
+ *
+ * Items will be added to the end of the group array unless the optional `index` parameter specifies
+ * a different insertion point. Adding an existing item will move it to the end of the array or the point specified by the `index`.
+ *
+ * @param {OO.ui.Element[]} items An array of items to add to the group
+ * @param {number} [index] Index of the insertion point
+ * @chainable
+ */
+OO.ui.mixin.GroupElement.prototype.addItems = function ( items, index ) {
+       var i, len, item, event, events, currentIndex,
+               itemElements = [];
+
+       for ( i = 0, len = items.length; i < len; i++ ) {
+               item = items[ i ];
+
+               // Check if item exists then remove it first, effectively "moving" it
+               currentIndex = this.items.indexOf( item );
+               if ( currentIndex >= 0 ) {
+                       this.removeItems( [ item ] );
+                       // Adjust index to compensate for removal
+                       if ( currentIndex < index ) {
+                               index--;
+                       }
+               }
+               // Add the item
+               if ( item.connect && item.disconnect && !$.isEmptyObject( this.aggregateItemEvents ) ) {
+                       events = {};
+                       for ( event in this.aggregateItemEvents ) {
+                               events[ event ] = [ 'emit', this.aggregateItemEvents[ event ], item ];
+                       }
+                       item.connect( this, events );
+               }
+               item.setElementGroup( this );
+               itemElements.push( item.$element.get( 0 ) );
+       }
+
+       if ( index === undefined || index < 0 || index >= this.items.length ) {
+               this.$group.append( itemElements );
+               this.items.push.apply( this.items, items );
+       } else if ( index === 0 ) {
+               this.$group.prepend( itemElements );
+               this.items.unshift.apply( this.items, items );
+       } else {
+               this.items[ index ].$element.before( itemElements );
+               this.items.splice.apply( this.items, [ index, 0 ].concat( items ) );
+       }
+
+       return this;
+};
+
+/**
+ * Remove the specified items from a group.
+ *
+ * Removed items are detached (not removed) from the DOM so that they may be reused.
+ * To remove all items from a group, you may wish to use the #clearItems method instead.
+ *
+ * @param {OO.ui.Element[]} items An array of items to remove
+ * @chainable
+ */
+OO.ui.mixin.GroupElement.prototype.removeItems = function ( items ) {
+       var i, len, item, index, remove, itemEvent;
+
+       // Remove specific items
+       for ( i = 0, len = items.length; i < len; i++ ) {
+               item = items[ i ];
+               index = this.items.indexOf( item );
+               if ( index !== -1 ) {
+                       if (
+                               item.connect && item.disconnect &&
+                               !$.isEmptyObject( this.aggregateItemEvents )
+                       ) {
+                               remove = {};
+                               if ( Object.prototype.hasOwnProperty.call( this.aggregateItemEvents, itemEvent ) ) {
+                                       remove[ itemEvent ] = [ 'emit', this.aggregateItemEvents[ itemEvent ], item ];
+                               }
+                               item.disconnect( this, remove );
+                       }
+                       item.setElementGroup( null );
+                       this.items.splice( index, 1 );
+                       item.$element.detach();
+               }
+       }
+
+       return this;
+};
+
+/**
+ * Clear all items from the group.
+ *
+ * Cleared items are detached from the DOM, not removed, so that they may be reused.
+ * To remove only a subset of items from a group, use the #removeItems method.
+ *
+ * @chainable
+ */
+OO.ui.mixin.GroupElement.prototype.clearItems = function () {
+       var i, len, item, remove, itemEvent;
+
+       // Remove all items
+       for ( i = 0, len = this.items.length; i < len; i++ ) {
+               item = this.items[ i ];
+               if (
+                       item.connect && item.disconnect &&
+                       !$.isEmptyObject( this.aggregateItemEvents )
+               ) {
+                       remove = {};
+                       if ( Object.prototype.hasOwnProperty.call( this.aggregateItemEvents, itemEvent ) ) {
+                               remove[ itemEvent ] = [ 'emit', this.aggregateItemEvents[ itemEvent ], item ];
+                       }
+                       item.disconnect( this, remove );
+               }
+               item.setElementGroup( null );
+               item.$element.detach();
+       }
+
+       this.items = [];
+       return this;
+};
+
+/**
+ * IconElement is often mixed into other classes to generate an icon.
+ * Icons are graphics, about the size of normal text. They are used to aid the user
+ * in locating a control or to convey information in a space-efficient way. See the
+ * [OOjs UI documentation on MediaWiki] [1] for a list of icons
+ * included in the library.
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Icons,_Indicators,_and_Labels#Icons
+ *
+ * @abstract
+ * @class
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {jQuery} [$icon] The icon element created by the class. If this configuration is omitted,
+ *  the icon element will use a generated `<span>`. To use a different HTML tag, or to specify that
+ *  the icon element be set to an existing icon instead of the one generated by this class, set a
+ *  value using a jQuery selection. For example:
+ *
+ *      // Use a <div> tag instead of a <span>
+ *     $icon: $("<div>")
+ *     // Use an existing icon element instead of the one generated by the class
+ *     $icon: this.$element
+ *     // Use an icon element from a child widget
+ *     $icon: this.childwidget.$element
+ * @cfg {Object|string} [icon=''] The symbolic name of the icon (e.g., ‘remove’ or ‘menu’), or a map of
+ *  symbolic names.  A map is used for i18n purposes and contains a `default` icon
+ *  name and additional names keyed by language code. The `default` name is used when no icon is keyed
+ *  by the user's language.
+ *
+ *  Example of an i18n map:
+ *
+ *     { default: 'bold-a', en: 'bold-b', de: 'bold-f' }
+ *  See the [OOjs UI documentation on MediaWiki] [2] for a list of icons included in the library.
+ * [2]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Icons,_Indicators,_and_Labels#Icons
+ * @cfg {string|Function} [iconTitle] A text string used as the icon title, or a function that returns title
+ *  text. The icon title is displayed when users move the mouse over the icon.
+ */
+OO.ui.mixin.IconElement = function OoUiMixinIconElement( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Properties
+       this.$icon = null;
+       this.icon = null;
+       this.iconTitle = null;
+
+       // Initialization
+       this.setIcon( config.icon || this.constructor.static.icon );
+       this.setIconTitle( config.iconTitle || this.constructor.static.iconTitle );
+       this.setIconElement( config.$icon || $( '<span>' ) );
+};
+
+/* Setup */
+
+OO.initClass( OO.ui.mixin.IconElement );
+
+/* Static Properties */
+
+/**
+ * The symbolic name of the icon (e.g., ‘remove’ or ‘menu’), or a map of symbolic names. A map is used
+ * for i18n purposes and contains a `default` icon name and additional names keyed by
+ * language code. The `default` name is used when no icon is keyed by the user's language.
+ *
+ * Example of an i18n map:
+ *
+ *     { default: 'bold-a', en: 'bold-b', de: 'bold-f' }
+ *
+ * Note: the static property will be overridden if the #icon configuration is used.
+ *
+ * @static
+ * @inheritable
+ * @property {Object|string}
+ */
+OO.ui.mixin.IconElement.static.icon = null;
+
+/**
+ * The icon title, displayed when users move the mouse over the icon. The value can be text, a
+ * function that returns title text, or `null` for no title.
+ *
+ * The static property will be overridden if the #iconTitle configuration is used.
+ *
+ * @static
+ * @inheritable
+ * @property {string|Function|null}
+ */
+OO.ui.mixin.IconElement.static.iconTitle = null;
+
+/* Methods */
+
+/**
+ * Set the icon element. This method is used to retarget an icon mixin so that its functionality
+ * applies to the specified icon element instead of the one created by the class. If an icon
+ * element is already set, the mixin’s effect on that element is removed. Generated CSS classes
+ * and mixin methods will no longer affect the element.
+ *
+ * @param {jQuery} $icon Element to use as icon
+ */
+OO.ui.mixin.IconElement.prototype.setIconElement = function ( $icon ) {
+       if ( this.$icon ) {
+               this.$icon
+                       .removeClass( 'oo-ui-iconElement-icon oo-ui-icon-' + this.icon )
+                       .removeAttr( 'title' );
+       }
+
+       this.$icon = $icon
+               .addClass( 'oo-ui-iconElement-icon' )
+               .toggleClass( 'oo-ui-icon-' + this.icon, !!this.icon );
+       if ( this.iconTitle !== null ) {
+               this.$icon.attr( 'title', this.iconTitle );
+       }
+
+       this.updateThemeClasses();
+};
+
+/**
+ * Set icon by symbolic name (e.g., ‘remove’ or ‘menu’). Use `null` to remove an icon.
+ * The icon parameter can also be set to a map of icon names. See the #icon config setting
+ * for an example.
+ *
+ * @param {Object|string|null} icon A symbolic icon name, a {@link #icon map of icon names} keyed
+ *  by language code, or `null` to remove the icon.
+ * @chainable
+ */
+OO.ui.mixin.IconElement.prototype.setIcon = function ( icon ) {
+       icon = OO.isPlainObject( icon ) ? OO.ui.getLocalValue( icon, null, 'default' ) : icon;
+       icon = typeof icon === 'string' && icon.trim().length ? icon.trim() : null;
+
+       if ( this.icon !== icon ) {
+               if ( this.$icon ) {
+                       if ( this.icon !== null ) {
+                               this.$icon.removeClass( 'oo-ui-icon-' + this.icon );
+                       }
+                       if ( icon !== null ) {
+                               this.$icon.addClass( 'oo-ui-icon-' + icon );
+                       }
+               }
+               this.icon = icon;
+       }
+
+       this.$element.toggleClass( 'oo-ui-iconElement', !!this.icon );
+       this.updateThemeClasses();
+
+       return this;
+};
+
+/**
+ * Set the icon title. Use `null` to remove the title.
+ *
+ * @param {string|Function|null} iconTitle A text string used as the icon title,
+ *  a function that returns title text, or `null` for no title.
+ * @chainable
+ */
+OO.ui.mixin.IconElement.prototype.setIconTitle = function ( iconTitle ) {
+       iconTitle = typeof iconTitle === 'function' ||
+               ( typeof iconTitle === 'string' && iconTitle.length ) ?
+                       OO.ui.resolveMsg( iconTitle ) : null;
+
+       if ( this.iconTitle !== iconTitle ) {
+               this.iconTitle = iconTitle;
+               if ( this.$icon ) {
+                       if ( this.iconTitle !== null ) {
+                               this.$icon.attr( 'title', iconTitle );
+                       } else {
+                               this.$icon.removeAttr( 'title' );
+                       }
+               }
+       }
+
+       return this;
+};
+
+/**
+ * Get the symbolic name of the icon.
+ *
+ * @return {string} Icon name
+ */
+OO.ui.mixin.IconElement.prototype.getIcon = function () {
+       return this.icon;
+};
+
+/**
+ * Get the icon title. The title text is displayed when a user moves the mouse over the icon.
+ *
+ * @return {string} Icon title text
+ */
+OO.ui.mixin.IconElement.prototype.getIconTitle = function () {
+       return this.iconTitle;
+};
+
+/**
+ * IndicatorElement is often mixed into other classes to generate an indicator.
+ * Indicators are small graphics that are generally used in two ways:
+ *
+ * - To draw attention to the status of an item. For example, an indicator might be
+ *   used to show that an item in a list has errors that need to be resolved.
+ * - To clarify the function of a control that acts in an exceptional way (a button
+ *   that opens a menu instead of performing an action directly, for example).
+ *
+ * For a list of indicators included in the library, please see the
+ * [OOjs UI documentation on MediaWiki] [1].
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Icons,_Indicators,_and_Labels#Indicators
+ *
+ * @abstract
+ * @class
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {jQuery} [$indicator] The indicator element created by the class. If this
+ *  configuration is omitted, the indicator element will use a generated `<span>`.
+ * @cfg {string} [indicator] Symbolic name of the indicator (e.g., ‘alert’ or  ‘down’).
+ *  See the [OOjs UI documentation on MediaWiki][2] for a list of indicators included
+ *  in the library.
+ * [2]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Icons,_Indicators,_and_Labels#Indicators
+ * @cfg {string|Function} [indicatorTitle] A text string used as the indicator title,
+ *  or a function that returns title text. The indicator title is displayed when users move
+ *  the mouse over the indicator.
+ */
+OO.ui.mixin.IndicatorElement = function OoUiMixinIndicatorElement( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Properties
+       this.$indicator = null;
+       this.indicator = null;
+       this.indicatorTitle = null;
+
+       // Initialization
+       this.setIndicator( config.indicator || this.constructor.static.indicator );
+       this.setIndicatorTitle( config.indicatorTitle || this.constructor.static.indicatorTitle );
+       this.setIndicatorElement( config.$indicator || $( '<span>' ) );
+};
+
+/* Setup */
+
+OO.initClass( OO.ui.mixin.IndicatorElement );
+
+/* Static Properties */
+
+/**
+ * Symbolic name of the indicator (e.g., ‘alert’ or  ‘down’).
+ * The static property will be overridden if the #indicator configuration is used.
+ *
+ * @static
+ * @inheritable
+ * @property {string|null}
+ */
+OO.ui.mixin.IndicatorElement.static.indicator = null;
+
+/**
+ * A text string used as the indicator title, a function that returns title text, or `null`
+ * for no title. The static property will be overridden if the #indicatorTitle configuration is used.
+ *
+ * @static
+ * @inheritable
+ * @property {string|Function|null}
+ */
+OO.ui.mixin.IndicatorElement.static.indicatorTitle = null;
+
+/* Methods */
+
+/**
+ * Set the indicator element.
+ *
+ * If an element is already set, it will be cleaned up before setting up the new element.
+ *
+ * @param {jQuery} $indicator Element to use as indicator
+ */
+OO.ui.mixin.IndicatorElement.prototype.setIndicatorElement = function ( $indicator ) {
+       if ( this.$indicator ) {
+               this.$indicator
+                       .removeClass( 'oo-ui-indicatorElement-indicator oo-ui-indicator-' + this.indicator )
+                       .removeAttr( 'title' );
+       }
+
+       this.$indicator = $indicator
+               .addClass( 'oo-ui-indicatorElement-indicator' )
+               .toggleClass( 'oo-ui-indicator-' + this.indicator, !!this.indicator );
+       if ( this.indicatorTitle !== null ) {
+               this.$indicator.attr( 'title', this.indicatorTitle );
+       }
+
+       this.updateThemeClasses();
+};
+
+/**
+ * Set the indicator by its symbolic name: ‘alert’, ‘down’, ‘next’, ‘previous’, ‘required’, ‘up’. Use `null` to remove the indicator.
+ *
+ * @param {string|null} indicator Symbolic name of indicator, or `null` for no indicator
+ * @chainable
+ */
+OO.ui.mixin.IndicatorElement.prototype.setIndicator = function ( indicator ) {
+       indicator = typeof indicator === 'string' && indicator.length ? indicator.trim() : null;
+
+       if ( this.indicator !== indicator ) {
+               if ( this.$indicator ) {
+                       if ( this.indicator !== null ) {
+                               this.$indicator.removeClass( 'oo-ui-indicator-' + this.indicator );
+                       }
+                       if ( indicator !== null ) {
+                               this.$indicator.addClass( 'oo-ui-indicator-' + indicator );
+                       }
+               }
+               this.indicator = indicator;
+       }
+
+       this.$element.toggleClass( 'oo-ui-indicatorElement', !!this.indicator );
+       this.updateThemeClasses();
+
+       return this;
+};
+
+/**
+ * Set the indicator title.
+ *
+ * The title is displayed when a user moves the mouse over the indicator.
+ *
+ * @param {string|Function|null} indicator Indicator title text, a function that returns text, or
+ *   `null` for no indicator title
+ * @chainable
+ */
+OO.ui.mixin.IndicatorElement.prototype.setIndicatorTitle = function ( indicatorTitle ) {
+       indicatorTitle = typeof indicatorTitle === 'function' ||
+               ( typeof indicatorTitle === 'string' && indicatorTitle.length ) ?
+                       OO.ui.resolveMsg( indicatorTitle ) : null;
+
+       if ( this.indicatorTitle !== indicatorTitle ) {
+               this.indicatorTitle = indicatorTitle;
+               if ( this.$indicator ) {
+                       if ( this.indicatorTitle !== null ) {
+                               this.$indicator.attr( 'title', indicatorTitle );
+                       } else {
+                               this.$indicator.removeAttr( 'title' );
+                       }
+               }
+       }
+
+       return this;
+};
+
+/**
+ * Get the symbolic name of the indicator (e.g., ‘alert’ or  ‘down’).
+ *
+ * @return {string} Symbolic name of indicator
+ */
+OO.ui.mixin.IndicatorElement.prototype.getIndicator = function () {
+       return this.indicator;
+};
+
+/**
+ * Get the indicator title.
+ *
+ * The title is displayed when a user moves the mouse over the indicator.
+ *
+ * @return {string} Indicator title text
+ */
+OO.ui.mixin.IndicatorElement.prototype.getIndicatorTitle = function () {
+       return this.indicatorTitle;
+};
+
+/**
+ * LabelElement is often mixed into other classes to generate a label, which
+ * helps identify the function of an interface element.
+ * See the [OOjs UI documentation on MediaWiki] [1] for more information.
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Icons,_Indicators,_and_Labels#Labels
+ *
+ * @abstract
+ * @class
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {jQuery} [$label] The label element created by the class. If this
+ *  configuration is omitted, the label element will use a generated `<span>`.
+ * @cfg {jQuery|string|Function|OO.ui.HtmlSnippet} [label] The label text. The label can be specified
+ *  as a plaintext string, a jQuery selection of elements, or a function that will produce a string
+ *  in the future. See the [OOjs UI documentation on MediaWiki] [2] for examples.
+ *  [2]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Icons,_Indicators,_and_Labels#Labels
+ * @cfg {boolean} [autoFitLabel=true] Fit the label to the width of the parent element.
+ *  The label will be truncated to fit if necessary.
+ */
+OO.ui.mixin.LabelElement = function OoUiMixinLabelElement( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Properties
+       this.$label = null;
+       this.label = null;
+       this.autoFitLabel = config.autoFitLabel === undefined || !!config.autoFitLabel;
+
+       // Initialization
+       this.setLabel( config.label || this.constructor.static.label );
+       this.setLabelElement( config.$label || $( '<span>' ) );
+};
+
+/* Setup */
+
+OO.initClass( OO.ui.mixin.LabelElement );
+
+/* Events */
+
+/**
+ * @event labelChange
+ * @param {string} value
+ */
+
+/* Static Properties */
+
+/**
+ * The label text. The label can be specified as a plaintext string, a function that will
+ * produce a string in the future, or `null` for no label. The static value will
+ * be overridden if a label is specified with the #label config option.
+ *
+ * @static
+ * @inheritable
+ * @property {string|Function|null}
+ */
+OO.ui.mixin.LabelElement.static.label = null;
+
+/* Methods */
+
+/**
+ * Set the label element.
+ *
+ * If an element is already set, it will be cleaned up before setting up the new element.
+ *
+ * @param {jQuery} $label Element to use as label
+ */
+OO.ui.mixin.LabelElement.prototype.setLabelElement = function ( $label ) {
+       if ( this.$label ) {
+               this.$label.removeClass( 'oo-ui-labelElement-label' ).empty();
+       }
+
+       this.$label = $label.addClass( 'oo-ui-labelElement-label' );
+       this.setLabelContent( this.label );
+};
+
+/**
+ * Set the label.
+ *
+ * An empty string will result in the label being hidden. A string containing only whitespace will
+ * be converted to a single `&nbsp;`.
+ *
+ * @param {jQuery|string|OO.ui.HtmlSnippet|Function|null} label Label nodes; text; a function that returns nodes or
+ *  text; or null for no label
+ * @chainable
+ */
+OO.ui.mixin.LabelElement.prototype.setLabel = function ( label ) {
+       label = typeof label === 'function' ? OO.ui.resolveMsg( label ) : label;
+       label = ( ( typeof label === 'string' && label.length ) || label instanceof jQuery || label instanceof OO.ui.HtmlSnippet ) ? label : null;
+
+       this.$element.toggleClass( 'oo-ui-labelElement', !!label );
+
+       if ( this.label !== label ) {
+               if ( this.$label ) {
+                       this.setLabelContent( label );
+               }
+               this.label = label;
+               this.emit( 'labelChange' );
+       }
+
+       return this;
+};
+
+/**
+ * Get the label.
+ *
+ * @return {jQuery|string|Function|null} Label nodes; text; a function that returns nodes or
+ *  text; or null for no label
+ */
+OO.ui.mixin.LabelElement.prototype.getLabel = function () {
+       return this.label;
+};
+
+/**
+ * Fit the label.
+ *
+ * @chainable
+ */
+OO.ui.mixin.LabelElement.prototype.fitLabel = function () {
+       if ( this.$label && this.$label.autoEllipsis && this.autoFitLabel ) {
+               this.$label.autoEllipsis( { hasSpan: false, tooltip: true } );
+       }
+
+       return this;
+};
+
+/**
+ * Set the content of the label.
+ *
+ * Do not call this method until after the label element has been set by #setLabelElement.
+ *
+ * @private
+ * @param {jQuery|string|Function|null} label Label nodes; text; a function that returns nodes or
+ *  text; or null for no label
+ */
+OO.ui.mixin.LabelElement.prototype.setLabelContent = function ( label ) {
+       if ( typeof label === 'string' ) {
+               if ( label.match( /^\s*$/ ) ) {
+                       // Convert whitespace only string to a single non-breaking space
+                       this.$label.html( '&nbsp;' );
+               } else {
+                       this.$label.text( label );
+               }
+       } else if ( label instanceof OO.ui.HtmlSnippet ) {
+               this.$label.html( label.toString() );
+       } else if ( label instanceof jQuery ) {
+               this.$label.empty().append( label );
+       } else {
+               this.$label.empty();
+       }
+};
+
+/**
+ * The FlaggedElement class is an attribute mixin, meaning that it is used to add
+ * additional functionality to an element created by another class. The class provides
+ * a ‘flags’ property assigned the name (or an array of names) of styling flags,
+ * which are used to customize the look and feel of a widget to better describe its
+ * importance and functionality.
+ *
+ * The library currently contains the following styling flags for general use:
+ *
+ * - **progressive**:  Progressive styling is applied to convey that the widget will move the user forward in a process.
+ * - **destructive**: Destructive styling is applied to convey that the widget will remove something.
+ * - **constructive**: Constructive styling is applied to convey that the widget will create something.
+ *
+ * The flags affect the appearance of the buttons:
+ *
+ *     @example
+ *     // FlaggedElement is mixed into ButtonWidget to provide styling flags
+ *     var button1 = new OO.ui.ButtonWidget( {
+ *         label: 'Constructive',
+ *         flags: 'constructive'
+ *     } );
+ *     var button2 = new OO.ui.ButtonWidget( {
+ *         label: 'Destructive',
+ *         flags: 'destructive'
+ *     } );
+ *     var button3 = new OO.ui.ButtonWidget( {
+ *         label: 'Progressive',
+ *         flags: 'progressive'
+ *     } );
+ *     $( 'body' ).append( button1.$element, button2.$element, button3.$element );
+ *
+ * {@link OO.ui.ActionWidget ActionWidgets}, which are a special kind of button that execute an action, use these flags: **primary** and **safe**.
+ * Please see the [OOjs UI documentation on MediaWiki] [1] for more information.
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Elements/Flagged
+ *
+ * @abstract
+ * @class
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {string|string[]} [flags] The name or names of the flags (e.g., 'constructive' or 'primary') to apply.
+ *  Please see the [OOjs UI documentation on MediaWiki] [2] for more information about available flags.
+ *  [2]: https://www.mediawiki.org/wiki/OOjs_UI/Elements/Flagged
+ * @cfg {jQuery} [$flagged] The flagged element. By default,
+ *  the flagged functionality is applied to the element created by the class ($element).
+ *  If a different element is specified, the flagged functionality will be applied to it instead.
+ */
+OO.ui.mixin.FlaggedElement = function OoUiMixinFlaggedElement( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Properties
+       this.flags = {};
+       this.$flagged = null;
+
+       // Initialization
+       this.setFlags( config.flags );
+       this.setFlaggedElement( config.$flagged || this.$element );
+};
+
+/* Events */
+
+/**
+ * @event flag
+ * A flag event is emitted when the #clearFlags or #setFlags methods are used. The `changes`
+ * parameter contains the name of each modified flag and indicates whether it was
+ * added or removed.
+ *
+ * @param {Object.<string,boolean>} changes Object keyed by flag name. A Boolean `true` indicates
+ * that the flag was added, `false` that the flag was removed.
+ */
+
+/* Methods */
+
+/**
+ * Set the flagged element.
+ *
+ * This method is used to retarget a flagged mixin so that its functionality applies to the specified element.
+ * If an element is already set, the method will remove the mixin’s effect on that element.
+ *
+ * @param {jQuery} $flagged Element that should be flagged
+ */
+OO.ui.mixin.FlaggedElement.prototype.setFlaggedElement = function ( $flagged ) {
+       var classNames = Object.keys( this.flags ).map( function ( flag ) {
+               return 'oo-ui-flaggedElement-' + flag;
+       } ).join( ' ' );
+
+       if ( this.$flagged ) {
+               this.$flagged.removeClass( classNames );
+       }
+
+       this.$flagged = $flagged.addClass( classNames );
+};
+
+/**
+ * Check if the specified flag is set.
+ *
+ * @param {string} flag Name of flag
+ * @return {boolean} The flag is set
+ */
+OO.ui.mixin.FlaggedElement.prototype.hasFlag = function ( flag ) {
+       // This may be called before the constructor, thus before this.flags is set
+       return this.flags && ( flag in this.flags );
+};
+
+/**
+ * Get the names of all flags set.
+ *
+ * @return {string[]} Flag names
+ */
+OO.ui.mixin.FlaggedElement.prototype.getFlags = function () {
+       // This may be called before the constructor, thus before this.flags is set
+       return Object.keys( this.flags || {} );
+};
+
+/**
+ * Clear all flags.
+ *
+ * @chainable
+ * @fires flag
+ */
+OO.ui.mixin.FlaggedElement.prototype.clearFlags = function () {
+       var flag, className,
+               changes = {},
+               remove = [],
+               classPrefix = 'oo-ui-flaggedElement-';
+
+       for ( flag in this.flags ) {
+               className = classPrefix + flag;
+               changes[ flag ] = false;
+               delete this.flags[ flag ];
+               remove.push( className );
+       }
+
+       if ( this.$flagged ) {
+               this.$flagged.removeClass( remove.join( ' ' ) );
+       }
+
+       this.updateThemeClasses();
+       this.emit( 'flag', changes );
+
+       return this;
+};
+
+/**
+ * Add one or more flags.
+ *
+ * @param {string|string[]|Object.<string, boolean>} flags A flag name, an array of flag names,
+ *  or an object keyed by flag name with a boolean value that indicates whether the flag should
+ *  be added (`true`) or removed (`false`).
+ * @chainable
+ * @fires flag
+ */
+OO.ui.mixin.FlaggedElement.prototype.setFlags = function ( flags ) {
+       var i, len, flag, className,
+               changes = {},
+               add = [],
+               remove = [],
+               classPrefix = 'oo-ui-flaggedElement-';
+
+       if ( typeof flags === 'string' ) {
+               className = classPrefix + flags;
+               // Set
+               if ( !this.flags[ flags ] ) {
+                       this.flags[ flags ] = true;
+                       add.push( className );
+               }
+       } else if ( Array.isArray( flags ) ) {
+               for ( i = 0, len = flags.length; i < len; i++ ) {
+                       flag = flags[ i ];
+                       className = classPrefix + flag;
+                       // Set
+                       if ( !this.flags[ flag ] ) {
+                               changes[ flag ] = true;
+                               this.flags[ flag ] = true;
+                               add.push( className );
+                       }
+               }
+       } else if ( OO.isPlainObject( flags ) ) {
+               for ( flag in flags ) {
+                       className = classPrefix + flag;
+                       if ( flags[ flag ] ) {
+                               // Set
+                               if ( !this.flags[ flag ] ) {
+                                       changes[ flag ] = true;
+                                       this.flags[ flag ] = true;
+                                       add.push( className );
+                               }
+                       } else {
+                               // Remove
+                               if ( this.flags[ flag ] ) {
+                                       changes[ flag ] = false;
+                                       delete this.flags[ flag ];
+                                       remove.push( className );
+                               }
+                       }
+               }
+       }
+
+       if ( this.$flagged ) {
+               this.$flagged
+                       .addClass( add.join( ' ' ) )
+                       .removeClass( remove.join( ' ' ) );
+       }
+
+       this.updateThemeClasses();
+       this.emit( 'flag', changes );
+
+       return this;
+};
+
+/**
+ * TitledElement is mixed into other classes to provide a `title` attribute.
+ * Titles are rendered by the browser and are made visible when the user moves
+ * the mouse over the element. Titles are not visible on touch devices.
+ *
+ *     @example
+ *     // TitledElement provides a 'title' attribute to the
+ *     // ButtonWidget class
+ *     var button = new OO.ui.ButtonWidget( {
+ *         label: 'Button with Title',
+ *         title: 'I am a button'
+ *     } );
+ *     $( 'body' ).append( button.$element );
+ *
+ * @abstract
+ * @class
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {jQuery} [$titled] The element to which the `title` attribute is applied.
+ *  If this config is omitted, the title functionality is applied to $element, the
+ *  element created by the class.
+ * @cfg {string|Function} [title] The title text or a function that returns text. If
+ *  this config is omitted, the value of the {@link #static-title static title} property is used.
+ */
+OO.ui.mixin.TitledElement = function OoUiMixinTitledElement( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Properties
+       this.$titled = null;
+       this.title = null;
+
+       // Initialization
+       this.setTitle( config.title || this.constructor.static.title );
+       this.setTitledElement( config.$titled || this.$element );
+};
+
+/* Setup */
+
+OO.initClass( OO.ui.mixin.TitledElement );
+
+/* Static Properties */
+
+/**
+ * The title text, a function that returns text, or `null` for no title. The value of the static property
+ * is overridden if the #title config option is used.
+ *
+ * @static
+ * @inheritable
+ * @property {string|Function|null}
+ */
+OO.ui.mixin.TitledElement.static.title = null;
+
+/* Methods */
+
+/**
+ * Set the titled element.
+ *
+ * This method is used to retarget a titledElement mixin so that its functionality applies to the specified element.
+ * If an element is already set, the mixin’s effect on that element is removed before the new element is set up.
+ *
+ * @param {jQuery} $titled Element that should use the 'titled' functionality
+ */
+OO.ui.mixin.TitledElement.prototype.setTitledElement = function ( $titled ) {
+       if ( this.$titled ) {
+               this.$titled.removeAttr( 'title' );
+       }
+
+       this.$titled = $titled;
+       if ( this.title ) {
+               this.$titled.attr( 'title', this.title );
+       }
+};
+
+/**
+ * Set title.
+ *
+ * @param {string|Function|null} title Title text, a function that returns text, or `null` for no title
+ * @chainable
+ */
+OO.ui.mixin.TitledElement.prototype.setTitle = function ( title ) {
+       title = typeof title === 'function' ? OO.ui.resolveMsg( title ) : title;
+       title = ( typeof title === 'string' && title.length ) ? title : null;
+
+       if ( this.title !== title ) {
+               if ( this.$titled ) {
+                       if ( title !== null ) {
+                               this.$titled.attr( 'title', title );
+                       } else {
+                               this.$titled.removeAttr( 'title' );
+                       }
+               }
+               this.title = title;
+       }
+
+       return this;
+};
+
+/**
+ * Get title.
+ *
+ * @return {string} Title string
+ */
+OO.ui.mixin.TitledElement.prototype.getTitle = function () {
+       return this.title;
+};
+
+/**
+ * AccessKeyedElement is mixed into other classes to provide an `accesskey` attribute.
+ * Accesskeys allow an user to go to a specific element by using
+ * a shortcut combination of a browser specific keys + the key
+ * set to the field.
+ *
+ *     @example
+ *     // AccessKeyedElement provides an 'accesskey' attribute to the
+ *     // ButtonWidget class
+ *     var button = new OO.ui.ButtonWidget( {
+ *         label: 'Button with Accesskey',
+ *         accessKey: 'k'
+ *     } );
+ *     $( 'body' ).append( button.$element );
+ *
+ * @abstract
+ * @class
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {jQuery} [$accessKeyed] The element to which the `accesskey` attribute is applied.
+ *  If this config is omitted, the accesskey functionality is applied to $element, the
+ *  element created by the class.
+ * @cfg {string|Function} [accessKey] The key or a function that returns the key. If
+ *  this config is omitted, no accesskey will be added.
+ */
+OO.ui.mixin.AccessKeyedElement = function OoUiMixinAccessKeyedElement( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Properties
+       this.$accessKeyed = null;
+       this.accessKey = null;
+
+       // Initialization
+       this.setAccessKey( config.accessKey || null );
+       this.setAccessKeyedElement( config.$accessKeyed || this.$element );
+};
+
+/* Setup */
+
+OO.initClass( OO.ui.mixin.AccessKeyedElement );
+
+/* Static Properties */
+
+/**
+ * The access key, a function that returns a key, or `null` for no accesskey.
+ *
+ * @static
+ * @inheritable
+ * @property {string|Function|null}
+ */
+OO.ui.mixin.AccessKeyedElement.static.accessKey = null;
+
+/* Methods */
+
+/**
+ * Set the accesskeyed element.
+ *
+ * This method is used to retarget a AccessKeyedElement mixin so that its functionality applies to the specified element.
+ * If an element is already set, the mixin's effect on that element is removed before the new element is set up.
+ *
+ * @param {jQuery} $accessKeyed Element that should use the 'accesskeyes' functionality
+ */
+OO.ui.mixin.AccessKeyedElement.prototype.setAccessKeyedElement = function ( $accessKeyed ) {
+       if ( this.$accessKeyed ) {
+               this.$accessKeyed.removeAttr( 'accesskey' );
+       }
+
+       this.$accessKeyed = $accessKeyed;
+       if ( this.accessKey ) {
+               this.$accessKeyed.attr( 'accesskey', this.accessKey );
+       }
+};
+
+/**
+ * Set accesskey.
+ *
+ * @param {string|Function|null} accesskey Key, a function that returns a key, or `null` for no accesskey
+ * @chainable
+ */
+OO.ui.mixin.AccessKeyedElement.prototype.setAccessKey = function ( accessKey ) {
+       accessKey = typeof accessKey === 'string' ? OO.ui.resolveMsg( accessKey ) : null;
+
+       if ( this.accessKey !== accessKey ) {
+               if ( this.$accessKeyed ) {
+                       if ( accessKey !== null ) {
+                               this.$accessKeyed.attr( 'accesskey', accessKey );
+                       } else {
+                               this.$accessKeyed.removeAttr( 'accesskey' );
+                       }
+               }
+               this.accessKey = accessKey;
+       }
+
+       return this;
+};
+
+/**
+ * Get accesskey.
+ *
+ * @return {string} accessKey string
+ */
+OO.ui.mixin.AccessKeyedElement.prototype.getAccessKey = function () {
+       return this.accessKey;
+};
+
+/**
+ * ButtonWidget is a generic widget for buttons. A wide variety of looks,
+ * feels, and functionality can be customized via the class’s configuration options
+ * and methods. Please see the [OOjs UI documentation on MediaWiki] [1] for more information
+ * and examples.
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Buttons_and_Switches
+ *
+ *     @example
+ *     // A button widget
+ *     var button = new OO.ui.ButtonWidget( {
+ *         label: 'Button with Icon',
+ *         icon: 'remove',
+ *         iconTitle: 'Remove'
+ *     } );
+ *     $( 'body' ).append( button.$element );
+ *
+ * NOTE: HTML form buttons should use the OO.ui.ButtonInputWidget class.
+ *
+ * @class
+ * @extends OO.ui.Widget
+ * @mixins OO.ui.mixin.ButtonElement
+ * @mixins OO.ui.mixin.IconElement
+ * @mixins OO.ui.mixin.IndicatorElement
+ * @mixins OO.ui.mixin.LabelElement
+ * @mixins OO.ui.mixin.TitledElement
+ * @mixins OO.ui.mixin.FlaggedElement
+ * @mixins OO.ui.mixin.TabIndexedElement
+ * @mixins OO.ui.mixin.AccessKeyedElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {string} [href] Hyperlink to visit when the button is clicked.
+ * @cfg {string} [target] The frame or window in which to open the hyperlink.
+ * @cfg {boolean} [noFollow] Search engine traversal hint (default: true)
+ */
+OO.ui.ButtonWidget = function OoUiButtonWidget( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Parent constructor
+       OO.ui.ButtonWidget.parent.call( this, config );
+
+       // Mixin constructors
+       OO.ui.mixin.ButtonElement.call( this, config );
+       OO.ui.mixin.IconElement.call( this, config );
+       OO.ui.mixin.IndicatorElement.call( this, config );
+       OO.ui.mixin.LabelElement.call( this, config );
+       OO.ui.mixin.TitledElement.call( this, $.extend( {}, config, { $titled: this.$button } ) );
+       OO.ui.mixin.FlaggedElement.call( this, config );
+       OO.ui.mixin.TabIndexedElement.call( this, $.extend( {}, config, { $tabIndexed: this.$button } ) );
+       OO.ui.mixin.AccessKeyedElement.call( this, $.extend( {}, config, { $accessKeyed: this.$button } ) );
+
+       // Properties
+       this.href = null;
+       this.target = null;
+       this.noFollow = false;
+
+       // Events
+       this.connect( this, { disable: 'onDisable' } );
+
+       // Initialization
+       this.$button.append( this.$icon, this.$label, this.$indicator );
+       this.$element
+               .addClass( 'oo-ui-buttonWidget' )
+               .append( this.$button );
+       this.setHref( config.href );
+       this.setTarget( config.target );
+       this.setNoFollow( config.noFollow );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.ButtonWidget, OO.ui.Widget );
+OO.mixinClass( OO.ui.ButtonWidget, OO.ui.mixin.ButtonElement );
+OO.mixinClass( OO.ui.ButtonWidget, OO.ui.mixin.IconElement );
+OO.mixinClass( OO.ui.ButtonWidget, OO.ui.mixin.IndicatorElement );
+OO.mixinClass( OO.ui.ButtonWidget, OO.ui.mixin.LabelElement );
+OO.mixinClass( OO.ui.ButtonWidget, OO.ui.mixin.TitledElement );
+OO.mixinClass( OO.ui.ButtonWidget, OO.ui.mixin.FlaggedElement );
+OO.mixinClass( OO.ui.ButtonWidget, OO.ui.mixin.TabIndexedElement );
+OO.mixinClass( OO.ui.ButtonWidget, OO.ui.mixin.AccessKeyedElement );
+
+/* Methods */
+
+/**
+ * @inheritdoc
+ */
+OO.ui.ButtonWidget.prototype.onMouseDown = function ( e ) {
+       if ( !this.isDisabled() ) {
+               // Remove the tab-index while the button is down to prevent the button from stealing focus
+               this.$button.removeAttr( 'tabindex' );
+       }
+
+       return OO.ui.mixin.ButtonElement.prototype.onMouseDown.call( this, e );
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.ButtonWidget.prototype.onMouseUp = function ( e ) {
+       if ( !this.isDisabled() ) {
+               // Restore the tab-index after the button is up to restore the button's accessibility
+               this.$button.attr( 'tabindex', this.tabIndex );
+       }
+
+       return OO.ui.mixin.ButtonElement.prototype.onMouseUp.call( this, e );
+};
+
+/**
+ * Get hyperlink location.
+ *
+ * @return {string} Hyperlink location
+ */
+OO.ui.ButtonWidget.prototype.getHref = function () {
+       return this.href;
+};
+
+/**
+ * Get hyperlink target.
+ *
+ * @return {string} Hyperlink target
+ */
+OO.ui.ButtonWidget.prototype.getTarget = function () {
+       return this.target;
+};
+
+/**
+ * Get search engine traversal hint.
+ *
+ * @return {boolean} Whether search engines should avoid traversing this hyperlink
+ */
+OO.ui.ButtonWidget.prototype.getNoFollow = function () {
+       return this.noFollow;
+};
+
+/**
+ * Set hyperlink location.
+ *
+ * @param {string|null} href Hyperlink location, null to remove
+ */
+OO.ui.ButtonWidget.prototype.setHref = function ( href ) {
+       href = typeof href === 'string' ? href : null;
+       if ( href !== null && !OO.ui.isSafeUrl( href ) ) {
+               href = './' + href;
+       }
+
+       if ( href !== this.href ) {
+               this.href = href;
+               this.updateHref();
+       }
+
+       return this;
+};
+
+/**
+ * Update the `href` attribute, in case of changes to href or
+ * disabled state.
+ *
+ * @private
+ * @chainable
+ */
+OO.ui.ButtonWidget.prototype.updateHref = function () {
+       if ( this.href !== null && !this.isDisabled() ) {
+               this.$button.attr( 'href', this.href );
+       } else {
+               this.$button.removeAttr( 'href' );
+       }
+
+       return this;
+};
+
+/**
+ * Handle disable events.
+ *
+ * @private
+ * @param {boolean} disabled Element is disabled
+ */
+OO.ui.ButtonWidget.prototype.onDisable = function () {
+       this.updateHref();
+};
+
+/**
+ * Set hyperlink target.
+ *
+ * @param {string|null} target Hyperlink target, null to remove
+ */
+OO.ui.ButtonWidget.prototype.setTarget = function ( target ) {
+       target = typeof target === 'string' ? target : null;
+
+       if ( target !== this.target ) {
+               this.target = target;
+               if ( target !== null ) {
+                       this.$button.attr( 'target', target );
+               } else {
+                       this.$button.removeAttr( 'target' );
+               }
+       }
+
+       return this;
+};
+
+/**
+ * Set search engine traversal hint.
+ *
+ * @param {boolean} noFollow True if search engines should avoid traversing this hyperlink
+ */
+OO.ui.ButtonWidget.prototype.setNoFollow = function ( noFollow ) {
+       noFollow = typeof noFollow === 'boolean' ? noFollow : true;
+
+       if ( noFollow !== this.noFollow ) {
+               this.noFollow = noFollow;
+               if ( noFollow ) {
+                       this.$button.attr( 'rel', 'nofollow' );
+               } else {
+                       this.$button.removeAttr( 'rel' );
+               }
+       }
+
+       return this;
+};
+
+/**
+ * A ButtonGroupWidget groups related buttons and is used together with OO.ui.ButtonWidget and
+ * its subclasses. Each button in a group is addressed by a unique reference. Buttons can be added,
+ * removed, and cleared from the group.
+ *
+ *     @example
+ *     // Example: A ButtonGroupWidget with two buttons
+ *     var button1 = new OO.ui.PopupButtonWidget( {
+ *         label: 'Select a category',
+ *         icon: 'menu',
+ *         popup: {
+ *             $content: $( '<p>List of categories...</p>' ),
+ *             padded: true,
+ *             align: 'left'
+ *         }
+ *     } );
+ *     var button2 = new OO.ui.ButtonWidget( {
+ *         label: 'Add item'
+ *     });
+ *     var buttonGroup = new OO.ui.ButtonGroupWidget( {
+ *         items: [button1, button2]
+ *     } );
+ *     $( 'body' ).append( buttonGroup.$element );
+ *
+ * @class
+ * @extends OO.ui.Widget
+ * @mixins OO.ui.mixin.GroupElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {OO.ui.ButtonWidget[]} [items] Buttons to add
+ */
+OO.ui.ButtonGroupWidget = function OoUiButtonGroupWidget( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Parent constructor
+       OO.ui.ButtonGroupWidget.parent.call( this, config );
+
+       // Mixin constructors
+       OO.ui.mixin.GroupElement.call( this, $.extend( {}, config, { $group: this.$element } ) );
+
+       // Initialization
+       this.$element.addClass( 'oo-ui-buttonGroupWidget' );
+       if ( Array.isArray( config.items ) ) {
+               this.addItems( config.items );
+       }
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.ButtonGroupWidget, OO.ui.Widget );
+OO.mixinClass( OO.ui.ButtonGroupWidget, OO.ui.mixin.GroupElement );
+
+/**
+ * IconWidget is a generic widget for {@link OO.ui.mixin.IconElement icons}. In general, IconWidgets should be used with OO.ui.LabelWidget,
+ * which creates a label that identifies the icon’s function. See the [OOjs UI documentation on MediaWiki] [1]
+ * for a list of icons included in the library.
+ *
+ *     @example
+ *     // An icon widget with a label
+ *     var myIcon = new OO.ui.IconWidget( {
+ *         icon: 'help',
+ *         iconTitle: 'Help'
+ *      } );
+ *      // Create a label.
+ *      var iconLabel = new OO.ui.LabelWidget( {
+ *          label: 'Help'
+ *      } );
+ *      $( 'body' ).append( myIcon.$element, iconLabel.$element );
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Icons,_Indicators,_and_Labels#Icons
+ *
+ * @class
+ * @extends OO.ui.Widget
+ * @mixins OO.ui.mixin.IconElement
+ * @mixins OO.ui.mixin.TitledElement
+ * @mixins OO.ui.mixin.FlaggedElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.IconWidget = function OoUiIconWidget( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Parent constructor
+       OO.ui.IconWidget.parent.call( this, config );
+
+       // Mixin constructors
+       OO.ui.mixin.IconElement.call( this, $.extend( {}, config, { $icon: this.$element } ) );
+       OO.ui.mixin.TitledElement.call( this, $.extend( {}, config, { $titled: this.$element } ) );
+       OO.ui.mixin.FlaggedElement.call( this, $.extend( {}, config, { $flagged: this.$element } ) );
+
+       // Initialization
+       this.$element.addClass( 'oo-ui-iconWidget' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.IconWidget, OO.ui.Widget );
+OO.mixinClass( OO.ui.IconWidget, OO.ui.mixin.IconElement );
+OO.mixinClass( OO.ui.IconWidget, OO.ui.mixin.TitledElement );
+OO.mixinClass( OO.ui.IconWidget, OO.ui.mixin.FlaggedElement );
+
+/* Static Properties */
+
+OO.ui.IconWidget.static.tagName = 'span';
+
+/**
+ * IndicatorWidgets create indicators, which are small graphics that are generally used to draw
+ * attention to the status of an item or to clarify the function of a control. For a list of
+ * indicators included in the library, please see the [OOjs UI documentation on MediaWiki][1].
+ *
+ *     @example
+ *     // Example of an indicator widget
+ *     var indicator1 = new OO.ui.IndicatorWidget( {
+ *         indicator: 'alert'
+ *     } );
+ *
+ *     // Create a fieldset layout to add a label
+ *     var fieldset = new OO.ui.FieldsetLayout();
+ *     fieldset.addItems( [
+ *         new OO.ui.FieldLayout( indicator1, { label: 'An alert indicator:' } )
+ *     ] );
+ *     $( 'body' ).append( fieldset.$element );
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Icons,_Indicators,_and_Labels#Indicators
+ *
+ * @class
+ * @extends OO.ui.Widget
+ * @mixins OO.ui.mixin.IndicatorElement
+ * @mixins OO.ui.mixin.TitledElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.IndicatorWidget = function OoUiIndicatorWidget( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Parent constructor
+       OO.ui.IndicatorWidget.parent.call( this, config );
+
+       // Mixin constructors
+       OO.ui.mixin.IndicatorElement.call( this, $.extend( {}, config, { $indicator: this.$element } ) );
+       OO.ui.mixin.TitledElement.call( this, $.extend( {}, config, { $titled: this.$element } ) );
+
+       // Initialization
+       this.$element.addClass( 'oo-ui-indicatorWidget' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.IndicatorWidget, OO.ui.Widget );
+OO.mixinClass( OO.ui.IndicatorWidget, OO.ui.mixin.IndicatorElement );
+OO.mixinClass( OO.ui.IndicatorWidget, OO.ui.mixin.TitledElement );
+
+/* Static Properties */
+
+OO.ui.IndicatorWidget.static.tagName = 'span';
+
+/**
+ * LabelWidgets help identify the function of interface elements. Each LabelWidget can
+ * be configured with a `label` option that is set to a string, a label node, or a function:
+ *
+ * - String: a plaintext string
+ * - jQuery selection: a jQuery selection, used for anything other than a plaintext label, e.g., a
+ *   label that includes a link or special styling, such as a gray color or additional graphical elements.
+ * - Function: a function that will produce a string in the future. Functions are used
+ *   in cases where the value of the label is not currently defined.
+ *
+ * In addition, the LabelWidget can be associated with an {@link OO.ui.InputWidget input widget}, which
+ * will come into focus when the label is clicked.
+ *
+ *     @example
+ *     // Examples of LabelWidgets
+ *     var label1 = new OO.ui.LabelWidget( {
+ *         label: 'plaintext label'
+ *     } );
+ *     var label2 = new OO.ui.LabelWidget( {
+ *         label: $( '<a href="default.html">jQuery label</a>' )
+ *     } );
+ *     // Create a fieldset layout with fields for each example
+ *     var fieldset = new OO.ui.FieldsetLayout();
+ *     fieldset.addItems( [
+ *         new OO.ui.FieldLayout( label1 ),
+ *         new OO.ui.FieldLayout( label2 )
+ *     ] );
+ *     $( 'body' ).append( fieldset.$element );
+ *
+ * @class
+ * @extends OO.ui.Widget
+ * @mixins OO.ui.mixin.LabelElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {OO.ui.InputWidget} [input] {@link OO.ui.InputWidget Input widget} that uses the label.
+ *  Clicking the label will focus the specified input field.
+ */
+OO.ui.LabelWidget = function OoUiLabelWidget( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Parent constructor
+       OO.ui.LabelWidget.parent.call( this, config );
+
+       // Mixin constructors
+       OO.ui.mixin.LabelElement.call( this, $.extend( {}, config, { $label: this.$element } ) );
+       OO.ui.mixin.TitledElement.call( this, config );
+
+       // Properties
+       this.input = config.input;
+
+       // Events
+       if ( this.input instanceof OO.ui.InputWidget ) {
+               this.$element.on( 'click', this.onClick.bind( this ) );
+       }
+
+       // Initialization
+       this.$element.addClass( 'oo-ui-labelWidget' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.LabelWidget, OO.ui.Widget );
+OO.mixinClass( OO.ui.LabelWidget, OO.ui.mixin.LabelElement );
+OO.mixinClass( OO.ui.LabelWidget, OO.ui.mixin.TitledElement );
+
+/* Static Properties */
+
+OO.ui.LabelWidget.static.tagName = 'span';
+
+/* Methods */
+
+/**
+ * Handles label mouse click events.
+ *
+ * @private
+ * @param {jQuery.Event} e Mouse click event
+ */
+OO.ui.LabelWidget.prototype.onClick = function () {
+       this.input.simulateLabelClick();
+       return false;
+};
+
+/**
+ * PendingElement is a mixin that is used to create elements that notify users that something is happening
+ * and that they should wait before proceeding. The pending state is visually represented with a pending
+ * texture that appears in the head of a pending {@link OO.ui.ProcessDialog process dialog} or in the input
+ * field of a {@link OO.ui.TextInputWidget text input widget}.
+ *
+ * Currently, {@link OO.ui.ActionWidget Action widgets}, which mix in this class, can also be marked as pending, but only when
+ * used in {@link OO.ui.MessageDialog message dialogs}. The behavior is not currently supported for action widgets used
+ * in process dialogs.
+ *
+ *     @example
+ *     function MessageDialog( config ) {
+ *         MessageDialog.parent.call( this, config );
+ *     }
+ *     OO.inheritClass( MessageDialog, OO.ui.MessageDialog );
+ *
+ *     MessageDialog.static.actions = [
+ *         { action: 'save', label: 'Done', flags: 'primary' },
+ *         { label: 'Cancel', flags: 'safe' }
+ *     ];
+ *
+ *     MessageDialog.prototype.initialize = function () {
+ *         MessageDialog.parent.prototype.initialize.apply( this, arguments );
+ *         this.content = new OO.ui.PanelLayout( { $: this.$, padded: true } );
+ *         this.content.$element.append( '<p>Click the \'Done\' action widget to see its pending state. Note that action widgets can be marked pending in message dialogs but not process dialogs.</p>' );
+ *         this.$body.append( this.content.$element );
+ *     };
+ *     MessageDialog.prototype.getBodyHeight = function () {
+ *         return 100;
+ *     }
+ *     MessageDialog.prototype.getActionProcess = function ( action ) {
+ *         var dialog = this;
+ *         if ( action === 'save' ) {
+ *             dialog.getActions().get({actions: 'save'})[0].pushPending();
+ *             return new OO.ui.Process()
+ *             .next( 1000 )
+ *             .next( function () {
+ *                 dialog.getActions().get({actions: 'save'})[0].popPending();
+ *             } );
+ *         }
+ *         return MessageDialog.parent.prototype.getActionProcess.call( this, action );
+ *     };
+ *
+ *     var windowManager = new OO.ui.WindowManager();
+ *     $( 'body' ).append( windowManager.$element );
+ *
+ *     var dialog = new MessageDialog();
+ *     windowManager.addWindows( [ dialog ] );
+ *     windowManager.openWindow( dialog );
+ *
+ * @abstract
+ * @class
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {jQuery} [$pending] Element to mark as pending, defaults to this.$element
+ */
+OO.ui.mixin.PendingElement = function OoUiMixinPendingElement( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Properties
+       this.pending = 0;
+       this.$pending = null;
+
+       // Initialisation
+       this.setPendingElement( config.$pending || this.$element );
+};
+
+/* Setup */
+
+OO.initClass( OO.ui.mixin.PendingElement );
+
+/* Methods */
+
+/**
+ * Set the pending element (and clean up any existing one).
+ *
+ * @param {jQuery} $pending The element to set to pending.
+ */
+OO.ui.mixin.PendingElement.prototype.setPendingElement = function ( $pending ) {
+       if ( this.$pending ) {
+               this.$pending.removeClass( 'oo-ui-pendingElement-pending' );
+       }
+
+       this.$pending = $pending;
+       if ( this.pending > 0 ) {
+               this.$pending.addClass( 'oo-ui-pendingElement-pending' );
+       }
+};
+
+/**
+ * Check if an element is pending.
+ *
+ * @return {boolean} Element is pending
+ */
+OO.ui.mixin.PendingElement.prototype.isPending = function () {
+       return !!this.pending;
+};
+
+/**
+ * Increase the pending counter. The pending state will remain active until the counter is zero
+ * (i.e., the number of calls to #pushPending and #popPending is the same).
+ *
+ * @chainable
+ */
+OO.ui.mixin.PendingElement.prototype.pushPending = function () {
+       if ( this.pending === 0 ) {
+               this.$pending.addClass( 'oo-ui-pendingElement-pending' );
+               this.updateThemeClasses();
+       }
+       this.pending++;
+
+       return this;
+};
+
+/**
+ * Decrease the pending counter. The pending state will remain active until the counter is zero
+ * (i.e., the number of calls to #pushPending and #popPending is the same).
+ *
+ * @chainable
+ */
+OO.ui.mixin.PendingElement.prototype.popPending = function () {
+       if ( this.pending === 1 ) {
+               this.$pending.removeClass( 'oo-ui-pendingElement-pending' );
+               this.updateThemeClasses();
+       }
+       this.pending = Math.max( 0, this.pending - 1 );
+
+       return this;
+};
+
+/**
+ * Element that can be automatically clipped to visible boundaries.
+ *
+ * Whenever the element's natural height changes, you have to call
+ * {@link OO.ui.mixin.ClippableElement#clip} to make sure it's still
+ * clipping correctly.
+ *
+ * The dimensions of #$clippableContainer will be compared to the boundaries of the
+ * nearest scrollable container. If #$clippableContainer is too tall and/or too wide,
+ * then #$clippable will be given a fixed reduced height and/or width and will be made
+ * scrollable. By default, #$clippable and #$clippableContainer are the same element,
+ * but you can build a static footer by setting #$clippableContainer to an element that contains
+ * #$clippable and the footer.
+ *
+ * @abstract
+ * @class
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {jQuery} [$clippable] Node to clip, assigned to #$clippable, omit to use #$element
+ * @cfg {jQuery} [$clippableContainer] Node to keep visible, assigned to #$clippableContainer,
+ *   omit to use #$clippable
+ */
+OO.ui.mixin.ClippableElement = function OoUiMixinClippableElement( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Properties
+       this.$clippable = null;
+       this.$clippableContainer = null;
+       this.clipping = false;
+       this.clippedHorizontally = false;
+       this.clippedVertically = false;
+       this.$clippableScrollableContainer = null;
+       this.$clippableScroller = null;
+       this.$clippableWindow = null;
+       this.idealWidth = null;
+       this.idealHeight = null;
+       this.onClippableScrollHandler = this.clip.bind( this );
+       this.onClippableWindowResizeHandler = this.clip.bind( this );
+
+       // Initialization
+       if ( config.$clippableContainer ) {
+               this.setClippableContainer( config.$clippableContainer );
+       }
+       this.setClippableElement( config.$clippable || this.$element );
+};
+
+/* Methods */
+
+/**
+ * Set clippable element.
+ *
+ * If an element is already set, it will be cleaned up before setting up the new element.
+ *
+ * @param {jQuery} $clippable Element to make clippable
+ */
+OO.ui.mixin.ClippableElement.prototype.setClippableElement = function ( $clippable ) {
+       if ( this.$clippable ) {
+               this.$clippable.removeClass( 'oo-ui-clippableElement-clippable' );
+               this.$clippable.css( { width: '', height: '', overflowX: '', overflowY: '' } );
+               OO.ui.Element.static.reconsiderScrollbars( this.$clippable[ 0 ] );
+       }
+
+       this.$clippable = $clippable.addClass( 'oo-ui-clippableElement-clippable' );
+       this.clip();
+};
+
+/**
+ * Set clippable container.
+ *
+ * This is the container that will be measured when deciding whether to clip. When clipping,
+ * #$clippable will be resized in order to keep the clippable container fully visible.
+ *
+ * If the clippable container is unset, #$clippable will be used.
+ *
+ * @param {jQuery|null} $clippableContainer Container to keep visible, or null to unset
+ */
+OO.ui.mixin.ClippableElement.prototype.setClippableContainer = function ( $clippableContainer ) {
+       this.$clippableContainer = $clippableContainer;
+       if ( this.$clippable ) {
+               this.clip();
+       }
+};
+
+/**
+ * Toggle clipping.
+ *
+ * Do not turn clipping on until after the element is attached to the DOM and visible.
+ *
+ * @param {boolean} [clipping] Enable clipping, omit to toggle
+ * @chainable
+ */
+OO.ui.mixin.ClippableElement.prototype.toggleClipping = function ( clipping ) {
+       clipping = clipping === undefined ? !this.clipping : !!clipping;
+
+       if ( this.clipping !== clipping ) {
+               this.clipping = clipping;
+               if ( clipping ) {
+                       this.$clippableScrollableContainer = $( this.getClosestScrollableElementContainer() );
+                       // If the clippable container is the root, we have to listen to scroll events and check
+                       // jQuery.scrollTop on the window because of browser inconsistencies
+                       this.$clippableScroller = this.$clippableScrollableContainer.is( 'html, body' ) ?
+                               $( OO.ui.Element.static.getWindow( this.$clippableScrollableContainer ) ) :
+                               this.$clippableScrollableContainer;
+                       this.$clippableScroller.on( 'scroll', this.onClippableScrollHandler );
+                       this.$clippableWindow = $( this.getElementWindow() )
+                               .on( 'resize', this.onClippableWindowResizeHandler );
+                       // Initial clip after visible
+                       this.clip();
+               } else {
+                       this.$clippable.css( { width: '', height: '', overflowX: '', overflowY: '' } );
+                       OO.ui.Element.static.reconsiderScrollbars( this.$clippable[ 0 ] );
+
+                       this.$clippableScrollableContainer = null;
+                       this.$clippableScroller.off( 'scroll', this.onClippableScrollHandler );
+                       this.$clippableScroller = null;
+                       this.$clippableWindow.off( 'resize', this.onClippableWindowResizeHandler );
+                       this.$clippableWindow = null;
+               }
+       }
+
+       return this;
+};
+
+/**
+ * Check if the element will be clipped to fit the visible area of the nearest scrollable container.
+ *
+ * @return {boolean} Element will be clipped to the visible area
+ */
+OO.ui.mixin.ClippableElement.prototype.isClipping = function () {
+       return this.clipping;
+};
+
+/**
+ * Check if the bottom or right of the element is being clipped by the nearest scrollable container.
+ *
+ * @return {boolean} Part of the element is being clipped
+ */
+OO.ui.mixin.ClippableElement.prototype.isClipped = function () {
+       return this.clippedHorizontally || this.clippedVertically;
+};
+
+/**
+ * Check if the right of the element is being clipped by the nearest scrollable container.
+ *
+ * @return {boolean} Part of the element is being clipped
+ */
+OO.ui.mixin.ClippableElement.prototype.isClippedHorizontally = function () {
+       return this.clippedHorizontally;
+};
+
+/**
+ * Check if the bottom of the element is being clipped by the nearest scrollable container.
+ *
+ * @return {boolean} Part of the element is being clipped
+ */
+OO.ui.mixin.ClippableElement.prototype.isClippedVertically = function () {
+       return this.clippedVertically;
+};
+
+/**
+ * Set the ideal size. These are the dimensions the element will have when it's not being clipped.
+ *
+ * @param {number|string} [width] Width as a number of pixels or CSS string with unit suffix
+ * @param {number|string} [height] Height as a number of pixels or CSS string with unit suffix
+ */
+OO.ui.mixin.ClippableElement.prototype.setIdealSize = function ( width, height ) {
+       this.idealWidth = width;
+       this.idealHeight = height;
+
+       if ( !this.clipping ) {
+               // Update dimensions
+               this.$clippable.css( { width: width, height: height } );
+       }
+       // While clipping, idealWidth and idealHeight are not considered
+};
+
+/**
+ * Clip element to visible boundaries and allow scrolling when needed. Call this method when
+ * the element's natural height changes.
+ *
+ * Element will be clipped the bottom or right of the element is within 10px of the edge of, or
+ * overlapped by, the visible area of the nearest scrollable container.
+ *
+ * @chainable
+ */
+OO.ui.mixin.ClippableElement.prototype.clip = function () {
+       var $container, extraHeight, extraWidth, ccOffset,
+               $scrollableContainer, scOffset, scHeight, scWidth,
+               ccWidth, scrollerIsWindow, scrollTop, scrollLeft,
+               desiredWidth, desiredHeight, allotedWidth, allotedHeight,
+               naturalWidth, naturalHeight, clipWidth, clipHeight,
+               buffer = 7; // Chosen by fair dice roll
+
+       if ( !this.clipping ) {
+               // this.$clippableScrollableContainer and this.$clippableWindow are null, so the below will fail
+               return this;
+       }
+
+       $container = this.$clippableContainer || this.$clippable;
+       extraHeight = $container.outerHeight() - this.$clippable.outerHeight();
+       extraWidth = $container.outerWidth() - this.$clippable.outerWidth();
+       ccOffset = $container.offset();
+       $scrollableContainer = this.$clippableScrollableContainer.is( 'html, body' ) ?
+               this.$clippableWindow : this.$clippableScrollableContainer;
+       scOffset = $scrollableContainer.offset() || { top: 0, left: 0 };
+       scHeight = $scrollableContainer.innerHeight() - buffer;
+       scWidth = $scrollableContainer.innerWidth() - buffer;
+       ccWidth = $container.outerWidth() + buffer;
+       scrollerIsWindow = this.$clippableScroller[ 0 ] === this.$clippableWindow[ 0 ];
+       scrollTop = scrollerIsWindow ? this.$clippableScroller.scrollTop() : 0;
+       scrollLeft = scrollerIsWindow ? this.$clippableScroller.scrollLeft() : 0;
+       desiredWidth = ccOffset.left < 0 ?
+               ccWidth + ccOffset.left :
+               ( scOffset.left + scrollLeft + scWidth ) - ccOffset.left;
+       desiredHeight = ( scOffset.top + scrollTop + scHeight ) - ccOffset.top;
+       allotedWidth = Math.ceil( desiredWidth - extraWidth );
+       allotedHeight = Math.ceil( desiredHeight - extraHeight );
+       naturalWidth = this.$clippable.prop( 'scrollWidth' );
+       naturalHeight = this.$clippable.prop( 'scrollHeight' );
+       clipWidth = allotedWidth < naturalWidth;
+       clipHeight = allotedHeight < naturalHeight;
+
+       if ( clipWidth ) {
+               this.$clippable.css( { overflowX: 'scroll', width: Math.max( 0, allotedWidth ) } );
+       } else {
+               this.$clippable.css( { width: this.idealWidth ? this.idealWidth - extraWidth : '', overflowX: '' } );
+       }
+       if ( clipHeight ) {
+               this.$clippable.css( { overflowY: 'scroll', height: Math.max( 0, allotedHeight ) } );
+       } else {
+               this.$clippable.css( { height: this.idealHeight ? this.idealHeight - extraHeight : '', overflowY: '' } );
+       }
+
+       // If we stopped clipping in at least one of the dimensions
+       if ( ( this.clippedHorizontally && !clipWidth ) || ( this.clippedVertically && !clipHeight ) ) {
+               OO.ui.Element.static.reconsiderScrollbars( this.$clippable[ 0 ] );
+       }
+
+       this.clippedHorizontally = clipWidth;
+       this.clippedVertically = clipHeight;
+
+       return this;
+};
+
+/**
+ * PopupWidget is a container for content. The popup is overlaid and positioned absolutely.
+ * By default, each popup has an anchor that points toward its origin.
+ * Please see the [OOjs UI documentation on Mediawiki] [1] for more information and examples.
+ *
+ *     @example
+ *     // A popup widget.
+ *     var popup = new OO.ui.PopupWidget( {
+ *         $content: $( '<p>Hi there!</p>' ),
+ *         padded: true,
+ *         width: 300
+ *     } );
+ *
+ *     $( 'body' ).append( popup.$element );
+ *     // To display the popup, toggle the visibility to 'true'.
+ *     popup.toggle( true );
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Popups
+ *
+ * @class
+ * @extends OO.ui.Widget
+ * @mixins OO.ui.mixin.LabelElement
+ * @mixins OO.ui.mixin.ClippableElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {number} [width=320] Width of popup in pixels
+ * @cfg {number} [height] Height of popup in pixels. Omit to use the automatic height.
+ * @cfg {boolean} [anchor=true] Show anchor pointing to origin of popup
+ * @cfg {string} [align='center'] Alignment of the popup: `center`, `force-left`, `force-right`, `backwards` or `forwards`.
+ *  If the popup is forced-left the popup body is leaning towards the left. For force-right alignment, the body of the
+ *  popup is leaning towards the right of the screen.
+ *  Using 'backwards' is a logical direction which will result in the popup leaning towards the beginning of the sentence
+ *  in the given language, which means it will flip to the correct positioning in right-to-left languages.
+ *  Using 'forward' will also result in a logical alignment where the body of the popup leans towards the end of the
+ *  sentence in the given language.
+ * @cfg {jQuery} [$container] Constrain the popup to the boundaries of the specified container.
+ *  See the [OOjs UI docs on MediaWiki][3] for an example.
+ *  [3]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Popups#containerExample
+ * @cfg {number} [containerPadding=10] Padding between the popup and its container, specified as a number of pixels.
+ * @cfg {jQuery} [$content] Content to append to the popup's body
+ * @cfg {jQuery} [$footer] Content to append to the popup's footer
+ * @cfg {boolean} [autoClose=false] Automatically close the popup when it loses focus.
+ * @cfg {jQuery} [$autoCloseIgnore] Elements that will not close the popup when clicked.
+ *  This config option is only relevant if #autoClose is set to `true`. See the [OOjs UI docs on MediaWiki][2]
+ *  for an example.
+ *  [2]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Popups#autocloseExample
+ * @cfg {boolean} [head] Show a popup header that contains a #label (if specified) and close
+ *  button.
+ * @cfg {boolean} [padded] Add padding to the popup's body
+ */
+OO.ui.PopupWidget = function OoUiPopupWidget( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Parent constructor
+       OO.ui.PopupWidget.parent.call( this, config );
+
+       // Properties (must be set before ClippableElement constructor call)
+       this.$body = $( '<div>' );
+       this.$popup = $( '<div>' );
+
+       // Mixin constructors
+       OO.ui.mixin.LabelElement.call( this, config );
+       OO.ui.mixin.ClippableElement.call( this, $.extend( {}, config, {
+               $clippable: this.$body,
+               $clippableContainer: this.$popup
+       } ) );
+
+       // Properties
+       this.$head = $( '<div>' );
+       this.$footer = $( '<div>' );
+       this.$anchor = $( '<div>' );
+       // If undefined, will be computed lazily in updateDimensions()
+       this.$container = config.$container;
+       this.containerPadding = config.containerPadding !== undefined ? config.containerPadding : 10;
+       this.autoClose = !!config.autoClose;
+       this.$autoCloseIgnore = config.$autoCloseIgnore;
+       this.transitionTimeout = null;
+       this.anchor = null;
+       this.width = config.width !== undefined ? config.width : 320;
+       this.height = config.height !== undefined ? config.height : null;
+       this.setAlignment( config.align );
+       this.closeButton = new OO.ui.ButtonWidget( { framed: false, icon: 'close' } );
+       this.onMouseDownHandler = this.onMouseDown.bind( this );
+       this.onDocumentKeyDownHandler = this.onDocumentKeyDown.bind( this );
+
+       // Events
+       this.closeButton.connect( this, { click: 'onCloseButtonClick' } );
+
+       // Initialization
+       this.toggleAnchor( config.anchor === undefined || config.anchor );
+       this.$body.addClass( 'oo-ui-popupWidget-body' );
+       this.$anchor.addClass( 'oo-ui-popupWidget-anchor' );
+       this.$head
+               .addClass( 'oo-ui-popupWidget-head' )
+               .append( this.$label, this.closeButton.$element );
+       this.$footer.addClass( 'oo-ui-popupWidget-footer' );
+       if ( !config.head ) {
+               this.$head.addClass( 'oo-ui-element-hidden' );
+       }
+       if ( !config.$footer ) {
+               this.$footer.addClass( 'oo-ui-element-hidden' );
+       }
+       this.$popup
+               .addClass( 'oo-ui-popupWidget-popup' )
+               .append( this.$head, this.$body, this.$footer );
+       this.$element
+               .addClass( 'oo-ui-popupWidget' )
+               .append( this.$popup, this.$anchor );
+       // Move content, which was added to #$element by OO.ui.Widget, to the body
+       if ( config.$content instanceof jQuery ) {
+               this.$body.append( config.$content );
+       }
+       if ( config.$footer instanceof jQuery ) {
+               this.$footer.append( config.$footer );
+       }
+       if ( config.padded ) {
+               this.$body.addClass( 'oo-ui-popupWidget-body-padded' );
+       }
+
+       // Initially hidden - using #toggle may cause errors if subclasses override toggle with methods
+       // that reference properties not initialized at that time of parent class construction
+       // TODO: Find a better way to handle post-constructor setup
+       this.visible = false;
+       this.$element.addClass( 'oo-ui-element-hidden' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.PopupWidget, OO.ui.Widget );
+OO.mixinClass( OO.ui.PopupWidget, OO.ui.mixin.LabelElement );
+OO.mixinClass( OO.ui.PopupWidget, OO.ui.mixin.ClippableElement );
+
+/* Methods */
+
+/**
+ * Handles mouse down events.
+ *
+ * @private
+ * @param {MouseEvent} e Mouse down event
+ */
+OO.ui.PopupWidget.prototype.onMouseDown = function ( e ) {
+       if (
+               this.isVisible() &&
+               !$.contains( this.$element[ 0 ], e.target ) &&
+               ( !this.$autoCloseIgnore || !this.$autoCloseIgnore.has( e.target ).length )
+       ) {
+               this.toggle( false );
+       }
+};
+
+/**
+ * Bind mouse down listener.
+ *
+ * @private
+ */
+OO.ui.PopupWidget.prototype.bindMouseDownListener = function () {
+       // Capture clicks outside popup
+       this.getElementWindow().addEventListener( 'mousedown', this.onMouseDownHandler, true );
+};
+
+/**
+ * Handles close button click events.
+ *
+ * @private
+ */
+OO.ui.PopupWidget.prototype.onCloseButtonClick = function () {
+       if ( this.isVisible() ) {
+               this.toggle( false );
+       }
+};
+
+/**
+ * Unbind mouse down listener.
+ *
+ * @private
+ */
+OO.ui.PopupWidget.prototype.unbindMouseDownListener = function () {
+       this.getElementWindow().removeEventListener( 'mousedown', this.onMouseDownHandler, true );
+};
+
+/**
+ * Handles key down events.
+ *
+ * @private
+ * @param {KeyboardEvent} e Key down event
+ */
+OO.ui.PopupWidget.prototype.onDocumentKeyDown = function ( e ) {
+       if (
+               e.which === OO.ui.Keys.ESCAPE &&
+               this.isVisible()
+       ) {
+               this.toggle( false );
+               e.preventDefault();
+               e.stopPropagation();
+       }
+};
+
+/**
+ * Bind key down listener.
+ *
+ * @private
+ */
+OO.ui.PopupWidget.prototype.bindKeyDownListener = function () {
+       this.getElementWindow().addEventListener( 'keydown', this.onDocumentKeyDownHandler, true );
+};
+
+/**
+ * Unbind key down listener.
+ *
+ * @private
+ */
+OO.ui.PopupWidget.prototype.unbindKeyDownListener = function () {
+       this.getElementWindow().removeEventListener( 'keydown', this.onDocumentKeyDownHandler, true );
+};
+
+/**
+ * Show, hide, or toggle the visibility of the anchor.
+ *
+ * @param {boolean} [show] Show anchor, omit to toggle
+ */
+OO.ui.PopupWidget.prototype.toggleAnchor = function ( show ) {
+       show = show === undefined ? !this.anchored : !!show;
+
+       if ( this.anchored !== show ) {
+               if ( show ) {
+                       this.$element.addClass( 'oo-ui-popupWidget-anchored' );
+               } else {
+                       this.$element.removeClass( 'oo-ui-popupWidget-anchored' );
+               }
+               this.anchored = show;
+       }
+};
+
+/**
+ * Check if the anchor is visible.
+ *
+ * @return {boolean} Anchor is visible
+ */
+OO.ui.PopupWidget.prototype.hasAnchor = function () {
+       return this.anchor;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.PopupWidget.prototype.toggle = function ( show ) {
+       var change;
+       show = show === undefined ? !this.isVisible() : !!show;
+
+       change = show !== this.isVisible();
+
+       // Parent method
+       OO.ui.PopupWidget.parent.prototype.toggle.call( this, show );
+
+       if ( change ) {
+               if ( show ) {
+                       if ( this.autoClose ) {
+                               this.bindMouseDownListener();
+                               this.bindKeyDownListener();
+                       }
+                       this.updateDimensions();
+                       this.toggleClipping( true );
+               } else {
+                       this.toggleClipping( false );
+                       if ( this.autoClose ) {
+                               this.unbindMouseDownListener();
+                               this.unbindKeyDownListener();
+                       }
+               }
+       }
+
+       return this;
+};
+
+/**
+ * Set the size of the popup.
+ *
+ * Changing the size may also change the popup's position depending on the alignment.
+ *
+ * @param {number} width Width in pixels
+ * @param {number} height Height in pixels
+ * @param {boolean} [transition=false] Use a smooth transition
+ * @chainable
+ */
+OO.ui.PopupWidget.prototype.setSize = function ( width, height, transition ) {
+       this.width = width;
+       this.height = height !== undefined ? height : null;
+       if ( this.isVisible() ) {
+               this.updateDimensions( transition );
+       }
+};
+
+/**
+ * Update the size and position.
+ *
+ * Only use this to keep the popup properly anchored. Use #setSize to change the size, and this will
+ * be called automatically.
+ *
+ * @param {boolean} [transition=false] Use a smooth transition
+ * @chainable
+ */
+OO.ui.PopupWidget.prototype.updateDimensions = function ( transition ) {
+       var popupOffset, originOffset, containerLeft, containerWidth, containerRight,
+               popupLeft, popupRight, overlapLeft, overlapRight, anchorWidth,
+               align = this.align,
+               widget = this;
+
+       if ( !this.$container ) {
+               // Lazy-initialize $container if not specified in constructor
+               this.$container = $( this.getClosestScrollableElementContainer() );
+       }
+
+       // Set height and width before measuring things, since it might cause our measurements
+       // to change (e.g. due to scrollbars appearing or disappearing)
+       this.$popup.css( {
+               width: this.width,
+               height: this.height !== null ? this.height : 'auto'
+       } );
+
+       // If we are in RTL, we need to flip the alignment, unless it is center
+       if ( align === 'forwards' || align === 'backwards' ) {
+               if ( this.$container.css( 'direction' ) === 'rtl' ) {
+                       align = ( { forwards: 'force-left', backwards: 'force-right' } )[ this.align ];
+               } else {
+                       align = ( { forwards: 'force-right', backwards: 'force-left' } )[ this.align ];
+               }
+
+       }
+
+       // Compute initial popupOffset based on alignment
+       popupOffset = this.width * ( { 'force-left': -1, center: -0.5, 'force-right': 0 } )[ align ];
+
+       // Figure out if this will cause the popup to go beyond the edge of the container
+       originOffset = this.$element.offset().left;
+       containerLeft = this.$container.offset().left;
+       containerWidth = this.$container.innerWidth();
+       containerRight = containerLeft + containerWidth;
+       popupLeft = popupOffset - this.containerPadding;
+       popupRight = popupOffset + this.containerPadding + this.width + this.containerPadding;
+       overlapLeft = ( originOffset + popupLeft ) - containerLeft;
+       overlapRight = containerRight - ( originOffset + popupRight );
+
+       // Adjust offset to make the popup not go beyond the edge, if needed
+       if ( overlapRight < 0 ) {
+               popupOffset += overlapRight;
+       } else if ( overlapLeft < 0 ) {
+               popupOffset -= overlapLeft;
+       }
+
+       // Adjust offset to avoid anchor being rendered too close to the edge
+       // $anchor.width() doesn't work with the pure CSS anchor (returns 0)
+       // TODO: Find a measurement that works for CSS anchors and image anchors
+       anchorWidth = this.$anchor[ 0 ].scrollWidth * 2;
+       if ( popupOffset + this.width < anchorWidth ) {
+               popupOffset = anchorWidth - this.width;
+       } else if ( -popupOffset < anchorWidth ) {
+               popupOffset = -anchorWidth;
+       }
+
+       // Prevent transition from being interrupted
+       clearTimeout( this.transitionTimeout );
+       if ( transition ) {
+               // Enable transition
+               this.$element.addClass( 'oo-ui-popupWidget-transitioning' );
+       }
+
+       // Position body relative to anchor
+       this.$popup.css( 'margin-left', popupOffset );
+
+       if ( transition ) {
+               // Prevent transitioning after transition is complete
+               this.transitionTimeout = setTimeout( function () {
+                       widget.$element.removeClass( 'oo-ui-popupWidget-transitioning' );
+               }, 200 );
+       } else {
+               // Prevent transitioning immediately
+               this.$element.removeClass( 'oo-ui-popupWidget-transitioning' );
+       }
+
+       // Reevaluate clipping state since we've relocated and resized the popup
+       this.clip();
+
+       return this;
+};
+
+/**
+ * Set popup alignment
+ * @param {string} align Alignment of the popup, `center`, `force-left`, `force-right`,
+ *  `backwards` or `forwards`.
+ */
+OO.ui.PopupWidget.prototype.setAlignment = function ( align ) {
+       // Validate alignment and transform deprecated values
+       if ( [ 'left', 'right', 'force-left', 'force-right', 'backwards', 'forwards', 'center' ].indexOf( align ) > -1 ) {
+               this.align = { left: 'force-right', right: 'force-left' }[ align ] || align;
+       } else {
+               this.align = 'center';
+       }
+};
+
+/**
+ * Get popup alignment
+ * @return {string} align Alignment of the popup, `center`, `force-left`, `force-right`,
+ *  `backwards` or `forwards`.
+ */
+OO.ui.PopupWidget.prototype.getAlignment = function () {
+       return this.align;
+};
+
+/**
+ * PopupElement is mixed into other classes to generate a {@link OO.ui.PopupWidget popup widget}.
+ * A popup is a container for content. It is overlaid and positioned absolutely. By default, each
+ * popup has an anchor, which is an arrow-like protrusion that points toward the popup’s origin.
+ * See {@link OO.ui.PopupWidget PopupWidget} for an example.
+ *
+ * @abstract
+ * @class
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {Object} [popup] Configuration to pass to popup
+ * @cfg {boolean} [popup.autoClose=true] Popup auto-closes when it loses focus
+ */
+OO.ui.mixin.PopupElement = function OoUiMixinPopupElement( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Properties
+       this.popup = new OO.ui.PopupWidget( $.extend(
+               { autoClose: true },
+               config.popup,
+               { $autoCloseIgnore: this.$element }
+       ) );
+};
+
+/* Methods */
+
+/**
+ * Get popup.
+ *
+ * @return {OO.ui.PopupWidget} Popup widget
+ */
+OO.ui.mixin.PopupElement.prototype.getPopup = function () {
+       return this.popup;
+};
+
+/**
+ * PopupButtonWidgets toggle the visibility of a contained {@link OO.ui.PopupWidget PopupWidget},
+ * which is used to display additional information or options.
+ *
+ *     @example
+ *     // Example of a popup button.
+ *     var popupButton = new OO.ui.PopupButtonWidget( {
+ *         label: 'Popup button with options',
+ *         icon: 'menu',
+ *         popup: {
+ *             $content: $( '<p>Additional options here.</p>' ),
+ *             padded: true,
+ *             align: 'force-left'
+ *         }
+ *     } );
+ *     // Append the button to the DOM.
+ *     $( 'body' ).append( popupButton.$element );
+ *
+ * @class
+ * @extends OO.ui.ButtonWidget
+ * @mixins OO.ui.mixin.PopupElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.PopupButtonWidget = function OoUiPopupButtonWidget( config ) {
+       // Parent constructor
+       OO.ui.PopupButtonWidget.parent.call( this, config );
+
+       // Mixin constructors
+       OO.ui.mixin.PopupElement.call( this, config );
+
+       // Events
+       this.connect( this, { click: 'onAction' } );
+
+       // Initialization
+       this.$element
+               .addClass( 'oo-ui-popupButtonWidget' )
+               .attr( 'aria-haspopup', 'true' )
+               .append( this.popup.$element );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.PopupButtonWidget, OO.ui.ButtonWidget );
+OO.mixinClass( OO.ui.PopupButtonWidget, OO.ui.mixin.PopupElement );
+
+/* Methods */
+
+/**
+ * Handle the button action being triggered.
+ *
+ * @private
+ */
+OO.ui.PopupButtonWidget.prototype.onAction = function () {
+       this.popup.toggle();
+};
+
+/**
+ * Mixin for OO.ui.Widget subclasses to provide OO.ui.mixin.GroupElement.
+ *
+ * Use together with OO.ui.mixin.ItemWidget to make disabled state inheritable.
+ *
+ * @private
+ * @abstract
+ * @class
+ * @extends OO.ui.mixin.GroupElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.mixin.GroupWidget = function OoUiMixinGroupWidget( config ) {
+       // Parent constructor
+       OO.ui.mixin.GroupWidget.parent.call( this, config );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.mixin.GroupWidget, OO.ui.mixin.GroupElement );
+
+/* Methods */
+
+/**
+ * Set the disabled state of the widget.
+ *
+ * This will also update the disabled state of child widgets.
+ *
+ * @param {boolean} disabled Disable widget
+ * @chainable
+ */
+OO.ui.mixin.GroupWidget.prototype.setDisabled = function ( disabled ) {
+       var i, len;
+
+       // Parent method
+       // Note: Calling #setDisabled this way assumes this is mixed into an OO.ui.Widget
+       OO.ui.Widget.prototype.setDisabled.call( this, disabled );
+
+       // During construction, #setDisabled is called before the OO.ui.mixin.GroupElement constructor
+       if ( this.items ) {
+               for ( i = 0, len = this.items.length; i < len; i++ ) {
+                       this.items[ i ].updateDisabled();
+               }
+       }
+
+       return this;
+};
+
+/**
+ * Mixin for widgets used as items in widgets that mix in OO.ui.mixin.GroupWidget.
+ *
+ * Item widgets have a reference to a OO.ui.mixin.GroupWidget while they are attached to the group. This
+ * allows bidirectional communication.
+ *
+ * Use together with OO.ui.mixin.GroupWidget to make disabled state inheritable.
+ *
+ * @private
+ * @abstract
+ * @class
+ *
+ * @constructor
+ */
+OO.ui.mixin.ItemWidget = function OoUiMixinItemWidget() {
+       //
+};
+
+/* Methods */
+
+/**
+ * Check if widget is disabled.
+ *
+ * Checks parent if present, making disabled state inheritable.
+ *
+ * @return {boolean} Widget is disabled
+ */
+OO.ui.mixin.ItemWidget.prototype.isDisabled = function () {
+       return this.disabled ||
+               ( this.elementGroup instanceof OO.ui.Widget && this.elementGroup.isDisabled() );
+};
+
+/**
+ * Set group element is in.
+ *
+ * @param {OO.ui.mixin.GroupElement|null} group Group element, null if none
+ * @chainable
+ */
+OO.ui.mixin.ItemWidget.prototype.setElementGroup = function ( group ) {
+       // Parent method
+       // Note: Calling #setElementGroup this way assumes this is mixed into an OO.ui.Element
+       OO.ui.Element.prototype.setElementGroup.call( this, group );
+
+       // Initialize item disabled states
+       this.updateDisabled();
+
+       return this;
+};
+
+/**
+ * OptionWidgets are special elements that can be selected and configured with data. The
+ * data is often unique for each option, but it does not have to be. OptionWidgets are used
+ * with OO.ui.SelectWidget to create a selection of mutually exclusive options. For more information
+ * and examples, please see the [OOjs UI documentation on MediaWiki][1].
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options
+ *
+ * @class
+ * @extends OO.ui.Widget
+ * @mixins OO.ui.mixin.LabelElement
+ * @mixins OO.ui.mixin.FlaggedElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.OptionWidget = function OoUiOptionWidget( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Parent constructor
+       OO.ui.OptionWidget.parent.call( this, config );
+
+       // Mixin constructors
+       OO.ui.mixin.ItemWidget.call( this );
+       OO.ui.mixin.LabelElement.call( this, config );
+       OO.ui.mixin.FlaggedElement.call( this, config );
+
+       // Properties
+       this.selected = false;
+       this.highlighted = false;
+       this.pressed = false;
+
+       // Initialization
+       this.$element
+               .data( 'oo-ui-optionWidget', this )
+               .attr( 'role', 'option' )
+               .attr( 'aria-selected', 'false' )
+               .addClass( 'oo-ui-optionWidget' )
+               .append( this.$label );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.OptionWidget, OO.ui.Widget );
+OO.mixinClass( OO.ui.OptionWidget, OO.ui.mixin.ItemWidget );
+OO.mixinClass( OO.ui.OptionWidget, OO.ui.mixin.LabelElement );
+OO.mixinClass( OO.ui.OptionWidget, OO.ui.mixin.FlaggedElement );
+
+/* Static Properties */
+
+OO.ui.OptionWidget.static.selectable = true;
+
+OO.ui.OptionWidget.static.highlightable = true;
+
+OO.ui.OptionWidget.static.pressable = true;
+
+OO.ui.OptionWidget.static.scrollIntoViewOnSelect = false;
+
+/* Methods */
+
+/**
+ * Check if the option can be selected.
+ *
+ * @return {boolean} Item is selectable
+ */
+OO.ui.OptionWidget.prototype.isSelectable = function () {
+       return this.constructor.static.selectable && !this.isDisabled() && this.isVisible();
+};
+
+/**
+ * Check if the option can be highlighted. A highlight indicates that the option
+ * may be selected when a user presses enter or clicks. Disabled items cannot
+ * be highlighted.
+ *
+ * @return {boolean} Item is highlightable
+ */
+OO.ui.OptionWidget.prototype.isHighlightable = function () {
+       return this.constructor.static.highlightable && !this.isDisabled() && this.isVisible();
+};
+
+/**
+ * Check if the option can be pressed. The pressed state occurs when a user mouses
+ * down on an item, but has not yet let go of the mouse.
+ *
+ * @return {boolean} Item is pressable
+ */
+OO.ui.OptionWidget.prototype.isPressable = function () {
+       return this.constructor.static.pressable && !this.isDisabled() && this.isVisible();
+};
+
+/**
+ * Check if the option is selected.
+ *
+ * @return {boolean} Item is selected
+ */
+OO.ui.OptionWidget.prototype.isSelected = function () {
+       return this.selected;
+};
+
+/**
+ * Check if the option is highlighted. A highlight indicates that the
+ * item may be selected when a user presses enter or clicks.
+ *
+ * @return {boolean} Item is highlighted
+ */
+OO.ui.OptionWidget.prototype.isHighlighted = function () {
+       return this.highlighted;
+};
+
+/**
+ * Check if the option is pressed. The pressed state occurs when a user mouses
+ * down on an item, but has not yet let go of the mouse. The item may appear
+ * selected, but it will not be selected until the user releases the mouse.
+ *
+ * @return {boolean} Item is pressed
+ */
+OO.ui.OptionWidget.prototype.isPressed = function () {
+       return this.pressed;
+};
+
+/**
+ * Set the option’s selected state. In general, all modifications to the selection
+ * should be handled by the SelectWidget’s {@link OO.ui.SelectWidget#selectItem selectItem( [item] )}
+ * method instead of this method.
+ *
+ * @param {boolean} [state=false] Select option
+ * @chainable
+ */
+OO.ui.OptionWidget.prototype.setSelected = function ( state ) {
+       if ( this.constructor.static.selectable ) {
+               this.selected = !!state;
+               this.$element
+                       .toggleClass( 'oo-ui-optionWidget-selected', state )
+                       .attr( 'aria-selected', state.toString() );
+               if ( state && this.constructor.static.scrollIntoViewOnSelect ) {
+                       this.scrollElementIntoView();
+               }
+               this.updateThemeClasses();
+       }
+       return this;
+};
+
+/**
+ * Set the option’s highlighted state. In general, all programmatic
+ * modifications to the highlight should be handled by the
+ * SelectWidget’s {@link OO.ui.SelectWidget#highlightItem highlightItem( [item] )}
+ * method instead of this method.
+ *
+ * @param {boolean} [state=false] Highlight option
+ * @chainable
+ */
+OO.ui.OptionWidget.prototype.setHighlighted = function ( state ) {
+       if ( this.constructor.static.highlightable ) {
+               this.highlighted = !!state;
+               this.$element.toggleClass( 'oo-ui-optionWidget-highlighted', state );
+               this.updateThemeClasses();
+       }
+       return this;
+};
+
+/**
+ * Set the option’s pressed state. In general, all
+ * programmatic modifications to the pressed state should be handled by the
+ * SelectWidget’s {@link OO.ui.SelectWidget#pressItem pressItem( [item] )}
+ * method instead of this method.
+ *
+ * @param {boolean} [state=false] Press option
+ * @chainable
+ */
+OO.ui.OptionWidget.prototype.setPressed = function ( state ) {
+       if ( this.constructor.static.pressable ) {
+               this.pressed = !!state;
+               this.$element.toggleClass( 'oo-ui-optionWidget-pressed', state );
+               this.updateThemeClasses();
+       }
+       return this;
+};
+
+/**
+ * A SelectWidget is of a generic selection of options. The OOjs UI library contains several types of
+ * select widgets, including {@link OO.ui.ButtonSelectWidget button selects},
+ * {@link OO.ui.RadioSelectWidget radio selects}, and {@link OO.ui.MenuSelectWidget
+ * menu selects}.
+ *
+ * This class should be used together with OO.ui.OptionWidget or OO.ui.DecoratedOptionWidget. For more
+ * information, please see the [OOjs UI documentation on MediaWiki][1].
+ *
+ *     @example
+ *     // Example of a select widget with three options
+ *     var select = new OO.ui.SelectWidget( {
+ *         items: [
+ *             new OO.ui.OptionWidget( {
+ *                 data: 'a',
+ *                 label: 'Option One',
+ *             } ),
+ *             new OO.ui.OptionWidget( {
+ *                 data: 'b',
+ *                 label: 'Option Two',
+ *             } ),
+ *             new OO.ui.OptionWidget( {
+ *                 data: 'c',
+ *                 label: 'Option Three',
+ *             } )
+ *         ]
+ *     } );
+ *     $( 'body' ).append( select.$element );
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options
+ *
+ * @abstract
+ * @class
+ * @extends OO.ui.Widget
+ * @mixins OO.ui.mixin.GroupWidget
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {OO.ui.OptionWidget[]} [items] An array of options to add to the select.
+ *  Options are created with {@link OO.ui.OptionWidget OptionWidget} classes. See
+ *  the [OOjs UI documentation on MediaWiki] [2] for examples.
+ *  [2]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options
+ */
+OO.ui.SelectWidget = function OoUiSelectWidget( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Parent constructor
+       OO.ui.SelectWidget.parent.call( this, config );
+
+       // Mixin constructors
+       OO.ui.mixin.GroupWidget.call( this, $.extend( {}, config, { $group: this.$element } ) );
+
+       // Properties
+       this.pressed = false;
+       this.selecting = null;
+       this.onMouseUpHandler = this.onMouseUp.bind( this );
+       this.onMouseMoveHandler = this.onMouseMove.bind( this );
+       this.onKeyDownHandler = this.onKeyDown.bind( this );
+       this.onKeyPressHandler = this.onKeyPress.bind( this );
+       this.keyPressBuffer = '';
+       this.keyPressBufferTimer = null;
+
+       // Events
+       this.connect( this, {
+               toggle: 'onToggle'
+       } );
+       this.$element.on( {
+               mousedown: this.onMouseDown.bind( this ),
+               mouseover: this.onMouseOver.bind( this ),
+               mouseleave: this.onMouseLeave.bind( this )
+       } );
+
+       // Initialization
+       this.$element
+               .addClass( 'oo-ui-selectWidget oo-ui-selectWidget-depressed' )
+               .attr( 'role', 'listbox' );
+       if ( Array.isArray( config.items ) ) {
+               this.addItems( config.items );
+       }
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.SelectWidget, OO.ui.Widget );
+
+// Need to mixin base class as well
+OO.mixinClass( OO.ui.SelectWidget, OO.ui.mixin.GroupElement );
+OO.mixinClass( OO.ui.SelectWidget, OO.ui.mixin.GroupWidget );
+
+/* Static */
+OO.ui.SelectWidget.static.passAllFilter = function () {
+       return true;
+};
+
+/* Events */
+
+/**
+ * @event highlight
+ *
+ * A `highlight` event is emitted when the highlight is changed with the #highlightItem method.
+ *
+ * @param {OO.ui.OptionWidget|null} item Highlighted item
+ */
+
+/**
+ * @event press
+ *
+ * A `press` event is emitted when the #pressItem method is used to programmatically modify the
+ * pressed state of an option.
+ *
+ * @param {OO.ui.OptionWidget|null} item Pressed item
+ */
+
+/**
+ * @event select
+ *
+ * A `select` event is emitted when the selection is modified programmatically with the #selectItem method.
+ *
+ * @param {OO.ui.OptionWidget|null} item Selected item
+ */
+
+/**
+ * @event choose
+ * A `choose` event is emitted when an item is chosen with the #chooseItem method.
+ * @param {OO.ui.OptionWidget} item Chosen item
+ */
+
+/**
+ * @event add
+ *
+ * An `add` event is emitted when options are added to the select with the #addItems method.
+ *
+ * @param {OO.ui.OptionWidget[]} items Added items
+ * @param {number} index Index of insertion point
+ */
+
+/**
+ * @event remove
+ *
+ * A `remove` event is emitted when options are removed from the select with the #clearItems
+ * or #removeItems methods.
+ *
+ * @param {OO.ui.OptionWidget[]} items Removed items
+ */
+
+/* Methods */
+
+/**
+ * Handle mouse down events.
+ *
+ * @private
+ * @param {jQuery.Event} e Mouse down event
+ */
+OO.ui.SelectWidget.prototype.onMouseDown = function ( e ) {
+       var item;
+
+       if ( !this.isDisabled() && e.which === OO.ui.MouseButtons.LEFT ) {
+               this.togglePressed( true );
+               item = this.getTargetItem( e );
+               if ( item && item.isSelectable() ) {
+                       this.pressItem( item );
+                       this.selecting = item;
+                       this.getElementDocument().addEventListener( 'mouseup', this.onMouseUpHandler, true );
+                       this.getElementDocument().addEventListener( 'mousemove', this.onMouseMoveHandler, true );
+               }
+       }
+       return false;
+};
+
+/**
+ * Handle mouse up events.
+ *
+ * @private
+ * @param {MouseEvent} e Mouse up event
+ */
+OO.ui.SelectWidget.prototype.onMouseUp = function ( e ) {
+       var item;
+
+       this.togglePressed( false );
+       if ( !this.selecting ) {
+               item = this.getTargetItem( e );
+               if ( item && item.isSelectable() ) {
+                       this.selecting = item;
+               }
+       }
+       if ( !this.isDisabled() && e.which === OO.ui.MouseButtons.LEFT && this.selecting ) {
+               this.pressItem( null );
+               this.chooseItem( this.selecting );
+               this.selecting = null;
+       }
+
+       this.getElementDocument().removeEventListener( 'mouseup', this.onMouseUpHandler, true );
+       this.getElementDocument().removeEventListener( 'mousemove', this.onMouseMoveHandler, true );
+
+       return false;
+};
+
+/**
+ * Handle mouse move events.
+ *
+ * @private
+ * @param {MouseEvent} e Mouse move event
+ */
+OO.ui.SelectWidget.prototype.onMouseMove = function ( e ) {
+       var item;
+
+       if ( !this.isDisabled() && this.pressed ) {
+               item = this.getTargetItem( e );
+               if ( item && item !== this.selecting && item.isSelectable() ) {
+                       this.pressItem( item );
+                       this.selecting = item;
+               }
+       }
+};
+
+/**
+ * Handle mouse over events.
+ *
+ * @private
+ * @param {jQuery.Event} e Mouse over event
+ */
+OO.ui.SelectWidget.prototype.onMouseOver = function ( e ) {
+       var item;
+
+       if ( !this.isDisabled() ) {
+               item = this.getTargetItem( e );
+               this.highlightItem( item && item.isHighlightable() ? item : null );
+       }
+       return false;
+};
+
+/**
+ * Handle mouse leave events.
+ *
+ * @private
+ * @param {jQuery.Event} e Mouse over event
+ */
+OO.ui.SelectWidget.prototype.onMouseLeave = function () {
+       if ( !this.isDisabled() ) {
+               this.highlightItem( null );
+       }
+       return false;
+};
+
+/**
+ * Handle key down events.
+ *
+ * @protected
+ * @param {KeyboardEvent} e Key down event
+ */
+OO.ui.SelectWidget.prototype.onKeyDown = function ( e ) {
+       var nextItem,
+               handled = false,
+               currentItem = this.getHighlightedItem() || this.getSelectedItem();
+
+       if ( !this.isDisabled() && this.isVisible() ) {
+               switch ( e.keyCode ) {
+                       case OO.ui.Keys.ENTER:
+                               if ( currentItem && currentItem.constructor.static.highlightable ) {
+                                       // Was only highlighted, now let's select it. No-op if already selected.
+                                       this.chooseItem( currentItem );
+                                       handled = true;
+                               }
+                               break;
+                       case OO.ui.Keys.UP:
+                       case OO.ui.Keys.LEFT:
+                               this.clearKeyPressBuffer();
+                               nextItem = this.getRelativeSelectableItem( currentItem, -1 );
+                               handled = true;
+                               break;
+                       case OO.ui.Keys.DOWN:
+                       case OO.ui.Keys.RIGHT:
+                               this.clearKeyPressBuffer();
+                               nextItem = this.getRelativeSelectableItem( currentItem, 1 );
+                               handled = true;
+                               break;
+                       case OO.ui.Keys.ESCAPE:
+                       case OO.ui.Keys.TAB:
+                               if ( currentItem && currentItem.constructor.static.highlightable ) {
+                                       currentItem.setHighlighted( false );
+                               }
+                               this.unbindKeyDownListener();
+                               this.unbindKeyPressListener();
+                               // Don't prevent tabbing away / defocusing
+                               handled = false;
+                               break;
+               }
+
+               if ( nextItem ) {
+                       if ( nextItem.constructor.static.highlightable ) {
+                               this.highlightItem( nextItem );
+                       } else {
+                               this.chooseItem( nextItem );
+                       }
+                       nextItem.scrollElementIntoView();
+               }
+
+               if ( handled ) {
+                       e.preventDefault();
+                       e.stopPropagation();
+               }
+       }
+};
+
+/**
+ * Bind key down listener.
+ *
+ * @protected
+ */
+OO.ui.SelectWidget.prototype.bindKeyDownListener = function () {
+       this.getElementWindow().addEventListener( 'keydown', this.onKeyDownHandler, true );
+};
+
+/**
+ * Unbind key down listener.
+ *
+ * @protected
+ */
+OO.ui.SelectWidget.prototype.unbindKeyDownListener = function () {
+       this.getElementWindow().removeEventListener( 'keydown', this.onKeyDownHandler, true );
+};
+
+/**
+ * Clear the key-press buffer
+ *
+ * @protected
+ */
+OO.ui.SelectWidget.prototype.clearKeyPressBuffer = function () {
+       if ( this.keyPressBufferTimer ) {
+               clearTimeout( this.keyPressBufferTimer );
+               this.keyPressBufferTimer = null;
+       }
+       this.keyPressBuffer = '';
+};
+
+/**
+ * Handle key press events.
+ *
+ * @protected
+ * @param {KeyboardEvent} e Key press event
+ */
+OO.ui.SelectWidget.prototype.onKeyPress = function ( e ) {
+       var c, filter, item;
+
+       if ( !e.charCode ) {
+               if ( e.keyCode === OO.ui.Keys.BACKSPACE && this.keyPressBuffer !== '' ) {
+                       this.keyPressBuffer = this.keyPressBuffer.substr( 0, this.keyPressBuffer.length - 1 );
+                       return false;
+               }
+               return;
+       }
+       if ( String.fromCodePoint ) {
+               c = String.fromCodePoint( e.charCode );
+       } else {
+               c = String.fromCharCode( e.charCode );
+       }
+
+       if ( this.keyPressBufferTimer ) {
+               clearTimeout( this.keyPressBufferTimer );
+       }
+       this.keyPressBufferTimer = setTimeout( this.clearKeyPressBuffer.bind( this ), 1500 );
+
+       item = this.getHighlightedItem() || this.getSelectedItem();
+
+       if ( this.keyPressBuffer === c ) {
+               // Common (if weird) special case: typing "xxxx" will cycle through all
+               // the items beginning with "x".
+               if ( item ) {
+                       item = this.getRelativeSelectableItem( item, 1 );
+               }
+       } else {
+               this.keyPressBuffer += c;
+       }
+
+       filter = this.getItemMatcher( this.keyPressBuffer, false );
+       if ( !item || !filter( item ) ) {
+               item = this.getRelativeSelectableItem( item, 1, filter );
+       }
+       if ( item ) {
+               if ( item.constructor.static.highlightable ) {
+                       this.highlightItem( item );
+               } else {
+                       this.chooseItem( item );
+               }
+               item.scrollElementIntoView();
+       }
+
+       e.preventDefault();
+       e.stopPropagation();
+};
+
+/**
+ * Get a matcher for the specific string
+ *
+ * @protected
+ * @param {string} s String to match against items
+ * @param {boolean} [exact=false] Only accept exact matches
+ * @return {Function} function ( OO.ui.OptionItem ) => boolean
+ */
+OO.ui.SelectWidget.prototype.getItemMatcher = function ( s, exact ) {
+       var re;
+
+       if ( s.normalize ) {
+               s = s.normalize();
+       }
+       s = exact ? s.trim() : s.replace( /^\s+/, '' );
+       re = '^\\s*' + s.replace( /([\\{}()|.?*+\-\^$\[\]])/g, '\\$1' ).replace( /\s+/g, '\\s+' );
+       if ( exact ) {
+               re += '\\s*$';
+       }
+       re = new RegExp( re, 'i' );
+       return function ( item ) {
+               var l = item.getLabel();
+               if ( typeof l !== 'string' ) {
+                       l = item.$label.text();
+               }
+               if ( l.normalize ) {
+                       l = l.normalize();
+               }
+               return re.test( l );
+       };
+};
+
+/**
+ * Bind key press listener.
+ *
+ * @protected
+ */
+OO.ui.SelectWidget.prototype.bindKeyPressListener = function () {
+       this.getElementWindow().addEventListener( 'keypress', this.onKeyPressHandler, true );
+};
+
+/**
+ * Unbind key down listener.
+ *
+ * If you override this, be sure to call this.clearKeyPressBuffer() from your
+ * implementation.
+ *
+ * @protected
+ */
+OO.ui.SelectWidget.prototype.unbindKeyPressListener = function () {
+       this.getElementWindow().removeEventListener( 'keypress', this.onKeyPressHandler, true );
+       this.clearKeyPressBuffer();
+};
+
+/**
+ * Visibility change handler
+ *
+ * @protected
+ * @param {boolean} visible
+ */
+OO.ui.SelectWidget.prototype.onToggle = function ( visible ) {
+       if ( !visible ) {
+               this.clearKeyPressBuffer();
+       }
+};
+
+/**
+ * Get the closest item to a jQuery.Event.
+ *
+ * @private
+ * @param {jQuery.Event} e
+ * @return {OO.ui.OptionWidget|null} Outline item widget, `null` if none was found
+ */
+OO.ui.SelectWidget.prototype.getTargetItem = function ( e ) {
+       return $( e.target ).closest( '.oo-ui-optionWidget' ).data( 'oo-ui-optionWidget' ) || null;
+};
+
+/**
+ * Get selected item.
+ *
+ * @return {OO.ui.OptionWidget|null} Selected item, `null` if no item is selected
+ */
+OO.ui.SelectWidget.prototype.getSelectedItem = function () {
+       var i, len;
+
+       for ( i = 0, len = this.items.length; i < len; i++ ) {
+               if ( this.items[ i ].isSelected() ) {
+                       return this.items[ i ];
+               }
+       }
+       return null;
+};
+
+/**
+ * Get highlighted item.
+ *
+ * @return {OO.ui.OptionWidget|null} Highlighted item, `null` if no item is highlighted
+ */
+OO.ui.SelectWidget.prototype.getHighlightedItem = function () {
+       var i, len;
+
+       for ( i = 0, len = this.items.length; i < len; i++ ) {
+               if ( this.items[ i ].isHighlighted() ) {
+                       return this.items[ i ];
+               }
+       }
+       return null;
+};
+
+/**
+ * Toggle pressed state.
+ *
+ * Press is a state that occurs when a user mouses down on an item, but
+ * has not yet let go of the mouse. The item may appear selected, but it will not be selected
+ * until the user releases the mouse.
+ *
+ * @param {boolean} pressed An option is being pressed
+ */
+OO.ui.SelectWidget.prototype.togglePressed = function ( pressed ) {
+       if ( pressed === undefined ) {
+               pressed = !this.pressed;
+       }
+       if ( pressed !== this.pressed ) {
+               this.$element
+                       .toggleClass( 'oo-ui-selectWidget-pressed', pressed )
+                       .toggleClass( 'oo-ui-selectWidget-depressed', !pressed );
+               this.pressed = pressed;
+       }
+};
+
+/**
+ * Highlight an option. If the `item` param is omitted, no options will be highlighted
+ * and any existing highlight will be removed. The highlight is mutually exclusive.
+ *
+ * @param {OO.ui.OptionWidget} [item] Item to highlight, omit for no highlight
+ * @fires highlight
+ * @chainable
+ */
+OO.ui.SelectWidget.prototype.highlightItem = function ( item ) {
+       var i, len, highlighted,
+               changed = false;
+
+       for ( i = 0, len = this.items.length; i < len; i++ ) {
+               highlighted = this.items[ i ] === item;
+               if ( this.items[ i ].isHighlighted() !== highlighted ) {
+                       this.items[ i ].setHighlighted( highlighted );
+                       changed = true;
+               }
+       }
+       if ( changed ) {
+               this.emit( 'highlight', item );
+       }
+
+       return this;
+};
+
+/**
+ * Fetch an item by its label.
+ *
+ * @param {string} label Label of the item to select.
+ * @param {boolean} [prefix=false] Allow a prefix match, if only a single item matches
+ * @return {OO.ui.Element|null} Item with equivalent label, `null` if none exists
+ */
+OO.ui.SelectWidget.prototype.getItemFromLabel = function ( label, prefix ) {
+       var i, item, found,
+               len = this.items.length,
+               filter = this.getItemMatcher( label, true );
+
+       for ( i = 0; i < len; i++ ) {
+               item = this.items[ i ];
+               if ( item instanceof OO.ui.OptionWidget && item.isSelectable() && filter( item ) ) {
+                       return item;
+               }
+       }
+
+       if ( prefix ) {
+               found = null;
+               filter = this.getItemMatcher( label, false );
+               for ( i = 0; i < len; i++ ) {
+                       item = this.items[ i ];
+                       if ( item instanceof OO.ui.OptionWidget && item.isSelectable() && filter( item ) ) {
+                               if ( found ) {
+                                       return null;
+                               }
+                               found = item;
+                       }
+               }
+               if ( found ) {
+                       return found;
+               }
+       }
+
+       return null;
+};
+
+/**
+ * Programmatically select an option by its label. If the item does not exist,
+ * all options will be deselected.
+ *
+ * @param {string} [label] Label of the item to select.
+ * @param {boolean} [prefix=false] Allow a prefix match, if only a single item matches
+ * @fires select
+ * @chainable
+ */
+OO.ui.SelectWidget.prototype.selectItemByLabel = function ( label, prefix ) {
+       var itemFromLabel = this.getItemFromLabel( label, !!prefix );
+       if ( label === undefined || !itemFromLabel ) {
+               return this.selectItem();
+       }
+       return this.selectItem( itemFromLabel );
+};
+
+/**
+ * Programmatically select an option by its data. If the `data` parameter is omitted,
+ * or if the item does not exist, all options will be deselected.
+ *
+ * @param {Object|string} [data] Value of the item to select, omit to deselect all
+ * @fires select
+ * @chainable
+ */
+OO.ui.SelectWidget.prototype.selectItemByData = function ( data ) {
+       var itemFromData = this.getItemFromData( data );
+       if ( data === undefined || !itemFromData ) {
+               return this.selectItem();
+       }
+       return this.selectItem( itemFromData );
+};
+
+/**
+ * Programmatically select an option by its reference. If the `item` parameter is omitted,
+ * all options will be deselected.
+ *
+ * @param {OO.ui.OptionWidget} [item] Item to select, omit to deselect all
+ * @fires select
+ * @chainable
+ */
+OO.ui.SelectWidget.prototype.selectItem = function ( item ) {
+       var i, len, selected,
+               changed = false;
+
+       for ( i = 0, len = this.items.length; i < len; i++ ) {
+               selected = this.items[ i ] === item;
+               if ( this.items[ i ].isSelected() !== selected ) {
+                       this.items[ i ].setSelected( selected );
+                       changed = true;
+               }
+       }
+       if ( changed ) {
+               this.emit( 'select', item );
+       }
+
+       return this;
+};
+
+/**
+ * Press an item.
+ *
+ * Press is a state that occurs when a user mouses down on an item, but has not
+ * yet let go of the mouse. The item may appear selected, but it will not be selected until the user
+ * releases the mouse.
+ *
+ * @param {OO.ui.OptionWidget} [item] Item to press, omit to depress all
+ * @fires press
+ * @chainable
+ */
+OO.ui.SelectWidget.prototype.pressItem = function ( item ) {
+       var i, len, pressed,
+               changed = false;
+
+       for ( i = 0, len = this.items.length; i < len; i++ ) {
+               pressed = this.items[ i ] === item;
+               if ( this.items[ i ].isPressed() !== pressed ) {
+                       this.items[ i ].setPressed( pressed );
+                       changed = true;
+               }
+       }
+       if ( changed ) {
+               this.emit( 'press', item );
+       }
+
+       return this;
+};
+
+/**
+ * Choose an item.
+ *
+ * Note that ‘choose’ should never be modified programmatically. A user can choose
+ * an option with the keyboard or mouse and it becomes selected. To select an item programmatically,
+ * use the #selectItem method.
+ *
+ * This method is identical to #selectItem, but may vary in subclasses that take additional action
+ * when users choose an item with the keyboard or mouse.
+ *
+ * @param {OO.ui.OptionWidget} item Item to choose
+ * @fires choose
+ * @chainable
+ */
+OO.ui.SelectWidget.prototype.chooseItem = function ( item ) {
+       if ( item ) {
+               this.selectItem( item );
+               this.emit( 'choose', item );
+       }
+
+       return this;
+};
+
+/**
+ * Get an option by its position relative to the specified item (or to the start of the option array,
+ * if item is `null`). The direction in which to search through the option array is specified with a
+ * number: -1 for reverse (the default) or 1 for forward. The method will return an option, or
+ * `null` if there are no options in the array.
+ *
+ * @param {OO.ui.OptionWidget|null} item Item to describe the start position, or `null` to start at the beginning of the array.
+ * @param {number} direction Direction to move in: -1 to move backward, 1 to move forward
+ * @param {Function} filter Only consider items for which this function returns
+ *  true. Function takes an OO.ui.OptionWidget and returns a boolean.
+ * @return {OO.ui.OptionWidget|null} Item at position, `null` if there are no items in the select
+ */
+OO.ui.SelectWidget.prototype.getRelativeSelectableItem = function ( item, direction, filter ) {
+       var currentIndex, nextIndex, i,
+               increase = direction > 0 ? 1 : -1,
+               len = this.items.length;
+
+       if ( !$.isFunction( filter ) ) {
+               filter = OO.ui.SelectWidget.static.passAllFilter;
+       }
+
+       if ( item instanceof OO.ui.OptionWidget ) {
+               currentIndex = this.items.indexOf( item );
+               nextIndex = ( currentIndex + increase + len ) % len;
+       } else {
+               // If no item is selected and moving forward, start at the beginning.
+               // If moving backward, start at the end.
+               nextIndex = direction > 0 ? 0 : len - 1;
+       }
+
+       for ( i = 0; i < len; i++ ) {
+               item = this.items[ nextIndex ];
+               if ( item instanceof OO.ui.OptionWidget && item.isSelectable() && filter( item ) ) {
+                       return item;
+               }
+               nextIndex = ( nextIndex + increase + len ) % len;
+       }
+       return null;
+};
+
+/**
+ * Get the next selectable item or `null` if there are no selectable items.
+ * Disabled options and menu-section markers and breaks are not selectable.
+ *
+ * @return {OO.ui.OptionWidget|null} Item, `null` if there aren't any selectable items
+ */
+OO.ui.SelectWidget.prototype.getFirstSelectableItem = function () {
+       var i, len, item;
+
+       for ( i = 0, len = this.items.length; i < len; i++ ) {
+               item = this.items[ i ];
+               if ( item instanceof OO.ui.OptionWidget && item.isSelectable() ) {
+                       return item;
+               }
+       }
+
+       return null;
+};
+
+/**
+ * Add an array of options to the select. Optionally, an index number can be used to
+ * specify an insertion point.
+ *
+ * @param {OO.ui.OptionWidget[]} items Items to add
+ * @param {number} [index] Index to insert items after
+ * @fires add
+ * @chainable
+ */
+OO.ui.SelectWidget.prototype.addItems = function ( items, index ) {
+       // Mixin method
+       OO.ui.mixin.GroupWidget.prototype.addItems.call( this, items, index );
+
+       // Always provide an index, even if it was omitted
+       this.emit( 'add', items, index === undefined ? this.items.length - items.length - 1 : index );
+
+       return this;
+};
+
+/**
+ * Remove the specified array of options from the select. Options will be detached
+ * from the DOM, not removed, so they can be reused later. To remove all options from
+ * the select, you may wish to use the #clearItems method instead.
+ *
+ * @param {OO.ui.OptionWidget[]} items Items to remove
+ * @fires remove
+ * @chainable
+ */
+OO.ui.SelectWidget.prototype.removeItems = function ( items ) {
+       var i, len, item;
+
+       // Deselect items being removed
+       for ( i = 0, len = items.length; i < len; i++ ) {
+               item = items[ i ];
+               if ( item.isSelected() ) {
+                       this.selectItem( null );
+               }
+       }
+
+       // Mixin method
+       OO.ui.mixin.GroupWidget.prototype.removeItems.call( this, items );
+
+       this.emit( 'remove', items );
+
+       return this;
+};
+
+/**
+ * Clear all options from the select. Options will be detached from the DOM, not removed,
+ * so that they can be reused later. To remove a subset of options from the select, use
+ * the #removeItems method.
+ *
+ * @fires remove
+ * @chainable
+ */
+OO.ui.SelectWidget.prototype.clearItems = function () {
+       var items = this.items.slice();
+
+       // Mixin method
+       OO.ui.mixin.GroupWidget.prototype.clearItems.call( this );
+
+       // Clear selection
+       this.selectItem( null );
+
+       this.emit( 'remove', items );
+
+       return this;
+};
+
+/**
+ * DecoratedOptionWidgets are {@link OO.ui.OptionWidget options} that can be configured
+ * with an {@link OO.ui.mixin.IconElement icon} and/or {@link OO.ui.mixin.IndicatorElement indicator}.
+ * This class is used with OO.ui.SelectWidget to create a selection of mutually exclusive
+ * options. For more information about options and selects, please see the
+ * [OOjs UI documentation on MediaWiki][1].
+ *
+ *     @example
+ *     // Decorated options in a select widget
+ *     var select = new OO.ui.SelectWidget( {
+ *         items: [
+ *             new OO.ui.DecoratedOptionWidget( {
+ *                 data: 'a',
+ *                 label: 'Option with icon',
+ *                 icon: 'help'
+ *             } ),
+ *             new OO.ui.DecoratedOptionWidget( {
+ *                 data: 'b',
+ *                 label: 'Option with indicator',
+ *                 indicator: 'next'
+ *             } )
+ *         ]
+ *     } );
+ *     $( 'body' ).append( select.$element );
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options
+ *
+ * @class
+ * @extends OO.ui.OptionWidget
+ * @mixins OO.ui.mixin.IconElement
+ * @mixins OO.ui.mixin.IndicatorElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.DecoratedOptionWidget = function OoUiDecoratedOptionWidget( config ) {
+       // Parent constructor
+       OO.ui.DecoratedOptionWidget.parent.call( this, config );
+
+       // Mixin constructors
+       OO.ui.mixin.IconElement.call( this, config );
+       OO.ui.mixin.IndicatorElement.call( this, config );
+
+       // Initialization
+       this.$element
+               .addClass( 'oo-ui-decoratedOptionWidget' )
+               .prepend( this.$icon )
+               .append( this.$indicator );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.DecoratedOptionWidget, OO.ui.OptionWidget );
+OO.mixinClass( OO.ui.DecoratedOptionWidget, OO.ui.mixin.IconElement );
+OO.mixinClass( OO.ui.DecoratedOptionWidget, OO.ui.mixin.IndicatorElement );
+
+/**
+ * MenuOptionWidget is an option widget that looks like a menu item. The class is used with
+ * OO.ui.MenuSelectWidget to create a menu of mutually exclusive options. Please see
+ * the [OOjs UI documentation on MediaWiki] [1] for more information.
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options#Menu_selects_and_options
+ *
+ * @class
+ * @extends OO.ui.DecoratedOptionWidget
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.MenuOptionWidget = function OoUiMenuOptionWidget( config ) {
+       // Configuration initialization
+       config = $.extend( { icon: 'check' }, config );
+
+       // Parent constructor
+       OO.ui.MenuOptionWidget.parent.call( this, config );
+
+       // Initialization
+       this.$element
+               .attr( 'role', 'menuitem' )
+               .addClass( 'oo-ui-menuOptionWidget' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.MenuOptionWidget, OO.ui.DecoratedOptionWidget );
+
+/* Static Properties */
+
+OO.ui.MenuOptionWidget.static.scrollIntoViewOnSelect = true;
+
+/**
+ * MenuSectionOptionWidgets are used inside {@link OO.ui.MenuSelectWidget menu select widgets} to group one or more related
+ * {@link OO.ui.MenuOptionWidget menu options}. MenuSectionOptionWidgets cannot be highlighted or selected.
+ *
+ *     @example
+ *     var myDropdown = new OO.ui.DropdownWidget( {
+ *         menu: {
+ *             items: [
+ *                 new OO.ui.MenuSectionOptionWidget( {
+ *                     label: 'Dogs'
+ *                 } ),
+ *                 new OO.ui.MenuOptionWidget( {
+ *                     data: 'corgi',
+ *                     label: 'Welsh Corgi'
+ *                 } ),
+ *                 new OO.ui.MenuOptionWidget( {
+ *                     data: 'poodle',
+ *                     label: 'Standard Poodle'
+ *                 } ),
+ *                 new OO.ui.MenuSectionOptionWidget( {
+ *                     label: 'Cats'
+ *                 } ),
+ *                 new OO.ui.MenuOptionWidget( {
+ *                     data: 'lion',
+ *                     label: 'Lion'
+ *                 } )
+ *             ]
+ *         }
+ *     } );
+ *     $( 'body' ).append( myDropdown.$element );
+ *
+ * @class
+ * @extends OO.ui.DecoratedOptionWidget
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.MenuSectionOptionWidget = function OoUiMenuSectionOptionWidget( config ) {
+       // Parent constructor
+       OO.ui.MenuSectionOptionWidget.parent.call( this, config );
+
+       // Initialization
+       this.$element.addClass( 'oo-ui-menuSectionOptionWidget' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.MenuSectionOptionWidget, OO.ui.DecoratedOptionWidget );
+
+/* Static Properties */
+
+OO.ui.MenuSectionOptionWidget.static.selectable = false;
+
+OO.ui.MenuSectionOptionWidget.static.highlightable = false;
+
+/**
+ * MenuSelectWidget is a {@link OO.ui.SelectWidget select widget} that contains options and
+ * is used together with OO.ui.MenuOptionWidget. It is designed be used as part of another widget.
+ * See {@link OO.ui.DropdownWidget DropdownWidget}, {@link OO.ui.ComboBoxInputWidget ComboBoxInputWidget},
+ * and {@link OO.ui.mixin.LookupElement LookupElement} for examples of widgets that contain menus.
+ * MenuSelectWidgets themselves are not instantiated directly, rather subclassed
+ * and customized to be opened, closed, and displayed as needed.
+ *
+ * By default, menus are clipped to the visible viewport and are not visible when a user presses the
+ * mouse outside the menu.
+ *
+ * Menus also have support for keyboard interaction:
+ *
+ * - Enter/Return key: choose and select a menu option
+ * - Up-arrow key: highlight the previous menu option
+ * - Down-arrow key: highlight the next menu option
+ * - Esc key: hide the menu
+ *
+ * Please see the [OOjs UI documentation on MediaWiki][1] for more information.
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options
+ *
+ * @class
+ * @extends OO.ui.SelectWidget
+ * @mixins OO.ui.mixin.ClippableElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {OO.ui.TextInputWidget} [input] Text input used to implement option highlighting for menu items that match
+ *  the text the user types. This config is used by {@link OO.ui.ComboBoxInputWidget ComboBoxInputWidget}
+ *  and {@link OO.ui.mixin.LookupElement LookupElement}
+ * @cfg {jQuery} [$input] Text input used to implement option highlighting for menu items that match
+ *  the text the user types. This config is used by {@link OO.ui.CapsuleMultiSelectWidget CapsuleMultiSelectWidget}
+ * @cfg {OO.ui.Widget} [widget] Widget associated with the menu's active state. If the user clicks the mouse
+ *  anywhere on the page outside of this widget, the menu is hidden. For example, if there is a button
+ *  that toggles the menu's visibility on click, the menu will be hidden then re-shown when the user clicks
+ *  that button, unless the button (or its parent widget) is passed in here.
+ * @cfg {boolean} [autoHide=true] Hide the menu when the mouse is pressed outside the menu.
+ * @cfg {boolean} [filterFromInput=false] Filter the displayed options from the input
+ */
+OO.ui.MenuSelectWidget = function OoUiMenuSelectWidget( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Parent constructor
+       OO.ui.MenuSelectWidget.parent.call( this, config );
+
+       // Mixin constructors
+       OO.ui.mixin.ClippableElement.call( this, $.extend( {}, config, { $clippable: this.$group } ) );
+
+       // Properties
+       this.newItems = null;
+       this.autoHide = config.autoHide === undefined || !!config.autoHide;
+       this.filterFromInput = !!config.filterFromInput;
+       this.$input = config.$input ? config.$input : config.input ? config.input.$input : null;
+       this.$widget = config.widget ? config.widget.$element : null;
+       this.onDocumentMouseDownHandler = this.onDocumentMouseDown.bind( this );
+       this.onInputEditHandler = OO.ui.debounce( this.updateItemVisibility.bind( this ), 100 );
+
+       // Initialization
+       this.$element
+               .addClass( 'oo-ui-menuSelectWidget' )
+               .attr( 'role', 'menu' );
+
+       // Initially hidden - using #toggle may cause errors if subclasses override toggle with methods
+       // that reference properties not initialized at that time of parent class construction
+       // TODO: Find a better way to handle post-constructor setup
+       this.visible = false;
+       this.$element.addClass( 'oo-ui-element-hidden' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.MenuSelectWidget, OO.ui.SelectWidget );
+OO.mixinClass( OO.ui.MenuSelectWidget, OO.ui.mixin.ClippableElement );
+
+/* Methods */
+
+/**
+ * Handles document mouse down events.
+ *
+ * @protected
+ * @param {MouseEvent} e Mouse down event
+ */
+OO.ui.MenuSelectWidget.prototype.onDocumentMouseDown = function ( e ) {
+       if (
+               !OO.ui.contains( this.$element[ 0 ], e.target, true ) &&
+               ( !this.$widget || !OO.ui.contains( this.$widget[ 0 ], e.target, true ) )
+       ) {
+               this.toggle( false );
+       }
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.MenuSelectWidget.prototype.onKeyDown = function ( e ) {
+       var currentItem = this.getHighlightedItem() || this.getSelectedItem();
+
+       if ( !this.isDisabled() && this.isVisible() ) {
+               switch ( e.keyCode ) {
+                       case OO.ui.Keys.LEFT:
+                       case OO.ui.Keys.RIGHT:
+                               // Do nothing if a text field is associated, arrow keys will be handled natively
+                               if ( !this.$input ) {
+                                       OO.ui.MenuSelectWidget.parent.prototype.onKeyDown.call( this, e );
+                               }
+                               break;
+                       case OO.ui.Keys.ESCAPE:
+                       case OO.ui.Keys.TAB:
+                               if ( currentItem ) {
+                                       currentItem.setHighlighted( false );
+                               }
+                               this.toggle( false );
+                               // Don't prevent tabbing away, prevent defocusing
+                               if ( e.keyCode === OO.ui.Keys.ESCAPE ) {
+                                       e.preventDefault();
+                                       e.stopPropagation();
+                               }
+                               break;
+                       default:
+                               OO.ui.MenuSelectWidget.parent.prototype.onKeyDown.call( this, e );
+                               return;
+               }
+       }
+};
+
+/**
+ * Update menu item visibility after input changes.
+ * @protected
+ */
+OO.ui.MenuSelectWidget.prototype.updateItemVisibility = function () {
+       var i, item,
+               len = this.items.length,
+               showAll = !this.isVisible(),
+               filter = showAll ? null : this.getItemMatcher( this.$input.val() );
+
+       for ( i = 0; i < len; i++ ) {
+               item = this.items[ i ];
+               if ( item instanceof OO.ui.OptionWidget ) {
+                       item.toggle( showAll || filter( item ) );
+               }
+       }
+
+       // Reevaluate clipping
+       this.clip();
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.MenuSelectWidget.prototype.bindKeyDownListener = function () {
+       if ( this.$input ) {
+               this.$input.on( 'keydown', this.onKeyDownHandler );
+       } else {
+               OO.ui.MenuSelectWidget.parent.prototype.bindKeyDownListener.call( this );
+       }
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.MenuSelectWidget.prototype.unbindKeyDownListener = function () {
+       if ( this.$input ) {
+               this.$input.off( 'keydown', this.onKeyDownHandler );
+       } else {
+               OO.ui.MenuSelectWidget.parent.prototype.unbindKeyDownListener.call( this );
+       }
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.MenuSelectWidget.prototype.bindKeyPressListener = function () {
+       if ( this.$input ) {
+               if ( this.filterFromInput ) {
+                       this.$input.on( 'keydown mouseup cut paste change input select', this.onInputEditHandler );
+               }
+       } else {
+               OO.ui.MenuSelectWidget.parent.prototype.bindKeyPressListener.call( this );
+       }
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.MenuSelectWidget.prototype.unbindKeyPressListener = function () {
+       if ( this.$input ) {
+               if ( this.filterFromInput ) {
+                       this.$input.off( 'keydown mouseup cut paste change input select', this.onInputEditHandler );
+                       this.updateItemVisibility();
+               }
+       } else {
+               OO.ui.MenuSelectWidget.parent.prototype.unbindKeyPressListener.call( this );
+       }
+};
+
+/**
+ * Choose an item.
+ *
+ * When a user chooses an item, the menu is closed.
+ *
+ * Note that ‘choose’ should never be modified programmatically. A user can choose an option with the keyboard
+ * or mouse and it becomes selected. To select an item programmatically, use the #selectItem method.
+ * @param {OO.ui.OptionWidget} item Item to choose
+ * @chainable
+ */
+OO.ui.MenuSelectWidget.prototype.chooseItem = function ( item ) {
+       OO.ui.MenuSelectWidget.parent.prototype.chooseItem.call( this, item );
+       this.toggle( false );
+       return this;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.MenuSelectWidget.prototype.addItems = function ( items, index ) {
+       var i, len, item;
+
+       // Parent method
+       OO.ui.MenuSelectWidget.parent.prototype.addItems.call( this, items, index );
+
+       // Auto-initialize
+       if ( !this.newItems ) {
+               this.newItems = [];
+       }
+
+       for ( i = 0, len = items.length; i < len; i++ ) {
+               item = items[ i ];
+               if ( this.isVisible() ) {
+                       // Defer fitting label until item has been attached
+                       item.fitLabel();
+               } else {
+                       this.newItems.push( item );
+               }
+       }
+
+       // Reevaluate clipping
+       this.clip();
+
+       return this;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.MenuSelectWidget.prototype.removeItems = function ( items ) {
+       // Parent method
+       OO.ui.MenuSelectWidget.parent.prototype.removeItems.call( this, items );
+
+       // Reevaluate clipping
+       this.clip();
+
+       return this;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.MenuSelectWidget.prototype.clearItems = function () {
+       // Parent method
+       OO.ui.MenuSelectWidget.parent.prototype.clearItems.call( this );
+
+       // Reevaluate clipping
+       this.clip();
+
+       return this;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.MenuSelectWidget.prototype.toggle = function ( visible ) {
+       var i, len, change;
+
+       visible = ( visible === undefined ? !this.visible : !!visible ) && !!this.items.length;
+       change = visible !== this.isVisible();
+
+       // Parent method
+       OO.ui.MenuSelectWidget.parent.prototype.toggle.call( this, visible );
+
+       if ( change ) {
+               if ( visible ) {
+                       this.bindKeyDownListener();
+                       this.bindKeyPressListener();
+
+                       if ( this.newItems && this.newItems.length ) {
+                               for ( i = 0, len = this.newItems.length; i < len; i++ ) {
+                                       this.newItems[ i ].fitLabel();
+                               }
+                               this.newItems = null;
+                       }
+                       this.toggleClipping( true );
+
+                       if ( this.getSelectedItem() ) {
+                               this.getSelectedItem().scrollElementIntoView( { duration: 0 } );
+                       }
+
+                       // Auto-hide
+                       if ( this.autoHide ) {
+                               this.getElementDocument().addEventListener( 'mousedown', this.onDocumentMouseDownHandler, true );
+                       }
+               } else {
+                       this.unbindKeyDownListener();
+                       this.unbindKeyPressListener();
+                       this.getElementDocument().removeEventListener( 'mousedown', this.onDocumentMouseDownHandler, true );
+                       this.toggleClipping( false );
+               }
+       }
+
+       return this;
+};
+
+/**
+ * DropdownWidgets are not menus themselves, rather they contain a menu of options created with
+ * OO.ui.MenuOptionWidget. The DropdownWidget takes care of opening and displaying the menu so that
+ * users can interact with it.
+ *
+ * If you want to use this within a HTML form, such as a OO.ui.FormLayout, use
+ * OO.ui.DropdownInputWidget instead.
+ *
+ *     @example
+ *     // Example: A DropdownWidget with a menu that contains three options
+ *     var dropDown = new OO.ui.DropdownWidget( {
+ *         label: 'Dropdown menu: Select a menu option',
+ *         menu: {
+ *             items: [
+ *                 new OO.ui.MenuOptionWidget( {
+ *                     data: 'a',
+ *                     label: 'First'
+ *                 } ),
+ *                 new OO.ui.MenuOptionWidget( {
+ *                     data: 'b',
+ *                     label: 'Second'
+ *                 } ),
+ *                 new OO.ui.MenuOptionWidget( {
+ *                     data: 'c',
+ *                     label: 'Third'
+ *                 } )
+ *             ]
+ *         }
+ *     } );
+ *
+ *     $( 'body' ).append( dropDown.$element );
+ *
+ *     dropDown.getMenu().selectItemByData( 'b' );
+ *
+ *     dropDown.getMenu().getSelectedItem().getData(); // returns 'b'
+ *
+ * For more information, please see the [OOjs UI documentation on MediaWiki] [1].
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options#Menu_selects_and_options
+ *
+ * @class
+ * @extends OO.ui.Widget
+ * @mixins OO.ui.mixin.IconElement
+ * @mixins OO.ui.mixin.IndicatorElement
+ * @mixins OO.ui.mixin.LabelElement
+ * @mixins OO.ui.mixin.TitledElement
+ * @mixins OO.ui.mixin.TabIndexedElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {Object} [menu] Configuration options to pass to {@link OO.ui.FloatingMenuSelectWidget menu select widget}
+ * @cfg {jQuery} [$overlay] Render the menu into a separate layer. This configuration is useful in cases where
+ *  the expanded menu is larger than its containing `<div>`. The specified overlay layer is usually on top of the
+ *  containing `<div>` and has a larger area. By default, the menu uses relative positioning.
+ */
+OO.ui.DropdownWidget = function OoUiDropdownWidget( config ) {
+       // Configuration initialization
+       config = $.extend( { indicator: 'down' }, config );
+
+       // Parent constructor
+       OO.ui.DropdownWidget.parent.call( this, config );
+
+       // Properties (must be set before TabIndexedElement constructor call)
+       this.$handle = this.$( '<span>' );
+       this.$overlay = config.$overlay || this.$element;
+
+       // Mixin constructors
+       OO.ui.mixin.IconElement.call( this, config );
+       OO.ui.mixin.IndicatorElement.call( this, config );
+       OO.ui.mixin.LabelElement.call( this, config );
+       OO.ui.mixin.TitledElement.call( this, $.extend( {}, config, { $titled: this.$label } ) );
+       OO.ui.mixin.TabIndexedElement.call( this, $.extend( {}, config, { $tabIndexed: this.$handle } ) );
+
+       // Properties
+       this.menu = new OO.ui.FloatingMenuSelectWidget( $.extend( {
+               widget: this,
+               $container: this.$element
+       }, config.menu ) );
+
+       // Events
+       this.$handle.on( {
+               click: this.onClick.bind( this ),
+               keydown: this.onKeyDown.bind( this )
+       } );
+       this.menu.connect( this, { select: 'onMenuSelect' } );
+
+       // Initialization
+       this.$handle
+               .addClass( 'oo-ui-dropdownWidget-handle' )
+               .append( this.$icon, this.$label, this.$indicator );
+       this.$element
+               .addClass( 'oo-ui-dropdownWidget' )
+               .append( this.$handle );
+       this.$overlay.append( this.menu.$element );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.DropdownWidget, OO.ui.Widget );
+OO.mixinClass( OO.ui.DropdownWidget, OO.ui.mixin.IconElement );
+OO.mixinClass( OO.ui.DropdownWidget, OO.ui.mixin.IndicatorElement );
+OO.mixinClass( OO.ui.DropdownWidget, OO.ui.mixin.LabelElement );
+OO.mixinClass( OO.ui.DropdownWidget, OO.ui.mixin.TitledElement );
+OO.mixinClass( OO.ui.DropdownWidget, OO.ui.mixin.TabIndexedElement );
+
+/* Methods */
+
+/**
+ * Get the menu.
+ *
+ * @return {OO.ui.MenuSelectWidget} Menu of widget
+ */
+OO.ui.DropdownWidget.prototype.getMenu = function () {
+       return this.menu;
+};
+
+/**
+ * Handles menu select events.
+ *
+ * @private
+ * @param {OO.ui.MenuOptionWidget} item Selected menu item
+ */
+OO.ui.DropdownWidget.prototype.onMenuSelect = function ( item ) {
+       var selectedLabel;
+
+       if ( !item ) {
+               this.setLabel( null );
+               return;
+       }
+
+       selectedLabel = item.getLabel();
+
+       // If the label is a DOM element, clone it, because setLabel will append() it
+       if ( selectedLabel instanceof jQuery ) {
+               selectedLabel = selectedLabel.clone();
+       }
+
+       this.setLabel( selectedLabel );
+};
+
+/**
+ * Handle mouse click events.
+ *
+ * @private
+ * @param {jQuery.Event} e Mouse click event
+ */
+OO.ui.DropdownWidget.prototype.onClick = function ( e ) {
+       if ( !this.isDisabled() && e.which === OO.ui.MouseButtons.LEFT ) {
+               this.menu.toggle();
+       }
+       return false;
+};
+
+/**
+ * Handle key down events.
+ *
+ * @private
+ * @param {jQuery.Event} e Key down event
+ */
+OO.ui.DropdownWidget.prototype.onKeyDown = function ( e ) {
+       if (
+               !this.isDisabled() &&
+               (
+                       e.which === OO.ui.Keys.ENTER ||
+                       (
+                               !this.menu.isVisible() &&
+                               (
+                                       e.which === OO.ui.Keys.SPACE ||
+                                       e.which === OO.ui.Keys.UP ||
+                                       e.which === OO.ui.Keys.DOWN
+                               )
+                       )
+               )
+       ) {
+               this.menu.toggle();
+               return false;
+       }
+};
+
+/**
+ * RadioOptionWidget is an option widget that looks like a radio button.
+ * The class is used with OO.ui.RadioSelectWidget to create a selection of radio options.
+ * Please see the [OOjs UI documentation on MediaWiki] [1] for more information.
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options#Button_selects_and_option
+ *
+ * @class
+ * @extends OO.ui.OptionWidget
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.RadioOptionWidget = function OoUiRadioOptionWidget( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Properties (must be done before parent constructor which calls #setDisabled)
+       this.radio = new OO.ui.RadioInputWidget( { value: config.data, tabIndex: -1 } );
+
+       // Parent constructor
+       OO.ui.RadioOptionWidget.parent.call( this, config );
+
+       // Events
+       this.radio.$input.on( 'focus', this.onInputFocus.bind( this ) );
+
+       // Initialization
+       // Remove implicit role, we're handling it ourselves
+       this.radio.$input.attr( 'role', 'presentation' );
+       this.$element
+               .addClass( 'oo-ui-radioOptionWidget' )
+               .attr( 'role', 'radio' )
+               .attr( 'aria-checked', 'false' )
+               .removeAttr( 'aria-selected' )
+               .prepend( this.radio.$element );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.RadioOptionWidget, OO.ui.OptionWidget );
+
+/* Static Properties */
+
+OO.ui.RadioOptionWidget.static.highlightable = false;
+
+OO.ui.RadioOptionWidget.static.scrollIntoViewOnSelect = true;
+
+OO.ui.RadioOptionWidget.static.pressable = false;
+
+OO.ui.RadioOptionWidget.static.tagName = 'label';
+
+/* Methods */
+
+/**
+ * @param {jQuery.Event} e Focus event
+ * @private
+ */
+OO.ui.RadioOptionWidget.prototype.onInputFocus = function () {
+       this.radio.$input.blur();
+       this.$element.parent().focus();
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.RadioOptionWidget.prototype.setSelected = function ( state ) {
+       OO.ui.RadioOptionWidget.parent.prototype.setSelected.call( this, state );
+
+       this.radio.setSelected( state );
+       this.$element
+               .attr( 'aria-checked', state.toString() )
+               .removeAttr( 'aria-selected' );
+
+       return this;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.RadioOptionWidget.prototype.setDisabled = function ( disabled ) {
+       OO.ui.RadioOptionWidget.parent.prototype.setDisabled.call( this, disabled );
+
+       this.radio.setDisabled( this.isDisabled() );
+
+       return this;
+};
+
+/**
+ * RadioSelectWidget is a {@link OO.ui.SelectWidget select widget} that contains radio
+ * options and is used together with OO.ui.RadioOptionWidget. The RadioSelectWidget provides
+ * an interface for adding, removing and selecting options.
+ * Please see the [OOjs UI documentation on MediaWiki][1] for more information.
+ *
+ * If you want to use this within a HTML form, such as a OO.ui.FormLayout, use
+ * OO.ui.RadioSelectInputWidget instead.
+ *
+ *     @example
+ *     // A RadioSelectWidget with RadioOptions.
+ *     var option1 = new OO.ui.RadioOptionWidget( {
+ *         data: 'a',
+ *         label: 'Selected radio option'
+ *     } );
+ *
+ *     var option2 = new OO.ui.RadioOptionWidget( {
+ *         data: 'b',
+ *         label: 'Unselected radio option'
+ *     } );
+ *
+ *     var radioSelect=new OO.ui.RadioSelectWidget( {
+ *         items: [ option1, option2 ]
+ *      } );
+ *
+ *     // Select 'option 1' using the RadioSelectWidget's selectItem() method.
+ *     radioSelect.selectItem( option1 );
+ *
+ *     $( 'body' ).append( radioSelect.$element );
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options
+
+ *
+ * @class
+ * @extends OO.ui.SelectWidget
+ * @mixins OO.ui.mixin.TabIndexedElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.RadioSelectWidget = function OoUiRadioSelectWidget( config ) {
+       // Parent constructor
+       OO.ui.RadioSelectWidget.parent.call( this, config );
+
+       // Mixin constructors
+       OO.ui.mixin.TabIndexedElement.call( this, config );
+
+       // Events
+       this.$element.on( {
+               focus: this.bindKeyDownListener.bind( this ),
+               blur: this.unbindKeyDownListener.bind( this )
+       } );
+
+       // Initialization
+       this.$element
+               .addClass( 'oo-ui-radioSelectWidget' )
+               .attr( 'role', 'radiogroup' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.RadioSelectWidget, OO.ui.SelectWidget );
+OO.mixinClass( OO.ui.RadioSelectWidget, OO.ui.mixin.TabIndexedElement );
+
+/**
+ * Element that will stick under a specified container, even when it is inserted elsewhere in the
+ * document (for example, in a OO.ui.Window's $overlay).
+ *
+ * The elements's position is automatically calculated and maintained when window is resized or the
+ * page is scrolled. If you reposition the container manually, you have to call #position to make
+ * sure the element is still placed correctly.
+ *
+ * As positioning is only possible when both the element and the container are attached to the DOM
+ * and visible, it's only done after you call #togglePositioning. You might want to do this inside
+ * the #toggle method to display a floating popup, for example.
+ *
+ * @abstract
+ * @class
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {jQuery} [$floatable] Node to position, assigned to #$floatable, omit to use #$element
+ * @cfg {jQuery} [$floatableContainer] Node to position below
+ */
+OO.ui.mixin.FloatableElement = function OoUiMixinFloatableElement( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Properties
+       this.$floatable = null;
+       this.$floatableContainer = null;
+       this.$floatableWindow = null;
+       this.$floatableClosestScrollable = null;
+       this.onFloatableScrollHandler = this.position.bind( this );
+       this.onFloatableWindowResizeHandler = this.position.bind( this );
+
+       // Initialization
+       this.setFloatableContainer( config.$floatableContainer );
+       this.setFloatableElement( config.$floatable || this.$element );
+};
+
+/* Methods */
+
+/**
+ * Set floatable element.
+ *
+ * If an element is already set, it will be cleaned up before setting up the new element.
+ *
+ * @param {jQuery} $floatable Element to make floatable
+ */
+OO.ui.mixin.FloatableElement.prototype.setFloatableElement = function ( $floatable ) {
+       if ( this.$floatable ) {
+               this.$floatable.removeClass( 'oo-ui-floatableElement-floatable' );
+               this.$floatable.css( { left: '', top: '' } );
+       }
+
+       this.$floatable = $floatable.addClass( 'oo-ui-floatableElement-floatable' );
+       this.position();
+};
+
+/**
+ * Set floatable container.
+ *
+ * The element will be always positioned under the specified container.
+ *
+ * @param {jQuery|null} $floatableContainer Container to keep visible, or null to unset
+ */
+OO.ui.mixin.FloatableElement.prototype.setFloatableContainer = function ( $floatableContainer ) {
+       this.$floatableContainer = $floatableContainer;
+       if ( this.$floatable ) {
+               this.position();
+       }
+};
+
+/**
+ * Toggle positioning.
+ *
+ * Do not turn positioning on until after the element is attached to the DOM and visible.
+ *
+ * @param {boolean} [positioning] Enable positioning, omit to toggle
+ * @chainable
+ */
+OO.ui.mixin.FloatableElement.prototype.togglePositioning = function ( positioning ) {
+       var closestScrollableOfContainer, closestScrollableOfFloatable;
+
+       positioning = positioning === undefined ? !this.positioning : !!positioning;
+
+       if ( this.positioning !== positioning ) {
+               this.positioning = positioning;
+
+               closestScrollableOfContainer = OO.ui.Element.static.getClosestScrollableContainer( this.$floatableContainer[ 0 ] );
+               closestScrollableOfFloatable = OO.ui.Element.static.getClosestScrollableContainer( this.$floatable[ 0 ] );
+               if ( closestScrollableOfContainer !== closestScrollableOfFloatable ) {
+                       // If the scrollable is the root, we have to listen to scroll events
+                       // on the window because of browser inconsistencies (or do we? someone should verify this)
+                       if ( $( closestScrollableOfContainer ).is( 'html, body' ) ) {
+                               closestScrollableOfContainer = OO.ui.Element.static.getWindow( closestScrollableOfContainer );
+                       }
+               }
+
+               if ( positioning ) {
+                       this.$floatableWindow = $( this.getElementWindow() );
+                       this.$floatableWindow.on( 'resize', this.onFloatableWindowResizeHandler );
+
+                       if ( closestScrollableOfContainer !== closestScrollableOfFloatable ) {
+                               this.$floatableClosestScrollable = $( closestScrollableOfContainer );
+                               this.$floatableClosestScrollable.on( 'scroll', this.onFloatableScrollHandler );
+                       }
+
+                       // Initial position after visible
+                       this.position();
+               } else {
+                       if ( this.$floatableWindow ) {
+                               this.$floatableWindow.off( 'resize', this.onFloatableWindowResizeHandler );
+                               this.$floatableWindow = null;
+                       }
+
+                       if ( this.$floatableClosestScrollable ) {
+                               this.$floatableClosestScrollable.off( 'scroll', this.onFloatableScrollHandler );
+                               this.$floatableClosestScrollable = null;
+                       }
+
+                       this.$floatable.css( { left: '', top: '' } );
+               }
+       }
+
+       return this;
+};
+
+/**
+ * Position the floatable below its container.
+ *
+ * This should only be done when both of them are attached to the DOM and visible.
+ *
+ * @chainable
+ */
+OO.ui.mixin.FloatableElement.prototype.position = function () {
+       var pos;
+
+       if ( !this.positioning ) {
+               return this;
+       }
+
+       pos = OO.ui.Element.static.getRelativePosition( this.$floatableContainer, this.$floatable.offsetParent() );
+
+       // Position under container
+       pos.top += this.$floatableContainer.height();
+       this.$floatable.css( pos );
+
+       // We updated the position, so re-evaluate the clipping state.
+       // (ClippableElement does not listen to 'scroll' events on $floatableContainer's parent, and so
+       // will not notice the need to update itself.)
+       // TODO: This is terrible, we shouldn't need to know about ClippableElement at all here. Why does
+       // it not listen to the right events in the right places?
+       if ( this.clip ) {
+               this.clip();
+       }
+
+       return this;
+};
+
+/**
+ * FloatingMenuSelectWidget is a menu that will stick under a specified
+ * container, even when it is inserted elsewhere in the document (for example,
+ * in a OO.ui.Window's $overlay). This is sometimes necessary to prevent the
+ * menu from being clipped too aggresively.
+ *
+ * The menu's position is automatically calculated and maintained when the menu
+ * is toggled or the window is resized.
+ *
+ * See OO.ui.ComboBoxInputWidget for an example of a widget that uses this class.
+ *
+ * @class
+ * @extends OO.ui.MenuSelectWidget
+ * @mixins OO.ui.mixin.FloatableElement
+ *
+ * @constructor
+ * @param {OO.ui.Widget} [inputWidget] Widget to provide the menu for.
+ *   Deprecated, omit this parameter and specify `$container` instead.
+ * @param {Object} [config] Configuration options
+ * @cfg {jQuery} [$container=inputWidget.$element] Element to render menu under
+ */
+OO.ui.FloatingMenuSelectWidget = function OoUiFloatingMenuSelectWidget( inputWidget, config ) {
+       // Allow 'inputWidget' parameter and config for backwards compatibility
+       if ( OO.isPlainObject( inputWidget ) && config === undefined ) {
+               config = inputWidget;
+               inputWidget = config.inputWidget;
+       }
+
+       // Configuration initialization
+       config = config || {};
+
+       // Parent constructor
+       OO.ui.FloatingMenuSelectWidget.parent.call( this, config );
+
+       // Properties (must be set before mixin constructors)
+       this.inputWidget = inputWidget; // For backwards compatibility
+       this.$container = config.$container || this.inputWidget.$element;
+
+       // Mixins constructors
+       OO.ui.mixin.FloatableElement.call( this, $.extend( {}, config, { $floatableContainer: this.$container } ) );
+
+       // Initialization
+       this.$element.addClass( 'oo-ui-floatingMenuSelectWidget' );
+       // For backwards compatibility
+       this.$element.addClass( 'oo-ui-textInputMenuSelectWidget' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.FloatingMenuSelectWidget, OO.ui.MenuSelectWidget );
+OO.mixinClass( OO.ui.FloatingMenuSelectWidget, OO.ui.mixin.FloatableElement );
+
+// For backwards compatibility
+OO.ui.TextInputMenuSelectWidget = OO.ui.FloatingMenuSelectWidget;
+
+/* Methods */
+
+/**
+ * @inheritdoc
+ */
+OO.ui.FloatingMenuSelectWidget.prototype.toggle = function ( visible ) {
+       var change;
+       visible = visible === undefined ? !this.isVisible() : !!visible;
+       change = visible !== this.isVisible();
+
+       if ( change && visible ) {
+               // Make sure the width is set before the parent method runs.
+               this.setIdealSize( this.$container.width() );
+       }
+
+       // Parent method
+       // This will call this.clip(), which is nonsensical since we're not positioned yet...
+       OO.ui.FloatingMenuSelectWidget.parent.prototype.toggle.call( this, visible );
+
+       if ( change ) {
+               this.togglePositioning( this.isVisible() );
+       }
+
+       return this;
+};
+
+/**
+ * InputWidget is the base class for all input widgets, which
+ * include {@link OO.ui.TextInputWidget text inputs}, {@link OO.ui.CheckboxInputWidget checkbox inputs},
+ * {@link OO.ui.RadioInputWidget radio inputs}, and {@link OO.ui.ButtonInputWidget button inputs}.
+ * See the [OOjs UI documentation on MediaWiki] [1] for more information and examples.
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Inputs
+ *
+ * @abstract
+ * @class
+ * @extends OO.ui.Widget
+ * @mixins OO.ui.mixin.FlaggedElement
+ * @mixins OO.ui.mixin.TabIndexedElement
+ * @mixins OO.ui.mixin.TitledElement
+ * @mixins OO.ui.mixin.AccessKeyedElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {string} [name=''] The value of the input’s HTML `name` attribute.
+ * @cfg {string} [value=''] The value of the input.
+ * @cfg {string} [dir] The directionality of the input (ltr/rtl).
+ * @cfg {Function} [inputFilter] The name of an input filter function. Input filters modify the value of an input
+ *  before it is accepted.
+ */
+OO.ui.InputWidget = function OoUiInputWidget( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Parent constructor
+       OO.ui.InputWidget.parent.call( this, config );
+
+       // Properties
+       this.$input = this.getInputElement( config );
+       this.value = '';
+       this.inputFilter = config.inputFilter;
+
+       // Mixin constructors
+       OO.ui.mixin.FlaggedElement.call( this, config );
+       OO.ui.mixin.TabIndexedElement.call( this, $.extend( {}, config, { $tabIndexed: this.$input } ) );
+       OO.ui.mixin.TitledElement.call( this, $.extend( {}, config, { $titled: this.$input } ) );
+       OO.ui.mixin.AccessKeyedElement.call( this, $.extend( {}, config, { $accessKeyed: this.$input } ) );
+
+       // Events
+       this.$input.on( 'keydown mouseup cut paste change input select', this.onEdit.bind( this ) );
+
+       // Initialization
+       this.$input
+               .addClass( 'oo-ui-inputWidget-input' )
+               .attr( 'name', config.name )
+               .prop( 'disabled', this.isDisabled() );
+       this.$element
+               .addClass( 'oo-ui-inputWidget' )
+               .append( this.$input );
+       this.setValue( config.value );
+       if ( config.dir ) {
+               this.setDir( config.dir );
+       }
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.InputWidget, OO.ui.Widget );
+OO.mixinClass( OO.ui.InputWidget, OO.ui.mixin.FlaggedElement );
+OO.mixinClass( OO.ui.InputWidget, OO.ui.mixin.TabIndexedElement );
+OO.mixinClass( OO.ui.InputWidget, OO.ui.mixin.TitledElement );
+OO.mixinClass( OO.ui.InputWidget, OO.ui.mixin.AccessKeyedElement );
+
+/* Static Properties */
+
+OO.ui.InputWidget.static.supportsSimpleLabel = true;
+
+/* Static Methods */
+
+/**
+ * @inheritdoc
+ */
+OO.ui.InputWidget.static.reusePreInfuseDOM = function ( node, config ) {
+       config = OO.ui.InputWidget.parent.static.reusePreInfuseDOM( node, config );
+       // Reusing $input lets browsers preserve inputted values across page reloads (T114134)
+       config.$input = $( node ).find( '.oo-ui-inputWidget-input' );
+       return config;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.InputWidget.static.gatherPreInfuseState = function ( node, config ) {
+       var state = OO.ui.InputWidget.parent.static.gatherPreInfuseState( node, config );
+       state.value = config.$input.val();
+       // Might be better in TabIndexedElement, but it's awkward to do there because mixins are awkward
+       state.focus = config.$input.is( ':focus' );
+       return state;
+};
+
+/* Events */
+
+/**
+ * @event change
+ *
+ * A change event is emitted when the value of the input changes.
+ *
+ * @param {string} value
+ */
+
+/* Methods */
+
+/**
+ * Get input element.
+ *
+ * Subclasses of OO.ui.InputWidget use the `config` parameter to produce different elements in
+ * different circumstances. The element must have a `value` property (like form elements).
+ *
+ * @protected
+ * @param {Object} config Configuration options
+ * @return {jQuery} Input element
+ */
+OO.ui.InputWidget.prototype.getInputElement = function ( config ) {
+       // See #reusePreInfuseDOM about config.$input
+       return config.$input || $( '<input>' );
+};
+
+/**
+ * Handle potentially value-changing events.
+ *
+ * @private
+ * @param {jQuery.Event} e Key down, mouse up, cut, paste, change, input, or select event
+ */
+OO.ui.InputWidget.prototype.onEdit = function () {
+       var widget = this;
+       if ( !this.isDisabled() ) {
+               // Allow the stack to clear so the value will be updated
+               setTimeout( function () {
+                       widget.setValue( widget.$input.val() );
+               } );
+       }
+};
+
+/**
+ * Get the value of the input.
+ *
+ * @return {string} Input value
+ */
+OO.ui.InputWidget.prototype.getValue = function () {
+       // Resynchronize our internal data with DOM data. Other scripts executing on the page can modify
+       // it, and we won't know unless they're kind enough to trigger a 'change' event.
+       var value = this.$input.val();
+       if ( this.value !== value ) {
+               this.setValue( value );
+       }
+       return this.value;
+};
+
+/**
+ * Set the directionality of the input, either RTL (right-to-left) or LTR (left-to-right).
+ *
+ * @deprecated since v0.13.1, use #setDir directly
+ * @param {boolean} isRTL Directionality is right-to-left
+ * @chainable
+ */
+OO.ui.InputWidget.prototype.setRTL = function ( isRTL ) {
+       this.setDir( isRTL ? 'rtl' : 'ltr' );
+       return this;
+};
+
+/**
+ * Set the directionality of the input.
+ *
+ * @param {string} dir Text directionality: 'ltr', 'rtl' or 'auto'
+ * @chainable
+ */
+OO.ui.InputWidget.prototype.setDir = function ( dir ) {
+       this.$input.prop( 'dir', dir );
+       return this;
+};
+
+/**
+ * Set the value of the input.
+ *
+ * @param {string} value New value
+ * @fires change
+ * @chainable
+ */
+OO.ui.InputWidget.prototype.setValue = function ( value ) {
+       value = this.cleanUpValue( value );
+       // Update the DOM if it has changed. Note that with cleanUpValue, it
+       // is possible for the DOM value to change without this.value changing.
+       if ( this.$input.val() !== value ) {
+               this.$input.val( value );
+       }
+       if ( this.value !== value ) {
+               this.value = value;
+               this.emit( 'change', this.value );
+       }
+       return this;
+};
+
+/**
+ * Clean up incoming value.
+ *
+ * Ensures value is a string, and converts undefined and null to empty string.
+ *
+ * @private
+ * @param {string} value Original value
+ * @return {string} Cleaned up value
+ */
+OO.ui.InputWidget.prototype.cleanUpValue = function ( value ) {
+       if ( value === undefined || value === null ) {
+               return '';
+       } else if ( this.inputFilter ) {
+               return this.inputFilter( String( value ) );
+       } else {
+               return String( value );
+       }
+};
+
+/**
+ * Simulate the behavior of clicking on a label bound to this input. This method is only called by
+ * {@link OO.ui.LabelWidget LabelWidget} and {@link OO.ui.FieldLayout FieldLayout}. It should not be
+ * called directly.
+ */
+OO.ui.InputWidget.prototype.simulateLabelClick = function () {
+       if ( !this.isDisabled() ) {
+               if ( this.$input.is( ':checkbox, :radio' ) ) {
+                       this.$input.click();
+               }
+               if ( this.$input.is( ':input' ) ) {
+                       this.$input[ 0 ].focus();
+               }
+       }
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.InputWidget.prototype.setDisabled = function ( state ) {
+       OO.ui.InputWidget.parent.prototype.setDisabled.call( this, state );
+       if ( this.$input ) {
+               this.$input.prop( 'disabled', this.isDisabled() );
+       }
+       return this;
+};
+
+/**
+ * Focus the input.
+ *
+ * @chainable
+ */
+OO.ui.InputWidget.prototype.focus = function () {
+       this.$input[ 0 ].focus();
+       return this;
+};
+
+/**
+ * Blur the input.
+ *
+ * @chainable
+ */
+OO.ui.InputWidget.prototype.blur = function () {
+       this.$input[ 0 ].blur();
+       return this;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.InputWidget.prototype.restorePreInfuseState = function ( state ) {
+       OO.ui.InputWidget.parent.prototype.restorePreInfuseState.call( this, state );
+       if ( state.value !== undefined && state.value !== this.getValue() ) {
+               this.setValue( state.value );
+       }
+       if ( state.focus ) {
+               this.focus();
+       }
+};
+
+/**
+ * ButtonInputWidget is used to submit HTML forms and is intended to be used within
+ * a OO.ui.FormLayout. If you do not need the button to work with HTML forms, you probably
+ * want to use OO.ui.ButtonWidget instead. Button input widgets can be rendered as either an
+ * HTML `<button/>` (the default) or an HTML `<input/>` tags. See the
+ * [OOjs UI documentation on MediaWiki] [1] for more information.
+ *
+ *     @example
+ *     // A ButtonInputWidget rendered as an HTML button, the default.
+ *     var button = new OO.ui.ButtonInputWidget( {
+ *         label: 'Input button',
+ *         icon: 'check',
+ *         value: 'check'
+ *     } );
+ *     $( 'body' ).append( button.$element );
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Inputs#Button_inputs
+ *
+ * @class
+ * @extends OO.ui.InputWidget
+ * @mixins OO.ui.mixin.ButtonElement
+ * @mixins OO.ui.mixin.IconElement
+ * @mixins OO.ui.mixin.IndicatorElement
+ * @mixins OO.ui.mixin.LabelElement
+ * @mixins OO.ui.mixin.TitledElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {string} [type='button'] The value of the HTML `'type'` attribute: 'button', 'submit' or 'reset'.
+ * @cfg {boolean} [useInputTag=false] Use an `<input/>` tag instead of a `<button/>` tag, the default.
+ *  Widgets configured to be an `<input/>` do not support {@link #icon icons} and {@link #indicator indicators},
+ *  non-plaintext {@link #label labels}, or {@link #value values}. In general, useInputTag should only
+ *  be set to `true` when there’s need to support IE6 in a form with multiple buttons.
+ */
+OO.ui.ButtonInputWidget = function OoUiButtonInputWidget( config ) {
+       // Configuration initialization
+       config = $.extend( { type: 'button', useInputTag: false }, config );
+
+       // Properties (must be set before parent constructor, which calls #setValue)
+       this.useInputTag = config.useInputTag;
+
+       // Parent constructor
+       OO.ui.ButtonInputWidget.parent.call( this, config );
+
+       // Mixin constructors
+       OO.ui.mixin.ButtonElement.call( this, $.extend( {}, config, { $button: this.$input } ) );
+       OO.ui.mixin.IconElement.call( this, config );
+       OO.ui.mixin.IndicatorElement.call( this, config );
+       OO.ui.mixin.LabelElement.call( this, config );
+       OO.ui.mixin.TitledElement.call( this, $.extend( {}, config, { $titled: this.$input } ) );
+
+       // Initialization
+       if ( !config.useInputTag ) {
+               this.$input.append( this.$icon, this.$label, this.$indicator );
+       }
+       this.$element.addClass( 'oo-ui-buttonInputWidget' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.ButtonInputWidget, OO.ui.InputWidget );
+OO.mixinClass( OO.ui.ButtonInputWidget, OO.ui.mixin.ButtonElement );
+OO.mixinClass( OO.ui.ButtonInputWidget, OO.ui.mixin.IconElement );
+OO.mixinClass( OO.ui.ButtonInputWidget, OO.ui.mixin.IndicatorElement );
+OO.mixinClass( OO.ui.ButtonInputWidget, OO.ui.mixin.LabelElement );
+OO.mixinClass( OO.ui.ButtonInputWidget, OO.ui.mixin.TitledElement );
+
+/* Static Properties */
+
+/**
+ * Disable generating `<label>` elements for buttons. One would very rarely need additional label
+ * for a button, and it's already a big clickable target, and it causes unexpected rendering.
+ */
+OO.ui.ButtonInputWidget.static.supportsSimpleLabel = false;
+
+/* Methods */
+
+/**
+ * @inheritdoc
+ * @protected
+ */
+OO.ui.ButtonInputWidget.prototype.getInputElement = function ( config ) {
+       var type;
+       // See InputWidget#reusePreInfuseDOM about config.$input
+       if ( config.$input ) {
+               return config.$input.empty();
+       }
+       type = [ 'button', 'submit', 'reset' ].indexOf( config.type ) !== -1 ? config.type : 'button';
+       return $( '<' + ( config.useInputTag ? 'input' : 'button' ) + ' type="' + type + '">' );
+};
+
+/**
+ * Set label value.
+ *
+ * If #useInputTag is `true`, the label is set as the `value` of the `<input/>` tag.
+ *
+ * @param {jQuery|string|Function|null} label Label nodes, text, a function that returns nodes or
+ *  text, or `null` for no label
+ * @chainable
+ */
+OO.ui.ButtonInputWidget.prototype.setLabel = function ( label ) {
+       OO.ui.mixin.LabelElement.prototype.setLabel.call( this, label );
+
+       if ( this.useInputTag ) {
+               if ( typeof label === 'function' ) {
+                       label = OO.ui.resolveMsg( label );
+               }
+               if ( label instanceof jQuery ) {
+                       label = label.text();
+               }
+               if ( !label ) {
+                       label = '';
+               }
+               this.$input.val( label );
+       }
+
+       return this;
+};
+
+/**
+ * Set the value of the input.
+ *
+ * This method is disabled for button inputs configured as {@link #useInputTag <input/> tags}, as
+ * they do not support {@link #value values}.
+ *
+ * @param {string} value New value
+ * @chainable
+ */
+OO.ui.ButtonInputWidget.prototype.setValue = function ( value ) {
+       if ( !this.useInputTag ) {
+               OO.ui.ButtonInputWidget.parent.prototype.setValue.call( this, value );
+       }
+       return this;
+};
+
+/**
+ * CheckboxInputWidgets, like HTML checkboxes, can be selected and/or configured with a value.
+ * Note that these {@link OO.ui.InputWidget input widgets} are best laid out
+ * in {@link OO.ui.FieldLayout field layouts} that use the {@link OO.ui.FieldLayout#align inline}
+ * alignment. For more information, please see the [OOjs UI documentation on MediaWiki][1].
+ *
+ * This widget can be used inside a HTML form, such as a OO.ui.FormLayout.
+ *
+ *     @example
+ *     // An example of selected, unselected, and disabled checkbox inputs
+ *     var checkbox1=new OO.ui.CheckboxInputWidget( {
+ *          value: 'a',
+ *          selected: true
+ *     } );
+ *     var checkbox2=new OO.ui.CheckboxInputWidget( {
+ *         value: 'b'
+ *     } );
+ *     var checkbox3=new OO.ui.CheckboxInputWidget( {
+ *         value:'c',
+ *         disabled: true
+ *     } );
+ *     // Create a fieldset layout with fields for each checkbox.
+ *     var fieldset = new OO.ui.FieldsetLayout( {
+ *         label: 'Checkboxes'
+ *     } );
+ *     fieldset.addItems( [
+ *         new OO.ui.FieldLayout( checkbox1, { label: 'Selected checkbox', align: 'inline' } ),
+ *         new OO.ui.FieldLayout( checkbox2, { label: 'Unselected checkbox', align: 'inline' } ),
+ *         new OO.ui.FieldLayout( checkbox3, { label: 'Disabled checkbox', align: 'inline' } ),
+ *     ] );
+ *     $( 'body' ).append( fieldset.$element );
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Inputs
+ *
+ * @class
+ * @extends OO.ui.InputWidget
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {boolean} [selected=false] Select the checkbox initially. By default, the checkbox is not selected.
+ */
+OO.ui.CheckboxInputWidget = function OoUiCheckboxInputWidget( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Parent constructor
+       OO.ui.CheckboxInputWidget.parent.call( this, config );
+
+       // Initialization
+       this.$element
+               .addClass( 'oo-ui-checkboxInputWidget' )
+               // Required for pretty styling in MediaWiki theme
+               .append( $( '<span>' ) );
+       this.setSelected( config.selected !== undefined ? config.selected : false );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.CheckboxInputWidget, OO.ui.InputWidget );
+
+/* Static Methods */
+
+/**
+ * @inheritdoc
+ */
+OO.ui.CheckboxInputWidget.static.gatherPreInfuseState = function ( node, config ) {
+       var state = OO.ui.CheckboxInputWidget.parent.static.gatherPreInfuseState( node, config );
+       state.checked = config.$input.prop( 'checked' );
+       return state;
+};
+
+/* Methods */
+
+/**
+ * @inheritdoc
+ * @protected
+ */
+OO.ui.CheckboxInputWidget.prototype.getInputElement = function () {
+       return $( '<input type="checkbox" />' );
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.CheckboxInputWidget.prototype.onEdit = function () {
+       var widget = this;
+       if ( !this.isDisabled() ) {
+               // Allow the stack to clear so the value will be updated
+               setTimeout( function () {
+                       widget.setSelected( widget.$input.prop( 'checked' ) );
+               } );
+       }
+};
+
+/**
+ * Set selection state of this checkbox.
+ *
+ * @param {boolean} state `true` for selected
+ * @chainable
+ */
+OO.ui.CheckboxInputWidget.prototype.setSelected = function ( state ) {
+       state = !!state;
+       if ( this.selected !== state ) {
+               this.selected = state;
+               this.$input.prop( 'checked', this.selected );
+               this.emit( 'change', this.selected );
+       }
+       return this;
+};
+
+/**
+ * Check if this checkbox is selected.
+ *
+ * @return {boolean} Checkbox is selected
+ */
+OO.ui.CheckboxInputWidget.prototype.isSelected = function () {
+       // Resynchronize our internal data with DOM data. Other scripts executing on the page can modify
+       // it, and we won't know unless they're kind enough to trigger a 'change' event.
+       var selected = this.$input.prop( 'checked' );
+       if ( this.selected !== selected ) {
+               this.setSelected( selected );
+       }
+       return this.selected;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.CheckboxInputWidget.prototype.restorePreInfuseState = function ( state ) {
+       OO.ui.CheckboxInputWidget.parent.prototype.restorePreInfuseState.call( this, state );
+       if ( state.checked !== undefined && state.checked !== this.isSelected() ) {
+               this.setSelected( state.checked );
+       }
+};
+
+/**
+ * DropdownInputWidget is a {@link OO.ui.DropdownWidget DropdownWidget} intended to be used
+ * within a HTML form, such as a OO.ui.FormLayout. The selected value is synchronized with the value
+ * of a hidden HTML `input` tag. Please see the [OOjs UI documentation on MediaWiki][1] for
+ * more information about input widgets.
+ *
+ * A DropdownInputWidget always has a value (one of the options is always selected), unless there
+ * are no options. If no `value` configuration option is provided, the first option is selected.
+ * If you need a state representing no value (no option being selected), use a DropdownWidget.
+ *
+ * This and OO.ui.RadioSelectInputWidget support the same configuration options.
+ *
+ *     @example
+ *     // Example: A DropdownInputWidget with three options
+ *     var dropdownInput = new OO.ui.DropdownInputWidget( {
+ *         options: [
+ *             { data: 'a', label: 'First' },
+ *             { data: 'b', label: 'Second'},
+ *             { data: 'c', label: 'Third' }
+ *         ]
+ *     } );
+ *     $( 'body' ).append( dropdownInput.$element );
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Inputs
+ *
+ * @class
+ * @extends OO.ui.InputWidget
+ * @mixins OO.ui.mixin.TitledElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {Object[]} [options=[]] Array of menu options in the format `{ data: …, label: … }`
+ * @cfg {Object} [dropdown] Configuration options for {@link OO.ui.DropdownWidget DropdownWidget}
+ */
+OO.ui.DropdownInputWidget = function OoUiDropdownInputWidget( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Properties (must be done before parent constructor which calls #setDisabled)
+       this.dropdownWidget = new OO.ui.DropdownWidget( config.dropdown );
+
+       // Parent constructor
+       OO.ui.DropdownInputWidget.parent.call( this, config );
+
+       // Mixin constructors
+       OO.ui.mixin.TitledElement.call( this, config );
+
+       // Events
+       this.dropdownWidget.getMenu().connect( this, { select: 'onMenuSelect' } );
+
+       // Initialization
+       this.setOptions( config.options || [] );
+       this.$element
+               .addClass( 'oo-ui-dropdownInputWidget' )
+               .append( this.dropdownWidget.$element );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.DropdownInputWidget, OO.ui.InputWidget );
+OO.mixinClass( OO.ui.DropdownInputWidget, OO.ui.mixin.TitledElement );
+
+/* Methods */
+
+/**
+ * @inheritdoc
+ * @protected
+ */
+OO.ui.DropdownInputWidget.prototype.getInputElement = function ( config ) {
+       // See InputWidget#reusePreInfuseDOM about config.$input
+       if ( config.$input ) {
+               return config.$input.addClass( 'oo-ui-element-hidden' );
+       }
+       return $( '<input type="hidden">' );
+};
+
+/**
+ * Handles menu select events.
+ *
+ * @private
+ * @param {OO.ui.MenuOptionWidget} item Selected menu item
+ */
+OO.ui.DropdownInputWidget.prototype.onMenuSelect = function ( item ) {
+       this.setValue( item.getData() );
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.DropdownInputWidget.prototype.setValue = function ( value ) {
+       value = this.cleanUpValue( value );
+       this.dropdownWidget.getMenu().selectItemByData( value );
+       OO.ui.DropdownInputWidget.parent.prototype.setValue.call( this, value );
+       return this;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.DropdownInputWidget.prototype.setDisabled = function ( state ) {
+       this.dropdownWidget.setDisabled( state );
+       OO.ui.DropdownInputWidget.parent.prototype.setDisabled.call( this, state );
+       return this;
+};
+
+/**
+ * Set the options available for this input.
+ *
+ * @param {Object[]} options Array of menu options in the format `{ data: …, label: … }`
+ * @chainable
+ */
+OO.ui.DropdownInputWidget.prototype.setOptions = function ( options ) {
+       var
+               value = this.getValue(),
+               widget = this;
+
+       // Rebuild the dropdown menu
+       this.dropdownWidget.getMenu()
+               .clearItems()
+               .addItems( options.map( function ( opt ) {
+                       var optValue = widget.cleanUpValue( opt.data );
+                       return new OO.ui.MenuOptionWidget( {
+                               data: optValue,
+                               label: opt.label !== undefined ? opt.label : optValue
+                       } );
+               } ) );
+
+       // Restore the previous value, or reset to something sensible
+       if ( this.dropdownWidget.getMenu().getItemFromData( value ) ) {
+               // Previous value is still available, ensure consistency with the dropdown
+               this.setValue( value );
+       } else {
+               // No longer valid, reset
+               if ( options.length ) {
+                       this.setValue( options[ 0 ].data );
+               }
+       }
+
+       return this;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.DropdownInputWidget.prototype.focus = function () {
+       this.dropdownWidget.getMenu().toggle( true );
+       return this;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.DropdownInputWidget.prototype.blur = function () {
+       this.dropdownWidget.getMenu().toggle( false );
+       return this;
+};
+
+/**
+ * RadioInputWidget creates a single radio button. Because radio buttons are usually used as a set,
+ * in most cases you will want to use a {@link OO.ui.RadioSelectWidget radio select}
+ * with {@link OO.ui.RadioOptionWidget radio options} instead of this class. For more information,
+ * please see the [OOjs UI documentation on MediaWiki][1].
+ *
+ * This widget can be used inside a HTML form, such as a OO.ui.FormLayout.
+ *
+ *     @example
+ *     // An example of selected, unselected, and disabled radio inputs
+ *     var radio1 = new OO.ui.RadioInputWidget( {
+ *         value: 'a',
+ *         selected: true
+ *     } );
+ *     var radio2 = new OO.ui.RadioInputWidget( {
+ *         value: 'b'
+ *     } );
+ *     var radio3 = new OO.ui.RadioInputWidget( {
+ *         value: 'c',
+ *         disabled: true
+ *     } );
+ *     // Create a fieldset layout with fields for each radio button.
+ *     var fieldset = new OO.ui.FieldsetLayout( {
+ *         label: 'Radio inputs'
+ *     } );
+ *     fieldset.addItems( [
+ *         new OO.ui.FieldLayout( radio1, { label: 'Selected', align: 'inline' } ),
+ *         new OO.ui.FieldLayout( radio2, { label: 'Unselected', align: 'inline' } ),
+ *         new OO.ui.FieldLayout( radio3, { label: 'Disabled', align: 'inline' } ),
+ *     ] );
+ *     $( 'body' ).append( fieldset.$element );
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Inputs
+ *
+ * @class
+ * @extends OO.ui.InputWidget
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {boolean} [selected=false] Select the radio button initially. By default, the radio button is not selected.
+ */
+OO.ui.RadioInputWidget = function OoUiRadioInputWidget( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Parent constructor
+       OO.ui.RadioInputWidget.parent.call( this, config );
+
+       // Initialization
+       this.$element
+               .addClass( 'oo-ui-radioInputWidget' )
+               // Required for pretty styling in MediaWiki theme
+               .append( $( '<span>' ) );
+       this.setSelected( config.selected !== undefined ? config.selected : false );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.RadioInputWidget, OO.ui.InputWidget );
+
+/* Static Methods */
+
+/**
+ * @inheritdoc
+ */
+OO.ui.RadioInputWidget.static.gatherPreInfuseState = function ( node, config ) {
+       var state = OO.ui.RadioInputWidget.parent.static.gatherPreInfuseState( node, config );
+       state.checked = config.$input.prop( 'checked' );
+       return state;
+};
+
+/* Methods */
+
+/**
+ * @inheritdoc
+ * @protected
+ */
+OO.ui.RadioInputWidget.prototype.getInputElement = function () {
+       return $( '<input type="radio" />' );
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.RadioInputWidget.prototype.onEdit = function () {
+       // RadioInputWidget doesn't track its state.
+};
+
+/**
+ * Set selection state of this radio button.
+ *
+ * @param {boolean} state `true` for selected
+ * @chainable
+ */
+OO.ui.RadioInputWidget.prototype.setSelected = function ( state ) {
+       // RadioInputWidget doesn't track its state.
+       this.$input.prop( 'checked', state );
+       return this;
+};
+
+/**
+ * Check if this radio button is selected.
+ *
+ * @return {boolean} Radio is selected
+ */
+OO.ui.RadioInputWidget.prototype.isSelected = function () {
+       return this.$input.prop( 'checked' );
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.RadioInputWidget.prototype.restorePreInfuseState = function ( state ) {
+       OO.ui.RadioInputWidget.parent.prototype.restorePreInfuseState.call( this, state );
+       if ( state.checked !== undefined && state.checked !== this.isSelected() ) {
+               this.setSelected( state.checked );
+       }
+};
+
+/**
+ * RadioSelectInputWidget is a {@link OO.ui.RadioSelectWidget RadioSelectWidget} intended to be used
+ * within a HTML form, such as a OO.ui.FormLayout. The selected value is synchronized with the value
+ * of a hidden HTML `input` tag. Please see the [OOjs UI documentation on MediaWiki][1] for
+ * more information about input widgets.
+ *
+ * This and OO.ui.DropdownInputWidget support the same configuration options.
+ *
+ *     @example
+ *     // Example: A RadioSelectInputWidget with three options
+ *     var radioSelectInput = new OO.ui.RadioSelectInputWidget( {
+ *         options: [
+ *             { data: 'a', label: 'First' },
+ *             { data: 'b', label: 'Second'},
+ *             { data: 'c', label: 'Third' }
+ *         ]
+ *     } );
+ *     $( 'body' ).append( radioSelectInput.$element );
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Inputs
+ *
+ * @class
+ * @extends OO.ui.InputWidget
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {Object[]} [options=[]] Array of menu options in the format `{ data: …, label: … }`
+ */
+OO.ui.RadioSelectInputWidget = function OoUiRadioSelectInputWidget( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Properties (must be done before parent constructor which calls #setDisabled)
+       this.radioSelectWidget = new OO.ui.RadioSelectWidget();
+
+       // Parent constructor
+       OO.ui.RadioSelectInputWidget.parent.call( this, config );
+
+       // Events
+       this.radioSelectWidget.connect( this, { select: 'onMenuSelect' } );
+
+       // Initialization
+       this.setOptions( config.options || [] );
+       this.$element
+               .addClass( 'oo-ui-radioSelectInputWidget' )
+               .append( this.radioSelectWidget.$element );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.RadioSelectInputWidget, OO.ui.InputWidget );
+
+/* Static Properties */
+
+OO.ui.RadioSelectInputWidget.static.supportsSimpleLabel = false;
+
+/* Static Methods */
+
+/**
+ * @inheritdoc
+ */
+OO.ui.RadioSelectInputWidget.static.gatherPreInfuseState = function ( node, config ) {
+       var state = OO.ui.RadioSelectInputWidget.parent.static.gatherPreInfuseState( node, config );
+       state.value = $( node ).find( '.oo-ui-radioInputWidget .oo-ui-inputWidget-input:checked' ).val();
+       return state;
+};
+
+/* Methods */
+
+/**
+ * @inheritdoc
+ * @protected
+ */
+OO.ui.RadioSelectInputWidget.prototype.getInputElement = function () {
+       return $( '<input type="hidden">' );
+};
+
+/**
+ * Handles menu select events.
+ *
+ * @private
+ * @param {OO.ui.RadioOptionWidget} item Selected menu item
+ */
+OO.ui.RadioSelectInputWidget.prototype.onMenuSelect = function ( item ) {
+       this.setValue( item.getData() );
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.RadioSelectInputWidget.prototype.setValue = function ( value ) {
+       value = this.cleanUpValue( value );
+       this.radioSelectWidget.selectItemByData( value );
+       OO.ui.RadioSelectInputWidget.parent.prototype.setValue.call( this, value );
+       return this;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.RadioSelectInputWidget.prototype.setDisabled = function ( state ) {
+       this.radioSelectWidget.setDisabled( state );
+       OO.ui.RadioSelectInputWidget.parent.prototype.setDisabled.call( this, state );
+       return this;
+};
+
+/**
+ * Set the options available for this input.
+ *
+ * @param {Object[]} options Array of menu options in the format `{ data: …, label: … }`
+ * @chainable
+ */
+OO.ui.RadioSelectInputWidget.prototype.setOptions = function ( options ) {
+       var
+               value = this.getValue(),
+               widget = this;
+
+       // Rebuild the radioSelect menu
+       this.radioSelectWidget
+               .clearItems()
+               .addItems( options.map( function ( opt ) {
+                       var optValue = widget.cleanUpValue( opt.data );
+                       return new OO.ui.RadioOptionWidget( {
+                               data: optValue,
+                               label: opt.label !== undefined ? opt.label : optValue
+                       } );
+               } ) );
+
+       // Restore the previous value, or reset to something sensible
+       if ( this.radioSelectWidget.getItemFromData( value ) ) {
+               // Previous value is still available, ensure consistency with the radioSelect
+               this.setValue( value );
+       } else {
+               // No longer valid, reset
+               if ( options.length ) {
+                       this.setValue( options[ 0 ].data );
+               }
+       }
+
+       return this;
+};
+
+/**
+ * TextInputWidgets, like HTML text inputs, can be configured with options that customize the
+ * size of the field as well as its presentation. In addition, these widgets can be configured
+ * with {@link OO.ui.mixin.IconElement icons}, {@link OO.ui.mixin.IndicatorElement indicators}, an optional
+ * validation-pattern (used to determine if an input value is valid or not) and an input filter,
+ * which modifies incoming values rather than validating them.
+ * Please see the [OOjs UI documentation on MediaWiki] [1] for more information and examples.
+ *
+ * This widget can be used inside a HTML form, such as a OO.ui.FormLayout.
+ *
+ *     @example
+ *     // Example of a text input widget
+ *     var textInput = new OO.ui.TextInputWidget( {
+ *         value: 'Text input'
+ *     } )
+ *     $( 'body' ).append( textInput.$element );
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Inputs
+ *
+ * @class
+ * @extends OO.ui.InputWidget
+ * @mixins OO.ui.mixin.IconElement
+ * @mixins OO.ui.mixin.IndicatorElement
+ * @mixins OO.ui.mixin.PendingElement
+ * @mixins OO.ui.mixin.LabelElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {string} [type='text'] The value of the HTML `type` attribute: 'text', 'password', 'search',
+ *  'email' or 'url'. Ignored if `multiline` is true.
+ *
+ *  Some values of `type` result in additional behaviors:
+ *
+ *  - `search`: implies `icon: 'search'` and `indicator: 'clear'`; when clicked, the indicator
+ *    empties the text field
+ * @cfg {string} [placeholder] Placeholder text
+ * @cfg {boolean} [autofocus=false] Use an HTML `autofocus` attribute to
+ *  instruct the browser to focus this widget.
+ * @cfg {boolean} [readOnly=false] Prevent changes to the value of the text input.
+ * @cfg {number} [maxLength] Maximum number of characters allowed in the input.
+ * @cfg {boolean} [multiline=false] Allow multiple lines of text
+ * @cfg {number} [rows] If multiline, number of visible lines in textarea. If used with `autosize`,
+ *  specifies minimum number of rows to display.
+ * @cfg {boolean} [autosize=false] Automatically resize the text input to fit its content.
+ *  Use the #maxRows config to specify a maximum number of displayed rows.
+ * @cfg {boolean} [maxRows] Maximum number of rows to display when #autosize is set to true.
+ *  Defaults to the maximum of `10` and `2 * rows`, or `10` if `rows` isn't provided.
+ * @cfg {string} [labelPosition='after'] The position of the inline label relative to that of
+ *  the value or placeholder text: `'before'` or `'after'`
+ * @cfg {boolean} [required=false] Mark the field as required. Implies `indicator: 'required'`.
+ * @cfg {boolean} [autocomplete=true] Should the browser support autocomplete for this field
+ * @cfg {RegExp|Function|string} [validate] Validation pattern: when string, a symbolic name of a
+ *  pattern defined by the class: 'non-empty' (the value cannot be an empty string) or 'integer'
+ *  (the value must contain only numbers); when RegExp, a regular expression that must match the
+ *  value for it to be considered valid; when Function, a function receiving the value as parameter
+ *  that must return true, or promise resolving to true, for it to be considered valid.
+ */
+OO.ui.TextInputWidget = function OoUiTextInputWidget( config ) {
+       // Configuration initialization
+       config = $.extend( {
+               type: 'text',
+               labelPosition: 'after'
+       }, config );
+       if ( config.type === 'search' ) {
+               if ( config.icon === undefined ) {
+                       config.icon = 'search';
+               }
+               // indicator: 'clear' is set dynamically later, depending on value
+       }
+       if ( config.required ) {
+               if ( config.indicator === undefined ) {
+                       config.indicator = 'required';
+               }
+       }
+
+       // Parent constructor
+       OO.ui.TextInputWidget.parent.call( this, config );
+
+       // Mixin constructors
+       OO.ui.mixin.IconElement.call( this, config );
+       OO.ui.mixin.IndicatorElement.call( this, config );
+       OO.ui.mixin.PendingElement.call( this, $.extend( {}, config, { $pending: this.$input } ) );
+       OO.ui.mixin.LabelElement.call( this, config );
+
+       // Properties
+       this.type = this.getSaneType( config );
+       this.readOnly = false;
+       this.multiline = !!config.multiline;
+       this.autosize = !!config.autosize;
+       this.minRows = config.rows !== undefined ? config.rows : '';
+       this.maxRows = config.maxRows || Math.max( 2 * ( this.minRows || 0 ), 10 );
+       this.validate = null;
+       this.styleHeight = null;
+       this.scrollWidth = null;
+
+       // Clone for resizing
+       if ( this.autosize ) {
+               this.$clone = this.$input
+                       .clone()
+                       .insertAfter( this.$input )
+                       .attr( 'aria-hidden', 'true' )
+                       .addClass( 'oo-ui-element-hidden' );
+       }
+
+       this.setValidation( config.validate );
+       this.setLabelPosition( config.labelPosition );
+
+       // Events
+       this.$input.on( {
+               keypress: this.onKeyPress.bind( this ),
+               blur: this.onBlur.bind( this )
+       } );
+       this.$input.one( {
+               focus: this.onElementAttach.bind( this )
+       } );
+       this.$icon.on( 'mousedown', this.onIconMouseDown.bind( this ) );
+       this.$indicator.on( 'mousedown', this.onIndicatorMouseDown.bind( this ) );
+       this.on( 'labelChange', this.updatePosition.bind( this ) );
+       this.connect( this, {
+               change: 'onChange',
+               disable: 'onDisable'
+       } );
+
+       // Initialization
+       this.$element
+               .addClass( 'oo-ui-textInputWidget oo-ui-textInputWidget-type-' + this.type )
+               .append( this.$icon, this.$indicator );
+       this.setReadOnly( !!config.readOnly );
+       this.updateSearchIndicator();
+       if ( config.placeholder ) {
+               this.$input.attr( 'placeholder', config.placeholder );
+       }
+       if ( config.maxLength !== undefined ) {
+               this.$input.attr( 'maxlength', config.maxLength );
+       }
+       if ( config.autofocus ) {
+               this.$input.attr( 'autofocus', 'autofocus' );
+       }
+       if ( config.required ) {
+               this.$input.attr( 'required', 'required' );
+               this.$input.attr( 'aria-required', 'true' );
+       }
+       if ( config.autocomplete === false ) {
+               this.$input.attr( 'autocomplete', 'off' );
+               // Turning off autocompletion also disables "form caching" when the user navigates to a
+               // different page and then clicks "Back". Re-enable it when leaving. Borrowed from jQuery UI.
+               $( window ).on( {
+                       beforeunload: function () {
+                               this.$input.removeAttr( 'autocomplete' );
+                       }.bind( this ),
+                       pageshow: function () {
+                               // Browsers don't seem to actually fire this event on "Back", they instead just reload the
+                               // whole page... it shouldn't hurt, though.
+                               this.$input.attr( 'autocomplete', 'off' );
+                       }.bind( this )
+               } );
+       }
+       if ( this.multiline && config.rows ) {
+               this.$input.attr( 'rows', config.rows );
+       }
+       if ( this.label || config.autosize ) {
+               this.installParentChangeDetector();
+       }
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.TextInputWidget, OO.ui.InputWidget );
+OO.mixinClass( OO.ui.TextInputWidget, OO.ui.mixin.IconElement );
+OO.mixinClass( OO.ui.TextInputWidget, OO.ui.mixin.IndicatorElement );
+OO.mixinClass( OO.ui.TextInputWidget, OO.ui.mixin.PendingElement );
+OO.mixinClass( OO.ui.TextInputWidget, OO.ui.mixin.LabelElement );
+
+/* Static Properties */
+
+OO.ui.TextInputWidget.static.validationPatterns = {
+       'non-empty': /.+/,
+       integer: /^\d+$/
+};
+
+/* Static Methods */
+
+/**
+ * @inheritdoc
+ */
+OO.ui.TextInputWidget.static.gatherPreInfuseState = function ( node, config ) {
+       var state = OO.ui.TextInputWidget.parent.static.gatherPreInfuseState( node, config );
+       if ( config.multiline ) {
+               state.scrollTop = config.$input.scrollTop();
+       }
+       return state;
+};
+
+/* Events */
+
+/**
+ * An `enter` event is emitted when the user presses 'enter' inside the text box.
+ *
+ * Not emitted if the input is multiline.
+ *
+ * @event enter
+ */
+
+/**
+ * A `resize` event is emitted when autosize is set and the widget resizes
+ *
+ * @event resize
+ */
+
+/* Methods */
+
+/**
+ * Handle icon mouse down events.
+ *
+ * @private
+ * @param {jQuery.Event} e Mouse down event
+ * @fires icon
+ */
+OO.ui.TextInputWidget.prototype.onIconMouseDown = function ( e ) {
+       if ( e.which === OO.ui.MouseButtons.LEFT ) {
+               this.$input[ 0 ].focus();
+               return false;
+       }
+};
+
+/**
+ * Handle indicator mouse down events.
+ *
+ * @private
+ * @param {jQuery.Event} e Mouse down event
+ * @fires indicator
+ */
+OO.ui.TextInputWidget.prototype.onIndicatorMouseDown = function ( e ) {
+       if ( e.which === OO.ui.MouseButtons.LEFT ) {
+               if ( this.type === 'search' ) {
+                       // Clear the text field
+                       this.setValue( '' );
+               }
+               this.$input[ 0 ].focus();
+               return false;
+       }
+};
+
+/**
+ * Handle key press events.
+ *
+ * @private
+ * @param {jQuery.Event} e Key press event
+ * @fires enter If enter key is pressed and input is not multiline
+ */
+OO.ui.TextInputWidget.prototype.onKeyPress = function ( e ) {
+       if ( e.which === OO.ui.Keys.ENTER && !this.multiline ) {
+               this.emit( 'enter', e );
+       }
+};
+
+/**
+ * Handle blur events.
+ *
+ * @private
+ * @param {jQuery.Event} e Blur event
+ */
+OO.ui.TextInputWidget.prototype.onBlur = function () {
+       this.setValidityFlag();
+};
+
+/**
+ * Handle element attach events.
+ *
+ * @private
+ * @param {jQuery.Event} e Element attach event
+ */
+OO.ui.TextInputWidget.prototype.onElementAttach = function () {
+       // Any previously calculated size is now probably invalid if we reattached elsewhere
+       this.valCache = null;
+       this.adjustSize();
+       this.positionLabel();
+};
+
+/**
+ * Handle change events.
+ *
+ * @param {string} value
+ * @private
+ */
+OO.ui.TextInputWidget.prototype.onChange = function () {
+       this.updateSearchIndicator();
+       this.setValidityFlag();
+       this.adjustSize();
+};
+
+/**
+ * Handle disable events.
+ *
+ * @param {boolean} disabled Element is disabled
+ * @private
+ */
+OO.ui.TextInputWidget.prototype.onDisable = function () {
+       this.updateSearchIndicator();
+};
+
+/**
+ * Check if the input is {@link #readOnly read-only}.
+ *
+ * @return {boolean}
+ */
+OO.ui.TextInputWidget.prototype.isReadOnly = function () {
+       return this.readOnly;
+};
+
+/**
+ * Set the {@link #readOnly read-only} state of the input.
+ *
+ * @param {boolean} state Make input read-only
+ * @chainable
+ */
+OO.ui.TextInputWidget.prototype.setReadOnly = function ( state ) {
+       this.readOnly = !!state;
+       this.$input.prop( 'readOnly', this.readOnly );
+       this.updateSearchIndicator();
+       return this;
+};
+
+/**
+ * Support function for making #onElementAttach work across browsers.
+ *
+ * This whole function could be replaced with one line of code using the DOMNodeInsertedIntoDocument
+ * event, but it's not supported by Firefox and allegedly deprecated, so we only use it as fallback.
+ *
+ * Due to MutationObserver performance woes, #onElementAttach is only somewhat reliably called the
+ * first time that the element gets attached to the documented.
+ */
+OO.ui.TextInputWidget.prototype.installParentChangeDetector = function () {
+       var mutationObserver, onRemove, topmostNode, fakeParentNode,
+               MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver,
+               widget = this;
+
+       if ( MutationObserver ) {
+               // The new way. If only it wasn't so ugly.
+
+               if ( this.$element.closest( 'html' ).length ) {
+                       // Widget is attached already, do nothing. This breaks the functionality of this function when
+                       // the widget is detached and reattached. Alas, doing this correctly with MutationObserver
+                       // would require observation of the whole document, which would hurt performance of other,
+                       // more important code.
+                       return;
+               }
+
+               // Find topmost node in the tree
+               topmostNode = this.$element[ 0 ];
+               while ( topmostNode.parentNode ) {
+                       topmostNode = topmostNode.parentNode;
+               }
+
+               // We have no way to detect the $element being attached somewhere without observing the entire
+               // DOM with subtree modifications, which would hurt performance. So we cheat: we hook to the
+               // parent node of $element, and instead detect when $element is removed from it (and thus
+               // probably attached somewhere else). If there is no parent, we create a "fake" one. If it
+               // doesn't get attached, we end up back here and create the parent.
+
+               mutationObserver = new MutationObserver( function ( mutations ) {
+                       var i, j, removedNodes;
+                       for ( i = 0; i < mutations.length; i++ ) {
+                               removedNodes = mutations[ i ].removedNodes;
+                               for ( j = 0; j < removedNodes.length; j++ ) {
+                                       if ( removedNodes[ j ] === topmostNode ) {
+                                               setTimeout( onRemove, 0 );
+                                               return;
+                                       }
+                               }
+                       }
+               } );
+
+               onRemove = function () {
+                       // If the node was attached somewhere else, report it
+                       if ( widget.$element.closest( 'html' ).length ) {
+                               widget.onElementAttach();
+                       }
+                       mutationObserver.disconnect();
+                       widget.installParentChangeDetector();
+               };
+
+               // Create a fake parent and observe it
+               fakeParentNode = $( '<div>' ).append( topmostNode )[ 0 ];
+               mutationObserver.observe( fakeParentNode, { childList: true } );
+       } else {
+               // Using the DOMNodeInsertedIntoDocument event is much nicer and less magical, and works for
+               // detachment and reattachment, but it's not supported by Firefox and allegedly deprecated.
+               this.$element.on( 'DOMNodeInsertedIntoDocument', this.onElementAttach.bind( this ) );
+       }
+};
+
+/**
+ * Automatically adjust the size of the text input.
+ *
+ * This only affects #multiline inputs that are {@link #autosize autosized}.
+ *
+ * @chainable
+ * @fires resize
+ */
+OO.ui.TextInputWidget.prototype.adjustSize = function () {
+       var scrollHeight, innerHeight, outerHeight, maxInnerHeight, measurementError,
+               idealHeight, newHeight, scrollWidth, property;
+
+       if ( this.multiline && this.$input.val() !== this.valCache ) {
+               if ( this.autosize ) {
+                       this.$clone
+                               .val( this.$input.val() )
+                               .attr( 'rows', this.minRows )
+                               // Set inline height property to 0 to measure scroll height
+                               .css( 'height', 0 );
+
+                       this.$clone.removeClass( 'oo-ui-element-hidden' );
+
+                       this.valCache = this.$input.val();
+
+                       scrollHeight = this.$clone[ 0 ].scrollHeight;
+
+                       // Remove inline height property to measure natural heights
+                       this.$clone.css( 'height', '' );
+                       innerHeight = this.$clone.innerHeight();
+                       outerHeight = this.$clone.outerHeight();
+
+                       // Measure max rows height
+                       this.$clone
+                               .attr( 'rows', this.maxRows )
+                               .css( 'height', 'auto' )
+                               .val( '' );
+                       maxInnerHeight = this.$clone.innerHeight();
+
+                       // Difference between reported innerHeight and scrollHeight with no scrollbars present
+                       // Equals 1 on Blink-based browsers and 0 everywhere else
+                       measurementError = maxInnerHeight - this.$clone[ 0 ].scrollHeight;
+                       idealHeight = Math.min( maxInnerHeight, scrollHeight + measurementError );
+
+                       this.$clone.addClass( 'oo-ui-element-hidden' );
+
+                       // Only apply inline height when expansion beyond natural height is needed
+                       // Use the difference between the inner and outer height as a buffer
+                       newHeight = idealHeight > innerHeight ? idealHeight + ( outerHeight - innerHeight ) : '';
+                       if ( newHeight !== this.styleHeight ) {
+                               this.$input.css( 'height', newHeight );
+                               this.styleHeight = newHeight;
+                               this.emit( 'resize' );
+                       }
+               }
+               scrollWidth = this.$input[ 0 ].offsetWidth - this.$input[ 0 ].clientWidth;
+               if ( scrollWidth !== this.scrollWidth ) {
+                       property = this.$element.css( 'direction' ) === 'rtl' ? 'left' : 'right';
+                       // Reset
+                       this.$label.css( { right: '', left: '' } );
+                       this.$indicator.css( { right: '', left: '' } );
+
+                       if ( scrollWidth ) {
+                               this.$indicator.css( property, scrollWidth );
+                               if ( this.labelPosition === 'after' ) {
+                                       this.$label.css( property, scrollWidth );
+                               }
+                       }
+
+                       this.scrollWidth = scrollWidth;
+                       this.positionLabel();
+               }
+       }
+       return this;
+};
+
+/**
+ * @inheritdoc
+ * @protected
+ */
+OO.ui.TextInputWidget.prototype.getInputElement = function ( config ) {
+       return config.multiline ?
+               $( '<textarea>' ) :
+               $( '<input type="' + this.getSaneType( config ) + '" />' );
+};
+
+/**
+ * Get sanitized value for 'type' for given config.
+ *
+ * @param {Object} config Configuration options
+ * @return {string|null}
+ * @private
+ */
+OO.ui.TextInputWidget.prototype.getSaneType = function ( config ) {
+       var type = [ 'text', 'password', 'search', 'email', 'url' ].indexOf( config.type ) !== -1 ?
+               config.type :
+               'text';
+       return config.multiline ? 'multiline' : type;
+};
+
+/**
+ * Check if the input supports multiple lines.
+ *
+ * @return {boolean}
+ */
+OO.ui.TextInputWidget.prototype.isMultiline = function () {
+       return !!this.multiline;
+};
+
+/**
+ * Check if the input automatically adjusts its size.
+ *
+ * @return {boolean}
+ */
+OO.ui.TextInputWidget.prototype.isAutosizing = function () {
+       return !!this.autosize;
+};
+
+/**
+ * Focus the input and select a specified range within the text.
+ *
+ * @param {number} from Select from offset
+ * @param {number} [to] Select to offset, defaults to from
+ * @chainable
+ */
+OO.ui.TextInputWidget.prototype.selectRange = function ( from, to ) {
+       var isBackwards, start, end,
+               input = this.$input[ 0 ];
+
+       to = to || from;
+
+       isBackwards = to < from;
+       start = isBackwards ? to : from;
+       end = isBackwards ? from : to;
+
+       this.focus();
+
+       input.setSelectionRange( start, end, isBackwards ? 'backward' : 'forward' );
+       return this;
+};
+
+/**
+ * Get an object describing the current selection range in a directional manner
+ *
+ * @return {Object} Object containing 'from' and 'to' offsets
+ */
+OO.ui.TextInputWidget.prototype.getRange = function () {
+       var input = this.$input[ 0 ],
+               start = input.selectionStart,
+               end = input.selectionEnd,
+               isBackwards = input.selectionDirection === 'backward';
+
+       return {
+               from: isBackwards ? end : start,
+               to: isBackwards ? start : end
+       };
+};
+
+/**
+ * Get the length of the text input value.
+ *
+ * This could differ from the length of #getValue if the
+ * value gets filtered
+ *
+ * @return {number} Input length
+ */
+OO.ui.TextInputWidget.prototype.getInputLength = function () {
+       return this.$input[ 0 ].value.length;
+};
+
+/**
+ * Focus the input and select the entire text.
+ *
+ * @chainable
+ */
+OO.ui.TextInputWidget.prototype.select = function () {
+       return this.selectRange( 0, this.getInputLength() );
+};
+
+/**
+ * Focus the input and move the cursor to the start.
+ *
+ * @chainable
+ */
+OO.ui.TextInputWidget.prototype.moveCursorToStart = function () {
+       return this.selectRange( 0 );
+};
+
+/**
+ * Focus the input and move the cursor to the end.
+ *
+ * @chainable
+ */
+OO.ui.TextInputWidget.prototype.moveCursorToEnd = function () {
+       return this.selectRange( this.getInputLength() );
+};
+
+/**
+ * Insert new content into the input.
+ *
+ * @param {string} content Content to be inserted
+ * @chainable
+ */
+OO.ui.TextInputWidget.prototype.insertContent = function ( content ) {
+       var start, end,
+               range = this.getRange(),
+               value = this.getValue();
+
+       start = Math.min( range.from, range.to );
+       end = Math.max( range.from, range.to );
+
+       this.setValue( value.slice( 0, start ) + content + value.slice( end ) );
+       this.selectRange( start + content.length );
+       return this;
+};
+
+/**
+ * Insert new content either side of a selection.
+ *
+ * @param {string} pre Content to be inserted before the selection
+ * @param {string} post Content to be inserted after the selection
+ * @chainable
+ */
+OO.ui.TextInputWidget.prototype.encapsulateContent = function ( pre, post ) {
+       var start, end,
+               range = this.getRange(),
+               offset = pre.length;
+
+       start = Math.min( range.from, range.to );
+       end = Math.max( range.from, range.to );
+
+       this.selectRange( start ).insertContent( pre );
+       this.selectRange( offset + end ).insertContent( post );
+
+       this.selectRange( offset + start, offset + end );
+       return this;
+};
+
+/**
+ * Set the validation pattern.
+ *
+ * The validation pattern is either a regular expression, a function, or the symbolic name of a
+ * pattern defined by the class: 'non-empty' (the value cannot be an empty string) or 'integer' (the
+ * value must contain only numbers).
+ *
+ * @param {RegExp|Function|string|null} validate Regular expression, function, or the symbolic name
+ *  of a pattern (either ‘integer’ or ‘non-empty’) defined by the class.
+ */
+OO.ui.TextInputWidget.prototype.setValidation = function ( validate ) {
+       if ( validate instanceof RegExp || validate instanceof Function ) {
+               this.validate = validate;
+       } else {
+               this.validate = this.constructor.static.validationPatterns[ validate ] || /.*/;
+       }
+};
+
+/**
+ * Sets the 'invalid' flag appropriately.
+ *
+ * @param {boolean} [isValid] Optionally override validation result
+ */
+OO.ui.TextInputWidget.prototype.setValidityFlag = function ( isValid ) {
+       var widget = this,
+               setFlag = function ( valid ) {
+                       if ( !valid ) {
+                               widget.$input.attr( 'aria-invalid', 'true' );
+                       } else {
+                               widget.$input.removeAttr( 'aria-invalid' );
+                       }
+                       widget.setFlags( { invalid: !valid } );
+               };
+
+       if ( isValid !== undefined ) {
+               setFlag( isValid );
+       } else {
+               this.getValidity().then( function () {
+                       setFlag( true );
+               }, function () {
+                       setFlag( false );
+               } );
+       }
+};
+
+/**
+ * Check if a value is valid.
+ *
+ * This method returns a promise that resolves with a boolean `true` if the current value is
+ * considered valid according to the supplied {@link #validate validation pattern}.
+ *
+ * @deprecated
+ * @return {jQuery.Promise} A promise that resolves to a boolean `true` if the value is valid.
+ */
+OO.ui.TextInputWidget.prototype.isValid = function () {
+       var result;
+
+       if ( this.validate instanceof Function ) {
+               result = this.validate( this.getValue() );
+               if ( result && $.isFunction( result.promise ) ) {
+                       return result.promise();
+               } else {
+                       return $.Deferred().resolve( !!result ).promise();
+               }
+       } else {
+               return $.Deferred().resolve( !!this.getValue().match( this.validate ) ).promise();
+       }
+};
+
+/**
+ * Get the validity of current value.
+ *
+ * This method returns a promise that resolves if the value is valid and rejects if
+ * it isn't. Uses the {@link #validate validation pattern}  to check for validity.
+ *
+ * @return {jQuery.Promise} A promise that resolves if the value is valid, rejects if not.
+ */
+OO.ui.TextInputWidget.prototype.getValidity = function () {
+       var result;
+
+       function rejectOrResolve( valid ) {
+               if ( valid ) {
+                       return $.Deferred().resolve().promise();
+               } else {
+                       return $.Deferred().reject().promise();
+               }
+       }
+
+       if ( this.validate instanceof Function ) {
+               result = this.validate( this.getValue() );
+               if ( result && $.isFunction( result.promise ) ) {
+                       return result.promise().then( function ( valid ) {
+                               return rejectOrResolve( valid );
+                       } );
+               } else {
+                       return rejectOrResolve( result );
+               }
+       } else {
+               return rejectOrResolve( this.getValue().match( this.validate ) );
+       }
+};
+
+/**
+ * Set the position of the inline label relative to that of the value: `‘before’` or `‘after’`.
+ *
+ * @param {string} labelPosition Label position, 'before' or 'after'
+ * @chainable
+ */
+OO.ui.TextInputWidget.prototype.setLabelPosition = function ( labelPosition ) {
+       this.labelPosition = labelPosition;
+       this.updatePosition();
+       return this;
+};
+
+/**
+ * Update the position of the inline label.
+ *
+ * This method is called by #setLabelPosition, and can also be called on its own if
+ * something causes the label to be mispositioned.
+ *
+ * @chainable
+ */
+OO.ui.TextInputWidget.prototype.updatePosition = function () {
+       var after = this.labelPosition === 'after';
+
+       this.$element
+               .toggleClass( 'oo-ui-textInputWidget-labelPosition-after', !!this.label && after )
+               .toggleClass( 'oo-ui-textInputWidget-labelPosition-before', !!this.label && !after );
+
+       this.valCache = null;
+       this.scrollWidth = null;
+       this.adjustSize();
+       this.positionLabel();
+
+       return this;
+};
+
+/**
+ * Update the 'clear' indicator displayed on type: 'search' text fields, hiding it when the field is
+ * already empty or when it's not editable.
+ */
+OO.ui.TextInputWidget.prototype.updateSearchIndicator = function () {
+       if ( this.type === 'search' ) {
+               if ( this.getValue() === '' || this.isDisabled() || this.isReadOnly() ) {
+                       this.setIndicator( null );
+               } else {
+                       this.setIndicator( 'clear' );
+               }
+       }
+};
+
+/**
+ * Position the label by setting the correct padding on the input.
+ *
+ * @private
+ * @chainable
+ */
+OO.ui.TextInputWidget.prototype.positionLabel = function () {
+       var after, rtl, property;
+       // Clear old values
+       this.$input
+               // Clear old values if present
+               .css( {
+                       'padding-right': '',
+                       'padding-left': ''
+               } );
+
+       if ( this.label ) {
+               this.$element.append( this.$label );
+       } else {
+               this.$label.detach();
+               return;
+       }
+
+       after = this.labelPosition === 'after';
+       rtl = this.$element.css( 'direction' ) === 'rtl';
+       property = after === rtl ? 'padding-left' : 'padding-right';
+
+       this.$input.css( property, this.$label.outerWidth( true ) + ( after ? this.scrollWidth : 0 ) );
+
+       return this;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.TextInputWidget.prototype.restorePreInfuseState = function ( state ) {
+       OO.ui.TextInputWidget.parent.prototype.restorePreInfuseState.call( this, state );
+       if ( state.scrollTop !== undefined ) {
+               this.$input.scrollTop( state.scrollTop );
+       }
+};
+
+/**
+ * ComboBoxInputWidgets combine a {@link OO.ui.TextInputWidget text input} (where a value
+ * can be entered manually) and a {@link OO.ui.MenuSelectWidget menu of options} (from which
+ * a value can be chosen instead). Users can choose options from the combo box in one of two ways:
+ *
+ * - by typing a value in the text input field. If the value exactly matches the value of a menu
+ *   option, that option will appear to be selected.
+ * - by choosing a value from the menu. The value of the chosen option will then appear in the text
+ *   input field.
+ *
+ * This widget can be used inside a HTML form, such as a OO.ui.FormLayout.
+ *
+ * For more information about menus and options, please see the [OOjs UI documentation on MediaWiki][1].
+ *
+ *     @example
+ *     // Example: A ComboBoxInputWidget.
+ *     var comboBox = new OO.ui.ComboBoxInputWidget( {
+ *         label: 'ComboBoxInputWidget',
+ *         value: 'Option 1',
+ *         menu: {
+ *             items: [
+ *                 new OO.ui.MenuOptionWidget( {
+ *                     data: 'Option 1',
+ *                     label: 'Option One'
+ *                 } ),
+ *                 new OO.ui.MenuOptionWidget( {
+ *                     data: 'Option 2',
+ *                     label: 'Option Two'
+ *                 } ),
+ *                 new OO.ui.MenuOptionWidget( {
+ *                     data: 'Option 3',
+ *                     label: 'Option Three'
+ *                 } ),
+ *                 new OO.ui.MenuOptionWidget( {
+ *                     data: 'Option 4',
+ *                     label: 'Option Four'
+ *                 } ),
+ *                 new OO.ui.MenuOptionWidget( {
+ *                     data: 'Option 5',
+ *                     label: 'Option Five'
+ *                 } )
+ *             ]
+ *         }
+ *     } );
+ *     $( 'body' ).append( comboBox.$element );
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options#Menu_selects_and_options
+ *
+ * @class
+ * @extends OO.ui.TextInputWidget
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {Object[]} [options=[]] Array of menu options in the format `{ data: …, label: … }`
+ * @cfg {Object} [menu] Configuration options to pass to the {@link OO.ui.FloatingMenuSelectWidget menu select widget}.
+ * @cfg {jQuery} [$overlay] Render the menu into a separate layer. This configuration is useful in cases where
+ *  the expanded menu is larger than its containing `<div>`. The specified overlay layer is usually on top of the
+ *  containing `<div>` and has a larger area. By default, the menu uses relative positioning.
+ */
+OO.ui.ComboBoxInputWidget = function OoUiComboBoxInputWidget( config ) {
+       // Configuration initialization
+       config = $.extend( {
+               indicator: 'down'
+       }, config );
+       // For backwards-compatibility with ComboBoxWidget config
+       $.extend( config, config.input );
+
+       // Parent constructor
+       OO.ui.ComboBoxInputWidget.parent.call( this, config );
+
+       // Properties
+       this.$overlay = config.$overlay || this.$element;
+       this.menu = new OO.ui.FloatingMenuSelectWidget( $.extend(
+               {
+                       widget: this,
+                       input: this,
+                       $container: this.$element,
+                       disabled: this.isDisabled()
+               },
+               config.menu
+       ) );
+       // For backwards-compatibility with ComboBoxWidget
+       this.input = this;
+
+       // Events
+       this.$indicator.on( {
+               click: this.onIndicatorClick.bind( this ),
+               keypress: this.onIndicatorKeyPress.bind( this )
+       } );
+       this.connect( this, {
+               change: 'onInputChange',
+               enter: 'onInputEnter'
+       } );
+       this.menu.connect( this, {
+               choose: 'onMenuChoose',
+               add: 'onMenuItemsChange',
+               remove: 'onMenuItemsChange'
+       } );
+
+       // Initialization
+       this.$input.attr( {
+               role: 'combobox',
+               'aria-autocomplete': 'list'
+       } );
+       // Do not override options set via config.menu.items
+       if ( config.options !== undefined ) {
+               this.setOptions( config.options );
+       }
+       // Extra class for backwards-compatibility with ComboBoxWidget
+       this.$element.addClass( 'oo-ui-comboBoxInputWidget oo-ui-comboBoxWidget' );
+       this.$overlay.append( this.menu.$element );
+       this.onMenuItemsChange();
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.ComboBoxInputWidget, OO.ui.TextInputWidget );
+
+/* Methods */
+
+/**
+ * Get the combobox's menu.
+ * @return {OO.ui.FloatingMenuSelectWidget} Menu widget
+ */
+OO.ui.ComboBoxInputWidget.prototype.getMenu = function () {
+       return this.menu;
+};
+
+/**
+ * Get the combobox's text input widget.
+ * @return {OO.ui.TextInputWidget} Text input widget
+ */
+OO.ui.ComboBoxInputWidget.prototype.getInput = function () {
+       return this;
+};
+
+/**
+ * Handle input change events.
+ *
+ * @private
+ * @param {string} value New value
+ */
+OO.ui.ComboBoxInputWidget.prototype.onInputChange = function ( value ) {
+       var match = this.menu.getItemFromData( value );
+
+       this.menu.selectItem( match );
+       if ( this.menu.getHighlightedItem() ) {
+               this.menu.highlightItem( match );
+       }
+
+       if ( !this.isDisabled() ) {
+               this.menu.toggle( true );
+       }
+};
+
+/**
+ * Handle mouse click events.
+ *
+ * @private
+ * @param {jQuery.Event} e Mouse click event
+ */
+OO.ui.ComboBoxInputWidget.prototype.onIndicatorClick = function ( e ) {
+       if ( !this.isDisabled() && e.which === OO.ui.MouseButtons.LEFT ) {
+               this.menu.toggle();
+               this.$input[ 0 ].focus();
+       }
+       return false;
+};
+
+/**
+ * Handle key press events.
+ *
+ * @private
+ * @param {jQuery.Event} e Key press event
+ */
+OO.ui.ComboBoxInputWidget.prototype.onIndicatorKeyPress = function ( e ) {
+       if ( !this.isDisabled() && ( e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER ) ) {
+               this.menu.toggle();
+               this.$input[ 0 ].focus();
+               return false;
+       }
+};
+
+/**
+ * Handle input enter events.
+ *
+ * @private
+ */
+OO.ui.ComboBoxInputWidget.prototype.onInputEnter = function () {
+       if ( !this.isDisabled() ) {
+               this.menu.toggle( false );
+       }
+};
+
+/**
+ * Handle menu choose events.
+ *
+ * @private
+ * @param {OO.ui.OptionWidget} item Chosen item
+ */
+OO.ui.ComboBoxInputWidget.prototype.onMenuChoose = function ( item ) {
+       this.setValue( item.getData() );
+};
+
+/**
+ * Handle menu item change events.
+ *
+ * @private
+ */
+OO.ui.ComboBoxInputWidget.prototype.onMenuItemsChange = function () {
+       var match = this.menu.getItemFromData( this.getValue() );
+       this.menu.selectItem( match );
+       if ( this.menu.getHighlightedItem() ) {
+               this.menu.highlightItem( match );
+       }
+       this.$element.toggleClass( 'oo-ui-comboBoxInputWidget-empty', this.menu.isEmpty() );
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.ComboBoxInputWidget.prototype.setDisabled = function ( disabled ) {
+       // Parent method
+       OO.ui.ComboBoxInputWidget.parent.prototype.setDisabled.call( this, disabled );
+
+       if ( this.menu ) {
+               this.menu.setDisabled( this.isDisabled() );
+       }
+
+       return this;
+};
+
+/**
+ * Set the options available for this input.
+ *
+ * @param {Object[]} options Array of menu options in the format `{ data: …, label: … }`
+ * @chainable
+ */
+OO.ui.ComboBoxInputWidget.prototype.setOptions = function ( options ) {
+       this.getMenu()
+               .clearItems()
+               .addItems( options.map( function ( opt ) {
+                       return new OO.ui.MenuOptionWidget( {
+                               data: opt.data,
+                               label: opt.label !== undefined ? opt.label : opt.data
+                       } );
+               } ) );
+
+       return this;
+};
+
+/**
+ * @class
+ * @deprecated Use OO.ui.ComboBoxInputWidget instead.
+ */
+OO.ui.ComboBoxWidget = OO.ui.ComboBoxInputWidget;
+
+/**
+ * FieldLayouts are used with OO.ui.FieldsetLayout. Each FieldLayout requires a field-widget,
+ * which is a widget that is specified by reference before any optional configuration settings.
+ *
+ * Field layouts can be configured with help text and/or labels. Labels are aligned in one of four ways:
+ *
+ * - **left**: The label is placed before the field-widget and aligned with the left margin.
+ *   A left-alignment is used for forms with many fields.
+ * - **right**: The label is placed before the field-widget and aligned to the right margin.
+ *   A right-alignment is used for long but familiar forms which users tab through,
+ *   verifying the current field with a quick glance at the label.
+ * - **top**: The label is placed above the field-widget. A top-alignment is used for brief forms
+ *   that users fill out from top to bottom.
+ * - **inline**: The label is placed after the field-widget and aligned to the left.
+ *   An inline-alignment is best used with checkboxes or radio buttons.
+ *
+ * Help text is accessed via a help icon that appears in the upper right corner of the rendered field layout.
+ * Please see the [OOjs UI documentation on MediaWiki] [1] for examples and more information.
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Layouts/Fields_and_Fieldsets
+ * @class
+ * @extends OO.ui.Layout
+ * @mixins OO.ui.mixin.LabelElement
+ * @mixins OO.ui.mixin.TitledElement
+ *
+ * @constructor
+ * @param {OO.ui.Widget} fieldWidget Field widget
+ * @param {Object} [config] Configuration options
+ * @cfg {string} [align='left'] Alignment of the label: 'left', 'right', 'top' or 'inline'
+ * @cfg {Array} [errors] Error messages about the widget, which will be displayed below the widget.
+ *  The array may contain strings or OO.ui.HtmlSnippet instances.
+ * @cfg {Array} [notices] Notices about the widget, which will be displayed below the widget.
+ *  The array may contain strings or OO.ui.HtmlSnippet instances.
+ * @cfg {string|OO.ui.HtmlSnippet} [help] Help text. When help text is specified, a "help" icon will appear
+ *  in the upper-right corner of the rendered field; clicking it will display the text in a popup.
+ *  For important messages, you are advised to use `notices`, as they are always shown.
+ *
+ * @throws {Error} An error is thrown if no widget is specified
+ */
+OO.ui.FieldLayout = function OoUiFieldLayout( fieldWidget, config ) {
+       var hasInputWidget, div;
+
+       // Allow passing positional parameters inside the config object
+       if ( OO.isPlainObject( fieldWidget ) && config === undefined ) {
+               config = fieldWidget;
+               fieldWidget = config.fieldWidget;
+       }
+
+       // Make sure we have required constructor arguments
+       if ( fieldWidget === undefined ) {
+               throw new Error( 'Widget not found' );
+       }
+
+       hasInputWidget = fieldWidget.constructor.static.supportsSimpleLabel;
+
+       // Configuration initialization
+       config = $.extend( { align: 'left' }, config );
+
+       // Parent constructor
+       OO.ui.FieldLayout.parent.call( this, config );
+
+       // Mixin constructors
+       OO.ui.mixin.LabelElement.call( this, config );
+       OO.ui.mixin.TitledElement.call( this, $.extend( {}, config, { $titled: this.$label } ) );
+
+       // Properties
+       this.fieldWidget = fieldWidget;
+       this.errors = [];
+       this.notices = [];
+       this.$field = $( '<div>' );
+       this.$messages = $( '<ul>' );
+       this.$body = $( '<' + ( hasInputWidget ? 'label' : 'div' ) + '>' );
+       this.align = null;
+       if ( config.help ) {
+               this.popupButtonWidget = new OO.ui.PopupButtonWidget( {
+                       classes: [ 'oo-ui-fieldLayout-help' ],
+                       framed: false,
+                       icon: 'info'
+               } );
+
+               div = $( '<div>' );
+               if ( config.help instanceof OO.ui.HtmlSnippet ) {
+                       div.html( config.help.toString() );
+               } else {
+                       div.text( config.help );
+               }
+               this.popupButtonWidget.getPopup().$body.append(
+                       div.addClass( 'oo-ui-fieldLayout-help-content' )
+               );
+               this.$help = this.popupButtonWidget.$element;
+       } else {
+               this.$help = $( [] );
+       }
+
+       // Events
+       if ( hasInputWidget ) {
+               this.$label.on( 'click', this.onLabelClick.bind( this ) );
+       }
+       this.fieldWidget.connect( this, { disable: 'onFieldDisable' } );
+
+       // Initialization
+       this.$element
+               .addClass( 'oo-ui-fieldLayout' )
+               .append( this.$help, this.$body );
+       this.$body.addClass( 'oo-ui-fieldLayout-body' );
+       this.$messages.addClass( 'oo-ui-fieldLayout-messages' );
+       this.$field
+               .addClass( 'oo-ui-fieldLayout-field' )
+               .toggleClass( 'oo-ui-fieldLayout-disable', this.fieldWidget.isDisabled() )
+               .append( this.fieldWidget.$element );
+
+       this.setErrors( config.errors || [] );
+       this.setNotices( config.notices || [] );
+       this.setAlignment( config.align );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.FieldLayout, OO.ui.Layout );
+OO.mixinClass( OO.ui.FieldLayout, OO.ui.mixin.LabelElement );
+OO.mixinClass( OO.ui.FieldLayout, OO.ui.mixin.TitledElement );
+
+/* Methods */
+
+/**
+ * Handle field disable events.
+ *
+ * @private
+ * @param {boolean} value Field is disabled
+ */
+OO.ui.FieldLayout.prototype.onFieldDisable = function ( value ) {
+       this.$element.toggleClass( 'oo-ui-fieldLayout-disabled', value );
+};
+
+/**
+ * Handle label mouse click events.
+ *
+ * @private
+ * @param {jQuery.Event} e Mouse click event
+ */
+OO.ui.FieldLayout.prototype.onLabelClick = function () {
+       this.fieldWidget.simulateLabelClick();
+       return false;
+};
+
+/**
+ * Get the widget contained by the field.
+ *
+ * @return {OO.ui.Widget} Field widget
+ */
+OO.ui.FieldLayout.prototype.getField = function () {
+       return this.fieldWidget;
+};
+
+/**
+ * @protected
+ * @param {string} kind 'error' or 'notice'
+ * @param {string|OO.ui.HtmlSnippet} text
+ * @return {jQuery}
+ */
+OO.ui.FieldLayout.prototype.makeMessage = function ( kind, text ) {
+       var $listItem, $icon, message;
+       $listItem = $( '<li>' );
+       if ( kind === 'error' ) {
+               $icon = new OO.ui.IconWidget( { icon: 'alert', flags: [ 'warning' ] } ).$element;
+       } else if ( kind === 'notice' ) {
+               $icon = new OO.ui.IconWidget( { icon: 'info' } ).$element;
+       } else {
+               $icon = '';
+       }
+       message = new OO.ui.LabelWidget( { label: text } );
+       $listItem
+               .append( $icon, message.$element )
+               .addClass( 'oo-ui-fieldLayout-messages-' + kind );
+       return $listItem;
+};
+
+/**
+ * Set the field alignment mode.
+ *
+ * @private
+ * @param {string} value Alignment mode, either 'left', 'right', 'top' or 'inline'
+ * @chainable
+ */
+OO.ui.FieldLayout.prototype.setAlignment = function ( value ) {
+       if ( value !== this.align ) {
+               // Default to 'left'
+               if ( [ 'left', 'right', 'top', 'inline' ].indexOf( value ) === -1 ) {
+                       value = 'left';
+               }
+               // Reorder elements
+               if ( value === 'inline' ) {
+                       this.$body.append( this.$field, this.$label );
+               } else {
+                       this.$body.append( this.$label, this.$field );
+               }
+               // Set classes. The following classes can be used here:
+               // * oo-ui-fieldLayout-align-left
+               // * oo-ui-fieldLayout-align-right
+               // * oo-ui-fieldLayout-align-top
+               // * oo-ui-fieldLayout-align-inline
+               if ( this.align ) {
+                       this.$element.removeClass( 'oo-ui-fieldLayout-align-' + this.align );
+               }
+               this.$element.addClass( 'oo-ui-fieldLayout-align-' + value );
+               this.align = value;
+       }
+
+       return this;
+};
+
+/**
+ * Set the list of error messages.
+ *
+ * @param {Array} errors Error messages about the widget, which will be displayed below the widget.
+ *  The array may contain strings or OO.ui.HtmlSnippet instances.
+ * @chainable
+ */
+OO.ui.FieldLayout.prototype.setErrors = function ( errors ) {
+       this.errors = errors.slice();
+       this.updateMessages();
+       return this;
+};
+
+/**
+ * Set the list of notice messages.
+ *
+ * @param {Array} notices Notices about the widget, which will be displayed below the widget.
+ *  The array may contain strings or OO.ui.HtmlSnippet instances.
+ * @chainable
+ */
+OO.ui.FieldLayout.prototype.setNotices = function ( notices ) {
+       this.notices = notices.slice();
+       this.updateMessages();
+       return this;
+};
+
+/**
+ * Update the rendering of error and notice messages.
+ *
+ * @private
+ */
+OO.ui.FieldLayout.prototype.updateMessages = function () {
+       var i;
+       this.$messages.empty();
+
+       if ( this.errors.length || this.notices.length ) {
+               this.$body.after( this.$messages );
+       } else {
+               this.$messages.remove();
+               return;
+       }
+
+       for ( i = 0; i < this.notices.length; i++ ) {
+               this.$messages.append( this.makeMessage( 'notice', this.notices[ i ] ) );
+       }
+       for ( i = 0; i < this.errors.length; i++ ) {
+               this.$messages.append( this.makeMessage( 'error', this.errors[ i ] ) );
+       }
+};
+
+/**
+ * ActionFieldLayouts are used with OO.ui.FieldsetLayout. The layout consists of a field-widget, a button,
+ * and an optional label and/or help text. The field-widget (e.g., a {@link OO.ui.TextInputWidget TextInputWidget}),
+ * is required and is specified before any optional configuration settings.
+ *
+ * Labels can be aligned in one of four ways:
+ *
+ * - **left**: The label is placed before the field-widget and aligned with the left margin.
+ *   A left-alignment is used for forms with many fields.
+ * - **right**: The label is placed before the field-widget and aligned to the right margin.
+ *   A right-alignment is used for long but familiar forms which users tab through,
+ *   verifying the current field with a quick glance at the label.
+ * - **top**: The label is placed above the field-widget. A top-alignment is used for brief forms
+ *   that users fill out from top to bottom.
+ * - **inline**: The label is placed after the field-widget and aligned to the left.
+ *   An inline-alignment is best used with checkboxes or radio buttons.
+ *
+ * Help text is accessed via a help icon that appears in the upper right corner of the rendered field layout when help
+ * text is specified.
+ *
+ *     @example
+ *     // Example of an ActionFieldLayout
+ *     var actionFieldLayout = new OO.ui.ActionFieldLayout(
+ *         new OO.ui.TextInputWidget( {
+ *             placeholder: 'Field widget'
+ *         } ),
+ *         new OO.ui.ButtonWidget( {
+ *             label: 'Button'
+ *         } ),
+ *         {
+ *             label: 'An ActionFieldLayout. This label is aligned top',
+ *             align: 'top',
+ *             help: 'This is help text'
+ *         }
+ *     );
+ *
+ *     $( 'body' ).append( actionFieldLayout.$element );
+ *
+ * @class
+ * @extends OO.ui.FieldLayout
+ *
+ * @constructor
+ * @param {OO.ui.Widget} fieldWidget Field widget
+ * @param {OO.ui.ButtonWidget} buttonWidget Button widget
+ */
+OO.ui.ActionFieldLayout = function OoUiActionFieldLayout( fieldWidget, buttonWidget, config ) {
+       // Allow passing positional parameters inside the config object
+       if ( OO.isPlainObject( fieldWidget ) && config === undefined ) {
+               config = fieldWidget;
+               fieldWidget = config.fieldWidget;
+               buttonWidget = config.buttonWidget;
+       }
+
+       // Parent constructor
+       OO.ui.ActionFieldLayout.parent.call( this, fieldWidget, config );
+
+       // Properties
+       this.buttonWidget = buttonWidget;
+       this.$button = $( '<div>' );
+       this.$input = $( '<div>' );
+
+       // Initialization
+       this.$element
+               .addClass( 'oo-ui-actionFieldLayout' );
+       this.$button
+               .addClass( 'oo-ui-actionFieldLayout-button' )
+               .append( this.buttonWidget.$element );
+       this.$input
+               .addClass( 'oo-ui-actionFieldLayout-input' )
+               .append( this.fieldWidget.$element );
+       this.$field
+               .append( this.$input, this.$button );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.ActionFieldLayout, OO.ui.FieldLayout );
+
+/**
+ * FieldsetLayouts are composed of one or more {@link OO.ui.FieldLayout FieldLayouts},
+ * which each contain an individual widget and, optionally, a label. Each Fieldset can be
+ * configured with a label as well. For more information and examples,
+ * please see the [OOjs UI documentation on MediaWiki][1].
+ *
+ *     @example
+ *     // Example of a fieldset layout
+ *     var input1 = new OO.ui.TextInputWidget( {
+ *         placeholder: 'A text input field'
+ *     } );
+ *
+ *     var input2 = new OO.ui.TextInputWidget( {
+ *         placeholder: 'A text input field'
+ *     } );
+ *
+ *     var fieldset = new OO.ui.FieldsetLayout( {
+ *         label: 'Example of a fieldset layout'
+ *     } );
+ *
+ *     fieldset.addItems( [
+ *         new OO.ui.FieldLayout( input1, {
+ *             label: 'Field One'
+ *         } ),
+ *         new OO.ui.FieldLayout( input2, {
+ *             label: 'Field Two'
+ *         } )
+ *     ] );
+ *     $( 'body' ).append( fieldset.$element );
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Layouts/Fields_and_Fieldsets
+ *
+ * @class
+ * @extends OO.ui.Layout
+ * @mixins OO.ui.mixin.IconElement
+ * @mixins OO.ui.mixin.LabelElement
+ * @mixins OO.ui.mixin.GroupElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {OO.ui.FieldLayout[]} [items] An array of fields to add to the fieldset. See OO.ui.FieldLayout for more information about fields.
+ */
+OO.ui.FieldsetLayout = function OoUiFieldsetLayout( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Parent constructor
+       OO.ui.FieldsetLayout.parent.call( this, config );
+
+       // Mixin constructors
+       OO.ui.mixin.IconElement.call( this, config );
+       OO.ui.mixin.LabelElement.call( this, config );
+       OO.ui.mixin.GroupElement.call( this, config );
+
+       if ( config.help ) {
+               this.popupButtonWidget = new OO.ui.PopupButtonWidget( {
+                       classes: [ 'oo-ui-fieldsetLayout-help' ],
+                       framed: false,
+                       icon: 'info'
+               } );
+
+               this.popupButtonWidget.getPopup().$body.append(
+                       $( '<div>' )
+                               .text( config.help )
+                               .addClass( 'oo-ui-fieldsetLayout-help-content' )
+               );
+               this.$help = this.popupButtonWidget.$element;
+       } else {
+               this.$help = $( [] );
+       }
+
+       // Initialization
+       this.$element
+               .addClass( 'oo-ui-fieldsetLayout' )
+               .prepend( this.$help, this.$icon, this.$label, this.$group );
+       if ( Array.isArray( config.items ) ) {
+               this.addItems( config.items );
+       }
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.FieldsetLayout, OO.ui.Layout );
+OO.mixinClass( OO.ui.FieldsetLayout, OO.ui.mixin.IconElement );
+OO.mixinClass( OO.ui.FieldsetLayout, OO.ui.mixin.LabelElement );
+OO.mixinClass( OO.ui.FieldsetLayout, OO.ui.mixin.GroupElement );
+
+/**
+ * FormLayouts are used to wrap {@link OO.ui.FieldsetLayout FieldsetLayouts} when you intend to use browser-based
+ * form submission for the fields instead of handling them in JavaScript. Form layouts can be configured with an
+ * HTML form action, an encoding type, and a method using the #action, #enctype, and #method configs, respectively.
+ * See the [OOjs UI documentation on MediaWiki] [1] for more information and examples.
+ *
+ * Only widgets from the {@link OO.ui.InputWidget InputWidget} family support form submission. It
+ * includes standard form elements like {@link OO.ui.CheckboxInputWidget checkboxes}, {@link
+ * OO.ui.RadioInputWidget radio buttons} and {@link OO.ui.TextInputWidget text fields}, as well as
+ * some fancier controls. Some controls have both regular and InputWidget variants, for example
+ * OO.ui.DropdownWidget and OO.ui.DropdownInputWidget – only the latter support form submission and
+ * often have simplified APIs to match the capabilities of HTML forms.
+ * See the [OOjs UI Inputs documentation on MediaWiki] [2] for more information about InputWidgets.
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Layouts/Forms
+ * [2]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Inputs
+ *
+ *     @example
+ *     // Example of a form layout that wraps a fieldset layout
+ *     var input1 = new OO.ui.TextInputWidget( {
+ *         placeholder: 'Username'
+ *     } );
+ *     var input2 = new OO.ui.TextInputWidget( {
+ *         placeholder: 'Password',
+ *         type: 'password'
+ *     } );
+ *     var submit = new OO.ui.ButtonInputWidget( {
+ *         label: 'Submit'
+ *     } );
+ *
+ *     var fieldset = new OO.ui.FieldsetLayout( {
+ *         label: 'A form layout'
+ *     } );
+ *     fieldset.addItems( [
+ *         new OO.ui.FieldLayout( input1, {
+ *             label: 'Username',
+ *             align: 'top'
+ *         } ),
+ *         new OO.ui.FieldLayout( input2, {
+ *             label: 'Password',
+ *             align: 'top'
+ *         } ),
+ *         new OO.ui.FieldLayout( submit )
+ *     ] );
+ *     var form = new OO.ui.FormLayout( {
+ *         items: [ fieldset ],
+ *         action: '/api/formhandler',
+ *         method: 'get'
+ *     } )
+ *     $( 'body' ).append( form.$element );
+ *
+ * @class
+ * @extends OO.ui.Layout
+ * @mixins OO.ui.mixin.GroupElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {string} [method] HTML form `method` attribute
+ * @cfg {string} [action] HTML form `action` attribute
+ * @cfg {string} [enctype] HTML form `enctype` attribute
+ * @cfg {OO.ui.FieldsetLayout[]} [items] Fieldset layouts to add to the form layout.
+ */
+OO.ui.FormLayout = function OoUiFormLayout( config ) {
+       var action;
+
+       // Configuration initialization
+       config = config || {};
+
+       // Parent constructor
+       OO.ui.FormLayout.parent.call( this, config );
+
+       // Mixin constructors
+       OO.ui.mixin.GroupElement.call( this, $.extend( {}, config, { $group: this.$element } ) );
+
+       // Events
+       this.$element.on( 'submit', this.onFormSubmit.bind( this ) );
+
+       // Make sure the action is safe
+       action = config.action;
+       if ( action !== undefined && !OO.ui.isSafeUrl( action ) ) {
+               action = './' + action;
+       }
+
+       // Initialization
+       this.$element
+               .addClass( 'oo-ui-formLayout' )
+               .attr( {
+                       method: config.method,
+                       action: action,
+                       enctype: config.enctype
+               } );
+       if ( Array.isArray( config.items ) ) {
+               this.addItems( config.items );
+       }
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.FormLayout, OO.ui.Layout );
+OO.mixinClass( OO.ui.FormLayout, OO.ui.mixin.GroupElement );
+
+/* Events */
+
+/**
+ * A 'submit' event is emitted when the form is submitted.
+ *
+ * @event submit
+ */
+
+/* Static Properties */
+
+OO.ui.FormLayout.static.tagName = 'form';
+
+/* Methods */
+
+/**
+ * Handle form submit events.
+ *
+ * @private
+ * @param {jQuery.Event} e Submit event
+ * @fires submit
+ */
+OO.ui.FormLayout.prototype.onFormSubmit = function () {
+       if ( this.emit( 'submit' ) ) {
+               return false;
+       }
+};
+
+/**
+ * PanelLayouts expand to cover the entire area of their parent. They can be configured with scrolling, padding,
+ * and a frame, and are often used together with {@link OO.ui.StackLayout StackLayouts}.
+ *
+ *     @example
+ *     // Example of a panel layout
+ *     var panel = new OO.ui.PanelLayout( {
+ *         expanded: false,
+ *         framed: true,
+ *         padded: true,
+ *         $content: $( '<p>A panel layout with padding and a frame.</p>' )
+ *     } );
+ *     $( 'body' ).append( panel.$element );
+ *
+ * @class
+ * @extends OO.ui.Layout
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {boolean} [scrollable=false] Allow vertical scrolling
+ * @cfg {boolean} [padded=false] Add padding between the content and the edges of the panel.
+ * @cfg {boolean} [expanded=true] Expand the panel to fill the entire parent element.
+ * @cfg {boolean} [framed=false] Render the panel with a frame to visually separate it from outside content.
+ */
+OO.ui.PanelLayout = function OoUiPanelLayout( config ) {
+       // Configuration initialization
+       config = $.extend( {
+               scrollable: false,
+               padded: false,
+               expanded: true,
+               framed: false
+       }, config );
+
+       // Parent constructor
+       OO.ui.PanelLayout.parent.call( this, config );
+
+       // Initialization
+       this.$element.addClass( 'oo-ui-panelLayout' );
+       if ( config.scrollable ) {
+               this.$element.addClass( 'oo-ui-panelLayout-scrollable' );
+       }
+       if ( config.padded ) {
+               this.$element.addClass( 'oo-ui-panelLayout-padded' );
+       }
+       if ( config.expanded ) {
+               this.$element.addClass( 'oo-ui-panelLayout-expanded' );
+       }
+       if ( config.framed ) {
+               this.$element.addClass( 'oo-ui-panelLayout-framed' );
+       }
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.PanelLayout, OO.ui.Layout );
+
+/* Methods */
+
+/**
+ * Focus the panel layout
+ *
+ * The default implementation just focuses the first focusable element in the panel
+ */
+OO.ui.PanelLayout.prototype.focus = function () {
+       OO.ui.findFocusable( this.$element ).focus();
+};
+
+/**
+ * HorizontalLayout arranges its contents in a single line (using `display: inline-block` for its
+ * items), with small margins between them. Convenient when you need to put a number of block-level
+ * widgets on a single line next to each other.
+ *
+ * Note that inline elements, such as OO.ui.ButtonWidgets, do not need this wrapper.
+ *
+ *     @example
+ *     // HorizontalLayout with a text input and a label
+ *     var layout = new OO.ui.HorizontalLayout( {
+ *       items: [
+ *         new OO.ui.LabelWidget( { label: 'Label' } ),
+ *         new OO.ui.TextInputWidget( { value: 'Text' } )
+ *       ]
+ *     } );
+ *     $( 'body' ).append( layout.$element );
+ *
+ * @class
+ * @extends OO.ui.Layout
+ * @mixins OO.ui.mixin.GroupElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {OO.ui.Widget[]|OO.ui.Layout[]} [items] Widgets or other layouts to add to the layout.
+ */
+OO.ui.HorizontalLayout = function OoUiHorizontalLayout( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Parent constructor
+       OO.ui.HorizontalLayout.parent.call( this, config );
+
+       // Mixin constructors
+       OO.ui.mixin.GroupElement.call( this, $.extend( {}, config, { $group: this.$element } ) );
+
+       // Initialization
+       this.$element.addClass( 'oo-ui-horizontalLayout' );
+       if ( Array.isArray( config.items ) ) {
+               this.addItems( config.items );
+       }
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.HorizontalLayout, OO.ui.Layout );
+OO.mixinClass( OO.ui.HorizontalLayout, OO.ui.mixin.GroupElement );
+
+}( OO ) );
diff --git a/resources/lib/oojs-ui/oojs-ui-mediawiki-noimages.css b/resources/lib/oojs-ui/oojs-ui-mediawiki-noimages.css
deleted file mode 100644 (file)
index cd5ef36..0000000
+++ /dev/null
@@ -1,3104 +0,0 @@
-/*!
- * OOjs UI v0.15.1
- * https://www.mediawiki.org/wiki/OOjs_UI
- *
- * Copyright 2011–2016 OOjs UI Team and other contributors.
- * Released under the MIT license
- * http://oojs.mit-license.org
- *
- * Date: 2016-01-26T21:14:25Z
- */
-@-webkit-keyframes oo-ui-progressBarWidget-slide {
-       from {
-               margin-left: -40%;
-       }
-       to {
-               margin-left: 100%;
-       }
-}
-@-moz-keyframes oo-ui-progressBarWidget-slide {
-       from {
-               margin-left: -40%;
-       }
-       to {
-               margin-left: 100%;
-       }
-}
-@keyframes oo-ui-progressBarWidget-slide {
-       from {
-               margin-left: -40%;
-       }
-       to {
-               margin-left: 100%;
-       }
-}
-/* @noflip */
-.oo-ui-rtl {
-       direction: rtl;
-}
-/* @noflip */
-.oo-ui-ltr {
-       direction: ltr;
-}
-.oo-ui-element-hidden {
-       display: none !important;
-}
-.oo-ui-buttonElement > .oo-ui-buttonElement-button {
-       cursor: pointer;
-       display: inline-block;
-       vertical-align: middle;
-       font: inherit;
-       white-space: nowrap;
-       -webkit-touch-callout: none;
-       -webkit-user-select: none;
-          -moz-user-select: none;
-           -ms-user-select: none;
-               user-select: none;
-}
-.oo-ui-buttonElement > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon,
-.oo-ui-buttonElement > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator {
-       display: none;
-}
-.oo-ui-buttonElement.oo-ui-widget-disabled > .oo-ui-buttonElement-button {
-       cursor: default;
-}
-.oo-ui-buttonElement.oo-ui-indicatorElement > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator,
-.oo-ui-buttonElement.oo-ui-iconElement > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon {
-       display: inline-block;
-       vertical-align: middle;
-}
-.oo-ui-buttonElement-frameless {
-       display: inline-block;
-       position: relative;
-}
-.oo-ui-buttonElement-frameless.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
-       display: inline-block;
-       vertical-align: middle;
-}
-.oo-ui-buttonElement-framed > .oo-ui-buttonElement-button {
-       display: inline-block;
-       vertical-align: top;
-       text-align: center;
-}
-.oo-ui-buttonElement-framed.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
-       display: inline-block;
-       vertical-align: middle;
-}
-.oo-ui-buttonElement-framed.oo-ui-widget-disabled > .oo-ui-buttonElement-button,
-.oo-ui-buttonElement-framed.oo-ui-widget-disabled.oo-ui-buttonElement-active > .oo-ui-buttonElement-button,
-.oo-ui-buttonElement-framed.oo-ui-widget-disabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
-       cursor: default;
-}
-.oo-ui-buttonElement > .oo-ui-buttonElement-button {
-       font-weight: bold;
-       text-decoration: none;
-}
-.oo-ui-buttonElement.oo-ui-iconElement > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon {
-       margin-left: 0;
-}
-.oo-ui-buttonElement.oo-ui-indicatorElement > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator {
-       width: 0.9375em;
-       height: 0.9375em;
-}
-.oo-ui-buttonElement.oo-ui-iconElement > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator {
-       margin-left: 0.46875em;
-}
-.oo-ui-buttonElement.oo-ui-iconElement > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon {
-       width: 1.875em;
-       height: 1.875em;
-}
-.oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button:focus {
-       box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.2), 0 0 0 1px rgba(0, 0, 0, 0.2);
-       outline: none;
-}
-.oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button .oo-ui-indicatorElement-indicator {
-       margin-right: 0;
-}
-.oo-ui-buttonElement-frameless.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
-       margin-left: 0.25em;
-       margin-right: 0.25em;
-}
-.oo-ui-buttonElement-frameless > input.oo-ui-buttonElement-button {
-       padding-left: 0.25em;
-       padding-right: 0.25em;
-       color: #333333;
-}
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled > input.oo-ui-buttonElement-button,
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
-       color: #555555;
-}
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > input.oo-ui-buttonElement-button,
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
-       color: #444444;
-}
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button:hover > .oo-ui-labelElement-label,
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button:focus > .oo-ui-labelElement-label {
-       color: #2962cc;
-}
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
-       color: #347bff;
-}
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active > .oo-ui-labelElement-label,
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
-       color: #1f4999;
-       box-shadow: none;
-}
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive > .oo-ui-buttonElement-button:hover > .oo-ui-labelElement-label,
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive > .oo-ui-buttonElement-button:focus > .oo-ui-labelElement-label {
-       color: #008064;
-}
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
-       color: #00af89;
-}
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active > .oo-ui-labelElement-label,
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
-       color: #005946;
-       box-shadow: none;
-}
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive > .oo-ui-buttonElement-button:hover > .oo-ui-labelElement-label,
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive > .oo-ui-buttonElement-button:focus > .oo-ui-labelElement-label {
-       color: #8c130d;
-}
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
-       color: #d11d13;
-}
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active > .oo-ui-labelElement-label,
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
-       color: #73100a;
-       box-shadow: none;
-}
-.oo-ui-buttonElement-frameless.oo-ui-widget-disabled > .oo-ui-buttonElement-button {
-       color: #cccccc;
-}
-.oo-ui-buttonElement-frameless.oo-ui-widget-disabled > .oo-ui-buttonElement-button:focus {
-       box-shadow: none;
-}
-.oo-ui-buttonElement-frameless.oo-ui-widget-disabled > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon,
-.oo-ui-buttonElement-frameless.oo-ui-widget-disabled > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator {
-       opacity: 0.2;
-}
-.oo-ui-buttonElement-framed.oo-ui-iconElement.oo-ui-labelElement > .oo-ui-buttonElement-button,
-.oo-ui-buttonElement-framed.oo-ui-iconElement.oo-ui-indicatorElement > .oo-ui-buttonElement-button {
-       padding-left: 2.4em;
-}
-.oo-ui-buttonElement-framed > .oo-ui-buttonElement-button {
-       padding: 0.5em 1em;
-       min-height: 1.2em;
-       min-width: 1em;
-       border-radius: 2px;
-       position: relative;
-       -webkit-transition: background 100ms ease, color 100ms ease, border-color 100ms ease, box-shadow 100ms ease;
-          -moz-transition: background 100ms ease, color 100ms ease, border-color 100ms ease, box-shadow 100ms ease;
-               transition: background 100ms ease, color 100ms ease, border-color 100ms ease, box-shadow 100ms ease;
-}
-.oo-ui-buttonElement-framed > .oo-ui-buttonElement-button:hover,
-.oo-ui-buttonElement-framed > .oo-ui-buttonElement-button:focus {
-       outline: none;
-}
-.oo-ui-buttonElement-framed > input.oo-ui-buttonElement-button,
-.oo-ui-buttonElement-framed.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
-       line-height: 1.2em;
-       display: inline-block;
-}
-.oo-ui-buttonElement-framed.oo-ui-iconElement > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon {
-       position: absolute;
-       top: 0.2em;
-       left: 0.5625em;
-}
-.oo-ui-buttonElement-framed.oo-ui-iconElement.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
-       margin-left: 0.3em;
-}
-.oo-ui-buttonElement-framed.oo-ui-indicatorElement > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator {
-       display: inline-block;
-}
-.oo-ui-buttonElement-framed.oo-ui-indicatorElement.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator,
-.oo-ui-buttonElement-framed.oo-ui-indicatorElement.oo-ui-iconElement:not( .oo-ui-labelElement ) > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator {
-       margin-left: 0.46875em;
-       margin-right: -0.275em;
-}
-.oo-ui-buttonElement-framed.oo-ui-indicatorElement.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator {
-       position: relative;
-       left: 0.2em;
-}
-.oo-ui-buttonElement-framed.oo-ui-widget-disabled > .oo-ui-buttonElement-button {
-       background: #dddddd;
-       color: #ffffff;
-       border: 1px solid #dddddd;
-}
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled > .oo-ui-buttonElement-button {
-       color: #555555;
-       background-color: #ffffff;
-       border: 1px solid #cccccc;
-}
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled > .oo-ui-buttonElement-button:hover {
-       background-color: #ebebeb;
-}
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled > .oo-ui-buttonElement-button:focus {
-       box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.2);
-}
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active,
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
-       background-color: #d9d9d9;
-       border-color: #d9d9d9;
-       box-shadow: none;
-}
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-buttonElement-active > .oo-ui-buttonElement-button {
-       background-color: #999999;
-       color: #ffffff;
-}
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button {
-       color: #347bff;
-}
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button:hover {
-       background-color: rgba(52, 123, 255, 0.1);
-       border-color: rgba(31, 73, 153, 0.5);
-}
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button:focus {
-       box-shadow: inset 0 0 0 1px #1f4999;
-       border-color: #1f4999;
-}
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active,
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
-       color: #1f4999;
-       border-color: #1f4999;
-       box-shadow: none;
-}
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive.oo-ui-widget-enabled.oo-ui-buttonElement-active > .oo-ui-buttonElement-button {
-       background-color: #999999;
-       color: #ffffff;
-}
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive > .oo-ui-buttonElement-button {
-       color: #00af89;
-}
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive > .oo-ui-buttonElement-button:hover {
-       background-color: rgba(0, 171, 137, 0.1);
-       border-color: rgba(0, 89, 70, 0.5);
-}
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive > .oo-ui-buttonElement-button:focus {
-       box-shadow: inset 0 0 0 1px #005946;
-       border-color: #005946;
-}
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active,
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
-       color: #005946;
-       border-color: #005946;
-       box-shadow: none;
-}
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive.oo-ui-widget-enabled.oo-ui-buttonElement-active > .oo-ui-buttonElement-button {
-       background-color: #999999;
-       color: #ffffff;
-}
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive > .oo-ui-buttonElement-button {
-       color: #d11d13;
-}
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive > .oo-ui-buttonElement-button:hover {
-       background-color: rgba(209, 29, 19, 0.1);
-       border-color: rgba(115, 16, 10, 0.5);
-}
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive > .oo-ui-buttonElement-button:focus {
-       box-shadow: inset 0 0 0 1px #73100a;
-       border-color: #73100a;
-}
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active,
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
-       color: #73100a;
-       border-color: #73100a;
-       box-shadow: none;
-}
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive.oo-ui-widget-enabled.oo-ui-buttonElement-active > .oo-ui-buttonElement-button {
-       background-color: #999999;
-       color: #ffffff;
-}
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button {
-       color: #ffffff;
-       background-color: #347bff;
-       border-color: #347bff;
-}
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button:hover {
-       background: #2962cc;
-       border-color: #2962cc;
-}
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button:focus {
-       box-shadow: inset 0 0 0 1px #ffffff;
-       border-color: #347bff;
-}
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-progressive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active,
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-progressive.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
-       color: #ffffff;
-       background-color: #1f4999;
-       border-color: #1f4999;
-       box-shadow: none;
-}
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-progressive.oo-ui-widget-enabled.oo-ui-buttonElement-active > .oo-ui-buttonElement-button {
-       background-color: #999999;
-       color: #ffffff;
-}
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-constructive > .oo-ui-buttonElement-button {
-       color: #ffffff;
-       background-color: #00af89;
-       border-color: #00af89;
-}
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-constructive > .oo-ui-buttonElement-button:hover {
-       background: #008064;
-       border-color: #008064;
-}
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-constructive > .oo-ui-buttonElement-button:focus {
-       box-shadow: inset 0 0 0 1px #ffffff;
-       border-color: #00af89;
-}
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-constructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active,
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-constructive.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
-       color: #ffffff;
-       background-color: #005946;
-       border-color: #005946;
-       box-shadow: none;
-}
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-constructive.oo-ui-widget-enabled.oo-ui-buttonElement-active > .oo-ui-buttonElement-button {
-       background-color: #999999;
-       color: #ffffff;
-}
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-destructive > .oo-ui-buttonElement-button {
-       color: #ffffff;
-       background-color: #d11d13;
-       border-color: #d11d13;
-}
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-destructive > .oo-ui-buttonElement-button:hover {
-       background: #8c130d;
-       border-color: #8c130d;
-}
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-destructive > .oo-ui-buttonElement-button:focus {
-       box-shadow: inset 0 0 0 1px #ffffff;
-       border-color: #d11d13;
-}
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-destructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active,
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-destructive.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
-       color: #ffffff;
-       background-color: #73100a;
-       border-color: #73100a;
-       box-shadow: none;
-}
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-destructive.oo-ui-widget-enabled.oo-ui-buttonElement-active > .oo-ui-buttonElement-button {
-       background-color: #999999;
-       color: #ffffff;
-}
-.oo-ui-clippableElement-clippable {
-       -webkit-box-sizing: border-box;
-          -moz-box-sizing: border-box;
-               box-sizing: border-box;
-}
-.oo-ui-draggableElement {
-       cursor: -webkit-grab -moz-grab, url(images/grab.cur), move;
-}
-.oo-ui-draggableElement-dragging {
-       cursor: -webkit-grabbing -moz-grabbing, url(images/grabbing.cur), move;
-       background: rgba(0, 0, 0, 0.2);
-       opacity: 0.4;
-}
-.oo-ui-draggableGroupElement-horizontal .oo-ui-draggableElement.oo-ui-optionWidget {
-       display: inline-block;
-}
-.oo-ui-draggableGroupElement-placeholder {
-       position: absolute;
-       display: block;
-       background: rgba(0, 0, 0, 0.4);
-}
-.oo-ui-iconElement .oo-ui-iconElement-icon,
-.oo-ui-iconElement.oo-ui-iconElement-icon {
-       background-size: contain;
-       background-position: center center;
-       background-repeat: no-repeat;
-}
-.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator,
-.oo-ui-indicatorElement.oo-ui-indicatorElement-indicator {
-       background-size: contain;
-       background-position: center center;
-       background-repeat: no-repeat;
-}
-.oo-ui-lookupElement > .oo-ui-menuSelectWidget {
-       z-index: 1;
-       width: 100%;
-}
-.oo-ui-pendingElement-pending {
-       background-image: /* @embed */ url(themes/mediawiki/images/textures/pending.gif);
-}
-.oo-ui-bookletLayout-stackLayout.oo-ui-stackLayout-continuous > .oo-ui-panelLayout-scrollable {
-       overflow-y: hidden;
-}
-.oo-ui-bookletLayout-stackLayout > .oo-ui-panelLayout {
-       width: 100%;
-       -webkit-box-sizing: border-box;
-          -moz-box-sizing: border-box;
-               box-sizing: border-box;
-}
-.oo-ui-bookletLayout-stackLayout > .oo-ui-panelLayout-scrollable {
-       overflow-y: auto;
-}
-.oo-ui-bookletLayout-stackLayout > .oo-ui-panelLayout-padded {
-       padding: 2em;
-}
-.oo-ui-bookletLayout-outlinePanel-editable > .oo-ui-outlineSelectWidget {
-       position: absolute;
-       top: 0;
-       left: 0;
-       right: 0;
-       bottom: 3em;
-       overflow-y: auto;
-}
-.oo-ui-bookletLayout-outlinePanel > .oo-ui-outlineControlsWidget {
-       position: absolute;
-       bottom: 0;
-       left: 0;
-       right: 0;
-}
-.oo-ui-bookletLayout-stackLayout > .oo-ui-panelLayout {
-       padding: 1.5em;
-}
-.oo-ui-bookletLayout-outlinePanel {
-       border-right: 1px solid #dddddd;
-}
-.oo-ui-bookletLayout-outlinePanel > .oo-ui-outlineControlsWidget {
-       box-shadow: 0 0.15em 0 0 rgba(0, 0, 0, 0.15);
-}
-.oo-ui-indexLayout > .oo-ui-menuLayout-menu {
-       height: 3em;
-}
-.oo-ui-indexLayout > .oo-ui-menuLayout-content {
-       top: 3em;
-}
-.oo-ui-indexLayout-stackLayout > .oo-ui-panelLayout {
-       padding: 1.5em;
-}
-.oo-ui-indexLayout > .oo-ui-menuLayout-menu {
-       height: 2.75em;
-}
-.oo-ui-indexLayout > .oo-ui-menuLayout-content {
-       top: 2.75em;
-}
-.oo-ui-fieldLayout {
-       display: block;
-       margin-bottom: 1em;
-}
-.oo-ui-fieldLayout:before,
-.oo-ui-fieldLayout:after {
-       content: " ";
-       display: table;
-}
-.oo-ui-fieldLayout:after {
-       clear: both;
-}
-.oo-ui-fieldLayout.oo-ui-fieldLayout-align-left > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label,
-.oo-ui-fieldLayout.oo-ui-fieldLayout-align-right > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label,
-.oo-ui-fieldLayout.oo-ui-fieldLayout-align-left > .oo-ui-fieldLayout-body > .oo-ui-fieldLayout-field,
-.oo-ui-fieldLayout.oo-ui-fieldLayout-align-right > .oo-ui-fieldLayout-body > .oo-ui-fieldLayout-field {
-       display: block;
-       float: left;
-}
-.oo-ui-fieldLayout.oo-ui-fieldLayout-align-right > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label {
-       text-align: right;
-}
-.oo-ui-fieldLayout.oo-ui-fieldLayout-align-inline > .oo-ui-fieldLayout-body {
-       display: table;
-}
-.oo-ui-fieldLayout.oo-ui-fieldLayout-align-inline > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label,
-.oo-ui-fieldLayout.oo-ui-fieldLayout-align-inline > .oo-ui-fieldLayout-body > .oo-ui-fieldLayout-field {
-       display: table-cell;
-       vertical-align: middle;
-}
-.oo-ui-fieldLayout.oo-ui-labelElement.oo-ui-fieldLayout-align-top > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label {
-       display: inline-block;
-}
-.oo-ui-fieldLayout > .oo-ui-fieldLayout-help {
-       float: right;
-}
-.oo-ui-fieldLayout > .oo-ui-fieldLayout-help > .oo-ui-popupWidget > .oo-ui-popupWidget-popup {
-       z-index: 1;
-}
-.oo-ui-fieldLayout > .oo-ui-fieldLayout-help .oo-ui-fieldLayout-help-content {
-       padding: 0.5em 0.75em;
-       line-height: 1.5em;
-}
-.oo-ui-fieldLayout:last-child {
-       margin-bottom: 0;
-}
-.oo-ui-fieldLayout.oo-ui-fieldLayout-align-left.oo-ui-labelElement > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label,
-.oo-ui-fieldLayout.oo-ui-fieldLayout-align-right.oo-ui-labelElement > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label {
-       padding-top: 0.5em;
-       margin-right: 5%;
-       width: 35%;
-}
-.oo-ui-fieldLayout.oo-ui-fieldLayout-align-left > .oo-ui-fieldLayout-body > .oo-ui-fieldLayout-field,
-.oo-ui-fieldLayout.oo-ui-fieldLayout-align-right > .oo-ui-fieldLayout-body > .oo-ui-fieldLayout-field {
-       width: 60%;
-}
-.oo-ui-fieldLayout.oo-ui-fieldLayout-align-inline {
-       margin-bottom: 1.25em;
-}
-.oo-ui-fieldLayout.oo-ui-fieldLayout-align-inline.oo-ui-labelElement > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label {
-       padding: 0.25em 0.25em 0.25em 1em;
-}
-.oo-ui-fieldLayout.oo-ui-fieldLayout-align-top.oo-ui-labelElement > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label {
-       padding-top: 0.25em;
-       padding-bottom: 0.5em;
-}
-.oo-ui-fieldLayout > .oo-ui-popupButtonWidget {
-       margin-right: 0;
-}
-.oo-ui-fieldLayout > .oo-ui-popupButtonWidget:last-child {
-       margin-right: 0;
-}
-.oo-ui-fieldLayout-disabled > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label {
-       color: #cccccc;
-}
-.oo-ui-fieldLayout-messages {
-       list-style: none none;
-       margin: 0.25em 0 0 0.25em;
-       padding: 0;
-}
-.oo-ui-fieldLayout-messages > li {
-       margin: 0;
-       padding: 0;
-       display: table;
-}
-.oo-ui-fieldLayout-messages .oo-ui-iconWidget {
-       display: table-cell;
-       border-right: 0.5em solid transparent;
-}
-.oo-ui-fieldLayout-messages .oo-ui-labelWidget {
-       display: table-cell;
-       padding: 0;
-       line-height: 1.875em;
-       vertical-align: middle;
-}
-.oo-ui-actionFieldLayout-input,
-.oo-ui-actionFieldLayout-button {
-       display: table-cell;
-       vertical-align: middle;
-}
-.oo-ui-actionFieldLayout-input {
-       padding-right: 1em;
-}
-.oo-ui-actionFieldLayout-button {
-       width: 1%;
-       white-space: nowrap;
-}
-.oo-ui-fieldsetLayout {
-       position: relative;
-       margin: 0;
-       padding: 0;
-       border: 0;
-}
-.oo-ui-fieldsetLayout.oo-ui-iconElement > .oo-ui-iconElement-icon {
-       display: block;
-       position: absolute;
-}
-.oo-ui-fieldsetLayout.oo-ui-labelElement > .oo-ui-labelElement-label {
-       display: inline-block;
-}
-.oo-ui-fieldsetLayout > .oo-ui-fieldsetLayout-help {
-       float: right;
-}
-.oo-ui-fieldsetLayout > .oo-ui-fieldsetLayout-help > .oo-ui-popupWidget > .oo-ui-popupWidget-popup {
-       z-index: 1;
-}
-.oo-ui-fieldsetLayout > .oo-ui-fieldsetLayout-help .oo-ui-fieldsetLayout-help-content {
-       padding: 0.5em 0.75em;
-       line-height: 1.5em;
-}
-.oo-ui-fieldsetLayout + .oo-ui-fieldsetLayout,
-.oo-ui-fieldsetLayout + .oo-ui-formLayout {
-       margin-top: 2em;
-}
-.oo-ui-fieldsetLayout > .oo-ui-labelElement-label {
-       font-size: 1.1em;
-       margin-bottom: 0.5em;
-       padding: 0.25em 0;
-       font-weight: bold;
-}
-.oo-ui-fieldsetLayout.oo-ui-iconElement > .oo-ui-labelElement-label {
-       padding-left: 2em;
-       line-height: 1.8em;
-}
-.oo-ui-fieldsetLayout.oo-ui-iconElement > .oo-ui-iconElement-icon {
-       left: 0;
-       top: 0.25em;
-       width: 1.875em;
-       height: 1.875em;
-}
-.oo-ui-fieldsetLayout > .oo-ui-popupButtonWidget {
-       margin-right: 0;
-}
-.oo-ui-fieldsetLayout > .oo-ui-popupButtonWidget:last-child {
-       margin-right: 0;
-}
-.oo-ui-formLayout + .oo-ui-fieldsetLayout,
-.oo-ui-formLayout + .oo-ui-formLayout {
-       margin-top: 2em;
-}
-.oo-ui-menuLayout {
-       position: absolute;
-       top: 0;
-       left: 0;
-       right: 0;
-       bottom: 0;
-}
-.oo-ui-menuLayout-menu,
-.oo-ui-menuLayout-content {
-       position: absolute;
-       -webkit-transition: all 200ms ease;
-          -moz-transition: all 200ms ease;
-               transition: all 200ms ease;
-}
-.oo-ui-menuLayout-menu {
-       height: 18em;
-       width: 18em;
-}
-.oo-ui-menuLayout-content {
-       top: 18em;
-       left: 18em;
-       right: 18em;
-       bottom: 18em;
-}
-.oo-ui-menuLayout.oo-ui-menuLayout-hideMenu > .oo-ui-menuLayout-menu {
-       width: 0 !important;
-       height: 0 !important;
-       overflow: hidden;
-}
-.oo-ui-menuLayout.oo-ui-menuLayout-hideMenu > .oo-ui-menuLayout-content {
-       top: 0 !important;
-       left: 0 !important;
-       right: 0 !important;
-       bottom: 0 !important;
-}
-.oo-ui-menuLayout.oo-ui-menuLayout-showMenu.oo-ui-menuLayout-top > .oo-ui-menuLayout-menu {
-       width: auto !important;
-       left: 0;
-       top: 0;
-       right: 0;
-}
-.oo-ui-menuLayout.oo-ui-menuLayout-showMenu.oo-ui-menuLayout-top > .oo-ui-menuLayout-content {
-       right: 0 !important;
-       bottom: 0 !important;
-       left: 0 !important;
-}
-.oo-ui-menuLayout.oo-ui-menuLayout-showMenu.oo-ui-menuLayout-after > .oo-ui-menuLayout-menu {
-       height: auto !important;
-       top: 0;
-       right: 0;
-       bottom: 0;
-}
-.oo-ui-menuLayout.oo-ui-menuLayout-showMenu.oo-ui-menuLayout-after > .oo-ui-menuLayout-content {
-       bottom: 0 !important;
-       left: 0 !important;
-       top: 0 !important;
-}
-.oo-ui-menuLayout.oo-ui-menuLayout-showMenu.oo-ui-menuLayout-bottom > .oo-ui-menuLayout-menu {
-       width: auto !important;
-       right: 0;
-       bottom: 0;
-       left: 0;
-}
-.oo-ui-menuLayout.oo-ui-menuLayout-showMenu.oo-ui-menuLayout-bottom > .oo-ui-menuLayout-content {
-       left: 0 !important;
-       top: 0 !important;
-       right: 0 !important;
-}
-.oo-ui-menuLayout.oo-ui-menuLayout-showMenu.oo-ui-menuLayout-before > .oo-ui-menuLayout-menu {
-       height: auto !important;
-       bottom: 0;
-       left: 0;
-       top: 0;
-}
-.oo-ui-menuLayout.oo-ui-menuLayout-showMenu.oo-ui-menuLayout-before > .oo-ui-menuLayout-content {
-       top: 0 !important;
-       right: 0 !important;
-       bottom: 0 !important;
-}
-.oo-ui-panelLayout {
-       position: relative;
-}
-.oo-ui-panelLayout-scrollable {
-       overflow-y: auto;
-}
-.oo-ui-panelLayout-expanded {
-       position: absolute;
-       top: 0;
-       left: 0;
-       right: 0;
-       bottom: 0;
-}
-.oo-ui-panelLayout-padded {
-       padding: 1.25em;
-}
-.oo-ui-panelLayout-framed {
-       border: 1px solid #aaaaaa;
-       border-radius: 2px;
-       box-shadow: 0 0.15em 0 0 rgba(0, 0, 0, 0.15);
-}
-.oo-ui-panelLayout-padded.oo-ui-panelLayout-framed {
-       margin: 1em 0;
-}
-.oo-ui-stackLayout-continuous > .oo-ui-panelLayout {
-       display: block;
-       position: relative;
-}
-.oo-ui-horizontalLayout > .oo-ui-widget {
-       display: inline-block;
-       vertical-align: middle;
-}
-.oo-ui-horizontalLayout > .oo-ui-layout {
-       display: inline-block;
-}
-.oo-ui-horizontalLayout > .oo-ui-layout,
-.oo-ui-horizontalLayout > .oo-ui-widget {
-       margin-right: 0.5em;
-}
-.oo-ui-horizontalLayout > .oo-ui-layout:last-child,
-.oo-ui-horizontalLayout > .oo-ui-widget:last-child {
-       margin-right: 0;
-}
-.oo-ui-horizontalLayout > .oo-ui-layout {
-       margin-bottom: 0;
-}
-.oo-ui-popupTool .oo-ui-popupWidget-popup,
-.oo-ui-popupTool .oo-ui-popupWidget-anchor {
-       z-index: 4;
-}
-.oo-ui-popupTool .oo-ui-popupWidget {
-       /* @noflip */
-       margin-left: 1.25em;
-}
-.oo-ui-toolGroupTool > .oo-ui-popupToolGroup {
-       border: 0;
-       border-radius: 0;
-       margin: 0;
-}
-.oo-ui-toolGroupTool > .oo-ui-toolGroup {
-       border-right: none;
-}
-.oo-ui-toolGroupTool > .oo-ui-popupToolGroup > .oo-ui-popupToolGroup-handle {
-       height: 2.5em;
-       padding: 0.3125em;
-}
-.oo-ui-toolGroupTool > .oo-ui-popupToolGroup > .oo-ui-popupToolGroup-handle .oo-ui-iconElement-icon {
-       height: 2.5em;
-       width: 1.875em;
-}
-.oo-ui-toolGroupTool > .oo-ui-popupToolGroup.oo-ui-labelElement > .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
-       line-height: 2.1em;
-}
-.oo-ui-toolGroup {
-       display: inline-block;
-       vertical-align: middle;
-       border-radius: 0;
-       border-right: 1px solid #dddddd;
-}
-.oo-ui-toolGroup-empty {
-       display: none;
-}
-.oo-ui-toolGroup .oo-ui-tool-link {
-       text-decoration: none;
-}
-.oo-ui-toolbar-narrow .oo-ui-toolGroup + .oo-ui-toolGroup {
-       margin-left: 0;
-}
-.oo-ui-toolGroup .oo-ui-toolGroup .oo-ui-widget-enabled {
-       border-right: none !important;
-}
-.oo-ui-barToolGroup > .oo-ui-iconElement-icon,
-.oo-ui-barToolGroup > .oo-ui-labelElement-label {
-       display: none;
-}
-.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool > .oo-ui-tool-link {
-       cursor: pointer;
-}
-.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool {
-       display: inline-block;
-       position: relative;
-       vertical-align: top;
-}
-.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool > .oo-ui-tool-link {
-       display: block;
-}
-.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool > .oo-ui-tool-link .oo-ui-tool-accel {
-       display: none;
-}
-.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-iconElement > .oo-ui-tool-link .oo-ui-iconElement-icon {
-       display: inline-block;
-       vertical-align: top;
-}
-.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-iconElement > .oo-ui-tool-link .oo-ui-tool-title {
-       display: none;
-}
-.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-iconElement.oo-ui-tool-with-label > .oo-ui-tool-link .oo-ui-tool-title {
-       display: inline;
-}
-.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-disabled > .oo-ui-tool-link {
-       outline: 0;
-       cursor: default;
-}
-.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool > .oo-ui-tool-link {
-       height: 1.875em;
-       padding: 0.625em;
-}
-.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool > .oo-ui-tool-link .oo-ui-iconElement-icon {
-       height: 1.875em;
-       width: 1.875em;
-}
-.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool > .oo-ui-tool-link .oo-ui-tool-title {
-       line-height: 2.1em;
-       padding: 0 0.4em;
-}
-.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-enabled:hover {
-       border-color: rgba(0, 0, 0, 0.2);
-       background-color: #eeeeee;
-}
-.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool > a.oo-ui-tool-link .oo-ui-tool-title {
-       color: #555555;
-}
-.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-tool-active.oo-ui-widget-enabled {
-       border-color: rgba(0, 0, 0, 0.2);
-       box-shadow: inset 0 0.07em 0.07em 0 rgba(0, 0, 0, 0.07);
-       background-color: #e5e5e5;
-}
-.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-tool-active.oo-ui-widget-enabled:hover {
-       background-color: #eeeeee;
-}
-.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-tool-active.oo-ui-widget-enabled + .oo-ui-tool-active.oo-ui-widget-enabled {
-       border-left-color: rgba(0, 0, 0, 0.1);
-}
-.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-disabled > .oo-ui-tool-link .oo-ui-tool-title {
-       color: #cccccc;
-}
-.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-disabled > .oo-ui-tool-link .oo-ui-iconElement-icon {
-       opacity: 0.2;
-}
-.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-enabled > .oo-ui-tool-link .oo-ui-iconElement-icon {
-       opacity: 0.7;
-}
-.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-enabled:hover > .oo-ui-tool-link .oo-ui-iconElement-icon {
-       opacity: 0.9;
-}
-.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-enabled:active {
-       background-color: #e7e7e7;
-}
-.oo-ui-barToolGroup.oo-ui-widget-disabled > .oo-ui-toolGroup-tools > .oo-ui-tool > a.oo-ui-tool-link .oo-ui-tool-title {
-       color: #cccccc;
-}
-.oo-ui-barToolGroup.oo-ui-widget-disabled > .oo-ui-toolGroup-tools > .oo-ui-tool > a.oo-ui-tool-link .oo-ui-iconElement-icon {
-       opacity: 0.2;
-}
-.oo-ui-popupToolGroup {
-       position: relative;
-       height: 3.125em;
-       min-width: 2em;
-}
-.oo-ui-popupToolGroup-handle {
-       display: block;
-       cursor: pointer;
-}
-.oo-ui-popupToolGroup-handle .oo-ui-indicatorElement-indicator,
-.oo-ui-popupToolGroup-handle .oo-ui-iconElement-icon {
-       position: absolute;
-}
-.oo-ui-popupToolGroup.oo-ui-widget-disabled .oo-ui-popupToolGroup-handle {
-       outline: 0;
-       cursor: default;
-}
-.oo-ui-popupToolGroup .oo-ui-toolGroup-tools {
-       display: none;
-       position: absolute;
-       z-index: 4;
-}
-.oo-ui-popupToolGroup-active.oo-ui-widget-enabled > .oo-ui-toolGroup-tools {
-       display: block;
-}
-.oo-ui-popupToolGroup-left > .oo-ui-toolGroup-tools {
-       left: 0;
-}
-.oo-ui-popupToolGroup-right > .oo-ui-toolGroup-tools {
-       right: 0;
-}
-.oo-ui-popupToolGroup .oo-ui-tool-link {
-       display: table;
-       width: 100%;
-       vertical-align: middle;
-       white-space: nowrap;
-}
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-iconElement-icon,
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel,
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title {
-       display: table-cell;
-       vertical-align: middle;
-}
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel {
-       text-align: right;
-}
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel:not(:empty) {
-       padding-left: 3em;
-}
-.oo-ui-toolbar-narrow .oo-ui-popupToolGroup {
-       min-width: 1.875em;
-}
-.oo-ui-popupToolGroup.oo-ui-iconElement {
-       min-width: 3.125em;
-}
-.oo-ui-toolbar-narrow .oo-ui-popupToolGroup.oo-ui-iconElement {
-       min-width: 2.5em;
-}
-.oo-ui-popupToolGroup.oo-ui-indicatorElement.oo-ui-iconElement {
-       min-width: 4.375em;
-}
-.oo-ui-toolbar-narrow .oo-ui-popupToolGroup.oo-ui-indicatorElement.oo-ui-iconElement {
-       min-width: 3.75em;
-}
-.oo-ui-popupToolGroup.oo-ui-labelElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
-       line-height: 2.6em;
-       margin: 0 1em;
-}
-.oo-ui-toolbar-narrow .oo-ui-popupToolGroup.oo-ui-labelElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
-       margin: 0 0.5em;
-}
-.oo-ui-popupToolGroup.oo-ui-labelElement.oo-ui-iconElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
-       margin-left: 3em;
-}
-.oo-ui-toolbar-narrow .oo-ui-popupToolGroup.oo-ui-labelElement.oo-ui-iconElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
-       margin-left: 2.5em;
-}
-.oo-ui-popupToolGroup.oo-ui-labelElement.oo-ui-indicatorElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
-       margin-right: 2em;
-}
-.oo-ui-toolbar-narrow .oo-ui-popupToolGroup.oo-ui-labelElement.oo-ui-indicatorElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
-       margin-right: 1.75em;
-}
-.oo-ui-popupToolGroup.oo-ui-widget-enabled .oo-ui-popupToolGroup-handle:hover {
-       background-color: #eeeeee;
-}
-.oo-ui-popupToolGroup.oo-ui-widget-enabled .oo-ui-popupToolGroup-handle:active {
-       background-color: #e5e5e5;
-}
-.oo-ui-popupToolGroup-handle {
-       padding: 0.3125em;
-       height: 2.5em;
-}
-.oo-ui-popupToolGroup-handle .oo-ui-indicatorElement-indicator {
-       width: 0.9375em;
-       height: 1.625em;
-       margin: 0.78125em 0.5em;
-       top: 0;
-       right: 0;
-       opacity: 0.3;
-}
-.oo-ui-toolbar-narrow .oo-ui-popupToolGroup-handle .oo-ui-indicatorElement-indicator {
-       right: -0.3125em;
-}
-.oo-ui-popupToolGroup-handle .oo-ui-iconElement-icon {
-       width: 1.875em;
-       height: 2.6em;
-       margin: 0.25em;
-       top: 0;
-       left: 0.3125em;
-       opacity: 0.7;
-}
-.oo-ui-toolbar-narrow .oo-ui-popupToolGroup-handle .oo-ui-iconElement-icon {
-       left: 0;
-}
-.oo-ui-popupToolGroup-header {
-       line-height: 2.6em;
-       margin: 0 0.6em;
-       font-weight: bold;
-}
-.oo-ui-popupToolGroup-active.oo-ui-widget-enabled {
-       border-bottom-left-radius: 0;
-       border-bottom-right-radius: 0;
-       box-shadow: inset 0 0.07em 0.07em 0 rgba(0, 0, 0, 0.07);
-       background-color: #eeeeee;
-}
-.oo-ui-popupToolGroup .oo-ui-toolGroup-tools {
-       top: 3.125em;
-       margin: 0 -1px;
-       border: 1px solid #cccccc;
-       background-color: #ffffff;
-       box-shadow: 0 2px 3px rgba(0, 0, 0, 0.2);
-       min-width: 16em;
-}
-.oo-ui-popupToolGroup .oo-ui-tool-link {
-       padding: 0.4em 0.625em;
-       box-sizing: border-box;
-}
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-iconElement-icon {
-       height: 2.5em;
-       width: 1.875em;
-       min-width: 1.875em;
-}
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title {
-       padding-left: 0.5em;
-       color: #555555;
-}
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel,
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title {
-       line-height: 2em;
-}
-.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel {
-       color: #888888;
-}
-.oo-ui-listToolGroup .oo-ui-tool {
-       display: block;
-       -webkit-box-sizing: border-box;
-          -moz-box-sizing: border-box;
-               box-sizing: border-box;
-}
-.oo-ui-listToolGroup .oo-ui-tool-link {
-       cursor: pointer;
-}
-.oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link {
-       cursor: default;
-}
-.oo-ui-listToolGroup.oo-ui-popupToolGroup-active {
-       border-color: rgba(0, 0, 0, 0.2);
-}
-.oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-enabled:hover {
-       border-color: rgba(0, 0, 0, 0.2);
-       background-color: #eeeeee;
-}
-.oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-enabled:active {
-       background-color: #e7e7e7;
-}
-.oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-enabled:hover .oo-ui-tool-link .oo-ui-iconElement-icon {
-       opacity: 0.9;
-}
-.oo-ui-listToolGroup .oo-ui-tool-active.oo-ui-widget-enabled {
-       border-color: rgba(0, 0, 0, 0.1);
-       box-shadow: inset 0 0.07em 0.07em 0 rgba(0, 0, 0, 0.07);
-       background-color: #e5e5e5;
-}
-.oo-ui-listToolGroup .oo-ui-tool-active.oo-ui-widget-enabled + .oo-ui-tool-active.oo-ui-widget-enabled {
-       border-top-color: rgba(0, 0, 0, 0.1);
-}
-.oo-ui-listToolGroup .oo-ui-tool-active.oo-ui-widget-enabled:hover {
-       border-color: rgba(0, 0, 0, 0.2);
-       background-color: #eeeeee;
-}
-.oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-tool-title {
-       color: #cccccc;
-}
-.oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-tool-accel {
-       color: #dddddd;
-}
-.oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-iconElement-icon {
-       opacity: 0.2;
-}
-.oo-ui-listToolGroup.oo-ui-widget-disabled {
-       color: #cccccc;
-}
-.oo-ui-listToolGroup.oo-ui-widget-disabled .oo-ui-indicatorElement-indicator,
-.oo-ui-listToolGroup.oo-ui-widget-disabled .oo-ui-iconElement-icon {
-       opacity: 0.2;
-}
-.oo-ui-menuToolGroup .oo-ui-tool {
-       display: block;
-}
-.oo-ui-menuToolGroup .oo-ui-tool-link {
-       cursor: pointer;
-}
-.oo-ui-menuToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link {
-       cursor: default;
-}
-.oo-ui-menuToolGroup .oo-ui-popupToolGroup-handle {
-       min-width: 10em;
-}
-.oo-ui-toolbar-narrow .oo-ui-menuToolGroup .oo-ui-popupToolGroup-handle {
-       min-width: 8.125em;
-}
-.oo-ui-menuToolGroup .oo-ui-tool-link .oo-ui-iconElement-icon {
-       background-image: none;
-}
-.oo-ui-menuToolGroup .oo-ui-tool-active .oo-ui-tool-link .oo-ui-iconElement-icon {
-       background-image: url("themes/mediawiki/images/icons/check.png");
-       background-image: -webkit-linear-gradient(transparent, transparent), /* @embed */ url("themes/mediawiki/images/icons/check.svg");
-       background-image:         linear-gradient(transparent, transparent), /* @embed */ url("themes/mediawiki/images/icons/check.svg");
-       background-image:      -o-linear-gradient(transparent, transparent), url("themes/mediawiki/images/icons/check.png");
-       background-size: contain;
-       background-position: center center;
-       background-repeat: no-repeat;
-}
-.oo-ui-menuToolGroup .oo-ui-tool.oo-ui-widget-enabled:hover {
-       background-color: #eeeeee;
-}
-.oo-ui-menuToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-tool-title {
-       color: #cccccc;
-}
-.oo-ui-menuToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-iconElement-icon {
-       opacity: 0.2;
-}
-.oo-ui-menuToolGroup.oo-ui-widget-disabled {
-       color: #cccccc;
-}
-.oo-ui-menuToolGroup.oo-ui-widget-disabled .oo-ui-indicatorElement-indicator,
-.oo-ui-menuToolGroup.oo-ui-widget-disabled .oo-ui-iconElement-icon {
-       opacity: 0.2;
-}
-.oo-ui-toolbar {
-       clear: both;
-}
-.oo-ui-toolbar-bar {
-       line-height: 1em;
-       position: relative;
-}
-.oo-ui-toolbar-actions {
-       float: right;
-}
-.oo-ui-toolbar-actions .oo-ui-toolbar {
-       display: inline-block;
-}
-.oo-ui-toolbar-tools {
-       display: inline;
-       white-space: nowrap;
-}
-.oo-ui-toolbar-narrow .oo-ui-toolbar-tools {
-       white-space: normal;
-}
-.oo-ui-toolbar-tools .oo-ui-tool {
-       white-space: normal;
-}
-.oo-ui-toolbar-tools,
-.oo-ui-toolbar-actions,
-.oo-ui-toolbar-shadow {
-       -webkit-touch-callout: none;
-       -webkit-user-select: none;
-          -moz-user-select: none;
-           -ms-user-select: none;
-               user-select: none;
-}
-.oo-ui-toolbar-actions .oo-ui-popupWidget {
-       -webkit-touch-callout: default;
-       -webkit-user-select: all;
-          -moz-user-select: all;
-           -ms-user-select: all;
-               user-select: all;
-}
-.oo-ui-toolbar-shadow {
-       background-position: left top;
-       background-repeat: repeat-x;
-       position: absolute;
-       width: 100%;
-       pointer-events: none;
-}
-.oo-ui-toolbar-bar {
-       border-bottom: 1px solid #cccccc;
-       background-color: #ffffff;
-       box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
-       font-weight: 500;
-       color: #555555;
-}
-.oo-ui-toolbar-bar .oo-ui-toolbar-bar {
-       border: 0;
-       background: none;
-       box-shadow: none;
-}
-.oo-ui-toolbar-actions > .oo-ui-buttonElement.oo-ui-labelElement {
-       margin: 0;
-}
-.oo-ui-toolbar-actions > .oo-ui-buttonElement.oo-ui-labelElement > .oo-ui-buttonElement-button {
-       border: 0;
-       border-radius: 0;
-       margin: 0;
-       padding: 0 0.3125em;
-}
-.oo-ui-toolbar-actions > .oo-ui-buttonElement.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
-       margin: 0 1em;
-       line-height: 3.125em;
-}
-.oo-ui-optionWidget {
-       position: relative;
-       display: block;
-       padding: 0.25em 0.5em;
-       border: 0;
-}
-.oo-ui-optionWidget.oo-ui-widget-enabled {
-       cursor: pointer;
-}
-.oo-ui-optionWidget.oo-ui-labelElement .oo-ui-labelElement-label {
-       display: block;
-       white-space: nowrap;
-       text-overflow: ellipsis;
-       overflow: hidden;
-}
-.oo-ui-optionWidget-highlighted {
-       background-color: #eeeeee;
-}
-.oo-ui-optionWidget .oo-ui-labelElement-label {
-       line-height: 1.5em;
-}
-.oo-ui-selectWidget-depressed .oo-ui-optionWidget-selected,
-.oo-ui-selectWidget-pressed .oo-ui-optionWidget-pressed,
-.oo-ui-selectWidget-pressed .oo-ui-optionWidget-pressed.oo-ui-optionWidget-highlighted,
-.oo-ui-selectWidget-pressed .oo-ui-optionWidget-pressed.oo-ui-optionWidget-highlighted.oo-ui-optionWidget-selected {
-       background-color: #d0d0d0;
-}
-.oo-ui-optionWidget.oo-ui-widget-disabled {
-       color: #cccccc;
-}
-.oo-ui-decoratedOptionWidget {
-       padding: 0.5em 2em 0.5em 3em;
-}
-.oo-ui-decoratedOptionWidget .oo-ui-iconElement-icon,
-.oo-ui-decoratedOptionWidget .oo-ui-indicatorElement-indicator {
-       position: absolute;
-}
-.oo-ui-decoratedOptionWidget.oo-ui-iconElement .oo-ui-iconElement-icon,
-.oo-ui-decoratedOptionWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
-       top: 0;
-       height: 100%;
-}
-.oo-ui-decoratedOptionWidget.oo-ui-iconElement .oo-ui-iconElement-icon {
-       width: 1.875em;
-       left: 0.5em;
-}
-.oo-ui-decoratedOptionWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
-       width: 0.9375em;
-       right: 0.5em;
-}
-.oo-ui-decoratedOptionWidget.oo-ui-widget-disabled .oo-ui-iconElement-icon,
-.oo-ui-decoratedOptionWidget.oo-ui-widget-disabled .oo-ui-indicatorElement-indicator {
-       opacity: 0.2;
-}
-.oo-ui-buttonSelectWidget {
-       display: inline-block;
-       white-space: nowrap;
-       border-radius: 2px;
-       margin-right: 0.5em;
-}
-.oo-ui-buttonSelectWidget:last-child {
-       margin-right: 0;
-}
-.oo-ui-buttonSelectWidget .oo-ui-buttonOptionWidget .oo-ui-buttonElement-button {
-       border-radius: 0;
-       margin-left: -1px;
-}
-.oo-ui-buttonSelectWidget .oo-ui-buttonOptionWidget:first-child .oo-ui-buttonElement-button {
-       border-bottom-left-radius: 2px;
-       border-top-left-radius: 2px;
-       margin-left: 0;
-}
-.oo-ui-buttonSelectWidget .oo-ui-buttonOptionWidget:last-child .oo-ui-buttonElement-button {
-       border-bottom-right-radius: 2px;
-       border-top-right-radius: 2px;
-}
-.oo-ui-buttonOptionWidget {
-       display: inline-block;
-       padding: 0;
-       background-color: transparent;
-}
-.oo-ui-buttonOptionWidget .oo-ui-buttonElement-button {
-       position: relative;
-}
-.oo-ui-buttonOptionWidget.oo-ui-iconElement .oo-ui-iconElement-icon,
-.oo-ui-buttonOptionWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
-       position: static;
-       display: inline-block;
-       vertical-align: middle;
-}
-.oo-ui-buttonOptionWidget.oo-ui-iconElement .oo-ui-iconElement-icon {
-       margin-top: 0;
-}
-.oo-ui-buttonOptionWidget.oo-ui-optionWidget-selected,
-.oo-ui-buttonOptionWidget.oo-ui-optionWidget-pressed,
-.oo-ui-buttonOptionWidget.oo-ui-optionWidget-highlighted {
-       background-color: transparent;
-}
-.oo-ui-buttonOptionWidget.oo-ui-widget-disabled .oo-ui-iconElement-icon,
-.oo-ui-buttonOptionWidget.oo-ui-widget-disabled .oo-ui-indicatorElement-indicator {
-       opacity: 1;
-}
-.oo-ui-radioOptionWidget {
-       cursor: default;
-       padding: 0.25em 0;
-       background-color: transparent;
-}
-.oo-ui-radioOptionWidget .oo-ui-radioInputWidget,
-.oo-ui-radioOptionWidget.oo-ui-labelElement .oo-ui-labelElement-label {
-       display: inline-block;
-       vertical-align: middle;
-}
-.oo-ui-radioOptionWidget.oo-ui-optionWidget-selected,
-.oo-ui-radioOptionWidget.oo-ui-optionWidget-pressed,
-.oo-ui-radioOptionWidget.oo-ui-optionWidget-highlighted {
-       background-color: transparent;
-}
-.oo-ui-radioOptionWidget.oo-ui-labelElement .oo-ui-labelElement-label {
-       padding: 0.25em 0.25em 0.25em 1em;
-}
-.oo-ui-radioOptionWidget .oo-ui-radioInputWidget {
-       margin-right: 0;
-}
-.oo-ui-labelWidget {
-       display: inline-block;
-}
-.oo-ui-iconWidget {
-       display: inline-block;
-       vertical-align: middle;
-       line-height: 2.5em;
-       width: 1.875em;
-       height: 1.875em;
-}
-.oo-ui-iconWidget.oo-ui-widget-disabled {
-       opacity: 0.2;
-}
-.oo-ui-indicatorWidget {
-       display: inline-block;
-       vertical-align: middle;
-       line-height: 2.5em;
-       width: 0.9375em;
-       height: 0.9375em;
-       margin: 0.46875em;
-}
-.oo-ui-indicatorWidget.oo-ui-widget-disabled {
-       opacity: 0.2;
-}
-.oo-ui-buttonWidget {
-       display: inline-block;
-       vertical-align: middle;
-       margin-right: 0.5em;
-}
-.oo-ui-buttonWidget:last-child {
-       margin-right: 0;
-}
-.oo-ui-buttonGroupWidget {
-       display: inline-block;
-       white-space: nowrap;
-       border-radius: 2px;
-       margin-right: 0.5em;
-}
-.oo-ui-buttonGroupWidget:last-child {
-       margin-right: 0;
-}
-.oo-ui-buttonGroupWidget .oo-ui-buttonElement {
-       margin-right: 0;
-}
-.oo-ui-buttonGroupWidget .oo-ui-buttonElement:last-child {
-       margin-right: 0;
-}
-.oo-ui-buttonGroupWidget .oo-ui-buttonElement-framed .oo-ui-buttonElement-button {
-       border-radius: 0;
-       margin-left: -1px;
-}
-.oo-ui-buttonGroupWidget .oo-ui-buttonElement-framed:first-child .oo-ui-buttonElement-button {
-       border-bottom-left-radius: 2px;
-       border-top-left-radius: 2px;
-       margin-left: 0;
-}
-.oo-ui-buttonGroupWidget .oo-ui-buttonElement-framed:last-child .oo-ui-buttonElement-button {
-       border-bottom-right-radius: 2px;
-       border-top-right-radius: 2px;
-}
-.oo-ui-toggleButtonWidget {
-       display: inline-block;
-       vertical-align: middle;
-       margin-right: 0.5em;
-}
-.oo-ui-toggleButtonWidget:last-child {
-       margin-right: 0;
-}
-.oo-ui-toggleSwitchWidget {
-       position: relative;
-       display: inline-block;
-       vertical-align: middle;
-       overflow: hidden;
-       -webkit-box-sizing: border-box;
-          -moz-box-sizing: border-box;
-               box-sizing: border-box;
-       -webkit-transform: translateZ(0);
-          -moz-transform: translateZ(0);
-           -ms-transform: translateZ(0);
-               transform: translateZ(0);
-       height: 2em;
-       width: 3.5em;
-       border: 1px solid #777777;
-       border-radius: 1em;
-       background-color: #ffffff;
-       margin-right: 0.5em;
-       -webkit-transition: background-color 100ms ease, border-color 100ms ease;
-          -moz-transition: background-color 100ms ease, border-color 100ms ease;
-               transition: background-color 100ms ease, border-color 100ms ease;
-}
-.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled {
-       cursor: pointer;
-}
-.oo-ui-toggleSwitchWidget-grip {
-       position: absolute;
-       display: block;
-       -webkit-box-sizing: border-box;
-          -moz-box-sizing: border-box;
-               box-sizing: border-box;
-}
-.oo-ui-toggleSwitchWidget .oo-ui-toggleSwitchWidget-glow {
-       position: absolute;
-       top: 0;
-       bottom: 0;
-       right: 0;
-       left: 0;
-       -webkit-touch-callout: none;
-       -webkit-user-select: none;
-          -moz-user-select: none;
-           -ms-user-select: none;
-               user-select: none;
-}
-.oo-ui-toggleWidget-off .oo-ui-toggleSwitchWidget-glow {
-       display: none;
-}
-.oo-ui-toggleSwitchWidget:last-child {
-       margin-right: 0;
-}
-.oo-ui-toggleSwitchWidget:before {
-       content: "";
-       display: block;
-       position: absolute;
-       top: 0;
-       left: 0;
-       bottom: 0;
-       right: 0;
-       border: 1px solid transparent;
-       border-radius: 1em;
-       z-index: 1;
-}
-.oo-ui-toggleSwitchWidget-grip {
-       top: 0.35em;
-       width: 1.2em;
-       height: 1.2em;
-       border-radius: 1.2em;
-       background-color: #555555;
-       -webkit-transition: left 100ms ease, margin-left 100ms ease;
-          -moz-transition: left 100ms ease, margin-left 100ms ease;
-               transition: left 100ms ease, margin-left 100ms ease;
-}
-.oo-ui-toggleSwitchWidget-glow {
-       display: none;
-}
-.oo-ui-toggleSwitchWidget.oo-ui-toggleWidget-on .oo-ui-toggleSwitchWidget-grip {
-       left: 1.9em;
-       margin-left: -2px;
-}
-.oo-ui-toggleSwitchWidget.oo-ui-toggleWidget-off .oo-ui-toggleSwitchWidget-grip {
-       left: 0.4em;
-       margin-left: 0;
-}
-.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled.oo-ui-toggleWidget-on {
-       background-color: #347bff;
-       border-color: #347bff;
-}
-.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled.oo-ui-toggleWidget-on .oo-ui-toggleSwitchWidget-grip {
-       background-color: #ffffff;
-       box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1);
-}
-.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:hover {
-       border-color: #2962cc;
-}
-.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:hover.oo-ui-toggleWidget-on {
-       background-color: #2962cc;
-       border-color: #2962cc;
-}
-.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:focus {
-       border-color: #347bff;
-       outline: none;
-}
-.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:focus.oo-ui-toggleWidget-on {
-       border-color: #347bff;
-}
-.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:focus.oo-ui-toggleWidget-on:before {
-       border-color: #ffffff;
-}
-.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:active,
-.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:active:hover {
-       background-color: #347bff;
-       border-color: #347bff;
-}
-.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:active .oo-ui-toggleSwitchWidget-grip,
-.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:active:hover .oo-ui-toggleSwitchWidget-grip {
-       background-color: #ffffff;
-       box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1);
-}
-.oo-ui-toggleSwitchWidget.oo-ui-widget-disabled {
-       background: #dddddd;
-       border-color: #dddddd;
-       outline: 0;
-}
-.oo-ui-toggleSwitchWidget.oo-ui-widget-disabled .oo-ui-toggleSwitchWidget-grip {
-       background: #ffffff;
-}
-.oo-ui-progressBarWidget {
-       max-width: 50em;
-       background-color: #ffffff;
-       border: 1px solid #cccccc;
-       border-radius: 2px;
-       overflow: hidden;
-}
-.oo-ui-progressBarWidget-bar {
-       height: 1em;
-       background: #dddddd;
-       -webkit-transition: width 200ms, margin-left 200ms;
-          -moz-transition: width 200ms, margin-left 200ms;
-               transition: width 200ms, margin-left 200ms;
-}
-.oo-ui-progressBarWidget-indeterminate .oo-ui-progressBarWidget-bar {
-       -webkit-animation: oo-ui-progressBarWidget-slide 2s infinite linear;
-          -moz-animation: oo-ui-progressBarWidget-slide 2s infinite linear;
-               animation: oo-ui-progressBarWidget-slide 2s infinite linear;
-       width: 40%;
-       margin-left: -10%;
-       border-left-width: 1px;
-}
-.oo-ui-progressBarWidget.oo-ui-widget-disabled {
-       opacity: 0.6;
-}
-.oo-ui-popupWidget {
-       position: absolute;
-       /* @noflip */
-       left: 0;
-}
-.oo-ui-popupWidget-popup {
-       position: relative;
-       overflow: hidden;
-       z-index: 1;
-}
-.oo-ui-popupWidget-anchor {
-       display: none;
-       z-index: 1;
-}
-.oo-ui-popupWidget-anchored .oo-ui-popupWidget-anchor {
-       display: block;
-       position: absolute;
-       top: 0;
-       /* @noflip */
-       left: 0;
-       background-repeat: no-repeat;
-}
-.oo-ui-popupWidget-head {
-       -webkit-touch-callout: none;
-       -webkit-user-select: none;
-          -moz-user-select: none;
-           -ms-user-select: none;
-               user-select: none;
-}
-.oo-ui-popupWidget-head > .oo-ui-buttonWidget {
-       float: right;
-}
-.oo-ui-popupWidget-head > .oo-ui-labelElement-label {
-       float: left;
-       cursor: default;
-}
-.oo-ui-popupWidget-body {
-       clear: both;
-       overflow: hidden;
-}
-.oo-ui-popupWidget-popup {
-       background-color: #ffffff;
-       border: 1px solid #aaaaaa;
-       border-radius: 2px;
-       box-shadow: 0 0.15em 0 0 rgba(0, 0, 0, 0.15);
-}
-.oo-ui-popupWidget-anchored .oo-ui-popupWidget-popup {
-       margin-top: 9px;
-}
-.oo-ui-popupWidget-anchored .oo-ui-popupWidget-anchor:before,
-.oo-ui-popupWidget-anchored .oo-ui-popupWidget-anchor:after {
-       content: "";
-       position: absolute;
-       width: 0;
-       height: 0;
-       border-style: solid;
-       border-color: transparent;
-       border-top: 0;
-}
-.oo-ui-popupWidget-anchored .oo-ui-popupWidget-anchor:before {
-       bottom: -10px;
-       left: -9px;
-       border-bottom-color: #888888;
-       border-width: 10px;
-}
-.oo-ui-popupWidget-anchored .oo-ui-popupWidget-anchor:after {
-       bottom: -10px;
-       left: -8px;
-       border-bottom-color: #ffffff;
-       border-width: 9px;
-}
-.oo-ui-popupWidget-transitioning .oo-ui-popupWidget-popup {
-       -webkit-transition: width 100ms ease, height 100ms ease, left 100ms ease;
-          -moz-transition: width 100ms ease, height 100ms ease, left 100ms ease;
-               transition: width 100ms ease, height 100ms ease, left 100ms ease;
-}
-.oo-ui-popupWidget-head {
-       height: 2.5em;
-}
-.oo-ui-popupWidget-head > .oo-ui-buttonWidget {
-       margin: 0.25em;
-}
-.oo-ui-popupWidget-head > .oo-ui-labelElement-label {
-       margin: 0.75em 1em;
-}
-.oo-ui-popupWidget-body-padded {
-       padding: 0 1em;
-}
-.oo-ui-popupButtonWidget {
-       position: relative;
-}
-.oo-ui-popupButtonWidget .oo-ui-popupWidget {
-       position: absolute;
-       cursor: auto;
-}
-.oo-ui-popupButtonWidget.oo-ui-buttonElement-frameless > .oo-ui-popupWidget {
-       /* @noflip */
-       left: 1em;
-}
-.oo-ui-popupButtonWidget.oo-ui-buttonElement-framed > .oo-ui-popupWidget {
-       /* @noflip */
-       left: 1.75em;
-}
-.oo-ui-inputWidget {
-       margin-right: 0.5em;
-}
-.oo-ui-inputWidget:last-child {
-       margin-right: 0;
-}
-.oo-ui-buttonInputWidget {
-       display: inline-block;
-       vertical-align: middle;
-}
-.oo-ui-buttonInputWidget > button,
-.oo-ui-buttonInputWidget > input {
-       border: 0;
-       padding: 0;
-       background-color: transparent;
-}
-.oo-ui-checkboxInputWidget {
-       position: relative;
-       line-height: 1.6em;
-       white-space: nowrap;
-}
-.oo-ui-checkboxInputWidget * {
-       font: inherit;
-       vertical-align: middle;
-}
-.oo-ui-checkboxInputWidget input[type="checkbox"] {
-       opacity: 0;
-       z-index: 1;
-       position: relative;
-       cursor: pointer;
-       margin: 0;
-       width: 1.6em;
-       height: 1.6em;
-       max-width: none;
-}
-.oo-ui-checkboxInputWidget input[type="checkbox"] + span {
-       -webkit-transition: background-size 200ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
-          -moz-transition: background-size 200ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
-               transition: background-size 200ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
-       -webkit-box-sizing: border-box;
-          -moz-box-sizing: border-box;
-               box-sizing: border-box;
-       position: absolute;
-       left: 0;
-       border-radius: 2px;
-       width: 1.6em;
-       height: 1.6em;
-       background-color: white;
-       border: 1px solid #777777;
-       background-image: url("themes/mediawiki/images/icons/check-constructive.png");
-       background-image: -webkit-linear-gradient(transparent, transparent), /* @embed */ url("themes/mediawiki/images/icons/check-constructive.svg");
-       background-image:         linear-gradient(transparent, transparent), /* @embed */ url("themes/mediawiki/images/icons/check-constructive.svg");
-       background-image:      -o-linear-gradient(transparent, transparent), url("themes/mediawiki/images/icons/check-constructive.png");
-       background-repeat: no-repeat;
-       background-position: center center;
-       background-origin: border-box;
-       background-size: 0 0;
-}
-.oo-ui-checkboxInputWidget input[type="checkbox"]:checked + span {
-       background-size: 100% 100%;
-}
-.oo-ui-checkboxInputWidget input[type="checkbox"]:active + span {
-       background-color: #cccccc;
-       border-color: #cccccc;
-}
-.oo-ui-checkboxInputWidget input[type="checkbox"]:focus + span {
-       border-width: 2px;
-}
-.oo-ui-checkboxInputWidget input[type="checkbox"]:focus:hover + span,
-.oo-ui-checkboxInputWidget input[type="checkbox"]:hover + span {
-       border-bottom-width: 3px;
-}
-.oo-ui-checkboxInputWidget input[type="checkbox"]:disabled {
-       cursor: default;
-}
-.oo-ui-checkboxInputWidget input[type="checkbox"]:disabled + span {
-       background-color: #dddddd;
-       border-color: #dddddd;
-}
-.oo-ui-checkboxInputWidget input[type="checkbox"]:disabled:checked + span {
-       background-image: url("themes/mediawiki/images/icons/check-invert.png");
-       background-image: -webkit-linear-gradient(transparent, transparent), /* @embed */ url("themes/mediawiki/images/icons/check-invert.svg");
-       background-image:         linear-gradient(transparent, transparent), /* @embed */ url("themes/mediawiki/images/icons/check-invert.svg");
-       background-image:      -o-linear-gradient(transparent, transparent), url("themes/mediawiki/images/icons/check-invert.png");
-}
-.oo-ui-dropdownInputWidget {
-       position: relative;
-       vertical-align: middle;
-       -webkit-box-sizing: border-box;
-          -moz-box-sizing: border-box;
-               box-sizing: border-box;
-       width: 100%;
-       max-width: 50em;
-}
-.oo-ui-dropdownInputWidget select {
-       display: inline-block;
-       width: 100%;
-       resize: none;
-       -webkit-box-sizing: border-box;
-          -moz-box-sizing: border-box;
-               box-sizing: border-box;
-}
-.oo-ui-dropdownInputWidget select {
-       background-color: #ffffff;
-       height: 2.275em;
-       font-size: inherit;
-       font-family: inherit;
-       -webkit-box-sizing: border-box;
-          -moz-box-sizing: border-box;
-               box-sizing: border-box;
-       border: 1px solid #cccccc;
-       border-radius: 2px;
-       padding-left: 1em;
-       vertical-align: middle;
-}
-.oo-ui-dropdownInputWidget.oo-ui-widget-enabled select:hover,
-.oo-ui-dropdownInputWidget.oo-ui-widget-enabled select:focus {
-       border-color: #aaaaaa;
-       outline: none;
-}
-.oo-ui-dropdownInputWidget.oo-ui-widget-disabled select {
-       color: #cccccc;
-       border-color: #dddddd;
-       background-color: #f3f3f3;
-}
-.oo-ui-radioInputWidget {
-       position: relative;
-       line-height: 1.6em;
-       white-space: nowrap;
-}
-.oo-ui-radioInputWidget * {
-       font: inherit;
-       vertical-align: middle;
-}
-.oo-ui-radioInputWidget input[type="radio"] {
-       opacity: 0;
-       z-index: 1;
-       position: relative;
-       cursor: pointer;
-       margin: 0;
-       width: 1.6em;
-       height: 1.6em;
-       max-width: none;
-}
-.oo-ui-radioInputWidget input[type="radio"] + span {
-       -webkit-transition: background-size 200ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
-          -moz-transition: background-size 200ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
-               transition: background-size 200ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
-       -webkit-box-sizing: border-box;
-          -moz-box-sizing: border-box;
-               box-sizing: border-box;
-       position: absolute;
-       left: 0;
-       border-radius: 100%;
-       width: 1.6em;
-       height: 1.6em;
-       background: white;
-       border: 1px solid #777777;
-       background-image: url("themes/mediawiki/images/icons/circle-constructive.png");
-       background-image: -webkit-linear-gradient(transparent, transparent), /* @embed */ url("themes/mediawiki/images/icons/circle-constructive.svg");
-       background-image:         linear-gradient(transparent, transparent), /* @embed */ url("themes/mediawiki/images/icons/circle-constructive.svg");
-       background-image:      -o-linear-gradient(transparent, transparent), url("themes/mediawiki/images/icons/circle-constructive.png");
-       background-repeat: no-repeat;
-       background-position: center center;
-       background-origin: border-box;
-       background-size: 0 0;
-}
-.oo-ui-radioInputWidget input[type="radio"]:checked + span {
-       background-size: 100% 100%;
-}
-.oo-ui-radioInputWidget input[type="radio"]:active + span {
-       background-color: #cccccc;
-       border-color: #cccccc;
-}
-.oo-ui-radioInputWidget input[type="radio"]:focus + span {
-       border-width: 2px;
-}
-.oo-ui-radioInputWidget input[type="radio"]:focus:hover + span,
-.oo-ui-radioInputWidget input[type="radio"]:hover + span {
-       border-bottom-width: 3px;
-}
-.oo-ui-radioInputWidget input[type="radio"]:disabled {
-       cursor: default;
-}
-.oo-ui-radioInputWidget input[type="radio"]:disabled + span {
-       background-color: #dddddd;
-       border-color: #dddddd;
-}
-.oo-ui-radioInputWidget input[type="radio"]:disabled:checked + span {
-       background-image: url("themes/mediawiki/images/icons/circle-invert.png");
-       background-image: -webkit-linear-gradient(transparent, transparent), /* @embed */ url("themes/mediawiki/images/icons/circle-invert.svg");
-       background-image:         linear-gradient(transparent, transparent), /* @embed */ url("themes/mediawiki/images/icons/circle-invert.svg");
-       background-image:      -o-linear-gradient(transparent, transparent), url("themes/mediawiki/images/icons/circle-invert.png");
-}
-.oo-ui-radioSelectInputWidget .oo-ui-fieldLayout {
-       margin-bottom: 0;
-}
-.oo-ui-textInputWidget {
-       position: relative;
-       vertical-align: middle;
-       -webkit-box-sizing: border-box;
-          -moz-box-sizing: border-box;
-               box-sizing: border-box;
-       width: 100%;
-       max-width: 50em;
-}
-.oo-ui-textInputWidget input,
-.oo-ui-textInputWidget textarea {
-       display: inline-block;
-       width: 100%;
-       resize: none;
-       -webkit-box-sizing: border-box;
-          -moz-box-sizing: border-box;
-               box-sizing: border-box;
-}
-.oo-ui-textInputWidget textarea {
-       overflow: auto;
-}
-.oo-ui-textInputWidget input[type="search"] {
-       -webkit-appearance: none;
-}
-.oo-ui-textInputWidget input[type="search"]::-ms-clear {
-       display: none;
-}
-.oo-ui-textInputWidget input[type="search"]::-ms-reveal {
-       display: none;
-}
-.oo-ui-textInputWidget input[type="search"]::-webkit-search-decoration,
-.oo-ui-textInputWidget input[type="search"]::-webkit-search-cancel-button,
-.oo-ui-textInputWidget input[type="search"]::-webkit-search-results-button,
-.oo-ui-textInputWidget input[type="search"]::-webkit-search-results-decoration {
-       display: none;
-}
-.oo-ui-textInputWidget > .oo-ui-iconElement-icon,
-.oo-ui-textInputWidget > .oo-ui-indicatorElement-indicator,
-.oo-ui-textInputWidget > .oo-ui-labelElement-label {
-       display: none;
-}
-.oo-ui-textInputWidget.oo-ui-iconElement > .oo-ui-iconElement-icon,
-.oo-ui-textInputWidget.oo-ui-indicatorElement > .oo-ui-indicatorElement-indicator {
-       display: block;
-       position: absolute;
-       top: 0;
-       height: 100%;
-       -webkit-touch-callout: none;
-       -webkit-user-select: none;
-          -moz-user-select: none;
-           -ms-user-select: none;
-               user-select: none;
-}
-.oo-ui-textInputWidget.oo-ui-widget-enabled > .oo-ui-iconElement-icon,
-.oo-ui-textInputWidget.oo-ui-widget-enabled > .oo-ui-indicatorElement-indicator {
-       cursor: text;
-}
-.oo-ui-textInputWidget.oo-ui-widget-enabled.oo-ui-textInputWidget-type-search > .oo-ui-indicatorElement-indicator {
-       cursor: pointer;
-}
-.oo-ui-textInputWidget.oo-ui-labelElement > .oo-ui-labelElement-label {
-       display: block;
-}
-.oo-ui-textInputWidget > .oo-ui-iconElement-icon {
-       left: 0;
-}
-.oo-ui-textInputWidget > .oo-ui-indicatorElement-indicator {
-       right: 0;
-}
-.oo-ui-textInputWidget > .oo-ui-labelElement-label {
-       position: absolute;
-       top: 0;
-}
-.oo-ui-textInputWidget-labelPosition-after > .oo-ui-labelElement-label {
-       right: 0;
-}
-.oo-ui-textInputWidget-labelPosition-before > .oo-ui-labelElement-label {
-       left: 0;
-}
-.oo-ui-textInputWidget input,
-.oo-ui-textInputWidget textarea {
-       padding: 0.5em;
-       line-height: 1.275em;
-       margin: 0;
-       font-size: inherit;
-       font-family: inherit;
-       background-color: #ffffff;
-       color: #000000;
-       border: 1px solid #cccccc;
-       border-radius: 2px;
-       -webkit-box-sizing: border-box;
-          -moz-box-sizing: border-box;
-               box-sizing: border-box;
-}
-.oo-ui-textInputWidget input.oo-ui-pendingElement-pending,
-.oo-ui-textInputWidget textarea.oo-ui-pendingElement-pending {
-       background-color: transparent;
-}
-.oo-ui-textInputWidget.oo-ui-widget-enabled input,
-.oo-ui-textInputWidget.oo-ui-widget-enabled textarea {
-       box-shadow: inset 0 0 0 0.1em #ffffff;
-       -webkit-transition: border 200ms cubic-bezier(0.39, 0.575, 0.565, 1), box-shadow 200ms cubic-bezier(0.39, 0.575, 0.565, 1);
-          -moz-transition: border 200ms cubic-bezier(0.39, 0.575, 0.565, 1), box-shadow 200ms cubic-bezier(0.39, 0.575, 0.565, 1);
-               transition: border 200ms cubic-bezier(0.39, 0.575, 0.565, 1), box-shadow 200ms cubic-bezier(0.39, 0.575, 0.565, 1);
-}
-.oo-ui-textInputWidget.oo-ui-widget-enabled input:focus,
-.oo-ui-textInputWidget.oo-ui-widget-enabled textarea:focus {
-       outline: none;
-       border-color: #347bff;
-       box-shadow: inset 0 0 0 0.1em #347bff;
-}
-.oo-ui-textInputWidget.oo-ui-widget-enabled input[readonly],
-.oo-ui-textInputWidget.oo-ui-widget-enabled textarea[readonly] {
-       color: #777777;
-       text-shadow: 0 1px 1px #ffffff;
-}
-.oo-ui-textInputWidget.oo-ui-widget-enabled input[readonly]:focus,
-.oo-ui-textInputWidget.oo-ui-widget-enabled textarea[readonly]:focus {
-       border-color: #cccccc;
-       box-shadow: inset 0 0 0 0.1em #cccccc;
-}
-.oo-ui-textInputWidget.oo-ui-widget-enabled.oo-ui-flaggedElement-invalid input,
-.oo-ui-textInputWidget.oo-ui-widget-enabled.oo-ui-flaggedElement-invalid textarea {
-       border-color: #ff0000;
-}
-.oo-ui-textInputWidget.oo-ui-widget-enabled.oo-ui-flaggedElement-invalid input:focus,
-.oo-ui-textInputWidget.oo-ui-widget-enabled.oo-ui-flaggedElement-invalid textarea:focus {
-       border-color: #ff0000;
-       box-shadow: inset 0 0 0 0.1em #ff0000;
-}
-.oo-ui-textInputWidget.oo-ui-widget-disabled input,
-.oo-ui-textInputWidget.oo-ui-widget-disabled textarea {
-       color: #cccccc;
-       text-shadow: 0 1px 1px #ffffff;
-       border-color: #dddddd;
-       background-color: #f3f3f3;
-}
-.oo-ui-textInputWidget.oo-ui-widget-disabled .oo-ui-iconElement-icon,
-.oo-ui-textInputWidget.oo-ui-widget-disabled .oo-ui-indicatorElement-indicator {
-       opacity: 0.2;
-}
-.oo-ui-textInputWidget.oo-ui-widget-disabled .oo-ui-labelElement-label {
-       color: #dddddd;
-       text-shadow: 0 1px 1px #ffffff;
-}
-.oo-ui-textInputWidget.oo-ui-iconElement input,
-.oo-ui-textInputWidget.oo-ui-iconElement textarea {
-       padding-left: 2.875em;
-}
-.oo-ui-textInputWidget.oo-ui-iconElement .oo-ui-iconElement-icon {
-       left: 0;
-       width: 1.875em;
-       max-height: 2.375em;
-       margin-left: 0.5em;
-       height: 100%;
-       background-position: right center;
-}
-.oo-ui-textInputWidget.oo-ui-indicatorElement input,
-.oo-ui-textInputWidget.oo-ui-indicatorElement textarea {
-       padding-right: 2.4875em;
-}
-.oo-ui-textInputWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
-       width: 0.9375em;
-       max-height: 2.375em;
-       margin: 0 0.775em;
-       height: 100%;
-}
-.oo-ui-textInputWidget > .oo-ui-labelElement-label {
-       padding: 0.4em;
-       line-height: 1.5em;
-       color: #888888;
-}
-.oo-ui-textInputWidget-labelPosition-after.oo-ui-indicatorElement > .oo-ui-labelElement-label {
-       margin-right: 2.0875em;
-}
-.oo-ui-textInputWidget-labelPosition-before.oo-ui-iconElement > .oo-ui-labelElement-label {
-       margin-left: 2.475em;
-}
-.oo-ui-menuSelectWidget {
-       position: absolute;
-       background-color: #ffffff;
-       margin-top: -1px;
-       border: 1px solid #aaaaaa;
-       border-radius: 0 0 2px 2px;
-       box-shadow: 0 0.15em 0 0 rgba(0, 0, 0, 0.15);
-}
-.oo-ui-menuSelectWidget input {
-       position: absolute;
-       width: 0;
-       height: 0;
-       overflow: hidden;
-       opacity: 0;
-}
-.oo-ui-menuOptionWidget {
-       position: relative;
-       padding: 0.5em 1em;
-}
-.oo-ui-menuOptionWidget .oo-ui-iconElement-icon {
-       display: none;
-}
-.oo-ui-menuOptionWidget.oo-ui-optionWidget-selected {
-       background-color: transparent;
-}
-.oo-ui-menuOptionWidget.oo-ui-optionWidget-selected .oo-ui-iconElement-icon {
-       display: block;
-}
-.oo-ui-menuOptionWidget.oo-ui-optionWidget-selected {
-       background-color: #d8e6fe;
-       color: rgba(0, 0, 0, 0.8);
-}
-.oo-ui-menuOptionWidget.oo-ui-optionWidget-selected .oo-ui-iconElement-icon {
-       display: none;
-}
-.oo-ui-menuOptionWidget.oo-ui-optionWidget-highlighted {
-       background-color: #eeeeee;
-       color: #000000;
-}
-.oo-ui-menuOptionWidget.oo-ui-optionWidget-selected.oo-ui-menuOptionWidget.oo-ui-optionWidget-highlighted {
-       background-color: #d8e6fe;
-}
-.oo-ui-menuSectionOptionWidget {
-       cursor: default;
-       padding: 0.33em 0.75em;
-       color: #888888;
-}
-.oo-ui-dropdownWidget {
-       display: inline-block;
-       position: relative;
-       width: 100%;
-       max-width: 50em;
-       background-color: #ffffff;
-       margin-right: 0.5em;
-}
-.oo-ui-dropdownWidget-handle {
-       width: 100%;
-       display: inline-block;
-       -webkit-touch-callout: none;
-       -webkit-user-select: none;
-          -moz-user-select: none;
-           -ms-user-select: none;
-               user-select: none;
-       -webkit-box-sizing: border-box;
-          -moz-box-sizing: border-box;
-               box-sizing: border-box;
-}
-.oo-ui-dropdownWidget-handle .oo-ui-indicatorElement-indicator,
-.oo-ui-dropdownWidget-handle .oo-ui-iconElement-icon {
-       position: absolute;
-}
-.oo-ui-dropdownWidget > .oo-ui-menuSelectWidget {
-       z-index: 1;
-       width: 100%;
-}
-.oo-ui-dropdownWidget.oo-ui-widget-enabled .oo-ui-dropdownWidget-handle {
-       cursor: pointer;
-}
-.oo-ui-dropdownWidget:last-child {
-       margin-right: 0;
-}
-.oo-ui-dropdownWidget-handle {
-       padding: 0.5em 0;
-       height: 2.275em;
-       line-height: 1.275;
-       border: 1px solid #cccccc;
-       border-radius: 2px;
-}
-.oo-ui-dropdownWidget-handle .oo-ui-indicatorElement-indicator {
-       right: 0;
-}
-.oo-ui-dropdownWidget-handle .oo-ui-iconElement-icon {
-       left: 0.25em;
-}
-.oo-ui-dropdownWidget-handle .oo-ui-labelElement-label {
-       line-height: 1.275em;
-       margin: 0 1em;
-}
-.oo-ui-dropdownWidget-handle .oo-ui-indicatorElement-indicator {
-       top: 0;
-       width: 0.9375em;
-       height: 0.9375em;
-       margin: 0.775em;
-}
-.oo-ui-dropdownWidget-handle .oo-ui-iconElement-icon {
-       top: 0;
-       width: 1.875em;
-       height: 1.875em;
-       margin: 0.3em;
-}
-.oo-ui-dropdownWidget:hover .oo-ui-dropdownWidget-handle {
-       border-color: #aaaaaa;
-}
-.oo-ui-dropdownWidget.oo-ui-widget-disabled .oo-ui-dropdownWidget-handle {
-       color: #cccccc;
-       text-shadow: 0 1px 1px #ffffff;
-       border-color: #dddddd;
-       background-color: #f3f3f3;
-}
-.oo-ui-dropdownWidget.oo-ui-widget-disabled .oo-ui-dropdownWidget-handle:focus {
-       outline: 0;
-}
-.oo-ui-dropdownWidget.oo-ui-widget-disabled .oo-ui-indicatorElement-indicator {
-       opacity: 0.2;
-}
-.oo-ui-dropdownWidget.oo-ui-iconElement .oo-ui-dropdownWidget-handle .oo-ui-labelElement-label {
-       margin-left: 3em;
-}
-.oo-ui-dropdownWidget.oo-ui-indicatorElement .oo-ui-dropdownWidget-handle .oo-ui-labelElement-label {
-       margin-right: 2em;
-}
-.oo-ui-dropdownWidget .oo-ui-selectWidget {
-       border-top-color: #ffffff;
-}
-.oo-ui-selectFileWidget {
-       display: inline-block;
-       vertical-align: middle;
-       width: 100%;
-       max-width: 50em;
-       margin-right: 0.5em;
-}
-.oo-ui-selectFileWidget-selectButton {
-       display: table-cell;
-       vertical-align: middle;
-}
-.oo-ui-selectFileWidget-selectButton > .oo-ui-buttonElement-button {
-       position: relative;
-       overflow: hidden;
-}
-.oo-ui-selectFileWidget-selectButton > .oo-ui-buttonElement-button > input[type="file"] {
-       position: absolute;
-       margin: 0;
-       top: 0;
-       bottom: 0;
-       left: 0;
-       right: 0;
-       width: 100%;
-       height: 100%;
-       opacity: 0;
-       z-index: 1;
-       cursor: pointer;
-       padding-top: 100px;
-}
-.oo-ui-selectFileWidget-selectButton.oo-ui-widget-disabled > .oo-ui-buttonElement-button > input[type="file"] {
-       display: none;
-}
-.oo-ui-selectFileWidget-info {
-       width: 100%;
-       display: table-cell;
-       vertical-align: middle;
-       position: relative;
-       overflow: hidden;
-       -webkit-box-sizing: border-box;
-          -moz-box-sizing: border-box;
-               box-sizing: border-box;
-}
-.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-label {
-       position: absolute;
-       top: 0;
-       bottom: 0;
-       left: 0;
-       right: 0;
-       text-overflow: ellipsis;
-}
-.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-label > .oo-ui-selectFileWidget-fileName {
-       float: left;
-}
-.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-label > .oo-ui-selectFileWidget-fileType {
-       float: right;
-}
-.oo-ui-selectFileWidget-info > .oo-ui-indicatorElement-indicator,
-.oo-ui-selectFileWidget-info > .oo-ui-iconElement-icon,
-.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-clearButton {
-       position: absolute;
-}
-.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-clearButton {
-       z-index: 2;
-}
-.oo-ui-selectFileWidget-dropTarget {
-       cursor: default;
-}
-.oo-ui-selectFileWidget-supported.oo-ui-widget-enabled .oo-ui-selectFileWidget-dropTarget {
-       cursor: pointer;
-}
-.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-clearButton,
-.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-clearButton {
-       display: none;
-}
-.oo-ui-selectFileWidget:last-child {
-       margin-right: 0;
-}
-.oo-ui-selectFileWidget-selectButton > .oo-ui-buttonElement-button {
-       margin-left: 0.5em;
-}
-.oo-ui-selectFileWidget-info {
-       height: 2.4em;
-       background-color: #ffffff;
-       border: 1px solid #cccccc;
-       border-radius: 2px;
-}
-.oo-ui-selectFileWidget-info > .oo-ui-indicatorElement-indicator {
-       right: 0;
-}
-.oo-ui-selectFileWidget-info > .oo-ui-iconElement-icon {
-       left: 0;
-}
-.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-label {
-       line-height: 2.3em;
-       margin: 0;
-       overflow: hidden;
-       white-space: nowrap;
-       -webkit-box-sizing: border-box;
-          -moz-box-sizing: border-box;
-               box-sizing: border-box;
-       text-overflow: ellipsis;
-       left: 0.5em;
-       right: 0.5em;
-}
-.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-label > .oo-ui-selectFileWidget-fileType {
-       color: #888888;
-}
-.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-clearButton {
-       top: 0;
-       width: 1.875em;
-       margin-right: 0;
-}
-.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-clearButton .oo-ui-buttonElement-button > .oo-ui-iconElement-icon {
-       height: 2.3em;
-}
-.oo-ui-selectFileWidget-info > .oo-ui-indicatorElement-indicator {
-       top: 0;
-       width: 0.9375em;
-       height: 2.3em;
-       margin-right: 0.775em;
-}
-.oo-ui-selectFileWidget-info > .oo-ui-iconElement-icon {
-       top: 0;
-       width: 1.875em;
-       height: 2.3em;
-       margin-left: 0.5em;
-}
-.oo-ui-selectFileWidget.oo-ui-widget-disabled .oo-ui-selectFileWidget-info {
-       color: #cccccc;
-       text-shadow: 0 1px 1px #ffffff;
-       border-color: #dddddd;
-       background-color: #f3f3f3;
-}
-.oo-ui-selectFileWidget.oo-ui-widget-disabled .oo-ui-selectFileWidget-info > .oo-ui-iconElement-icon,
-.oo-ui-selectFileWidget.oo-ui-widget-disabled .oo-ui-selectFileWidget-info > .oo-ui-indicatorElement-indicator {
-       opacity: 0.2;
-}
-.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-label {
-       color: #cccccc;
-}
-.oo-ui-selectFileWidget.oo-ui-iconElement .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-label {
-       left: 2.875em;
-}
-.oo-ui-selectFileWidget .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-label {
-       right: 2.375em;
-}
-.oo-ui-selectFileWidget .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-clearButton {
-       right: 0;
-}
-.oo-ui-selectFileWidget.oo-ui-indicatorElement .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-label {
-       right: 4.4625em;
-}
-.oo-ui-selectFileWidget.oo-ui-indicatorElement .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-clearButton {
-       right: 2.0875em;
-}
-.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-label,
-.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-label {
-       right: 0.5em;
-}
-.oo-ui-selectFileWidget-empty.oo-ui-indicatorElement .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-label,
-.oo-ui-selectFileWidget-notsupported.oo-ui-indicatorElement .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-label {
-       right: 2em;
-}
-.oo-ui-selectFileWidget-dropTarget {
-       line-height: 3.5em;
-       background-color: #ffffff;
-       border: 1px dashed #cccccc;
-       padding: 0.5em 1em;
-       margin-bottom: 0.5em;
-       text-align: center;
-       vertical-align: middle;
-}
-.oo-ui-selectFileWidget-supported.oo-ui-widget-enabled .oo-ui-selectFileWidget-dropTarget:hover {
-       background-color: #eeeeee;
-}
-.oo-ui-selectFileWidget-supported.oo-ui-widget-enabled.oo-ui-selectFileWidget-canDrop .oo-ui-selectFileWidget-dropTarget {
-       background: rgba(52, 123, 255, 0.1);
-}
-.oo-ui-selectFileWidget.oo-ui-widget-disabled .oo-ui-selectFileWidget-dropTarget,
-.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-dropTarget {
-       color: #cccccc;
-       text-shadow: 0 1px 1px #ffffff;
-       border-color: #dddddd;
-       background-color: #f3f3f3;
-}
-.oo-ui-outlineOptionWidget {
-       position: relative;
-       cursor: pointer;
-       -webkit-touch-callout: none;
-       -webkit-user-select: none;
-          -moz-user-select: none;
-           -ms-user-select: none;
-               user-select: none;
-       font-size: 1.1em;
-       padding: 0.75em;
-}
-.oo-ui-outlineOptionWidget.oo-ui-indicatorElement .oo-ui-labelElement-label {
-       padding-right: 1.5em;
-}
-.oo-ui-outlineOptionWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
-       opacity: 0.5;
-}
-.oo-ui-outlineOptionWidget-level-0 {
-       padding-left: 3.5em;
-}
-.oo-ui-outlineOptionWidget-level-0 .oo-ui-iconElement-icon {
-       left: 1em;
-}
-.oo-ui-outlineOptionWidget-level-1 {
-       padding-left: 5em;
-}
-.oo-ui-outlineOptionWidget-level-1 .oo-ui-iconElement-icon {
-       left: 2.5em;
-}
-.oo-ui-outlineOptionWidget-level-2 {
-       padding-left: 6.5em;
-}
-.oo-ui-outlineOptionWidget-level-2 .oo-ui-iconElement-icon {
-       left: 4em;
-}
-.oo-ui-selectWidget-depressed .oo-ui-outlineOptionWidget.oo-ui-optionWidget-selected {
-       background-color: #d0d0d0;
-       text-shadow: 0 1px 1px #ffffff;
-}
-.oo-ui-outlineOptionWidget.oo-ui-flaggedElement-important {
-       font-weight: bold;
-}
-.oo-ui-outlineOptionWidget.oo-ui-flaggedElement-placeholder {
-       font-style: italic;
-}
-.oo-ui-outlineOptionWidget.oo-ui-flaggedElement-empty .oo-ui-iconElement-icon {
-       opacity: 0.5;
-}
-.oo-ui-outlineOptionWidget.oo-ui-flaggedElement-empty .oo-ui-labelElement-label {
-       color: #777777;
-}
-.oo-ui-outlineControlsWidget {
-       height: 3em;
-       background-color: #ffffff;
-}
-.oo-ui-outlineControlsWidget-items,
-.oo-ui-outlineControlsWidget-movers {
-       float: left;
-       -webkit-box-sizing: border-box;
-          -moz-box-sizing: border-box;
-               box-sizing: border-box;
-}
-.oo-ui-outlineControlsWidget > .oo-ui-iconElement-icon {
-       float: left;
-       background-position: right center;
-}
-.oo-ui-outlineControlsWidget-items {
-       float: left;
-}
-.oo-ui-outlineControlsWidget-items .oo-ui-buttonWidget {
-       float: left;
-}
-.oo-ui-outlineControlsWidget-movers {
-       float: right;
-}
-.oo-ui-outlineControlsWidget-movers .oo-ui-buttonWidget {
-       float: right;
-}
-.oo-ui-outlineControlsWidget-items,
-.oo-ui-outlineControlsWidget-movers {
-       height: 2em;
-       margin: 0.5em 0.5em 0.5em 0;
-       padding: 0;
-}
-.oo-ui-outlineControlsWidget > .oo-ui-iconElement-icon {
-       width: 1.5em;
-       height: 2em;
-       margin: 0.5em 0 0.5em 0.5em;
-       opacity: 0.2;
-}
-.oo-ui-tabSelectWidget {
-       text-align: left;
-       white-space: nowrap;
-       overflow: hidden;
-       background-color: #dddddd;
-}
-.oo-ui-tabOptionWidget {
-       display: inline-block;
-       vertical-align: bottom;
-       padding: 0.35em 1em;
-       margin: 0.5em 0 0 0.75em;
-       border: 1px solid transparent;
-       border-bottom: none;
-       border-top-left-radius: 2px;
-       border-top-right-radius: 2px;
-       color: #555555;
-       font-weight: bold;
-}
-.oo-ui-tabOptionWidget.oo-ui-widget-enabled:hover {
-       background-color: rgba(255, 255, 255, 0.3);
-}
-.oo-ui-tabOptionWidget.oo-ui-widget-enabled:active {
-       background-color: rgba(255, 255, 255, 0.8);
-}
-.oo-ui-tabOptionWidget.oo-ui-indicatorElement .oo-ui-labelElement-label {
-       padding-right: 1.5em;
-}
-.oo-ui-tabOptionWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
-       opacity: 0.5;
-}
-.oo-ui-selectWidget-pressed .oo-ui-tabOptionWidget.oo-ui-optionWidget-selected,
-.oo-ui-selectWidget-depressed .oo-ui-tabOptionWidget.oo-ui-optionWidget-selected,
-.oo-ui-tabOptionWidget.oo-ui-optionWidget-selected:hover {
-       background-color: #ffffff;
-       color: #333333;
-}
-.oo-ui-capsuleMultiSelectWidget {
-       display: inline-block;
-       position: relative;
-       width: 100%;
-       max-width: 50em;
-}
-.oo-ui-capsuleMultiSelectWidget-handle {
-       width: 100%;
-       display: inline-block;
-       position: relative;
-}
-.oo-ui-capsuleMultiSelectWidget-content {
-       position: relative;
-}
-.oo-ui-capsuleMultiSelectWidget.oo-ui-widget-disabled .oo-ui-capsuleMultiSelectWidget-content > input {
-       display: none;
-}
-.oo-ui-capsuleMultiSelectWidget-group {
-       display: inline;
-}
-.oo-ui-capsuleMultiSelectWidget > .oo-ui-menuSelectWidget {
-       z-index: 1;
-       width: 100%;
-}
-.oo-ui-capsuleMultiSelectWidget-handle {
-       background-color: #ffffff;
-       cursor: text;
-       min-height: 2.4em;
-       margin-right: 0.5em;
-       padding: 0.15em 0.25em;
-       border: 1px solid #cccccc;
-       border-radius: 2px;
-       -webkit-box-sizing: border-box;
-          -moz-box-sizing: border-box;
-               box-sizing: border-box;
-}
-.oo-ui-capsuleMultiSelectWidget-handle:last-child {
-       margin-right: 0;
-}
-.oo-ui-capsuleMultiSelectWidget-handle > .oo-ui-indicatorElement-indicator,
-.oo-ui-capsuleMultiSelectWidget-handle > .oo-ui-iconElement-icon {
-       position: absolute;
-       background-position: center center;
-       background-repeat: no-repeat;
-}
-.oo-ui-capsuleMultiSelectWidget-handle > .oo-ui-capsuleMultiSelectWidget-content > input {
-       border: 0;
-       line-height: 1.675em;
-       margin: 0 0 0 0.2em;
-       padding: 0;
-       font-size: inherit;
-       font-family: inherit;
-       background-color: transparent;
-       color: #000000;
-       vertical-align: middle;
-}
-.oo-ui-capsuleMultiSelectWidget-handle > .oo-ui-capsuleMultiSelectWidget-content > input:focus {
-       outline: none;
-}
-.oo-ui-capsuleMultiSelectWidget.oo-ui-indicatorElement .oo-ui-capsuleMultiSelectWidget-handle {
-       padding-right: 2.4875em;
-}
-.oo-ui-capsuleMultiSelectWidget.oo-ui-indicatorElement .oo-ui-capsuleMultiSelectWidget-handle > .oo-ui-indicatorElement-indicator {
-       right: 0;
-       top: 0;
-       width: 0.9375em;
-       height: 0.9375em;
-       margin: 0.775em;
-}
-.oo-ui-capsuleMultiSelectWidget.oo-ui-iconElement .oo-ui-capsuleMultiSelectWidget-handle {
-       padding-left: 2.475em;
-}
-.oo-ui-capsuleMultiSelectWidget.oo-ui-iconElement .oo-ui-capsuleMultiSelectWidget-handle > .oo-ui-iconElement-icon {
-       left: 0;
-       top: 0;
-       width: 1.875em;
-       height: 1.875em;
-       margin: 0.3em;
-}
-.oo-ui-capsuleMultiSelectWidget:hover .oo-ui-capsuleMultiSelectWidget-handle {
-       border-color: #aaaaaa;
-}
-.oo-ui-capsuleMultiSelectWidget.oo-ui-widget-disabled .oo-ui-capsuleMultiSelectWidget-handle {
-       color: #cccccc;
-       text-shadow: 0 1px 1px #ffffff;
-       border-color: #dddddd;
-       background-color: #f3f3f3;
-       cursor: default;
-}
-.oo-ui-capsuleMultiSelectWidget.oo-ui-widget-disabled .oo-ui-capsuleMultiSelectWidget-handle > .oo-ui-iconElement-icon,
-.oo-ui-capsuleMultiSelectWidget.oo-ui-widget-disabled .oo-ui-capsuleMultiSelectWidget-handle > .oo-ui-indicatorElement-indicator {
-       opacity: 0.2;
-}
-.oo-ui-capsuleMultiSelectWidget .oo-ui-selectWidget {
-       border-top-color: #ffffff;
-}
-.oo-ui-capsuleItemWidget {
-       position: relative;
-       display: inline-block;
-       cursor: default;
-       white-space: nowrap;
-       width: auto;
-       max-width: 100%;
-       -webkit-box-sizing: border-box;
-          -moz-box-sizing: border-box;
-               box-sizing: border-box;
-       vertical-align: middle;
-       padding: 0 0.4em;
-       margin: 0.1em;
-       height: 1.7em;
-       line-height: 1.7em;
-       background-color: #eeeeee;
-       border: 1px solid #cccccc;
-       color: #555555;
-       border-radius: 2px;
-}
-.oo-ui-capsuleItemWidget > .oo-ui-iconElement-icon {
-       cursor: pointer;
-}
-.oo-ui-capsuleItemWidget.oo-ui-widget-disabled > .oo-ui-iconElement-icon {
-       cursor: default;
-}
-.oo-ui-capsuleItemWidget.oo-ui-labelElement .oo-ui-labelElement-label {
-       display: block;
-       text-overflow: ellipsis;
-       overflow: hidden;
-}
-.oo-ui-capsuleItemWidget.oo-ui-indicatorElement > .oo-ui-labelElement-label {
-       padding-right: 1.3375em;
-}
-.oo-ui-capsuleItemWidget.oo-ui-indicatorElement > .oo-ui-indicatorElement-indicator {
-       position: absolute;
-       right: 0.4em;
-       top: 0;
-       width: 0.9375em;
-       height: 100%;
-       background-repeat: no-repeat;
-}
-.oo-ui-capsuleItemWidget.oo-ui-indicatorElement > .oo-ui-indicator-clear {
-       cursor: pointer;
-}
-.oo-ui-capsuleItemWidget.oo-ui-widget-disabled {
-       color: #cccccc;
-       text-shadow: 0 1px 1px #ffffff;
-       border-color: #dddddd;
-       background-color: #f3f3f3;
-}
-.oo-ui-capsuleItemWidget.oo-ui-widget-disabled > .oo-ui-indicatorElement-indicator {
-       opacity: 0.2;
-}
-.oo-ui-comboBoxInputWidget {
-       display: inline-block;
-       position: relative;
-       width: 100%;
-       max-width: 50em;
-       margin-right: 0.5em;
-}
-.oo-ui-comboBoxInputWidget > .oo-ui-menuSelectWidget {
-       z-index: 1;
-       width: 100%;
-}
-.oo-ui-comboBoxInputWidget.oo-ui-widget-enabled > .oo-ui-indicatorElement-indicator {
-       cursor: pointer;
-}
-.oo-ui-comboBoxInputWidget-php input::-webkit-calendar-picker-indicator {
-       opacity: 0 !important;
-       position: absolute;
-       right: 0;
-       top: 0;
-       height: 2.5em;
-       width: 2.5em;
-       padding: 0;
-}
-.oo-ui-comboBoxInputWidget-php > .oo-ui-indicatorElement-indicator {
-       pointer-events: none;
-}
-.oo-ui-comboBoxInputWidget:last-child {
-       margin-right: 0;
-}
-.oo-ui-comboBoxInputWidget input,
-.oo-ui-comboBoxInputWidget textarea {
-       height: 2.35em;
-}
-.oo-ui-searchWidget-query {
-       position: absolute;
-       top: 0;
-       left: 0;
-       right: 0;
-}
-.oo-ui-searchWidget-query .oo-ui-textInputWidget {
-       width: 100%;
-}
-.oo-ui-searchWidget-results {
-       position: absolute;
-       bottom: 0;
-       left: 0;
-       right: 0;
-       overflow-x: hidden;
-       overflow-y: auto;
-}
-.oo-ui-searchWidget-query {
-       height: 4em;
-       padding: 0 1em;
-       border-bottom: 1px solid #cccccc;
-}
-.oo-ui-searchWidget-query .oo-ui-textInputWidget {
-       margin: 0.75em 0;
-}
-.oo-ui-searchWidget-results {
-       top: 4em;
-       padding: 1em;
-       line-height: 0;
-}
-.oo-ui-numberInputWidget {
-       display: inline-block;
-       position: relative;
-       max-width: 50em;
-}
-.oo-ui-numberInputWidget-field {
-       display: table;
-       table-layout: fixed;
-       width: 100%;
-}
-.oo-ui-numberInputWidget-field > .oo-ui-buttonWidget,
-.oo-ui-numberInputWidget-field > .oo-ui-textInputWidget {
-       display: table-cell;
-       vertical-align: middle;
-}
-.oo-ui-numberInputWidget-field > .oo-ui-textInputWidget {
-       width: 100%;
-}
-.oo-ui-numberInputWidget-field > .oo-ui-buttonWidget {
-       white-space: nowrap;
-}
-.oo-ui-numberInputWidget-field > .oo-ui-buttonWidget > .oo-ui-buttonElement-button {
-       -webkit-box-sizing: border-box;
-          -moz-box-sizing: border-box;
-               box-sizing: border-box;
-}
-.oo-ui-numberInputWidget-field > .oo-ui-buttonWidget {
-       width: 2.5em;
-}
-.oo-ui-numberInputWidget-minusButton.oo-ui-buttonElement-framed.oo-ui-widget-enabled > .oo-ui-buttonElement-button {
-       border-top-right-radius: 0;
-       border-bottom-right-radius: 0;
-       border-right-width: 0;
-}
-.oo-ui-numberInputWidget-plusButton.oo-ui-buttonElement-framed.oo-ui-widget-enabled > .oo-ui-buttonElement-button {
-       border-top-left-radius: 0;
-       border-bottom-left-radius: 0;
-       border-left-width: 0;
-}
-.oo-ui-numberInputWidget .oo-ui-textInputWidget input {
-       border-radius: 0;
-}
-.oo-ui-window {
-       background: transparent;
-}
-.oo-ui-window-frame {
-       -webkit-box-sizing: border-box;
-          -moz-box-sizing: border-box;
-               box-sizing: border-box;
-}
-.oo-ui-window-content:focus {
-       outline: none;
-}
-.oo-ui-window-head,
-.oo-ui-window-foot {
-       -webkit-touch-callout: none;
-       -webkit-user-select: none;
-          -moz-user-select: none;
-           -ms-user-select: none;
-               user-select: none;
-}
-.oo-ui-window-body {
-       margin: 0;
-       padding: 0;
-       background: none;
-}
-.oo-ui-window-overlay {
-       position: absolute;
-       top: 0;
-       /* @noflip */
-       left: 0;
-}
-.oo-ui-dialog-content > .oo-ui-window-head,
-.oo-ui-dialog-content > .oo-ui-window-body,
-.oo-ui-dialog-content > .oo-ui-window-foot {
-       position: absolute;
-       left: 0;
-       right: 0;
-       -webkit-box-sizing: border-box;
-          -moz-box-sizing: border-box;
-               box-sizing: border-box;
-}
-.oo-ui-dialog-content > .oo-ui-window-head {
-       overflow: hidden;
-       z-index: 1;
-       top: 0;
-}
-.oo-ui-dialog-content > .oo-ui-window-body {
-       overflow: auto;
-       z-index: 2;
-       top: 0;
-       bottom: 0;
-}
-.oo-ui-dialog-content > .oo-ui-window-foot {
-       overflow: hidden;
-       z-index: 1;
-       bottom: 0;
-}
-.oo-ui-dialog-content > .oo-ui-window-body {
-       outline: 1px solid #aaaaaa;
-}
-.oo-ui-messageDialog-actions-horizontal {
-       display: table;
-       table-layout: fixed;
-       width: 100%;
-}
-.oo-ui-messageDialog-actions-horizontal .oo-ui-actionWidget {
-       display: table-cell;
-       width: 1%;
-}
-.oo-ui-messageDialog-actions-vertical {
-       display: block;
-}
-.oo-ui-messageDialog-actions-vertical .oo-ui-actionWidget {
-       display: block;
-       overflow: hidden;
-       text-overflow: ellipsis;
-}
-.oo-ui-messageDialog-actions .oo-ui-actionWidget {
-       position: relative;
-       text-align: center;
-}
-.oo-ui-messageDialog-actions .oo-ui-actionWidget .oo-ui-buttonElement-button {
-       display: block;
-}
-.oo-ui-messageDialog-actions .oo-ui-actionWidget .oo-ui-labelElement-label {
-       position: relative;
-       top: auto;
-       bottom: auto;
-       display: inline;
-       white-space: nowrap;
-}
-.oo-ui-messageDialog-title,
-.oo-ui-messageDialog-message {
-       display: block;
-       text-align: center;
-}
-.oo-ui-messageDialog-title.oo-ui-labelElement,
-.oo-ui-messageDialog-message.oo-ui-labelElement {
-       padding-top: 0.5em;
-}
-.oo-ui-messageDialog-title {
-       font-size: 1.5em;
-       line-height: 1em;
-       color: #000000;
-}
-.oo-ui-messageDialog-message {
-       font-size: 0.9em;
-       line-height: 1.25em;
-       color: #555555;
-}
-.oo-ui-messageDialog-message-verbose {
-       font-size: 1.1em;
-       line-height: 1.5em;
-       text-align: left;
-}
-.oo-ui-messageDialog-actions-horizontal .oo-ui-actionWidget {
-       border-right: 1px solid #e5e5e5;
-       margin: 0;
-}
-.oo-ui-messageDialog-actions-horizontal .oo-ui-actionWidget:last-child {
-       border-right-width: 0;
-}
-.oo-ui-messageDialog-actions-vertical .oo-ui-actionWidget {
-       border-bottom: 1px solid #e5e5e5;
-       margin: 0;
-}
-.oo-ui-messageDialog-actions-vertical .oo-ui-actionWidget:last-child {
-       border-bottom-width: 0;
-}
-.oo-ui-messageDialog-actions .oo-ui-actionWidget {
-       height: 3.4em;
-       margin-right: 0;
-}
-.oo-ui-messageDialog-actions .oo-ui-actionWidget:last-child {
-       margin-right: 0;
-}
-.oo-ui-messageDialog-actions .oo-ui-actionWidget.oo-ui-labelElement .oo-ui-labelElement-label {
-       text-align: center;
-       line-height: 3.4em;
-       padding: 0 2em;
-}
-.oo-ui-messageDialog-actions .oo-ui-actionWidget:hover {
-       background-color: rgba(0, 0, 0, 0.05);
-}
-.oo-ui-messageDialog-actions .oo-ui-actionWidget:active {
-       background-color: rgba(0, 0, 0, 0.1);
-}
-.oo-ui-messageDialog-actions .oo-ui-actionWidget.oo-ui-flaggedElement-progressive:hover {
-       background-color: rgba(8, 126, 204, 0.05);
-}
-.oo-ui-messageDialog-actions .oo-ui-actionWidget.oo-ui-flaggedElement-progressive:active {
-       background-color: rgba(8, 126, 204, 0.1);
-}
-.oo-ui-messageDialog-actions .oo-ui-actionWidget.oo-ui-flaggedElement-progressive .oo-ui-labelElement-label {
-       font-weight: bold;
-}
-.oo-ui-messageDialog-actions .oo-ui-actionWidget.oo-ui-flaggedElement-constructive:hover {
-       background-color: rgba(118, 171, 54, 0.05);
-}
-.oo-ui-messageDialog-actions .oo-ui-actionWidget.oo-ui-flaggedElement-constructive:active {
-       background-color: rgba(118, 171, 54, 0.1);
-}
-.oo-ui-messageDialog-actions .oo-ui-actionWidget.oo-ui-flaggedElement-destructive:hover {
-       background-color: rgba(212, 83, 83, 0.05);
-}
-.oo-ui-messageDialog-actions .oo-ui-actionWidget.oo-ui-flaggedElement-destructive:active {
-       background-color: rgba(212, 83, 83, 0.1);
-}
-.oo-ui-processDialog-location {
-       overflow: hidden;
-       text-overflow: ellipsis;
-       white-space: nowrap;
-}
-.oo-ui-processDialog-title {
-       display: inline;
-       padding: 0;
-}
-.oo-ui-processDialog-actions-safe .oo-ui-actionWidget,
-.oo-ui-processDialog-actions-primary .oo-ui-actionWidget,
-.oo-ui-processDialog-actions-other .oo-ui-actionWidget {
-       white-space: nowrap;
-}
-.oo-ui-processDialog-actions-safe,
-.oo-ui-processDialog-actions-primary {
-       position: absolute;
-       top: 0;
-       bottom: 0;
-}
-.oo-ui-processDialog-actions-safe {
-       left: 0;
-}
-.oo-ui-processDialog-actions-primary {
-       right: 0;
-}
-.oo-ui-processDialog-errors {
-       position: absolute;
-       top: 0;
-       left: 0;
-       right: 0;
-       bottom: 0;
-       z-index: 2;
-       overflow-x: hidden;
-       overflow-y: auto;
-}
-.oo-ui-processDialog-content .oo-ui-window-head {
-       height: 3.4em;
-}
-.oo-ui-processDialog-content .oo-ui-window-body {
-       top: 3.4em;
-       outline: 1px solid rgba(0, 0, 0, 0.2);
-}
-.oo-ui-processDialog-navigation {
-       position: relative;
-       height: 3.4em;
-       padding: 0 1em;
-}
-.oo-ui-processDialog-location {
-       padding: 0.75em 0;
-       height: 1.875em;
-       cursor: default;
-       text-align: center;
-}
-.oo-ui-processDialog-title {
-       font-weight: bold;
-       line-height: 1.875em;
-}
-.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-buttonElement-framed,
-.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-buttonElement-framed,
-.oo-ui-processDialog-actions-other .oo-ui-actionWidget.oo-ui-buttonElement-framed {
-       margin: 0.5em;
-}
-.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-buttonElement-frameless,
-.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-buttonElement-frameless,
-.oo-ui-processDialog-actions-other .oo-ui-actionWidget.oo-ui-buttonElement-frameless {
-       margin: 0;
-}
-.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-buttonElement-frameless .oo-ui-buttonElement-button,
-.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-buttonElement-frameless .oo-ui-buttonElement-button,
-.oo-ui-processDialog-actions-other .oo-ui-actionWidget.oo-ui-buttonElement-frameless .oo-ui-buttonElement-button {
-       padding: 0.75em 1em;
-       vertical-align: middle;
-}
-.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-buttonElement-frameless .oo-ui-labelElement-label,
-.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-buttonElement-frameless .oo-ui-labelElement-label,
-.oo-ui-processDialog-actions-other .oo-ui-actionWidget.oo-ui-buttonElement-frameless .oo-ui-labelElement-label {
-       line-height: 1.875em;
-}
-.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-buttonElement-frameless:hover,
-.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-buttonElement-frameless:hover {
-       background-color: rgba(0, 0, 0, 0.05);
-}
-.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-buttonElement-frameless:active,
-.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-buttonElement-frameless:active {
-       background-color: rgba(0, 0, 0, 0.1);
-}
-.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-buttonElement-frameless.oo-ui-flaggedElement-progressive:hover,
-.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-buttonElement-frameless.oo-ui-flaggedElement-progressive:hover {
-       background-color: rgba(8, 126, 204, 0.05);
-}
-.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-buttonElement-frameless.oo-ui-flaggedElement-progressive:active,
-.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-buttonElement-frameless.oo-ui-flaggedElement-progressive:active {
-       background-color: rgba(8, 126, 204, 0.1);
-}
-.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-buttonElement-frameless.oo-ui-flaggedElement-progressive .oo-ui-labelElement-label,
-.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-buttonElement-frameless.oo-ui-flaggedElement-progressive .oo-ui-labelElement-label {
-       font-weight: bold;
-}
-.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-buttonElement-frameless.oo-ui-flaggedElement-constructive:hover,
-.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-buttonElement-frameless.oo-ui-flaggedElement-constructive:hover {
-       background-color: rgba(118, 171, 54, 0.05);
-}
-.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-buttonElement-frameless.oo-ui-flaggedElement-constructive:active,
-.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-buttonElement-frameless.oo-ui-flaggedElement-constructive:active {
-       background-color: rgba(118, 171, 54, 0.1);
-}
-.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-buttonElement-frameless.oo-ui-flaggedElement-destructive:hover,
-.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-buttonElement-frameless.oo-ui-flaggedElement-destructive:hover {
-       background-color: rgba(212, 83, 83, 0.05);
-}
-.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-buttonElement-frameless.oo-ui-flaggedElement-destructive:active,
-.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-buttonElement-frameless.oo-ui-flaggedElement-destructive:active {
-       background-color: rgba(212, 83, 83, 0.1);
-}
-.oo-ui-processDialog-actions-other .oo-ui-actionWidget.oo-ui-buttonElement {
-       margin-right: 0;
-}
-.oo-ui-processDialog > .oo-ui-window-frame {
-       min-height: 5em;
-}
-.oo-ui-processDialog-errors {
-       background-color: rgba(255, 255, 255, 0.9);
-       padding: 3em 3em 1.5em 3em;
-       text-align: center;
-}
-.oo-ui-processDialog-errors .oo-ui-buttonWidget {
-       margin: 2em 1em 2em 1em;
-}
-.oo-ui-processDialog-errors-title {
-       font-size: 1.5em;
-       color: #000000;
-       margin-bottom: 2em;
-}
-.oo-ui-processDialog-error {
-       text-align: left;
-       margin: 1em;
-       padding: 1em;
-       border: 1px solid #ff9e9e;
-       background-color: #fff7f7;
-       border-radius: 2px;
-}
-.oo-ui-windowManager-modal > .oo-ui-dialog {
-       position: fixed;
-       width: 0;
-       height: 0;
-       overflow: hidden;
-}
-.oo-ui-windowManager-modal > .oo-ui-dialog.oo-ui-window-active {
-       width: auto;
-       height: auto;
-       top: 0;
-       right: 0;
-       bottom: 0;
-       left: 0;
-       padding: 1em;
-}
-.oo-ui-windowManager-modal > .oo-ui-dialog.oo-ui-window-setup > .oo-ui-window-frame {
-       position: absolute;
-       right: 0;
-       left: 0;
-       margin: auto;
-       overflow: hidden;
-       max-width: 100%;
-       max-height: 100%;
-}
-.oo-ui-windowManager-fullscreen > .oo-ui-dialog > .oo-ui-window-frame {
-       width: 100%;
-       height: 100%;
-       top: 0;
-       bottom: 0;
-}
-.oo-ui-windowManager-modal > .oo-ui-dialog {
-       background-color: rgba(255, 255, 255, 0.5);
-       opacity: 0;
-       -webkit-transition: opacity 250ms ease;
-          -moz-transition: opacity 250ms ease;
-               transition: opacity 250ms ease;
-}
-.oo-ui-windowManager-modal > .oo-ui-dialog > .oo-ui-window-frame {
-       background-color: #ffffff;
-       opacity: 0;
-       -webkit-transform: scale(0.5);
-          -moz-transform: scale(0.5);
-           -ms-transform: scale(0.5);
-               transform: scale(0.5);
-       -webkit-transition: all 250ms ease;
-          -moz-transition: all 250ms ease;
-               transition: all 250ms ease;
-}
-.oo-ui-windowManager-modal > .oo-ui-dialog.oo-ui-window-setup {
-       opacity: 1;
-}
-.oo-ui-windowManager-modal > .oo-ui-dialog.oo-ui-window-ready > .oo-ui-window-frame {
-       opacity: 1;
-       -webkit-transform: scale(1);
-          -moz-transform: scale(1);
-           -ms-transform: scale(1);
-               transform: scale(1);
-}
-.oo-ui-windowManager-modal.oo-ui-windowManager-floating > .oo-ui-dialog > .oo-ui-window-frame {
-       top: 1em;
-       bottom: 1em;
-       border: 1px solid #aaaaaa;
-       border-radius: 2px;
-       box-shadow: 0 0.15em 0 0 rgba(0, 0, 0, 0.15);
-}
index ea70fbb..27a4657 100644 (file)
@@ -1,13 +1,17 @@
 /*!
- * OOjs UI v0.15.1
+ * OOjs UI v0.15.3
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-01-26T21:14:23Z
+ * Date: 2016-02-09T21:21:16Z
  */
+( function ( OO ) {
+
+'use strict';
+
 /**
  * @class
  * @extends OO.ui.Theme
@@ -67,3 +71,5 @@ OO.ui.MediaWikiTheme.prototype.getElementClasses = function ( element ) {
 /* Instantiation */
 
 OO.ui.theme = new OO.ui.MediaWikiTheme();
+
+}( OO ) );
diff --git a/resources/lib/oojs-ui/oojs-ui-toolbars-apex.css b/resources/lib/oojs-ui/oojs-ui-toolbars-apex.css
new file mode 100644 (file)
index 0000000..0021721
--- /dev/null
@@ -0,0 +1,515 @@
+/*!
+ * OOjs UI v0.15.3
+ * https://www.mediawiki.org/wiki/OOjs_UI
+ *
+ * Copyright 2011–2016 OOjs UI Team and other contributors.
+ * Released under the MIT license
+ * http://oojs.mit-license.org
+ *
+ * Date: 2016-02-09T21:21:21Z
+ */
+.oo-ui-popupTool .oo-ui-popupWidget-popup,
+.oo-ui-popupTool .oo-ui-popupWidget-anchor {
+       z-index: 4;
+}
+.oo-ui-popupTool .oo-ui-popupWidget {
+       /* @noflip */
+       margin-left: 1.25em;
+}
+.oo-ui-toolGroupTool > .oo-ui-popupToolGroup {
+       border: 0;
+       border-radius: 0;
+       margin: 0;
+}
+.oo-ui-toolGroupTool:first-child > .oo-ui-popupToolGroup {
+       border-top-left-radius: 0.3125em;
+       border-bottom-left-radius: 0.3125em;
+}
+.oo-ui-toolGroupTool:last-child > .oo-ui-popupToolGroup {
+       border-top-right-radius: 0.3125em;
+       border-bottom-right-radius: 0.3125em;
+}
+.oo-ui-toolGroupTool > .oo-ui-popupToolGroup > .oo-ui-popupToolGroup-handle {
+       height: 1.875em;
+       padding: 0.3125em;
+}
+.oo-ui-toolGroupTool > .oo-ui-popupToolGroup > .oo-ui-popupToolGroup-handle .oo-ui-iconElement-icon {
+       height: 1.875em;
+       width: 1.875em;
+}
+.oo-ui-toolGroupTool > .oo-ui-popupToolGroup.oo-ui-labelElement > .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
+       line-height: 2.1em;
+}
+.oo-ui-toolGroup {
+       display: inline-block;
+       vertical-align: middle;
+       margin: 0.375em;
+       border-radius: 0.3125em;
+       border: 1px solid transparent;
+       -webkit-transition: border-color 250ms ease;
+          -moz-transition: border-color 250ms ease;
+               transition: border-color 250ms ease;
+}
+.oo-ui-toolGroup-empty {
+       display: none;
+}
+.oo-ui-toolGroup .oo-ui-tool-link {
+       text-decoration: none;
+}
+.oo-ui-toolbar-narrow .oo-ui-toolGroup + .oo-ui-toolGroup {
+       margin-left: 0;
+}
+.oo-ui-toolGroup.oo-ui-widget-enabled:hover {
+       border-color: rgba(0, 0, 0, 0.1);
+}
+.oo-ui-toolGroup.oo-ui-widget-enabled .oo-ui-tool-link .oo-ui-tool-title {
+       color: #000000;
+}
+.oo-ui-barToolGroup > .oo-ui-iconElement-icon,
+.oo-ui-barToolGroup > .oo-ui-labelElement-label {
+       display: none;
+}
+.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool > .oo-ui-tool-link {
+       cursor: pointer;
+}
+.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool {
+       display: inline-block;
+       position: relative;
+       vertical-align: top;
+}
+.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool > .oo-ui-tool-link {
+       display: block;
+}
+.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool > .oo-ui-tool-link .oo-ui-tool-accel {
+       display: none;
+}
+.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-iconElement > .oo-ui-tool-link .oo-ui-iconElement-icon {
+       display: inline-block;
+       vertical-align: top;
+}
+.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-iconElement > .oo-ui-tool-link .oo-ui-tool-title {
+       display: none;
+}
+.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-iconElement.oo-ui-tool-with-label > .oo-ui-tool-link .oo-ui-tool-title {
+       display: inline;
+}
+.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-disabled > .oo-ui-tool-link {
+       outline: 0;
+       cursor: default;
+}
+.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool {
+       margin: -1px 0 -1px -1px;
+       border: 1px solid transparent;
+}
+.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool:first-child {
+       border-top-left-radius: 0.3125em;
+       border-bottom-left-radius: 0.3125em;
+}
+.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool:last-child {
+       margin-right: -1px;
+       border-top-right-radius: 0.3125em;
+       border-bottom-right-radius: 0.3125em;
+}
+.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool > .oo-ui-tool-link {
+       height: 1.875em;
+       padding: 0.3125em;
+}
+.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool > .oo-ui-tool-link .oo-ui-iconElement-icon {
+       height: 1.875em;
+       width: 1.875em;
+}
+.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool > .oo-ui-tool-link .oo-ui-tool-title {
+       line-height: 2.1em;
+}
+.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-enabled:hover {
+       border-color: rgba(0, 0, 0, 0.2);
+}
+.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-tool-active.oo-ui-widget-enabled {
+       border-color: rgba(0, 0, 0, 0.2);
+       box-shadow: inset 0 0.0875em 0.0875em 0 rgba(0, 0, 0, 0.07);
+       background-color: #f8fbfd;
+       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0, #f1f7fb), color-stop(100%, #ffffff));
+       background-image: -webkit-linear-gradient(top, #f1f7fb 0, #ffffff 100%);
+       background-image:    -moz-linear-gradient(top, #f1f7fb 0, #ffffff 100%);
+       background-image:         linear-gradient(to bottom, #f1f7fb 0, #ffffff 100%);
+       -ms-filter: "progid:DXImageTransform.Microsoft.gradient( startColorstr='#fff1f7fb', endColorstr='#ffffffff' )";
+}
+.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-tool-active.oo-ui-widget-enabled + .oo-ui-tool-active.oo-ui-widget-enabled {
+       border-left-color: rgba(0, 0, 0, 0.1);
+}
+.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-disabled > .oo-ui-tool-link:focus {
+       outline: 0;
+}
+.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-disabled > .oo-ui-tool-link .oo-ui-tool-title {
+       color: #cccccc;
+}
+.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-disabled > .oo-ui-tool-link .oo-ui-iconElement-icon {
+       opacity: 0.2;
+}
+.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-enabled:hover > .oo-ui-tool-link .oo-ui-iconElement-icon {
+       opacity: 1;
+}
+.oo-ui-barToolGroup.oo-ui-widget-disabled > .oo-ui-toolGroup-tools > .oo-ui-tool:focus {
+       outline: 0;
+}
+.oo-ui-barToolGroup.oo-ui-widget-disabled > .oo-ui-toolGroup-tools > .oo-ui-tool > .oo-ui-tool-link:focus {
+       outline: 0;
+}
+.oo-ui-barToolGroup.oo-ui-widget-disabled > .oo-ui-toolGroup-tools > .oo-ui-tool > .oo-ui-tool-link .oo-ui-tool-title {
+       color: #cccccc;
+}
+.oo-ui-barToolGroup.oo-ui-widget-disabled > .oo-ui-toolGroup-tools > .oo-ui-tool > .oo-ui-tool-link .oo-ui-iconElement-icon {
+       opacity: 0.2;
+}
+.oo-ui-popupToolGroup {
+       position: relative;
+       height: 2.5em;
+       min-width: 2.5em;
+}
+.oo-ui-popupToolGroup-handle {
+       display: block;
+       cursor: pointer;
+}
+.oo-ui-popupToolGroup-handle .oo-ui-indicatorElement-indicator,
+.oo-ui-popupToolGroup-handle .oo-ui-iconElement-icon {
+       position: absolute;
+}
+.oo-ui-popupToolGroup.oo-ui-widget-disabled .oo-ui-popupToolGroup-handle {
+       outline: 0;
+       cursor: default;
+}
+.oo-ui-popupToolGroup .oo-ui-toolGroup-tools {
+       display: none;
+       position: absolute;
+       z-index: 4;
+}
+.oo-ui-popupToolGroup-active.oo-ui-widget-enabled > .oo-ui-toolGroup-tools {
+       display: block;
+}
+.oo-ui-popupToolGroup-left > .oo-ui-toolGroup-tools {
+       left: 0;
+}
+.oo-ui-popupToolGroup-right > .oo-ui-toolGroup-tools {
+       right: 0;
+}
+.oo-ui-popupToolGroup .oo-ui-tool-link {
+       display: table;
+       width: 100%;
+       vertical-align: middle;
+       white-space: nowrap;
+}
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-iconElement-icon,
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel,
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title {
+       display: table-cell;
+       vertical-align: middle;
+}
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel {
+       text-align: right;
+}
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel:not(:empty) {
+       padding-left: 3em;
+}
+.oo-ui-toolbar-narrow .oo-ui-popupToolGroup {
+       min-width: 1.875em;
+}
+.oo-ui-popupToolGroup.oo-ui-iconElement {
+       min-width: 3.125em;
+}
+.oo-ui-toolbar-narrow .oo-ui-popupToolGroup.oo-ui-iconElement {
+       min-width: 2.5em;
+}
+.oo-ui-popupToolGroup.oo-ui-indicatorElement.oo-ui-iconElement {
+       min-width: 4.375em;
+}
+.oo-ui-toolbar-narrow .oo-ui-popupToolGroup.oo-ui-indicatorElement.oo-ui-iconElement {
+       min-width: 3.75em;
+}
+.oo-ui-popupToolGroup.oo-ui-labelElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
+       line-height: 2.6em;
+       margin: 0 1em;
+}
+.oo-ui-toolbar-narrow .oo-ui-popupToolGroup.oo-ui-labelElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
+       margin: 0 0.5em;
+}
+.oo-ui-popupToolGroup.oo-ui-labelElement.oo-ui-iconElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
+       margin-left: 3em;
+}
+.oo-ui-toolbar-narrow .oo-ui-popupToolGroup.oo-ui-labelElement.oo-ui-iconElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
+       margin-left: 2.5em;
+}
+.oo-ui-popupToolGroup.oo-ui-labelElement.oo-ui-indicatorElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
+       margin-right: 2.25em;
+}
+.oo-ui-toolbar-narrow .oo-ui-popupToolGroup.oo-ui-labelElement.oo-ui-indicatorElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
+       margin-right: 1.75em;
+}
+.oo-ui-popupToolGroup-handle .oo-ui-indicatorElement-indicator {
+       width: 0.9375em;
+       height: 0.9375em;
+       margin: 0.78125em;
+       top: 0;
+       right: 0;
+}
+.oo-ui-toolbar-narrow .oo-ui-popupToolGroup-handle .oo-ui-indicatorElement-indicator {
+       right: -0.3125em;
+}
+.oo-ui-popupToolGroup-handle .oo-ui-iconElement-icon {
+       width: 1.875em;
+       height: 1.875em;
+       margin: 0.3125em;
+       top: 0;
+       left: 0.3125em;
+}
+.oo-ui-toolbar-narrow .oo-ui-popupToolGroup-handle .oo-ui-iconElement-icon {
+       left: 0;
+}
+.oo-ui-popupToolGroup-header {
+       line-height: 2.6em;
+       margin: 0 0.6em;
+       font-weight: bold;
+}
+.oo-ui-popupToolGroup-active.oo-ui-widget-enabled {
+       border-bottom-left-radius: 0;
+       border-bottom-right-radius: 0;
+       box-shadow: inset 0 0.0875em 0.0875em 0 rgba(0, 0, 0, 0.07);
+       background-color: #f8fbfd;
+       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0, #f1f7fb), color-stop(100%, #ffffff));
+       background-image: -webkit-linear-gradient(top, #f1f7fb 0, #ffffff 100%);
+       background-image:    -moz-linear-gradient(top, #f1f7fb 0, #ffffff 100%);
+       background-image:         linear-gradient(to bottom, #f1f7fb 0, #ffffff 100%);
+       -ms-filter: "progid:DXImageTransform.Microsoft.gradient( startColorstr='#fff1f7fb', endColorstr='#ffffffff' )";
+}
+.oo-ui-popupToolGroup .oo-ui-toolGroup-tools {
+       top: 2.5em;
+       margin: 0 -1px;
+       border: 1px solid #cccccc;
+       background-color: white;
+       box-shadow: 0 0.3125em 1.25em rgba(0, 0, 0, 0.25);
+}
+.oo-ui-popupToolGroup .oo-ui-tool-link {
+       padding: 0.3125em 0 0.3125em 0.3125em;
+}
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-iconElement-icon {
+       height: 1.875em;
+       width: 1.875em;
+       min-width: 1.875em;
+}
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title {
+       padding-left: 0.5em;
+}
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel,
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title {
+       line-height: 2em;
+}
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel {
+       color: #888888;
+}
+.oo-ui-listToolGroup .oo-ui-tool {
+       display: block;
+       -webkit-box-sizing: border-box;
+          -moz-box-sizing: border-box;
+               box-sizing: border-box;
+}
+.oo-ui-listToolGroup .oo-ui-tool-link {
+       cursor: pointer;
+}
+.oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link {
+       cursor: default;
+}
+.oo-ui-listToolGroup .oo-ui-toolGroup-tools {
+       padding: 0.3125em;
+}
+.oo-ui-listToolGroup.oo-ui-popupToolGroup-active {
+       border-color: rgba(0, 0, 0, 0.2);
+}
+.oo-ui-listToolGroup .oo-ui-tool {
+       border: 1px solid transparent;
+       margin: -1px 0;
+       padding: 0 0.625em 0 0;
+}
+.oo-ui-listToolGroup .oo-ui-tool-active.oo-ui-widget-enabled {
+       border-color: rgba(0, 0, 0, 0.1);
+       box-shadow: inset 0 0.0875em 0.0875em 0 rgba(0, 0, 0, 0.07);
+       background-color: #f8fbfd;
+       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0, #f1f7fb), color-stop(100%, #ffffff));
+       background-image: -webkit-linear-gradient(top, #f1f7fb 0, #ffffff 100%);
+       background-image:    -moz-linear-gradient(top, #f1f7fb 0, #ffffff 100%);
+       background-image:         linear-gradient(to bottom, #f1f7fb 0, #ffffff 100%);
+       -ms-filter: "progid:DXImageTransform.Microsoft.gradient( startColorstr='#fff1f7fb', endColorstr='#ffffffff' )";
+}
+.oo-ui-listToolGroup .oo-ui-tool-active.oo-ui-widget-enabled + .oo-ui-tool-active.oo-ui-widget-enabled {
+       border-top-color: rgba(0, 0, 0, 0.1);
+}
+.oo-ui-listToolGroup .oo-ui-tool-active.oo-ui-widget-enabled:hover {
+       border-color: rgba(0, 0, 0, 0.2);
+}
+.oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-enabled:hover {
+       border-color: rgba(0, 0, 0, 0.2);
+}
+.oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-enabled:hover .oo-ui-tool-link .oo-ui-iconElement-icon {
+       opacity: 1;
+}
+.oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-tool-title {
+       color: #cccccc;
+}
+.oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-tool-accel {
+       color: #dddddd;
+}
+.oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-iconElement-icon {
+       opacity: 0.2;
+}
+.oo-ui-listToolGroup.oo-ui-widget-disabled {
+       color: #cccccc;
+}
+.oo-ui-listToolGroup.oo-ui-widget-disabled .oo-ui-indicatorElement-indicator,
+.oo-ui-listToolGroup.oo-ui-widget-disabled .oo-ui-iconElement-icon {
+       opacity: 0.2;
+}
+.oo-ui-menuToolGroup {
+       border-color: rgba(0, 0, 0, 0.1);
+}
+.oo-ui-menuToolGroup .oo-ui-tool {
+       display: block;
+}
+.oo-ui-menuToolGroup .oo-ui-tool-link {
+       cursor: pointer;
+}
+.oo-ui-menuToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link {
+       cursor: default;
+}
+.oo-ui-menuToolGroup .oo-ui-popupToolGroup-handle {
+       min-width: 10em;
+}
+.oo-ui-toolbar-narrow .oo-ui-menuToolGroup .oo-ui-popupToolGroup-handle {
+       min-width: 8.125em;
+}
+.oo-ui-menuToolGroup .oo-ui-toolGroup-tools {
+       padding: 0.3125em 0 0.3125em 0;
+}
+.oo-ui-menuToolGroup.oo-ui-widget-enabled:hover {
+       border-color: rgba(0, 0, 0, 0.2);
+}
+.oo-ui-menuToolGroup.oo-ui-popupToolGroup-active {
+       border-color: rgba(0, 0, 0, 0.25);
+}
+.oo-ui-menuToolGroup .oo-ui-tool {
+       padding: 0 1.25em 0 0.3125em;
+}
+.oo-ui-menuToolGroup .oo-ui-tool-link .oo-ui-iconElement-icon {
+       background-image: none;
+}
+.oo-ui-menuToolGroup .oo-ui-tool-active .oo-ui-tool-link .oo-ui-iconElement-icon {
+       background-image: url("themes/apex/images/icons/check.png");
+       background-image: -webkit-linear-gradient(transparent, transparent), /* @embed */ url("themes/apex/images/icons/check.svg");
+       background-image:         linear-gradient(transparent, transparent), /* @embed */ url("themes/apex/images/icons/check.svg");
+       background-image:      -o-linear-gradient(transparent, transparent), url("themes/apex/images/icons/check.png");
+       background-size: contain;
+       background-position: center center;
+       background-repeat: no-repeat;
+}
+.oo-ui-menuToolGroup .oo-ui-tool.oo-ui-widget-enabled:hover {
+       background-color: #e1f3ff;
+}
+.oo-ui-menuToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-tool-title {
+       color: #cccccc;
+}
+.oo-ui-menuToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-iconElement-icon {
+       opacity: 0.2;
+}
+.oo-ui-menuToolGroup.oo-ui-widget-disabled {
+       color: #cccccc;
+       border-color: rgba(0, 0, 0, 0.05);
+}
+.oo-ui-menuToolGroup.oo-ui-widget-disabled .oo-ui-indicatorElement-indicator,
+.oo-ui-menuToolGroup.oo-ui-widget-disabled .oo-ui-iconElement-icon {
+       opacity: 0.2;
+}
+.oo-ui-toolbar {
+       clear: both;
+}
+.oo-ui-toolbar-bar {
+       line-height: 1em;
+       position: relative;
+}
+.oo-ui-toolbar-actions {
+       float: right;
+}
+.oo-ui-toolbar-actions .oo-ui-toolbar {
+       display: inline-block;
+}
+.oo-ui-toolbar-tools {
+       display: inline;
+       white-space: nowrap;
+}
+.oo-ui-toolbar-narrow .oo-ui-toolbar-tools {
+       white-space: normal;
+}
+.oo-ui-toolbar-tools .oo-ui-tool {
+       white-space: normal;
+}
+.oo-ui-toolbar-tools,
+.oo-ui-toolbar-actions,
+.oo-ui-toolbar-shadow {
+       -webkit-touch-callout: none;
+       -webkit-user-select: none;
+          -moz-user-select: none;
+           -ms-user-select: none;
+               user-select: none;
+}
+.oo-ui-toolbar-actions .oo-ui-popupWidget {
+       -webkit-touch-callout: default;
+       -webkit-user-select: all;
+          -moz-user-select: all;
+           -ms-user-select: all;
+               user-select: all;
+}
+.oo-ui-toolbar-shadow {
+       background-position: left top;
+       background-repeat: repeat-x;
+       position: absolute;
+       width: 100%;
+       pointer-events: none;
+}
+.oo-ui-toolbar-bar {
+       border-bottom: 1px solid #cccccc;
+       background-color: #f8fbfd;
+       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0, #ffffff), color-stop(100%, #f1f7fb));
+       background-image: -webkit-linear-gradient(top, #ffffff 0, #f1f7fb 100%);
+       background-image:    -moz-linear-gradient(top, #ffffff 0, #f1f7fb 100%);
+       background-image:         linear-gradient(to bottom, #ffffff 0, #f1f7fb 100%);
+       -ms-filter: "progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffffff', endColorstr='#fff1f7fb' )";
+}
+.oo-ui-toolbar-bar .oo-ui-toolbar-bar {
+       border: none;
+       background: none;
+}
+.oo-ui-toolbar-actions > .oo-ui-buttonElement-framed,
+.oo-ui-toolbar-actions > .oo-ui-buttonElement-framed:last-child {
+       margin-top: 0.4em;
+       margin-bottom: 0.4em;
+       margin-right: 0.5em;
+}
+.oo-ui-toolbar-actions > .oo-ui-buttonElement-frameless.oo-ui-labelElement,
+.oo-ui-toolbar-actions > .oo-ui-buttonElement-frameless:last-child.oo-ui-labelElement {
+       margin: 0;
+}
+.oo-ui-toolbar-actions > .oo-ui-buttonElement-frameless.oo-ui-labelElement > .oo-ui-buttonElement-button,
+.oo-ui-toolbar-actions > .oo-ui-buttonElement-frameless:last-child.oo-ui-labelElement > .oo-ui-buttonElement-button {
+       margin: 0;
+       padding: 0 0.3125em;
+}
+.oo-ui-toolbar-actions > .oo-ui-buttonElement-frameless.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label,
+.oo-ui-toolbar-actions > .oo-ui-buttonElement-frameless:last-child.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
+       margin: 0 1em;
+       line-height: 3.40625em;
+}
+.oo-ui-toolbar-shadow {
+       background-image: /* @embed */ url(themes/apex/images/toolbar-shadow.png);
+       bottom: -9px;
+       height: 9px;
+       opacity: 0.5;
+       -webkit-transition: opacity 500ms ease;
+          -moz-transition: opacity 500ms ease;
+               transition: opacity 500ms ease;
+}
diff --git a/resources/lib/oojs-ui/oojs-ui-toolbars-mediawiki.css b/resources/lib/oojs-ui/oojs-ui-toolbars-mediawiki.css
new file mode 100644 (file)
index 0000000..7b1e5b7
--- /dev/null
@@ -0,0 +1,459 @@
+/*!
+ * OOjs UI v0.15.3
+ * https://www.mediawiki.org/wiki/OOjs_UI
+ *
+ * Copyright 2011–2016 OOjs UI Team and other contributors.
+ * Released under the MIT license
+ * http://oojs.mit-license.org
+ *
+ * Date: 2016-02-09T21:21:21Z
+ */
+.oo-ui-popupTool .oo-ui-popupWidget-popup,
+.oo-ui-popupTool .oo-ui-popupWidget-anchor {
+       z-index: 4;
+}
+.oo-ui-popupTool .oo-ui-popupWidget {
+       /* @noflip */
+       margin-left: 1.25em;
+}
+.oo-ui-toolGroupTool > .oo-ui-popupToolGroup {
+       border: 0;
+       border-radius: 0;
+       margin: 0;
+}
+.oo-ui-toolGroupTool > .oo-ui-toolGroup {
+       border-right: none;
+}
+.oo-ui-toolGroupTool > .oo-ui-popupToolGroup > .oo-ui-popupToolGroup-handle {
+       height: 2.5em;
+       padding: 0.3125em;
+}
+.oo-ui-toolGroupTool > .oo-ui-popupToolGroup > .oo-ui-popupToolGroup-handle .oo-ui-iconElement-icon {
+       height: 2.5em;
+       width: 1.875em;
+}
+.oo-ui-toolGroupTool > .oo-ui-popupToolGroup.oo-ui-labelElement > .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
+       line-height: 2.1em;
+}
+.oo-ui-toolGroup {
+       display: inline-block;
+       vertical-align: middle;
+       border-radius: 0;
+       border-right: 1px solid #dddddd;
+}
+.oo-ui-toolGroup-empty {
+       display: none;
+}
+.oo-ui-toolGroup .oo-ui-tool-link {
+       text-decoration: none;
+}
+.oo-ui-toolbar-narrow .oo-ui-toolGroup + .oo-ui-toolGroup {
+       margin-left: 0;
+}
+.oo-ui-toolGroup .oo-ui-toolGroup .oo-ui-widget-enabled {
+       border-right: none !important;
+}
+.oo-ui-barToolGroup > .oo-ui-iconElement-icon,
+.oo-ui-barToolGroup > .oo-ui-labelElement-label {
+       display: none;
+}
+.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool > .oo-ui-tool-link {
+       cursor: pointer;
+}
+.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool {
+       display: inline-block;
+       position: relative;
+       vertical-align: top;
+}
+.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool > .oo-ui-tool-link {
+       display: block;
+}
+.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool > .oo-ui-tool-link .oo-ui-tool-accel {
+       display: none;
+}
+.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-iconElement > .oo-ui-tool-link .oo-ui-iconElement-icon {
+       display: inline-block;
+       vertical-align: top;
+}
+.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-iconElement > .oo-ui-tool-link .oo-ui-tool-title {
+       display: none;
+}
+.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-iconElement.oo-ui-tool-with-label > .oo-ui-tool-link .oo-ui-tool-title {
+       display: inline;
+}
+.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-disabled > .oo-ui-tool-link {
+       outline: 0;
+       cursor: default;
+}
+.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool > .oo-ui-tool-link {
+       height: 1.875em;
+       padding: 0.625em;
+}
+.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool > .oo-ui-tool-link .oo-ui-iconElement-icon {
+       height: 1.875em;
+       width: 1.875em;
+}
+.oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool > .oo-ui-tool-link .oo-ui-tool-title {
+       line-height: 2.1em;
+       padding: 0 0.4em;
+}
+.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-enabled:hover {
+       border-color: rgba(0, 0, 0, 0.2);
+       background-color: #eeeeee;
+}
+.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool > a.oo-ui-tool-link .oo-ui-tool-title {
+       color: #555555;
+}
+.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-tool-active.oo-ui-widget-enabled {
+       border-color: rgba(0, 0, 0, 0.2);
+       box-shadow: inset 0 0.07em 0.07em 0 rgba(0, 0, 0, 0.07);
+       background-color: #e5e5e5;
+}
+.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-tool-active.oo-ui-widget-enabled:hover {
+       background-color: #eeeeee;
+}
+.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-tool-active.oo-ui-widget-enabled + .oo-ui-tool-active.oo-ui-widget-enabled {
+       border-left-color: rgba(0, 0, 0, 0.1);
+}
+.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-disabled > .oo-ui-tool-link .oo-ui-tool-title {
+       color: #cccccc;
+}
+.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-disabled > .oo-ui-tool-link .oo-ui-iconElement-icon {
+       opacity: 0.2;
+}
+.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-enabled > .oo-ui-tool-link .oo-ui-iconElement-icon {
+       opacity: 0.7;
+}
+.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-enabled:hover > .oo-ui-tool-link .oo-ui-iconElement-icon {
+       opacity: 0.9;
+}
+.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-enabled:active {
+       background-color: #e7e7e7;
+}
+.oo-ui-barToolGroup.oo-ui-widget-disabled > .oo-ui-toolGroup-tools > .oo-ui-tool > a.oo-ui-tool-link .oo-ui-tool-title {
+       color: #cccccc;
+}
+.oo-ui-barToolGroup.oo-ui-widget-disabled > .oo-ui-toolGroup-tools > .oo-ui-tool > a.oo-ui-tool-link .oo-ui-iconElement-icon {
+       opacity: 0.2;
+}
+.oo-ui-popupToolGroup {
+       position: relative;
+       height: 3.125em;
+       min-width: 2em;
+}
+.oo-ui-popupToolGroup-handle {
+       display: block;
+       cursor: pointer;
+}
+.oo-ui-popupToolGroup-handle .oo-ui-indicatorElement-indicator,
+.oo-ui-popupToolGroup-handle .oo-ui-iconElement-icon {
+       position: absolute;
+}
+.oo-ui-popupToolGroup.oo-ui-widget-disabled .oo-ui-popupToolGroup-handle {
+       outline: 0;
+       cursor: default;
+}
+.oo-ui-popupToolGroup .oo-ui-toolGroup-tools {
+       display: none;
+       position: absolute;
+       z-index: 4;
+}
+.oo-ui-popupToolGroup-active.oo-ui-widget-enabled > .oo-ui-toolGroup-tools {
+       display: block;
+}
+.oo-ui-popupToolGroup-left > .oo-ui-toolGroup-tools {
+       left: 0;
+}
+.oo-ui-popupToolGroup-right > .oo-ui-toolGroup-tools {
+       right: 0;
+}
+.oo-ui-popupToolGroup .oo-ui-tool-link {
+       display: table;
+       width: 100%;
+       vertical-align: middle;
+       white-space: nowrap;
+}
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-iconElement-icon,
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel,
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title {
+       display: table-cell;
+       vertical-align: middle;
+}
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel {
+       text-align: right;
+}
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel:not(:empty) {
+       padding-left: 3em;
+}
+.oo-ui-toolbar-narrow .oo-ui-popupToolGroup {
+       min-width: 1.875em;
+}
+.oo-ui-popupToolGroup.oo-ui-iconElement {
+       min-width: 3.125em;
+}
+.oo-ui-toolbar-narrow .oo-ui-popupToolGroup.oo-ui-iconElement {
+       min-width: 2.5em;
+}
+.oo-ui-popupToolGroup.oo-ui-indicatorElement.oo-ui-iconElement {
+       min-width: 4.375em;
+}
+.oo-ui-toolbar-narrow .oo-ui-popupToolGroup.oo-ui-indicatorElement.oo-ui-iconElement {
+       min-width: 3.75em;
+}
+.oo-ui-popupToolGroup.oo-ui-labelElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
+       line-height: 2.6em;
+       margin: 0 1em;
+}
+.oo-ui-toolbar-narrow .oo-ui-popupToolGroup.oo-ui-labelElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
+       margin: 0 0.5em;
+}
+.oo-ui-popupToolGroup.oo-ui-labelElement.oo-ui-iconElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
+       margin-left: 3em;
+}
+.oo-ui-toolbar-narrow .oo-ui-popupToolGroup.oo-ui-labelElement.oo-ui-iconElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
+       margin-left: 2.5em;
+}
+.oo-ui-popupToolGroup.oo-ui-labelElement.oo-ui-indicatorElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
+       margin-right: 2em;
+}
+.oo-ui-toolbar-narrow .oo-ui-popupToolGroup.oo-ui-labelElement.oo-ui-indicatorElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
+       margin-right: 1.75em;
+}
+.oo-ui-popupToolGroup.oo-ui-widget-enabled .oo-ui-popupToolGroup-handle:hover {
+       background-color: #eeeeee;
+}
+.oo-ui-popupToolGroup.oo-ui-widget-enabled .oo-ui-popupToolGroup-handle:active {
+       background-color: #e5e5e5;
+}
+.oo-ui-popupToolGroup-handle {
+       padding: 0.3125em;
+       height: 2.5em;
+}
+.oo-ui-popupToolGroup-handle .oo-ui-indicatorElement-indicator {
+       width: 0.9375em;
+       height: 1.625em;
+       margin: 0.78125em 0.5em;
+       top: 0;
+       right: 0;
+       opacity: 0.3;
+}
+.oo-ui-toolbar-narrow .oo-ui-popupToolGroup-handle .oo-ui-indicatorElement-indicator {
+       right: -0.3125em;
+}
+.oo-ui-popupToolGroup-handle .oo-ui-iconElement-icon {
+       width: 1.875em;
+       height: 2.6em;
+       margin: 0.25em;
+       top: 0;
+       left: 0.3125em;
+       opacity: 0.7;
+}
+.oo-ui-toolbar-narrow .oo-ui-popupToolGroup-handle .oo-ui-iconElement-icon {
+       left: 0;
+}
+.oo-ui-popupToolGroup-header {
+       line-height: 2.6em;
+       margin: 0 0.6em;
+       font-weight: bold;
+}
+.oo-ui-popupToolGroup-active.oo-ui-widget-enabled {
+       border-bottom-left-radius: 0;
+       border-bottom-right-radius: 0;
+       box-shadow: inset 0 0.07em 0.07em 0 rgba(0, 0, 0, 0.07);
+       background-color: #eeeeee;
+}
+.oo-ui-popupToolGroup .oo-ui-toolGroup-tools {
+       top: 3.125em;
+       margin: 0 -1px;
+       border: 1px solid #cccccc;
+       background-color: #ffffff;
+       box-shadow: 0 2px 3px rgba(0, 0, 0, 0.2);
+       min-width: 16em;
+}
+.oo-ui-popupToolGroup .oo-ui-tool-link {
+       padding: 0.4em 0.625em;
+       box-sizing: border-box;
+}
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-iconElement-icon {
+       height: 2.5em;
+       width: 1.875em;
+       min-width: 1.875em;
+}
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title {
+       padding-left: 0.5em;
+       color: #555555;
+}
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel,
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title {
+       line-height: 2em;
+}
+.oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel {
+       color: #888888;
+}
+.oo-ui-listToolGroup .oo-ui-tool {
+       display: block;
+       -webkit-box-sizing: border-box;
+          -moz-box-sizing: border-box;
+               box-sizing: border-box;
+}
+.oo-ui-listToolGroup .oo-ui-tool-link {
+       cursor: pointer;
+}
+.oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link {
+       cursor: default;
+}
+.oo-ui-listToolGroup.oo-ui-popupToolGroup-active {
+       border-color: rgba(0, 0, 0, 0.2);
+}
+.oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-enabled:hover {
+       border-color: rgba(0, 0, 0, 0.2);
+       background-color: #eeeeee;
+}
+.oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-enabled:active {
+       background-color: #e7e7e7;
+}
+.oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-enabled:hover .oo-ui-tool-link .oo-ui-iconElement-icon {
+       opacity: 0.9;
+}
+.oo-ui-listToolGroup .oo-ui-tool-active.oo-ui-widget-enabled {
+       border-color: rgba(0, 0, 0, 0.1);
+       box-shadow: inset 0 0.07em 0.07em 0 rgba(0, 0, 0, 0.07);
+       background-color: #e5e5e5;
+}
+.oo-ui-listToolGroup .oo-ui-tool-active.oo-ui-widget-enabled + .oo-ui-tool-active.oo-ui-widget-enabled {
+       border-top-color: rgba(0, 0, 0, 0.1);
+}
+.oo-ui-listToolGroup .oo-ui-tool-active.oo-ui-widget-enabled:hover {
+       border-color: rgba(0, 0, 0, 0.2);
+       background-color: #eeeeee;
+}
+.oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-tool-title {
+       color: #cccccc;
+}
+.oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-tool-accel {
+       color: #dddddd;
+}
+.oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-iconElement-icon {
+       opacity: 0.2;
+}
+.oo-ui-listToolGroup.oo-ui-widget-disabled {
+       color: #cccccc;
+}
+.oo-ui-listToolGroup.oo-ui-widget-disabled .oo-ui-indicatorElement-indicator,
+.oo-ui-listToolGroup.oo-ui-widget-disabled .oo-ui-iconElement-icon {
+       opacity: 0.2;
+}
+.oo-ui-menuToolGroup .oo-ui-tool {
+       display: block;
+}
+.oo-ui-menuToolGroup .oo-ui-tool-link {
+       cursor: pointer;
+}
+.oo-ui-menuToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link {
+       cursor: default;
+}
+.oo-ui-menuToolGroup .oo-ui-popupToolGroup-handle {
+       min-width: 10em;
+}
+.oo-ui-toolbar-narrow .oo-ui-menuToolGroup .oo-ui-popupToolGroup-handle {
+       min-width: 8.125em;
+}
+.oo-ui-menuToolGroup .oo-ui-tool-link .oo-ui-iconElement-icon {
+       background-image: none;
+}
+.oo-ui-menuToolGroup .oo-ui-tool-active .oo-ui-tool-link .oo-ui-iconElement-icon {
+       background-image: url("themes/mediawiki/images/icons/check.png");
+       background-image: -webkit-linear-gradient(transparent, transparent), /* @embed */ url("themes/mediawiki/images/icons/check.svg");
+       background-image:         linear-gradient(transparent, transparent), /* @embed */ url("themes/mediawiki/images/icons/check.svg");
+       background-image:      -o-linear-gradient(transparent, transparent), url("themes/mediawiki/images/icons/check.png");
+       background-size: contain;
+       background-position: center center;
+       background-repeat: no-repeat;
+}
+.oo-ui-menuToolGroup .oo-ui-tool.oo-ui-widget-enabled:hover {
+       background-color: #eeeeee;
+}
+.oo-ui-menuToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-tool-title {
+       color: #cccccc;
+}
+.oo-ui-menuToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-iconElement-icon {
+       opacity: 0.2;
+}
+.oo-ui-menuToolGroup.oo-ui-widget-disabled {
+       color: #cccccc;
+}
+.oo-ui-menuToolGroup.oo-ui-widget-disabled .oo-ui-indicatorElement-indicator,
+.oo-ui-menuToolGroup.oo-ui-widget-disabled .oo-ui-iconElement-icon {
+       opacity: 0.2;
+}
+.oo-ui-toolbar {
+       clear: both;
+}
+.oo-ui-toolbar-bar {
+       line-height: 1em;
+       position: relative;
+}
+.oo-ui-toolbar-actions {
+       float: right;
+}
+.oo-ui-toolbar-actions .oo-ui-toolbar {
+       display: inline-block;
+}
+.oo-ui-toolbar-tools {
+       display: inline;
+       white-space: nowrap;
+}
+.oo-ui-toolbar-narrow .oo-ui-toolbar-tools {
+       white-space: normal;
+}
+.oo-ui-toolbar-tools .oo-ui-tool {
+       white-space: normal;
+}
+.oo-ui-toolbar-tools,
+.oo-ui-toolbar-actions,
+.oo-ui-toolbar-shadow {
+       -webkit-touch-callout: none;
+       -webkit-user-select: none;
+          -moz-user-select: none;
+           -ms-user-select: none;
+               user-select: none;
+}
+.oo-ui-toolbar-actions .oo-ui-popupWidget {
+       -webkit-touch-callout: default;
+       -webkit-user-select: all;
+          -moz-user-select: all;
+           -ms-user-select: all;
+               user-select: all;
+}
+.oo-ui-toolbar-shadow {
+       background-position: left top;
+       background-repeat: repeat-x;
+       position: absolute;
+       width: 100%;
+       pointer-events: none;
+}
+.oo-ui-toolbar-bar {
+       border-bottom: 1px solid #cccccc;
+       background-color: #ffffff;
+       box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
+       font-weight: 500;
+       color: #555555;
+}
+.oo-ui-toolbar-bar .oo-ui-toolbar-bar {
+       border: 0;
+       background: none;
+       box-shadow: none;
+}
+.oo-ui-toolbar-actions > .oo-ui-buttonElement.oo-ui-labelElement {
+       margin: 0;
+}
+.oo-ui-toolbar-actions > .oo-ui-buttonElement.oo-ui-labelElement > .oo-ui-buttonElement-button {
+       border: 0;
+       border-radius: 0;
+       margin: 0;
+       padding: 0 0.3125em;
+}
+.oo-ui-toolbar-actions > .oo-ui-buttonElement.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
+       margin: 0 1em;
+       line-height: 3.125em;
+}
diff --git a/resources/lib/oojs-ui/oojs-ui-toolbars.js b/resources/lib/oojs-ui/oojs-ui-toolbars.js
new file mode 100644 (file)
index 0000000..1d1ed87
--- /dev/null
@@ -0,0 +1,2305 @@
+/*!
+ * OOjs UI v0.15.3
+ * https://www.mediawiki.org/wiki/OOjs_UI
+ *
+ * Copyright 2011–2016 OOjs UI Team and other contributors.
+ * Released under the MIT license
+ * http://oojs.mit-license.org
+ *
+ * Date: 2016-02-09T21:21:16Z
+ */
+( function ( OO ) {
+
+'use strict';
+
+/**
+ * Toolbars are complex interface components that permit users to easily access a variety
+ * of {@link OO.ui.Tool tools} (e.g., formatting commands) and actions, which are additional commands that are
+ * part of the toolbar, but not configured as tools.
+ *
+ * Individual tools are customized and then registered with a {@link OO.ui.ToolFactory tool factory}, which creates
+ * the tools on demand. Each tool has a symbolic name (used when registering the tool), a title (e.g., ‘Insert
+ * image’), and an icon.
+ *
+ * Individual tools are organized in {@link OO.ui.ToolGroup toolgroups}, which can be {@link OO.ui.MenuToolGroup menus}
+ * of tools, {@link OO.ui.ListToolGroup lists} of tools, or a single {@link OO.ui.BarToolGroup bar} of tools.
+ * The arrangement and order of the toolgroups is customized when the toolbar is set up. Tools can be presented in
+ * any order, but each can only appear once in the toolbar.
+ *
+ * The toolbar can be synchronized with the state of the external "application", like a text
+ * editor's editing area, marking tools as active/inactive (e.g. a 'bold' tool would be shown as
+ * active when the text cursor was inside bolded text) or enabled/disabled (e.g. a table caption
+ * tool would be disabled while the user is not editing a table). A state change is signalled by
+ * emitting the {@link #event-updateState 'updateState' event}, which calls Tools'
+ * {@link OO.ui.Tool#onUpdateState onUpdateState method}.
+ *
+ * The following is an example of a basic toolbar.
+ *
+ *     @example
+ *     // Example of a toolbar
+ *     // Create the toolbar
+ *     var toolFactory = new OO.ui.ToolFactory();
+ *     var toolGroupFactory = new OO.ui.ToolGroupFactory();
+ *     var toolbar = new OO.ui.Toolbar( toolFactory, toolGroupFactory );
+ *
+ *     // We will be placing status text in this element when tools are used
+ *     var $area = $( '<p>' ).text( 'Toolbar example' );
+ *
+ *     // Define the tools that we're going to place in our toolbar
+ *
+ *     // Create a class inheriting from OO.ui.Tool
+ *     function SearchTool() {
+ *         SearchTool.parent.apply( this, arguments );
+ *     }
+ *     OO.inheritClass( SearchTool, OO.ui.Tool );
+ *     // Each tool must have a 'name' (used as an internal identifier, see later) and at least one
+ *     // of 'icon' and 'title' (displayed icon and text).
+ *     SearchTool.static.name = 'search';
+ *     SearchTool.static.icon = 'search';
+ *     SearchTool.static.title = 'Search...';
+ *     // Defines the action that will happen when this tool is selected (clicked).
+ *     SearchTool.prototype.onSelect = function () {
+ *         $area.text( 'Search tool clicked!' );
+ *         // Never display this tool as "active" (selected).
+ *         this.setActive( false );
+ *     };
+ *     SearchTool.prototype.onUpdateState = function () {};
+ *     // Make this tool available in our toolFactory and thus our toolbar
+ *     toolFactory.register( SearchTool );
+ *
+ *     // Register two more tools, nothing interesting here
+ *     function SettingsTool() {
+ *         SettingsTool.parent.apply( this, arguments );
+ *     }
+ *     OO.inheritClass( SettingsTool, OO.ui.Tool );
+ *     SettingsTool.static.name = 'settings';
+ *     SettingsTool.static.icon = 'settings';
+ *     SettingsTool.static.title = 'Change settings';
+ *     SettingsTool.prototype.onSelect = function () {
+ *         $area.text( 'Settings tool clicked!' );
+ *         this.setActive( false );
+ *     };
+ *     SettingsTool.prototype.onUpdateState = function () {};
+ *     toolFactory.register( SettingsTool );
+ *
+ *     // Register two more tools, nothing interesting here
+ *     function StuffTool() {
+ *         StuffTool.parent.apply( this, arguments );
+ *     }
+ *     OO.inheritClass( StuffTool, OO.ui.Tool );
+ *     StuffTool.static.name = 'stuff';
+ *     StuffTool.static.icon = 'ellipsis';
+ *     StuffTool.static.title = 'More stuff';
+ *     StuffTool.prototype.onSelect = function () {
+ *         $area.text( 'More stuff tool clicked!' );
+ *         this.setActive( false );
+ *     };
+ *     StuffTool.prototype.onUpdateState = function () {};
+ *     toolFactory.register( StuffTool );
+ *
+ *     // This is a PopupTool. Rather than having a custom 'onSelect' action, it will display a
+ *     // little popup window (a PopupWidget).
+ *     function HelpTool( toolGroup, config ) {
+ *         OO.ui.PopupTool.call( this, toolGroup, $.extend( { popup: {
+ *             padded: true,
+ *             label: 'Help',
+ *             head: true
+ *         } }, config ) );
+ *         this.popup.$body.append( '<p>I am helpful!</p>' );
+ *     }
+ *     OO.inheritClass( HelpTool, OO.ui.PopupTool );
+ *     HelpTool.static.name = 'help';
+ *     HelpTool.static.icon = 'help';
+ *     HelpTool.static.title = 'Help';
+ *     toolFactory.register( HelpTool );
+ *
+ *     // Finally define which tools and in what order appear in the toolbar. Each tool may only be
+ *     // used once (but not all defined tools must be used).
+ *     toolbar.setup( [
+ *         {
+ *             // 'bar' tool groups display tools' icons only, side-by-side.
+ *             type: 'bar',
+ *             include: [ 'search', 'help' ]
+ *         },
+ *         {
+ *             // 'list' tool groups display both the titles and icons, in a dropdown list.
+ *             type: 'list',
+ *             indicator: 'down',
+ *             label: 'More',
+ *             include: [ 'settings', 'stuff' ]
+ *         }
+ *         // Note how the tools themselves are toolgroup-agnostic - the same tool can be displayed
+ *         // either in a 'list' or a 'bar'. There is a 'menu' tool group too, not showcased here,
+ *         // since it's more complicated to use. (See the next example snippet on this page.)
+ *     ] );
+ *
+ *     // Create some UI around the toolbar and place it in the document
+ *     var frame = new OO.ui.PanelLayout( {
+ *         expanded: false,
+ *         framed: true
+ *     } );
+ *     var contentFrame = new OO.ui.PanelLayout( {
+ *         expanded: false,
+ *         padded: true
+ *     } );
+ *     frame.$element.append(
+ *         toolbar.$element,
+ *         contentFrame.$element.append( $area )
+ *     );
+ *     $( 'body' ).append( frame.$element );
+ *
+ *     // Here is where the toolbar is actually built. This must be done after inserting it into the
+ *     // document.
+ *     toolbar.initialize();
+ *     toolbar.emit( 'updateState' );
+ *
+ * The following example extends the previous one to illustrate 'menu' toolgroups and the usage of
+ * {@link #event-updateState 'updateState' event}.
+ *
+ *     @example
+ *     // Create the toolbar
+ *     var toolFactory = new OO.ui.ToolFactory();
+ *     var toolGroupFactory = new OO.ui.ToolGroupFactory();
+ *     var toolbar = new OO.ui.Toolbar( toolFactory, toolGroupFactory );
+ *
+ *     // We will be placing status text in this element when tools are used
+ *     var $area = $( '<p>' ).text( 'Toolbar example' );
+ *
+ *     // Define the tools that we're going to place in our toolbar
+ *
+ *     // Create a class inheriting from OO.ui.Tool
+ *     function SearchTool() {
+ *         SearchTool.parent.apply( this, arguments );
+ *     }
+ *     OO.inheritClass( SearchTool, OO.ui.Tool );
+ *     // Each tool must have a 'name' (used as an internal identifier, see later) and at least one
+ *     // of 'icon' and 'title' (displayed icon and text).
+ *     SearchTool.static.name = 'search';
+ *     SearchTool.static.icon = 'search';
+ *     SearchTool.static.title = 'Search...';
+ *     // Defines the action that will happen when this tool is selected (clicked).
+ *     SearchTool.prototype.onSelect = function () {
+ *         $area.text( 'Search tool clicked!' );
+ *         // Never display this tool as "active" (selected).
+ *         this.setActive( false );
+ *     };
+ *     SearchTool.prototype.onUpdateState = function () {};
+ *     // Make this tool available in our toolFactory and thus our toolbar
+ *     toolFactory.register( SearchTool );
+ *
+ *     // Register two more tools, nothing interesting here
+ *     function SettingsTool() {
+ *         SettingsTool.parent.apply( this, arguments );
+ *         this.reallyActive = false;
+ *     }
+ *     OO.inheritClass( SettingsTool, OO.ui.Tool );
+ *     SettingsTool.static.name = 'settings';
+ *     SettingsTool.static.icon = 'settings';
+ *     SettingsTool.static.title = 'Change settings';
+ *     SettingsTool.prototype.onSelect = function () {
+ *         $area.text( 'Settings tool clicked!' );
+ *         // Toggle the active state on each click
+ *         this.reallyActive = !this.reallyActive;
+ *         this.setActive( this.reallyActive );
+ *         // To update the menu label
+ *         this.toolbar.emit( 'updateState' );
+ *     };
+ *     SettingsTool.prototype.onUpdateState = function () {};
+ *     toolFactory.register( SettingsTool );
+ *
+ *     // Register two more tools, nothing interesting here
+ *     function StuffTool() {
+ *         StuffTool.parent.apply( this, arguments );
+ *         this.reallyActive = false;
+ *     }
+ *     OO.inheritClass( StuffTool, OO.ui.Tool );
+ *     StuffTool.static.name = 'stuff';
+ *     StuffTool.static.icon = 'ellipsis';
+ *     StuffTool.static.title = 'More stuff';
+ *     StuffTool.prototype.onSelect = function () {
+ *         $area.text( 'More stuff tool clicked!' );
+ *         // Toggle the active state on each click
+ *         this.reallyActive = !this.reallyActive;
+ *         this.setActive( this.reallyActive );
+ *         // To update the menu label
+ *         this.toolbar.emit( 'updateState' );
+ *     };
+ *     StuffTool.prototype.onUpdateState = function () {};
+ *     toolFactory.register( StuffTool );
+ *
+ *     // This is a PopupTool. Rather than having a custom 'onSelect' action, it will display a
+ *     // little popup window (a PopupWidget). 'onUpdateState' is also already implemented.
+ *     function HelpTool( toolGroup, config ) {
+ *         OO.ui.PopupTool.call( this, toolGroup, $.extend( { popup: {
+ *             padded: true,
+ *             label: 'Help',
+ *             head: true
+ *         } }, config ) );
+ *         this.popup.$body.append( '<p>I am helpful!</p>' );
+ *     }
+ *     OO.inheritClass( HelpTool, OO.ui.PopupTool );
+ *     HelpTool.static.name = 'help';
+ *     HelpTool.static.icon = 'help';
+ *     HelpTool.static.title = 'Help';
+ *     toolFactory.register( HelpTool );
+ *
+ *     // Finally define which tools and in what order appear in the toolbar. Each tool may only be
+ *     // used once (but not all defined tools must be used).
+ *     toolbar.setup( [
+ *         {
+ *             // 'bar' tool groups display tools' icons only, side-by-side.
+ *             type: 'bar',
+ *             include: [ 'search', 'help' ]
+ *         },
+ *         {
+ *             // 'menu' tool groups display both the titles and icons, in a dropdown menu.
+ *             // Menu label indicates which items are selected.
+ *             type: 'menu',
+ *             indicator: 'down',
+ *             include: [ 'settings', 'stuff' ]
+ *         }
+ *     ] );
+ *
+ *     // Create some UI around the toolbar and place it in the document
+ *     var frame = new OO.ui.PanelLayout( {
+ *         expanded: false,
+ *         framed: true
+ *     } );
+ *     var contentFrame = new OO.ui.PanelLayout( {
+ *         expanded: false,
+ *         padded: true
+ *     } );
+ *     frame.$element.append(
+ *         toolbar.$element,
+ *         contentFrame.$element.append( $area )
+ *     );
+ *     $( 'body' ).append( frame.$element );
+ *
+ *     // Here is where the toolbar is actually built. This must be done after inserting it into the
+ *     // document.
+ *     toolbar.initialize();
+ *     toolbar.emit( 'updateState' );
+ *
+ * @class
+ * @extends OO.ui.Element
+ * @mixins OO.EventEmitter
+ * @mixins OO.ui.mixin.GroupElement
+ *
+ * @constructor
+ * @param {OO.ui.ToolFactory} toolFactory Factory for creating tools
+ * @param {OO.ui.ToolGroupFactory} toolGroupFactory Factory for creating toolgroups
+ * @param {Object} [config] Configuration options
+ * @cfg {boolean} [actions] Add an actions section to the toolbar. Actions are commands that are included
+ *  in the toolbar, but are not configured as tools. By default, actions are displayed on the right side of
+ *  the toolbar.
+ * @cfg {boolean} [shadow] Add a shadow below the toolbar.
+ */
+OO.ui.Toolbar = function OoUiToolbar( toolFactory, toolGroupFactory, config ) {
+       // Allow passing positional parameters inside the config object
+       if ( OO.isPlainObject( toolFactory ) && config === undefined ) {
+               config = toolFactory;
+               toolFactory = config.toolFactory;
+               toolGroupFactory = config.toolGroupFactory;
+       }
+
+       // Configuration initialization
+       config = config || {};
+
+       // Parent constructor
+       OO.ui.Toolbar.parent.call( this, config );
+
+       // Mixin constructors
+       OO.EventEmitter.call( this );
+       OO.ui.mixin.GroupElement.call( this, config );
+
+       // Properties
+       this.toolFactory = toolFactory;
+       this.toolGroupFactory = toolGroupFactory;
+       this.groups = [];
+       this.tools = {};
+       this.$bar = $( '<div>' );
+       this.$actions = $( '<div>' );
+       this.initialized = false;
+       this.onWindowResizeHandler = this.onWindowResize.bind( this );
+
+       // Events
+       this.$element
+               .add( this.$bar ).add( this.$group ).add( this.$actions )
+               .on( 'mousedown keydown', this.onPointerDown.bind( this ) );
+
+       // Initialization
+       this.$group.addClass( 'oo-ui-toolbar-tools' );
+       if ( config.actions ) {
+               this.$bar.append( this.$actions.addClass( 'oo-ui-toolbar-actions' ) );
+       }
+       this.$bar
+               .addClass( 'oo-ui-toolbar-bar' )
+               .append( this.$group, '<div style="clear:both"></div>' );
+       if ( config.shadow ) {
+               this.$bar.append( '<div class="oo-ui-toolbar-shadow"></div>' );
+       }
+       this.$element.addClass( 'oo-ui-toolbar' ).append( this.$bar );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.Toolbar, OO.ui.Element );
+OO.mixinClass( OO.ui.Toolbar, OO.EventEmitter );
+OO.mixinClass( OO.ui.Toolbar, OO.ui.mixin.GroupElement );
+
+/* Events */
+
+/**
+ * @event updateState
+ *
+ * An 'updateState' event must be emitted on the Toolbar (by calling `toolbar.emit( 'updateState' )`)
+ * every time the state of the application using the toolbar changes, and an update to the state of
+ * tools is required.
+ *
+ * @param {Mixed...} data Application-defined parameters
+ */
+
+/* Methods */
+
+/**
+ * Get the tool factory.
+ *
+ * @return {OO.ui.ToolFactory} Tool factory
+ */
+OO.ui.Toolbar.prototype.getToolFactory = function () {
+       return this.toolFactory;
+};
+
+/**
+ * Get the toolgroup factory.
+ *
+ * @return {OO.Factory} Toolgroup factory
+ */
+OO.ui.Toolbar.prototype.getToolGroupFactory = function () {
+       return this.toolGroupFactory;
+};
+
+/**
+ * Handles mouse down events.
+ *
+ * @private
+ * @param {jQuery.Event} e Mouse down event
+ */
+OO.ui.Toolbar.prototype.onPointerDown = function ( e ) {
+       var $closestWidgetToEvent = $( e.target ).closest( '.oo-ui-widget' ),
+               $closestWidgetToToolbar = this.$element.closest( '.oo-ui-widget' );
+       if ( !$closestWidgetToEvent.length || $closestWidgetToEvent[ 0 ] === $closestWidgetToToolbar[ 0 ] ) {
+               return false;
+       }
+};
+
+/**
+ * Handle window resize event.
+ *
+ * @private
+ * @param {jQuery.Event} e Window resize event
+ */
+OO.ui.Toolbar.prototype.onWindowResize = function () {
+       this.$element.toggleClass(
+               'oo-ui-toolbar-narrow',
+               this.$bar.width() <= this.narrowThreshold
+       );
+};
+
+/**
+ * Sets up handles and preloads required information for the toolbar to work.
+ * This must be called after it is attached to a visible document and before doing anything else.
+ */
+OO.ui.Toolbar.prototype.initialize = function () {
+       if ( !this.initialized ) {
+               this.initialized = true;
+               this.narrowThreshold = this.$group.width() + this.$actions.width();
+               $( this.getElementWindow() ).on( 'resize', this.onWindowResizeHandler );
+               this.onWindowResize();
+       }
+};
+
+/**
+ * Set up the toolbar.
+ *
+ * The toolbar is set up with a list of toolgroup configurations that specify the type of
+ * toolgroup ({@link OO.ui.BarToolGroup bar}, {@link OO.ui.MenuToolGroup menu}, or {@link OO.ui.ListToolGroup list})
+ * to add and which tools to include, exclude, promote, or demote within that toolgroup. Please
+ * see {@link OO.ui.ToolGroup toolgroups} for more information about including tools in toolgroups.
+ *
+ * @param {Object.<string,Array>} groups List of toolgroup configurations
+ * @param {Array|string} [groups.include] Tools to include in the toolgroup
+ * @param {Array|string} [groups.exclude] Tools to exclude from the toolgroup
+ * @param {Array|string} [groups.promote] Tools to promote to the beginning of the toolgroup
+ * @param {Array|string} [groups.demote] Tools to demote to the end of the toolgroup
+ */
+OO.ui.Toolbar.prototype.setup = function ( groups ) {
+       var i, len, type, group,
+               items = [],
+               defaultType = 'bar';
+
+       // Cleanup previous groups
+       this.reset();
+
+       // Build out new groups
+       for ( i = 0, len = groups.length; i < len; i++ ) {
+               group = groups[ i ];
+               if ( group.include === '*' ) {
+                       // Apply defaults to catch-all groups
+                       if ( group.type === undefined ) {
+                               group.type = 'list';
+                       }
+                       if ( group.label === undefined ) {
+                               group.label = OO.ui.msg( 'ooui-toolbar-more' );
+                       }
+               }
+               // Check type has been registered
+               type = this.getToolGroupFactory().lookup( group.type ) ? group.type : defaultType;
+               items.push(
+                       this.getToolGroupFactory().create( type, this, group )
+               );
+       }
+       this.addItems( items );
+};
+
+/**
+ * Remove all tools and toolgroups from the toolbar.
+ */
+OO.ui.Toolbar.prototype.reset = function () {
+       var i, len;
+
+       this.groups = [];
+       this.tools = {};
+       for ( i = 0, len = this.items.length; i < len; i++ ) {
+               this.items[ i ].destroy();
+       }
+       this.clearItems();
+};
+
+/**
+ * Destroy the toolbar.
+ *
+ * Destroying the toolbar removes all event handlers and DOM elements that constitute the toolbar. Call
+ * this method whenever you are done using a toolbar.
+ */
+OO.ui.Toolbar.prototype.destroy = function () {
+       $( this.getElementWindow() ).off( 'resize', this.onWindowResizeHandler );
+       this.reset();
+       this.$element.remove();
+};
+
+/**
+ * Check if the tool is available.
+ *
+ * Available tools are ones that have not yet been added to the toolbar.
+ *
+ * @param {string} name Symbolic name of tool
+ * @return {boolean} Tool is available
+ */
+OO.ui.Toolbar.prototype.isToolAvailable = function ( name ) {
+       return !this.tools[ name ];
+};
+
+/**
+ * Prevent tool from being used again.
+ *
+ * @param {OO.ui.Tool} tool Tool to reserve
+ */
+OO.ui.Toolbar.prototype.reserveTool = function ( tool ) {
+       this.tools[ tool.getName() ] = tool;
+};
+
+/**
+ * Allow tool to be used again.
+ *
+ * @param {OO.ui.Tool} tool Tool to release
+ */
+OO.ui.Toolbar.prototype.releaseTool = function ( tool ) {
+       delete this.tools[ tool.getName() ];
+};
+
+/**
+ * Get accelerator label for tool.
+ *
+ * The OOjs UI library does not contain an accelerator system, but this is the hook for one. To
+ * use an accelerator system, subclass the toolbar and override this method, which is meant to return a label
+ * that describes the accelerator keys for the tool passed (by symbolic name) to the method.
+ *
+ * @param {string} name Symbolic name of tool
+ * @return {string|undefined} Tool accelerator label if available
+ */
+OO.ui.Toolbar.prototype.getToolAccelerator = function () {
+       return undefined;
+};
+
+/**
+ * Tools, together with {@link OO.ui.ToolGroup toolgroups}, constitute {@link OO.ui.Toolbar toolbars}.
+ * Each tool is configured with a static name, title, and icon and is customized with the command to carry
+ * out when the tool is selected. Tools must also be registered with a {@link OO.ui.ToolFactory tool factory},
+ * which creates the tools on demand.
+ *
+ * Every Tool subclass must implement two methods:
+ *
+ * - {@link #onUpdateState}
+ * - {@link #onSelect}
+ *
+ * Tools are added to toolgroups ({@link OO.ui.ListToolGroup ListToolGroup},
+ * {@link OO.ui.BarToolGroup BarToolGroup}, or {@link OO.ui.MenuToolGroup MenuToolGroup}), which determine how
+ * the tool is displayed in the toolbar. See {@link OO.ui.Toolbar toolbars} for an example.
+ *
+ * For more information, please see the [OOjs UI documentation on MediaWiki][1].
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Toolbars
+ *
+ * @abstract
+ * @class
+ * @extends OO.ui.Widget
+ * @mixins OO.ui.mixin.IconElement
+ * @mixins OO.ui.mixin.FlaggedElement
+ * @mixins OO.ui.mixin.TabIndexedElement
+ *
+ * @constructor
+ * @param {OO.ui.ToolGroup} toolGroup
+ * @param {Object} [config] Configuration options
+ * @cfg {string|Function} [title] Title text or a function that returns text. If this config is omitted, the value of
+ *  the {@link #static-title static title} property is used.
+ *
+ *  The title is used in different ways depending on the type of toolgroup that contains the tool. The
+ *  title is used as a tooltip if the tool is part of a {@link OO.ui.BarToolGroup bar} toolgroup, or as the label text if the tool is
+ *  part of a {@link OO.ui.ListToolGroup list} or {@link OO.ui.MenuToolGroup menu} toolgroup.
+ *
+ *  For bar toolgroups, a description of the accelerator key is appended to the title if an accelerator key
+ *  is associated with an action by the same name as the tool and accelerator functionality has been added to the application.
+ *  To add accelerator key functionality, you must subclass OO.ui.Toolbar and override the {@link OO.ui.Toolbar#getToolAccelerator getToolAccelerator} method.
+ */
+OO.ui.Tool = function OoUiTool( toolGroup, config ) {
+       // Allow passing positional parameters inside the config object
+       if ( OO.isPlainObject( toolGroup ) && config === undefined ) {
+               config = toolGroup;
+               toolGroup = config.toolGroup;
+       }
+
+       // Configuration initialization
+       config = config || {};
+
+       // Parent constructor
+       OO.ui.Tool.parent.call( this, config );
+
+       // Properties
+       this.toolGroup = toolGroup;
+       this.toolbar = this.toolGroup.getToolbar();
+       this.active = false;
+       this.$title = $( '<span>' );
+       this.$accel = $( '<span>' );
+       this.$link = $( '<a>' );
+       this.title = null;
+
+       // Mixin constructors
+       OO.ui.mixin.IconElement.call( this, config );
+       OO.ui.mixin.FlaggedElement.call( this, config );
+       OO.ui.mixin.TabIndexedElement.call( this, $.extend( {}, config, { $tabIndexed: this.$link } ) );
+
+       // Events
+       this.toolbar.connect( this, { updateState: 'onUpdateState' } );
+
+       // Initialization
+       this.$title.addClass( 'oo-ui-tool-title' );
+       this.$accel
+               .addClass( 'oo-ui-tool-accel' )
+               .prop( {
+                       // This may need to be changed if the key names are ever localized,
+                       // but for now they are essentially written in English
+                       dir: 'ltr',
+                       lang: 'en'
+               } );
+       this.$link
+               .addClass( 'oo-ui-tool-link' )
+               .append( this.$icon, this.$title, this.$accel )
+               .attr( 'role', 'button' );
+       this.$element
+               .data( 'oo-ui-tool', this )
+               .addClass(
+                       'oo-ui-tool ' + 'oo-ui-tool-name-' +
+                       this.constructor.static.name.replace( /^([^\/]+)\/([^\/]+).*$/, '$1-$2' )
+               )
+               .toggleClass( 'oo-ui-tool-with-label', this.constructor.static.displayBothIconAndLabel )
+               .append( this.$link );
+       this.setTitle( config.title || this.constructor.static.title );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.Tool, OO.ui.Widget );
+OO.mixinClass( OO.ui.Tool, OO.ui.mixin.IconElement );
+OO.mixinClass( OO.ui.Tool, OO.ui.mixin.FlaggedElement );
+OO.mixinClass( OO.ui.Tool, OO.ui.mixin.TabIndexedElement );
+
+/* Static Properties */
+
+/**
+ * @static
+ * @inheritdoc
+ */
+OO.ui.Tool.static.tagName = 'span';
+
+/**
+ * Symbolic name of tool.
+ *
+ * The symbolic name is used internally to register the tool with a {@link OO.ui.ToolFactory ToolFactory}. It can
+ * also be used when adding tools to toolgroups.
+ *
+ * @abstract
+ * @static
+ * @inheritable
+ * @property {string}
+ */
+OO.ui.Tool.static.name = '';
+
+/**
+ * Symbolic name of the group.
+ *
+ * The group name is used to associate tools with each other so that they can be selected later by
+ * a {@link OO.ui.ToolGroup toolgroup}.
+ *
+ * @abstract
+ * @static
+ * @inheritable
+ * @property {string}
+ */
+OO.ui.Tool.static.group = '';
+
+/**
+ * Tool title text or a function that returns title text. The value of the static property is overridden if the #title config option is used.
+ *
+ * @abstract
+ * @static
+ * @inheritable
+ * @property {string|Function}
+ */
+OO.ui.Tool.static.title = '';
+
+/**
+ * Display both icon and label when the tool is used in a {@link OO.ui.BarToolGroup bar} toolgroup.
+ * Normally only the icon is displayed, or only the label if no icon is given.
+ *
+ * @static
+ * @inheritable
+ * @property {boolean}
+ */
+OO.ui.Tool.static.displayBothIconAndLabel = false;
+
+/**
+ * Add tool to catch-all groups automatically.
+ *
+ * A catch-all group, which contains all tools that do not currently belong to a toolgroup,
+ * can be included in a toolgroup using the wildcard selector, an asterisk (*).
+ *
+ * @static
+ * @inheritable
+ * @property {boolean}
+ */
+OO.ui.Tool.static.autoAddToCatchall = true;
+
+/**
+ * Add tool to named groups automatically.
+ *
+ * By default, tools that are configured with a static ‘group’ property are added
+ * to that group and will be selected when the symbolic name of the group is specified (e.g., when
+ * toolgroups include tools by group name).
+ *
+ * @static
+ * @property {boolean}
+ * @inheritable
+ */
+OO.ui.Tool.static.autoAddToGroup = true;
+
+/**
+ * Check if this tool is compatible with given data.
+ *
+ * This is a stub that can be overridden to provide support for filtering tools based on an
+ * arbitrary piece of information  (e.g., where the cursor is in a document). The implementation
+ * must also call this method so that the compatibility check can be performed.
+ *
+ * @static
+ * @inheritable
+ * @param {Mixed} data Data to check
+ * @return {boolean} Tool can be used with data
+ */
+OO.ui.Tool.static.isCompatibleWith = function () {
+       return false;
+};
+
+/* Methods */
+
+/**
+ * Handle the toolbar state being updated. This method is called when the
+ * {@link OO.ui.Toolbar#event-updateState 'updateState' event} is emitted on the
+ * {@link OO.ui.Toolbar Toolbar} that uses this tool, and should set the state of this tool
+ * depending on application state (usually by calling #setDisabled to enable or disable the tool,
+ * or #setActive to mark is as currently in-use or not).
+ *
+ * This is an abstract method that must be overridden in a concrete subclass.
+ *
+ * @method
+ * @protected
+ * @abstract
+ */
+OO.ui.Tool.prototype.onUpdateState = null;
+
+/**
+ * Handle the tool being selected. This method is called when the user triggers this tool,
+ * usually by clicking on its label/icon.
+ *
+ * This is an abstract method that must be overridden in a concrete subclass.
+ *
+ * @method
+ * @protected
+ * @abstract
+ */
+OO.ui.Tool.prototype.onSelect = null;
+
+/**
+ * Check if the tool is active.
+ *
+ * Tools become active when their #onSelect or #onUpdateState handlers change them to appear pressed
+ * with the #setActive method. Additional CSS is applied to the tool to reflect the active state.
+ *
+ * @return {boolean} Tool is active
+ */
+OO.ui.Tool.prototype.isActive = function () {
+       return this.active;
+};
+
+/**
+ * Make the tool appear active or inactive.
+ *
+ * This method should be called within #onSelect or #onUpdateState event handlers to make the tool
+ * appear pressed or not.
+ *
+ * @param {boolean} state Make tool appear active
+ */
+OO.ui.Tool.prototype.setActive = function ( state ) {
+       this.active = !!state;
+       if ( this.active ) {
+               this.$element.addClass( 'oo-ui-tool-active' );
+       } else {
+               this.$element.removeClass( 'oo-ui-tool-active' );
+       }
+};
+
+/**
+ * Set the tool #title.
+ *
+ * @param {string|Function} title Title text or a function that returns text
+ * @chainable
+ */
+OO.ui.Tool.prototype.setTitle = function ( title ) {
+       this.title = OO.ui.resolveMsg( title );
+       this.updateTitle();
+       return this;
+};
+
+/**
+ * Get the tool #title.
+ *
+ * @return {string} Title text
+ */
+OO.ui.Tool.prototype.getTitle = function () {
+       return this.title;
+};
+
+/**
+ * Get the tool's symbolic name.
+ *
+ * @return {string} Symbolic name of tool
+ */
+OO.ui.Tool.prototype.getName = function () {
+       return this.constructor.static.name;
+};
+
+/**
+ * Update the title.
+ */
+OO.ui.Tool.prototype.updateTitle = function () {
+       var titleTooltips = this.toolGroup.constructor.static.titleTooltips,
+               accelTooltips = this.toolGroup.constructor.static.accelTooltips,
+               accel = this.toolbar.getToolAccelerator( this.constructor.static.name ),
+               tooltipParts = [];
+
+       this.$title.text( this.title );
+       this.$accel.text( accel );
+
+       if ( titleTooltips && typeof this.title === 'string' && this.title.length ) {
+               tooltipParts.push( this.title );
+       }
+       if ( accelTooltips && typeof accel === 'string' && accel.length ) {
+               tooltipParts.push( accel );
+       }
+       if ( tooltipParts.length ) {
+               this.$link.attr( 'title', tooltipParts.join( ' ' ) );
+       } else {
+               this.$link.removeAttr( 'title' );
+       }
+};
+
+/**
+ * Destroy tool.
+ *
+ * Destroying the tool removes all event handlers and the tool’s DOM elements.
+ * Call this method whenever you are done using a tool.
+ */
+OO.ui.Tool.prototype.destroy = function () {
+       this.toolbar.disconnect( this );
+       this.$element.remove();
+};
+
+/**
+ * ToolGroups are collections of {@link OO.ui.Tool tools} that are used in a {@link OO.ui.Toolbar toolbar}.
+ * The type of toolgroup ({@link OO.ui.ListToolGroup list}, {@link OO.ui.BarToolGroup bar}, or {@link OO.ui.MenuToolGroup menu})
+ * to which a tool belongs determines how the tool is arranged and displayed in the toolbar. Toolgroups
+ * themselves are created on demand with a {@link OO.ui.ToolGroupFactory toolgroup factory}.
+ *
+ * Toolgroups can contain individual tools, groups of tools, or all available tools, as specified
+ * using the `include` config option. See OO.ui.ToolFactory#extract on documentation of the format.
+ * The options `exclude`, `promote`, and `demote` support the same formats.
+ *
+ * See {@link OO.ui.Toolbar toolbars} for a full example. For more information about toolbars in general,
+ * please see the [OOjs UI documentation on MediaWiki][1].
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Toolbars
+ *
+ * @abstract
+ * @class
+ * @extends OO.ui.Widget
+ * @mixins OO.ui.mixin.GroupElement
+ *
+ * @constructor
+ * @param {OO.ui.Toolbar} toolbar
+ * @param {Object} [config] Configuration options
+ * @cfg {Array|string} [include] List of tools to include in the toolgroup, see above.
+ * @cfg {Array|string} [exclude] List of tools to exclude from the toolgroup, see above.
+ * @cfg {Array|string} [promote] List of tools to promote to the beginning of the toolgroup, see above.
+ * @cfg {Array|string} [demote] List of tools to demote to the end of the toolgroup, see above.
+ *  This setting is particularly useful when tools have been added to the toolgroup
+ *  en masse (e.g., via the catch-all selector).
+ */
+OO.ui.ToolGroup = function OoUiToolGroup( toolbar, config ) {
+       // Allow passing positional parameters inside the config object
+       if ( OO.isPlainObject( toolbar ) && config === undefined ) {
+               config = toolbar;
+               toolbar = config.toolbar;
+       }
+
+       // Configuration initialization
+       config = config || {};
+
+       // Parent constructor
+       OO.ui.ToolGroup.parent.call( this, config );
+
+       // Mixin constructors
+       OO.ui.mixin.GroupElement.call( this, config );
+
+       // Properties
+       this.toolbar = toolbar;
+       this.tools = {};
+       this.pressed = null;
+       this.autoDisabled = false;
+       this.include = config.include || [];
+       this.exclude = config.exclude || [];
+       this.promote = config.promote || [];
+       this.demote = config.demote || [];
+       this.onCapturedMouseKeyUpHandler = this.onCapturedMouseKeyUp.bind( this );
+
+       // Events
+       this.$element.on( {
+               mousedown: this.onMouseKeyDown.bind( this ),
+               mouseup: this.onMouseKeyUp.bind( this ),
+               keydown: this.onMouseKeyDown.bind( this ),
+               keyup: this.onMouseKeyUp.bind( this ),
+               focus: this.onMouseOverFocus.bind( this ),
+               blur: this.onMouseOutBlur.bind( this ),
+               mouseover: this.onMouseOverFocus.bind( this ),
+               mouseout: this.onMouseOutBlur.bind( this )
+       } );
+       this.toolbar.getToolFactory().connect( this, { register: 'onToolFactoryRegister' } );
+       this.aggregate( { disable: 'itemDisable' } );
+       this.connect( this, { itemDisable: 'updateDisabled' } );
+
+       // Initialization
+       this.$group.addClass( 'oo-ui-toolGroup-tools' );
+       this.$element
+               .addClass( 'oo-ui-toolGroup' )
+               .append( this.$group );
+       this.populate();
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.ToolGroup, OO.ui.Widget );
+OO.mixinClass( OO.ui.ToolGroup, OO.ui.mixin.GroupElement );
+
+/* Events */
+
+/**
+ * @event update
+ */
+
+/* Static Properties */
+
+/**
+ * Show labels in tooltips.
+ *
+ * @static
+ * @inheritable
+ * @property {boolean}
+ */
+OO.ui.ToolGroup.static.titleTooltips = false;
+
+/**
+ * Show acceleration labels in tooltips.
+ *
+ * Note: The OOjs UI library does not include an accelerator system, but does contain
+ * a hook for one. To use an accelerator system, subclass the {@link OO.ui.Toolbar toolbar} and
+ * override the {@link OO.ui.Toolbar#getToolAccelerator getToolAccelerator} method, which is
+ * meant to return a label that describes the accelerator keys for a given tool (e.g., 'Ctrl + M').
+ *
+ * @static
+ * @inheritable
+ * @property {boolean}
+ */
+OO.ui.ToolGroup.static.accelTooltips = false;
+
+/**
+ * Automatically disable the toolgroup when all tools are disabled
+ *
+ * @static
+ * @inheritable
+ * @property {boolean}
+ */
+OO.ui.ToolGroup.static.autoDisable = true;
+
+/* Methods */
+
+/**
+ * @inheritdoc
+ */
+OO.ui.ToolGroup.prototype.isDisabled = function () {
+       return this.autoDisabled || OO.ui.ToolGroup.parent.prototype.isDisabled.apply( this, arguments );
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.ToolGroup.prototype.updateDisabled = function () {
+       var i, item, allDisabled = true;
+
+       if ( this.constructor.static.autoDisable ) {
+               for ( i = this.items.length - 1; i >= 0; i-- ) {
+                       item = this.items[ i ];
+                       if ( !item.isDisabled() ) {
+                               allDisabled = false;
+                               break;
+                       }
+               }
+               this.autoDisabled = allDisabled;
+       }
+       OO.ui.ToolGroup.parent.prototype.updateDisabled.apply( this, arguments );
+};
+
+/**
+ * Handle mouse down and key down events.
+ *
+ * @protected
+ * @param {jQuery.Event} e Mouse down or key down event
+ */
+OO.ui.ToolGroup.prototype.onMouseKeyDown = function ( e ) {
+       if (
+               !this.isDisabled() &&
+               ( e.which === OO.ui.MouseButtons.LEFT || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
+       ) {
+               this.pressed = this.getTargetTool( e );
+               if ( this.pressed ) {
+                       this.pressed.setActive( true );
+                       this.getElementDocument().addEventListener( 'mouseup', this.onCapturedMouseKeyUpHandler, true );
+                       this.getElementDocument().addEventListener( 'keyup', this.onCapturedMouseKeyUpHandler, true );
+               }
+               return false;
+       }
+};
+
+/**
+ * Handle captured mouse up and key up events.
+ *
+ * @protected
+ * @param {MouseEvent|KeyboardEvent} e Mouse up or key up event
+ */
+OO.ui.ToolGroup.prototype.onCapturedMouseKeyUp = function ( e ) {
+       this.getElementDocument().removeEventListener( 'mouseup', this.onCapturedMouseKeyUpHandler, true );
+       this.getElementDocument().removeEventListener( 'keyup', this.onCapturedMouseKeyUpHandler, true );
+       // onMouseKeyUp may be called a second time, depending on where the mouse is when the button is
+       // released, but since `this.pressed` will no longer be true, the second call will be ignored.
+       this.onMouseKeyUp( e );
+};
+
+/**
+ * Handle mouse up and key up events.
+ *
+ * @protected
+ * @param {MouseEvent|KeyboardEvent} e Mouse up or key up event
+ */
+OO.ui.ToolGroup.prototype.onMouseKeyUp = function ( e ) {
+       var tool = this.getTargetTool( e );
+
+       if (
+               !this.isDisabled() && this.pressed && this.pressed === tool &&
+               ( e.which === OO.ui.MouseButtons.LEFT || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
+       ) {
+               this.pressed.onSelect();
+               this.pressed = null;
+               e.preventDefault();
+               e.stopPropagation();
+       }
+
+       this.pressed = null;
+};
+
+/**
+ * Handle mouse over and focus events.
+ *
+ * @protected
+ * @param {jQuery.Event} e Mouse over or focus event
+ */
+OO.ui.ToolGroup.prototype.onMouseOverFocus = function ( e ) {
+       var tool = this.getTargetTool( e );
+
+       if ( this.pressed && this.pressed === tool ) {
+               this.pressed.setActive( true );
+       }
+};
+
+/**
+ * Handle mouse out and blur events.
+ *
+ * @protected
+ * @param {jQuery.Event} e Mouse out or blur event
+ */
+OO.ui.ToolGroup.prototype.onMouseOutBlur = function ( e ) {
+       var tool = this.getTargetTool( e );
+
+       if ( this.pressed && this.pressed === tool ) {
+               this.pressed.setActive( false );
+       }
+};
+
+/**
+ * Get the closest tool to a jQuery.Event.
+ *
+ * Only tool links are considered, which prevents other elements in the tool such as popups from
+ * triggering tool group interactions.
+ *
+ * @private
+ * @param {jQuery.Event} e
+ * @return {OO.ui.Tool|null} Tool, `null` if none was found
+ */
+OO.ui.ToolGroup.prototype.getTargetTool = function ( e ) {
+       var tool,
+               $item = $( e.target ).closest( '.oo-ui-tool-link' );
+
+       if ( $item.length ) {
+               tool = $item.parent().data( 'oo-ui-tool' );
+       }
+
+       return tool && !tool.isDisabled() ? tool : null;
+};
+
+/**
+ * Handle tool registry register events.
+ *
+ * If a tool is registered after the group is created, we must repopulate the list to account for:
+ *
+ * - a tool being added that may be included
+ * - a tool already included being overridden
+ *
+ * @protected
+ * @param {string} name Symbolic name of tool
+ */
+OO.ui.ToolGroup.prototype.onToolFactoryRegister = function () {
+       this.populate();
+};
+
+/**
+ * Get the toolbar that contains the toolgroup.
+ *
+ * @return {OO.ui.Toolbar} Toolbar that contains the toolgroup
+ */
+OO.ui.ToolGroup.prototype.getToolbar = function () {
+       return this.toolbar;
+};
+
+/**
+ * Add and remove tools based on configuration.
+ */
+OO.ui.ToolGroup.prototype.populate = function () {
+       var i, len, name, tool,
+               toolFactory = this.toolbar.getToolFactory(),
+               names = {},
+               add = [],
+               remove = [],
+               list = this.toolbar.getToolFactory().getTools(
+                       this.include, this.exclude, this.promote, this.demote
+               );
+
+       // Build a list of needed tools
+       for ( i = 0, len = list.length; i < len; i++ ) {
+               name = list[ i ];
+               if (
+                       // Tool exists
+                       toolFactory.lookup( name ) &&
+                       // Tool is available or is already in this group
+                       ( this.toolbar.isToolAvailable( name ) || this.tools[ name ] )
+               ) {
+                       // Hack to prevent infinite recursion via ToolGroupTool. We need to reserve the tool before
+                       // creating it, but we can't call reserveTool() yet because we haven't created the tool.
+                       this.toolbar.tools[ name ] = true;
+                       tool = this.tools[ name ];
+                       if ( !tool ) {
+                               // Auto-initialize tools on first use
+                               this.tools[ name ] = tool = toolFactory.create( name, this );
+                               tool.updateTitle();
+                       }
+                       this.toolbar.reserveTool( tool );
+                       add.push( tool );
+                       names[ name ] = true;
+               }
+       }
+       // Remove tools that are no longer needed
+       for ( name in this.tools ) {
+               if ( !names[ name ] ) {
+                       this.tools[ name ].destroy();
+                       this.toolbar.releaseTool( this.tools[ name ] );
+                       remove.push( this.tools[ name ] );
+                       delete this.tools[ name ];
+               }
+       }
+       if ( remove.length ) {
+               this.removeItems( remove );
+       }
+       // Update emptiness state
+       if ( add.length ) {
+               this.$element.removeClass( 'oo-ui-toolGroup-empty' );
+       } else {
+               this.$element.addClass( 'oo-ui-toolGroup-empty' );
+       }
+       // Re-add tools (moving existing ones to new locations)
+       this.addItems( add );
+       // Disabled state may depend on items
+       this.updateDisabled();
+};
+
+/**
+ * Destroy toolgroup.
+ */
+OO.ui.ToolGroup.prototype.destroy = function () {
+       var name;
+
+       this.clearItems();
+       this.toolbar.getToolFactory().disconnect( this );
+       for ( name in this.tools ) {
+               this.toolbar.releaseTool( this.tools[ name ] );
+               this.tools[ name ].disconnect( this ).destroy();
+               delete this.tools[ name ];
+       }
+       this.$element.remove();
+};
+
+/**
+ * A ToolFactory creates tools on demand. All tools ({@link OO.ui.Tool Tools}, {@link OO.ui.PopupTool PopupTools},
+ * and {@link OO.ui.ToolGroupTool ToolGroupTools}) must be registered with a tool factory. Tools are
+ * registered by their symbolic name. See {@link OO.ui.Toolbar toolbars} for an example.
+ *
+ * For more information about toolbars in general, please see the [OOjs UI documentation on MediaWiki][1].
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Toolbars
+ *
+ * @class
+ * @extends OO.Factory
+ * @constructor
+ */
+OO.ui.ToolFactory = function OoUiToolFactory() {
+       // Parent constructor
+       OO.ui.ToolFactory.parent.call( this );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.ToolFactory, OO.Factory );
+
+/* Methods */
+
+/**
+ * Get tools from the factory
+ *
+ * @param {Array|string} [include] Included tools, see #extract for format
+ * @param {Array|string} [exclude] Excluded tools, see #extract for format
+ * @param {Array|string} [promote] Promoted tools, see #extract for format
+ * @param {Array|string} [demote] Demoted tools, see #extract for format
+ * @return {string[]} List of tools
+ */
+OO.ui.ToolFactory.prototype.getTools = function ( include, exclude, promote, demote ) {
+       var i, len, included, promoted, demoted,
+               auto = [],
+               used = {};
+
+       // Collect included and not excluded tools
+       included = OO.simpleArrayDifference( this.extract( include ), this.extract( exclude ) );
+
+       // Promotion
+       promoted = this.extract( promote, used );
+       demoted = this.extract( demote, used );
+
+       // Auto
+       for ( i = 0, len = included.length; i < len; i++ ) {
+               if ( !used[ included[ i ] ] ) {
+                       auto.push( included[ i ] );
+               }
+       }
+
+       return promoted.concat( auto ).concat( demoted );
+};
+
+/**
+ * Get a flat list of names from a list of names or groups.
+ *
+ * Normally, `collection` is an array of tool specifications. Tools can be specified in the
+ * following ways:
+ *
+ * - To include an individual tool, use the symbolic name: `{ name: 'tool-name' }` or `'tool-name'`.
+ * - To include all tools in a group, use the group name: `{ group: 'group-name' }`. (To assign the
+ *   tool to a group, use OO.ui.Tool.static.group.)
+ *
+ * Alternatively, to include all tools that are not yet assigned to any other toolgroup, use the
+ * catch-all selector `'*'`.
+ *
+ * If `used` is passed, tool names that appear as properties in this object will be considered
+ * already assigned, and will not be returned even if specified otherwise. The tool names extracted
+ * by this function call will be added as new properties in the object.
+ *
+ * @private
+ * @param {Array|string} collection List of tools, see above
+ * @param {Object} [used] Object containing information about used tools, see above
+ * @return {string[]} List of extracted tool names
+ */
+OO.ui.ToolFactory.prototype.extract = function ( collection, used ) {
+       var i, len, item, name, tool,
+               names = [];
+
+       if ( collection === '*' ) {
+               for ( name in this.registry ) {
+                       tool = this.registry[ name ];
+                       if (
+                               // Only add tools by group name when auto-add is enabled
+                               tool.static.autoAddToCatchall &&
+                               // Exclude already used tools
+                               ( !used || !used[ name ] )
+                       ) {
+                               names.push( name );
+                               if ( used ) {
+                                       used[ name ] = true;
+                               }
+                       }
+               }
+       } else if ( Array.isArray( collection ) ) {
+               for ( i = 0, len = collection.length; i < len; i++ ) {
+                       item = collection[ i ];
+                       // Allow plain strings as shorthand for named tools
+                       if ( typeof item === 'string' ) {
+                               item = { name: item };
+                       }
+                       if ( OO.isPlainObject( item ) ) {
+                               if ( item.group ) {
+                                       for ( name in this.registry ) {
+                                               tool = this.registry[ name ];
+                                               if (
+                                                       // Include tools with matching group
+                                                       tool.static.group === item.group &&
+                                                       // Only add tools by group name when auto-add is enabled
+                                                       tool.static.autoAddToGroup &&
+                                                       // Exclude already used tools
+                                                       ( !used || !used[ name ] )
+                                               ) {
+                                                       names.push( name );
+                                                       if ( used ) {
+                                                               used[ name ] = true;
+                                                       }
+                                               }
+                                       }
+                               // Include tools with matching name and exclude already used tools
+                               } else if ( item.name && ( !used || !used[ item.name ] ) ) {
+                                       names.push( item.name );
+                                       if ( used ) {
+                                               used[ item.name ] = true;
+                                       }
+                               }
+                       }
+               }
+       }
+       return names;
+};
+
+/**
+ * ToolGroupFactories create {@link OO.ui.ToolGroup toolgroups} on demand. The toolgroup classes must
+ * specify a symbolic name and be registered with the factory. The following classes are registered by
+ * default:
+ *
+ * - {@link OO.ui.BarToolGroup BarToolGroups} (‘bar’)
+ * - {@link OO.ui.MenuToolGroup MenuToolGroups} (‘menu’)
+ * - {@link OO.ui.ListToolGroup ListToolGroups} (‘list’)
+ *
+ * See {@link OO.ui.Toolbar toolbars} for an example.
+ *
+ * For more information about toolbars in general, please see the [OOjs UI documentation on MediaWiki][1].
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Toolbars
+ * @class
+ * @extends OO.Factory
+ * @constructor
+ */
+OO.ui.ToolGroupFactory = function OoUiToolGroupFactory() {
+       var i, l, defaultClasses;
+       // Parent constructor
+       OO.Factory.call( this );
+
+       defaultClasses = this.constructor.static.getDefaultClasses();
+
+       // Register default toolgroups
+       for ( i = 0, l = defaultClasses.length; i < l; i++ ) {
+               this.register( defaultClasses[ i ] );
+       }
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.ToolGroupFactory, OO.Factory );
+
+/* Static Methods */
+
+/**
+ * Get a default set of classes to be registered on construction.
+ *
+ * @return {Function[]} Default classes
+ */
+OO.ui.ToolGroupFactory.static.getDefaultClasses = function () {
+       return [
+               OO.ui.BarToolGroup,
+               OO.ui.ListToolGroup,
+               OO.ui.MenuToolGroup
+       ];
+};
+
+/**
+ * Popup tools open a popup window when they are selected from the {@link OO.ui.Toolbar toolbar}. Each popup tool is configured
+ * with a static name, title, and icon, as well with as any popup configurations. Unlike other tools, popup tools do not require that developers specify
+ * an #onSelect or #onUpdateState method, as these methods have been implemented already.
+ *
+ *     // Example of a popup tool. When selected, a popup tool displays
+ *     // a popup window.
+ *     function HelpTool( toolGroup, config ) {
+ *        OO.ui.PopupTool.call( this, toolGroup, $.extend( { popup: {
+ *            padded: true,
+ *            label: 'Help',
+ *            head: true
+ *        } }, config ) );
+ *        this.popup.$body.append( '<p>I am helpful!</p>' );
+ *     };
+ *     OO.inheritClass( HelpTool, OO.ui.PopupTool );
+ *     HelpTool.static.name = 'help';
+ *     HelpTool.static.icon = 'help';
+ *     HelpTool.static.title = 'Help';
+ *     toolFactory.register( HelpTool );
+ *
+ * For an example of a toolbar that contains a popup tool, see {@link OO.ui.Toolbar toolbars}. For more information about
+ * toolbars in genreral, please see the [OOjs UI documentation on MediaWiki][1].
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Toolbars
+ *
+ * @abstract
+ * @class
+ * @extends OO.ui.Tool
+ * @mixins OO.ui.mixin.PopupElement
+ *
+ * @constructor
+ * @param {OO.ui.ToolGroup} toolGroup
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.PopupTool = function OoUiPopupTool( toolGroup, config ) {
+       // Allow passing positional parameters inside the config object
+       if ( OO.isPlainObject( toolGroup ) && config === undefined ) {
+               config = toolGroup;
+               toolGroup = config.toolGroup;
+       }
+
+       // Parent constructor
+       OO.ui.PopupTool.parent.call( this, toolGroup, config );
+
+       // Mixin constructors
+       OO.ui.mixin.PopupElement.call( this, config );
+
+       // Initialization
+       this.$element
+               .addClass( 'oo-ui-popupTool' )
+               .append( this.popup.$element );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.PopupTool, OO.ui.Tool );
+OO.mixinClass( OO.ui.PopupTool, OO.ui.mixin.PopupElement );
+
+/* Methods */
+
+/**
+ * Handle the tool being selected.
+ *
+ * @inheritdoc
+ */
+OO.ui.PopupTool.prototype.onSelect = function () {
+       if ( !this.isDisabled() ) {
+               this.popup.toggle();
+       }
+       this.setActive( false );
+       return false;
+};
+
+/**
+ * Handle the toolbar state being updated.
+ *
+ * @inheritdoc
+ */
+OO.ui.PopupTool.prototype.onUpdateState = function () {
+       this.setActive( false );
+};
+
+/**
+ * A ToolGroupTool is a special sort of tool that can contain other {@link OO.ui.Tool tools}
+ * and {@link OO.ui.ToolGroup toolgroups}. The ToolGroupTool was specifically designed to be used
+ * inside a {@link OO.ui.BarToolGroup bar} toolgroup to provide access to additional tools from
+ * the bar item. Included tools will be displayed in a dropdown {@link OO.ui.ListToolGroup list}
+ * when the ToolGroupTool is selected.
+ *
+ *     // Example: ToolGroupTool with two nested tools, 'setting1' and 'setting2', defined elsewhere.
+ *
+ *     function SettingsTool() {
+ *         SettingsTool.parent.apply( this, arguments );
+ *     };
+ *     OO.inheritClass( SettingsTool, OO.ui.ToolGroupTool );
+ *     SettingsTool.static.name = 'settings';
+ *     SettingsTool.static.title = 'Change settings';
+ *     SettingsTool.static.groupConfig = {
+ *         icon: 'settings',
+ *         label: 'ToolGroupTool',
+ *         include: [  'setting1', 'setting2'  ]
+ *     };
+ *     toolFactory.register( SettingsTool );
+ *
+ * For more information, please see the [OOjs UI documentation on MediaWiki][1].
+ *
+ * Please note that this implementation is subject to change per [T74159] [2].
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Toolbars#ToolGroupTool
+ * [2]: https://phabricator.wikimedia.org/T74159
+ *
+ * @abstract
+ * @class
+ * @extends OO.ui.Tool
+ *
+ * @constructor
+ * @param {OO.ui.ToolGroup} toolGroup
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.ToolGroupTool = function OoUiToolGroupTool( toolGroup, config ) {
+       // Allow passing positional parameters inside the config object
+       if ( OO.isPlainObject( toolGroup ) && config === undefined ) {
+               config = toolGroup;
+               toolGroup = config.toolGroup;
+       }
+
+       // Parent constructor
+       OO.ui.ToolGroupTool.parent.call( this, toolGroup, config );
+
+       // Properties
+       this.innerToolGroup = this.createGroup( this.constructor.static.groupConfig );
+
+       // Events
+       this.innerToolGroup.connect( this, { disable: 'onToolGroupDisable' } );
+
+       // Initialization
+       this.$link.remove();
+       this.$element
+               .addClass( 'oo-ui-toolGroupTool' )
+               .append( this.innerToolGroup.$element );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.ToolGroupTool, OO.ui.Tool );
+
+/* Static Properties */
+
+/**
+ * Toolgroup configuration.
+ *
+ * The toolgroup configuration consists of the tools to include, as well as an icon and label
+ * to use for the bar item. Tools can be included by symbolic name, group, or with the
+ * wildcard selector. Please see {@link OO.ui.ToolGroup toolgroup} for more information.
+ *
+ * @property {Object.<string,Array>}
+ */
+OO.ui.ToolGroupTool.static.groupConfig = {};
+
+/* Methods */
+
+/**
+ * Handle the tool being selected.
+ *
+ * @inheritdoc
+ */
+OO.ui.ToolGroupTool.prototype.onSelect = function () {
+       this.innerToolGroup.setActive( !this.innerToolGroup.active );
+       return false;
+};
+
+/**
+ * Synchronize disabledness state of the tool with the inner toolgroup.
+ *
+ * @private
+ * @param {boolean} disabled Element is disabled
+ */
+OO.ui.ToolGroupTool.prototype.onToolGroupDisable = function ( disabled ) {
+       this.setDisabled( disabled );
+};
+
+/**
+ * Handle the toolbar state being updated.
+ *
+ * @inheritdoc
+ */
+OO.ui.ToolGroupTool.prototype.onUpdateState = function () {
+       this.setActive( false );
+};
+
+/**
+ * Build a {@link OO.ui.ToolGroup toolgroup} from the specified configuration.
+ *
+ * @param {Object.<string,Array>} group Toolgroup configuration. Please see {@link OO.ui.ToolGroup toolgroup} for
+ *  more information.
+ * @return {OO.ui.ListToolGroup}
+ */
+OO.ui.ToolGroupTool.prototype.createGroup = function ( group ) {
+       if ( group.include === '*' ) {
+               // Apply defaults to catch-all groups
+               if ( group.label === undefined ) {
+                       group.label = OO.ui.msg( 'ooui-toolbar-more' );
+               }
+       }
+
+       return this.toolbar.getToolGroupFactory().create( 'list', this.toolbar, group );
+};
+
+/**
+ * BarToolGroups are one of three types of {@link OO.ui.ToolGroup toolgroups} that are used to
+ * create {@link OO.ui.Toolbar toolbars} (the other types of groups are {@link OO.ui.MenuToolGroup MenuToolGroup}
+ * and {@link OO.ui.ListToolGroup ListToolGroup}). The {@link OO.ui.Tool tools} in a BarToolGroup are
+ * displayed by icon in a single row. The title of the tool is displayed when users move the mouse over
+ * the tool.
+ *
+ * BarToolGroups are created by a {@link OO.ui.ToolGroupFactory tool group factory} when the toolbar is
+ * set up.
+ *
+ *     @example
+ *     // Example of a BarToolGroup with two tools
+ *     var toolFactory = new OO.ui.ToolFactory();
+ *     var toolGroupFactory = new OO.ui.ToolGroupFactory();
+ *     var toolbar = new OO.ui.Toolbar( toolFactory, toolGroupFactory );
+ *
+ *     // We will be placing status text in this element when tools are used
+ *     var $area = $( '<p>' ).text( 'Example of a BarToolGroup with two tools.' );
+ *
+ *     // Define the tools that we're going to place in our toolbar
+ *
+ *     // Create a class inheriting from OO.ui.Tool
+ *     function SearchTool() {
+ *         SearchTool.parent.apply( this, arguments );
+ *     }
+ *     OO.inheritClass( SearchTool, OO.ui.Tool );
+ *     // Each tool must have a 'name' (used as an internal identifier, see later) and at least one
+ *     // of 'icon' and 'title' (displayed icon and text).
+ *     SearchTool.static.name = 'search';
+ *     SearchTool.static.icon = 'search';
+ *     SearchTool.static.title = 'Search...';
+ *     // Defines the action that will happen when this tool is selected (clicked).
+ *     SearchTool.prototype.onSelect = function () {
+ *         $area.text( 'Search tool clicked!' );
+ *         // Never display this tool as "active" (selected).
+ *         this.setActive( false );
+ *     };
+ *     SearchTool.prototype.onUpdateState = function () {};
+ *     // Make this tool available in our toolFactory and thus our toolbar
+ *     toolFactory.register( SearchTool );
+ *
+ *     // This is a PopupTool. Rather than having a custom 'onSelect' action, it will display a
+ *     // little popup window (a PopupWidget).
+ *     function HelpTool( toolGroup, config ) {
+ *         OO.ui.PopupTool.call( this, toolGroup, $.extend( { popup: {
+ *             padded: true,
+ *             label: 'Help',
+ *             head: true
+ *         } }, config ) );
+ *         this.popup.$body.append( '<p>I am helpful!</p>' );
+ *     }
+ *     OO.inheritClass( HelpTool, OO.ui.PopupTool );
+ *     HelpTool.static.name = 'help';
+ *     HelpTool.static.icon = 'help';
+ *     HelpTool.static.title = 'Help';
+ *     toolFactory.register( HelpTool );
+ *
+ *     // Finally define which tools and in what order appear in the toolbar. Each tool may only be
+ *     // used once (but not all defined tools must be used).
+ *     toolbar.setup( [
+ *         {
+ *             // 'bar' tool groups display tools by icon only
+ *             type: 'bar',
+ *             include: [ 'search', 'help' ]
+ *         }
+ *     ] );
+ *
+ *     // Create some UI around the toolbar and place it in the document
+ *     var frame = new OO.ui.PanelLayout( {
+ *         expanded: false,
+ *         framed: true
+ *     } );
+ *     var contentFrame = new OO.ui.PanelLayout( {
+ *         expanded: false,
+ *         padded: true
+ *     } );
+ *     frame.$element.append(
+ *         toolbar.$element,
+ *         contentFrame.$element.append( $area )
+ *     );
+ *     $( 'body' ).append( frame.$element );
+ *
+ *     // Here is where the toolbar is actually built. This must be done after inserting it into the
+ *     // document.
+ *     toolbar.initialize();
+ *
+ * For more information about how to add tools to a bar tool group, please see {@link OO.ui.ToolGroup toolgroup}.
+ * For more information about toolbars in general, please see the [OOjs UI documentation on MediaWiki][1].
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Toolbars
+ *
+ * @class
+ * @extends OO.ui.ToolGroup
+ *
+ * @constructor
+ * @param {OO.ui.Toolbar} toolbar
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.BarToolGroup = function OoUiBarToolGroup( toolbar, config ) {
+       // Allow passing positional parameters inside the config object
+       if ( OO.isPlainObject( toolbar ) && config === undefined ) {
+               config = toolbar;
+               toolbar = config.toolbar;
+       }
+
+       // Parent constructor
+       OO.ui.BarToolGroup.parent.call( this, toolbar, config );
+
+       // Initialization
+       this.$element.addClass( 'oo-ui-barToolGroup' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.BarToolGroup, OO.ui.ToolGroup );
+
+/* Static Properties */
+
+OO.ui.BarToolGroup.static.titleTooltips = true;
+
+OO.ui.BarToolGroup.static.accelTooltips = true;
+
+OO.ui.BarToolGroup.static.name = 'bar';
+
+/**
+ * PopupToolGroup is an abstract base class used by both {@link OO.ui.MenuToolGroup MenuToolGroup}
+ * and {@link OO.ui.ListToolGroup ListToolGroup} to provide a popup--an overlaid menu or list of tools with an
+ * optional icon and label. This class can be used for other base classes that also use this functionality.
+ *
+ * @abstract
+ * @class
+ * @extends OO.ui.ToolGroup
+ * @mixins OO.ui.mixin.IconElement
+ * @mixins OO.ui.mixin.IndicatorElement
+ * @mixins OO.ui.mixin.LabelElement
+ * @mixins OO.ui.mixin.TitledElement
+ * @mixins OO.ui.mixin.ClippableElement
+ * @mixins OO.ui.mixin.TabIndexedElement
+ *
+ * @constructor
+ * @param {OO.ui.Toolbar} toolbar
+ * @param {Object} [config] Configuration options
+ * @cfg {string} [header] Text to display at the top of the popup
+ */
+OO.ui.PopupToolGroup = function OoUiPopupToolGroup( toolbar, config ) {
+       // Allow passing positional parameters inside the config object
+       if ( OO.isPlainObject( toolbar ) && config === undefined ) {
+               config = toolbar;
+               toolbar = config.toolbar;
+       }
+
+       // Configuration initialization
+       config = config || {};
+
+       // Parent constructor
+       OO.ui.PopupToolGroup.parent.call( this, toolbar, config );
+
+       // Properties
+       this.active = false;
+       this.dragging = false;
+       this.onBlurHandler = this.onBlur.bind( this );
+       this.$handle = $( '<span>' );
+
+       // Mixin constructors
+       OO.ui.mixin.IconElement.call( this, config );
+       OO.ui.mixin.IndicatorElement.call( this, config );
+       OO.ui.mixin.LabelElement.call( this, config );
+       OO.ui.mixin.TitledElement.call( this, config );
+       OO.ui.mixin.ClippableElement.call( this, $.extend( {}, config, { $clippable: this.$group } ) );
+       OO.ui.mixin.TabIndexedElement.call( this, $.extend( {}, config, { $tabIndexed: this.$handle } ) );
+
+       // Events
+       this.$handle.on( {
+               keydown: this.onHandleMouseKeyDown.bind( this ),
+               keyup: this.onHandleMouseKeyUp.bind( this ),
+               mousedown: this.onHandleMouseKeyDown.bind( this ),
+               mouseup: this.onHandleMouseKeyUp.bind( this )
+       } );
+
+       // Initialization
+       this.$handle
+               .addClass( 'oo-ui-popupToolGroup-handle' )
+               .append( this.$icon, this.$label, this.$indicator );
+       // If the pop-up should have a header, add it to the top of the toolGroup.
+       // Note: If this feature is useful for other widgets, we could abstract it into an
+       // OO.ui.HeaderedElement mixin constructor.
+       if ( config.header !== undefined ) {
+               this.$group
+                       .prepend( $( '<span>' )
+                               .addClass( 'oo-ui-popupToolGroup-header' )
+                               .text( config.header )
+                       );
+       }
+       this.$element
+               .addClass( 'oo-ui-popupToolGroup' )
+               .prepend( this.$handle );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.PopupToolGroup, OO.ui.ToolGroup );
+OO.mixinClass( OO.ui.PopupToolGroup, OO.ui.mixin.IconElement );
+OO.mixinClass( OO.ui.PopupToolGroup, OO.ui.mixin.IndicatorElement );
+OO.mixinClass( OO.ui.PopupToolGroup, OO.ui.mixin.LabelElement );
+OO.mixinClass( OO.ui.PopupToolGroup, OO.ui.mixin.TitledElement );
+OO.mixinClass( OO.ui.PopupToolGroup, OO.ui.mixin.ClippableElement );
+OO.mixinClass( OO.ui.PopupToolGroup, OO.ui.mixin.TabIndexedElement );
+
+/* Methods */
+
+/**
+ * @inheritdoc
+ */
+OO.ui.PopupToolGroup.prototype.setDisabled = function () {
+       // Parent method
+       OO.ui.PopupToolGroup.parent.prototype.setDisabled.apply( this, arguments );
+
+       if ( this.isDisabled() && this.isElementAttached() ) {
+               this.setActive( false );
+       }
+};
+
+/**
+ * Handle focus being lost.
+ *
+ * The event is actually generated from a mouseup/keyup, so it is not a normal blur event object.
+ *
+ * @protected
+ * @param {MouseEvent|KeyboardEvent} e Mouse up or key up event
+ */
+OO.ui.PopupToolGroup.prototype.onBlur = function ( e ) {
+       // Only deactivate when clicking outside the dropdown element
+       if ( $( e.target ).closest( '.oo-ui-popupToolGroup' )[ 0 ] !== this.$element[ 0 ] ) {
+               this.setActive( false );
+       }
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.PopupToolGroup.prototype.onMouseKeyUp = function ( e ) {
+       // Only close toolgroup when a tool was actually selected
+       if (
+               !this.isDisabled() && this.pressed && this.pressed === this.getTargetTool( e ) &&
+               ( e.which === OO.ui.MouseButtons.LEFT || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
+       ) {
+               this.setActive( false );
+       }
+       return OO.ui.PopupToolGroup.parent.prototype.onMouseKeyUp.call( this, e );
+};
+
+/**
+ * Handle mouse up and key up events.
+ *
+ * @protected
+ * @param {jQuery.Event} e Mouse up or key up event
+ */
+OO.ui.PopupToolGroup.prototype.onHandleMouseKeyUp = function ( e ) {
+       if (
+               !this.isDisabled() &&
+               ( e.which === OO.ui.MouseButtons.LEFT || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
+       ) {
+               return false;
+       }
+};
+
+/**
+ * Handle mouse down and key down events.
+ *
+ * @protected
+ * @param {jQuery.Event} e Mouse down or key down event
+ */
+OO.ui.PopupToolGroup.prototype.onHandleMouseKeyDown = function ( e ) {
+       if (
+               !this.isDisabled() &&
+               ( e.which === OO.ui.MouseButtons.LEFT || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
+       ) {
+               this.setActive( !this.active );
+               return false;
+       }
+};
+
+/**
+ * Switch into 'active' mode.
+ *
+ * When active, the popup is visible. A mouseup event anywhere in the document will trigger
+ * deactivation.
+ */
+OO.ui.PopupToolGroup.prototype.setActive = function ( value ) {
+       var containerWidth, containerLeft;
+       value = !!value;
+       if ( this.active !== value ) {
+               this.active = value;
+               if ( value ) {
+                       this.getElementDocument().addEventListener( 'mouseup', this.onBlurHandler, true );
+                       this.getElementDocument().addEventListener( 'keyup', this.onBlurHandler, true );
+
+                       this.$clippable.css( 'left', '' );
+                       // Try anchoring the popup to the left first
+                       this.$element.addClass( 'oo-ui-popupToolGroup-active oo-ui-popupToolGroup-left' );
+                       this.toggleClipping( true );
+                       if ( this.isClippedHorizontally() ) {
+                               // Anchoring to the left caused the popup to clip, so anchor it to the right instead
+                               this.toggleClipping( false );
+                               this.$element
+                                       .removeClass( 'oo-ui-popupToolGroup-left' )
+                                       .addClass( 'oo-ui-popupToolGroup-right' );
+                               this.toggleClipping( true );
+                       }
+                       if ( this.isClippedHorizontally() ) {
+                               // Anchoring to the right also caused the popup to clip, so just make it fill the container
+                               containerWidth = this.$clippableScrollableContainer.width();
+                               containerLeft = this.$clippableScrollableContainer.offset().left;
+
+                               this.toggleClipping( false );
+                               this.$element.removeClass( 'oo-ui-popupToolGroup-right' );
+
+                               this.$clippable.css( {
+                                       left: -( this.$element.offset().left - containerLeft ),
+                                       width: containerWidth
+                               } );
+                       }
+               } else {
+                       this.getElementDocument().removeEventListener( 'mouseup', this.onBlurHandler, true );
+                       this.getElementDocument().removeEventListener( 'keyup', this.onBlurHandler, true );
+                       this.$element.removeClass(
+                               'oo-ui-popupToolGroup-active oo-ui-popupToolGroup-left  oo-ui-popupToolGroup-right'
+                       );
+                       this.toggleClipping( false );
+               }
+       }
+};
+
+/**
+ * ListToolGroups are one of three types of {@link OO.ui.ToolGroup toolgroups} that are used to
+ * create {@link OO.ui.Toolbar toolbars} (the other types of groups are {@link OO.ui.MenuToolGroup MenuToolGroup}
+ * and {@link OO.ui.BarToolGroup BarToolGroup}). The {@link OO.ui.Tool tools} in a ListToolGroup are displayed
+ * by label in a dropdown menu. The title of the tool is used as the label text. The menu itself can be configured
+ * with a label, icon, indicator, header, and title.
+ *
+ * ListToolGroups can be configured to be expanded and collapsed. Collapsed lists will have a ‘More’ option that
+ * users can select to see the full list of tools. If a collapsed toolgroup is expanded, a ‘Fewer’ option permits
+ * users to collapse the list again.
+ *
+ * ListToolGroups are created by a {@link OO.ui.ToolGroupFactory toolgroup factory} when the toolbar is set up. The factory
+ * requires the ListToolGroup's symbolic name, 'list', which is specified along with the other configurations. For more
+ * information about how to add tools to a ListToolGroup, please see {@link OO.ui.ToolGroup toolgroup}.
+ *
+ *     @example
+ *     // Example of a ListToolGroup
+ *     var toolFactory = new OO.ui.ToolFactory();
+ *     var toolGroupFactory = new OO.ui.ToolGroupFactory();
+ *     var toolbar = new OO.ui.Toolbar( toolFactory, toolGroupFactory );
+ *
+ *     // Configure and register two tools
+ *     function SettingsTool() {
+ *         SettingsTool.parent.apply( this, arguments );
+ *     }
+ *     OO.inheritClass( SettingsTool, OO.ui.Tool );
+ *     SettingsTool.static.name = 'settings';
+ *     SettingsTool.static.icon = 'settings';
+ *     SettingsTool.static.title = 'Change settings';
+ *     SettingsTool.prototype.onSelect = function () {
+ *         this.setActive( false );
+ *     };
+ *     SettingsTool.prototype.onUpdateState = function () {};
+ *     toolFactory.register( SettingsTool );
+ *     // Register two more tools, nothing interesting here
+ *     function StuffTool() {
+ *         StuffTool.parent.apply( this, arguments );
+ *     }
+ *     OO.inheritClass( StuffTool, OO.ui.Tool );
+ *     StuffTool.static.name = 'stuff';
+ *     StuffTool.static.icon = 'search';
+ *     StuffTool.static.title = 'Change the world';
+ *     StuffTool.prototype.onSelect = function () {
+ *         this.setActive( false );
+ *     };
+ *     StuffTool.prototype.onUpdateState = function () {};
+ *     toolFactory.register( StuffTool );
+ *     toolbar.setup( [
+ *         {
+ *             // Configurations for list toolgroup.
+ *             type: 'list',
+ *             label: 'ListToolGroup',
+ *             indicator: 'down',
+ *             icon: 'ellipsis',
+ *             title: 'This is the title, displayed when user moves the mouse over the list toolgroup',
+ *             header: 'This is the header',
+ *             include: [ 'settings', 'stuff' ],
+ *             allowCollapse: ['stuff']
+ *         }
+ *     ] );
+ *
+ *     // Create some UI around the toolbar and place it in the document
+ *     var frame = new OO.ui.PanelLayout( {
+ *         expanded: false,
+ *         framed: true
+ *     } );
+ *     frame.$element.append(
+ *         toolbar.$element
+ *     );
+ *     $( 'body' ).append( frame.$element );
+ *     // Build the toolbar. This must be done after the toolbar has been appended to the document.
+ *     toolbar.initialize();
+ *
+ * For more information about toolbars in general, please see the [OOjs UI documentation on MediaWiki][1].
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Toolbars
+ *
+ * @class
+ * @extends OO.ui.PopupToolGroup
+ *
+ * @constructor
+ * @param {OO.ui.Toolbar} toolbar
+ * @param {Object} [config] Configuration options
+ * @cfg {Array} [allowCollapse] Allow the specified tools to be collapsed. By default, collapsible tools
+ *  will only be displayed if users click the ‘More’ option displayed at the bottom of the list. If
+ *  the list is expanded, a ‘Fewer’ option permits users to collapse the list again. Any tools that
+ *  are included in the toolgroup, but are not designated as collapsible, will always be displayed.
+ *  To open a collapsible list in its expanded state, set #expanded to 'true'.
+ * @cfg {Array} [forceExpand] Expand the specified tools. All other tools will be designated as collapsible.
+ *  Unless #expanded is set to true, the collapsible tools will be collapsed when the list is first opened.
+ * @cfg {boolean} [expanded=false] Expand collapsible tools. This config is only relevant if tools have
+ *  been designated as collapsible. When expanded is set to true, all tools in the group will be displayed
+ *  when the list is first opened. Users can collapse the list with a ‘Fewer’ option at the bottom.
+ */
+OO.ui.ListToolGroup = function OoUiListToolGroup( toolbar, config ) {
+       // Allow passing positional parameters inside the config object
+       if ( OO.isPlainObject( toolbar ) && config === undefined ) {
+               config = toolbar;
+               toolbar = config.toolbar;
+       }
+
+       // Configuration initialization
+       config = config || {};
+
+       // Properties (must be set before parent constructor, which calls #populate)
+       this.allowCollapse = config.allowCollapse;
+       this.forceExpand = config.forceExpand;
+       this.expanded = config.expanded !== undefined ? config.expanded : false;
+       this.collapsibleTools = [];
+
+       // Parent constructor
+       OO.ui.ListToolGroup.parent.call( this, toolbar, config );
+
+       // Initialization
+       this.$element.addClass( 'oo-ui-listToolGroup' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.ListToolGroup, OO.ui.PopupToolGroup );
+
+/* Static Properties */
+
+OO.ui.ListToolGroup.static.name = 'list';
+
+/* Methods */
+
+/**
+ * @inheritdoc
+ */
+OO.ui.ListToolGroup.prototype.populate = function () {
+       var i, len, allowCollapse = [];
+
+       OO.ui.ListToolGroup.parent.prototype.populate.call( this );
+
+       // Update the list of collapsible tools
+       if ( this.allowCollapse !== undefined ) {
+               allowCollapse = this.allowCollapse;
+       } else if ( this.forceExpand !== undefined ) {
+               allowCollapse = OO.simpleArrayDifference( Object.keys( this.tools ), this.forceExpand );
+       }
+
+       this.collapsibleTools = [];
+       for ( i = 0, len = allowCollapse.length; i < len; i++ ) {
+               if ( this.tools[ allowCollapse[ i ] ] !== undefined ) {
+                       this.collapsibleTools.push( this.tools[ allowCollapse[ i ] ] );
+               }
+       }
+
+       // Keep at the end, even when tools are added
+       this.$group.append( this.getExpandCollapseTool().$element );
+
+       this.getExpandCollapseTool().toggle( this.collapsibleTools.length !== 0 );
+       this.updateCollapsibleState();
+};
+
+OO.ui.ListToolGroup.prototype.getExpandCollapseTool = function () {
+       var ExpandCollapseTool;
+       if ( this.expandCollapseTool === undefined ) {
+               ExpandCollapseTool = function () {
+                       ExpandCollapseTool.parent.apply( this, arguments );
+               };
+
+               OO.inheritClass( ExpandCollapseTool, OO.ui.Tool );
+
+               ExpandCollapseTool.prototype.onSelect = function () {
+                       this.toolGroup.expanded = !this.toolGroup.expanded;
+                       this.toolGroup.updateCollapsibleState();
+                       this.setActive( false );
+               };
+               ExpandCollapseTool.prototype.onUpdateState = function () {
+                       // Do nothing. Tool interface requires an implementation of this function.
+               };
+
+               ExpandCollapseTool.static.name = 'more-fewer';
+
+               this.expandCollapseTool = new ExpandCollapseTool( this );
+       }
+       return this.expandCollapseTool;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.ListToolGroup.prototype.onMouseKeyUp = function ( e ) {
+       // Do not close the popup when the user wants to show more/fewer tools
+       if (
+               $( e.target ).closest( '.oo-ui-tool-name-more-fewer' ).length &&
+               ( e.which === OO.ui.MouseButtons.LEFT || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
+       ) {
+               // HACK: Prevent the popup list from being hidden. Skip the PopupToolGroup implementation (which
+               // hides the popup list when a tool is selected) and call ToolGroup's implementation directly.
+               return OO.ui.ListToolGroup.parent.parent.prototype.onMouseKeyUp.call( this, e );
+       } else {
+               return OO.ui.ListToolGroup.parent.prototype.onMouseKeyUp.call( this, e );
+       }
+};
+
+OO.ui.ListToolGroup.prototype.updateCollapsibleState = function () {
+       var i, len;
+
+       this.getExpandCollapseTool()
+               .setIcon( this.expanded ? 'collapse' : 'expand' )
+               .setTitle( OO.ui.msg( this.expanded ? 'ooui-toolgroup-collapse' : 'ooui-toolgroup-expand' ) );
+
+       for ( i = 0, len = this.collapsibleTools.length; i < len; i++ ) {
+               this.collapsibleTools[ i ].toggle( this.expanded );
+       }
+};
+
+/**
+ * MenuToolGroups are one of three types of {@link OO.ui.ToolGroup toolgroups} that are used to
+ * create {@link OO.ui.Toolbar toolbars} (the other types of groups are {@link OO.ui.BarToolGroup BarToolGroup}
+ * and {@link OO.ui.ListToolGroup ListToolGroup}). MenuToolGroups contain selectable {@link OO.ui.Tool tools},
+ * which are displayed by label in a dropdown menu. The tool's title is used as the label text, and the
+ * menu label is updated to reflect which tool or tools are currently selected. If no tools are selected,
+ * the menu label is empty. The menu can be configured with an indicator, icon, title, and/or header.
+ *
+ * MenuToolGroups are created by a {@link OO.ui.ToolGroupFactory tool group factory} when the toolbar
+ * is set up.
+ *
+ *     @example
+ *     // Example of a MenuToolGroup
+ *     var toolFactory = new OO.ui.ToolFactory();
+ *     var toolGroupFactory = new OO.ui.ToolGroupFactory();
+ *     var toolbar = new OO.ui.Toolbar( toolFactory, toolGroupFactory );
+ *
+ *     // We will be placing status text in this element when tools are used
+ *     var $area = $( '<p>' ).text( 'An example of a MenuToolGroup. Select a tool from the dropdown menu.' );
+ *
+ *     // Define the tools that we're going to place in our toolbar
+ *
+ *     function SettingsTool() {
+ *         SettingsTool.parent.apply( this, arguments );
+ *         this.reallyActive = false;
+ *     }
+ *     OO.inheritClass( SettingsTool, OO.ui.Tool );
+ *     SettingsTool.static.name = 'settings';
+ *     SettingsTool.static.icon = 'settings';
+ *     SettingsTool.static.title = 'Change settings';
+ *     SettingsTool.prototype.onSelect = function () {
+ *         $area.text( 'Settings tool clicked!' );
+ *         // Toggle the active state on each click
+ *         this.reallyActive = !this.reallyActive;
+ *         this.setActive( this.reallyActive );
+ *         // To update the menu label
+ *         this.toolbar.emit( 'updateState' );
+ *     };
+ *     SettingsTool.prototype.onUpdateState = function () {};
+ *     toolFactory.register( SettingsTool );
+ *
+ *     function StuffTool() {
+ *         StuffTool.parent.apply( this, arguments );
+ *         this.reallyActive = false;
+ *     }
+ *     OO.inheritClass( StuffTool, OO.ui.Tool );
+ *     StuffTool.static.name = 'stuff';
+ *     StuffTool.static.icon = 'ellipsis';
+ *     StuffTool.static.title = 'More stuff';
+ *     StuffTool.prototype.onSelect = function () {
+ *         $area.text( 'More stuff tool clicked!' );
+ *         // Toggle the active state on each click
+ *         this.reallyActive = !this.reallyActive;
+ *         this.setActive( this.reallyActive );
+ *         // To update the menu label
+ *         this.toolbar.emit( 'updateState' );
+ *     };
+ *     StuffTool.prototype.onUpdateState = function () {};
+ *     toolFactory.register( StuffTool );
+ *
+ *     // Finally define which tools and in what order appear in the toolbar. Each tool may only be
+ *     // used once (but not all defined tools must be used).
+ *     toolbar.setup( [
+ *         {
+ *             type: 'menu',
+ *             header: 'This is the (optional) header',
+ *             title: 'This is the (optional) title',
+ *             indicator: 'down',
+ *             include: [ 'settings', 'stuff' ]
+ *         }
+ *     ] );
+ *
+ *     // Create some UI around the toolbar and place it in the document
+ *     var frame = new OO.ui.PanelLayout( {
+ *         expanded: false,
+ *         framed: true
+ *     } );
+ *     var contentFrame = new OO.ui.PanelLayout( {
+ *         expanded: false,
+ *         padded: true
+ *     } );
+ *     frame.$element.append(
+ *         toolbar.$element,
+ *         contentFrame.$element.append( $area )
+ *     );
+ *     $( 'body' ).append( frame.$element );
+ *
+ *     // Here is where the toolbar is actually built. This must be done after inserting it into the
+ *     // document.
+ *     toolbar.initialize();
+ *     toolbar.emit( 'updateState' );
+ *
+ * For more information about how to add tools to a MenuToolGroup, please see {@link OO.ui.ToolGroup toolgroup}.
+ * For more information about toolbars in general, please see the [OOjs UI documentation on MediaWiki] [1].
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Toolbars
+ *
+ * @class
+ * @extends OO.ui.PopupToolGroup
+ *
+ * @constructor
+ * @param {OO.ui.Toolbar} toolbar
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.MenuToolGroup = function OoUiMenuToolGroup( toolbar, config ) {
+       // Allow passing positional parameters inside the config object
+       if ( OO.isPlainObject( toolbar ) && config === undefined ) {
+               config = toolbar;
+               toolbar = config.toolbar;
+       }
+
+       // Configuration initialization
+       config = config || {};
+
+       // Parent constructor
+       OO.ui.MenuToolGroup.parent.call( this, toolbar, config );
+
+       // Events
+       this.toolbar.connect( this, { updateState: 'onUpdateState' } );
+
+       // Initialization
+       this.$element.addClass( 'oo-ui-menuToolGroup' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.MenuToolGroup, OO.ui.PopupToolGroup );
+
+/* Static Properties */
+
+OO.ui.MenuToolGroup.static.name = 'menu';
+
+/* Methods */
+
+/**
+ * Handle the toolbar state being updated.
+ *
+ * When the state changes, the title of each active item in the menu will be joined together and
+ * used as a label for the group. The label will be empty if none of the items are active.
+ *
+ * @private
+ */
+OO.ui.MenuToolGroup.prototype.onUpdateState = function () {
+       var name,
+               labelTexts = [];
+
+       for ( name in this.tools ) {
+               if ( this.tools[ name ].isActive() ) {
+                       labelTexts.push( this.tools[ name ].getTitle() );
+               }
+       }
+
+       this.setLabel( labelTexts.join( ', ' ) || ' ' );
+};
+
+}( OO ) );
diff --git a/resources/lib/oojs-ui/oojs-ui-widgets-apex.css b/resources/lib/oojs-ui/oojs-ui-widgets-apex.css
new file mode 100644 (file)
index 0000000..071e33b
--- /dev/null
@@ -0,0 +1,987 @@
+/*!
+ * OOjs UI v0.15.3
+ * https://www.mediawiki.org/wiki/OOjs_UI
+ *
+ * Copyright 2011–2016 OOjs UI Team and other contributors.
+ * Released under the MIT license
+ * http://oojs.mit-license.org
+ *
+ * Date: 2016-02-09T21:21:21Z
+ */
+.oo-ui-draggableElement {
+       cursor: -webkit-grab -moz-grab, url(images/grab.cur), move;
+}
+.oo-ui-draggableElement-dragging {
+       cursor: -webkit-grabbing -moz-grabbing, url(images/grabbing.cur), move;
+       background: rgba(0, 0, 0, 0.2);
+       opacity: 0.4;
+}
+.oo-ui-draggableGroupElement-horizontal .oo-ui-draggableElement.oo-ui-optionWidget {
+       display: inline-block;
+}
+.oo-ui-draggableGroupElement-placeholder {
+       position: absolute;
+       display: block;
+       background: rgba(0, 0, 0, 0.4);
+}
+.oo-ui-lookupElement > .oo-ui-menuSelectWidget {
+       z-index: 1;
+       width: 100%;
+}
+.oo-ui-bookletLayout-stackLayout.oo-ui-stackLayout-continuous > .oo-ui-panelLayout-scrollable {
+       overflow-y: hidden;
+}
+.oo-ui-bookletLayout-stackLayout > .oo-ui-panelLayout {
+       width: 100%;
+       -webkit-box-sizing: border-box;
+          -moz-box-sizing: border-box;
+               box-sizing: border-box;
+}
+.oo-ui-bookletLayout-stackLayout > .oo-ui-panelLayout-scrollable {
+       overflow-y: auto;
+}
+.oo-ui-bookletLayout-stackLayout > .oo-ui-panelLayout-padded {
+       padding: 2em;
+}
+.oo-ui-bookletLayout-outlinePanel-editable > .oo-ui-outlineSelectWidget {
+       position: absolute;
+       top: 0;
+       left: 0;
+       right: 0;
+       bottom: 3em;
+       overflow-y: auto;
+}
+.oo-ui-bookletLayout-outlinePanel > .oo-ui-outlineControlsWidget {
+       position: absolute;
+       bottom: 0;
+       left: 0;
+       right: 0;
+}
+.oo-ui-bookletLayout-stackLayout > .oo-ui-panelLayout {
+       padding: 1.5em;
+}
+.oo-ui-bookletLayout-outlinePanel {
+       border-right: 1px solid #dddddd;
+}
+.oo-ui-bookletLayout-outlinePanel > .oo-ui-outlineControlsWidget {
+       box-shadow: 0 0 0.25em rgba(0, 0, 0, 0.25);
+}
+.oo-ui-indexLayout > .oo-ui-menuLayout-menu {
+       height: 3em;
+}
+.oo-ui-indexLayout > .oo-ui-menuLayout-content {
+       top: 3em;
+}
+.oo-ui-indexLayout-stackLayout > .oo-ui-panelLayout {
+       padding: 1.5em;
+}
+.oo-ui-menuLayout {
+       position: absolute;
+       top: 0;
+       left: 0;
+       right: 0;
+       bottom: 0;
+}
+.oo-ui-menuLayout-menu,
+.oo-ui-menuLayout-content {
+       position: absolute;
+       -webkit-transition: all 200ms ease;
+          -moz-transition: all 200ms ease;
+               transition: all 200ms ease;
+}
+.oo-ui-menuLayout-menu {
+       height: 18em;
+       width: 18em;
+}
+.oo-ui-menuLayout-content {
+       top: 18em;
+       left: 18em;
+       right: 18em;
+       bottom: 18em;
+}
+.oo-ui-menuLayout.oo-ui-menuLayout-hideMenu > .oo-ui-menuLayout-menu {
+       width: 0 !important;
+       height: 0 !important;
+       overflow: hidden;
+}
+.oo-ui-menuLayout.oo-ui-menuLayout-hideMenu > .oo-ui-menuLayout-content {
+       top: 0 !important;
+       left: 0 !important;
+       right: 0 !important;
+       bottom: 0 !important;
+}
+.oo-ui-menuLayout.oo-ui-menuLayout-showMenu.oo-ui-menuLayout-top > .oo-ui-menuLayout-menu {
+       width: auto !important;
+       left: 0;
+       top: 0;
+       right: 0;
+}
+.oo-ui-menuLayout.oo-ui-menuLayout-showMenu.oo-ui-menuLayout-top > .oo-ui-menuLayout-content {
+       right: 0 !important;
+       bottom: 0 !important;
+       left: 0 !important;
+}
+.oo-ui-menuLayout.oo-ui-menuLayout-showMenu.oo-ui-menuLayout-after > .oo-ui-menuLayout-menu {
+       height: auto !important;
+       top: 0;
+       right: 0;
+       bottom: 0;
+}
+.oo-ui-menuLayout.oo-ui-menuLayout-showMenu.oo-ui-menuLayout-after > .oo-ui-menuLayout-content {
+       bottom: 0 !important;
+       left: 0 !important;
+       top: 0 !important;
+}
+.oo-ui-menuLayout.oo-ui-menuLayout-showMenu.oo-ui-menuLayout-bottom > .oo-ui-menuLayout-menu {
+       width: auto !important;
+       right: 0;
+       bottom: 0;
+       left: 0;
+}
+.oo-ui-menuLayout.oo-ui-menuLayout-showMenu.oo-ui-menuLayout-bottom > .oo-ui-menuLayout-content {
+       left: 0 !important;
+       top: 0 !important;
+       right: 0 !important;
+}
+.oo-ui-menuLayout.oo-ui-menuLayout-showMenu.oo-ui-menuLayout-before > .oo-ui-menuLayout-menu {
+       height: auto !important;
+       bottom: 0;
+       left: 0;
+       top: 0;
+}
+.oo-ui-menuLayout.oo-ui-menuLayout-showMenu.oo-ui-menuLayout-before > .oo-ui-menuLayout-content {
+       top: 0 !important;
+       right: 0 !important;
+       bottom: 0 !important;
+}
+.oo-ui-stackLayout-continuous > .oo-ui-panelLayout {
+       display: block;
+       position: relative;
+}
+.oo-ui-buttonSelectWidget {
+       display: inline-block;
+       white-space: nowrap;
+       border-radius: 0.3em;
+       margin-right: 0.5em;
+}
+.oo-ui-buttonSelectWidget:last-child {
+       margin-right: 0;
+}
+.oo-ui-buttonSelectWidget .oo-ui-buttonOptionWidget .oo-ui-buttonElement-button {
+       border-radius: 0;
+       margin-left: -1px;
+}
+.oo-ui-buttonSelectWidget .oo-ui-buttonOptionWidget:first-child .oo-ui-buttonElement-button {
+       border-bottom-left-radius: 0.3em;
+       border-top-left-radius: 0.3em;
+       margin-left: 0;
+}
+.oo-ui-buttonSelectWidget .oo-ui-buttonOptionWidget:last-child .oo-ui-buttonElement-button {
+       border-bottom-right-radius: 0.3em;
+       border-top-right-radius: 0.3em;
+}
+.oo-ui-buttonOptionWidget {
+       display: inline-block;
+       padding: 0;
+       background-color: transparent;
+}
+.oo-ui-buttonOptionWidget .oo-ui-buttonElement-button {
+       position: relative;
+}
+.oo-ui-buttonOptionWidget.oo-ui-iconElement .oo-ui-iconElement-icon,
+.oo-ui-buttonOptionWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
+       position: static;
+       display: inline-block;
+       vertical-align: middle;
+}
+.oo-ui-buttonOptionWidget .oo-ui-buttonElement-button {
+       height: 1.875em;
+}
+.oo-ui-buttonOptionWidget.oo-ui-iconElement .oo-ui-iconElement-icon {
+       margin-top: 0;
+}
+.oo-ui-buttonOptionWidget.oo-ui-optionWidget-selected,
+.oo-ui-buttonOptionWidget.oo-ui-optionWidget-pressed,
+.oo-ui-buttonOptionWidget.oo-ui-optionWidget-highlighted {
+       background-color: transparent;
+}
+.oo-ui-toggleButtonWidget {
+       display: inline-block;
+       vertical-align: middle;
+       margin-right: 0.5em;
+}
+.oo-ui-toggleButtonWidget:last-child {
+       margin-right: 0;
+}
+.oo-ui-toggleSwitchWidget {
+       position: relative;
+       display: inline-block;
+       vertical-align: middle;
+       overflow: hidden;
+       -webkit-box-sizing: border-box;
+          -moz-box-sizing: border-box;
+               box-sizing: border-box;
+       -webkit-transform: translateZ(0);
+          -moz-transform: translateZ(0);
+           -ms-transform: translateZ(0);
+               transform: translateZ(0);
+       height: 2em;
+       width: 4em;
+       border-radius: 1em;
+       box-shadow: 0 0 0 white, inset 0 0.1em 0.2em #dddddd;
+       border: 1px solid #cccccc;
+       margin-right: 0.5em;
+       background-color: #eeeeee;
+       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0, #dddddd), color-stop(100%, #ffffff));
+       background-image: -webkit-linear-gradient(top, #dddddd 0, #ffffff 100%);
+       background-image:    -moz-linear-gradient(top, #dddddd 0, #ffffff 100%);
+       background-image:         linear-gradient(to bottom, #dddddd 0, #ffffff 100%);
+       -ms-filter: "progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffdddddd', endColorstr='#ffffffff' )";
+}
+.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled {
+       cursor: pointer;
+}
+.oo-ui-toggleSwitchWidget-grip {
+       position: absolute;
+       display: block;
+       -webkit-box-sizing: border-box;
+          -moz-box-sizing: border-box;
+               box-sizing: border-box;
+}
+.oo-ui-toggleSwitchWidget .oo-ui-toggleSwitchWidget-glow {
+       position: absolute;
+       top: 0;
+       bottom: 0;
+       right: 0;
+       left: 0;
+       -webkit-touch-callout: none;
+       -webkit-user-select: none;
+          -moz-user-select: none;
+           -ms-user-select: none;
+               user-select: none;
+}
+.oo-ui-toggleWidget-off .oo-ui-toggleSwitchWidget-glow {
+       display: none;
+}
+.oo-ui-toggleSwitchWidget:last-child {
+       margin-right: 0;
+}
+.oo-ui-toggleSwitchWidget.oo-ui-widget-disabled {
+       opacity: 0.5;
+}
+.oo-ui-toggleSwitchWidget-grip {
+       top: 0.25em;
+       left: 0.25em;
+       width: 1.5em;
+       height: 1.5em;
+       margin-top: -1px;
+       border-radius: 1em;
+       box-shadow: 0 0.1em 0.25em rgba(0, 0, 0, 0.1);
+       border: 1px #c9c9c9 solid;
+       -webkit-transition: left 250ms ease, margin-left 250ms ease;
+          -moz-transition: left 250ms ease, margin-left 250ms ease;
+               transition: left 250ms ease, margin-left 250ms ease;
+       background-color: #eeeeee;
+       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0, #ffffff), color-stop(100%, #dddddd));
+       background-image: -webkit-linear-gradient(top, #ffffff 0, #dddddd 100%);
+       background-image:    -moz-linear-gradient(top, #ffffff 0, #dddddd 100%);
+       background-image:         linear-gradient(to bottom, #ffffff 0, #dddddd 100%);
+       -ms-filter: "progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffffff', endColorstr='#ffdddddd' )";
+}
+.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:hover,
+.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:hover .oo-ui-toggleSwitchWidget-grip {
+       border-color: #aaaaaa;
+}
+.oo-ui-toggleSwitchWidget .oo-ui-toggleSwitchWidget-glow {
+       border-radius: 1em;
+       box-shadow: inset 0 1px 4px 0 rgba(0, 0, 0, 0.07);
+       -webkit-transition: opacity 250ms ease;
+          -moz-transition: opacity 250ms ease;
+               transition: opacity 250ms ease;
+       background-color: #cde7f4;
+       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0, #b0d9ee), color-stop(100%, #eaf4fa));
+       background-image: -webkit-linear-gradient(top, #b0d9ee 0, #eaf4fa 100%);
+       background-image:    -moz-linear-gradient(top, #b0d9ee 0, #eaf4fa 100%);
+       background-image:         linear-gradient(to bottom, #b0d9ee 0, #eaf4fa 100%);
+       -ms-filter: "progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffb0d9ee', endColorstr='#ffeaf4fa' )";
+}
+.oo-ui-toggleWidget-on .oo-ui-toggleSwitchWidget-glow {
+       opacity: 1;
+}
+.oo-ui-toggleWidget-on .oo-ui-toggleSwitchWidget-grip {
+       left: 2.25em;
+       margin-left: -2px;
+}
+.oo-ui-toggleWidget-off .oo-ui-toggleSwitchWidget-glow {
+       display: block;
+       opacity: 0;
+}
+.oo-ui-toggleWidget-off .oo-ui-toggleSwitchWidget-grip {
+       left: 0.25em;
+       margin-left: 0;
+}
+.oo-ui-progressBarWidget {
+       max-width: 50em;
+       background-color: #ffffff;
+       border: 1px solid #cccccc;
+       border-radius: 0.25em;
+       overflow: hidden;
+}
+.oo-ui-progressBarWidget-bar {
+       height: 1em;
+       border-right: 1px solid #cccccc;
+       -webkit-transition: width 250ms ease, margin-left 250ms ease;
+          -moz-transition: width 250ms ease, margin-left 250ms ease;
+               transition: width 250ms ease, margin-left 250ms ease;
+       background-color: #cde7f4;
+       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0, #eaf4fa), color-stop(100%, #b0d9ee));
+       background-image: -webkit-linear-gradient(top, #eaf4fa 0, #b0d9ee 100%);
+       background-image:    -moz-linear-gradient(top, #eaf4fa 0, #b0d9ee 100%);
+       background-image:         linear-gradient(to bottom, #eaf4fa 0, #b0d9ee 100%);
+       -ms-filter: "progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffeaf4fa', endColorstr='#ffb0d9ee' )";
+}
+.oo-ui-progressBarWidget-indeterminate .oo-ui-progressBarWidget-bar {
+       -webkit-animation: oo-ui-progressBarWidget-slide 2s infinite linear;
+          -moz-animation: oo-ui-progressBarWidget-slide 2s infinite linear;
+               animation: oo-ui-progressBarWidget-slide 2s infinite linear;
+       width: 40%;
+       margin-left: -10%;
+       border-left: 1px solid #a6cee1;
+}
+.oo-ui-progressBarWidget.oo-ui-widget-disabled {
+       opacity: 0.6;
+}
+@-webkit-keyframes oo-ui-progressBarWidget-slide {
+       from {
+               margin-left: -40%;
+       }
+       to {
+               margin-left: 100%;
+       }
+}
+@-moz-keyframes oo-ui-progressBarWidget-slide {
+       from {
+               margin-left: -40%;
+       }
+       to {
+               margin-left: 100%;
+       }
+}
+@keyframes oo-ui-progressBarWidget-slide {
+       from {
+               margin-left: -40%;
+       }
+       to {
+               margin-left: 100%;
+       }
+}
+.oo-ui-selectFileWidget {
+       display: inline-block;
+       vertical-align: middle;
+       width: 100%;
+       max-width: 50em;
+       margin-right: 0.5em;
+}
+.oo-ui-selectFileWidget-selectButton {
+       display: table-cell;
+       vertical-align: middle;
+}
+.oo-ui-selectFileWidget-selectButton > .oo-ui-buttonElement-button {
+       position: relative;
+       overflow: hidden;
+}
+.oo-ui-selectFileWidget-selectButton > .oo-ui-buttonElement-button > input[type="file"] {
+       position: absolute;
+       margin: 0;
+       top: 0;
+       bottom: 0;
+       left: 0;
+       right: 0;
+       width: 100%;
+       height: 100%;
+       opacity: 0;
+       z-index: 1;
+       cursor: pointer;
+       padding-top: 100px;
+}
+.oo-ui-selectFileWidget-selectButton.oo-ui-widget-disabled > .oo-ui-buttonElement-button > input[type="file"] {
+       display: none;
+}
+.oo-ui-selectFileWidget-info {
+       width: 100%;
+       display: table-cell;
+       vertical-align: middle;
+       position: relative;
+       overflow: hidden;
+       -webkit-box-sizing: border-box;
+          -moz-box-sizing: border-box;
+               box-sizing: border-box;
+}
+.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-label {
+       position: absolute;
+       top: 0;
+       bottom: 0;
+       left: 0;
+       right: 0;
+       text-overflow: ellipsis;
+}
+.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-label > .oo-ui-selectFileWidget-fileName {
+       float: left;
+}
+.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-label > .oo-ui-selectFileWidget-fileType {
+       float: right;
+}
+.oo-ui-selectFileWidget-info > .oo-ui-indicatorElement-indicator,
+.oo-ui-selectFileWidget-info > .oo-ui-iconElement-icon,
+.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-clearButton {
+       position: absolute;
+}
+.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-clearButton {
+       z-index: 2;
+}
+.oo-ui-selectFileWidget-dropTarget {
+       cursor: default;
+       height: 5.5em;
+       text-align: left;
+       padding: 0;
+}
+.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-dropLabel,
+.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-selectButton {
+       display: none;
+}
+.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-thumbnail {
+       height: 5.5em;
+       width: 5.5em;
+       position: absolute;
+       background-size: cover;
+       background-position: center center;
+}
+.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-thumbnail.oo-ui-pendingElement-pending {
+       background-size: auto;
+}
+.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-thumbnail > .oo-ui-selectFileWidget-noThumbnail-icon {
+       opacity: 0.4;
+       background-color: #cccccc;
+       height: 5.5em;
+       width: 5.5em;
+}
+.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-info {
+       border: none;
+       background: none;
+       display: block;
+       height: 100%;
+       width: auto;
+       margin-left: 5.5em;
+}
+.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-label {
+       position: relative;
+}
+.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-label > .oo-ui-selectFileWidget-fileName {
+       display: block;
+       float: none;
+}
+.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-label > .oo-ui-selectFileWidget-fileType {
+       display: block;
+       float: none;
+}
+.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-clearButton {
+       position: absolute;
+       right: 0.5em;
+}
+.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-dropTarget {
+       text-align: center;
+}
+.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-dropLabel {
+       display: block;
+}
+.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-thumbnail,
+.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-info {
+       display: none;
+}
+.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-selectButton {
+       display: block;
+       margin: 0.7em;
+}
+.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-dropTarget,
+.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-dropTarget {
+       text-align: center;
+}
+.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-info,
+.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-info {
+       margin: 0;
+}
+.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-clearButton,
+.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-clearButton {
+       display: none;
+}
+.oo-ui-selectFileWidget:last-child {
+       margin-right: 0;
+}
+.oo-ui-selectFileWidget-selectButton > .oo-ui-buttonElement-button {
+       margin-left: 0.5em;
+}
+.oo-ui-selectFileWidget-info {
+       height: 2.4em;
+       background-color: #ffffff;
+       border: 1px solid rgba(0, 0, 0, 0.1);
+       border-radius: 0.25em;
+}
+.oo-ui-selectFileWidget-info > .oo-ui-indicatorElement-indicator {
+       right: 0;
+}
+.oo-ui-selectFileWidget-info > .oo-ui-iconElement-icon {
+       left: 0;
+}
+.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-label {
+       line-height: 2.3em;
+       margin: 0;
+       overflow: hidden;
+       white-space: nowrap;
+       -webkit-box-sizing: border-box;
+          -moz-box-sizing: border-box;
+               box-sizing: border-box;
+       text-overflow: ellipsis;
+       left: 0.5em;
+       right: 0.5em;
+}
+.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-label > .oo-ui-selectFileWidget-fileType {
+       color: #888888;
+}
+.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-clearButton {
+       top: 0;
+       width: 1.875em;
+       margin-right: 0;
+}
+.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-clearButton .oo-ui-buttonElement-button > .oo-ui-iconElement-icon {
+       height: 2.3em;
+}
+.oo-ui-selectFileWidget-info > .oo-ui-indicatorElement-indicator {
+       top: 0;
+       width: 0.9375em;
+       height: 2.3em;
+       margin-right: 0.775em;
+}
+.oo-ui-selectFileWidget-info > .oo-ui-iconElement-icon {
+       top: 0;
+       width: 1.875em;
+       height: 2.3em;
+       margin-left: 0.3em;
+}
+.oo-ui-selectFileWidget.oo-ui-widget-disabled .oo-ui-selectFileWidget-info {
+       color: #cccccc;
+       text-shadow: 0 1px 1px #ffffff;
+       border-color: #dddddd;
+       background-color: #f3f3f3;
+}
+.oo-ui-selectFileWidget.oo-ui-widget-disabled .oo-ui-selectFileWidget-info > .oo-ui-iconElement-icon,
+.oo-ui-selectFileWidget.oo-ui-widget-disabled .oo-ui-selectFileWidget-info > .oo-ui-indicatorElement-indicator {
+       opacity: 0.2;
+}
+.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-label {
+       color: #cccccc;
+}
+.oo-ui-selectFileWidget.oo-ui-iconElement .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-label {
+       left: 2.475em;
+}
+.oo-ui-selectFileWidget .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-label {
+       right: 2.175em;
+}
+.oo-ui-selectFileWidget .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-clearButton {
+       right: 0;
+}
+.oo-ui-selectFileWidget.oo-ui-indicatorElement .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-label {
+       right: 4.2625em;
+}
+.oo-ui-selectFileWidget.oo-ui-indicatorElement .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-clearButton {
+       right: 2.0875em;
+}
+.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-label,
+.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-label {
+       right: 0.5em;
+}
+.oo-ui-selectFileWidget-empty.oo-ui-indicatorElement .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-label,
+.oo-ui-selectFileWidget-notsupported.oo-ui-indicatorElement .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-label {
+       right: 2em;
+}
+.oo-ui-selectFileWidget-supported.oo-ui-widget-enabled.oo-ui-selectFileWidget-canDrop .oo-ui-selectFileWidget-dropTarget {
+       background-color: #e1f3ff;
+}
+.oo-ui-selectFileWidget-empty.oo-ui-widget-disabled .oo-ui-selectFileWidget-dropTarget,
+.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-dropTarget {
+       color: #cccccc;
+       text-shadow: 0 1px 1px #ffffff;
+       border-color: #dddddd;
+       background-color: #f3f3f3;
+}
+.oo-ui-selectFileWidget-dropTarget {
+       background-color: #ffffff;
+       border: 1px solid #aaaaaa;
+       margin-bottom: 0.5em;
+       vertical-align: middle;
+       border-radius: 0.25em;
+}
+.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-dropTarget {
+       border-style: dashed;
+}
+.oo-ui-outlineOptionWidget {
+       position: relative;
+       cursor: pointer;
+       -webkit-touch-callout: none;
+       -webkit-user-select: none;
+          -moz-user-select: none;
+           -ms-user-select: none;
+               user-select: none;
+       font-size: 1.1em;
+       padding: 0.75em;
+}
+.oo-ui-outlineOptionWidget.oo-ui-indicatorElement .oo-ui-labelElement-label {
+       padding-right: 1.5em;
+}
+.oo-ui-outlineOptionWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
+       opacity: 0.5;
+}
+.oo-ui-outlineOptionWidget-level-0 {
+       padding-left: 3.5em;
+}
+.oo-ui-outlineOptionWidget-level-0 .oo-ui-iconElement-icon {
+       left: 1em;
+}
+.oo-ui-outlineOptionWidget-level-1 {
+       padding-left: 5em;
+}
+.oo-ui-outlineOptionWidget-level-1 .oo-ui-iconElement-icon {
+       left: 2.5em;
+}
+.oo-ui-outlineOptionWidget-level-2 {
+       padding-left: 6.5em;
+}
+.oo-ui-outlineOptionWidget-level-2 .oo-ui-iconElement-icon {
+       left: 4em;
+}
+.oo-ui-selectWidget-depressed .oo-ui-outlineOptionWidget.oo-ui-optionWidget-selected {
+       background-color: #a7dcff;
+       text-shadow: 0 1px 1px rgba(255, 255, 255, 0.5);
+}
+.oo-ui-outlineOptionWidget.oo-ui-flaggedElement-important {
+       font-weight: bold;
+}
+.oo-ui-outlineOptionWidget.oo-ui-flaggedElement-placeholder {
+       font-style: italic;
+}
+.oo-ui-outlineOptionWidget.oo-ui-flaggedElement-empty .oo-ui-iconElement-icon {
+       opacity: 0.5;
+}
+.oo-ui-outlineOptionWidget.oo-ui-flaggedElement-empty .oo-ui-labelElement-label {
+       color: #777777;
+}
+.oo-ui-outlineControlsWidget {
+       height: 3em;
+       background-color: #ffffff;
+}
+.oo-ui-outlineControlsWidget-items,
+.oo-ui-outlineControlsWidget-movers {
+       float: left;
+       -webkit-box-sizing: border-box;
+          -moz-box-sizing: border-box;
+               box-sizing: border-box;
+}
+.oo-ui-outlineControlsWidget > .oo-ui-iconElement-icon {
+       float: left;
+       background-position: right center;
+}
+.oo-ui-outlineControlsWidget-items {
+       float: left;
+}
+.oo-ui-outlineControlsWidget-items .oo-ui-buttonWidget {
+       float: left;
+}
+.oo-ui-outlineControlsWidget-movers {
+       float: right;
+}
+.oo-ui-outlineControlsWidget-movers .oo-ui-buttonWidget {
+       float: right;
+}
+.oo-ui-outlineControlsWidget-items,
+.oo-ui-outlineControlsWidget-movers {
+       height: 2em;
+       margin: 0.5em 0.5em 0.5em 0;
+       padding: 0;
+}
+.oo-ui-outlineControlsWidget > .oo-ui-iconElement-icon {
+       width: 1.5em;
+       height: 2em;
+       margin: 0.5em 0 0.5em 0.5em;
+       opacity: 0.2;
+}
+.oo-ui-tabSelectWidget {
+       text-align: left;
+       white-space: nowrap;
+       overflow: hidden;
+       background-color: #eeeeee;
+       box-shadow: inset 0 -0.015em 0.1em rgba(0, 0, 0, 0.1);
+}
+.oo-ui-tabOptionWidget {
+       display: inline-block;
+       vertical-align: bottom;
+       padding: 0.5em 1em;
+       margin: 0.5em 0 0 0.75em;
+       border: 1px solid transparent;
+       border-bottom: none;
+       border-top-left-radius: 0.5em;
+       border-top-right-radius: 0.5em;
+}
+.oo-ui-tabOptionWidget.oo-ui-indicatorElement .oo-ui-labelElement-label {
+       padding-right: 1.5em;
+}
+.oo-ui-tabOptionWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
+       opacity: 0.5;
+}
+.oo-ui-selectWidget-pressed .oo-ui-tabOptionWidget.oo-ui-optionWidget-pressed {
+       background-color: transparent;
+}
+.oo-ui-tabOptionWidget.oo-ui-widget-enabled:hover {
+       background-color: rgba(255, 255, 255, 0.2);
+       border-color: #dddddd;
+}
+.oo-ui-tabOptionWidget.oo-ui-widget-enabled:active {
+       background-color: #ffffff;
+       border-color: #dddddd;
+}
+.oo-ui-selectWidget-pressed .oo-ui-tabOptionWidget.oo-ui-optionWidget-selected,
+.oo-ui-selectWidget-depressed .oo-ui-tabOptionWidget.oo-ui-optionWidget-selected,
+.oo-ui-tabOptionWidget.oo-ui-optionWidget-selected:hover {
+       background-color: #ffffff;
+       border-color: #dddddd;
+}
+.oo-ui-capsuleMultiSelectWidget {
+       display: inline-block;
+       position: relative;
+       width: 100%;
+       max-width: 50em;
+}
+.oo-ui-capsuleMultiSelectWidget-handle {
+       width: 100%;
+       display: inline-block;
+       position: relative;
+}
+.oo-ui-capsuleMultiSelectWidget-content {
+       position: relative;
+}
+.oo-ui-capsuleMultiSelectWidget.oo-ui-widget-disabled .oo-ui-capsuleMultiSelectWidget-content > input {
+       display: none;
+}
+.oo-ui-capsuleMultiSelectWidget-group {
+       display: inline;
+}
+.oo-ui-capsuleMultiSelectWidget > .oo-ui-menuSelectWidget {
+       z-index: 1;
+       width: 100%;
+}
+.oo-ui-capsuleMultiSelectWidget-handle {
+       background-color: #ffffff;
+       cursor: text;
+       min-height: 2.4em;
+       margin-right: 0.5em;
+       padding: 0.15em 0.25em;
+       border: 1px solid rgba(0, 0, 0, 0.1);
+       border-radius: 0.25em;
+       -webkit-box-sizing: border-box;
+          -moz-box-sizing: border-box;
+               box-sizing: border-box;
+}
+.oo-ui-capsuleMultiSelectWidget-handle:last-child {
+       margin-right: 0;
+}
+.oo-ui-capsuleMultiSelectWidget-handle > .oo-ui-indicatorElement-indicator,
+.oo-ui-capsuleMultiSelectWidget-handle > .oo-ui-iconElement-icon {
+       position: absolute;
+       background-position: center center;
+       background-repeat: no-repeat;
+}
+.oo-ui-capsuleMultiSelectWidget-handle > .oo-ui-capsuleMultiSelectWidget-content > input {
+       border: none;
+       line-height: 1.675em;
+       margin: 0;
+       margin-left: 0.2em;
+       padding: 0;
+       font-size: inherit;
+       font-family: inherit;
+       background-color: transparent;
+       color: black;
+       vertical-align: middle;
+}
+.oo-ui-capsuleMultiSelectWidget-handle > .oo-ui-capsuleMultiSelectWidget-content > input:focus {
+       outline: none;
+}
+.oo-ui-capsuleMultiSelectWidget.oo-ui-indicatorElement .oo-ui-capsuleMultiSelectWidget-handle {
+       padding-right: 2.4875em;
+}
+.oo-ui-capsuleMultiSelectWidget.oo-ui-indicatorElement .oo-ui-capsuleMultiSelectWidget-handle > .oo-ui-indicatorElement-indicator {
+       right: 0;
+       top: 0;
+       width: 0.9375em;
+       height: 0.9375em;
+       margin: 0.775em;
+}
+.oo-ui-capsuleMultiSelectWidget.oo-ui-iconElement .oo-ui-capsuleMultiSelectWidget-handle {
+       padding-left: 2.475em;
+}
+.oo-ui-capsuleMultiSelectWidget.oo-ui-iconElement .oo-ui-capsuleMultiSelectWidget-handle > .oo-ui-iconElement-icon {
+       left: 0;
+       top: 0;
+       width: 1.875em;
+       height: 1.875em;
+       margin: 0.3em;
+}
+.oo-ui-capsuleMultiSelectWidget:hover .oo-ui-capsuleMultiSelectWidget-handle {
+       border-color: rgba(0, 0, 0, 0.2);
+}
+.oo-ui-capsuleMultiSelectWidget.oo-ui-widget-disabled .oo-ui-capsuleMultiSelectWidget-handle {
+       color: #cccccc;
+       text-shadow: 0 1px 1px #ffffff;
+       border-color: #dddddd;
+       background-color: #f3f3f3;
+       cursor: default;
+}
+.oo-ui-capsuleMultiSelectWidget.oo-ui-widget-disabled .oo-ui-capsuleMultiSelectWidget-handle > .oo-ui-iconElement-icon,
+.oo-ui-capsuleMultiSelectWidget.oo-ui-widget-disabled .oo-ui-capsuleMultiSelectWidget-handle > .oo-ui-indicatorElement-indicator {
+       opacity: 0.2;
+}
+.oo-ui-capsuleMultiSelectWidget .oo-ui-selectWidget {
+       border-top-color: #ffffff;
+}
+.oo-ui-capsuleItemWidget {
+       position: relative;
+       display: inline-block;
+       cursor: default;
+       white-space: nowrap;
+       width: auto;
+       max-width: 100%;
+       -webkit-box-sizing: border-box;
+          -moz-box-sizing: border-box;
+               box-sizing: border-box;
+       vertical-align: middle;
+       padding: 0 0.4em;
+       margin: 0.1em;
+       height: 1.7em;
+       line-height: 1.7em;
+       background-color: #eeeeee;
+       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0, #ffffff), color-stop(100%, #dddddd));
+       background-image: -webkit-linear-gradient(top, #ffffff 0, #dddddd 100%);
+       background-image:    -moz-linear-gradient(top, #ffffff 0, #dddddd 100%);
+       background-image:         linear-gradient(to bottom, #ffffff 0, #dddddd 100%);
+       -ms-filter: "progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffffff', endColorstr='#ffdddddd' )";
+       border: 1px solid #cccccc;
+       color: #555555;
+       border-radius: 0.25em;
+}
+.oo-ui-capsuleItemWidget.oo-ui-labelElement .oo-ui-labelElement-label {
+       display: inline-block;
+       text-overflow: ellipsis;
+       overflow: hidden;
+}
+.oo-ui-capsuleItemWidget .oo-ui-buttonElement {
+       margin-top: -1.6em;
+       padding-left: 0.3em;
+}
+.oo-ui-capsuleItemWidget:focus {
+       outline: none;
+       border-color: #087ecc;
+}
+.oo-ui-capsuleItemWidget.oo-ui-indicatorElement > .oo-ui-labelElement-label {
+       padding-right: 1.3375em;
+}
+.oo-ui-capsuleItemWidget.oo-ui-indicatorElement > .oo-ui-indicatorElement-indicator {
+       position: absolute;
+       right: 0.4em;
+       top: 0;
+       width: 0.9375em;
+       height: 100%;
+       background-repeat: no-repeat;
+}
+.oo-ui-capsuleItemWidget.oo-ui-indicatorElement > .oo-ui-indicator-clear {
+       cursor: pointer;
+}
+.oo-ui-capsuleItemWidget.oo-ui-widget-disabled {
+       opacity: 0.5;
+       -webkit-transform: translate3d(0, 0, 0);
+       box-shadow: none;
+       color: #333333;
+       background: #eeeeee;
+       border-color: #cccccc;
+}
+.oo-ui-capsuleItemWidget.oo-ui-widget-disabled > .oo-ui-indicatorElement-indicator {
+       opacity: 0.2;
+}
+.oo-ui-searchWidget-query {
+       position: absolute;
+       top: 0;
+       left: 0;
+       right: 0;
+}
+.oo-ui-searchWidget-query .oo-ui-textInputWidget {
+       width: 100%;
+}
+.oo-ui-searchWidget-results {
+       position: absolute;
+       bottom: 0;
+       left: 0;
+       right: 0;
+       overflow-x: hidden;
+       overflow-y: auto;
+}
+.oo-ui-searchWidget-query {
+       height: 4em;
+       padding: 0 1em;
+       box-shadow: 0 0 0.5em rgba(0, 0, 0, 0.2);
+}
+.oo-ui-searchWidget-query .oo-ui-textInputWidget {
+       margin: 0.75em 0;
+}
+.oo-ui-searchWidget-results {
+       top: 4em;
+       padding: 1em;
+       line-height: 0;
+}
+.oo-ui-numberInputWidget {
+       display: inline-block;
+       position: relative;
+       max-width: 50em;
+}
+.oo-ui-numberInputWidget-field {
+       display: table;
+       table-layout: fixed;
+       width: 100%;
+}
+.oo-ui-numberInputWidget-field > .oo-ui-buttonWidget,
+.oo-ui-numberInputWidget-field > .oo-ui-textInputWidget {
+       display: table-cell;
+       vertical-align: middle;
+}
+.oo-ui-numberInputWidget-field > .oo-ui-textInputWidget {
+       width: 100%;
+}
+.oo-ui-numberInputWidget-field > .oo-ui-buttonWidget {
+       white-space: nowrap;
+}
+.oo-ui-numberInputWidget-field > .oo-ui-buttonWidget > .oo-ui-buttonElement-button {
+       -webkit-box-sizing: border-box;
+          -moz-box-sizing: border-box;
+               box-sizing: border-box;
+}
+.oo-ui-numberInputWidget-field > .oo-ui-buttonWidget {
+       width: 2.25em;
+}
+.oo-ui-numberInputWidget-minusButton.oo-ui-buttonElement-framed.oo-ui-widget-enabled > .oo-ui-buttonElement-button {
+       border-top-right-radius: 0;
+       border-bottom-right-radius: 0;
+       border-right-width: 0;
+}
+.oo-ui-numberInputWidget-plusButton.oo-ui-buttonElement-framed.oo-ui-widget-enabled > .oo-ui-buttonElement-button {
+       border-top-left-radius: 0;
+       border-bottom-left-radius: 0;
+       border-left-width: 0;
+}
+.oo-ui-numberInputWidget .oo-ui-textInputWidget input {
+       border-radius: 0;
+}
diff --git a/resources/lib/oojs-ui/oojs-ui-widgets-mediawiki.css b/resources/lib/oojs-ui/oojs-ui-widgets-mediawiki.css
new file mode 100644 (file)
index 0000000..8475dd1
--- /dev/null
@@ -0,0 +1,1002 @@
+/*!
+ * OOjs UI v0.15.3
+ * https://www.mediawiki.org/wiki/OOjs_UI
+ *
+ * Copyright 2011–2016 OOjs UI Team and other contributors.
+ * Released under the MIT license
+ * http://oojs.mit-license.org
+ *
+ * Date: 2016-02-09T21:21:21Z
+ */
+.oo-ui-draggableElement {
+       cursor: -webkit-grab -moz-grab, url(images/grab.cur), move;
+}
+.oo-ui-draggableElement-dragging {
+       cursor: -webkit-grabbing -moz-grabbing, url(images/grabbing.cur), move;
+       background: rgba(0, 0, 0, 0.2);
+       opacity: 0.4;
+}
+.oo-ui-draggableGroupElement-horizontal .oo-ui-draggableElement.oo-ui-optionWidget {
+       display: inline-block;
+}
+.oo-ui-draggableGroupElement-placeholder {
+       position: absolute;
+       display: block;
+       background: rgba(0, 0, 0, 0.4);
+}
+.oo-ui-lookupElement > .oo-ui-menuSelectWidget {
+       z-index: 1;
+       width: 100%;
+}
+.oo-ui-bookletLayout-stackLayout.oo-ui-stackLayout-continuous > .oo-ui-panelLayout-scrollable {
+       overflow-y: hidden;
+}
+.oo-ui-bookletLayout-stackLayout > .oo-ui-panelLayout {
+       width: 100%;
+       -webkit-box-sizing: border-box;
+          -moz-box-sizing: border-box;
+               box-sizing: border-box;
+}
+.oo-ui-bookletLayout-stackLayout > .oo-ui-panelLayout-scrollable {
+       overflow-y: auto;
+}
+.oo-ui-bookletLayout-stackLayout > .oo-ui-panelLayout-padded {
+       padding: 2em;
+}
+.oo-ui-bookletLayout-outlinePanel-editable > .oo-ui-outlineSelectWidget {
+       position: absolute;
+       top: 0;
+       left: 0;
+       right: 0;
+       bottom: 3em;
+       overflow-y: auto;
+}
+.oo-ui-bookletLayout-outlinePanel > .oo-ui-outlineControlsWidget {
+       position: absolute;
+       bottom: 0;
+       left: 0;
+       right: 0;
+}
+.oo-ui-bookletLayout-stackLayout > .oo-ui-panelLayout {
+       padding: 1.5em;
+}
+.oo-ui-bookletLayout-outlinePanel {
+       border-right: 1px solid #dddddd;
+}
+.oo-ui-bookletLayout-outlinePanel > .oo-ui-outlineControlsWidget {
+       box-shadow: 0 0.15em 0 0 rgba(0, 0, 0, 0.15);
+}
+.oo-ui-indexLayout > .oo-ui-menuLayout-menu {
+       height: 3em;
+}
+.oo-ui-indexLayout > .oo-ui-menuLayout-content {
+       top: 3em;
+}
+.oo-ui-indexLayout-stackLayout > .oo-ui-panelLayout {
+       padding: 1.5em;
+}
+.oo-ui-indexLayout > .oo-ui-menuLayout-menu {
+       height: 2.75em;
+}
+.oo-ui-indexLayout > .oo-ui-menuLayout-content {
+       top: 2.75em;
+}
+.oo-ui-menuLayout {
+       position: absolute;
+       top: 0;
+       left: 0;
+       right: 0;
+       bottom: 0;
+}
+.oo-ui-menuLayout-menu,
+.oo-ui-menuLayout-content {
+       position: absolute;
+       -webkit-transition: all 200ms ease;
+          -moz-transition: all 200ms ease;
+               transition: all 200ms ease;
+}
+.oo-ui-menuLayout-menu {
+       height: 18em;
+       width: 18em;
+}
+.oo-ui-menuLayout-content {
+       top: 18em;
+       left: 18em;
+       right: 18em;
+       bottom: 18em;
+}
+.oo-ui-menuLayout.oo-ui-menuLayout-hideMenu > .oo-ui-menuLayout-menu {
+       width: 0 !important;
+       height: 0 !important;
+       overflow: hidden;
+}
+.oo-ui-menuLayout.oo-ui-menuLayout-hideMenu > .oo-ui-menuLayout-content {
+       top: 0 !important;
+       left: 0 !important;
+       right: 0 !important;
+       bottom: 0 !important;
+}
+.oo-ui-menuLayout.oo-ui-menuLayout-showMenu.oo-ui-menuLayout-top > .oo-ui-menuLayout-menu {
+       width: auto !important;
+       left: 0;
+       top: 0;
+       right: 0;
+}
+.oo-ui-menuLayout.oo-ui-menuLayout-showMenu.oo-ui-menuLayout-top > .oo-ui-menuLayout-content {
+       right: 0 !important;
+       bottom: 0 !important;
+       left: 0 !important;
+}
+.oo-ui-menuLayout.oo-ui-menuLayout-showMenu.oo-ui-menuLayout-after > .oo-ui-menuLayout-menu {
+       height: auto !important;
+       top: 0;
+       right: 0;
+       bottom: 0;
+}
+.oo-ui-menuLayout.oo-ui-menuLayout-showMenu.oo-ui-menuLayout-after > .oo-ui-menuLayout-content {
+       bottom: 0 !important;
+       left: 0 !important;
+       top: 0 !important;
+}
+.oo-ui-menuLayout.oo-ui-menuLayout-showMenu.oo-ui-menuLayout-bottom > .oo-ui-menuLayout-menu {
+       width: auto !important;
+       right: 0;
+       bottom: 0;
+       left: 0;
+}
+.oo-ui-menuLayout.oo-ui-menuLayout-showMenu.oo-ui-menuLayout-bottom > .oo-ui-menuLayout-content {
+       left: 0 !important;
+       top: 0 !important;
+       right: 0 !important;
+}
+.oo-ui-menuLayout.oo-ui-menuLayout-showMenu.oo-ui-menuLayout-before > .oo-ui-menuLayout-menu {
+       height: auto !important;
+       bottom: 0;
+       left: 0;
+       top: 0;
+}
+.oo-ui-menuLayout.oo-ui-menuLayout-showMenu.oo-ui-menuLayout-before > .oo-ui-menuLayout-content {
+       top: 0 !important;
+       right: 0 !important;
+       bottom: 0 !important;
+}
+.oo-ui-stackLayout-continuous > .oo-ui-panelLayout {
+       display: block;
+       position: relative;
+}
+.oo-ui-buttonSelectWidget {
+       display: inline-block;
+       white-space: nowrap;
+       border-radius: 2px;
+       margin-right: 0.5em;
+}
+.oo-ui-buttonSelectWidget:last-child {
+       margin-right: 0;
+}
+.oo-ui-buttonSelectWidget .oo-ui-buttonOptionWidget .oo-ui-buttonElement-button {
+       border-radius: 0;
+       margin-left: -1px;
+}
+.oo-ui-buttonSelectWidget .oo-ui-buttonOptionWidget:first-child .oo-ui-buttonElement-button {
+       border-bottom-left-radius: 2px;
+       border-top-left-radius: 2px;
+       margin-left: 0;
+}
+.oo-ui-buttonSelectWidget .oo-ui-buttonOptionWidget:last-child .oo-ui-buttonElement-button {
+       border-bottom-right-radius: 2px;
+       border-top-right-radius: 2px;
+}
+.oo-ui-buttonOptionWidget {
+       display: inline-block;
+       padding: 0;
+       background-color: transparent;
+}
+.oo-ui-buttonOptionWidget .oo-ui-buttonElement-button {
+       position: relative;
+}
+.oo-ui-buttonOptionWidget.oo-ui-iconElement .oo-ui-iconElement-icon,
+.oo-ui-buttonOptionWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
+       position: static;
+       display: inline-block;
+       vertical-align: middle;
+}
+.oo-ui-buttonOptionWidget.oo-ui-iconElement .oo-ui-iconElement-icon {
+       margin-top: 0;
+}
+.oo-ui-buttonOptionWidget.oo-ui-optionWidget-selected,
+.oo-ui-buttonOptionWidget.oo-ui-optionWidget-pressed,
+.oo-ui-buttonOptionWidget.oo-ui-optionWidget-highlighted {
+       background-color: transparent;
+}
+.oo-ui-buttonOptionWidget.oo-ui-widget-disabled .oo-ui-iconElement-icon,
+.oo-ui-buttonOptionWidget.oo-ui-widget-disabled .oo-ui-indicatorElement-indicator {
+       opacity: 1;
+}
+.oo-ui-toggleButtonWidget {
+       display: inline-block;
+       vertical-align: middle;
+       margin-right: 0.5em;
+}
+.oo-ui-toggleButtonWidget:last-child {
+       margin-right: 0;
+}
+.oo-ui-toggleSwitchWidget {
+       position: relative;
+       display: inline-block;
+       vertical-align: middle;
+       overflow: hidden;
+       -webkit-box-sizing: border-box;
+          -moz-box-sizing: border-box;
+               box-sizing: border-box;
+       -webkit-transform: translateZ(0);
+          -moz-transform: translateZ(0);
+           -ms-transform: translateZ(0);
+               transform: translateZ(0);
+       height: 2em;
+       width: 3.5em;
+       border: 1px solid #777777;
+       border-radius: 1em;
+       background-color: #ffffff;
+       margin-right: 0.5em;
+       -webkit-transition: background-color 100ms ease, border-color 100ms ease;
+          -moz-transition: background-color 100ms ease, border-color 100ms ease;
+               transition: background-color 100ms ease, border-color 100ms ease;
+}
+.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled {
+       cursor: pointer;
+}
+.oo-ui-toggleSwitchWidget-grip {
+       position: absolute;
+       display: block;
+       -webkit-box-sizing: border-box;
+          -moz-box-sizing: border-box;
+               box-sizing: border-box;
+}
+.oo-ui-toggleSwitchWidget .oo-ui-toggleSwitchWidget-glow {
+       position: absolute;
+       top: 0;
+       bottom: 0;
+       right: 0;
+       left: 0;
+       -webkit-touch-callout: none;
+       -webkit-user-select: none;
+          -moz-user-select: none;
+           -ms-user-select: none;
+               user-select: none;
+}
+.oo-ui-toggleWidget-off .oo-ui-toggleSwitchWidget-glow {
+       display: none;
+}
+.oo-ui-toggleSwitchWidget:last-child {
+       margin-right: 0;
+}
+.oo-ui-toggleSwitchWidget:before {
+       content: "";
+       display: block;
+       position: absolute;
+       top: 0;
+       left: 0;
+       bottom: 0;
+       right: 0;
+       border: 1px solid transparent;
+       border-radius: 1em;
+       z-index: 1;
+}
+.oo-ui-toggleSwitchWidget-grip {
+       top: 0.35em;
+       width: 1.2em;
+       height: 1.2em;
+       border-radius: 1.2em;
+       background-color: #555555;
+       -webkit-transition: left 100ms ease, margin-left 100ms ease;
+          -moz-transition: left 100ms ease, margin-left 100ms ease;
+               transition: left 100ms ease, margin-left 100ms ease;
+}
+.oo-ui-toggleSwitchWidget-glow {
+       display: none;
+}
+.oo-ui-toggleSwitchWidget.oo-ui-toggleWidget-on .oo-ui-toggleSwitchWidget-grip {
+       left: 1.9em;
+       margin-left: -2px;
+}
+.oo-ui-toggleSwitchWidget.oo-ui-toggleWidget-off .oo-ui-toggleSwitchWidget-grip {
+       left: 0.4em;
+       margin-left: 0;
+}
+.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled.oo-ui-toggleWidget-on {
+       background-color: #347bff;
+       border-color: #347bff;
+}
+.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled.oo-ui-toggleWidget-on .oo-ui-toggleSwitchWidget-grip {
+       background-color: #ffffff;
+       box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1);
+}
+.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:hover {
+       border-color: #2962cc;
+}
+.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:hover.oo-ui-toggleWidget-on {
+       background-color: #2962cc;
+       border-color: #2962cc;
+}
+.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:focus {
+       border-color: #347bff;
+       outline: none;
+}
+.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:focus.oo-ui-toggleWidget-on {
+       border-color: #347bff;
+}
+.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:focus.oo-ui-toggleWidget-on:before {
+       border-color: #ffffff;
+}
+.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:active,
+.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:active:hover {
+       background-color: #347bff;
+       border-color: #347bff;
+}
+.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:active .oo-ui-toggleSwitchWidget-grip,
+.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:active:hover .oo-ui-toggleSwitchWidget-grip {
+       background-color: #ffffff;
+       box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1);
+}
+.oo-ui-toggleSwitchWidget.oo-ui-widget-disabled {
+       background: #dddddd;
+       border-color: #dddddd;
+       outline: 0;
+}
+.oo-ui-toggleSwitchWidget.oo-ui-widget-disabled .oo-ui-toggleSwitchWidget-grip {
+       background: #ffffff;
+}
+.oo-ui-progressBarWidget {
+       max-width: 50em;
+       background-color: #ffffff;
+       border: 1px solid #cccccc;
+       border-radius: 2px;
+       overflow: hidden;
+}
+.oo-ui-progressBarWidget-bar {
+       height: 1em;
+       background: #dddddd;
+       -webkit-transition: width 200ms, margin-left 200ms;
+          -moz-transition: width 200ms, margin-left 200ms;
+               transition: width 200ms, margin-left 200ms;
+}
+.oo-ui-progressBarWidget-indeterminate .oo-ui-progressBarWidget-bar {
+       -webkit-animation: oo-ui-progressBarWidget-slide 2s infinite linear;
+          -moz-animation: oo-ui-progressBarWidget-slide 2s infinite linear;
+               animation: oo-ui-progressBarWidget-slide 2s infinite linear;
+       width: 40%;
+       margin-left: -10%;
+       border-left-width: 1px;
+}
+.oo-ui-progressBarWidget.oo-ui-widget-disabled {
+       opacity: 0.6;
+}
+@-webkit-keyframes oo-ui-progressBarWidget-slide {
+       from {
+               margin-left: -40%;
+       }
+       to {
+               margin-left: 100%;
+       }
+}
+@-moz-keyframes oo-ui-progressBarWidget-slide {
+       from {
+               margin-left: -40%;
+       }
+       to {
+               margin-left: 100%;
+       }
+}
+@keyframes oo-ui-progressBarWidget-slide {
+       from {
+               margin-left: -40%;
+       }
+       to {
+               margin-left: 100%;
+       }
+}
+.oo-ui-selectFileWidget {
+       display: inline-block;
+       vertical-align: middle;
+       width: 100%;
+       max-width: 50em;
+       margin-right: 0.5em;
+}
+.oo-ui-selectFileWidget-selectButton {
+       display: table-cell;
+       vertical-align: middle;
+}
+.oo-ui-selectFileWidget-selectButton > .oo-ui-buttonElement-button {
+       position: relative;
+       overflow: hidden;
+}
+.oo-ui-selectFileWidget-selectButton > .oo-ui-buttonElement-button > input[type="file"] {
+       position: absolute;
+       margin: 0;
+       top: 0;
+       bottom: 0;
+       left: 0;
+       right: 0;
+       width: 100%;
+       height: 100%;
+       opacity: 0;
+       z-index: 1;
+       cursor: pointer;
+       padding-top: 100px;
+}
+.oo-ui-selectFileWidget-selectButton.oo-ui-widget-disabled > .oo-ui-buttonElement-button > input[type="file"] {
+       display: none;
+}
+.oo-ui-selectFileWidget-info {
+       width: 100%;
+       display: table-cell;
+       vertical-align: middle;
+       position: relative;
+       overflow: hidden;
+       -webkit-box-sizing: border-box;
+          -moz-box-sizing: border-box;
+               box-sizing: border-box;
+}
+.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-label {
+       position: absolute;
+       top: 0;
+       bottom: 0;
+       left: 0;
+       right: 0;
+       text-overflow: ellipsis;
+}
+.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-label > .oo-ui-selectFileWidget-fileName {
+       float: left;
+}
+.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-label > .oo-ui-selectFileWidget-fileType {
+       float: right;
+}
+.oo-ui-selectFileWidget-info > .oo-ui-indicatorElement-indicator,
+.oo-ui-selectFileWidget-info > .oo-ui-iconElement-icon,
+.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-clearButton {
+       position: absolute;
+}
+.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-clearButton {
+       z-index: 2;
+}
+.oo-ui-selectFileWidget-dropTarget {
+       cursor: default;
+       height: 5.5em;
+       text-align: left;
+       padding: 0;
+}
+.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-dropLabel,
+.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-selectButton {
+       display: none;
+}
+.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-thumbnail {
+       height: 5.5em;
+       width: 5.5em;
+       position: absolute;
+       background-size: cover;
+       background-position: center center;
+}
+.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-thumbnail.oo-ui-pendingElement-pending {
+       background-size: auto;
+}
+.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-thumbnail > .oo-ui-selectFileWidget-noThumbnail-icon {
+       opacity: 0.4;
+       background-color: #cccccc;
+       height: 5.5em;
+       width: 5.5em;
+}
+.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-info {
+       border: none;
+       background: none;
+       display: block;
+       height: 100%;
+       width: auto;
+       margin-left: 5.5em;
+}
+.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-label {
+       position: relative;
+}
+.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-label > .oo-ui-selectFileWidget-fileName {
+       display: block;
+       float: none;
+}
+.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-label > .oo-ui-selectFileWidget-fileType {
+       display: block;
+       float: none;
+}
+.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-clearButton {
+       position: absolute;
+       right: 0.5em;
+}
+.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-dropTarget {
+       text-align: center;
+}
+.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-dropLabel {
+       display: block;
+}
+.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-thumbnail,
+.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-info {
+       display: none;
+}
+.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-selectButton {
+       display: block;
+       margin: 0.7em;
+}
+.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-dropTarget,
+.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-dropTarget {
+       text-align: center;
+}
+.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-info,
+.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-info {
+       margin: 0;
+}
+.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-clearButton,
+.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-clearButton {
+       display: none;
+}
+.oo-ui-selectFileWidget:last-child {
+       margin-right: 0;
+}
+.oo-ui-selectFileWidget-selectButton > .oo-ui-buttonElement-button {
+       margin-left: 0.5em;
+}
+.oo-ui-selectFileWidget-info {
+       height: 2.4em;
+       background-color: #ffffff;
+       border: 1px solid #cccccc;
+       border-radius: 2px;
+}
+.oo-ui-selectFileWidget-info > .oo-ui-indicatorElement-indicator {
+       right: 0;
+}
+.oo-ui-selectFileWidget-info > .oo-ui-iconElement-icon {
+       left: 0;
+}
+.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-label {
+       line-height: 2.3em;
+       margin: 0;
+       overflow: hidden;
+       white-space: nowrap;
+       -webkit-box-sizing: border-box;
+          -moz-box-sizing: border-box;
+               box-sizing: border-box;
+       text-overflow: ellipsis;
+       left: 0.5em;
+       right: 0.5em;
+}
+.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-label > .oo-ui-selectFileWidget-fileType {
+       color: #888888;
+}
+.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-clearButton {
+       top: 0;
+       width: 1.875em;
+       margin-right: 0;
+}
+.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-clearButton .oo-ui-buttonElement-button > .oo-ui-iconElement-icon {
+       height: 2.3em;
+}
+.oo-ui-selectFileWidget-info > .oo-ui-indicatorElement-indicator {
+       top: 0;
+       width: 0.9375em;
+       height: 2.3em;
+       margin-right: 0.775em;
+}
+.oo-ui-selectFileWidget-info > .oo-ui-iconElement-icon {
+       top: 0;
+       width: 1.875em;
+       height: 2.3em;
+       margin-left: 0.5em;
+}
+.oo-ui-selectFileWidget.oo-ui-widget-disabled .oo-ui-selectFileWidget-info {
+       color: #cccccc;
+       text-shadow: 0 1px 1px #ffffff;
+       border-color: #dddddd;
+       background-color: #f3f3f3;
+}
+.oo-ui-selectFileWidget.oo-ui-widget-disabled .oo-ui-selectFileWidget-info > .oo-ui-iconElement-icon,
+.oo-ui-selectFileWidget.oo-ui-widget-disabled .oo-ui-selectFileWidget-info > .oo-ui-indicatorElement-indicator {
+       opacity: 0.2;
+}
+.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-label {
+       color: #cccccc;
+}
+.oo-ui-selectFileWidget.oo-ui-iconElement .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-label {
+       left: 2.875em;
+}
+.oo-ui-selectFileWidget .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-label {
+       right: 2.375em;
+}
+.oo-ui-selectFileWidget .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-clearButton {
+       right: 0;
+}
+.oo-ui-selectFileWidget.oo-ui-indicatorElement .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-label {
+       right: 4.4625em;
+}
+.oo-ui-selectFileWidget.oo-ui-indicatorElement .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-clearButton {
+       right: 2.0875em;
+}
+.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-label,
+.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-label {
+       right: 0.5em;
+}
+.oo-ui-selectFileWidget-empty.oo-ui-indicatorElement .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-label,
+.oo-ui-selectFileWidget-notsupported.oo-ui-indicatorElement .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-label {
+       right: 2em;
+}
+.oo-ui-selectFileWidget-supported.oo-ui-widget-enabled.oo-ui-selectFileWidget-canDrop .oo-ui-selectFileWidget-dropTarget {
+       background: rgba(52, 123, 255, 0.1);
+}
+.oo-ui-selectFileWidget-empty.oo-ui-widget-disabled .oo-ui-selectFileWidget-dropTarget,
+.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-dropTarget {
+       border-color: #dddddd;
+       background-color: #f3f3f3;
+}
+.oo-ui-selectFileWidget-empty.oo-ui-widget-disabled .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-info,
+.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-info,
+.oo-ui-selectFileWidget-empty.oo-ui-widget-disabled .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-dropLabel,
+.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-dropLabel {
+       color: #cccccc;
+       text-shadow: 0 1px 1px #ffffff;
+}
+.oo-ui-selectFileWidget-dropTarget {
+       background-color: #ffffff;
+       border: 1px solid #cccccc;
+       margin-bottom: 0.5em;
+       vertical-align: middle;
+       overflow: hidden;
+       border-radius: 2px;
+}
+.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-dropTarget {
+       background-color: #eeeeee;
+       border-style: dashed;
+}
+.oo-ui-outlineOptionWidget {
+       position: relative;
+       cursor: pointer;
+       -webkit-touch-callout: none;
+       -webkit-user-select: none;
+          -moz-user-select: none;
+           -ms-user-select: none;
+               user-select: none;
+       font-size: 1.1em;
+       padding: 0.75em;
+}
+.oo-ui-outlineOptionWidget.oo-ui-indicatorElement .oo-ui-labelElement-label {
+       padding-right: 1.5em;
+}
+.oo-ui-outlineOptionWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
+       opacity: 0.5;
+}
+.oo-ui-outlineOptionWidget-level-0 {
+       padding-left: 3.5em;
+}
+.oo-ui-outlineOptionWidget-level-0 .oo-ui-iconElement-icon {
+       left: 1em;
+}
+.oo-ui-outlineOptionWidget-level-1 {
+       padding-left: 5em;
+}
+.oo-ui-outlineOptionWidget-level-1 .oo-ui-iconElement-icon {
+       left: 2.5em;
+}
+.oo-ui-outlineOptionWidget-level-2 {
+       padding-left: 6.5em;
+}
+.oo-ui-outlineOptionWidget-level-2 .oo-ui-iconElement-icon {
+       left: 4em;
+}
+.oo-ui-selectWidget-depressed .oo-ui-outlineOptionWidget.oo-ui-optionWidget-selected {
+       background-color: #d0d0d0;
+       text-shadow: 0 1px 1px #ffffff;
+}
+.oo-ui-outlineOptionWidget.oo-ui-flaggedElement-important {
+       font-weight: bold;
+}
+.oo-ui-outlineOptionWidget.oo-ui-flaggedElement-placeholder {
+       font-style: italic;
+}
+.oo-ui-outlineOptionWidget.oo-ui-flaggedElement-empty .oo-ui-iconElement-icon {
+       opacity: 0.5;
+}
+.oo-ui-outlineOptionWidget.oo-ui-flaggedElement-empty .oo-ui-labelElement-label {
+       color: #777777;
+}
+.oo-ui-outlineControlsWidget {
+       height: 3em;
+       background-color: #ffffff;
+}
+.oo-ui-outlineControlsWidget-items,
+.oo-ui-outlineControlsWidget-movers {
+       float: left;
+       -webkit-box-sizing: border-box;
+          -moz-box-sizing: border-box;
+               box-sizing: border-box;
+}
+.oo-ui-outlineControlsWidget > .oo-ui-iconElement-icon {
+       float: left;
+       background-position: right center;
+}
+.oo-ui-outlineControlsWidget-items {
+       float: left;
+}
+.oo-ui-outlineControlsWidget-items .oo-ui-buttonWidget {
+       float: left;
+}
+.oo-ui-outlineControlsWidget-movers {
+       float: right;
+}
+.oo-ui-outlineControlsWidget-movers .oo-ui-buttonWidget {
+       float: right;
+}
+.oo-ui-outlineControlsWidget-items,
+.oo-ui-outlineControlsWidget-movers {
+       height: 2em;
+       margin: 0.5em 0.5em 0.5em 0;
+       padding: 0;
+}
+.oo-ui-outlineControlsWidget > .oo-ui-iconElement-icon {
+       width: 1.5em;
+       height: 2em;
+       margin: 0.5em 0 0.5em 0.5em;
+       opacity: 0.2;
+}
+.oo-ui-tabSelectWidget {
+       text-align: left;
+       white-space: nowrap;
+       overflow: hidden;
+       background-color: #dddddd;
+}
+.oo-ui-tabOptionWidget {
+       display: inline-block;
+       vertical-align: bottom;
+       padding: 0.35em 1em;
+       margin: 0.5em 0 0 0.75em;
+       border: 1px solid transparent;
+       border-bottom: none;
+       border-top-left-radius: 2px;
+       border-top-right-radius: 2px;
+       color: #555555;
+       font-weight: bold;
+}
+.oo-ui-tabOptionWidget.oo-ui-widget-enabled:hover {
+       background-color: rgba(255, 255, 255, 0.3);
+}
+.oo-ui-tabOptionWidget.oo-ui-widget-enabled:active {
+       background-color: rgba(255, 255, 255, 0.8);
+}
+.oo-ui-tabOptionWidget.oo-ui-indicatorElement .oo-ui-labelElement-label {
+       padding-right: 1.5em;
+}
+.oo-ui-tabOptionWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
+       opacity: 0.5;
+}
+.oo-ui-selectWidget-pressed .oo-ui-tabOptionWidget.oo-ui-optionWidget-selected,
+.oo-ui-selectWidget-depressed .oo-ui-tabOptionWidget.oo-ui-optionWidget-selected,
+.oo-ui-tabOptionWidget.oo-ui-optionWidget-selected:hover {
+       background-color: #ffffff;
+       color: #333333;
+}
+.oo-ui-capsuleMultiSelectWidget {
+       display: inline-block;
+       position: relative;
+       width: 100%;
+       max-width: 50em;
+}
+.oo-ui-capsuleMultiSelectWidget-handle {
+       width: 100%;
+       display: inline-block;
+       position: relative;
+}
+.oo-ui-capsuleMultiSelectWidget-content {
+       position: relative;
+}
+.oo-ui-capsuleMultiSelectWidget.oo-ui-widget-disabled .oo-ui-capsuleMultiSelectWidget-content > input {
+       display: none;
+}
+.oo-ui-capsuleMultiSelectWidget-group {
+       display: inline;
+}
+.oo-ui-capsuleMultiSelectWidget > .oo-ui-menuSelectWidget {
+       z-index: 1;
+       width: 100%;
+}
+.oo-ui-capsuleMultiSelectWidget-handle {
+       background-color: #ffffff;
+       cursor: text;
+       min-height: 2.4em;
+       margin-right: 0.5em;
+       padding: 0.15em 0.25em;
+       border: 1px solid #cccccc;
+       border-radius: 2px;
+       -webkit-box-sizing: border-box;
+          -moz-box-sizing: border-box;
+               box-sizing: border-box;
+}
+.oo-ui-capsuleMultiSelectWidget-handle:last-child {
+       margin-right: 0;
+}
+.oo-ui-capsuleMultiSelectWidget-handle > .oo-ui-indicatorElement-indicator,
+.oo-ui-capsuleMultiSelectWidget-handle > .oo-ui-iconElement-icon {
+       position: absolute;
+       background-position: center center;
+       background-repeat: no-repeat;
+}
+.oo-ui-capsuleMultiSelectWidget-handle > .oo-ui-capsuleMultiSelectWidget-content > input {
+       border: 0;
+       line-height: 1.675em;
+       margin: 0 0 0 0.2em;
+       padding: 0;
+       font-size: inherit;
+       font-family: inherit;
+       background-color: transparent;
+       color: #000000;
+       vertical-align: middle;
+}
+.oo-ui-capsuleMultiSelectWidget-handle > .oo-ui-capsuleMultiSelectWidget-content > input:focus {
+       outline: none;
+}
+.oo-ui-capsuleMultiSelectWidget.oo-ui-indicatorElement .oo-ui-capsuleMultiSelectWidget-handle {
+       padding-right: 2.4875em;
+}
+.oo-ui-capsuleMultiSelectWidget.oo-ui-indicatorElement .oo-ui-capsuleMultiSelectWidget-handle > .oo-ui-indicatorElement-indicator {
+       right: 0;
+       top: 0;
+       width: 0.9375em;
+       height: 0.9375em;
+       margin: 0.775em;
+}
+.oo-ui-capsuleMultiSelectWidget.oo-ui-iconElement .oo-ui-capsuleMultiSelectWidget-handle {
+       padding-left: 2.475em;
+}
+.oo-ui-capsuleMultiSelectWidget.oo-ui-iconElement .oo-ui-capsuleMultiSelectWidget-handle > .oo-ui-iconElement-icon {
+       left: 0;
+       top: 0;
+       width: 1.875em;
+       height: 1.875em;
+       margin: 0.3em;
+}
+.oo-ui-capsuleMultiSelectWidget:hover .oo-ui-capsuleMultiSelectWidget-handle {
+       border-color: #aaaaaa;
+}
+.oo-ui-capsuleMultiSelectWidget.oo-ui-widget-disabled .oo-ui-capsuleMultiSelectWidget-handle {
+       color: #cccccc;
+       text-shadow: 0 1px 1px #ffffff;
+       border-color: #dddddd;
+       background-color: #f3f3f3;
+       cursor: default;
+}
+.oo-ui-capsuleMultiSelectWidget.oo-ui-widget-disabled .oo-ui-capsuleMultiSelectWidget-handle > .oo-ui-iconElement-icon,
+.oo-ui-capsuleMultiSelectWidget.oo-ui-widget-disabled .oo-ui-capsuleMultiSelectWidget-handle > .oo-ui-indicatorElement-indicator {
+       opacity: 0.2;
+}
+.oo-ui-capsuleMultiSelectWidget .oo-ui-selectWidget {
+       border-top-color: #ffffff;
+}
+.oo-ui-capsuleItemWidget {
+       position: relative;
+       display: inline-block;
+       cursor: default;
+       white-space: nowrap;
+       width: auto;
+       max-width: 100%;
+       -webkit-box-sizing: border-box;
+          -moz-box-sizing: border-box;
+               box-sizing: border-box;
+       vertical-align: middle;
+       padding: 0 0.4em;
+       margin: 0.1em;
+       height: 1.7em;
+       line-height: 1.7em;
+       background-color: #eeeeee;
+       border: 1px solid #cccccc;
+       color: #555555;
+       border-radius: 2px;
+}
+.oo-ui-capsuleItemWidget.oo-ui-labelElement .oo-ui-labelElement-label {
+       display: inline-block;
+       text-overflow: ellipsis;
+       overflow: hidden;
+}
+.oo-ui-capsuleItemWidget .oo-ui-buttonElement {
+       margin-top: -1.6em;
+       padding-left: 0.3em;
+}
+.oo-ui-capsuleItemWidget:focus {
+       outline: none;
+       border-color: #347bff;
+}
+.oo-ui-capsuleItemWidget.oo-ui-indicatorElement > .oo-ui-labelElement-label {
+       padding-right: 1.3375em;
+}
+.oo-ui-capsuleItemWidget.oo-ui-indicatorElement > .oo-ui-indicatorElement-indicator {
+       position: absolute;
+       right: 0.4em;
+       top: 0;
+       width: 0.9375em;
+       height: 100%;
+       background-repeat: no-repeat;
+}
+.oo-ui-capsuleItemWidget.oo-ui-indicatorElement > .oo-ui-indicator-clear {
+       cursor: pointer;
+}
+.oo-ui-capsuleItemWidget.oo-ui-widget-disabled {
+       color: #cccccc;
+       text-shadow: 0 1px 1px #ffffff;
+       border-color: #dddddd;
+       background-color: #f3f3f3;
+}
+.oo-ui-capsuleItemWidget.oo-ui-widget-disabled > .oo-ui-indicatorElement-indicator {
+       opacity: 0.2;
+}
+.oo-ui-searchWidget-query {
+       position: absolute;
+       top: 0;
+       left: 0;
+       right: 0;
+}
+.oo-ui-searchWidget-query .oo-ui-textInputWidget {
+       width: 100%;
+}
+.oo-ui-searchWidget-results {
+       position: absolute;
+       bottom: 0;
+       left: 0;
+       right: 0;
+       overflow-x: hidden;
+       overflow-y: auto;
+}
+.oo-ui-searchWidget-query {
+       height: 4em;
+       padding: 0 1em;
+       border-bottom: 1px solid #cccccc;
+}
+.oo-ui-searchWidget-query .oo-ui-textInputWidget {
+       margin: 0.75em 0;
+}
+.oo-ui-searchWidget-results {
+       top: 4em;
+       padding: 1em;
+       line-height: 0;
+}
+.oo-ui-numberInputWidget {
+       display: inline-block;
+       position: relative;
+       max-width: 50em;
+}
+.oo-ui-numberInputWidget-field {
+       display: table;
+       table-layout: fixed;
+       width: 100%;
+}
+.oo-ui-numberInputWidget-field > .oo-ui-buttonWidget,
+.oo-ui-numberInputWidget-field > .oo-ui-textInputWidget {
+       display: table-cell;
+       vertical-align: middle;
+}
+.oo-ui-numberInputWidget-field > .oo-ui-textInputWidget {
+       width: 100%;
+}
+.oo-ui-numberInputWidget-field > .oo-ui-buttonWidget {
+       white-space: nowrap;
+}
+.oo-ui-numberInputWidget-field > .oo-ui-buttonWidget > .oo-ui-buttonElement-button {
+       -webkit-box-sizing: border-box;
+          -moz-box-sizing: border-box;
+               box-sizing: border-box;
+}
+.oo-ui-numberInputWidget-field > .oo-ui-buttonWidget {
+       width: 2.5em;
+}
+.oo-ui-numberInputWidget-minusButton.oo-ui-buttonElement-framed.oo-ui-widget-enabled > .oo-ui-buttonElement-button {
+       border-top-right-radius: 0;
+       border-bottom-right-radius: 0;
+       border-right-width: 0;
+}
+.oo-ui-numberInputWidget-plusButton.oo-ui-buttonElement-framed.oo-ui-widget-enabled > .oo-ui-buttonElement-button {
+       border-top-left-radius: 0;
+       border-bottom-left-radius: 0;
+       border-left-width: 0;
+}
+.oo-ui-numberInputWidget .oo-ui-textInputWidget input {
+       border-radius: 0;
+}
diff --git a/resources/lib/oojs-ui/oojs-ui-widgets.js b/resources/lib/oojs-ui/oojs-ui-widgets.js
new file mode 100644 (file)
index 0000000..521dfbb
--- /dev/null
@@ -0,0 +1,5324 @@
+/*!
+ * OOjs UI v0.15.3
+ * https://www.mediawiki.org/wiki/OOjs_UI
+ *
+ * Copyright 2011–2016 OOjs UI Team and other contributors.
+ * Released under the MIT license
+ * http://oojs.mit-license.org
+ *
+ * Date: 2016-02-09T21:21:16Z
+ */
+( function ( OO ) {
+
+'use strict';
+
+/**
+ * DraggableElement is a mixin class used to create elements that can be clicked
+ * and dragged by a mouse to a new position within a group. This class must be used
+ * in conjunction with OO.ui.mixin.DraggableGroupElement, which provides a container for
+ * the draggable elements.
+ *
+ * @abstract
+ * @class
+ *
+ * @constructor
+ */
+OO.ui.mixin.DraggableElement = function OoUiMixinDraggableElement() {
+       // Properties
+       this.index = null;
+
+       // Initialize and events
+       this.$element
+               .attr( 'draggable', true )
+               .addClass( 'oo-ui-draggableElement' )
+               .on( {
+                       dragstart: this.onDragStart.bind( this ),
+                       dragover: this.onDragOver.bind( this ),
+                       dragend: this.onDragEnd.bind( this ),
+                       drop: this.onDrop.bind( this )
+               } );
+};
+
+OO.initClass( OO.ui.mixin.DraggableElement );
+
+/* Events */
+
+/**
+ * @event dragstart
+ *
+ * A dragstart event is emitted when the user clicks and begins dragging an item.
+ * @param {OO.ui.mixin.DraggableElement} item The item the user has clicked and is dragging with the mouse.
+ */
+
+/**
+ * @event dragend
+ * A dragend event is emitted when the user drags an item and releases the mouse,
+ * thus terminating the drag operation.
+ */
+
+/**
+ * @event drop
+ * A drop event is emitted when the user drags an item and then releases the mouse button
+ * over a valid target.
+ */
+
+/* Static Properties */
+
+/**
+ * @inheritdoc OO.ui.mixin.ButtonElement
+ */
+OO.ui.mixin.DraggableElement.static.cancelButtonMouseDownEvents = false;
+
+/* Methods */
+
+/**
+ * Respond to dragstart event.
+ *
+ * @private
+ * @param {jQuery.Event} event jQuery event
+ * @fires dragstart
+ */
+OO.ui.mixin.DraggableElement.prototype.onDragStart = function ( e ) {
+       var dataTransfer = e.originalEvent.dataTransfer;
+       // Define drop effect
+       dataTransfer.dropEffect = 'none';
+       dataTransfer.effectAllowed = 'move';
+       // Support: Firefox
+       // We must set up a dataTransfer data property or Firefox seems to
+       // ignore the fact the element is draggable.
+       try {
+               dataTransfer.setData( 'application-x/OOjs-UI-draggable', this.getIndex() );
+       } catch ( err ) {
+               // The above is only for Firefox. Move on if it fails.
+       }
+       // Add dragging class
+       this.$element.addClass( 'oo-ui-draggableElement-dragging' );
+       // Emit event
+       this.emit( 'dragstart', this );
+       return true;
+};
+
+/**
+ * Respond to dragend event.
+ *
+ * @private
+ * @fires dragend
+ */
+OO.ui.mixin.DraggableElement.prototype.onDragEnd = function () {
+       this.$element.removeClass( 'oo-ui-draggableElement-dragging' );
+       this.emit( 'dragend' );
+};
+
+/**
+ * Handle drop event.
+ *
+ * @private
+ * @param {jQuery.Event} event jQuery event
+ * @fires drop
+ */
+OO.ui.mixin.DraggableElement.prototype.onDrop = function ( e ) {
+       e.preventDefault();
+       this.emit( 'drop', e );
+};
+
+/**
+ * In order for drag/drop to work, the dragover event must
+ * return false and stop propogation.
+ *
+ * @private
+ */
+OO.ui.mixin.DraggableElement.prototype.onDragOver = function ( e ) {
+       e.preventDefault();
+};
+
+/**
+ * Set item index.
+ * Store it in the DOM so we can access from the widget drag event
+ *
+ * @private
+ * @param {number} Item index
+ */
+OO.ui.mixin.DraggableElement.prototype.setIndex = function ( index ) {
+       if ( this.index !== index ) {
+               this.index = index;
+               this.$element.data( 'index', index );
+       }
+};
+
+/**
+ * Get item index
+ *
+ * @private
+ * @return {number} Item index
+ */
+OO.ui.mixin.DraggableElement.prototype.getIndex = function () {
+       return this.index;
+};
+
+/**
+ * DraggableGroupElement is a mixin class used to create a group element to
+ * contain draggable elements, which are items that can be clicked and dragged by a mouse.
+ * The class is used with OO.ui.mixin.DraggableElement.
+ *
+ * @abstract
+ * @class
+ * @mixins OO.ui.mixin.GroupElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {string} [orientation] Item orientation: 'horizontal' or 'vertical'. The orientation
+ *  should match the layout of the items. Items displayed in a single row
+ *  or in several rows should use horizontal orientation. The vertical orientation should only be
+ *  used when the items are displayed in a single column. Defaults to 'vertical'
+ */
+OO.ui.mixin.DraggableGroupElement = function OoUiMixinDraggableGroupElement( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Parent constructor
+       OO.ui.mixin.GroupElement.call( this, config );
+
+       // Properties
+       this.orientation = config.orientation || 'vertical';
+       this.dragItem = null;
+       this.itemDragOver = null;
+       this.itemKeys = {};
+       this.sideInsertion = '';
+
+       // Events
+       this.aggregate( {
+               dragstart: 'itemDragStart',
+               dragend: 'itemDragEnd',
+               drop: 'itemDrop'
+       } );
+       this.connect( this, {
+               itemDragStart: 'onItemDragStart',
+               itemDrop: 'onItemDrop',
+               itemDragEnd: 'onItemDragEnd'
+       } );
+       this.$element.on( {
+               dragover: this.onDragOver.bind( this ),
+               dragleave: this.onDragLeave.bind( this )
+       } );
+
+       // Initialize
+       if ( Array.isArray( config.items ) ) {
+               this.addItems( config.items );
+       }
+       this.$placeholder = $( '<div>' )
+               .addClass( 'oo-ui-draggableGroupElement-placeholder' );
+       this.$element
+               .addClass( 'oo-ui-draggableGroupElement' )
+               .append( this.$status )
+               .toggleClass( 'oo-ui-draggableGroupElement-horizontal', this.orientation === 'horizontal' )
+               .prepend( this.$placeholder );
+};
+
+/* Setup */
+OO.mixinClass( OO.ui.mixin.DraggableGroupElement, OO.ui.mixin.GroupElement );
+
+/* Events */
+
+/**
+ * A 'reorder' event is emitted when the order of items in the group changes.
+ *
+ * @event reorder
+ * @param {OO.ui.mixin.DraggableElement} item Reordered item
+ * @param {number} [newIndex] New index for the item
+ */
+
+/* Methods */
+
+/**
+ * Respond to item drag start event
+ *
+ * @private
+ * @param {OO.ui.mixin.DraggableElement} item Dragged item
+ */
+OO.ui.mixin.DraggableGroupElement.prototype.onItemDragStart = function ( item ) {
+       var i, len;
+
+       // Map the index of each object
+       for ( i = 0, len = this.items.length; i < len; i++ ) {
+               this.items[ i ].setIndex( i );
+       }
+
+       if ( this.orientation === 'horizontal' ) {
+               // Set the height of the indicator
+               this.$placeholder.css( {
+                       height: item.$element.outerHeight(),
+                       width: 2
+               } );
+       } else {
+               // Set the width of the indicator
+               this.$placeholder.css( {
+                       height: 2,
+                       width: item.$element.outerWidth()
+               } );
+       }
+       this.setDragItem( item );
+};
+
+/**
+ * Respond to item drag end event
+ *
+ * @private
+ */
+OO.ui.mixin.DraggableGroupElement.prototype.onItemDragEnd = function () {
+       this.unsetDragItem();
+       return false;
+};
+
+/**
+ * Handle drop event and switch the order of the items accordingly
+ *
+ * @private
+ * @param {OO.ui.mixin.DraggableElement} item Dropped item
+ * @fires reorder
+ */
+OO.ui.mixin.DraggableGroupElement.prototype.onItemDrop = function ( item ) {
+       var toIndex = item.getIndex();
+       // Check if the dropped item is from the current group
+       // TODO: Figure out a way to configure a list of legally droppable
+       // elements even if they are not yet in the list
+       if ( this.getDragItem() ) {
+               // If the insertion point is 'after', the insertion index
+               // is shifted to the right (or to the left in RTL, hence 'after')
+               if ( this.sideInsertion === 'after' ) {
+                       toIndex++;
+               }
+               // Emit change event
+               this.emit( 'reorder', this.getDragItem(), toIndex );
+       }
+       this.unsetDragItem();
+       // Return false to prevent propogation
+       return false;
+};
+
+/**
+ * Handle dragleave event.
+ *
+ * @private
+ */
+OO.ui.mixin.DraggableGroupElement.prototype.onDragLeave = function () {
+       // This means the item was dragged outside the widget
+       this.$placeholder
+               .css( 'left', 0 )
+               .addClass( 'oo-ui-element-hidden' );
+};
+
+/**
+ * Respond to dragover event
+ *
+ * @private
+ * @param {jQuery.Event} event Event details
+ */
+OO.ui.mixin.DraggableGroupElement.prototype.onDragOver = function ( e ) {
+       var dragOverObj, $optionWidget, itemOffset, itemMidpoint, itemBoundingRect,
+               itemSize, cssOutput, dragPosition, itemIndex, itemPosition,
+               clientX = e.originalEvent.clientX,
+               clientY = e.originalEvent.clientY;
+
+       // Get the OptionWidget item we are dragging over
+       dragOverObj = this.getElementDocument().elementFromPoint( clientX, clientY );
+       $optionWidget = $( dragOverObj ).closest( '.oo-ui-draggableElement' );
+       if ( $optionWidget[ 0 ] ) {
+               itemOffset = $optionWidget.offset();
+               itemBoundingRect = $optionWidget[ 0 ].getBoundingClientRect();
+               itemPosition = $optionWidget.position();
+               itemIndex = $optionWidget.data( 'index' );
+       }
+
+       if (
+               itemOffset &&
+               this.isDragging() &&
+               itemIndex !== this.getDragItem().getIndex()
+       ) {
+               if ( this.orientation === 'horizontal' ) {
+                       // Calculate where the mouse is relative to the item width
+                       itemSize = itemBoundingRect.width;
+                       itemMidpoint = itemBoundingRect.left + itemSize / 2;
+                       dragPosition = clientX;
+                       // Which side of the item we hover over will dictate
+                       // where the placeholder will appear, on the left or
+                       // on the right
+                       cssOutput = {
+                               left: dragPosition < itemMidpoint ? itemPosition.left : itemPosition.left + itemSize,
+                               top: itemPosition.top
+                       };
+               } else {
+                       // Calculate where the mouse is relative to the item height
+                       itemSize = itemBoundingRect.height;
+                       itemMidpoint = itemBoundingRect.top + itemSize / 2;
+                       dragPosition = clientY;
+                       // Which side of the item we hover over will dictate
+                       // where the placeholder will appear, on the top or
+                       // on the bottom
+                       cssOutput = {
+                               top: dragPosition < itemMidpoint ? itemPosition.top : itemPosition.top + itemSize,
+                               left: itemPosition.left
+                       };
+               }
+               // Store whether we are before or after an item to rearrange
+               // For horizontal layout, we need to account for RTL, as this is flipped
+               if (  this.orientation === 'horizontal' && this.$element.css( 'direction' ) === 'rtl' ) {
+                       this.sideInsertion = dragPosition < itemMidpoint ? 'after' : 'before';
+               } else {
+                       this.sideInsertion = dragPosition < itemMidpoint ? 'before' : 'after';
+               }
+               // Add drop indicator between objects
+               this.$placeholder
+                       .css( cssOutput )
+                       .removeClass( 'oo-ui-element-hidden' );
+       } else {
+               // This means the item was dragged outside the widget
+               this.$placeholder
+                       .css( 'left', 0 )
+                       .addClass( 'oo-ui-element-hidden' );
+       }
+       // Prevent default
+       e.preventDefault();
+};
+
+/**
+ * Set a dragged item
+ *
+ * @param {OO.ui.mixin.DraggableElement} item Dragged item
+ */
+OO.ui.mixin.DraggableGroupElement.prototype.setDragItem = function ( item ) {
+       this.dragItem = item;
+};
+
+/**
+ * Unset the current dragged item
+ */
+OO.ui.mixin.DraggableGroupElement.prototype.unsetDragItem = function () {
+       this.dragItem = null;
+       this.itemDragOver = null;
+       this.$placeholder.addClass( 'oo-ui-element-hidden' );
+       this.sideInsertion = '';
+};
+
+/**
+ * Get the item that is currently being dragged.
+ *
+ * @return {OO.ui.mixin.DraggableElement|null} The currently dragged item, or `null` if no item is being dragged
+ */
+OO.ui.mixin.DraggableGroupElement.prototype.getDragItem = function () {
+       return this.dragItem;
+};
+
+/**
+ * Check if an item in the group is currently being dragged.
+ *
+ * @return {Boolean} Item is being dragged
+ */
+OO.ui.mixin.DraggableGroupElement.prototype.isDragging = function () {
+       return this.getDragItem() !== null;
+};
+
+/**
+ * RequestManager is a mixin that manages the lifecycle of a promise-backed request for a widget, such as
+ * the {@link OO.ui.mixin.LookupElement}.
+ *
+ * @class
+ * @abstract
+ *
+ * @constructor
+ */
+OO.ui.mixin.RequestManager = function OoUiMixinRequestManager() {
+       this.requestCache = {};
+       this.requestQuery = null;
+       this.requestRequest = null;
+};
+
+/* Setup */
+
+OO.initClass( OO.ui.mixin.RequestManager );
+
+/**
+ * Get request results for the current query.
+ *
+ * @return {jQuery.Promise} Promise object which will be passed response data as the first argument of
+ *   the done event. If the request was aborted to make way for a subsequent request, this promise
+ *   may not be rejected, depending on what jQuery feels like doing.
+ */
+OO.ui.mixin.RequestManager.prototype.getRequestData = function () {
+       var widget = this,
+               value = this.getRequestQuery(),
+               deferred = $.Deferred(),
+               ourRequest;
+
+       this.abortRequest();
+       if ( Object.prototype.hasOwnProperty.call( this.requestCache, value ) ) {
+               deferred.resolve( this.requestCache[ value ] );
+       } else {
+               if ( this.pushPending ) {
+                       this.pushPending();
+               }
+               this.requestQuery = value;
+               ourRequest = this.requestRequest = this.getRequest();
+               ourRequest
+                       .always( function () {
+                               // We need to pop pending even if this is an old request, otherwise
+                               // the widget will remain pending forever.
+                               // TODO: this assumes that an aborted request will fail or succeed soon after
+                               // being aborted, or at least eventually. It would be nice if we could popPending()
+                               // at abort time, but only if we knew that we hadn't already called popPending()
+                               // for that request.
+                               if ( widget.popPending ) {
+                                       widget.popPending();
+                               }
+                       } )
+                       .done( function ( response ) {
+                               // If this is an old request (and aborting it somehow caused it to still succeed),
+                               // ignore its success completely
+                               if ( ourRequest === widget.requestRequest ) {
+                                       widget.requestQuery = null;
+                                       widget.requestRequest = null;
+                                       widget.requestCache[ value ] = widget.getRequestCacheDataFromResponse( response );
+                                       deferred.resolve( widget.requestCache[ value ] );
+                               }
+                       } )
+                       .fail( function () {
+                               // If this is an old request (or a request failing because it's being aborted),
+                               // ignore its failure completely
+                               if ( ourRequest === widget.requestRequest ) {
+                                       widget.requestQuery = null;
+                                       widget.requestRequest = null;
+                                       deferred.reject();
+                               }
+                       } );
+       }
+       return deferred.promise();
+};
+
+/**
+ * Abort the currently pending request, if any.
+ *
+ * @private
+ */
+OO.ui.mixin.RequestManager.prototype.abortRequest = function () {
+       var oldRequest = this.requestRequest;
+       if ( oldRequest ) {
+               // First unset this.requestRequest to the fail handler will notice
+               // that the request is no longer current
+               this.requestRequest = null;
+               this.requestQuery = null;
+               oldRequest.abort();
+       }
+};
+
+/**
+ * Get the query to be made.
+ *
+ * @protected
+ * @method
+ * @abstract
+ * @return {string} query to be used
+ */
+OO.ui.mixin.RequestManager.prototype.getRequestQuery = null;
+
+/**
+ * Get a new request object of the current query value.
+ *
+ * @protected
+ * @method
+ * @abstract
+ * @return {jQuery.Promise} jQuery AJAX object, or promise object with an .abort() method
+ */
+OO.ui.mixin.RequestManager.prototype.getRequest = null;
+
+/**
+ * Pre-process data returned by the request from #getRequest.
+ *
+ * The return value of this function will be cached, and any further queries for the given value
+ * will use the cache rather than doing API requests.
+ *
+ * @protected
+ * @method
+ * @abstract
+ * @param {Mixed} response Response from server
+ * @return {Mixed} Cached result data
+ */
+OO.ui.mixin.RequestManager.prototype.getRequestCacheDataFromResponse = null;
+
+/**
+ * LookupElement is a mixin that creates a {@link OO.ui.FloatingMenuSelectWidget menu} of suggested values for
+ * a {@link OO.ui.TextInputWidget text input widget}. Suggested values are based on the characters the user types
+ * into the text input field and, in general, the menu is only displayed when the user types. If a suggested value is chosen
+ * from the lookup menu, that value becomes the value of the input field.
+ *
+ * Note that a new menu of suggested items is displayed when a value is chosen from the lookup menu. If this is
+ * not the desired behavior, disable lookup menus with the #setLookupsDisabled method, then set the value, then
+ * re-enable lookups.
+ *
+ * See the [OOjs UI demos][1] for an example.
+ *
+ * [1]: https://tools.wmflabs.org/oojs-ui/oojs-ui/demos/index.html#widgets-apex-vector-ltr
+ *
+ * @class
+ * @abstract
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {jQuery} [$overlay] Overlay for the lookup menu; defaults to relative positioning
+ * @cfg {jQuery} [$container=this.$element] The container element. The lookup menu is rendered beneath the specified element.
+ * @cfg {boolean} [allowSuggestionsWhenEmpty=false] Request and display a lookup menu when the text input is empty.
+ *  By default, the lookup menu is not generated and displayed until the user begins to type.
+ * @cfg {boolean} [highlightFirst=true] Whether the first lookup result should be highlighted (so, that the user can
+ *  take it over into the input with simply pressing return) automatically or not.
+ */
+OO.ui.mixin.LookupElement = function OoUiMixinLookupElement( config ) {
+       // Configuration initialization
+       config = $.extend( { highlightFirst: true }, config );
+
+       // Mixin constructors
+       OO.ui.mixin.RequestManager.call( this, config );
+
+       // Properties
+       this.$overlay = config.$overlay || this.$element;
+       this.lookupMenu = new OO.ui.FloatingMenuSelectWidget( {
+               widget: this,
+               input: this,
+               $container: config.$container || this.$element
+       } );
+
+       this.allowSuggestionsWhenEmpty = config.allowSuggestionsWhenEmpty || false;
+
+       this.lookupsDisabled = false;
+       this.lookupInputFocused = false;
+       this.lookupHighlightFirstItem = config.highlightFirst;
+
+       // Events
+       this.$input.on( {
+               focus: this.onLookupInputFocus.bind( this ),
+               blur: this.onLookupInputBlur.bind( this ),
+               mousedown: this.onLookupInputMouseDown.bind( this )
+       } );
+       this.connect( this, { change: 'onLookupInputChange' } );
+       this.lookupMenu.connect( this, {
+               toggle: 'onLookupMenuToggle',
+               choose: 'onLookupMenuItemChoose'
+       } );
+
+       // Initialization
+       this.$element.addClass( 'oo-ui-lookupElement' );
+       this.lookupMenu.$element.addClass( 'oo-ui-lookupElement-menu' );
+       this.$overlay.append( this.lookupMenu.$element );
+};
+
+/* Setup */
+
+OO.mixinClass( OO.ui.mixin.LookupElement, OO.ui.mixin.RequestManager );
+
+/* Methods */
+
+/**
+ * Handle input focus event.
+ *
+ * @protected
+ * @param {jQuery.Event} e Input focus event
+ */
+OO.ui.mixin.LookupElement.prototype.onLookupInputFocus = function () {
+       this.lookupInputFocused = true;
+       this.populateLookupMenu();
+};
+
+/**
+ * Handle input blur event.
+ *
+ * @protected
+ * @param {jQuery.Event} e Input blur event
+ */
+OO.ui.mixin.LookupElement.prototype.onLookupInputBlur = function () {
+       this.closeLookupMenu();
+       this.lookupInputFocused = false;
+};
+
+/**
+ * Handle input mouse down event.
+ *
+ * @protected
+ * @param {jQuery.Event} e Input mouse down event
+ */
+OO.ui.mixin.LookupElement.prototype.onLookupInputMouseDown = function () {
+       // Only open the menu if the input was already focused.
+       // This way we allow the user to open the menu again after closing it with Esc
+       // by clicking in the input. Opening (and populating) the menu when initially
+       // clicking into the input is handled by the focus handler.
+       if ( this.lookupInputFocused && !this.lookupMenu.isVisible() ) {
+               this.populateLookupMenu();
+       }
+};
+
+/**
+ * Handle input change event.
+ *
+ * @protected
+ * @param {string} value New input value
+ */
+OO.ui.mixin.LookupElement.prototype.onLookupInputChange = function () {
+       if ( this.lookupInputFocused ) {
+               this.populateLookupMenu();
+       }
+};
+
+/**
+ * Handle the lookup menu being shown/hidden.
+ *
+ * @protected
+ * @param {boolean} visible Whether the lookup menu is now visible.
+ */
+OO.ui.mixin.LookupElement.prototype.onLookupMenuToggle = function ( visible ) {
+       if ( !visible ) {
+               // When the menu is hidden, abort any active request and clear the menu.
+               // This has to be done here in addition to closeLookupMenu(), because
+               // MenuSelectWidget will close itself when the user presses Esc.
+               this.abortLookupRequest();
+               this.lookupMenu.clearItems();
+       }
+};
+
+/**
+ * Handle menu item 'choose' event, updating the text input value to the value of the clicked item.
+ *
+ * @protected
+ * @param {OO.ui.MenuOptionWidget} item Selected item
+ */
+OO.ui.mixin.LookupElement.prototype.onLookupMenuItemChoose = function ( item ) {
+       this.setValue( item.getData() );
+};
+
+/**
+ * Get lookup menu.
+ *
+ * @private
+ * @return {OO.ui.FloatingMenuSelectWidget}
+ */
+OO.ui.mixin.LookupElement.prototype.getLookupMenu = function () {
+       return this.lookupMenu;
+};
+
+/**
+ * Disable or re-enable lookups.
+ *
+ * When lookups are disabled, calls to #populateLookupMenu will be ignored.
+ *
+ * @param {boolean} disabled Disable lookups
+ */
+OO.ui.mixin.LookupElement.prototype.setLookupsDisabled = function ( disabled ) {
+       this.lookupsDisabled = !!disabled;
+};
+
+/**
+ * Open the menu. If there are no entries in the menu, this does nothing.
+ *
+ * @private
+ * @chainable
+ */
+OO.ui.mixin.LookupElement.prototype.openLookupMenu = function () {
+       if ( !this.lookupMenu.isEmpty() ) {
+               this.lookupMenu.toggle( true );
+       }
+       return this;
+};
+
+/**
+ * Close the menu, empty it, and abort any pending request.
+ *
+ * @private
+ * @chainable
+ */
+OO.ui.mixin.LookupElement.prototype.closeLookupMenu = function () {
+       this.lookupMenu.toggle( false );
+       this.abortLookupRequest();
+       this.lookupMenu.clearItems();
+       return this;
+};
+
+/**
+ * Request menu items based on the input's current value, and when they arrive,
+ * populate the menu with these items and show the menu.
+ *
+ * If lookups have been disabled with #setLookupsDisabled, this function does nothing.
+ *
+ * @private
+ * @chainable
+ */
+OO.ui.mixin.LookupElement.prototype.populateLookupMenu = function () {
+       var widget = this,
+               value = this.getValue();
+
+       if ( this.lookupsDisabled || this.isReadOnly() ) {
+               return;
+       }
+
+       // If the input is empty, clear the menu, unless suggestions when empty are allowed.
+       if ( !this.allowSuggestionsWhenEmpty && value === '' ) {
+               this.closeLookupMenu();
+       // Skip population if there is already a request pending for the current value
+       } else if ( value !== this.lookupQuery ) {
+               this.getLookupMenuItems()
+                       .done( function ( items ) {
+                               widget.lookupMenu.clearItems();
+                               if ( items.length ) {
+                                       widget.lookupMenu
+                                               .addItems( items )
+                                               .toggle( true );
+                                       widget.initializeLookupMenuSelection();
+                               } else {
+                                       widget.lookupMenu.toggle( false );
+                               }
+                       } )
+                       .fail( function () {
+                               widget.lookupMenu.clearItems();
+                       } );
+       }
+
+       return this;
+};
+
+/**
+ * Highlight the first selectable item in the menu, if configured.
+ *
+ * @private
+ * @chainable
+ */
+OO.ui.mixin.LookupElement.prototype.initializeLookupMenuSelection = function () {
+       if ( this.lookupHighlightFirstItem && !this.lookupMenu.getSelectedItem() ) {
+               this.lookupMenu.highlightItem( this.lookupMenu.getFirstSelectableItem() );
+       }
+};
+
+/**
+ * Get lookup menu items for the current query.
+ *
+ * @private
+ * @return {jQuery.Promise} Promise object which will be passed menu items as the first argument of
+ *   the done event. If the request was aborted to make way for a subsequent request, this promise
+ *   will not be rejected: it will remain pending forever.
+ */
+OO.ui.mixin.LookupElement.prototype.getLookupMenuItems = function () {
+       return this.getRequestData().then( function ( data ) {
+               return this.getLookupMenuOptionsFromData( data );
+       }.bind( this ) );
+};
+
+/**
+ * Abort the currently pending lookup request, if any.
+ *
+ * @private
+ */
+OO.ui.mixin.LookupElement.prototype.abortLookupRequest = function () {
+       this.abortRequest();
+};
+
+/**
+ * Get a new request object of the current lookup query value.
+ *
+ * @protected
+ * @method
+ * @abstract
+ * @return {jQuery.Promise} jQuery AJAX object, or promise object with an .abort() method
+ */
+OO.ui.mixin.LookupElement.prototype.getLookupRequest = null;
+
+/**
+ * Pre-process data returned by the request from #getLookupRequest.
+ *
+ * The return value of this function will be cached, and any further queries for the given value
+ * will use the cache rather than doing API requests.
+ *
+ * @protected
+ * @method
+ * @abstract
+ * @param {Mixed} response Response from server
+ * @return {Mixed} Cached result data
+ */
+OO.ui.mixin.LookupElement.prototype.getLookupCacheDataFromResponse = null;
+
+/**
+ * Get a list of menu option widgets from the (possibly cached) data returned by
+ * #getLookupCacheDataFromResponse.
+ *
+ * @protected
+ * @method
+ * @abstract
+ * @param {Mixed} data Cached result data, usually an array
+ * @return {OO.ui.MenuOptionWidget[]} Menu items
+ */
+OO.ui.mixin.LookupElement.prototype.getLookupMenuOptionsFromData = null;
+
+/**
+ * Set the read-only state of the widget.
+ *
+ * This will also disable/enable the lookups functionality.
+ *
+ * @param {boolean} readOnly Make input read-only
+ * @chainable
+ */
+OO.ui.mixin.LookupElement.prototype.setReadOnly = function ( readOnly ) {
+       // Parent method
+       // Note: Calling #setReadOnly this way assumes this is mixed into an OO.ui.TextInputWidget
+       OO.ui.TextInputWidget.prototype.setReadOnly.call( this, readOnly );
+
+       // During construction, #setReadOnly is called before the OO.ui.mixin.LookupElement constructor
+       if ( this.isReadOnly() && this.lookupMenu ) {
+               this.closeLookupMenu();
+       }
+
+       return this;
+};
+
+/**
+ * @inheritdoc OO.ui.mixin.RequestManager
+ */
+OO.ui.mixin.LookupElement.prototype.getRequestQuery = function () {
+       return this.getValue();
+};
+
+/**
+ * @inheritdoc OO.ui.mixin.RequestManager
+ */
+OO.ui.mixin.LookupElement.prototype.getRequest = function () {
+       return this.getLookupRequest();
+};
+
+/**
+ * @inheritdoc OO.ui.mixin.RequestManager
+ */
+OO.ui.mixin.LookupElement.prototype.getRequestCacheDataFromResponse = function ( response ) {
+       return this.getLookupCacheDataFromResponse( response );
+};
+
+/**
+ * CardLayouts are used within {@link OO.ui.IndexLayout index layouts} to create cards that users can select and display
+ * from the index's optional {@link OO.ui.TabSelectWidget tab} navigation. Cards are usually not instantiated directly,
+ * rather extended to include the required content and functionality.
+ *
+ * Each card must have a unique symbolic name, which is passed to the constructor. In addition, the card's tab
+ * item is customized (with a label) using the #setupTabItem method. See
+ * {@link OO.ui.IndexLayout IndexLayout} for an example.
+ *
+ * @class
+ * @extends OO.ui.PanelLayout
+ *
+ * @constructor
+ * @param {string} name Unique symbolic name of card
+ * @param {Object} [config] Configuration options
+ * @cfg {jQuery|string|Function|OO.ui.HtmlSnippet} [label] Label for card's tab
+ */
+OO.ui.CardLayout = function OoUiCardLayout( name, config ) {
+       // Allow passing positional parameters inside the config object
+       if ( OO.isPlainObject( name ) && config === undefined ) {
+               config = name;
+               name = config.name;
+       }
+
+       // Configuration initialization
+       config = $.extend( { scrollable: true }, config );
+
+       // Parent constructor
+       OO.ui.CardLayout.parent.call( this, config );
+
+       // Properties
+       this.name = name;
+       this.label = config.label;
+       this.tabItem = null;
+       this.active = false;
+
+       // Initialization
+       this.$element.addClass( 'oo-ui-cardLayout' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.CardLayout, OO.ui.PanelLayout );
+
+/* Events */
+
+/**
+ * An 'active' event is emitted when the card becomes active. Cards become active when they are
+ * shown in a index layout that is configured to display only one card at a time.
+ *
+ * @event active
+ * @param {boolean} active Card is active
+ */
+
+/* Methods */
+
+/**
+ * Get the symbolic name of the card.
+ *
+ * @return {string} Symbolic name of card
+ */
+OO.ui.CardLayout.prototype.getName = function () {
+       return this.name;
+};
+
+/**
+ * Check if card is active.
+ *
+ * Cards become active when they are shown in a {@link OO.ui.IndexLayout index layout} that is configured to display
+ * only one card at a time. Additional CSS is applied to the card's tab item to reflect the active state.
+ *
+ * @return {boolean} Card is active
+ */
+OO.ui.CardLayout.prototype.isActive = function () {
+       return this.active;
+};
+
+/**
+ * Get tab item.
+ *
+ * The tab item allows users to access the card from the index's tab
+ * navigation. The tab item itself can be customized (with a label, level, etc.) using the #setupTabItem method.
+ *
+ * @return {OO.ui.TabOptionWidget|null} Tab option widget
+ */
+OO.ui.CardLayout.prototype.getTabItem = function () {
+       return this.tabItem;
+};
+
+/**
+ * Set or unset the tab item.
+ *
+ * Specify a {@link OO.ui.TabOptionWidget tab option} to set it,
+ * or `null` to clear the tab item. To customize the tab item itself (e.g., to set a label or tab
+ * level), use #setupTabItem instead of this method.
+ *
+ * @param {OO.ui.TabOptionWidget|null} tabItem Tab option widget, null to clear
+ * @chainable
+ */
+OO.ui.CardLayout.prototype.setTabItem = function ( tabItem ) {
+       this.tabItem = tabItem || null;
+       if ( tabItem ) {
+               this.setupTabItem();
+       }
+       return this;
+};
+
+/**
+ * Set up the tab item.
+ *
+ * Use this method to customize the tab item (e.g., to add a label or tab level). To set or unset
+ * the tab item itself (with a {@link OO.ui.TabOptionWidget tab option} or `null`), use
+ * the #setTabItem method instead.
+ *
+ * @param {OO.ui.TabOptionWidget} tabItem Tab option widget to set up
+ * @chainable
+ */
+OO.ui.CardLayout.prototype.setupTabItem = function () {
+       if ( this.label ) {
+               this.tabItem.setLabel( this.label );
+       }
+       return this;
+};
+
+/**
+ * Set the card to its 'active' state.
+ *
+ * Cards become active when they are shown in a index layout that is configured to display only one card at a time. Additional
+ * CSS is applied to the tab item to reflect the card's active state. Outside of the index
+ * context, setting the active state on a card does nothing.
+ *
+ * @param {boolean} value Card is active
+ * @fires active
+ */
+OO.ui.CardLayout.prototype.setActive = function ( active ) {
+       active = !!active;
+
+       if ( active !== this.active ) {
+               this.active = active;
+               this.$element.toggleClass( 'oo-ui-cardLayout-active', this.active );
+               this.emit( 'active', this.active );
+       }
+};
+
+/**
+ * PageLayouts are used within {@link OO.ui.BookletLayout booklet layouts} to create pages that users can select and display
+ * from the booklet's optional {@link OO.ui.OutlineSelectWidget outline} navigation. Pages are usually not instantiated directly,
+ * rather extended to include the required content and functionality.
+ *
+ * Each page must have a unique symbolic name, which is passed to the constructor. In addition, the page's outline
+ * item is customized (with a label, outline level, etc.) using the #setupOutlineItem method. See
+ * {@link OO.ui.BookletLayout BookletLayout} for an example.
+ *
+ * @class
+ * @extends OO.ui.PanelLayout
+ *
+ * @constructor
+ * @param {string} name Unique symbolic name of page
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.PageLayout = function OoUiPageLayout( name, config ) {
+       // Allow passing positional parameters inside the config object
+       if ( OO.isPlainObject( name ) && config === undefined ) {
+               config = name;
+               name = config.name;
+       }
+
+       // Configuration initialization
+       config = $.extend( { scrollable: true }, config );
+
+       // Parent constructor
+       OO.ui.PageLayout.parent.call( this, config );
+
+       // Properties
+       this.name = name;
+       this.outlineItem = null;
+       this.active = false;
+
+       // Initialization
+       this.$element.addClass( 'oo-ui-pageLayout' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.PageLayout, OO.ui.PanelLayout );
+
+/* Events */
+
+/**
+ * An 'active' event is emitted when the page becomes active. Pages become active when they are
+ * shown in a booklet layout that is configured to display only one page at a time.
+ *
+ * @event active
+ * @param {boolean} active Page is active
+ */
+
+/* Methods */
+
+/**
+ * Get the symbolic name of the page.
+ *
+ * @return {string} Symbolic name of page
+ */
+OO.ui.PageLayout.prototype.getName = function () {
+       return this.name;
+};
+
+/**
+ * Check if page is active.
+ *
+ * Pages become active when they are shown in a {@link OO.ui.BookletLayout booklet layout} that is configured to display
+ * only one page at a time. Additional CSS is applied to the page's outline item to reflect the active state.
+ *
+ * @return {boolean} Page is active
+ */
+OO.ui.PageLayout.prototype.isActive = function () {
+       return this.active;
+};
+
+/**
+ * Get outline item.
+ *
+ * The outline item allows users to access the page from the booklet's outline
+ * navigation. The outline item itself can be customized (with a label, level, etc.) using the #setupOutlineItem method.
+ *
+ * @return {OO.ui.OutlineOptionWidget|null} Outline option widget
+ */
+OO.ui.PageLayout.prototype.getOutlineItem = function () {
+       return this.outlineItem;
+};
+
+/**
+ * Set or unset the outline item.
+ *
+ * Specify an {@link OO.ui.OutlineOptionWidget outline option} to set it,
+ * or `null` to clear the outline item. To customize the outline item itself (e.g., to set a label or outline
+ * level), use #setupOutlineItem instead of this method.
+ *
+ * @param {OO.ui.OutlineOptionWidget|null} outlineItem Outline option widget, null to clear
+ * @chainable
+ */
+OO.ui.PageLayout.prototype.setOutlineItem = function ( outlineItem ) {
+       this.outlineItem = outlineItem || null;
+       if ( outlineItem ) {
+               this.setupOutlineItem();
+       }
+       return this;
+};
+
+/**
+ * Set up the outline item.
+ *
+ * Use this method to customize the outline item (e.g., to add a label or outline level). To set or unset
+ * the outline item itself (with an {@link OO.ui.OutlineOptionWidget outline option} or `null`), use
+ * the #setOutlineItem method instead.
+ *
+ * @param {OO.ui.OutlineOptionWidget} outlineItem Outline option widget to set up
+ * @chainable
+ */
+OO.ui.PageLayout.prototype.setupOutlineItem = function () {
+       return this;
+};
+
+/**
+ * Set the page to its 'active' state.
+ *
+ * Pages become active when they are shown in a booklet layout that is configured to display only one page at a time. Additional
+ * CSS is applied to the outline item to reflect the page's active state. Outside of the booklet
+ * context, setting the active state on a page does nothing.
+ *
+ * @param {boolean} value Page is active
+ * @fires active
+ */
+OO.ui.PageLayout.prototype.setActive = function ( active ) {
+       active = !!active;
+
+       if ( active !== this.active ) {
+               this.active = active;
+               this.$element.toggleClass( 'oo-ui-pageLayout-active', active );
+               this.emit( 'active', this.active );
+       }
+};
+
+/**
+ * StackLayouts contain a series of {@link OO.ui.PanelLayout panel layouts}. By default, only one panel is displayed
+ * at a time, though the stack layout can also be configured to show all contained panels, one after another,
+ * by setting the #continuous option to 'true'.
+ *
+ *     @example
+ *     // A stack layout with two panels, configured to be displayed continously
+ *     var myStack = new OO.ui.StackLayout( {
+ *         items: [
+ *             new OO.ui.PanelLayout( {
+ *                 $content: $( '<p>Panel One</p>' ),
+ *                 padded: true,
+ *                 framed: true
+ *             } ),
+ *             new OO.ui.PanelLayout( {
+ *                 $content: $( '<p>Panel Two</p>' ),
+ *                 padded: true,
+ *                 framed: true
+ *             } )
+ *         ],
+ *         continuous: true
+ *     } );
+ *     $( 'body' ).append( myStack.$element );
+ *
+ * @class
+ * @extends OO.ui.PanelLayout
+ * @mixins OO.ui.mixin.GroupElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {boolean} [continuous=false] Show all panels, one after another. By default, only one panel is displayed at a time.
+ * @cfg {OO.ui.Layout[]} [items] Panel layouts to add to the stack layout.
+ */
+OO.ui.StackLayout = function OoUiStackLayout( config ) {
+       // Configuration initialization
+       config = $.extend( { scrollable: true }, config );
+
+       // Parent constructor
+       OO.ui.StackLayout.parent.call( this, config );
+
+       // Mixin constructors
+       OO.ui.mixin.GroupElement.call( this, $.extend( {}, config, { $group: this.$element } ) );
+
+       // Properties
+       this.currentItem = null;
+       this.continuous = !!config.continuous;
+
+       // Initialization
+       this.$element.addClass( 'oo-ui-stackLayout' );
+       if ( this.continuous ) {
+               this.$element.addClass( 'oo-ui-stackLayout-continuous' );
+               this.$element.on( 'scroll', OO.ui.debounce( this.onScroll.bind( this ), 250 ) );
+       }
+       if ( Array.isArray( config.items ) ) {
+               this.addItems( config.items );
+       }
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.StackLayout, OO.ui.PanelLayout );
+OO.mixinClass( OO.ui.StackLayout, OO.ui.mixin.GroupElement );
+
+/* Events */
+
+/**
+ * A 'set' event is emitted when panels are {@link #addItems added}, {@link #removeItems removed},
+ * {@link #clearItems cleared} or {@link #setItem displayed}.
+ *
+ * @event set
+ * @param {OO.ui.Layout|null} item Current panel or `null` if no panel is shown
+ */
+
+/**
+ * When used in continuous mode, this event is emitted when the user scrolls down
+ * far enough such that currentItem is no longer visible.
+ *
+ * @event visibleItemChange
+ * @param {OO.ui.PanelLayout} panel The next visible item in the layout
+ */
+
+/* Methods */
+
+/**
+ * Handle scroll events from the layout element
+ *
+ * @param {jQuery.Event} e
+ * @fires visibleItemChange
+ */
+OO.ui.StackLayout.prototype.onScroll = function () {
+       var currentRect,
+               len = this.items.length,
+               currentIndex = this.items.indexOf( this.currentItem ),
+               newIndex = currentIndex,
+               containerRect = this.$element[ 0 ].getBoundingClientRect();
+
+       if ( !containerRect || ( !containerRect.top && !containerRect.bottom ) ) {
+               // Can't get bounding rect, possibly not attached.
+               return;
+       }
+
+       function getRect( item ) {
+               return item.$element[ 0 ].getBoundingClientRect();
+       }
+
+       function isVisible( item ) {
+               var rect = getRect( item );
+               return rect.bottom > containerRect.top && rect.top < containerRect.bottom;
+       }
+
+       currentRect = getRect( this.currentItem );
+
+       if ( currentRect.bottom < containerRect.top ) {
+               // Scrolled down past current item
+               while ( ++newIndex < len ) {
+                       if ( isVisible( this.items[ newIndex ] ) ) {
+                               break;
+                       }
+               }
+       } else if ( currentRect.top > containerRect.bottom ) {
+               // Scrolled up past current item
+               while ( --newIndex >= 0 ) {
+                       if ( isVisible( this.items[ newIndex ] ) ) {
+                               break;
+                       }
+               }
+       }
+
+       if ( newIndex !== currentIndex ) {
+               this.emit( 'visibleItemChange', this.items[ newIndex ] );
+       }
+};
+
+/**
+ * Get the current panel.
+ *
+ * @return {OO.ui.Layout|null}
+ */
+OO.ui.StackLayout.prototype.getCurrentItem = function () {
+       return this.currentItem;
+};
+
+/**
+ * Unset the current item.
+ *
+ * @private
+ * @param {OO.ui.StackLayout} layout
+ * @fires set
+ */
+OO.ui.StackLayout.prototype.unsetCurrentItem = function () {
+       var prevItem = this.currentItem;
+       if ( prevItem === null ) {
+               return;
+       }
+
+       this.currentItem = null;
+       this.emit( 'set', null );
+};
+
+/**
+ * Add panel layouts to the stack layout.
+ *
+ * Panels will be added to the end of the stack layout array unless the optional index parameter specifies a different
+ * insertion point. Adding a panel that is already in the stack will move it to the end of the array or the point specified
+ * by the index.
+ *
+ * @param {OO.ui.Layout[]} items Panels to add
+ * @param {number} [index] Index of the insertion point
+ * @chainable
+ */
+OO.ui.StackLayout.prototype.addItems = function ( items, index ) {
+       // Update the visibility
+       this.updateHiddenState( items, this.currentItem );
+
+       // Mixin method
+       OO.ui.mixin.GroupElement.prototype.addItems.call( this, items, index );
+
+       if ( !this.currentItem && items.length ) {
+               this.setItem( items[ 0 ] );
+       }
+
+       return this;
+};
+
+/**
+ * Remove the specified panels from the stack layout.
+ *
+ * Removed panels are detached from the DOM, not removed, so that they may be reused. To remove all panels,
+ * you may wish to use the #clearItems method instead.
+ *
+ * @param {OO.ui.Layout[]} items Panels to remove
+ * @chainable
+ * @fires set
+ */
+OO.ui.StackLayout.prototype.removeItems = function ( items ) {
+       // Mixin method
+       OO.ui.mixin.GroupElement.prototype.removeItems.call( this, items );
+
+       if ( items.indexOf( this.currentItem ) !== -1 ) {
+               if ( this.items.length ) {
+                       this.setItem( this.items[ 0 ] );
+               } else {
+                       this.unsetCurrentItem();
+               }
+       }
+
+       return this;
+};
+
+/**
+ * Clear all panels from the stack layout.
+ *
+ * Cleared panels are detached from the DOM, not removed, so that they may be reused. To remove only
+ * a subset of panels, use the #removeItems method.
+ *
+ * @chainable
+ * @fires set
+ */
+OO.ui.StackLayout.prototype.clearItems = function () {
+       this.unsetCurrentItem();
+       OO.ui.mixin.GroupElement.prototype.clearItems.call( this );
+
+       return this;
+};
+
+/**
+ * Show the specified panel.
+ *
+ * If another panel is currently displayed, it will be hidden.
+ *
+ * @param {OO.ui.Layout} item Panel to show
+ * @chainable
+ * @fires set
+ */
+OO.ui.StackLayout.prototype.setItem = function ( item ) {
+       if ( item !== this.currentItem ) {
+               this.updateHiddenState( this.items, item );
+
+               if ( this.items.indexOf( item ) !== -1 ) {
+                       this.currentItem = item;
+                       this.emit( 'set', item );
+               } else {
+                       this.unsetCurrentItem();
+               }
+       }
+
+       return this;
+};
+
+/**
+ * Update the visibility of all items in case of non-continuous view.
+ *
+ * Ensure all items are hidden except for the selected one.
+ * This method does nothing when the stack is continuous.
+ *
+ * @private
+ * @param {OO.ui.Layout[]} items Item list iterate over
+ * @param {OO.ui.Layout} [selectedItem] Selected item to show
+ */
+OO.ui.StackLayout.prototype.updateHiddenState = function ( items, selectedItem ) {
+       var i, len;
+
+       if ( !this.continuous ) {
+               for ( i = 0, len = items.length; i < len; i++ ) {
+                       if ( !selectedItem || selectedItem !== items[ i ] ) {
+                               items[ i ].$element.addClass( 'oo-ui-element-hidden' );
+                       }
+               }
+               if ( selectedItem ) {
+                       selectedItem.$element.removeClass( 'oo-ui-element-hidden' );
+               }
+       }
+};
+
+/**
+ * MenuLayouts combine a menu and a content {@link OO.ui.PanelLayout panel}. The menu is positioned relative to the content (after, before, top, or bottom)
+ * and its size is customized with the #menuSize config. The content area will fill all remaining space.
+ *
+ *     @example
+ *     var menuLayout = new OO.ui.MenuLayout( {
+ *         position: 'top'
+ *     } ),
+ *         menuPanel = new OO.ui.PanelLayout( { padded: true, expanded: true, scrollable: true } ),
+ *         contentPanel = new OO.ui.PanelLayout( { padded: true, expanded: true, scrollable: true } ),
+ *         select = new OO.ui.SelectWidget( {
+ *             items: [
+ *                 new OO.ui.OptionWidget( {
+ *                     data: 'before',
+ *                     label: 'Before',
+ *                 } ),
+ *                 new OO.ui.OptionWidget( {
+ *                     data: 'after',
+ *                     label: 'After',
+ *                 } ),
+ *                 new OO.ui.OptionWidget( {
+ *                     data: 'top',
+ *                     label: 'Top',
+ *                 } ),
+ *                 new OO.ui.OptionWidget( {
+ *                     data: 'bottom',
+ *                     label: 'Bottom',
+ *                 } )
+ *              ]
+ *         } ).on( 'select', function ( item ) {
+ *            menuLayout.setMenuPosition( item.getData() );
+ *         } );
+ *
+ *     menuLayout.$menu.append(
+ *         menuPanel.$element.append( '<b>Menu panel</b>', select.$element )
+ *     );
+ *     menuLayout.$content.append(
+ *         contentPanel.$element.append( '<b>Content panel</b>', '<p>Note that the menu is positioned relative to the content panel: top, bottom, after, before.</p>')
+ *     );
+ *     $( 'body' ).append( menuLayout.$element );
+ *
+ * If menu size needs to be overridden, it can be accomplished using CSS similar to the snippet
+ * below. MenuLayout's CSS will override the appropriate values with 'auto' or '0' to display the
+ * menu correctly. If `menuPosition` is known beforehand, CSS rules corresponding to other positions
+ * may be omitted.
+ *
+ *     .oo-ui-menuLayout-menu {
+ *         height: 200px;
+ *         width: 200px;
+ *     }
+ *     .oo-ui-menuLayout-content {
+ *         top: 200px;
+ *         left: 200px;
+ *         right: 200px;
+ *         bottom: 200px;
+ *     }
+ *
+ * @class
+ * @extends OO.ui.Layout
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {boolean} [showMenu=true] Show menu
+ * @cfg {string} [menuPosition='before'] Position of menu: `top`, `after`, `bottom` or `before`
+ */
+OO.ui.MenuLayout = function OoUiMenuLayout( config ) {
+       // Configuration initialization
+       config = $.extend( {
+               showMenu: true,
+               menuPosition: 'before'
+       }, config );
+
+       // Parent constructor
+       OO.ui.MenuLayout.parent.call( this, config );
+
+       /**
+        * Menu DOM node
+        *
+        * @property {jQuery}
+        */
+       this.$menu = $( '<div>' );
+       /**
+        * Content DOM node
+        *
+        * @property {jQuery}
+        */
+       this.$content = $( '<div>' );
+
+       // Initialization
+       this.$menu
+               .addClass( 'oo-ui-menuLayout-menu' );
+       this.$content.addClass( 'oo-ui-menuLayout-content' );
+       this.$element
+               .addClass( 'oo-ui-menuLayout' )
+               .append( this.$content, this.$menu );
+       this.setMenuPosition( config.menuPosition );
+       this.toggleMenu( config.showMenu );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.MenuLayout, OO.ui.Layout );
+
+/* Methods */
+
+/**
+ * Toggle menu.
+ *
+ * @param {boolean} showMenu Show menu, omit to toggle
+ * @chainable
+ */
+OO.ui.MenuLayout.prototype.toggleMenu = function ( showMenu ) {
+       showMenu = showMenu === undefined ? !this.showMenu : !!showMenu;
+
+       if ( this.showMenu !== showMenu ) {
+               this.showMenu = showMenu;
+               this.$element
+                       .toggleClass( 'oo-ui-menuLayout-showMenu', this.showMenu )
+                       .toggleClass( 'oo-ui-menuLayout-hideMenu', !this.showMenu );
+       }
+
+       return this;
+};
+
+/**
+ * Check if menu is visible
+ *
+ * @return {boolean} Menu is visible
+ */
+OO.ui.MenuLayout.prototype.isMenuVisible = function () {
+       return this.showMenu;
+};
+
+/**
+ * Set menu position.
+ *
+ * @param {string} position Position of menu, either `top`, `after`, `bottom` or `before`
+ * @throws {Error} If position value is not supported
+ * @chainable
+ */
+OO.ui.MenuLayout.prototype.setMenuPosition = function ( position ) {
+       this.$element.removeClass( 'oo-ui-menuLayout-' + this.menuPosition );
+       this.menuPosition = position;
+       this.$element.addClass( 'oo-ui-menuLayout-' + position );
+
+       return this;
+};
+
+/**
+ * Get menu position.
+ *
+ * @return {string} Menu position
+ */
+OO.ui.MenuLayout.prototype.getMenuPosition = function () {
+       return this.menuPosition;
+};
+
+/**
+ * BookletLayouts contain {@link OO.ui.PageLayout page layouts} as well as
+ * an {@link OO.ui.OutlineSelectWidget outline} that allows users to easily navigate
+ * through the pages and select which one to display. By default, only one page is
+ * displayed at a time and the outline is hidden. When a user navigates to a new page,
+ * the booklet layout automatically focuses on the first focusable element, unless the
+ * default setting is changed. Optionally, booklets can be configured to show
+ * {@link OO.ui.OutlineControlsWidget controls} for adding, moving, and removing items.
+ *
+ *     @example
+ *     // Example of a BookletLayout that contains two PageLayouts.
+ *
+ *     function PageOneLayout( name, config ) {
+ *         PageOneLayout.parent.call( this, name, config );
+ *         this.$element.append( '<p>First page</p><p>(This booklet has an outline, displayed on the left)</p>' );
+ *     }
+ *     OO.inheritClass( PageOneLayout, OO.ui.PageLayout );
+ *     PageOneLayout.prototype.setupOutlineItem = function () {
+ *         this.outlineItem.setLabel( 'Page One' );
+ *     };
+ *
+ *     function PageTwoLayout( name, config ) {
+ *         PageTwoLayout.parent.call( this, name, config );
+ *         this.$element.append( '<p>Second page</p>' );
+ *     }
+ *     OO.inheritClass( PageTwoLayout, OO.ui.PageLayout );
+ *     PageTwoLayout.prototype.setupOutlineItem = function () {
+ *         this.outlineItem.setLabel( 'Page Two' );
+ *     };
+ *
+ *     var page1 = new PageOneLayout( 'one' ),
+ *         page2 = new PageTwoLayout( 'two' );
+ *
+ *     var booklet = new OO.ui.BookletLayout( {
+ *         outlined: true
+ *     } );
+ *
+ *     booklet.addPages ( [ page1, page2 ] );
+ *     $( 'body' ).append( booklet.$element );
+ *
+ * @class
+ * @extends OO.ui.MenuLayout
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {boolean} [continuous=false] Show all pages, one after another
+ * @cfg {boolean} [autoFocus=true] Focus on the first focusable element when a new page is displayed.
+ * @cfg {boolean} [outlined=false] Show the outline. The outline is used to navigate through the pages of the booklet.
+ * @cfg {boolean} [editable=false] Show controls for adding, removing and reordering pages
+ */
+OO.ui.BookletLayout = function OoUiBookletLayout( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Parent constructor
+       OO.ui.BookletLayout.parent.call( this, config );
+
+       // Properties
+       this.currentPageName = null;
+       this.pages = {};
+       this.ignoreFocus = false;
+       this.stackLayout = new OO.ui.StackLayout( { continuous: !!config.continuous } );
+       this.$content.append( this.stackLayout.$element );
+       this.autoFocus = config.autoFocus === undefined || !!config.autoFocus;
+       this.outlineVisible = false;
+       this.outlined = !!config.outlined;
+       if ( this.outlined ) {
+               this.editable = !!config.editable;
+               this.outlineControlsWidget = null;
+               this.outlineSelectWidget = new OO.ui.OutlineSelectWidget();
+               this.outlinePanel = new OO.ui.PanelLayout( { scrollable: true } );
+               this.$menu.append( this.outlinePanel.$element );
+               this.outlineVisible = true;
+               if ( this.editable ) {
+                       this.outlineControlsWidget = new OO.ui.OutlineControlsWidget(
+                               this.outlineSelectWidget
+                       );
+               }
+       }
+       this.toggleMenu( this.outlined );
+
+       // Events
+       this.stackLayout.connect( this, { set: 'onStackLayoutSet' } );
+       if ( this.outlined ) {
+               this.outlineSelectWidget.connect( this, { select: 'onOutlineSelectWidgetSelect' } );
+               this.scrolling = false;
+               this.stackLayout.connect( this, { visibleItemChange: 'onStackLayoutVisibleItemChange' } );
+       }
+       if ( this.autoFocus ) {
+               // Event 'focus' does not bubble, but 'focusin' does
+               this.stackLayout.$element.on( 'focusin', this.onStackLayoutFocus.bind( this ) );
+       }
+
+       // Initialization
+       this.$element.addClass( 'oo-ui-bookletLayout' );
+       this.stackLayout.$element.addClass( 'oo-ui-bookletLayout-stackLayout' );
+       if ( this.outlined ) {
+               this.outlinePanel.$element
+                       .addClass( 'oo-ui-bookletLayout-outlinePanel' )
+                       .append( this.outlineSelectWidget.$element );
+               if ( this.editable ) {
+                       this.outlinePanel.$element
+                               .addClass( 'oo-ui-bookletLayout-outlinePanel-editable' )
+                               .append( this.outlineControlsWidget.$element );
+               }
+       }
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.BookletLayout, OO.ui.MenuLayout );
+
+/* Events */
+
+/**
+ * A 'set' event is emitted when a page is {@link #setPage set} to be displayed by the booklet layout.
+ * @event set
+ * @param {OO.ui.PageLayout} page Current page
+ */
+
+/**
+ * An 'add' event is emitted when pages are {@link #addPages added} to the booklet layout.
+ *
+ * @event add
+ * @param {OO.ui.PageLayout[]} page Added pages
+ * @param {number} index Index pages were added at
+ */
+
+/**
+ * A 'remove' event is emitted when pages are {@link #clearPages cleared} or
+ * {@link #removePages removed} from the booklet.
+ *
+ * @event remove
+ * @param {OO.ui.PageLayout[]} pages Removed pages
+ */
+
+/* Methods */
+
+/**
+ * Handle stack layout focus.
+ *
+ * @private
+ * @param {jQuery.Event} e Focusin event
+ */
+OO.ui.BookletLayout.prototype.onStackLayoutFocus = function ( e ) {
+       var name, $target;
+
+       // Find the page that an element was focused within
+       $target = $( e.target ).closest( '.oo-ui-pageLayout' );
+       for ( name in this.pages ) {
+               // Check for page match, exclude current page to find only page changes
+               if ( this.pages[ name ].$element[ 0 ] === $target[ 0 ] && name !== this.currentPageName ) {
+                       this.setPage( name );
+                       break;
+               }
+       }
+};
+
+/**
+ * Handle visibleItemChange events from the stackLayout
+ *
+ * The next visible page is set as the current page by selecting it
+ * in the outline
+ *
+ * @param {OO.ui.PageLayout} page The next visible page in the layout
+ */
+OO.ui.BookletLayout.prototype.onStackLayoutVisibleItemChange = function ( page ) {
+       // Set a flag to so that the resulting call to #onStackLayoutSet doesn't
+       // try and scroll the item into view again.
+       this.scrolling = true;
+       this.outlineSelectWidget.selectItemByData( page.getName() );
+       this.scrolling = false;
+};
+
+/**
+ * Handle stack layout set events.
+ *
+ * @private
+ * @param {OO.ui.PanelLayout|null} page The page panel that is now the current panel
+ */
+OO.ui.BookletLayout.prototype.onStackLayoutSet = function ( page ) {
+       var layout = this;
+       if ( !this.scrolling && page ) {
+               page.scrollElementIntoView( { complete: function () {
+                       if ( layout.autoFocus ) {
+                               layout.focus();
+                       }
+               } } );
+       }
+};
+
+/**
+ * Focus the first input in the current page.
+ *
+ * If no page is selected, the first selectable page will be selected.
+ * If the focus is already in an element on the current page, nothing will happen.
+ * @param {number} [itemIndex] A specific item to focus on
+ */
+OO.ui.BookletLayout.prototype.focus = function ( itemIndex ) {
+       var page,
+               items = this.stackLayout.getItems();
+
+       if ( itemIndex !== undefined && items[ itemIndex ] ) {
+               page = items[ itemIndex ];
+       } else {
+               page = this.stackLayout.getCurrentItem();
+       }
+
+       if ( !page && this.outlined ) {
+               this.selectFirstSelectablePage();
+               page = this.stackLayout.getCurrentItem();
+       }
+       if ( !page ) {
+               return;
+       }
+       // Only change the focus if is not already in the current page
+       if ( !OO.ui.contains( page.$element[ 0 ], this.getElementDocument().activeElement, true ) ) {
+               page.focus();
+       }
+};
+
+/**
+ * Find the first focusable input in the booklet layout and focus
+ * on it.
+ */
+OO.ui.BookletLayout.prototype.focusFirstFocusable = function () {
+       OO.ui.findFocusable( this.stackLayout.$element ).focus();
+};
+
+/**
+ * Handle outline widget select events.
+ *
+ * @private
+ * @param {OO.ui.OptionWidget|null} item Selected item
+ */
+OO.ui.BookletLayout.prototype.onOutlineSelectWidgetSelect = function ( item ) {
+       if ( item ) {
+               this.setPage( item.getData() );
+       }
+};
+
+/**
+ * Check if booklet has an outline.
+ *
+ * @return {boolean} Booklet has an outline
+ */
+OO.ui.BookletLayout.prototype.isOutlined = function () {
+       return this.outlined;
+};
+
+/**
+ * Check if booklet has editing controls.
+ *
+ * @return {boolean} Booklet is editable
+ */
+OO.ui.BookletLayout.prototype.isEditable = function () {
+       return this.editable;
+};
+
+/**
+ * Check if booklet has a visible outline.
+ *
+ * @return {boolean} Outline is visible
+ */
+OO.ui.BookletLayout.prototype.isOutlineVisible = function () {
+       return this.outlined && this.outlineVisible;
+};
+
+/**
+ * Hide or show the outline.
+ *
+ * @param {boolean} [show] Show outline, omit to invert current state
+ * @chainable
+ */
+OO.ui.BookletLayout.prototype.toggleOutline = function ( show ) {
+       if ( this.outlined ) {
+               show = show === undefined ? !this.outlineVisible : !!show;
+               this.outlineVisible = show;
+               this.toggleMenu( show );
+       }
+
+       return this;
+};
+
+/**
+ * Get the page closest to the specified page.
+ *
+ * @param {OO.ui.PageLayout} page Page to use as a reference point
+ * @return {OO.ui.PageLayout|null} Page closest to the specified page
+ */
+OO.ui.BookletLayout.prototype.getClosestPage = function ( page ) {
+       var next, prev, level,
+               pages = this.stackLayout.getItems(),
+               index = pages.indexOf( page );
+
+       if ( index !== -1 ) {
+               next = pages[ index + 1 ];
+               prev = pages[ index - 1 ];
+               // Prefer adjacent pages at the same level
+               if ( this.outlined ) {
+                       level = this.outlineSelectWidget.getItemFromData( page.getName() ).getLevel();
+                       if (
+                               prev &&
+                               level === this.outlineSelectWidget.getItemFromData( prev.getName() ).getLevel()
+                       ) {
+                               return prev;
+                       }
+                       if (
+                               next &&
+                               level === this.outlineSelectWidget.getItemFromData( next.getName() ).getLevel()
+                       ) {
+                               return next;
+                       }
+               }
+       }
+       return prev || next || null;
+};
+
+/**
+ * Get the outline widget.
+ *
+ * If the booklet is not outlined, the method will return `null`.
+ *
+ * @return {OO.ui.OutlineSelectWidget|null} Outline widget, or null if the booklet is not outlined
+ */
+OO.ui.BookletLayout.prototype.getOutline = function () {
+       return this.outlineSelectWidget;
+};
+
+/**
+ * Get the outline controls widget.
+ *
+ * If the outline is not editable, the method will return `null`.
+ *
+ * @return {OO.ui.OutlineControlsWidget|null} The outline controls widget.
+ */
+OO.ui.BookletLayout.prototype.getOutlineControls = function () {
+       return this.outlineControlsWidget;
+};
+
+/**
+ * Get a page by its symbolic name.
+ *
+ * @param {string} name Symbolic name of page
+ * @return {OO.ui.PageLayout|undefined} Page, if found
+ */
+OO.ui.BookletLayout.prototype.getPage = function ( name ) {
+       return this.pages[ name ];
+};
+
+/**
+ * Get the current page.
+ *
+ * @return {OO.ui.PageLayout|undefined} Current page, if found
+ */
+OO.ui.BookletLayout.prototype.getCurrentPage = function () {
+       var name = this.getCurrentPageName();
+       return name ? this.getPage( name ) : undefined;
+};
+
+/**
+ * Get the symbolic name of the current page.
+ *
+ * @return {string|null} Symbolic name of the current page
+ */
+OO.ui.BookletLayout.prototype.getCurrentPageName = function () {
+       return this.currentPageName;
+};
+
+/**
+ * Add pages to the booklet layout
+ *
+ * When pages are added with the same names as existing pages, the existing pages will be
+ * automatically removed before the new pages are added.
+ *
+ * @param {OO.ui.PageLayout[]} pages Pages to add
+ * @param {number} index Index of the insertion point
+ * @fires add
+ * @chainable
+ */
+OO.ui.BookletLayout.prototype.addPages = function ( pages, index ) {
+       var i, len, name, page, item, currentIndex,
+               stackLayoutPages = this.stackLayout.getItems(),
+               remove = [],
+               items = [];
+
+       // Remove pages with same names
+       for ( i = 0, len = pages.length; i < len; i++ ) {
+               page = pages[ i ];
+               name = page.getName();
+
+               if ( Object.prototype.hasOwnProperty.call( this.pages, name ) ) {
+                       // Correct the insertion index
+                       currentIndex = stackLayoutPages.indexOf( this.pages[ name ] );
+                       if ( currentIndex !== -1 && currentIndex + 1 < index ) {
+                               index--;
+                       }
+                       remove.push( this.pages[ name ] );
+               }
+       }
+       if ( remove.length ) {
+               this.removePages( remove );
+       }
+
+       // Add new pages
+       for ( i = 0, len = pages.length; i < len; i++ ) {
+               page = pages[ i ];
+               name = page.getName();
+               this.pages[ page.getName() ] = page;
+               if ( this.outlined ) {
+                       item = new OO.ui.OutlineOptionWidget( { data: name } );
+                       page.setOutlineItem( item );
+                       items.push( item );
+               }
+       }
+
+       if ( this.outlined && items.length ) {
+               this.outlineSelectWidget.addItems( items, index );
+               this.selectFirstSelectablePage();
+       }
+       this.stackLayout.addItems( pages, index );
+       this.emit( 'add', pages, index );
+
+       return this;
+};
+
+/**
+ * Remove the specified pages from the booklet layout.
+ *
+ * To remove all pages from the booklet, you may wish to use the #clearPages method instead.
+ *
+ * @param {OO.ui.PageLayout[]} pages An array of pages to remove
+ * @fires remove
+ * @chainable
+ */
+OO.ui.BookletLayout.prototype.removePages = function ( pages ) {
+       var i, len, name, page,
+               items = [];
+
+       for ( i = 0, len = pages.length; i < len; i++ ) {
+               page = pages[ i ];
+               name = page.getName();
+               delete this.pages[ name ];
+               if ( this.outlined ) {
+                       items.push( this.outlineSelectWidget.getItemFromData( name ) );
+                       page.setOutlineItem( null );
+               }
+       }
+       if ( this.outlined && items.length ) {
+               this.outlineSelectWidget.removeItems( items );
+               this.selectFirstSelectablePage();
+       }
+       this.stackLayout.removeItems( pages );
+       this.emit( 'remove', pages );
+
+       return this;
+};
+
+/**
+ * Clear all pages from the booklet layout.
+ *
+ * To remove only a subset of pages from the booklet, use the #removePages method.
+ *
+ * @fires remove
+ * @chainable
+ */
+OO.ui.BookletLayout.prototype.clearPages = function () {
+       var i, len,
+               pages = this.stackLayout.getItems();
+
+       this.pages = {};
+       this.currentPageName = null;
+       if ( this.outlined ) {
+               this.outlineSelectWidget.clearItems();
+               for ( i = 0, len = pages.length; i < len; i++ ) {
+                       pages[ i ].setOutlineItem( null );
+               }
+       }
+       this.stackLayout.clearItems();
+
+       this.emit( 'remove', pages );
+
+       return this;
+};
+
+/**
+ * Set the current page by symbolic name.
+ *
+ * @fires set
+ * @param {string} name Symbolic name of page
+ */
+OO.ui.BookletLayout.prototype.setPage = function ( name ) {
+       var selectedItem,
+               $focused,
+               page = this.pages[ name ],
+               previousPage = this.currentPageName && this.pages[ this.currentPageName ];
+
+       if ( name !== this.currentPageName ) {
+               if ( this.outlined ) {
+                       selectedItem = this.outlineSelectWidget.getSelectedItem();
+                       if ( selectedItem && selectedItem.getData() !== name ) {
+                               this.outlineSelectWidget.selectItemByData( name );
+                       }
+               }
+               if ( page ) {
+                       if ( previousPage ) {
+                               previousPage.setActive( false );
+                               // Blur anything focused if the next page doesn't have anything focusable.
+                               // This is not needed if the next page has something focusable (because once it is focused
+                               // this blur happens automatically). If the layout is non-continuous, this check is
+                               // meaningless because the next page is not visible yet and thus can't hold focus.
+                               if (
+                                       this.autoFocus &&
+                                       this.stackLayout.continuous &&
+                                       OO.ui.findFocusable( page.$element ).length !== 0
+                               ) {
+                                       $focused = previousPage.$element.find( ':focus' );
+                                       if ( $focused.length ) {
+                                               $focused[ 0 ].blur();
+                                       }
+                               }
+                       }
+                       this.currentPageName = name;
+                       page.setActive( true );
+                       this.stackLayout.setItem( page );
+                       if ( !this.stackLayout.continuous && previousPage ) {
+                               // This should not be necessary, since any inputs on the previous page should have been
+                               // blurred when it was hidden, but browsers are not very consistent about this.
+                               $focused = previousPage.$element.find( ':focus' );
+                               if ( $focused.length ) {
+                                       $focused[ 0 ].blur();
+                               }
+                       }
+                       this.emit( 'set', page );
+               }
+       }
+};
+
+/**
+ * Select the first selectable page.
+ *
+ * @chainable
+ */
+OO.ui.BookletLayout.prototype.selectFirstSelectablePage = function () {
+       if ( !this.outlineSelectWidget.getSelectedItem() ) {
+               this.outlineSelectWidget.selectItem( this.outlineSelectWidget.getFirstSelectableItem() );
+       }
+
+       return this;
+};
+
+/**
+ * IndexLayouts contain {@link OO.ui.CardLayout card layouts} as well as
+ * {@link OO.ui.TabSelectWidget tabs} that allow users to easily navigate through the cards and
+ * select which one to display. By default, only one card is displayed at a time. When a user
+ * navigates to a new card, the index layout automatically focuses on the first focusable element,
+ * unless the default setting is changed.
+ *
+ * TODO: This class is similar to BookletLayout, we may want to refactor to reduce duplication
+ *
+ *     @example
+ *     // Example of a IndexLayout that contains two CardLayouts.
+ *
+ *     function CardOneLayout( name, config ) {
+ *         CardOneLayout.parent.call( this, name, config );
+ *         this.$element.append( '<p>First card</p>' );
+ *     }
+ *     OO.inheritClass( CardOneLayout, OO.ui.CardLayout );
+ *     CardOneLayout.prototype.setupTabItem = function () {
+ *         this.tabItem.setLabel( 'Card one' );
+ *     };
+ *
+ *     var card1 = new CardOneLayout( 'one' ),
+ *         card2 = new CardLayout( 'two', { label: 'Card two' } );
+ *
+ *     card2.$element.append( '<p>Second card</p>' );
+ *
+ *     var index = new OO.ui.IndexLayout();
+ *
+ *     index.addCards ( [ card1, card2 ] );
+ *     $( 'body' ).append( index.$element );
+ *
+ * @class
+ * @extends OO.ui.MenuLayout
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {boolean} [continuous=false] Show all cards, one after another
+ * @cfg {boolean} [expanded=true] Expand the content panel to fill the entire parent element.
+ * @cfg {boolean} [autoFocus=true] Focus on the first focusable element when a new card is displayed.
+ */
+OO.ui.IndexLayout = function OoUiIndexLayout( config ) {
+       // Configuration initialization
+       config = $.extend( {}, config, { menuPosition: 'top' } );
+
+       // Parent constructor
+       OO.ui.IndexLayout.parent.call( this, config );
+
+       // Properties
+       this.currentCardName = null;
+       this.cards = {};
+       this.ignoreFocus = false;
+       this.stackLayout = new OO.ui.StackLayout( {
+               continuous: !!config.continuous,
+               expanded: config.expanded
+       } );
+       this.$content.append( this.stackLayout.$element );
+       this.autoFocus = config.autoFocus === undefined || !!config.autoFocus;
+
+       this.tabSelectWidget = new OO.ui.TabSelectWidget();
+       this.tabPanel = new OO.ui.PanelLayout();
+       this.$menu.append( this.tabPanel.$element );
+
+       this.toggleMenu( true );
+
+       // Events
+       this.stackLayout.connect( this, { set: 'onStackLayoutSet' } );
+       this.tabSelectWidget.connect( this, { select: 'onTabSelectWidgetSelect' } );
+       if ( this.autoFocus ) {
+               // Event 'focus' does not bubble, but 'focusin' does
+               this.stackLayout.$element.on( 'focusin', this.onStackLayoutFocus.bind( this ) );
+       }
+
+       // Initialization
+       this.$element.addClass( 'oo-ui-indexLayout' );
+       this.stackLayout.$element.addClass( 'oo-ui-indexLayout-stackLayout' );
+       this.tabPanel.$element
+               .addClass( 'oo-ui-indexLayout-tabPanel' )
+               .append( this.tabSelectWidget.$element );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.IndexLayout, OO.ui.MenuLayout );
+
+/* Events */
+
+/**
+ * A 'set' event is emitted when a card is {@link #setCard set} to be displayed by the index layout.
+ * @event set
+ * @param {OO.ui.CardLayout} card Current card
+ */
+
+/**
+ * An 'add' event is emitted when cards are {@link #addCards added} to the index layout.
+ *
+ * @event add
+ * @param {OO.ui.CardLayout[]} card Added cards
+ * @param {number} index Index cards were added at
+ */
+
+/**
+ * A 'remove' event is emitted when cards are {@link #clearCards cleared} or
+ * {@link #removeCards removed} from the index.
+ *
+ * @event remove
+ * @param {OO.ui.CardLayout[]} cards Removed cards
+ */
+
+/* Methods */
+
+/**
+ * Handle stack layout focus.
+ *
+ * @private
+ * @param {jQuery.Event} e Focusin event
+ */
+OO.ui.IndexLayout.prototype.onStackLayoutFocus = function ( e ) {
+       var name, $target;
+
+       // Find the card that an element was focused within
+       $target = $( e.target ).closest( '.oo-ui-cardLayout' );
+       for ( name in this.cards ) {
+               // Check for card match, exclude current card to find only card changes
+               if ( this.cards[ name ].$element[ 0 ] === $target[ 0 ] && name !== this.currentCardName ) {
+                       this.setCard( name );
+                       break;
+               }
+       }
+};
+
+/**
+ * Handle stack layout set events.
+ *
+ * @private
+ * @param {OO.ui.PanelLayout|null} card The card panel that is now the current panel
+ */
+OO.ui.IndexLayout.prototype.onStackLayoutSet = function ( card ) {
+       var layout = this;
+       if ( card ) {
+               card.scrollElementIntoView( { complete: function () {
+                       if ( layout.autoFocus ) {
+                               layout.focus();
+                       }
+               } } );
+       }
+};
+
+/**
+ * Focus the first input in the current card.
+ *
+ * If no card is selected, the first selectable card will be selected.
+ * If the focus is already in an element on the current card, nothing will happen.
+ * @param {number} [itemIndex] A specific item to focus on
+ */
+OO.ui.IndexLayout.prototype.focus = function ( itemIndex ) {
+       var card,
+               items = this.stackLayout.getItems();
+
+       if ( itemIndex !== undefined && items[ itemIndex ] ) {
+               card = items[ itemIndex ];
+       } else {
+               card = this.stackLayout.getCurrentItem();
+       }
+
+       if ( !card ) {
+               this.selectFirstSelectableCard();
+               card = this.stackLayout.getCurrentItem();
+       }
+       if ( !card ) {
+               return;
+       }
+       // Only change the focus if is not already in the current page
+       if ( !OO.ui.contains( card.$element[ 0 ], this.getElementDocument().activeElement, true ) ) {
+               card.focus();
+       }
+};
+
+/**
+ * Find the first focusable input in the index layout and focus
+ * on it.
+ */
+OO.ui.IndexLayout.prototype.focusFirstFocusable = function () {
+       OO.ui.findFocusable( this.stackLayout.$element ).focus();
+};
+
+/**
+ * Handle tab widget select events.
+ *
+ * @private
+ * @param {OO.ui.OptionWidget|null} item Selected item
+ */
+OO.ui.IndexLayout.prototype.onTabSelectWidgetSelect = function ( item ) {
+       if ( item ) {
+               this.setCard( item.getData() );
+       }
+};
+
+/**
+ * Get the card closest to the specified card.
+ *
+ * @param {OO.ui.CardLayout} card Card to use as a reference point
+ * @return {OO.ui.CardLayout|null} Card closest to the specified card
+ */
+OO.ui.IndexLayout.prototype.getClosestCard = function ( card ) {
+       var next, prev, level,
+               cards = this.stackLayout.getItems(),
+               index = cards.indexOf( card );
+
+       if ( index !== -1 ) {
+               next = cards[ index + 1 ];
+               prev = cards[ index - 1 ];
+               // Prefer adjacent cards at the same level
+               level = this.tabSelectWidget.getItemFromData( card.getName() ).getLevel();
+               if (
+                       prev &&
+                       level === this.tabSelectWidget.getItemFromData( prev.getName() ).getLevel()
+               ) {
+                       return prev;
+               }
+               if (
+                       next &&
+                       level === this.tabSelectWidget.getItemFromData( next.getName() ).getLevel()
+               ) {
+                       return next;
+               }
+       }
+       return prev || next || null;
+};
+
+/**
+ * Get the tabs widget.
+ *
+ * @return {OO.ui.TabSelectWidget} Tabs widget
+ */
+OO.ui.IndexLayout.prototype.getTabs = function () {
+       return this.tabSelectWidget;
+};
+
+/**
+ * Get a card by its symbolic name.
+ *
+ * @param {string} name Symbolic name of card
+ * @return {OO.ui.CardLayout|undefined} Card, if found
+ */
+OO.ui.IndexLayout.prototype.getCard = function ( name ) {
+       return this.cards[ name ];
+};
+
+/**
+ * Get the current card.
+ *
+ * @return {OO.ui.CardLayout|undefined} Current card, if found
+ */
+OO.ui.IndexLayout.prototype.getCurrentCard = function () {
+       var name = this.getCurrentCardName();
+       return name ? this.getCard( name ) : undefined;
+};
+
+/**
+ * Get the symbolic name of the current card.
+ *
+ * @return {string|null} Symbolic name of the current card
+ */
+OO.ui.IndexLayout.prototype.getCurrentCardName = function () {
+       return this.currentCardName;
+};
+
+/**
+ * Add cards to the index layout
+ *
+ * When cards are added with the same names as existing cards, the existing cards will be
+ * automatically removed before the new cards are added.
+ *
+ * @param {OO.ui.CardLayout[]} cards Cards to add
+ * @param {number} index Index of the insertion point
+ * @fires add
+ * @chainable
+ */
+OO.ui.IndexLayout.prototype.addCards = function ( cards, index ) {
+       var i, len, name, card, item, currentIndex,
+               stackLayoutCards = this.stackLayout.getItems(),
+               remove = [],
+               items = [];
+
+       // Remove cards with same names
+       for ( i = 0, len = cards.length; i < len; i++ ) {
+               card = cards[ i ];
+               name = card.getName();
+
+               if ( Object.prototype.hasOwnProperty.call( this.cards, name ) ) {
+                       // Correct the insertion index
+                       currentIndex = stackLayoutCards.indexOf( this.cards[ name ] );
+                       if ( currentIndex !== -1 && currentIndex + 1 < index ) {
+                               index--;
+                       }
+                       remove.push( this.cards[ name ] );
+               }
+       }
+       if ( remove.length ) {
+               this.removeCards( remove );
+       }
+
+       // Add new cards
+       for ( i = 0, len = cards.length; i < len; i++ ) {
+               card = cards[ i ];
+               name = card.getName();
+               this.cards[ card.getName() ] = card;
+               item = new OO.ui.TabOptionWidget( { data: name } );
+               card.setTabItem( item );
+               items.push( item );
+       }
+
+       if ( items.length ) {
+               this.tabSelectWidget.addItems( items, index );
+               this.selectFirstSelectableCard();
+       }
+       this.stackLayout.addItems( cards, index );
+       this.emit( 'add', cards, index );
+
+       return this;
+};
+
+/**
+ * Remove the specified cards from the index layout.
+ *
+ * To remove all cards from the index, you may wish to use the #clearCards method instead.
+ *
+ * @param {OO.ui.CardLayout[]} cards An array of cards to remove
+ * @fires remove
+ * @chainable
+ */
+OO.ui.IndexLayout.prototype.removeCards = function ( cards ) {
+       var i, len, name, card,
+               items = [];
+
+       for ( i = 0, len = cards.length; i < len; i++ ) {
+               card = cards[ i ];
+               name = card.getName();
+               delete this.cards[ name ];
+               items.push( this.tabSelectWidget.getItemFromData( name ) );
+               card.setTabItem( null );
+       }
+       if ( items.length ) {
+               this.tabSelectWidget.removeItems( items );
+               this.selectFirstSelectableCard();
+       }
+       this.stackLayout.removeItems( cards );
+       this.emit( 'remove', cards );
+
+       return this;
+};
+
+/**
+ * Clear all cards from the index layout.
+ *
+ * To remove only a subset of cards from the index, use the #removeCards method.
+ *
+ * @fires remove
+ * @chainable
+ */
+OO.ui.IndexLayout.prototype.clearCards = function () {
+       var i, len,
+               cards = this.stackLayout.getItems();
+
+       this.cards = {};
+       this.currentCardName = null;
+       this.tabSelectWidget.clearItems();
+       for ( i = 0, len = cards.length; i < len; i++ ) {
+               cards[ i ].setTabItem( null );
+       }
+       this.stackLayout.clearItems();
+
+       this.emit( 'remove', cards );
+
+       return this;
+};
+
+/**
+ * Set the current card by symbolic name.
+ *
+ * @fires set
+ * @param {string} name Symbolic name of card
+ */
+OO.ui.IndexLayout.prototype.setCard = function ( name ) {
+       var selectedItem,
+               $focused,
+               card = this.cards[ name ],
+               previousCard = this.currentCardName && this.cards[ this.currentCardName ];
+
+       if ( name !== this.currentCardName ) {
+               selectedItem = this.tabSelectWidget.getSelectedItem();
+               if ( selectedItem && selectedItem.getData() !== name ) {
+                       this.tabSelectWidget.selectItemByData( name );
+               }
+               if ( card ) {
+                       if ( previousCard ) {
+                               previousCard.setActive( false );
+                               // Blur anything focused if the next card doesn't have anything focusable.
+                               // This is not needed if the next card has something focusable (because once it is focused
+                               // this blur happens automatically). If the layout is non-continuous, this check is
+                               // meaningless because the next card is not visible yet and thus can't hold focus.
+                               if (
+                                       this.autoFocus &&
+                                       this.stackLayout.continuous &&
+                                       OO.ui.findFocusable( card.$element ).length !== 0
+                               ) {
+                                       $focused = previousCard.$element.find( ':focus' );
+                                       if ( $focused.length ) {
+                                               $focused[ 0 ].blur();
+                                       }
+                               }
+                       }
+                       this.currentCardName = name;
+                       card.setActive( true );
+                       this.stackLayout.setItem( card );
+                       if ( !this.stackLayout.continuous && previousCard ) {
+                               // This should not be necessary, since any inputs on the previous card should have been
+                               // blurred when it was hidden, but browsers are not very consistent about this.
+                               $focused = previousCard.$element.find( ':focus' );
+                               if ( $focused.length ) {
+                                       $focused[ 0 ].blur();
+                               }
+                       }
+                       this.emit( 'set', card );
+               }
+       }
+};
+
+/**
+ * Select the first selectable card.
+ *
+ * @chainable
+ */
+OO.ui.IndexLayout.prototype.selectFirstSelectableCard = function () {
+       if ( !this.tabSelectWidget.getSelectedItem() ) {
+               this.tabSelectWidget.selectItem( this.tabSelectWidget.getFirstSelectableItem() );
+       }
+
+       return this;
+};
+
+/**
+ * ToggleWidget implements basic behavior of widgets with an on/off state.
+ * Please see OO.ui.ToggleButtonWidget and OO.ui.ToggleSwitchWidget for examples.
+ *
+ * @abstract
+ * @class
+ * @extends OO.ui.Widget
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {boolean} [value=false] The toggle’s initial on/off state.
+ *  By default, the toggle is in the 'off' state.
+ */
+OO.ui.ToggleWidget = function OoUiToggleWidget( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Parent constructor
+       OO.ui.ToggleWidget.parent.call( this, config );
+
+       // Properties
+       this.value = null;
+
+       // Initialization
+       this.$element.addClass( 'oo-ui-toggleWidget' );
+       this.setValue( !!config.value );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.ToggleWidget, OO.ui.Widget );
+
+/* Events */
+
+/**
+ * @event change
+ *
+ * A change event is emitted when the on/off state of the toggle changes.
+ *
+ * @param {boolean} value Value representing the new state of the toggle
+ */
+
+/* Methods */
+
+/**
+ * Get the value representing the toggle’s state.
+ *
+ * @return {boolean} The on/off state of the toggle
+ */
+OO.ui.ToggleWidget.prototype.getValue = function () {
+       return this.value;
+};
+
+/**
+ * Set the state of the toggle: `true` for 'on', `false' for 'off'.
+ *
+ * @param {boolean} value The state of the toggle
+ * @fires change
+ * @chainable
+ */
+OO.ui.ToggleWidget.prototype.setValue = function ( value ) {
+       value = !!value;
+       if ( this.value !== value ) {
+               this.value = value;
+               this.emit( 'change', value );
+               this.$element.toggleClass( 'oo-ui-toggleWidget-on', value );
+               this.$element.toggleClass( 'oo-ui-toggleWidget-off', !value );
+               this.$element.attr( 'aria-checked', value.toString() );
+       }
+       return this;
+};
+
+/**
+ * ToggleButtons are buttons that have a state (‘on’ or ‘off’) that is represented by a
+ * Boolean value. Like other {@link OO.ui.ButtonWidget buttons}, toggle buttons can be
+ * configured with {@link OO.ui.mixin.IconElement icons}, {@link OO.ui.mixin.IndicatorElement indicators},
+ * {@link OO.ui.mixin.TitledElement titles}, {@link OO.ui.mixin.FlaggedElement styling flags},
+ * and {@link OO.ui.mixin.LabelElement labels}. Please see
+ * the [OOjs UI documentation][1] on MediaWiki for more information.
+ *
+ *     @example
+ *     // Toggle buttons in the 'off' and 'on' state.
+ *     var toggleButton1 = new OO.ui.ToggleButtonWidget( {
+ *         label: 'Toggle Button off'
+ *     } );
+ *     var toggleButton2 = new OO.ui.ToggleButtonWidget( {
+ *         label: 'Toggle Button on',
+ *         value: true
+ *     } );
+ *     // Append the buttons to the DOM.
+ *     $( 'body' ).append( toggleButton1.$element, toggleButton2.$element );
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Buttons_and_Switches#Toggle_buttons
+ *
+ * @class
+ * @extends OO.ui.ToggleWidget
+ * @mixins OO.ui.mixin.ButtonElement
+ * @mixins OO.ui.mixin.IconElement
+ * @mixins OO.ui.mixin.IndicatorElement
+ * @mixins OO.ui.mixin.LabelElement
+ * @mixins OO.ui.mixin.TitledElement
+ * @mixins OO.ui.mixin.FlaggedElement
+ * @mixins OO.ui.mixin.TabIndexedElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {boolean} [value=false] The toggle button’s initial on/off
+ *  state. By default, the button is in the 'off' state.
+ */
+OO.ui.ToggleButtonWidget = function OoUiToggleButtonWidget( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Parent constructor
+       OO.ui.ToggleButtonWidget.parent.call( this, config );
+
+       // Mixin constructors
+       OO.ui.mixin.ButtonElement.call( this, config );
+       OO.ui.mixin.IconElement.call( this, config );
+       OO.ui.mixin.IndicatorElement.call( this, config );
+       OO.ui.mixin.LabelElement.call( this, config );
+       OO.ui.mixin.TitledElement.call( this, $.extend( {}, config, { $titled: this.$button } ) );
+       OO.ui.mixin.FlaggedElement.call( this, config );
+       OO.ui.mixin.TabIndexedElement.call( this, $.extend( {}, config, { $tabIndexed: this.$button } ) );
+
+       // Events
+       this.connect( this, { click: 'onAction' } );
+
+       // Initialization
+       this.$button.append( this.$icon, this.$label, this.$indicator );
+       this.$element
+               .addClass( 'oo-ui-toggleButtonWidget' )
+               .append( this.$button );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.ToggleButtonWidget, OO.ui.ToggleWidget );
+OO.mixinClass( OO.ui.ToggleButtonWidget, OO.ui.mixin.ButtonElement );
+OO.mixinClass( OO.ui.ToggleButtonWidget, OO.ui.mixin.IconElement );
+OO.mixinClass( OO.ui.ToggleButtonWidget, OO.ui.mixin.IndicatorElement );
+OO.mixinClass( OO.ui.ToggleButtonWidget, OO.ui.mixin.LabelElement );
+OO.mixinClass( OO.ui.ToggleButtonWidget, OO.ui.mixin.TitledElement );
+OO.mixinClass( OO.ui.ToggleButtonWidget, OO.ui.mixin.FlaggedElement );
+OO.mixinClass( OO.ui.ToggleButtonWidget, OO.ui.mixin.TabIndexedElement );
+
+/* Methods */
+
+/**
+ * Handle the button action being triggered.
+ *
+ * @private
+ */
+OO.ui.ToggleButtonWidget.prototype.onAction = function () {
+       this.setValue( !this.value );
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.ToggleButtonWidget.prototype.setValue = function ( value ) {
+       value = !!value;
+       if ( value !== this.value ) {
+               // Might be called from parent constructor before ButtonElement constructor
+               if ( this.$button ) {
+                       this.$button.attr( 'aria-pressed', value.toString() );
+               }
+               this.setActive( value );
+       }
+
+       // Parent method
+       OO.ui.ToggleButtonWidget.parent.prototype.setValue.call( this, value );
+
+       return this;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.ToggleButtonWidget.prototype.setButtonElement = function ( $button ) {
+       if ( this.$button ) {
+               this.$button.removeAttr( 'aria-pressed' );
+       }
+       OO.ui.mixin.ButtonElement.prototype.setButtonElement.call( this, $button );
+       this.$button.attr( 'aria-pressed', this.value.toString() );
+};
+
+/**
+ * ToggleSwitches are switches that slide on and off. Their state is represented by a Boolean
+ * value (`true` for ‘on’, and `false` otherwise, the default). The ‘off’ state is represented
+ * visually by a slider in the leftmost position.
+ *
+ *     @example
+ *     // Toggle switches in the 'off' and 'on' position.
+ *     var toggleSwitch1 = new OO.ui.ToggleSwitchWidget();
+ *     var toggleSwitch2 = new OO.ui.ToggleSwitchWidget( {
+ *         value: true
+ *     } );
+ *
+ *     // Create a FieldsetLayout to layout and label switches
+ *     var fieldset = new OO.ui.FieldsetLayout( {
+ *        label: 'Toggle switches'
+ *     } );
+ *     fieldset.addItems( [
+ *         new OO.ui.FieldLayout( toggleSwitch1, { label: 'Off', align: 'top' } ),
+ *         new OO.ui.FieldLayout( toggleSwitch2, { label: 'On', align: 'top' } )
+ *     ] );
+ *     $( 'body' ).append( fieldset.$element );
+ *
+ * @class
+ * @extends OO.ui.ToggleWidget
+ * @mixins OO.ui.mixin.TabIndexedElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {boolean} [value=false] The toggle switch’s initial on/off state.
+ *  By default, the toggle switch is in the 'off' position.
+ */
+OO.ui.ToggleSwitchWidget = function OoUiToggleSwitchWidget( config ) {
+       // Parent constructor
+       OO.ui.ToggleSwitchWidget.parent.call( this, config );
+
+       // Mixin constructors
+       OO.ui.mixin.TabIndexedElement.call( this, config );
+
+       // Properties
+       this.dragging = false;
+       this.dragStart = null;
+       this.sliding = false;
+       this.$glow = $( '<span>' );
+       this.$grip = $( '<span>' );
+
+       // Events
+       this.$element.on( {
+               click: this.onClick.bind( this ),
+               keypress: this.onKeyPress.bind( this )
+       } );
+
+       // Initialization
+       this.$glow.addClass( 'oo-ui-toggleSwitchWidget-glow' );
+       this.$grip.addClass( 'oo-ui-toggleSwitchWidget-grip' );
+       this.$element
+               .addClass( 'oo-ui-toggleSwitchWidget' )
+               .attr( 'role', 'checkbox' )
+               .append( this.$glow, this.$grip );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.ToggleSwitchWidget, OO.ui.ToggleWidget );
+OO.mixinClass( OO.ui.ToggleSwitchWidget, OO.ui.mixin.TabIndexedElement );
+
+/* Methods */
+
+/**
+ * Handle mouse click events.
+ *
+ * @private
+ * @param {jQuery.Event} e Mouse click event
+ */
+OO.ui.ToggleSwitchWidget.prototype.onClick = function ( e ) {
+       if ( !this.isDisabled() && e.which === OO.ui.MouseButtons.LEFT ) {
+               this.setValue( !this.value );
+       }
+       return false;
+};
+
+/**
+ * Handle key press events.
+ *
+ * @private
+ * @param {jQuery.Event} e Key press event
+ */
+OO.ui.ToggleSwitchWidget.prototype.onKeyPress = function ( e ) {
+       if ( !this.isDisabled() && ( e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER ) ) {
+               this.setValue( !this.value );
+               return false;
+       }
+};
+
+/**
+ * OutlineControlsWidget is a set of controls for an {@link OO.ui.OutlineSelectWidget outline select widget}.
+ * Controls include moving items up and down, removing items, and adding different kinds of items.
+ *
+ * **Currently, this class is only used by {@link OO.ui.BookletLayout booklet layouts}.**
+ *
+ * @class
+ * @extends OO.ui.Widget
+ * @mixins OO.ui.mixin.GroupElement
+ * @mixins OO.ui.mixin.IconElement
+ *
+ * @constructor
+ * @param {OO.ui.OutlineSelectWidget} outline Outline to control
+ * @param {Object} [config] Configuration options
+ * @cfg {Object} [abilities] List of abilties
+ * @cfg {boolean} [abilities.move=true] Allow moving movable items
+ * @cfg {boolean} [abilities.remove=true] Allow removing removable items
+ */
+OO.ui.OutlineControlsWidget = function OoUiOutlineControlsWidget( outline, config ) {
+       // Allow passing positional parameters inside the config object
+       if ( OO.isPlainObject( outline ) && config === undefined ) {
+               config = outline;
+               outline = config.outline;
+       }
+
+       // Configuration initialization
+       config = $.extend( { icon: 'add' }, config );
+
+       // Parent constructor
+       OO.ui.OutlineControlsWidget.parent.call( this, config );
+
+       // Mixin constructors
+       OO.ui.mixin.GroupElement.call( this, config );
+       OO.ui.mixin.IconElement.call( this, config );
+
+       // Properties
+       this.outline = outline;
+       this.$movers = $( '<div>' );
+       this.upButton = new OO.ui.ButtonWidget( {
+               framed: false,
+               icon: 'collapse',
+               title: OO.ui.msg( 'ooui-outline-control-move-up' )
+       } );
+       this.downButton = new OO.ui.ButtonWidget( {
+               framed: false,
+               icon: 'expand',
+               title: OO.ui.msg( 'ooui-outline-control-move-down' )
+       } );
+       this.removeButton = new OO.ui.ButtonWidget( {
+               framed: false,
+               icon: 'remove',
+               title: OO.ui.msg( 'ooui-outline-control-remove' )
+       } );
+       this.abilities = { move: true, remove: true };
+
+       // Events
+       outline.connect( this, {
+               select: 'onOutlineChange',
+               add: 'onOutlineChange',
+               remove: 'onOutlineChange'
+       } );
+       this.upButton.connect( this, { click: [ 'emit', 'move', -1 ] } );
+       this.downButton.connect( this, { click: [ 'emit', 'move', 1 ] } );
+       this.removeButton.connect( this, { click: [ 'emit', 'remove' ] } );
+
+       // Initialization
+       this.$element.addClass( 'oo-ui-outlineControlsWidget' );
+       this.$group.addClass( 'oo-ui-outlineControlsWidget-items' );
+       this.$movers
+               .addClass( 'oo-ui-outlineControlsWidget-movers' )
+               .append( this.removeButton.$element, this.upButton.$element, this.downButton.$element );
+       this.$element.append( this.$icon, this.$group, this.$movers );
+       this.setAbilities( config.abilities || {} );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.OutlineControlsWidget, OO.ui.Widget );
+OO.mixinClass( OO.ui.OutlineControlsWidget, OO.ui.mixin.GroupElement );
+OO.mixinClass( OO.ui.OutlineControlsWidget, OO.ui.mixin.IconElement );
+
+/* Events */
+
+/**
+ * @event move
+ * @param {number} places Number of places to move
+ */
+
+/**
+ * @event remove
+ */
+
+/* Methods */
+
+/**
+ * Set abilities.
+ *
+ * @param {Object} abilities List of abilties
+ * @param {boolean} [abilities.move] Allow moving movable items
+ * @param {boolean} [abilities.remove] Allow removing removable items
+ */
+OO.ui.OutlineControlsWidget.prototype.setAbilities = function ( abilities ) {
+       var ability;
+
+       for ( ability in this.abilities ) {
+               if ( abilities[ ability ] !== undefined ) {
+                       this.abilities[ ability ] = !!abilities[ ability ];
+               }
+       }
+
+       this.onOutlineChange();
+};
+
+/**
+ * @private
+ * Handle outline change events.
+ */
+OO.ui.OutlineControlsWidget.prototype.onOutlineChange = function () {
+       var i, len, firstMovable, lastMovable,
+               items = this.outline.getItems(),
+               selectedItem = this.outline.getSelectedItem(),
+               movable = this.abilities.move && selectedItem && selectedItem.isMovable(),
+               removable = this.abilities.remove && selectedItem && selectedItem.isRemovable();
+
+       if ( movable ) {
+               i = -1;
+               len = items.length;
+               while ( ++i < len ) {
+                       if ( items[ i ].isMovable() ) {
+                               firstMovable = items[ i ];
+                               break;
+                       }
+               }
+               i = len;
+               while ( i-- ) {
+                       if ( items[ i ].isMovable() ) {
+                               lastMovable = items[ i ];
+                               break;
+                       }
+               }
+       }
+       this.upButton.setDisabled( !movable || selectedItem === firstMovable );
+       this.downButton.setDisabled( !movable || selectedItem === lastMovable );
+       this.removeButton.setDisabled( !removable );
+};
+
+/**
+ * OutlineOptionWidget is an item in an {@link OO.ui.OutlineSelectWidget OutlineSelectWidget}.
+ *
+ * Currently, this class is only used by {@link OO.ui.BookletLayout booklet layouts}, which contain
+ * {@link OO.ui.PageLayout page layouts}. See {@link OO.ui.BookletLayout BookletLayout}
+ * for an example.
+ *
+ * @class
+ * @extends OO.ui.DecoratedOptionWidget
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {number} [level] Indentation level
+ * @cfg {boolean} [movable] Allow modification from {@link OO.ui.OutlineControlsWidget outline controls}.
+ */
+OO.ui.OutlineOptionWidget = function OoUiOutlineOptionWidget( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Parent constructor
+       OO.ui.OutlineOptionWidget.parent.call( this, config );
+
+       // Properties
+       this.level = 0;
+       this.movable = !!config.movable;
+       this.removable = !!config.removable;
+
+       // Initialization
+       this.$element.addClass( 'oo-ui-outlineOptionWidget' );
+       this.setLevel( config.level );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.OutlineOptionWidget, OO.ui.DecoratedOptionWidget );
+
+/* Static Properties */
+
+OO.ui.OutlineOptionWidget.static.highlightable = false;
+
+OO.ui.OutlineOptionWidget.static.scrollIntoViewOnSelect = true;
+
+OO.ui.OutlineOptionWidget.static.levelClass = 'oo-ui-outlineOptionWidget-level-';
+
+OO.ui.OutlineOptionWidget.static.levels = 3;
+
+/* Methods */
+
+/**
+ * Check if item is movable.
+ *
+ * Movability is used by {@link OO.ui.OutlineControlsWidget outline controls}.
+ *
+ * @return {boolean} Item is movable
+ */
+OO.ui.OutlineOptionWidget.prototype.isMovable = function () {
+       return this.movable;
+};
+
+/**
+ * Check if item is removable.
+ *
+ * Removability is used by {@link OO.ui.OutlineControlsWidget outline controls}.
+ *
+ * @return {boolean} Item is removable
+ */
+OO.ui.OutlineOptionWidget.prototype.isRemovable = function () {
+       return this.removable;
+};
+
+/**
+ * Get indentation level.
+ *
+ * @return {number} Indentation level
+ */
+OO.ui.OutlineOptionWidget.prototype.getLevel = function () {
+       return this.level;
+};
+
+/**
+ * Set movability.
+ *
+ * Movability is used by {@link OO.ui.OutlineControlsWidget outline controls}.
+ *
+ * @param {boolean} movable Item is movable
+ * @chainable
+ */
+OO.ui.OutlineOptionWidget.prototype.setMovable = function ( movable ) {
+       this.movable = !!movable;
+       this.updateThemeClasses();
+       return this;
+};
+
+/**
+ * Set removability.
+ *
+ * Removability is used by {@link OO.ui.OutlineControlsWidget outline controls}.
+ *
+ * @param {boolean} removable Item is removable
+ * @chainable
+ */
+OO.ui.OutlineOptionWidget.prototype.setRemovable = function ( removable ) {
+       this.removable = !!removable;
+       this.updateThemeClasses();
+       return this;
+};
+
+/**
+ * Set indentation level.
+ *
+ * @param {number} [level=0] Indentation level, in the range of [0,#maxLevel]
+ * @chainable
+ */
+OO.ui.OutlineOptionWidget.prototype.setLevel = function ( level ) {
+       var levels = this.constructor.static.levels,
+               levelClass = this.constructor.static.levelClass,
+               i = levels;
+
+       this.level = level ? Math.max( 0, Math.min( levels - 1, level ) ) : 0;
+       while ( i-- ) {
+               if ( this.level === i ) {
+                       this.$element.addClass( levelClass + i );
+               } else {
+                       this.$element.removeClass( levelClass + i );
+               }
+       }
+       this.updateThemeClasses();
+
+       return this;
+};
+
+/**
+ * OutlineSelectWidget is a structured list that contains {@link OO.ui.OutlineOptionWidget outline options}
+ * A set of controls can be provided with an {@link OO.ui.OutlineControlsWidget outline controls} widget.
+ *
+ * **Currently, this class is only used by {@link OO.ui.BookletLayout booklet layouts}.**
+ *
+ * @class
+ * @extends OO.ui.SelectWidget
+ * @mixins OO.ui.mixin.TabIndexedElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.OutlineSelectWidget = function OoUiOutlineSelectWidget( config ) {
+       // Parent constructor
+       OO.ui.OutlineSelectWidget.parent.call( this, config );
+
+       // Mixin constructors
+       OO.ui.mixin.TabIndexedElement.call( this, config );
+
+       // Events
+       this.$element.on( {
+               focus: this.bindKeyDownListener.bind( this ),
+               blur: this.unbindKeyDownListener.bind( this )
+       } );
+
+       // Initialization
+       this.$element.addClass( 'oo-ui-outlineSelectWidget' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.OutlineSelectWidget, OO.ui.SelectWidget );
+OO.mixinClass( OO.ui.OutlineSelectWidget, OO.ui.mixin.TabIndexedElement );
+
+/**
+ * ButtonOptionWidget is a special type of {@link OO.ui.mixin.ButtonElement button element} that
+ * can be selected and configured with data. The class is
+ * used with OO.ui.ButtonSelectWidget to create a selection of button options. Please see the
+ * [OOjs UI documentation on MediaWiki] [1] for more information.
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options#Button_selects_and_options
+ *
+ * @class
+ * @extends OO.ui.DecoratedOptionWidget
+ * @mixins OO.ui.mixin.ButtonElement
+ * @mixins OO.ui.mixin.TabIndexedElement
+ * @mixins OO.ui.mixin.TitledElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.ButtonOptionWidget = function OoUiButtonOptionWidget( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Parent constructor
+       OO.ui.ButtonOptionWidget.parent.call( this, config );
+
+       // Mixin constructors
+       OO.ui.mixin.ButtonElement.call( this, config );
+       OO.ui.mixin.TitledElement.call( this, $.extend( {}, config, { $titled: this.$button } ) );
+       OO.ui.mixin.TabIndexedElement.call( this, $.extend( {}, config, {
+               $tabIndexed: this.$button,
+               tabIndex: -1
+       } ) );
+
+       // Initialization
+       this.$element.addClass( 'oo-ui-buttonOptionWidget' );
+       this.$button.append( this.$element.contents() );
+       this.$element.append( this.$button );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.ButtonOptionWidget, OO.ui.DecoratedOptionWidget );
+OO.mixinClass( OO.ui.ButtonOptionWidget, OO.ui.mixin.ButtonElement );
+OO.mixinClass( OO.ui.ButtonOptionWidget, OO.ui.mixin.TitledElement );
+OO.mixinClass( OO.ui.ButtonOptionWidget, OO.ui.mixin.TabIndexedElement );
+
+/* Static Properties */
+
+// Allow button mouse down events to pass through so they can be handled by the parent select widget
+OO.ui.ButtonOptionWidget.static.cancelButtonMouseDownEvents = false;
+
+OO.ui.ButtonOptionWidget.static.highlightable = false;
+
+/* Methods */
+
+/**
+ * @inheritdoc
+ */
+OO.ui.ButtonOptionWidget.prototype.setSelected = function ( state ) {
+       OO.ui.ButtonOptionWidget.parent.prototype.setSelected.call( this, state );
+
+       if ( this.constructor.static.selectable ) {
+               this.setActive( state );
+       }
+
+       return this;
+};
+
+/**
+ * ButtonSelectWidget is a {@link OO.ui.SelectWidget select widget} that contains
+ * button options and is used together with
+ * OO.ui.ButtonOptionWidget. The ButtonSelectWidget provides an interface for
+ * highlighting, choosing, and selecting mutually exclusive options. Please see
+ * the [OOjs UI documentation on MediaWiki] [1] for more information.
+ *
+ *     @example
+ *     // Example: A ButtonSelectWidget that contains three ButtonOptionWidgets
+ *     var option1 = new OO.ui.ButtonOptionWidget( {
+ *         data: 1,
+ *         label: 'Option 1',
+ *         title: 'Button option 1'
+ *     } );
+ *
+ *     var option2 = new OO.ui.ButtonOptionWidget( {
+ *         data: 2,
+ *         label: 'Option 2',
+ *         title: 'Button option 2'
+ *     } );
+ *
+ *     var option3 = new OO.ui.ButtonOptionWidget( {
+ *         data: 3,
+ *         label: 'Option 3',
+ *         title: 'Button option 3'
+ *     } );
+ *
+ *     var buttonSelect=new OO.ui.ButtonSelectWidget( {
+ *         items: [ option1, option2, option3 ]
+ *     } );
+ *     $( 'body' ).append( buttonSelect.$element );
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options
+ *
+ * @class
+ * @extends OO.ui.SelectWidget
+ * @mixins OO.ui.mixin.TabIndexedElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.ButtonSelectWidget = function OoUiButtonSelectWidget( config ) {
+       // Parent constructor
+       OO.ui.ButtonSelectWidget.parent.call( this, config );
+
+       // Mixin constructors
+       OO.ui.mixin.TabIndexedElement.call( this, config );
+
+       // Events
+       this.$element.on( {
+               focus: this.bindKeyDownListener.bind( this ),
+               blur: this.unbindKeyDownListener.bind( this )
+       } );
+
+       // Initialization
+       this.$element.addClass( 'oo-ui-buttonSelectWidget' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.ButtonSelectWidget, OO.ui.SelectWidget );
+OO.mixinClass( OO.ui.ButtonSelectWidget, OO.ui.mixin.TabIndexedElement );
+
+/**
+ * TabOptionWidget is an item in a {@link OO.ui.TabSelectWidget TabSelectWidget}.
+ *
+ * Currently, this class is only used by {@link OO.ui.IndexLayout index layouts}, which contain
+ * {@link OO.ui.CardLayout card layouts}. See {@link OO.ui.IndexLayout IndexLayout}
+ * for an example.
+ *
+ * @class
+ * @extends OO.ui.OptionWidget
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.TabOptionWidget = function OoUiTabOptionWidget( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Parent constructor
+       OO.ui.TabOptionWidget.parent.call( this, config );
+
+       // Initialization
+       this.$element.addClass( 'oo-ui-tabOptionWidget' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.TabOptionWidget, OO.ui.OptionWidget );
+
+/* Static Properties */
+
+OO.ui.TabOptionWidget.static.highlightable = false;
+
+/**
+ * TabSelectWidget is a list that contains {@link OO.ui.TabOptionWidget tab options}
+ *
+ * **Currently, this class is only used by {@link OO.ui.IndexLayout index layouts}.**
+ *
+ * @class
+ * @extends OO.ui.SelectWidget
+ * @mixins OO.ui.mixin.TabIndexedElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.TabSelectWidget = function OoUiTabSelectWidget( config ) {
+       // Parent constructor
+       OO.ui.TabSelectWidget.parent.call( this, config );
+
+       // Mixin constructors
+       OO.ui.mixin.TabIndexedElement.call( this, config );
+
+       // Events
+       this.$element.on( {
+               focus: this.bindKeyDownListener.bind( this ),
+               blur: this.unbindKeyDownListener.bind( this )
+       } );
+
+       // Initialization
+       this.$element.addClass( 'oo-ui-tabSelectWidget' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.TabSelectWidget, OO.ui.SelectWidget );
+OO.mixinClass( OO.ui.TabSelectWidget, OO.ui.mixin.TabIndexedElement );
+
+/**
+ * CapsuleItemWidgets are used within a {@link OO.ui.CapsuleMultiSelectWidget
+ * CapsuleMultiSelectWidget} to display the selected items.
+ *
+ * @class
+ * @extends OO.ui.Widget
+ * @mixins OO.ui.mixin.ItemWidget
+ * @mixins OO.ui.mixin.LabelElement
+ * @mixins OO.ui.mixin.FlaggedElement
+ * @mixins OO.ui.mixin.TabIndexedElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.CapsuleItemWidget = function OoUiCapsuleItemWidget( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Parent constructor
+       OO.ui.CapsuleItemWidget.parent.call( this, config );
+
+       // Mixin constructors
+       OO.ui.mixin.ItemWidget.call( this );
+       OO.ui.mixin.LabelElement.call( this, config );
+       OO.ui.mixin.FlaggedElement.call( this, config );
+       OO.ui.mixin.TabIndexedElement.call( this, config );
+
+       // Events
+       this.closeButton = new OO.ui.ButtonWidget( {
+               framed: false,
+               indicator: 'clear',
+               tabIndex: -1
+       } ).on( 'click', this.onCloseClick.bind( this ) );
+
+       this.on( 'disable', function ( disabled ) {
+               this.closeButton.setDisabled( disabled );
+       }.bind( this ) );
+
+       // Initialization
+       this.$element
+               .on( {
+                       click: this.onClick.bind( this ),
+                       keydown: this.onKeyDown.bind( this )
+               } )
+               .addClass( 'oo-ui-capsuleItemWidget' )
+               .append( this.$label, this.closeButton.$element );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.CapsuleItemWidget, OO.ui.Widget );
+OO.mixinClass( OO.ui.CapsuleItemWidget, OO.ui.mixin.ItemWidget );
+OO.mixinClass( OO.ui.CapsuleItemWidget, OO.ui.mixin.LabelElement );
+OO.mixinClass( OO.ui.CapsuleItemWidget, OO.ui.mixin.FlaggedElement );
+OO.mixinClass( OO.ui.CapsuleItemWidget, OO.ui.mixin.TabIndexedElement );
+
+/* Methods */
+
+/**
+ * Handle close icon clicks
+ */
+OO.ui.CapsuleItemWidget.prototype.onCloseClick = function () {
+       var element = this.getElementGroup();
+
+       if ( element && $.isFunction( element.removeItems ) ) {
+               element.removeItems( [ this ] );
+               element.focus();
+       }
+};
+
+/**
+ * Handle click event for the entire capsule
+ */
+OO.ui.CapsuleItemWidget.prototype.onClick = function () {
+       var element = this.getElementGroup();
+
+       if ( !this.isDisabled() && element && $.isFunction( element.editItem ) ) {
+               element.editItem( this );
+       }
+};
+
+/**
+ * Handle keyDown event for the entire capsule
+ */
+OO.ui.CapsuleItemWidget.prototype.onKeyDown = function ( e ) {
+       var element = this.getElementGroup();
+
+       if ( e.keyCode === OO.ui.Keys.BACKSPACE || e.keyCode === OO.ui.Keys.DELETE ) {
+               element.removeItems( [ this ] );
+               element.focus();
+               return false;
+       } else if ( e.keyCode === OO.ui.Keys.ENTER ) {
+               element.editItem( this );
+               return false;
+       } else if ( e.keyCode === OO.ui.Keys.LEFT ) {
+               element.getPreviousItem( this ).focus();
+       } else if ( e.keyCode === OO.ui.Keys.RIGHT ) {
+               element.getNextItem( this ).focus();
+       }
+};
+
+/**
+ * Focuses the capsule
+ */
+OO.ui.CapsuleItemWidget.prototype.focus = function () {
+       this.$element.focus();
+};
+
+/**
+ * CapsuleMultiSelectWidgets are something like a {@link OO.ui.ComboBoxInputWidget combo box widget}
+ * that allows for selecting multiple values.
+ *
+ * For more information about menus and options, please see the [OOjs UI documentation on MediaWiki][1].
+ *
+ *     @example
+ *     // Example: A CapsuleMultiSelectWidget.
+ *     var capsule = new OO.ui.CapsuleMultiSelectWidget( {
+ *         label: 'CapsuleMultiSelectWidget',
+ *         selected: [ 'Option 1', 'Option 3' ],
+ *         menu: {
+ *             items: [
+ *                 new OO.ui.MenuOptionWidget( {
+ *                     data: 'Option 1',
+ *                     label: 'Option One'
+ *                 } ),
+ *                 new OO.ui.MenuOptionWidget( {
+ *                     data: 'Option 2',
+ *                     label: 'Option Two'
+ *                 } ),
+ *                 new OO.ui.MenuOptionWidget( {
+ *                     data: 'Option 3',
+ *                     label: 'Option Three'
+ *                 } ),
+ *                 new OO.ui.MenuOptionWidget( {
+ *                     data: 'Option 4',
+ *                     label: 'Option Four'
+ *                 } ),
+ *                 new OO.ui.MenuOptionWidget( {
+ *                     data: 'Option 5',
+ *                     label: 'Option Five'
+ *                 } )
+ *             ]
+ *         }
+ *     } );
+ *     $( 'body' ).append( capsule.$element );
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options#Menu_selects_and_options
+ *
+ * @class
+ * @extends OO.ui.Widget
+ * @mixins OO.ui.mixin.TabIndexedElement
+ * @mixins OO.ui.mixin.GroupElement
+ * @uses OO.ui.CapsuleItemWidget
+ * @uses OO.ui.FloatingMenuSelectWidget
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {boolean} [allowArbitrary=false] Allow data items to be added even if not present in the menu.
+ * @cfg {Object} [menu] (required) Configuration options to pass to the
+ *  {@link OO.ui.MenuSelectWidget menu select widget}.
+ * @cfg {Object} [popup] Configuration options to pass to the {@link OO.ui.PopupWidget popup widget}.
+ *  If specified, this popup will be shown instead of the menu (but the menu
+ *  will still be used for item labels and allowArbitrary=false). The widgets
+ *  in the popup should use {@link #addItemsFromData} or {@link #addItems} as necessary.
+ * @cfg {jQuery} [$overlay=this.$element] Render the menu or popup into a separate layer.
+ *  This configuration is useful in cases where the expanded menu is larger than
+ *  its containing `<div>`. The specified overlay layer is usually on top of
+ *  the containing `<div>` and has a larger area. By default, the menu uses
+ *  relative positioning.
+ */
+OO.ui.CapsuleMultiSelectWidget = function OoUiCapsuleMultiSelectWidget( config ) {
+       var $tabFocus;
+
+       // Parent constructor
+       OO.ui.CapsuleMultiSelectWidget.parent.call( this, config );
+
+       // Configuration initialization
+       config = $.extend( {
+               allowArbitrary: false,
+               $overlay: this.$element
+       }, config );
+
+       // Properties (must be set before mixin constructor calls)
+       this.$input = config.popup ? null : $( '<input>' );
+       this.$handle = $( '<div>' );
+
+       // Mixin constructors
+       OO.ui.mixin.GroupElement.call( this, config );
+       if ( config.popup ) {
+               config.popup = $.extend( {}, config.popup, {
+                       align: 'forwards',
+                       anchor: false
+               } );
+               OO.ui.mixin.PopupElement.call( this, config );
+               $tabFocus = $( '<span>' );
+               OO.ui.mixin.TabIndexedElement.call( this, $.extend( {}, config, { $tabIndexed: $tabFocus } ) );
+       } else {
+               this.popup = null;
+               $tabFocus = null;
+               OO.ui.mixin.TabIndexedElement.call( this, $.extend( {}, config, { $tabIndexed: this.$input } ) );
+       }
+       OO.ui.mixin.IndicatorElement.call( this, config );
+       OO.ui.mixin.IconElement.call( this, config );
+
+       // Properties
+       this.$content = $( '<div>' );
+       this.allowArbitrary = config.allowArbitrary;
+       this.$overlay = config.$overlay;
+       this.menu = new OO.ui.FloatingMenuSelectWidget( $.extend(
+               {
+                       widget: this,
+                       $input: this.$input,
+                       $container: this.$element,
+                       filterFromInput: true,
+                       disabled: this.isDisabled()
+               },
+               config.menu
+       ) );
+
+       // Events
+       if ( this.popup ) {
+               $tabFocus.on( {
+                       focus: this.onFocusForPopup.bind( this )
+               } );
+               this.popup.$element.on( 'focusout', this.onPopupFocusOut.bind( this ) );
+               if ( this.popup.$autoCloseIgnore ) {
+                       this.popup.$autoCloseIgnore.on( 'focusout', this.onPopupFocusOut.bind( this ) );
+               }
+               this.popup.connect( this, {
+                       toggle: function ( visible ) {
+                               $tabFocus.toggle( !visible );
+                       }
+               } );
+       } else {
+               this.$input.on( {
+                       focus: this.onInputFocus.bind( this ),
+                       blur: this.onInputBlur.bind( this ),
+                       'propertychange change click mouseup keydown keyup input cut paste select focus':
+                               OO.ui.debounce( this.updateInputSize.bind( this ) ),
+                       keydown: this.onKeyDown.bind( this ),
+                       keypress: this.onKeyPress.bind( this )
+               } );
+       }
+       this.menu.connect( this, {
+               choose: 'onMenuChoose',
+               add: 'onMenuItemsChange',
+               remove: 'onMenuItemsChange'
+       } );
+       this.$handle.on( {
+               mousedown: this.onMouseDown.bind( this )
+       } );
+
+       // Initialization
+       if ( this.$input ) {
+               this.$input.prop( 'disabled', this.isDisabled() );
+               this.$input.attr( {
+                       role: 'combobox',
+                       'aria-autocomplete': 'list'
+               } );
+               this.updateInputSize();
+       }
+       if ( config.data ) {
+               this.setItemsFromData( config.data );
+       }
+       this.$content.addClass( 'oo-ui-capsuleMultiSelectWidget-content' )
+               .append( this.$group );
+       this.$group.addClass( 'oo-ui-capsuleMultiSelectWidget-group' );
+       this.$handle.addClass( 'oo-ui-capsuleMultiSelectWidget-handle' )
+               .append( this.$indicator, this.$icon, this.$content );
+       this.$element.addClass( 'oo-ui-capsuleMultiSelectWidget' )
+               .append( this.$handle );
+       if ( this.popup ) {
+               this.$content.append( $tabFocus );
+               this.$overlay.append( this.popup.$element );
+       } else {
+               this.$content.append( this.$input );
+               this.$overlay.append( this.menu.$element );
+       }
+       this.onMenuItemsChange();
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.CapsuleMultiSelectWidget, OO.ui.Widget );
+OO.mixinClass( OO.ui.CapsuleMultiSelectWidget, OO.ui.mixin.GroupElement );
+OO.mixinClass( OO.ui.CapsuleMultiSelectWidget, OO.ui.mixin.PopupElement );
+OO.mixinClass( OO.ui.CapsuleMultiSelectWidget, OO.ui.mixin.TabIndexedElement );
+OO.mixinClass( OO.ui.CapsuleMultiSelectWidget, OO.ui.mixin.IndicatorElement );
+OO.mixinClass( OO.ui.CapsuleMultiSelectWidget, OO.ui.mixin.IconElement );
+
+/* Events */
+
+/**
+ * @event change
+ *
+ * A change event is emitted when the set of selected items changes.
+ *
+ * @param {Mixed[]} datas Data of the now-selected items
+ */
+
+/* Methods */
+
+/**
+ * Construct a OO.ui.CapsuleItemWidget (or a subclass thereof) from given label and data.
+ *
+ * @protected
+ * @param {Mixed} data Custom data of any type.
+ * @param {string} label The label text.
+ * @return {OO.ui.CapsuleItemWidget}
+ */
+OO.ui.CapsuleMultiSelectWidget.prototype.createItemWidget = function ( data, label ) {
+       return new OO.ui.CapsuleItemWidget( { data: data, label: label } );
+};
+
+/**
+ * Get the data of the items in the capsule
+ * @return {Mixed[]}
+ */
+OO.ui.CapsuleMultiSelectWidget.prototype.getItemsData = function () {
+       return $.map( this.getItems(), function ( e ) { return e.data; } );
+};
+
+/**
+ * Set the items in the capsule by providing data
+ * @chainable
+ * @param {Mixed[]} datas
+ * @return {OO.ui.CapsuleMultiSelectWidget}
+ */
+OO.ui.CapsuleMultiSelectWidget.prototype.setItemsFromData = function ( datas ) {
+       var widget = this,
+               menu = this.menu,
+               items = this.getItems();
+
+       $.each( datas, function ( i, data ) {
+               var j, label,
+                       item = menu.getItemFromData( data );
+
+               if ( item ) {
+                       label = item.label;
+               } else if ( widget.allowArbitrary ) {
+                       label = String( data );
+               } else {
+                       return;
+               }
+
+               item = null;
+               for ( j = 0; j < items.length; j++ ) {
+                       if ( items[ j ].data === data && items[ j ].label === label ) {
+                               item = items[ j ];
+                               items.splice( j, 1 );
+                               break;
+                       }
+               }
+               if ( !item ) {
+                       item = widget.createItemWidget( data, label );
+               }
+               widget.addItems( [ item ], i );
+       } );
+
+       if ( items.length ) {
+               widget.removeItems( items );
+       }
+
+       return this;
+};
+
+/**
+ * Add items to the capsule by providing their data
+ * @chainable
+ * @param {Mixed[]} datas
+ * @return {OO.ui.CapsuleMultiSelectWidget}
+ */
+OO.ui.CapsuleMultiSelectWidget.prototype.addItemsFromData = function ( datas ) {
+       var widget = this,
+               menu = this.menu,
+               items = [];
+
+       $.each( datas, function ( i, data ) {
+               var item;
+
+               if ( !widget.getItemFromData( data ) ) {
+                       item = menu.getItemFromData( data );
+                       if ( item ) {
+                               items.push( widget.createItemWidget( data, item.label ) );
+                       } else if ( widget.allowArbitrary ) {
+                               items.push( widget.createItemWidget( data, String( data ) ) );
+                       }
+               }
+       } );
+
+       if ( items.length ) {
+               this.addItems( items );
+       }
+
+       return this;
+};
+
+/**
+ * Add items to the capsule by providing a label
+ * @param {string} label
+ * @return {boolean} Whether the item was added or not
+ */
+OO.ui.CapsuleMultiSelectWidget.prototype.addItemFromLabel = function ( label ) {
+       var item = this.menu.getItemFromLabel( label, true );
+       if ( item ) {
+               this.addItemsFromData( [ item.data ] );
+               return true;
+       } else if ( this.allowArbitrary && this.$input.val().trim() !== '' ) {
+               this.addItemsFromData( [ label ] );
+               return true;
+       }
+       return false;
+};
+
+/**
+ * Remove items by data
+ * @chainable
+ * @param {Mixed[]} datas
+ * @return {OO.ui.CapsuleMultiSelectWidget}
+ */
+OO.ui.CapsuleMultiSelectWidget.prototype.removeItemsFromData = function ( datas ) {
+       var widget = this,
+               items = [];
+
+       $.each( datas, function ( i, data ) {
+               var item = widget.getItemFromData( data );
+               if ( item ) {
+                       items.push( item );
+               }
+       } );
+
+       if ( items.length ) {
+               this.removeItems( items );
+       }
+
+       return this;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.CapsuleMultiSelectWidget.prototype.addItems = function ( items ) {
+       var same, i, l,
+               oldItems = this.items.slice();
+
+       OO.ui.mixin.GroupElement.prototype.addItems.call( this, items );
+
+       if ( this.items.length !== oldItems.length ) {
+               same = false;
+       } else {
+               same = true;
+               for ( i = 0, l = oldItems.length; same && i < l; i++ ) {
+                       same = same && this.items[ i ] === oldItems[ i ];
+               }
+       }
+       if ( !same ) {
+               this.emit( 'change', this.getItemsData() );
+               this.menu.position();
+       }
+
+       return this;
+};
+
+/**
+ * Removes the item from the list and copies its label to `this.$input`.
+ *
+ * @param {Object} item
+ */
+OO.ui.CapsuleMultiSelectWidget.prototype.editItem = function ( item ) {
+       this.$input.val( item.label );
+       this.updateInputSize();
+       this.focus();
+       this.removeItems( [ item ] );
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.CapsuleMultiSelectWidget.prototype.removeItems = function ( items ) {
+       var same, i, l,
+               oldItems = this.items.slice();
+
+       OO.ui.mixin.GroupElement.prototype.removeItems.call( this, items );
+
+       if ( this.items.length !== oldItems.length ) {
+               same = false;
+       } else {
+               same = true;
+               for ( i = 0, l = oldItems.length; same && i < l; i++ ) {
+                       same = same && this.items[ i ] === oldItems[ i ];
+               }
+       }
+       if ( !same ) {
+               this.emit( 'change', this.getItemsData() );
+               this.menu.position();
+       }
+
+       return this;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.CapsuleMultiSelectWidget.prototype.clearItems = function () {
+       if ( this.items.length ) {
+               OO.ui.mixin.GroupElement.prototype.clearItems.call( this );
+               this.emit( 'change', this.getItemsData() );
+               this.menu.position();
+       }
+       return this;
+};
+
+/**
+ * Given an item, returns the item after it. If its the last item,
+ * returns `this.$input`. If no item is passed, returns the very first
+ * item.
+ *
+ * @param {OO.ui.CapsuleItemWidget} [item]
+ * @return {OO.ui.CapsuleItemWidget|jQuery|boolean}
+ */
+OO.ui.CapsuleMultiSelectWidget.prototype.getNextItem = function ( item ) {
+       var itemIndex;
+
+       if ( item === undefined ) {
+               return this.items[ 0 ];
+       }
+
+       itemIndex = this.items.indexOf( item );
+       if ( itemIndex < 0 ) { // Item not in list
+               return false;
+       } else if ( itemIndex === this.items.length - 1 ) { // Last item
+               return this.$input;
+       } else {
+               return this.items[ itemIndex + 1 ];
+       }
+};
+
+/**
+ * Given an item, returns the item before it. If its the first item,
+ * returns `this.$input`. If no item is passed, returns the very last
+ * item.
+ *
+ * @param {OO.ui.CapsuleItemWidget} [item]
+ * @return {OO.ui.CapsuleItemWidget|jQuery|boolean}
+ */
+OO.ui.CapsuleMultiSelectWidget.prototype.getPreviousItem = function ( item ) {
+       var itemIndex;
+
+       if ( item === undefined ) {
+               return this.items[ this.items.length - 1 ];
+       }
+
+       itemIndex = this.items.indexOf( item );
+       if ( itemIndex < 0 ) { // Item not in list
+               return false;
+       } else if ( itemIndex === 0 ) { // First item
+               return this.$input;
+       } else {
+               return this.items[ itemIndex - 1 ];
+       }
+};
+
+/**
+ * Get the capsule widget's menu.
+ * @return {OO.ui.MenuSelectWidget} Menu widget
+ */
+OO.ui.CapsuleMultiSelectWidget.prototype.getMenu = function () {
+       return this.menu;
+};
+
+/**
+ * Handle focus events
+ *
+ * @private
+ * @param {jQuery.Event} event
+ */
+OO.ui.CapsuleMultiSelectWidget.prototype.onInputFocus = function () {
+       if ( !this.isDisabled() ) {
+               this.menu.toggle( true );
+       }
+};
+
+/**
+ * Handle blur events
+ *
+ * @private
+ * @param {jQuery.Event} event
+ */
+OO.ui.CapsuleMultiSelectWidget.prototype.onInputBlur = function () {
+       this.addItemFromLabel( this.$input.val() );
+       this.clearInput();
+};
+
+/**
+ * Handle focus events
+ *
+ * @private
+ * @param {jQuery.Event} event
+ */
+OO.ui.CapsuleMultiSelectWidget.prototype.onFocusForPopup = function () {
+       if ( !this.isDisabled() ) {
+               this.popup.setSize( this.$handle.width() );
+               this.popup.toggle( true );
+               this.popup.$element.find( '*' )
+                       .filter( function () { return OO.ui.isFocusableElement( $( this ), true ); } )
+                       .first()
+                       .focus();
+       }
+};
+
+/**
+ * Handles popup focus out events.
+ *
+ * @private
+ * @param {jQuery.Event} e Focus out event
+ */
+OO.ui.CapsuleMultiSelectWidget.prototype.onPopupFocusOut = function () {
+       var widget = this.popup;
+
+       setTimeout( function () {
+               if (
+                       widget.isVisible() &&
+                       !OO.ui.contains( widget.$element[ 0 ], document.activeElement, true ) &&
+                       ( !widget.$autoCloseIgnore || !widget.$autoCloseIgnore.has( document.activeElement ).length )
+               ) {
+                       widget.toggle( false );
+               }
+       } );
+};
+
+/**
+ * Handle mouse down events.
+ *
+ * @private
+ * @param {jQuery.Event} e Mouse down event
+ */
+OO.ui.CapsuleMultiSelectWidget.prototype.onMouseDown = function ( e ) {
+       if ( e.which === OO.ui.MouseButtons.LEFT ) {
+               this.focus();
+               return false;
+       } else {
+               this.updateInputSize();
+       }
+};
+
+/**
+ * Handle key press events.
+ *
+ * @private
+ * @param {jQuery.Event} e Key press event
+ */
+OO.ui.CapsuleMultiSelectWidget.prototype.onKeyPress = function ( e ) {
+       if ( !this.isDisabled() ) {
+               if ( e.which === OO.ui.Keys.ESCAPE ) {
+                       this.clearInput();
+                       return false;
+               }
+
+               if ( !this.popup ) {
+                       this.menu.toggle( true );
+                       if ( e.which === OO.ui.Keys.ENTER ) {
+                               if ( this.addItemFromLabel( this.$input.val() ) ) {
+                                       this.clearInput();
+                               }
+                               return false;
+                       }
+
+                       // Make sure the input gets resized.
+                       setTimeout( this.updateInputSize.bind( this ), 0 );
+               }
+       }
+};
+
+/**
+ * Handle key down events.
+ *
+ * @private
+ * @param {jQuery.Event} e Key down event
+ */
+OO.ui.CapsuleMultiSelectWidget.prototype.onKeyDown = function ( e ) {
+       if (
+               !this.isDisabled() &&
+               this.$input.val() === '' &&
+               this.items.length
+       ) {
+               // 'keypress' event is not triggered for Backspace
+               if ( e.keyCode === OO.ui.Keys.BACKSPACE ) {
+                       if ( e.metaKey || e.ctrlKey ) {
+                               this.removeItems( this.items.slice( -1 ) );
+                       } else {
+                               this.editItem( this.items[ this.items.length - 1 ] );
+                       }
+                       return false;
+               } else if ( e.keyCode === OO.ui.Keys.LEFT ) {
+                       this.getPreviousItem().focus();
+               } else if ( e.keyCode === OO.ui.Keys.RIGHT ) {
+                       this.getNextItem().focus();
+               }
+       }
+};
+
+/**
+ * Update the dimensions of the text input field to encompass all available area.
+ *
+ * @private
+ * @param {jQuery.Event} e Event of some sort
+ */
+OO.ui.CapsuleMultiSelectWidget.prototype.updateInputSize = function () {
+       var $lastItem, direction, contentWidth, currentWidth, bestWidth;
+       if ( !this.isDisabled() ) {
+               this.$input.css( 'width', '1em' );
+               $lastItem = this.$group.children().last();
+               direction = OO.ui.Element.static.getDir( this.$handle );
+               contentWidth = this.$input[ 0 ].scrollWidth;
+               currentWidth = this.$input.width();
+
+               if ( contentWidth < currentWidth ) {
+                       // All is fine, don't perform expensive calculations
+                       return;
+               }
+
+               if ( !$lastItem.length ) {
+                       bestWidth = this.$content.innerWidth();
+               } else {
+                       bestWidth = direction === 'ltr' ?
+                               this.$content.innerWidth() - $lastItem.position().left - $lastItem.outerWidth() :
+                               $lastItem.position().left;
+               }
+               // Some safety margin for sanity, because I *really* don't feel like finding out where the few
+               // pixels this is off by are coming from.
+               bestWidth -= 10;
+               if ( contentWidth > bestWidth ) {
+                       // This will result in the input getting shifted to the next line
+                       bestWidth = this.$content.innerWidth() - 10;
+               }
+               this.$input.width( Math.floor( bestWidth ) );
+
+               this.menu.position();
+       }
+};
+
+/**
+ * Handle menu choose events.
+ *
+ * @private
+ * @param {OO.ui.OptionWidget} item Chosen item
+ */
+OO.ui.CapsuleMultiSelectWidget.prototype.onMenuChoose = function ( item ) {
+       if ( item && item.isVisible() ) {
+               this.addItemsFromData( [ item.getData() ] );
+               this.clearInput();
+       }
+};
+
+/**
+ * Handle menu item change events.
+ *
+ * @private
+ */
+OO.ui.CapsuleMultiSelectWidget.prototype.onMenuItemsChange = function () {
+       this.setItemsFromData( this.getItemsData() );
+       this.$element.toggleClass( 'oo-ui-capsuleMultiSelectWidget-empty', this.menu.isEmpty() );
+};
+
+/**
+ * Clear the input field
+ * @private
+ */
+OO.ui.CapsuleMultiSelectWidget.prototype.clearInput = function () {
+       if ( this.$input ) {
+               this.$input.val( '' );
+               this.updateInputSize();
+       }
+       if ( this.popup ) {
+               this.popup.toggle( false );
+       }
+       this.menu.toggle( false );
+       this.menu.selectItem();
+       this.menu.highlightItem();
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.CapsuleMultiSelectWidget.prototype.setDisabled = function ( disabled ) {
+       var i, len;
+
+       // Parent method
+       OO.ui.CapsuleMultiSelectWidget.parent.prototype.setDisabled.call( this, disabled );
+
+       if ( this.$input ) {
+               this.$input.prop( 'disabled', this.isDisabled() );
+       }
+       if ( this.menu ) {
+               this.menu.setDisabled( this.isDisabled() );
+       }
+       if ( this.popup ) {
+               this.popup.setDisabled( this.isDisabled() );
+       }
+
+       if ( this.items ) {
+               for ( i = 0, len = this.items.length; i < len; i++ ) {
+                       this.items[ i ].updateDisabled();
+               }
+       }
+
+       return this;
+};
+
+/**
+ * Focus the widget
+ * @chainable
+ * @return {OO.ui.CapsuleMultiSelectWidget}
+ */
+OO.ui.CapsuleMultiSelectWidget.prototype.focus = function () {
+       if ( !this.isDisabled() ) {
+               if ( this.popup ) {
+                       this.popup.setSize( this.$handle.width() );
+                       this.popup.toggle( true );
+                       this.popup.$element.find( '*' )
+                               .filter( function () { return OO.ui.isFocusableElement( $( this ), true ); } )
+                               .first()
+                               .focus();
+               } else {
+                       this.updateInputSize();
+                       this.menu.toggle( true );
+                       this.$input.focus();
+               }
+       }
+       return this;
+};
+
+/**
+ * SelectFileWidgets allow for selecting files, using the HTML5 File API. These
+ * widgets can be configured with {@link OO.ui.mixin.IconElement icons} and {@link
+ * OO.ui.mixin.IndicatorElement indicators}.
+ * Please see the [OOjs UI documentation on MediaWiki] [1] for more information and examples.
+ *
+ *     @example
+ *     // Example of a file select widget
+ *     var selectFile = new OO.ui.SelectFileWidget();
+ *     $( 'body' ).append( selectFile.$element );
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets
+ *
+ * @class
+ * @extends OO.ui.Widget
+ * @mixins OO.ui.mixin.IconElement
+ * @mixins OO.ui.mixin.IndicatorElement
+ * @mixins OO.ui.mixin.PendingElement
+ * @mixins OO.ui.mixin.LabelElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {string[]|null} [accept=null] MIME types to accept. null accepts all types.
+ * @cfg {string} [placeholder] Text to display when no file is selected.
+ * @cfg {string} [notsupported] Text to display when file support is missing in the browser.
+ * @cfg {boolean} [droppable=true] Whether to accept files by drag and drop.
+ * @cfg {boolean} [showDropTarget=false] Whether to show a drop target. Requires droppable to be true.
+ * @cfg {boolean} [dragDropUI=false] Deprecated alias for showDropTarget
+ * @cfg {Number} [thumbnailSizeLimit=20] File size limit in MiB above which to not try and show a
+ *  preview (for performance)
+ */
+OO.ui.SelectFileWidget = function OoUiSelectFileWidget( config ) {
+       var dragHandler;
+
+       // TODO: Remove in next release
+       if ( config && config.dragDropUI ) {
+               config.showDropTarget = true;
+       }
+
+       // Configuration initialization
+       config = $.extend( {
+               accept: null,
+               placeholder: OO.ui.msg( 'ooui-selectfile-placeholder' ),
+               notsupported: OO.ui.msg( 'ooui-selectfile-not-supported' ),
+               droppable: true,
+               showDropTarget: false,
+               thumbnailSizeLimit: 20
+       }, config );
+
+       // Parent constructor
+       OO.ui.SelectFileWidget.parent.call( this, config );
+
+       // Mixin constructors
+       OO.ui.mixin.IconElement.call( this, config );
+       OO.ui.mixin.IndicatorElement.call( this, config );
+       OO.ui.mixin.PendingElement.call( this, $.extend( {}, config, { $pending: this.$info } ) );
+       OO.ui.mixin.LabelElement.call( this, $.extend( {}, config, { autoFitLabel: true } ) );
+
+       // Properties
+       this.$info = $( '<span>' );
+       this.showDropTarget = config.showDropTarget;
+       this.thumbnailSizeLimit = config.thumbnailSizeLimit;
+       this.isSupported = this.constructor.static.isSupported();
+       this.currentFile = null;
+       if ( Array.isArray( config.accept ) ) {
+               this.accept = config.accept;
+       } else {
+               this.accept = null;
+       }
+       this.placeholder = config.placeholder;
+       this.notsupported = config.notsupported;
+       this.onFileSelectedHandler = this.onFileSelected.bind( this );
+
+       this.selectButton = new OO.ui.ButtonWidget( {
+               classes: [ 'oo-ui-selectFileWidget-selectButton' ],
+               label: OO.ui.msg( 'ooui-selectfile-button-select' ),
+               disabled: this.disabled || !this.isSupported
+       } );
+
+       this.clearButton = new OO.ui.ButtonWidget( {
+               classes: [ 'oo-ui-selectFileWidget-clearButton' ],
+               framed: false,
+               icon: 'close',
+               disabled: this.disabled
+       } );
+
+       // Events
+       this.selectButton.$button.on( {
+               keypress: this.onKeyPress.bind( this )
+       } );
+       this.clearButton.connect( this, {
+               click: 'onClearClick'
+       } );
+       if ( config.droppable ) {
+               dragHandler = this.onDragEnterOrOver.bind( this );
+               this.$element.on( {
+                       dragenter: dragHandler,
+                       dragover: dragHandler,
+                       dragleave: this.onDragLeave.bind( this ),
+                       drop: this.onDrop.bind( this )
+               } );
+       }
+
+       // Initialization
+       this.addInput();
+       this.$label.addClass( 'oo-ui-selectFileWidget-label' );
+       this.$info
+               .addClass( 'oo-ui-selectFileWidget-info' )
+               .append( this.$icon, this.$label, this.clearButton.$element, this.$indicator );
+
+       if ( config.droppable && config.showDropTarget ) {
+               this.selectButton.setIcon( 'upload' );
+               this.$thumbnail = $( '<div>' ).addClass( 'oo-ui-selectFileWidget-thumbnail' );
+               this.setPendingElement( this.$thumbnail );
+               this.$dropTarget = $( '<div>' )
+                       .addClass( 'oo-ui-selectFileWidget-dropTarget' )
+                       .on( {
+                               click: this.onDropTargetClick.bind( this )
+                       } )
+                       .append(
+                               this.$thumbnail,
+                               this.$info,
+                               this.selectButton.$element,
+                               $( '<span>' )
+                                       .addClass( 'oo-ui-selectFileWidget-dropLabel' )
+                                       .text( OO.ui.msg( 'ooui-selectfile-dragdrop-placeholder' ) )
+                       );
+               this.$element.append( this.$dropTarget );
+       } else {
+               this.$element
+                       .addClass( 'oo-ui-selectFileWidget' )
+                       .append( this.$info, this.selectButton.$element );
+       }
+       this.updateUI();
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.SelectFileWidget, OO.ui.Widget );
+OO.mixinClass( OO.ui.SelectFileWidget, OO.ui.mixin.IconElement );
+OO.mixinClass( OO.ui.SelectFileWidget, OO.ui.mixin.IndicatorElement );
+OO.mixinClass( OO.ui.SelectFileWidget, OO.ui.mixin.PendingElement );
+OO.mixinClass( OO.ui.SelectFileWidget, OO.ui.mixin.LabelElement );
+
+/* Static Properties */
+
+/**
+ * Check if this widget is supported
+ *
+ * @static
+ * @return {boolean}
+ */
+OO.ui.SelectFileWidget.static.isSupported = function () {
+       var $input;
+       if ( OO.ui.SelectFileWidget.static.isSupportedCache === null ) {
+               $input = $( '<input type="file">' );
+               OO.ui.SelectFileWidget.static.isSupportedCache = $input[ 0 ].files !== undefined;
+       }
+       return OO.ui.SelectFileWidget.static.isSupportedCache;
+};
+
+OO.ui.SelectFileWidget.static.isSupportedCache = null;
+
+/* Events */
+
+/**
+ * @event change
+ *
+ * A change event is emitted when the on/off state of the toggle changes.
+ *
+ * @param {File|null} value New value
+ */
+
+/* Methods */
+
+/**
+ * Get the current value of the field
+ *
+ * @return {File|null}
+ */
+OO.ui.SelectFileWidget.prototype.getValue = function () {
+       return this.currentFile;
+};
+
+/**
+ * Set the current value of the field
+ *
+ * @param {File|null} file File to select
+ */
+OO.ui.SelectFileWidget.prototype.setValue = function ( file ) {
+       if ( this.currentFile !== file ) {
+               this.currentFile = file;
+               this.updateUI();
+               this.emit( 'change', this.currentFile );
+       }
+};
+
+/**
+ * Focus the widget.
+ *
+ * Focusses the select file button.
+ *
+ * @chainable
+ */
+OO.ui.SelectFileWidget.prototype.focus = function () {
+       this.selectButton.$button[ 0 ].focus();
+       return this;
+};
+
+/**
+ * Update the user interface when a file is selected or unselected
+ *
+ * @protected
+ */
+OO.ui.SelectFileWidget.prototype.updateUI = function () {
+       var $label;
+       if ( !this.isSupported ) {
+               this.$element.addClass( 'oo-ui-selectFileWidget-notsupported' );
+               this.$element.removeClass( 'oo-ui-selectFileWidget-empty' );
+               this.setLabel( this.notsupported );
+       } else {
+               this.$element.addClass( 'oo-ui-selectFileWidget-supported' );
+               if ( this.currentFile ) {
+                       this.$element.removeClass( 'oo-ui-selectFileWidget-empty' );
+                       $label = $( [] );
+                       $label = $label.add(
+                               $( '<span>' )
+                                       .addClass( 'oo-ui-selectFileWidget-fileName' )
+                                       .text( this.currentFile.name )
+                       );
+                       if ( this.currentFile.type !== '' ) {
+                               $label = $label.add(
+                                       $( '<span>' )
+                                               .addClass( 'oo-ui-selectFileWidget-fileType' )
+                                               .text( this.currentFile.type )
+                               );
+                       }
+                       this.setLabel( $label );
+
+                       if ( this.showDropTarget ) {
+                               this.pushPending();
+                               this.loadAndGetImageUrl().done( function ( url ) {
+                                       this.$thumbnail.css( 'background-image', 'url( ' + url + ' )' );
+                               }.bind( this ) ).fail( function () {
+                                       this.$thumbnail.append(
+                                               new OO.ui.IconWidget( {
+                                                       icon: 'attachment',
+                                                       classes: [ 'oo-ui-selectFileWidget-noThumbnail-icon' ]
+                                               } ).$element
+                                       );
+                               }.bind( this ) ).always( function () {
+                                       this.popPending();
+                               }.bind( this ) );
+                               this.$dropTarget.off( 'click' );
+                       }
+               } else {
+                       if ( this.showDropTarget ) {
+                               this.$dropTarget.off( 'click' );
+                               this.$dropTarget.on( {
+                                       click: this.onDropTargetClick.bind( this )
+                               } );
+                               this.$thumbnail
+                                       .empty()
+                                       .css( 'background-image', '' );
+                       }
+                       this.$element.addClass( 'oo-ui-selectFileWidget-empty' );
+                       this.setLabel( this.placeholder );
+               }
+       }
+};
+
+/**
+ * If the selected file is an image, get its URL and load it.
+ *
+ * @return {jQuery.Promise} Promise resolves with the image URL after it has loaded
+ */
+OO.ui.SelectFileWidget.prototype.loadAndGetImageUrl = function () {
+       var deferred = $.Deferred(),
+               file = this.currentFile,
+               reader = new FileReader();
+
+       if (
+               file &&
+               ( OO.getProp( file, 'type' ) || '' ).indexOf( 'image/' ) === 0 &&
+               file.size < this.thumbnailSizeLimit * 1024 * 1024
+       ) {
+               reader.onload = function ( event ) {
+                       var img = document.createElement( 'img' );
+                       img.addEventListener( 'load', function () {
+                               if (
+                                       img.naturalWidth === 0 ||
+                                       img.naturalHeight === 0 ||
+                                       img.complete === false
+                               ) {
+                                       deferred.reject();
+                               } else {
+                                       deferred.resolve( event.target.result );
+                               }
+                       } );
+                       img.src = event.target.result;
+               };
+               reader.readAsDataURL( file );
+       } else {
+               deferred.reject();
+       }
+
+       return deferred.promise();
+};
+
+/**
+ * Add the input to the widget
+ *
+ * @private
+ */
+OO.ui.SelectFileWidget.prototype.addInput = function () {
+       if ( this.$input ) {
+               this.$input.remove();
+       }
+
+       if ( !this.isSupported ) {
+               this.$input = null;
+               return;
+       }
+
+       this.$input = $( '<input type="file">' );
+       this.$input.on( 'change', this.onFileSelectedHandler );
+       this.$input.on( 'click', function ( e ) {
+               // Prevents dropTarget to get clicked which calls
+               // a click on this input
+               e.stopPropagation();
+       } );
+       this.$input.attr( {
+               tabindex: -1
+       } );
+       if ( this.accept ) {
+               this.$input.attr( 'accept', this.accept.join( ', ' ) );
+       }
+       this.selectButton.$button.append( this.$input );
+};
+
+/**
+ * Determine if we should accept this file
+ *
+ * @private
+ * @param {string} File MIME type
+ * @return {boolean}
+ */
+OO.ui.SelectFileWidget.prototype.isAllowedType = function ( mimeType ) {
+       var i, mimeTest;
+
+       if ( !this.accept || !mimeType ) {
+               return true;
+       }
+
+       for ( i = 0; i < this.accept.length; i++ ) {
+               mimeTest = this.accept[ i ];
+               if ( mimeTest === mimeType ) {
+                       return true;
+               } else if ( mimeTest.substr( -2 ) === '/*' ) {
+                       mimeTest = mimeTest.substr( 0, mimeTest.length - 1 );
+                       if ( mimeType.substr( 0, mimeTest.length ) === mimeTest ) {
+                               return true;
+                       }
+               }
+       }
+
+       return false;
+};
+
+/**
+ * Handle file selection from the input
+ *
+ * @private
+ * @param {jQuery.Event} e
+ */
+OO.ui.SelectFileWidget.prototype.onFileSelected = function ( e ) {
+       var file = OO.getProp( e.target, 'files', 0 ) || null;
+
+       if ( file && !this.isAllowedType( file.type ) ) {
+               file = null;
+       }
+
+       this.setValue( file );
+       this.addInput();
+};
+
+/**
+ * Handle clear button click events.
+ *
+ * @private
+ */
+OO.ui.SelectFileWidget.prototype.onClearClick = function () {
+       this.setValue( null );
+       return false;
+};
+
+/**
+ * Handle key press events.
+ *
+ * @private
+ * @param {jQuery.Event} e Key press event
+ */
+OO.ui.SelectFileWidget.prototype.onKeyPress = function ( e ) {
+       if ( this.isSupported && !this.isDisabled() && this.$input &&
+               ( e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
+       ) {
+               this.$input.click();
+               return false;
+       }
+};
+
+/**
+ * Handle drop target click events.
+ *
+ * @private
+ * @param {jQuery.Event} e Key press event
+ */
+OO.ui.SelectFileWidget.prototype.onDropTargetClick = function () {
+       if ( this.isSupported && !this.isDisabled() && this.$input ) {
+               this.$input.click();
+               return false;
+       }
+};
+
+/**
+ * Handle drag enter and over events
+ *
+ * @private
+ * @param {jQuery.Event} e Drag event
+ */
+OO.ui.SelectFileWidget.prototype.onDragEnterOrOver = function ( e ) {
+       var itemOrFile,
+               droppableFile = false,
+               dt = e.originalEvent.dataTransfer;
+
+       e.preventDefault();
+       e.stopPropagation();
+
+       if ( this.isDisabled() || !this.isSupported ) {
+               this.$element.removeClass( 'oo-ui-selectFileWidget-canDrop' );
+               dt.dropEffect = 'none';
+               return false;
+       }
+
+       // DataTransferItem and File both have a type property, but in Chrome files
+       // have no information at this point.
+       itemOrFile = OO.getProp( dt, 'items', 0 ) || OO.getProp( dt, 'files', 0 );
+       if ( itemOrFile ) {
+               if ( this.isAllowedType( itemOrFile.type ) ) {
+                       droppableFile = true;
+               }
+       // dt.types is Array-like, but not an Array
+       } else if ( Array.prototype.indexOf.call( OO.getProp( dt, 'types' ) || [], 'Files' ) !== -1 ) {
+               // File information is not available at this point for security so just assume
+               // it is acceptable for now.
+               // https://bugzilla.mozilla.org/show_bug.cgi?id=640534
+               droppableFile = true;
+       }
+
+       this.$element.toggleClass( 'oo-ui-selectFileWidget-canDrop', droppableFile );
+       if ( !droppableFile ) {
+               dt.dropEffect = 'none';
+       }
+
+       return false;
+};
+
+/**
+ * Handle drag leave events
+ *
+ * @private
+ * @param {jQuery.Event} e Drag event
+ */
+OO.ui.SelectFileWidget.prototype.onDragLeave = function () {
+       this.$element.removeClass( 'oo-ui-selectFileWidget-canDrop' );
+};
+
+/**
+ * Handle drop events
+ *
+ * @private
+ * @param {jQuery.Event} e Drop event
+ */
+OO.ui.SelectFileWidget.prototype.onDrop = function ( e ) {
+       var file = null,
+               dt = e.originalEvent.dataTransfer;
+
+       e.preventDefault();
+       e.stopPropagation();
+       this.$element.removeClass( 'oo-ui-selectFileWidget-canDrop' );
+
+       if ( this.isDisabled() || !this.isSupported ) {
+               return false;
+       }
+
+       file = OO.getProp( dt, 'files', 0 );
+       if ( file && !this.isAllowedType( file.type ) ) {
+               file = null;
+       }
+       if ( file ) {
+               this.setValue( file );
+       }
+
+       return false;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.SelectFileWidget.prototype.setDisabled = function ( disabled ) {
+       OO.ui.SelectFileWidget.parent.prototype.setDisabled.call( this, disabled );
+       if ( this.selectButton ) {
+               this.selectButton.setDisabled( disabled );
+       }
+       if ( this.clearButton ) {
+               this.clearButton.setDisabled( disabled );
+       }
+       return this;
+};
+
+/**
+ * Progress bars visually display the status of an operation, such as a download,
+ * and can be either determinate or indeterminate:
+ *
+ * - **determinate** process bars show the percent of an operation that is complete.
+ *
+ * - **indeterminate** process bars use a visual display of motion to indicate that an operation
+ *   is taking place. Because the extent of an indeterminate operation is unknown, the bar does
+ *   not use percentages.
+ *
+ * The value of the `progress` configuration determines whether the bar is determinate or indeterminate.
+ *
+ *     @example
+ *     // Examples of determinate and indeterminate progress bars.
+ *     var progressBar1 = new OO.ui.ProgressBarWidget( {
+ *         progress: 33
+ *     } );
+ *     var progressBar2 = new OO.ui.ProgressBarWidget();
+ *
+ *     // Create a FieldsetLayout to layout progress bars
+ *     var fieldset = new OO.ui.FieldsetLayout;
+ *     fieldset.addItems( [
+ *        new OO.ui.FieldLayout( progressBar1, {label: 'Determinate', align: 'top'}),
+ *        new OO.ui.FieldLayout( progressBar2, {label: 'Indeterminate', align: 'top'})
+ *     ] );
+ *     $( 'body' ).append( fieldset.$element );
+ *
+ * @class
+ * @extends OO.ui.Widget
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {number|boolean} [progress=false] The type of progress bar (determinate or indeterminate).
+ *  To create a determinate progress bar, specify a number that reflects the initial percent complete.
+ *  By default, the progress bar is indeterminate.
+ */
+OO.ui.ProgressBarWidget = function OoUiProgressBarWidget( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Parent constructor
+       OO.ui.ProgressBarWidget.parent.call( this, config );
+
+       // Properties
+       this.$bar = $( '<div>' );
+       this.progress = null;
+
+       // Initialization
+       this.setProgress( config.progress !== undefined ? config.progress : false );
+       this.$bar.addClass( 'oo-ui-progressBarWidget-bar' );
+       this.$element
+               .attr( {
+                       role: 'progressbar',
+                       'aria-valuemin': 0,
+                       'aria-valuemax': 100
+               } )
+               .addClass( 'oo-ui-progressBarWidget' )
+               .append( this.$bar );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.ProgressBarWidget, OO.ui.Widget );
+
+/* Static Properties */
+
+OO.ui.ProgressBarWidget.static.tagName = 'div';
+
+/* Methods */
+
+/**
+ * Get the percent of the progress that has been completed. Indeterminate progresses will return `false`.
+ *
+ * @return {number|boolean} Progress percent
+ */
+OO.ui.ProgressBarWidget.prototype.getProgress = function () {
+       return this.progress;
+};
+
+/**
+ * Set the percent of the process completed or `false` for an indeterminate process.
+ *
+ * @param {number|boolean} progress Progress percent or `false` for indeterminate
+ */
+OO.ui.ProgressBarWidget.prototype.setProgress = function ( progress ) {
+       this.progress = progress;
+
+       if ( progress !== false ) {
+               this.$bar.css( 'width', this.progress + '%' );
+               this.$element.attr( 'aria-valuenow', this.progress );
+       } else {
+               this.$bar.css( 'width', '' );
+               this.$element.removeAttr( 'aria-valuenow' );
+       }
+       this.$element.toggleClass( 'oo-ui-progressBarWidget-indeterminate', !progress );
+};
+
+/**
+ * SearchWidgets combine a {@link OO.ui.TextInputWidget text input field}, where users can type a search query,
+ * and a menu of search results, which is displayed beneath the query
+ * field. Unlike {@link OO.ui.mixin.LookupElement lookup menus}, search result menus are always visible to the user.
+ * Users can choose an item from the menu or type a query into the text field to search for a matching result item.
+ * In general, search widgets are used inside a separate {@link OO.ui.Dialog dialog} window.
+ *
+ * Each time the query is changed, the search result menu is cleared and repopulated. Please see
+ * the [OOjs UI demos][1] for an example.
+ *
+ * [1]: https://tools.wmflabs.org/oojs-ui/oojs-ui/demos/#dialogs-mediawiki-vector-ltr
+ *
+ * @class
+ * @extends OO.ui.Widget
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {string|jQuery} [placeholder] Placeholder text for query input
+ * @cfg {string} [value] Initial query value
+ */
+OO.ui.SearchWidget = function OoUiSearchWidget( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Parent constructor
+       OO.ui.SearchWidget.parent.call( this, config );
+
+       // Properties
+       this.query = new OO.ui.TextInputWidget( {
+               icon: 'search',
+               placeholder: config.placeholder,
+               value: config.value
+       } );
+       this.results = new OO.ui.SelectWidget();
+       this.$query = $( '<div>' );
+       this.$results = $( '<div>' );
+
+       // Events
+       this.query.connect( this, {
+               change: 'onQueryChange',
+               enter: 'onQueryEnter'
+       } );
+       this.query.$input.on( 'keydown', this.onQueryKeydown.bind( this ) );
+
+       // Initialization
+       this.$query
+               .addClass( 'oo-ui-searchWidget-query' )
+               .append( this.query.$element );
+       this.$results
+               .addClass( 'oo-ui-searchWidget-results' )
+               .append( this.results.$element );
+       this.$element
+               .addClass( 'oo-ui-searchWidget' )
+               .append( this.$results, this.$query );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.SearchWidget, OO.ui.Widget );
+
+/* Methods */
+
+/**
+ * Handle query key down events.
+ *
+ * @private
+ * @param {jQuery.Event} e Key down event
+ */
+OO.ui.SearchWidget.prototype.onQueryKeydown = function ( e ) {
+       var highlightedItem, nextItem,
+               dir = e.which === OO.ui.Keys.DOWN ? 1 : ( e.which === OO.ui.Keys.UP ? -1 : 0 );
+
+       if ( dir ) {
+               highlightedItem = this.results.getHighlightedItem();
+               if ( !highlightedItem ) {
+                       highlightedItem = this.results.getSelectedItem();
+               }
+               nextItem = this.results.getRelativeSelectableItem( highlightedItem, dir );
+               this.results.highlightItem( nextItem );
+               nextItem.scrollElementIntoView();
+       }
+};
+
+/**
+ * Handle select widget select events.
+ *
+ * Clears existing results. Subclasses should repopulate items according to new query.
+ *
+ * @private
+ * @param {string} value New value
+ */
+OO.ui.SearchWidget.prototype.onQueryChange = function () {
+       // Reset
+       this.results.clearItems();
+};
+
+/**
+ * Handle select widget enter key events.
+ *
+ * Chooses highlighted item.
+ *
+ * @private
+ * @param {string} value New value
+ */
+OO.ui.SearchWidget.prototype.onQueryEnter = function () {
+       var highlightedItem = this.results.getHighlightedItem();
+       if ( highlightedItem ) {
+               this.results.chooseItem( highlightedItem );
+       }
+};
+
+/**
+ * Get the query input.
+ *
+ * @return {OO.ui.TextInputWidget} Query input
+ */
+OO.ui.SearchWidget.prototype.getQuery = function () {
+       return this.query;
+};
+
+/**
+ * Get the search results menu.
+ *
+ * @return {OO.ui.SelectWidget} Menu of search results
+ */
+OO.ui.SearchWidget.prototype.getResults = function () {
+       return this.results;
+};
+
+/**
+ * NumberInputWidgets combine a {@link OO.ui.TextInputWidget text input} (where a value
+ * can be entered manually) and two {@link OO.ui.ButtonWidget button widgets}
+ * (to adjust the value in increments) to allow the user to enter a number.
+ *
+ *     @example
+ *     // Example: A NumberInputWidget.
+ *     var numberInput = new OO.ui.NumberInputWidget( {
+ *         label: 'NumberInputWidget',
+ *         input: { value: 5, min: 1, max: 10 }
+ *     } );
+ *     $( 'body' ).append( numberInput.$element );
+ *
+ * @class
+ * @extends OO.ui.Widget
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {Object} [input] Configuration options to pass to the {@link OO.ui.TextInputWidget text input widget}.
+ * @cfg {Object} [minusButton] Configuration options to pass to the {@link OO.ui.ButtonWidget decrementing button widget}.
+ * @cfg {Object} [plusButton] Configuration options to pass to the {@link OO.ui.ButtonWidget incrementing button widget}.
+ * @cfg {boolean} [isInteger=false] Whether the field accepts only integer values.
+ * @cfg {number} [min=-Infinity] Minimum allowed value
+ * @cfg {number} [max=Infinity] Maximum allowed value
+ * @cfg {number} [step=1] Delta when using the buttons or up/down arrow keys
+ * @cfg {number|null} [pageStep] Delta when using the page-up/page-down keys. Defaults to 10 times #step.
+ */
+OO.ui.NumberInputWidget = function OoUiNumberInputWidget( config ) {
+       // Configuration initialization
+       config = $.extend( {
+               isInteger: false,
+               min: -Infinity,
+               max: Infinity,
+               step: 1,
+               pageStep: null
+       }, config );
+
+       // Parent constructor
+       OO.ui.NumberInputWidget.parent.call( this, config );
+
+       // Properties
+       this.input = new OO.ui.TextInputWidget( $.extend(
+               {
+                       disabled: this.isDisabled()
+               },
+               config.input
+       ) );
+       this.minusButton = new OO.ui.ButtonWidget( $.extend(
+               {
+                       disabled: this.isDisabled(),
+                       tabIndex: -1
+               },
+               config.minusButton,
+               {
+                       classes: [ 'oo-ui-numberInputWidget-minusButton' ],
+                       label: '−'
+               }
+       ) );
+       this.plusButton = new OO.ui.ButtonWidget( $.extend(
+               {
+                       disabled: this.isDisabled(),
+                       tabIndex: -1
+               },
+               config.plusButton,
+               {
+                       classes: [ 'oo-ui-numberInputWidget-plusButton' ],
+                       label: '+'
+               }
+       ) );
+
+       // Events
+       this.input.connect( this, {
+               change: this.emit.bind( this, 'change' ),
+               enter: this.emit.bind( this, 'enter' )
+       } );
+       this.input.$input.on( {
+               keydown: this.onKeyDown.bind( this ),
+               'wheel mousewheel DOMMouseScroll': this.onWheel.bind( this )
+       } );
+       this.plusButton.connect( this, {
+               click: [ 'onButtonClick', +1 ]
+       } );
+       this.minusButton.connect( this, {
+               click: [ 'onButtonClick', -1 ]
+       } );
+
+       // Initialization
+       this.setIsInteger( !!config.isInteger );
+       this.setRange( config.min, config.max );
+       this.setStep( config.step, config.pageStep );
+
+       this.$field = $( '<div>' ).addClass( 'oo-ui-numberInputWidget-field' )
+               .append(
+                       this.minusButton.$element,
+                       this.input.$element,
+                       this.plusButton.$element
+               );
+       this.$element.addClass( 'oo-ui-numberInputWidget' ).append( this.$field );
+       this.input.setValidation( this.validateNumber.bind( this ) );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.NumberInputWidget, OO.ui.Widget );
+
+/* Events */
+
+/**
+ * A `change` event is emitted when the value of the input changes.
+ *
+ * @event change
+ */
+
+/**
+ * An `enter` event is emitted when the user presses 'enter' inside the text box.
+ *
+ * @event enter
+ */
+
+/* Methods */
+
+/**
+ * Set whether only integers are allowed
+ * @param {boolean} flag
+ */
+OO.ui.NumberInputWidget.prototype.setIsInteger = function ( flag ) {
+       this.isInteger = !!flag;
+       this.input.setValidityFlag();
+};
+
+/**
+ * Get whether only integers are allowed
+ * @return {boolean} Flag value
+ */
+OO.ui.NumberInputWidget.prototype.getIsInteger = function () {
+       return this.isInteger;
+};
+
+/**
+ * Set the range of allowed values
+ * @param {number} min Minimum allowed value
+ * @param {number} max Maximum allowed value
+ */
+OO.ui.NumberInputWidget.prototype.setRange = function ( min, max ) {
+       if ( min > max ) {
+               throw new Error( 'Minimum (' + min + ') must not be greater than maximum (' + max + ')' );
+       }
+       this.min = min;
+       this.max = max;
+       this.input.setValidityFlag();
+};
+
+/**
+ * Get the current range
+ * @return {number[]} Minimum and maximum values
+ */
+OO.ui.NumberInputWidget.prototype.getRange = function () {
+       return [ this.min, this.max ];
+};
+
+/**
+ * Set the stepping deltas
+ * @param {number} step Normal step
+ * @param {number|null} pageStep Page step. If null, 10 * step will be used.
+ */
+OO.ui.NumberInputWidget.prototype.setStep = function ( step, pageStep ) {
+       if ( step <= 0 ) {
+               throw new Error( 'Step value must be positive' );
+       }
+       if ( pageStep === null ) {
+               pageStep = step * 10;
+       } else if ( pageStep <= 0 ) {
+               throw new Error( 'Page step value must be positive' );
+       }
+       this.step = step;
+       this.pageStep = pageStep;
+};
+
+/**
+ * Get the current stepping values
+ * @return {number[]} Step and page step
+ */
+OO.ui.NumberInputWidget.prototype.getStep = function () {
+       return [ this.step, this.pageStep ];
+};
+
+/**
+ * Get the current value of the widget
+ * @return {string}
+ */
+OO.ui.NumberInputWidget.prototype.getValue = function () {
+       return this.input.getValue();
+};
+
+/**
+ * Get the current value of the widget as a number
+ * @return {number} May be NaN, or an invalid number
+ */
+OO.ui.NumberInputWidget.prototype.getNumericValue = function () {
+       return +this.input.getValue();
+};
+
+/**
+ * Set the value of the widget
+ * @param {string} value Invalid values are allowed
+ */
+OO.ui.NumberInputWidget.prototype.setValue = function ( value ) {
+       this.input.setValue( value );
+};
+
+/**
+ * Adjust the value of the widget
+ * @param {number} delta Adjustment amount
+ */
+OO.ui.NumberInputWidget.prototype.adjustValue = function ( delta ) {
+       var n, v = this.getNumericValue();
+
+       delta = +delta;
+       if ( isNaN( delta ) || !isFinite( delta ) ) {
+               throw new Error( 'Delta must be a finite number' );
+       }
+
+       if ( isNaN( v ) ) {
+               n = 0;
+       } else {
+               n = v + delta;
+               n = Math.max( Math.min( n, this.max ), this.min );
+               if ( this.isInteger ) {
+                       n = Math.round( n );
+               }
+       }
+
+       if ( n !== v ) {
+               this.setValue( n );
+       }
+};
+
+/**
+ * Validate input
+ * @private
+ * @param {string} value Field value
+ * @return {boolean}
+ */
+OO.ui.NumberInputWidget.prototype.validateNumber = function ( value ) {
+       var n = +value;
+       if ( isNaN( n ) || !isFinite( n ) ) {
+               return false;
+       }
+
+       /*jshint bitwise: false */
+       if ( this.isInteger && ( n | 0 ) !== n ) {
+               return false;
+       }
+       /*jshint bitwise: true */
+
+       if ( n < this.min || n > this.max ) {
+               return false;
+       }
+
+       return true;
+};
+
+/**
+ * Handle mouse click events.
+ *
+ * @private
+ * @param {number} dir +1 or -1
+ */
+OO.ui.NumberInputWidget.prototype.onButtonClick = function ( dir ) {
+       this.adjustValue( dir * this.step );
+};
+
+/**
+ * Handle mouse wheel events.
+ *
+ * @private
+ * @param {jQuery.Event} event
+ */
+OO.ui.NumberInputWidget.prototype.onWheel = function ( event ) {
+       var delta = 0;
+
+       // Standard 'wheel' event
+       if ( event.originalEvent.deltaMode !== undefined ) {
+               this.sawWheelEvent = true;
+       }
+       if ( event.originalEvent.deltaY ) {
+               delta = -event.originalEvent.deltaY;
+       } else if ( event.originalEvent.deltaX ) {
+               delta = event.originalEvent.deltaX;
+       }
+
+       // Non-standard events
+       if ( !this.sawWheelEvent ) {
+               if ( event.originalEvent.wheelDeltaX ) {
+                       delta = -event.originalEvent.wheelDeltaX;
+               } else if ( event.originalEvent.wheelDeltaY ) {
+                       delta = event.originalEvent.wheelDeltaY;
+               } else if ( event.originalEvent.wheelDelta ) {
+                       delta = event.originalEvent.wheelDelta;
+               } else if ( event.originalEvent.detail ) {
+                       delta = -event.originalEvent.detail;
+               }
+       }
+
+       if ( delta ) {
+               delta = delta < 0 ? -1 : 1;
+               this.adjustValue( delta * this.step );
+       }
+
+       return false;
+};
+
+/**
+ * Handle key down events.
+ *
+ * @private
+ * @param {jQuery.Event} e Key down event
+ */
+OO.ui.NumberInputWidget.prototype.onKeyDown = function ( e ) {
+       if ( !this.isDisabled() ) {
+               switch ( e.which ) {
+                       case OO.ui.Keys.UP:
+                               this.adjustValue( this.step );
+                               return false;
+                       case OO.ui.Keys.DOWN:
+                               this.adjustValue( -this.step );
+                               return false;
+                       case OO.ui.Keys.PAGEUP:
+                               this.adjustValue( this.pageStep );
+                               return false;
+                       case OO.ui.Keys.PAGEDOWN:
+                               this.adjustValue( -this.pageStep );
+                               return false;
+               }
+       }
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.NumberInputWidget.prototype.setDisabled = function ( disabled ) {
+       // Parent method
+       OO.ui.NumberInputWidget.parent.prototype.setDisabled.call( this, disabled );
+
+       if ( this.input ) {
+               this.input.setDisabled( this.isDisabled() );
+       }
+       if ( this.minusButton ) {
+               this.minusButton.setDisabled( this.isDisabled() );
+       }
+       if ( this.plusButton ) {
+               this.plusButton.setDisabled( this.isDisabled() );
+       }
+
+       return this;
+};
+
+}( OO ) );
diff --git a/resources/lib/oojs-ui/oojs-ui-windows-apex.css b/resources/lib/oojs-ui/oojs-ui-windows-apex.css
new file mode 100644 (file)
index 0000000..adf3bfd
--- /dev/null
@@ -0,0 +1,409 @@
+/*!
+ * OOjs UI v0.15.3
+ * https://www.mediawiki.org/wiki/OOjs_UI
+ *
+ * Copyright 2011–2016 OOjs UI Team and other contributors.
+ * Released under the MIT license
+ * http://oojs.mit-license.org
+ *
+ * Date: 2016-02-09T21:21:21Z
+ */
+.oo-ui-actionWidget.oo-ui-pendingElement-pending {
+       background-image: /* @embed */ url(themes/apex/images/textures/pending.gif);
+}
+.oo-ui-window {
+       background-color: transparent;
+       background-image: none;
+}
+.oo-ui-window-frame {
+       -webkit-box-sizing: border-box;
+          -moz-box-sizing: border-box;
+               box-sizing: border-box;
+}
+.oo-ui-window-content:focus {
+       outline: none;
+}
+.oo-ui-window-head,
+.oo-ui-window-foot {
+       -webkit-touch-callout: none;
+       -webkit-user-select: none;
+          -moz-user-select: none;
+           -ms-user-select: none;
+               user-select: none;
+}
+.oo-ui-window-body {
+       margin: 0;
+       padding: 0;
+       background: none;
+}
+.oo-ui-window-overlay {
+       position: absolute;
+       top: 0;
+       /* @noflip */
+       left: 0;
+}
+.oo-ui-dialog-content > .oo-ui-window-head,
+.oo-ui-dialog-content > .oo-ui-window-body,
+.oo-ui-dialog-content > .oo-ui-window-foot {
+       position: absolute;
+       left: 0;
+       right: 0;
+       -webkit-box-sizing: border-box;
+          -moz-box-sizing: border-box;
+               box-sizing: border-box;
+}
+.oo-ui-dialog-content > .oo-ui-window-head {
+       overflow: hidden;
+       z-index: 1;
+       top: 0;
+}
+.oo-ui-dialog-content > .oo-ui-window-body {
+       overflow: auto;
+       z-index: 2;
+       top: 0;
+       bottom: 0;
+}
+.oo-ui-dialog-content > .oo-ui-window-foot {
+       overflow: hidden;
+       z-index: 1;
+       bottom: 0;
+}
+.oo-ui-dialog-content > .oo-ui-window-body {
+       box-shadow: 0 0 0.66em rgba(0, 0, 0, 0.25);
+}
+.oo-ui-messageDialog-actions-horizontal {
+       display: table;
+       table-layout: fixed;
+       width: 100%;
+}
+.oo-ui-messageDialog-actions-horizontal .oo-ui-actionWidget {
+       display: table-cell;
+       width: 1%;
+}
+.oo-ui-messageDialog-actions-vertical {
+       display: block;
+}
+.oo-ui-messageDialog-actions-vertical .oo-ui-actionWidget {
+       display: block;
+       overflow: hidden;
+       text-overflow: ellipsis;
+}
+.oo-ui-messageDialog-actions .oo-ui-actionWidget {
+       position: relative;
+       text-align: center;
+}
+.oo-ui-messageDialog-actions .oo-ui-actionWidget .oo-ui-buttonElement-button {
+       display: block;
+}
+.oo-ui-messageDialog-actions .oo-ui-actionWidget .oo-ui-labelElement-label {
+       position: relative;
+       top: auto;
+       bottom: auto;
+       display: inline;
+       white-space: nowrap;
+}
+.oo-ui-messageDialog-content .oo-ui-window-body {
+       box-shadow: 0 0 0.33em rgba(0, 0, 0, 0.33);
+}
+.oo-ui-messageDialog-title,
+.oo-ui-messageDialog-message {
+       display: block;
+       text-align: center;
+}
+.oo-ui-messageDialog-title.oo-ui-labelElement,
+.oo-ui-messageDialog-message.oo-ui-labelElement {
+       padding-top: 0.5em;
+}
+.oo-ui-messageDialog-title {
+       font-size: 1.5em;
+       line-height: 1em;
+       color: #000000;
+}
+.oo-ui-messageDialog-message {
+       font-size: 0.9em;
+       line-height: 1.25em;
+       color: #666666;
+}
+.oo-ui-messageDialog-message-verbose {
+       font-size: 1.1em;
+       line-height: 1.5em;
+       text-align: left;
+}
+.oo-ui-messageDialog-actions-horizontal .oo-ui-actionWidget {
+       border-right: 1px solid #e5e5e5;
+       margin: 0;
+}
+.oo-ui-messageDialog-actions-horizontal .oo-ui-actionWidget:last-child {
+       border-right-width: 0;
+}
+.oo-ui-messageDialog-actions-vertical .oo-ui-actionWidget {
+       border-bottom: 1px solid #e5e5e5;
+       margin: 0;
+}
+.oo-ui-messageDialog-actions-vertical .oo-ui-actionWidget:last-child {
+       border-bottom-width: 0;
+}
+.oo-ui-messageDialog-actions .oo-ui-actionWidget {
+       height: 3.4em;
+       margin-right: 0;
+}
+.oo-ui-messageDialog-actions .oo-ui-actionWidget:last-child {
+       margin-right: 0;
+}
+.oo-ui-messageDialog-actions .oo-ui-actionWidget.oo-ui-labelElement .oo-ui-labelElement-label {
+       text-align: center;
+       line-height: 3.4em;
+       padding: 0 2em;
+}
+.oo-ui-messageDialog-actions .oo-ui-actionWidget:hover {
+       background-color: rgba(0, 0, 0, 0.05);
+}
+.oo-ui-messageDialog-actions .oo-ui-actionWidget:active {
+       background-color: rgba(0, 0, 0, 0.1);
+}
+.oo-ui-messageDialog-actions .oo-ui-actionWidget.oo-ui-flaggedElement-progressive:hover {
+       background-color: rgba(8, 126, 204, 0.05);
+}
+.oo-ui-messageDialog-actions .oo-ui-actionWidget.oo-ui-flaggedElement-progressive:active {
+       background-color: rgba(8, 126, 204, 0.1);
+}
+.oo-ui-messageDialog-actions .oo-ui-actionWidget.oo-ui-flaggedElement-progressive .oo-ui-labelElement-label {
+       font-weight: bold;
+}
+.oo-ui-messageDialog-actions .oo-ui-actionWidget.oo-ui-flaggedElement-constructive:hover {
+       background-color: rgba(118, 171, 54, 0.05);
+}
+.oo-ui-messageDialog-actions .oo-ui-actionWidget.oo-ui-flaggedElement-constructive:active {
+       background-color: rgba(118, 171, 54, 0.1);
+}
+.oo-ui-messageDialog-actions .oo-ui-actionWidget.oo-ui-flaggedElement-destructive:hover {
+       background-color: rgba(212, 83, 83, 0.05);
+}
+.oo-ui-messageDialog-actions .oo-ui-actionWidget.oo-ui-flaggedElement-destructive:active {
+       background-color: rgba(212, 83, 83, 0.1);
+}
+.oo-ui-processDialog-location {
+       overflow: hidden;
+       text-overflow: ellipsis;
+       white-space: nowrap;
+}
+.oo-ui-processDialog-title {
+       display: inline;
+       padding: 0;
+}
+.oo-ui-processDialog-actions-safe .oo-ui-actionWidget,
+.oo-ui-processDialog-actions-primary .oo-ui-actionWidget,
+.oo-ui-processDialog-actions-other .oo-ui-actionWidget {
+       white-space: nowrap;
+}
+.oo-ui-processDialog-actions-safe,
+.oo-ui-processDialog-actions-primary {
+       position: absolute;
+       top: 0;
+       bottom: 0;
+}
+.oo-ui-processDialog-actions-safe {
+       left: 0;
+}
+.oo-ui-processDialog-actions-primary {
+       right: 0;
+}
+.oo-ui-processDialog-errors {
+       position: absolute;
+       top: 0;
+       left: 0;
+       right: 0;
+       bottom: 0;
+       z-index: 2;
+       overflow-x: hidden;
+       overflow-y: auto;
+}
+.oo-ui-processDialog-content .oo-ui-window-head {
+       height: 3.4em;
+}
+.oo-ui-processDialog-content .oo-ui-window-body {
+       top: 3.4em;
+       box-shadow: 0 0 0.33em rgba(0, 0, 0, 0.33);
+}
+.oo-ui-processDialog-navigation {
+       position: relative;
+       height: 3.4em;
+       padding: 0 1em;
+}
+.oo-ui-processDialog-location {
+       padding: 0.75em 0;
+       height: 1.875em;
+       cursor: default;
+       text-align: center;
+}
+.oo-ui-processDialog-title {
+       font-weight: bold;
+       line-height: 1.875em;
+}
+.oo-ui-processDialog-actions-safe .oo-ui-actionWidget .oo-ui-buttonElement-button,
+.oo-ui-processDialog-actions-primary .oo-ui-actionWidget .oo-ui-buttonElement-button,
+.oo-ui-processDialog-actions-other .oo-ui-actionWidget .oo-ui-buttonElement-button {
+       min-width: 1.875em;
+       min-height: 1.875em;
+}
+.oo-ui-processDialog-actions-safe .oo-ui-actionWidget .oo-ui-labelElement-label,
+.oo-ui-processDialog-actions-primary .oo-ui-actionWidget .oo-ui-labelElement-label,
+.oo-ui-processDialog-actions-other .oo-ui-actionWidget .oo-ui-labelElement-label {
+       line-height: 1.875em;
+}
+.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-iconElement .oo-ui-iconElement-icon,
+.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-iconElement .oo-ui-iconElement-icon,
+.oo-ui-processDialog-actions-other .oo-ui-actionWidget.oo-ui-iconElement .oo-ui-iconElement-icon {
+       margin-top: -0.125em;
+}
+.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-buttonElement-framed,
+.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-buttonElement-framed,
+.oo-ui-processDialog-actions-other .oo-ui-actionWidget.oo-ui-buttonElement-framed {
+       margin: 0.75em;
+}
+.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-buttonElement-framed .oo-ui-buttonElement-button,
+.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-buttonElement-framed .oo-ui-buttonElement-button,
+.oo-ui-processDialog-actions-other .oo-ui-actionWidget.oo-ui-buttonElement-framed .oo-ui-buttonElement-button {
+       padding: 0 1em;
+       vertical-align: middle;
+       margin: -1px;
+}
+.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-buttonElement-frameless,
+.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-buttonElement-frameless,
+.oo-ui-processDialog-actions-other .oo-ui-actionWidget.oo-ui-buttonElement-frameless {
+       margin: 0;
+}
+.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-buttonElement-frameless .oo-ui-buttonElement-button,
+.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-buttonElement-frameless .oo-ui-buttonElement-button,
+.oo-ui-processDialog-actions-other .oo-ui-actionWidget.oo-ui-buttonElement-frameless .oo-ui-buttonElement-button {
+       padding: 0.75em 1em;
+       vertical-align: middle;
+}
+.oo-ui-processDialog-actions-safe .oo-ui-actionWidget:hover,
+.oo-ui-processDialog-actions-primary .oo-ui-actionWidget:hover {
+       background-color: rgba(0, 0, 0, 0.05);
+}
+.oo-ui-processDialog-actions-safe .oo-ui-actionWidget:active,
+.oo-ui-processDialog-actions-primary .oo-ui-actionWidget:active {
+       background-color: rgba(0, 0, 0, 0.1);
+}
+.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-flaggedElement-progressive:hover,
+.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-flaggedElement-progressive:hover {
+       background-color: rgba(8, 126, 204, 0.05);
+}
+.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-flaggedElement-progressive:active,
+.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-flaggedElement-progressive:active {
+       background-color: rgba(8, 126, 204, 0.1);
+}
+.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-flaggedElement-progressive .oo-ui-labelElement-label,
+.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-flaggedElement-progressive .oo-ui-labelElement-label {
+       font-weight: bold;
+}
+.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-flaggedElement-constructive:hover,
+.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-flaggedElement-constructive:hover {
+       background-color: rgba(118, 171, 54, 0.05);
+}
+.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-flaggedElement-constructive:active,
+.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-flaggedElement-constructive:active {
+       background-color: rgba(118, 171, 54, 0.1);
+}
+.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-flaggedElement-destructive:hover,
+.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-flaggedElement-destructive:hover {
+       background-color: rgba(212, 83, 83, 0.05);
+}
+.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-flaggedElement-destructive:active,
+.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-flaggedElement-destructive:active {
+       background-color: rgba(212, 83, 83, 0.1);
+}
+.oo-ui-processDialog-actions-other .oo-ui-actionWidget.oo-ui-buttonElement {
+       margin-right: 0;
+}
+.oo-ui-processDialog > .oo-ui-window-frame {
+       min-height: 5em;
+}
+.oo-ui-processDialog-errors {
+       background-color: rgba(255, 255, 255, 0.9);
+       padding: 3em 3em 1.5em 3em;
+       text-align: center;
+}
+.oo-ui-processDialog-errors .oo-ui-buttonWidget {
+       margin: 2em 1em 2em 1em;
+}
+.oo-ui-processDialog-errors-title {
+       font-size: 1.5em;
+       color: #000000;
+       margin-bottom: 2em;
+}
+.oo-ui-processDialog-error {
+       text-align: left;
+       margin: 1em;
+       padding: 1em;
+       border: 1px solid #ff9e9e;
+       background-color: #fff7f7;
+       border-radius: 0.25em;
+}
+.oo-ui-windowManager-modal > .oo-ui-dialog {
+       position: fixed;
+       width: 0;
+       height: 0;
+       overflow: hidden;
+}
+.oo-ui-windowManager-modal > .oo-ui-dialog.oo-ui-window-active {
+       width: auto;
+       height: auto;
+       top: 0;
+       right: 0;
+       bottom: 0;
+       left: 0;
+       padding: 1em;
+}
+.oo-ui-windowManager-modal > .oo-ui-dialog.oo-ui-window-setup > .oo-ui-window-frame {
+       position: absolute;
+       right: 0;
+       left: 0;
+       margin: auto;
+       overflow: hidden;
+       max-width: 100%;
+       max-height: 100%;
+}
+.oo-ui-windowManager-fullscreen > .oo-ui-dialog > .oo-ui-window-frame {
+       width: 100%;
+       height: 100%;
+       top: 0;
+       bottom: 0;
+}
+.oo-ui-windowManager-modal > .oo-ui-dialog {
+       background-color: rgba(255, 255, 255, 0.5);
+       opacity: 0;
+       -webkit-transition: opacity 250ms ease;
+          -moz-transition: opacity 250ms ease;
+               transition: opacity 250ms ease;
+}
+.oo-ui-windowManager-modal > .oo-ui-dialog > .oo-ui-window-frame {
+       background-color: #ffffff;
+       opacity: 0;
+       -webkit-transform: scale(0.5);
+          -moz-transform: scale(0.5);
+           -ms-transform: scale(0.5);
+               transform: scale(0.5);
+       -webkit-transition: all 250ms ease;
+          -moz-transition: all 250ms ease;
+               transition: all 250ms ease;
+}
+.oo-ui-windowManager-modal > .oo-ui-dialog.oo-ui-window-setup {
+       opacity: 1;
+}
+.oo-ui-windowManager-modal > .oo-ui-dialog.oo-ui-window-ready > .oo-ui-window-frame {
+       opacity: 1;
+       -webkit-transform: scale(1);
+          -moz-transform: scale(1);
+           -ms-transform: scale(1);
+               transform: scale(1);
+}
+.oo-ui-windowManager-modal.oo-ui-windowManager-floating > .oo-ui-dialog > .oo-ui-window-frame {
+       top: 1em;
+       bottom: 1em;
+       border: 1px solid #cccccc;
+       border-radius: 0.5em;
+       box-shadow: 0 0.2em 1em rgba(0, 0, 0, 0.3);
+}
diff --git a/resources/lib/oojs-ui/oojs-ui-windows-mediawiki.css b/resources/lib/oojs-ui/oojs-ui-windows-mediawiki.css
new file mode 100644 (file)
index 0000000..101673c
--- /dev/null
@@ -0,0 +1,384 @@
+/*!
+ * OOjs UI v0.15.3
+ * https://www.mediawiki.org/wiki/OOjs_UI
+ *
+ * Copyright 2011–2016 OOjs UI Team and other contributors.
+ * Released under the MIT license
+ * http://oojs.mit-license.org
+ *
+ * Date: 2016-02-09T21:21:21Z
+ */
+.oo-ui-window {
+       background: transparent;
+}
+.oo-ui-window-frame {
+       -webkit-box-sizing: border-box;
+          -moz-box-sizing: border-box;
+               box-sizing: border-box;
+}
+.oo-ui-window-content:focus {
+       outline: none;
+}
+.oo-ui-window-head,
+.oo-ui-window-foot {
+       -webkit-touch-callout: none;
+       -webkit-user-select: none;
+          -moz-user-select: none;
+           -ms-user-select: none;
+               user-select: none;
+}
+.oo-ui-window-body {
+       margin: 0;
+       padding: 0;
+       background: none;
+}
+.oo-ui-window-overlay {
+       position: absolute;
+       top: 0;
+       /* @noflip */
+       left: 0;
+}
+.oo-ui-dialog-content > .oo-ui-window-head,
+.oo-ui-dialog-content > .oo-ui-window-body,
+.oo-ui-dialog-content > .oo-ui-window-foot {
+       position: absolute;
+       left: 0;
+       right: 0;
+       -webkit-box-sizing: border-box;
+          -moz-box-sizing: border-box;
+               box-sizing: border-box;
+}
+.oo-ui-dialog-content > .oo-ui-window-head {
+       overflow: hidden;
+       z-index: 1;
+       top: 0;
+}
+.oo-ui-dialog-content > .oo-ui-window-body {
+       overflow: auto;
+       z-index: 2;
+       top: 0;
+       bottom: 0;
+}
+.oo-ui-dialog-content > .oo-ui-window-foot {
+       overflow: hidden;
+       z-index: 1;
+       bottom: 0;
+}
+.oo-ui-dialog-content > .oo-ui-window-body {
+       outline: 1px solid #aaaaaa;
+}
+.oo-ui-messageDialog-actions-horizontal {
+       display: table;
+       table-layout: fixed;
+       width: 100%;
+}
+.oo-ui-messageDialog-actions-horizontal .oo-ui-actionWidget {
+       display: table-cell;
+       width: 1%;
+}
+.oo-ui-messageDialog-actions-vertical {
+       display: block;
+}
+.oo-ui-messageDialog-actions-vertical .oo-ui-actionWidget {
+       display: block;
+       overflow: hidden;
+       text-overflow: ellipsis;
+}
+.oo-ui-messageDialog-actions .oo-ui-actionWidget {
+       position: relative;
+       text-align: center;
+}
+.oo-ui-messageDialog-actions .oo-ui-actionWidget .oo-ui-buttonElement-button {
+       display: block;
+}
+.oo-ui-messageDialog-actions .oo-ui-actionWidget .oo-ui-labelElement-label {
+       position: relative;
+       top: auto;
+       bottom: auto;
+       display: inline;
+       white-space: nowrap;
+}
+.oo-ui-messageDialog-title,
+.oo-ui-messageDialog-message {
+       display: block;
+       text-align: center;
+}
+.oo-ui-messageDialog-title.oo-ui-labelElement,
+.oo-ui-messageDialog-message.oo-ui-labelElement {
+       padding-top: 0.5em;
+}
+.oo-ui-messageDialog-title {
+       font-size: 1.5em;
+       line-height: 1em;
+       color: #000000;
+}
+.oo-ui-messageDialog-message {
+       font-size: 0.9em;
+       line-height: 1.25em;
+       color: #555555;
+}
+.oo-ui-messageDialog-message-verbose {
+       font-size: 1.1em;
+       line-height: 1.5em;
+       text-align: left;
+}
+.oo-ui-messageDialog-actions-horizontal .oo-ui-actionWidget {
+       border-right: 1px solid #e5e5e5;
+       margin: 0;
+}
+.oo-ui-messageDialog-actions-horizontal .oo-ui-actionWidget:last-child {
+       border-right-width: 0;
+}
+.oo-ui-messageDialog-actions-vertical .oo-ui-actionWidget {
+       border-bottom: 1px solid #e5e5e5;
+       margin: 0;
+}
+.oo-ui-messageDialog-actions-vertical .oo-ui-actionWidget:last-child {
+       border-bottom-width: 0;
+}
+.oo-ui-messageDialog-actions .oo-ui-actionWidget {
+       height: 3.4em;
+       margin-right: 0;
+}
+.oo-ui-messageDialog-actions .oo-ui-actionWidget:last-child {
+       margin-right: 0;
+}
+.oo-ui-messageDialog-actions .oo-ui-actionWidget.oo-ui-labelElement .oo-ui-labelElement-label {
+       text-align: center;
+       line-height: 3.4em;
+       padding: 0 2em;
+}
+.oo-ui-messageDialog-actions .oo-ui-actionWidget:hover {
+       background-color: rgba(0, 0, 0, 0.05);
+}
+.oo-ui-messageDialog-actions .oo-ui-actionWidget:active {
+       background-color: rgba(0, 0, 0, 0.1);
+}
+.oo-ui-messageDialog-actions .oo-ui-actionWidget.oo-ui-flaggedElement-progressive:hover {
+       background-color: rgba(8, 126, 204, 0.05);
+}
+.oo-ui-messageDialog-actions .oo-ui-actionWidget.oo-ui-flaggedElement-progressive:active {
+       background-color: rgba(8, 126, 204, 0.1);
+}
+.oo-ui-messageDialog-actions .oo-ui-actionWidget.oo-ui-flaggedElement-progressive .oo-ui-labelElement-label {
+       font-weight: bold;
+}
+.oo-ui-messageDialog-actions .oo-ui-actionWidget.oo-ui-flaggedElement-constructive:hover {
+       background-color: rgba(118, 171, 54, 0.05);
+}
+.oo-ui-messageDialog-actions .oo-ui-actionWidget.oo-ui-flaggedElement-constructive:active {
+       background-color: rgba(118, 171, 54, 0.1);
+}
+.oo-ui-messageDialog-actions .oo-ui-actionWidget.oo-ui-flaggedElement-destructive:hover {
+       background-color: rgba(212, 83, 83, 0.05);
+}
+.oo-ui-messageDialog-actions .oo-ui-actionWidget.oo-ui-flaggedElement-destructive:active {
+       background-color: rgba(212, 83, 83, 0.1);
+}
+.oo-ui-processDialog-location {
+       overflow: hidden;
+       text-overflow: ellipsis;
+       white-space: nowrap;
+}
+.oo-ui-processDialog-title {
+       display: inline;
+       padding: 0;
+}
+.oo-ui-processDialog-actions-safe .oo-ui-actionWidget,
+.oo-ui-processDialog-actions-primary .oo-ui-actionWidget,
+.oo-ui-processDialog-actions-other .oo-ui-actionWidget {
+       white-space: nowrap;
+}
+.oo-ui-processDialog-actions-safe,
+.oo-ui-processDialog-actions-primary {
+       position: absolute;
+       top: 0;
+       bottom: 0;
+}
+.oo-ui-processDialog-actions-safe {
+       left: 0;
+}
+.oo-ui-processDialog-actions-primary {
+       right: 0;
+}
+.oo-ui-processDialog-errors {
+       position: absolute;
+       top: 0;
+       left: 0;
+       right: 0;
+       bottom: 0;
+       z-index: 2;
+       overflow-x: hidden;
+       overflow-y: auto;
+}
+.oo-ui-processDialog-content .oo-ui-window-head {
+       height: 3.4em;
+}
+.oo-ui-processDialog-content .oo-ui-window-body {
+       top: 3.4em;
+       outline: 1px solid rgba(0, 0, 0, 0.2);
+}
+.oo-ui-processDialog-navigation {
+       position: relative;
+       height: 3.4em;
+       padding: 0 1em;
+}
+.oo-ui-processDialog-location {
+       padding: 0.75em 0;
+       height: 1.875em;
+       cursor: default;
+       text-align: center;
+}
+.oo-ui-processDialog-title {
+       font-weight: bold;
+       line-height: 1.875em;
+}
+.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-buttonElement-framed,
+.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-buttonElement-framed,
+.oo-ui-processDialog-actions-other .oo-ui-actionWidget.oo-ui-buttonElement-framed {
+       margin: 0.5em;
+}
+.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-buttonElement-frameless,
+.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-buttonElement-frameless,
+.oo-ui-processDialog-actions-other .oo-ui-actionWidget.oo-ui-buttonElement-frameless {
+       margin: 0;
+}
+.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-buttonElement-frameless .oo-ui-buttonElement-button,
+.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-buttonElement-frameless .oo-ui-buttonElement-button,
+.oo-ui-processDialog-actions-other .oo-ui-actionWidget.oo-ui-buttonElement-frameless .oo-ui-buttonElement-button {
+       padding: 0.75em 1em;
+       vertical-align: middle;
+}
+.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-buttonElement-frameless .oo-ui-labelElement-label,
+.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-buttonElement-frameless .oo-ui-labelElement-label,
+.oo-ui-processDialog-actions-other .oo-ui-actionWidget.oo-ui-buttonElement-frameless .oo-ui-labelElement-label {
+       line-height: 1.875em;
+}
+.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-buttonElement-frameless:hover,
+.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-buttonElement-frameless:hover {
+       background-color: rgba(0, 0, 0, 0.05);
+}
+.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-buttonElement-frameless:active,
+.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-buttonElement-frameless:active {
+       background-color: rgba(0, 0, 0, 0.1);
+}
+.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-buttonElement-frameless.oo-ui-flaggedElement-progressive:hover,
+.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-buttonElement-frameless.oo-ui-flaggedElement-progressive:hover {
+       background-color: rgba(8, 126, 204, 0.05);
+}
+.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-buttonElement-frameless.oo-ui-flaggedElement-progressive:active,
+.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-buttonElement-frameless.oo-ui-flaggedElement-progressive:active {
+       background-color: rgba(8, 126, 204, 0.1);
+}
+.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-buttonElement-frameless.oo-ui-flaggedElement-progressive .oo-ui-labelElement-label,
+.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-buttonElement-frameless.oo-ui-flaggedElement-progressive .oo-ui-labelElement-label {
+       font-weight: bold;
+}
+.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-buttonElement-frameless.oo-ui-flaggedElement-constructive:hover,
+.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-buttonElement-frameless.oo-ui-flaggedElement-constructive:hover {
+       background-color: rgba(118, 171, 54, 0.05);
+}
+.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-buttonElement-frameless.oo-ui-flaggedElement-constructive:active,
+.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-buttonElement-frameless.oo-ui-flaggedElement-constructive:active {
+       background-color: rgba(118, 171, 54, 0.1);
+}
+.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-buttonElement-frameless.oo-ui-flaggedElement-destructive:hover,
+.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-buttonElement-frameless.oo-ui-flaggedElement-destructive:hover {
+       background-color: rgba(212, 83, 83, 0.05);
+}
+.oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-buttonElement-frameless.oo-ui-flaggedElement-destructive:active,
+.oo-ui-processDialog-actions-primary .oo-ui-actionWidget.oo-ui-buttonElement-frameless.oo-ui-flaggedElement-destructive:active {
+       background-color: rgba(212, 83, 83, 0.1);
+}
+.oo-ui-processDialog-actions-other .oo-ui-actionWidget.oo-ui-buttonElement {
+       margin-right: 0;
+}
+.oo-ui-processDialog > .oo-ui-window-frame {
+       min-height: 5em;
+}
+.oo-ui-processDialog-errors {
+       background-color: rgba(255, 255, 255, 0.9);
+       padding: 3em 3em 1.5em 3em;
+       text-align: center;
+}
+.oo-ui-processDialog-errors .oo-ui-buttonWidget {
+       margin: 2em 1em 2em 1em;
+}
+.oo-ui-processDialog-errors-title {
+       font-size: 1.5em;
+       color: #000000;
+       margin-bottom: 2em;
+}
+.oo-ui-processDialog-error {
+       text-align: left;
+       margin: 1em;
+       padding: 1em;
+       border: 1px solid #ff9e9e;
+       background-color: #fff7f7;
+       border-radius: 2px;
+}
+.oo-ui-windowManager-modal > .oo-ui-dialog {
+       position: fixed;
+       width: 0;
+       height: 0;
+       overflow: hidden;
+}
+.oo-ui-windowManager-modal > .oo-ui-dialog.oo-ui-window-active {
+       width: auto;
+       height: auto;
+       top: 0;
+       right: 0;
+       bottom: 0;
+       left: 0;
+       padding: 1em;
+}
+.oo-ui-windowManager-modal > .oo-ui-dialog.oo-ui-window-setup > .oo-ui-window-frame {
+       position: absolute;
+       right: 0;
+       left: 0;
+       margin: auto;
+       overflow: hidden;
+       max-width: 100%;
+       max-height: 100%;
+}
+.oo-ui-windowManager-fullscreen > .oo-ui-dialog > .oo-ui-window-frame {
+       width: 100%;
+       height: 100%;
+       top: 0;
+       bottom: 0;
+}
+.oo-ui-windowManager-modal > .oo-ui-dialog {
+       background-color: rgba(255, 255, 255, 0.5);
+       opacity: 0;
+       -webkit-transition: opacity 250ms ease;
+          -moz-transition: opacity 250ms ease;
+               transition: opacity 250ms ease;
+}
+.oo-ui-windowManager-modal > .oo-ui-dialog > .oo-ui-window-frame {
+       background-color: #ffffff;
+       opacity: 0;
+       -webkit-transform: scale(0.5);
+          -moz-transform: scale(0.5);
+           -ms-transform: scale(0.5);
+               transform: scale(0.5);
+       -webkit-transition: all 250ms ease;
+          -moz-transition: all 250ms ease;
+               transition: all 250ms ease;
+}
+.oo-ui-windowManager-modal > .oo-ui-dialog.oo-ui-window-setup {
+       opacity: 1;
+}
+.oo-ui-windowManager-modal > .oo-ui-dialog.oo-ui-window-ready > .oo-ui-window-frame {
+       opacity: 1;
+       -webkit-transform: scale(1);
+          -moz-transform: scale(1);
+           -ms-transform: scale(1);
+               transform: scale(1);
+}
+.oo-ui-windowManager-modal.oo-ui-windowManager-floating > .oo-ui-dialog > .oo-ui-window-frame {
+       top: 1em;
+       bottom: 1em;
+       border: 1px solid #aaaaaa;
+       border-radius: 2px;
+       box-shadow: 0 0.15em 0 0 rgba(0, 0, 0, 0.15);
+}
diff --git a/resources/lib/oojs-ui/oojs-ui-windows.js b/resources/lib/oojs-ui/oojs-ui-windows.js
new file mode 100644 (file)
index 0000000..1a56945
--- /dev/null
@@ -0,0 +1,3395 @@
+/*!
+ * OOjs UI v0.15.3
+ * https://www.mediawiki.org/wiki/OOjs_UI
+ *
+ * Copyright 2011–2016 OOjs UI Team and other contributors.
+ * Released under the MIT license
+ * http://oojs.mit-license.org
+ *
+ * Date: 2016-02-09T21:21:16Z
+ */
+( function ( OO ) {
+
+'use strict';
+
+/**
+ * An ActionWidget is a {@link OO.ui.ButtonWidget button widget} that executes an action.
+ * Action widgets are used with OO.ui.ActionSet, which manages the behavior and availability
+ * of the actions.
+ *
+ * Both actions and action sets are primarily used with {@link OO.ui.Dialog Dialogs}.
+ * Please see the [OOjs UI documentation on MediaWiki] [1] for more information
+ * and examples.
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Process_Dialogs#Action_sets
+ *
+ * @class
+ * @extends OO.ui.ButtonWidget
+ * @mixins OO.ui.mixin.PendingElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {string} [action] Symbolic name of the action (e.g., ‘continue’ or ‘cancel’).
+ * @cfg {string[]} [modes] Symbolic names of the modes (e.g., ‘edit’ or ‘read’) in which the action
+ *  should be made available. See the action set's {@link OO.ui.ActionSet#setMode setMode} method
+ *  for more information about setting modes.
+ * @cfg {boolean} [framed=false] Render the action button with a frame
+ */
+OO.ui.ActionWidget = function OoUiActionWidget( config ) {
+       // Configuration initialization
+       config = $.extend( { framed: false }, config );
+
+       // Parent constructor
+       OO.ui.ActionWidget.parent.call( this, config );
+
+       // Mixin constructors
+       OO.ui.mixin.PendingElement.call( this, config );
+
+       // Properties
+       this.action = config.action || '';
+       this.modes = config.modes || [];
+       this.width = 0;
+       this.height = 0;
+
+       // Initialization
+       this.$element.addClass( 'oo-ui-actionWidget' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.ActionWidget, OO.ui.ButtonWidget );
+OO.mixinClass( OO.ui.ActionWidget, OO.ui.mixin.PendingElement );
+
+/* Events */
+
+/**
+ * A resize event is emitted when the size of the widget changes.
+ *
+ * @event resize
+ */
+
+/* Methods */
+
+/**
+ * Check if the action is configured to be available in the specified `mode`.
+ *
+ * @param {string} mode Name of mode
+ * @return {boolean} The action is configured with the mode
+ */
+OO.ui.ActionWidget.prototype.hasMode = function ( mode ) {
+       return this.modes.indexOf( mode ) !== -1;
+};
+
+/**
+ * Get the symbolic name of the action (e.g., ‘continue’ or ‘cancel’).
+ *
+ * @return {string}
+ */
+OO.ui.ActionWidget.prototype.getAction = function () {
+       return this.action;
+};
+
+/**
+ * Get the symbolic name of the mode or modes for which the action is configured to be available.
+ *
+ * The current mode is set with the action set's {@link OO.ui.ActionSet#setMode setMode} method.
+ * Only actions that are configured to be avaiable in the current mode will be visible. All other actions
+ * are hidden.
+ *
+ * @return {string[]}
+ */
+OO.ui.ActionWidget.prototype.getModes = function () {
+       return this.modes.slice();
+};
+
+/**
+ * Emit a resize event if the size has changed.
+ *
+ * @private
+ * @chainable
+ */
+OO.ui.ActionWidget.prototype.propagateResize = function () {
+       var width, height;
+
+       if ( this.isElementAttached() ) {
+               width = this.$element.width();
+               height = this.$element.height();
+
+               if ( width !== this.width || height !== this.height ) {
+                       this.width = width;
+                       this.height = height;
+                       this.emit( 'resize' );
+               }
+       }
+
+       return this;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.ActionWidget.prototype.setIcon = function () {
+       // Mixin method
+       OO.ui.mixin.IconElement.prototype.setIcon.apply( this, arguments );
+       this.propagateResize();
+
+       return this;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.ActionWidget.prototype.setLabel = function () {
+       // Mixin method
+       OO.ui.mixin.LabelElement.prototype.setLabel.apply( this, arguments );
+       this.propagateResize();
+
+       return this;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.ActionWidget.prototype.setFlags = function () {
+       // Mixin method
+       OO.ui.mixin.FlaggedElement.prototype.setFlags.apply( this, arguments );
+       this.propagateResize();
+
+       return this;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.ActionWidget.prototype.clearFlags = function () {
+       // Mixin method
+       OO.ui.mixin.FlaggedElement.prototype.clearFlags.apply( this, arguments );
+       this.propagateResize();
+
+       return this;
+};
+
+/**
+ * Toggle the visibility of the action button.
+ *
+ * @param {boolean} [show] Show button, omit to toggle visibility
+ * @chainable
+ */
+OO.ui.ActionWidget.prototype.toggle = function () {
+       // Parent method
+       OO.ui.ActionWidget.parent.prototype.toggle.apply( this, arguments );
+       this.propagateResize();
+
+       return this;
+};
+
+/**
+ * ActionSets manage the behavior of the {@link OO.ui.ActionWidget action widgets} that comprise them.
+ * Actions can be made available for specific contexts (modes) and circumstances
+ * (abilities). Action sets are primarily used with {@link OO.ui.Dialog Dialogs}.
+ *
+ * ActionSets contain two types of actions:
+ *
+ * - Special: Special actions are the first visible actions with special flags, such as 'safe' and 'primary', the default special flags. Additional special flags can be configured in subclasses with the static #specialFlags property.
+ * - Other: Other actions include all non-special visible actions.
+ *
+ * Please see the [OOjs UI documentation on MediaWiki][1] for more information.
+ *
+ *     @example
+ *     // Example: An action set used in a process dialog
+ *     function MyProcessDialog( config ) {
+ *         MyProcessDialog.parent.call( this, config );
+ *     }
+ *     OO.inheritClass( MyProcessDialog, OO.ui.ProcessDialog );
+ *     MyProcessDialog.static.title = 'An action set in a process dialog';
+ *     // An action set that uses modes ('edit' and 'help' mode, in this example).
+ *     MyProcessDialog.static.actions = [
+ *         { action: 'continue', modes: 'edit', label: 'Continue', flags: [ 'primary', 'constructive' ] },
+ *         { action: 'help', modes: 'edit', label: 'Help' },
+ *         { modes: 'edit', label: 'Cancel', flags: 'safe' },
+ *         { action: 'back', modes: 'help', label: 'Back', flags: 'safe' }
+ *     ];
+ *
+ *     MyProcessDialog.prototype.initialize = function () {
+ *         MyProcessDialog.parent.prototype.initialize.apply( this, arguments );
+ *         this.panel1 = new OO.ui.PanelLayout( { padded: true, expanded: false } );
+ *         this.panel1.$element.append( '<p>This dialog uses an action set (continue, help, cancel, back) configured with modes. This is edit mode. Click \'help\' to see help mode.</p>' );
+ *         this.panel2 = new OO.ui.PanelLayout( { padded: true, expanded: false } );
+ *         this.panel2.$element.append( '<p>This is help mode. Only the \'back\' action widget is configured to be visible here. Click \'back\' to return to \'edit\' mode.</p>' );
+ *         this.stackLayout = new OO.ui.StackLayout( {
+ *             items: [ this.panel1, this.panel2 ]
+ *         } );
+ *         this.$body.append( this.stackLayout.$element );
+ *     };
+ *     MyProcessDialog.prototype.getSetupProcess = function ( data ) {
+ *         return MyProcessDialog.parent.prototype.getSetupProcess.call( this, data )
+ *             .next( function () {
+ *                 this.actions.setMode( 'edit' );
+ *             }, this );
+ *     };
+ *     MyProcessDialog.prototype.getActionProcess = function ( action ) {
+ *         if ( action === 'help' ) {
+ *             this.actions.setMode( 'help' );
+ *             this.stackLayout.setItem( this.panel2 );
+ *         } else if ( action === 'back' ) {
+ *             this.actions.setMode( 'edit' );
+ *             this.stackLayout.setItem( this.panel1 );
+ *         } else if ( action === 'continue' ) {
+ *             var dialog = this;
+ *             return new OO.ui.Process( function () {
+ *                 dialog.close();
+ *             } );
+ *         }
+ *         return MyProcessDialog.parent.prototype.getActionProcess.call( this, action );
+ *     };
+ *     MyProcessDialog.prototype.getBodyHeight = function () {
+ *         return this.panel1.$element.outerHeight( true );
+ *     };
+ *     var windowManager = new OO.ui.WindowManager();
+ *     $( 'body' ).append( windowManager.$element );
+ *     var dialog = new MyProcessDialog( {
+ *         size: 'medium'
+ *     } );
+ *     windowManager.addWindows( [ dialog ] );
+ *     windowManager.openWindow( dialog );
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Process_Dialogs#Action_sets
+ *
+ * @abstract
+ * @class
+ * @mixins OO.EventEmitter
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.ActionSet = function OoUiActionSet( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Mixin constructors
+       OO.EventEmitter.call( this );
+
+       // Properties
+       this.list = [];
+       this.categories = {
+               actions: 'getAction',
+               flags: 'getFlags',
+               modes: 'getModes'
+       };
+       this.categorized = {};
+       this.special = {};
+       this.others = [];
+       this.organized = false;
+       this.changing = false;
+       this.changed = false;
+};
+
+/* Setup */
+
+OO.mixinClass( OO.ui.ActionSet, OO.EventEmitter );
+
+/* Static Properties */
+
+/**
+ * Symbolic name of the flags used to identify special actions. Special actions are displayed in the
+ *  header of a {@link OO.ui.ProcessDialog process dialog}.
+ *  See the [OOjs UI documentation on MediaWiki][2] for more information and examples.
+ *
+ *  [2]:https://www.mediawiki.org/wiki/OOjs_UI/Windows/Process_Dialogs
+ *
+ * @abstract
+ * @static
+ * @inheritable
+ * @property {string}
+ */
+OO.ui.ActionSet.static.specialFlags = [ 'safe', 'primary' ];
+
+/* Events */
+
+/**
+ * @event click
+ *
+ * A 'click' event is emitted when an action is clicked.
+ *
+ * @param {OO.ui.ActionWidget} action Action that was clicked
+ */
+
+/**
+ * @event resize
+ *
+ * A 'resize' event is emitted when an action widget is resized.
+ *
+ * @param {OO.ui.ActionWidget} action Action that was resized
+ */
+
+/**
+ * @event add
+ *
+ * An 'add' event is emitted when actions are {@link #method-add added} to the action set.
+ *
+ * @param {OO.ui.ActionWidget[]} added Actions added
+ */
+
+/**
+ * @event remove
+ *
+ * A 'remove' event is emitted when actions are {@link #method-remove removed}
+ *  or {@link #clear cleared}.
+ *
+ * @param {OO.ui.ActionWidget[]} added Actions removed
+ */
+
+/**
+ * @event change
+ *
+ * A 'change' event is emitted when actions are {@link #method-add added}, {@link #clear cleared},
+ * or {@link #method-remove removed} from the action set or when the {@link #setMode mode} is changed.
+ *
+ */
+
+/* Methods */
+
+/**
+ * Handle action change events.
+ *
+ * @private
+ * @fires change
+ */
+OO.ui.ActionSet.prototype.onActionChange = function () {
+       this.organized = false;
+       if ( this.changing ) {
+               this.changed = true;
+       } else {
+               this.emit( 'change' );
+       }
+};
+
+/**
+ * Check if an action is one of the special actions.
+ *
+ * @param {OO.ui.ActionWidget} action Action to check
+ * @return {boolean} Action is special
+ */
+OO.ui.ActionSet.prototype.isSpecial = function ( action ) {
+       var flag;
+
+       for ( flag in this.special ) {
+               if ( action === this.special[ flag ] ) {
+                       return true;
+               }
+       }
+
+       return false;
+};
+
+/**
+ * Get action widgets based on the specified filter: ‘actions’, ‘flags’, ‘modes’, ‘visible’,
+ *  or ‘disabled’.
+ *
+ * @param {Object} [filters] Filters to use, omit to get all actions
+ * @param {string|string[]} [filters.actions] Actions that action widgets must have
+ * @param {string|string[]} [filters.flags] Flags that action widgets must have (e.g., 'safe')
+ * @param {string|string[]} [filters.modes] Modes that action widgets must have
+ * @param {boolean} [filters.visible] Action widgets must be visible
+ * @param {boolean} [filters.disabled] Action widgets must be disabled
+ * @return {OO.ui.ActionWidget[]} Action widgets matching all criteria
+ */
+OO.ui.ActionSet.prototype.get = function ( filters ) {
+       var i, len, list, category, actions, index, match, matches;
+
+       if ( filters ) {
+               this.organize();
+
+               // Collect category candidates
+               matches = [];
+               for ( category in this.categorized ) {
+                       list = filters[ category ];
+                       if ( list ) {
+                               if ( !Array.isArray( list ) ) {
+                                       list = [ list ];
+                               }
+                               for ( i = 0, len = list.length; i < len; i++ ) {
+                                       actions = this.categorized[ category ][ list[ i ] ];
+                                       if ( Array.isArray( actions ) ) {
+                                               matches.push.apply( matches, actions );
+                                       }
+                               }
+                       }
+               }
+               // Remove by boolean filters
+               for ( i = 0, len = matches.length; i < len; i++ ) {
+                       match = matches[ i ];
+                       if (
+                               ( filters.visible !== undefined && match.isVisible() !== filters.visible ) ||
+                               ( filters.disabled !== undefined && match.isDisabled() !== filters.disabled )
+                       ) {
+                               matches.splice( i, 1 );
+                               len--;
+                               i--;
+                       }
+               }
+               // Remove duplicates
+               for ( i = 0, len = matches.length; i < len; i++ ) {
+                       match = matches[ i ];
+                       index = matches.lastIndexOf( match );
+                       while ( index !== i ) {
+                               matches.splice( index, 1 );
+                               len--;
+                               index = matches.lastIndexOf( match );
+                       }
+               }
+               return matches;
+       }
+       return this.list.slice();
+};
+
+/**
+ * Get 'special' actions.
+ *
+ * Special actions are the first visible action widgets with special flags, such as 'safe' and 'primary'.
+ * Special flags can be configured in subclasses by changing the static #specialFlags property.
+ *
+ * @return {OO.ui.ActionWidget[]|null} 'Special' action widgets.
+ */
+OO.ui.ActionSet.prototype.getSpecial = function () {
+       this.organize();
+       return $.extend( {}, this.special );
+};
+
+/**
+ * Get 'other' actions.
+ *
+ * Other actions include all non-special visible action widgets.
+ *
+ * @return {OO.ui.ActionWidget[]} 'Other' action widgets
+ */
+OO.ui.ActionSet.prototype.getOthers = function () {
+       this.organize();
+       return this.others.slice();
+};
+
+/**
+ * Set the mode  (e.g., ‘edit’ or ‘view’). Only {@link OO.ui.ActionWidget#modes actions} configured
+ * to be available in the specified mode will be made visible. All other actions will be hidden.
+ *
+ * @param {string} mode The mode. Only actions configured to be available in the specified
+ *  mode will be made visible.
+ * @chainable
+ * @fires toggle
+ * @fires change
+ */
+OO.ui.ActionSet.prototype.setMode = function ( mode ) {
+       var i, len, action;
+
+       this.changing = true;
+       for ( i = 0, len = this.list.length; i < len; i++ ) {
+               action = this.list[ i ];
+               action.toggle( action.hasMode( mode ) );
+       }
+
+       this.organized = false;
+       this.changing = false;
+       this.emit( 'change' );
+
+       return this;
+};
+
+/**
+ * Set the abilities of the specified actions.
+ *
+ * Action widgets that are configured with the specified actions will be enabled
+ * or disabled based on the boolean values specified in the `actions`
+ * parameter.
+ *
+ * @param {Object.<string,boolean>} actions A list keyed by action name with boolean
+ *  values that indicate whether or not the action should be enabled.
+ * @chainable
+ */
+OO.ui.ActionSet.prototype.setAbilities = function ( actions ) {
+       var i, len, action, item;
+
+       for ( i = 0, len = this.list.length; i < len; i++ ) {
+               item = this.list[ i ];
+               action = item.getAction();
+               if ( actions[ action ] !== undefined ) {
+                       item.setDisabled( !actions[ action ] );
+               }
+       }
+
+       return this;
+};
+
+/**
+ * Executes a function once per action.
+ *
+ * When making changes to multiple actions, use this method instead of iterating over the actions
+ * manually to defer emitting a #change event until after all actions have been changed.
+ *
+ * @param {Object|null} actions Filters to use to determine which actions to iterate over; see #get
+ * @param {Function} callback Callback to run for each action; callback is invoked with three
+ *   arguments: the action, the action's index, the list of actions being iterated over
+ * @chainable
+ */
+OO.ui.ActionSet.prototype.forEach = function ( filter, callback ) {
+       this.changed = false;
+       this.changing = true;
+       this.get( filter ).forEach( callback );
+       this.changing = false;
+       if ( this.changed ) {
+               this.emit( 'change' );
+       }
+
+       return this;
+};
+
+/**
+ * Add action widgets to the action set.
+ *
+ * @param {OO.ui.ActionWidget[]} actions Action widgets to add
+ * @chainable
+ * @fires add
+ * @fires change
+ */
+OO.ui.ActionSet.prototype.add = function ( actions ) {
+       var i, len, action;
+
+       this.changing = true;
+       for ( i = 0, len = actions.length; i < len; i++ ) {
+               action = actions[ i ];
+               action.connect( this, {
+                       click: [ 'emit', 'click', action ],
+                       resize: [ 'emit', 'resize', action ],
+                       toggle: [ 'onActionChange' ]
+               } );
+               this.list.push( action );
+       }
+       this.organized = false;
+       this.emit( 'add', actions );
+       this.changing = false;
+       this.emit( 'change' );
+
+       return this;
+};
+
+/**
+ * Remove action widgets from the set.
+ *
+ * To remove all actions, you may wish to use the #clear method instead.
+ *
+ * @param {OO.ui.ActionWidget[]} actions Action widgets to remove
+ * @chainable
+ * @fires remove
+ * @fires change
+ */
+OO.ui.ActionSet.prototype.remove = function ( actions ) {
+       var i, len, index, action;
+
+       this.changing = true;
+       for ( i = 0, len = actions.length; i < len; i++ ) {
+               action = actions[ i ];
+               index = this.list.indexOf( action );
+               if ( index !== -1 ) {
+                       action.disconnect( this );
+                       this.list.splice( index, 1 );
+               }
+       }
+       this.organized = false;
+       this.emit( 'remove', actions );
+       this.changing = false;
+       this.emit( 'change' );
+
+       return this;
+};
+
+/**
+ * Remove all action widets from the set.
+ *
+ * To remove only specified actions, use the {@link #method-remove remove} method instead.
+ *
+ * @chainable
+ * @fires remove
+ * @fires change
+ */
+OO.ui.ActionSet.prototype.clear = function () {
+       var i, len, action,
+               removed = this.list.slice();
+
+       this.changing = true;
+       for ( i = 0, len = this.list.length; i < len; i++ ) {
+               action = this.list[ i ];
+               action.disconnect( this );
+       }
+
+       this.list = [];
+
+       this.organized = false;
+       this.emit( 'remove', removed );
+       this.changing = false;
+       this.emit( 'change' );
+
+       return this;
+};
+
+/**
+ * Organize actions.
+ *
+ * This is called whenever organized information is requested. It will only reorganize the actions
+ * if something has changed since the last time it ran.
+ *
+ * @private
+ * @chainable
+ */
+OO.ui.ActionSet.prototype.organize = function () {
+       var i, iLen, j, jLen, flag, action, category, list, item, special,
+               specialFlags = this.constructor.static.specialFlags;
+
+       if ( !this.organized ) {
+               this.categorized = {};
+               this.special = {};
+               this.others = [];
+               for ( i = 0, iLen = this.list.length; i < iLen; i++ ) {
+                       action = this.list[ i ];
+                       if ( action.isVisible() ) {
+                               // Populate categories
+                               for ( category in this.categories ) {
+                                       if ( !this.categorized[ category ] ) {
+                                               this.categorized[ category ] = {};
+                                       }
+                                       list = action[ this.categories[ category ] ]();
+                                       if ( !Array.isArray( list ) ) {
+                                               list = [ list ];
+                                       }
+                                       for ( j = 0, jLen = list.length; j < jLen; j++ ) {
+                                               item = list[ j ];
+                                               if ( !this.categorized[ category ][ item ] ) {
+                                                       this.categorized[ category ][ item ] = [];
+                                               }
+                                               this.categorized[ category ][ item ].push( action );
+                                       }
+                               }
+                               // Populate special/others
+                               special = false;
+                               for ( j = 0, jLen = specialFlags.length; j < jLen; j++ ) {
+                                       flag = specialFlags[ j ];
+                                       if ( !this.special[ flag ] && action.hasFlag( flag ) ) {
+                                               this.special[ flag ] = action;
+                                               special = true;
+                                               break;
+                                       }
+                               }
+                               if ( !special ) {
+                                       this.others.push( action );
+                               }
+                       }
+               }
+               this.organized = true;
+       }
+
+       return this;
+};
+
+/**
+ * Errors contain a required message (either a string or jQuery selection) that is used to describe what went wrong
+ * in a {@link OO.ui.Process process}. The error's #recoverable and #warning configurations are used to customize the
+ * appearance and functionality of the error interface.
+ *
+ * The basic error interface contains a formatted error message as well as two buttons: 'Dismiss' and 'Try again' (i.e., the error
+ * is 'recoverable' by default). If the error is not recoverable, the 'Try again' button will not be rendered and the widget
+ * that initiated the failed process will be disabled.
+ *
+ * If the error is a warning, the error interface will include a 'Dismiss' and a 'Continue' button, which will try the
+ * process again.
+ *
+ * For an example of error interfaces, please see the [OOjs UI documentation on MediaWiki][1].
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Process_Dialogs#Processes_and_errors
+ *
+ * @class
+ *
+ * @constructor
+ * @param {string|jQuery} message Description of error
+ * @param {Object} [config] Configuration options
+ * @cfg {boolean} [recoverable=true] Error is recoverable.
+ *  By default, errors are recoverable, and users can try the process again.
+ * @cfg {boolean} [warning=false] Error is a warning.
+ *  If the error is a warning, the error interface will include a
+ *  'Dismiss' and a 'Continue' button. It is the responsibility of the developer to ensure that the warning
+ *  is not triggered a second time if the user chooses to continue.
+ */
+OO.ui.Error = function OoUiError( message, config ) {
+       // Allow passing positional parameters inside the config object
+       if ( OO.isPlainObject( message ) && config === undefined ) {
+               config = message;
+               message = config.message;
+       }
+
+       // Configuration initialization
+       config = config || {};
+
+       // Properties
+       this.message = message instanceof jQuery ? message : String( message );
+       this.recoverable = config.recoverable === undefined || !!config.recoverable;
+       this.warning = !!config.warning;
+};
+
+/* Setup */
+
+OO.initClass( OO.ui.Error );
+
+/* Methods */
+
+/**
+ * Check if the error is recoverable.
+ *
+ * If the error is recoverable, users are able to try the process again.
+ *
+ * @return {boolean} Error is recoverable
+ */
+OO.ui.Error.prototype.isRecoverable = function () {
+       return this.recoverable;
+};
+
+/**
+ * Check if the error is a warning.
+ *
+ * If the error is a warning, the error interface will include a 'Dismiss' and a 'Continue' button.
+ *
+ * @return {boolean} Error is warning
+ */
+OO.ui.Error.prototype.isWarning = function () {
+       return this.warning;
+};
+
+/**
+ * Get error message as DOM nodes.
+ *
+ * @return {jQuery} Error message in DOM nodes
+ */
+OO.ui.Error.prototype.getMessage = function () {
+       return this.message instanceof jQuery ?
+               this.message.clone() :
+               $( '<div>' ).text( this.message ).contents();
+};
+
+/**
+ * Get the error message text.
+ *
+ * @return {string} Error message
+ */
+OO.ui.Error.prototype.getMessageText = function () {
+       return this.message instanceof jQuery ? this.message.text() : this.message;
+};
+
+/**
+ * A Process is a list of steps that are called in sequence. The step can be a number, a jQuery promise,
+ * or a function:
+ *
+ * - **number**: the process will wait for the specified number of milliseconds before proceeding.
+ * - **promise**: the process will continue to the next step when the promise is successfully resolved
+ *  or stop if the promise is rejected.
+ * - **function**: the process will execute the function. The process will stop if the function returns
+ *  either a boolean `false` or a promise that is rejected; if the function returns a number, the process
+ *  will wait for that number of milliseconds before proceeding.
+ *
+ * If the process fails, an {@link OO.ui.Error error} is generated. Depending on how the error is
+ * configured, users can dismiss the error and try the process again, or not. If a process is stopped,
+ * its remaining steps will not be performed.
+ *
+ * @class
+ *
+ * @constructor
+ * @param {number|jQuery.Promise|Function} step Number of miliseconds to wait before proceeding, promise
+ *  that must be resolved before proceeding, or a function to execute. See #createStep for more information. see #createStep for more information
+ * @param {Object} [context=null] Execution context of the function. The context is ignored if the step is
+ *  a number or promise.
+ * @return {Object} Step object, with `callback` and `context` properties
+ */
+OO.ui.Process = function ( step, context ) {
+       // Properties
+       this.steps = [];
+
+       // Initialization
+       if ( step !== undefined ) {
+               this.next( step, context );
+       }
+};
+
+/* Setup */
+
+OO.initClass( OO.ui.Process );
+
+/* Methods */
+
+/**
+ * Start the process.
+ *
+ * @return {jQuery.Promise} Promise that is resolved when all steps have successfully completed.
+ *  If any of the steps return a promise that is rejected or a boolean false, this promise is rejected
+ *  and any remaining steps are not performed.
+ */
+OO.ui.Process.prototype.execute = function () {
+       var i, len, promise;
+
+       /**
+        * Continue execution.
+        *
+        * @ignore
+        * @param {Array} step A function and the context it should be called in
+        * @return {Function} Function that continues the process
+        */
+       function proceed( step ) {
+               return function () {
+                       // Execute step in the correct context
+                       var deferred,
+                               result = step.callback.call( step.context );
+
+                       if ( result === false ) {
+                               // Use rejected promise for boolean false results
+                               return $.Deferred().reject( [] ).promise();
+                       }
+                       if ( typeof result === 'number' ) {
+                               if ( result < 0 ) {
+                                       throw new Error( 'Cannot go back in time: flux capacitor is out of service' );
+                               }
+                               // Use a delayed promise for numbers, expecting them to be in milliseconds
+                               deferred = $.Deferred();
+                               setTimeout( deferred.resolve, result );
+                               return deferred.promise();
+                       }
+                       if ( result instanceof OO.ui.Error ) {
+                               // Use rejected promise for error
+                               return $.Deferred().reject( [ result ] ).promise();
+                       }
+                       if ( Array.isArray( result ) && result.length && result[ 0 ] instanceof OO.ui.Error ) {
+                               // Use rejected promise for list of errors
+                               return $.Deferred().reject( result ).promise();
+                       }
+                       // Duck-type the object to see if it can produce a promise
+                       if ( result && $.isFunction( result.promise ) ) {
+                               // Use a promise generated from the result
+                               return result.promise();
+                       }
+                       // Use resolved promise for other results
+                       return $.Deferred().resolve().promise();
+               };
+       }
+
+       if ( this.steps.length ) {
+               // Generate a chain reaction of promises
+               promise = proceed( this.steps[ 0 ] )();
+               for ( i = 1, len = this.steps.length; i < len; i++ ) {
+                       promise = promise.then( proceed( this.steps[ i ] ) );
+               }
+       } else {
+               promise = $.Deferred().resolve().promise();
+       }
+
+       return promise;
+};
+
+/**
+ * Create a process step.
+ *
+ * @private
+ * @param {number|jQuery.Promise|Function} step
+ *
+ * - Number of milliseconds to wait before proceeding
+ * - Promise that must be resolved before proceeding
+ * - Function to execute
+ *   - If the function returns a boolean false the process will stop
+ *   - If the function returns a promise, the process will continue to the next
+ *     step when the promise is resolved or stop if the promise is rejected
+ *   - If the function returns a number, the process will wait for that number of
+ *     milliseconds before proceeding
+ * @param {Object} [context=null] Execution context of the function. The context is
+ *  ignored if the step is a number or promise.
+ * @return {Object} Step object, with `callback` and `context` properties
+ */
+OO.ui.Process.prototype.createStep = function ( step, context ) {
+       if ( typeof step === 'number' || $.isFunction( step.promise ) ) {
+               return {
+                       callback: function () {
+                               return step;
+                       },
+                       context: null
+               };
+       }
+       if ( $.isFunction( step ) ) {
+               return {
+                       callback: step,
+                       context: context
+               };
+       }
+       throw new Error( 'Cannot create process step: number, promise or function expected' );
+};
+
+/**
+ * Add step to the beginning of the process.
+ *
+ * @inheritdoc #createStep
+ * @return {OO.ui.Process} this
+ * @chainable
+ */
+OO.ui.Process.prototype.first = function ( step, context ) {
+       this.steps.unshift( this.createStep( step, context ) );
+       return this;
+};
+
+/**
+ * Add step to the end of the process.
+ *
+ * @inheritdoc #createStep
+ * @return {OO.ui.Process} this
+ * @chainable
+ */
+OO.ui.Process.prototype.next = function ( step, context ) {
+       this.steps.push( this.createStep( step, context ) );
+       return this;
+};
+
+/**
+ * Window managers are used to open and close {@link OO.ui.Window windows} and control their presentation.
+ * Managed windows are mutually exclusive. If a new window is opened while a current window is opening
+ * or is opened, the current window will be closed and any ongoing {@link OO.ui.Process process} will be cancelled. Windows
+ * themselves are persistent and—rather than being torn down when closed—can be repopulated with the
+ * pertinent data and reused.
+ *
+ * Over the lifecycle of a window, the window manager makes available three promises: `opening`,
+ * `opened`, and `closing`, which represent the primary stages of the cycle:
+ *
+ * **Opening**: the opening stage begins when the window manager’s #openWindow or a window’s
+ * {@link OO.ui.Window#open open} method is used, and the window manager begins to open the window.
+ *
+ * - an `opening` event is emitted with an `opening` promise
+ * - the #getSetupDelay method is called and the returned value is used to time a pause in execution before
+ *   the window’s {@link OO.ui.Window#getSetupProcess getSetupProcess} method is called on the
+ *   window and its result executed
+ * - a `setup` progress notification is emitted from the `opening` promise
+ * - the #getReadyDelay method is called the returned value is used to time a pause in execution before
+ *   the window’s {@link OO.ui.Window#getReadyProcess getReadyProcess} method is called on the
+ *   window and its result executed
+ * - a `ready` progress notification is emitted from the `opening` promise
+ * - the `opening` promise is resolved with an `opened` promise
+ *
+ * **Opened**: the window is now open.
+ *
+ * **Closing**: the closing stage begins when the window manager's #closeWindow or the
+ * window's {@link OO.ui.Window#close close} methods is used, and the window manager begins
+ * to close the window.
+ *
+ * - the `opened` promise is resolved with `closing` promise and a `closing` event is emitted
+ * - the #getHoldDelay method is called and the returned value is used to time a pause in execution before
+ *   the window's {@link OO.ui.Window#getHoldProcess getHoldProces} method is called on the
+ *   window and its result executed
+ * - a `hold` progress notification is emitted from the `closing` promise
+ * - the #getTeardownDelay() method is called and the returned value is used to time a pause in execution before
+ *   the window's {@link OO.ui.Window#getTeardownProcess getTeardownProcess} method is called on the
+ *   window and its result executed
+ * - a `teardown` progress notification is emitted from the `closing` promise
+ * - the `closing` promise is resolved. The window is now closed
+ *
+ * See the [OOjs UI documentation on MediaWiki][1] for more information.
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Window_managers
+ *
+ * @class
+ * @extends OO.ui.Element
+ * @mixins OO.EventEmitter
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {OO.Factory} [factory] Window factory to use for automatic instantiation
+ *  Note that window classes that are instantiated with a factory must have
+ *  a {@link OO.ui.Dialog#static-name static name} property that specifies a symbolic name.
+ * @cfg {boolean} [modal=true] Prevent interaction outside the dialog
+ */
+OO.ui.WindowManager = function OoUiWindowManager( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Parent constructor
+       OO.ui.WindowManager.parent.call( this, config );
+
+       // Mixin constructors
+       OO.EventEmitter.call( this );
+
+       // Properties
+       this.factory = config.factory;
+       this.modal = config.modal === undefined || !!config.modal;
+       this.windows = {};
+       this.opening = null;
+       this.opened = null;
+       this.closing = null;
+       this.preparingToOpen = null;
+       this.preparingToClose = null;
+       this.currentWindow = null;
+       this.globalEvents = false;
+       this.$ariaHidden = null;
+       this.onWindowResizeTimeout = null;
+       this.onWindowResizeHandler = this.onWindowResize.bind( this );
+       this.afterWindowResizeHandler = this.afterWindowResize.bind( this );
+
+       // Initialization
+       this.$element
+               .addClass( 'oo-ui-windowManager' )
+               .toggleClass( 'oo-ui-windowManager-modal', this.modal );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.WindowManager, OO.ui.Element );
+OO.mixinClass( OO.ui.WindowManager, OO.EventEmitter );
+
+/* Events */
+
+/**
+ * An 'opening' event is emitted when the window begins to be opened.
+ *
+ * @event opening
+ * @param {OO.ui.Window} win Window that's being opened
+ * @param {jQuery.Promise} opening An `opening` promise resolved with a value when the window is opened successfully.
+ *  When the `opening` promise is resolved, the first argument of the value is an 'opened' promise, the second argument
+ *  is the opening data. The `opening` promise emits `setup` and `ready` notifications when those processes are complete.
+ * @param {Object} data Window opening data
+ */
+
+/**
+ * A 'closing' event is emitted when the window begins to be closed.
+ *
+ * @event closing
+ * @param {OO.ui.Window} win Window that's being closed
+ * @param {jQuery.Promise} closing A `closing` promise is resolved with a value when the window
+ *  is closed successfully. The promise emits `hold` and `teardown` notifications when those
+ *  processes are complete. When the `closing` promise is resolved, the first argument of its value
+ *  is the closing data.
+ * @param {Object} data Window closing data
+ */
+
+/**
+ * A 'resize' event is emitted when a window is resized.
+ *
+ * @event resize
+ * @param {OO.ui.Window} win Window that was resized
+ */
+
+/* Static Properties */
+
+/**
+ * Map of the symbolic name of each window size and its CSS properties.
+ *
+ * @static
+ * @inheritable
+ * @property {Object}
+ */
+OO.ui.WindowManager.static.sizes = {
+       small: {
+               width: 300
+       },
+       medium: {
+               width: 500
+       },
+       large: {
+               width: 700
+       },
+       larger: {
+               width: 900
+       },
+       full: {
+               // These can be non-numeric because they are never used in calculations
+               width: '100%',
+               height: '100%'
+       }
+};
+
+/**
+ * Symbolic name of the default window size.
+ *
+ * The default size is used if the window's requested size is not recognized.
+ *
+ * @static
+ * @inheritable
+ * @property {string}
+ */
+OO.ui.WindowManager.static.defaultSize = 'medium';
+
+/* Methods */
+
+/**
+ * Handle window resize events.
+ *
+ * @private
+ * @param {jQuery.Event} e Window resize event
+ */
+OO.ui.WindowManager.prototype.onWindowResize = function () {
+       clearTimeout( this.onWindowResizeTimeout );
+       this.onWindowResizeTimeout = setTimeout( this.afterWindowResizeHandler, 200 );
+};
+
+/**
+ * Handle window resize events.
+ *
+ * @private
+ * @param {jQuery.Event} e Window resize event
+ */
+OO.ui.WindowManager.prototype.afterWindowResize = function () {
+       if ( this.currentWindow ) {
+               this.updateWindowSize( this.currentWindow );
+       }
+};
+
+/**
+ * Check if window is opening.
+ *
+ * @return {boolean} Window is opening
+ */
+OO.ui.WindowManager.prototype.isOpening = function ( win ) {
+       return win === this.currentWindow && !!this.opening && this.opening.state() === 'pending';
+};
+
+/**
+ * Check if window is closing.
+ *
+ * @return {boolean} Window is closing
+ */
+OO.ui.WindowManager.prototype.isClosing = function ( win ) {
+       return win === this.currentWindow && !!this.closing && this.closing.state() === 'pending';
+};
+
+/**
+ * Check if window is opened.
+ *
+ * @return {boolean} Window is opened
+ */
+OO.ui.WindowManager.prototype.isOpened = function ( win ) {
+       return win === this.currentWindow && !!this.opened && this.opened.state() === 'pending';
+};
+
+/**
+ * Check if a window is being managed.
+ *
+ * @param {OO.ui.Window} win Window to check
+ * @return {boolean} Window is being managed
+ */
+OO.ui.WindowManager.prototype.hasWindow = function ( win ) {
+       var name;
+
+       for ( name in this.windows ) {
+               if ( this.windows[ name ] === win ) {
+                       return true;
+               }
+       }
+
+       return false;
+};
+
+/**
+ * Get the number of milliseconds to wait after opening begins before executing the ‘setup’ process.
+ *
+ * @param {OO.ui.Window} win Window being opened
+ * @param {Object} [data] Window opening data
+ * @return {number} Milliseconds to wait
+ */
+OO.ui.WindowManager.prototype.getSetupDelay = function () {
+       return 0;
+};
+
+/**
+ * Get the number of milliseconds to wait after setup has finished before executing the ‘ready’ process.
+ *
+ * @param {OO.ui.Window} win Window being opened
+ * @param {Object} [data] Window opening data
+ * @return {number} Milliseconds to wait
+ */
+OO.ui.WindowManager.prototype.getReadyDelay = function () {
+       return 0;
+};
+
+/**
+ * Get the number of milliseconds to wait after closing has begun before executing the 'hold' process.
+ *
+ * @param {OO.ui.Window} win Window being closed
+ * @param {Object} [data] Window closing data
+ * @return {number} Milliseconds to wait
+ */
+OO.ui.WindowManager.prototype.getHoldDelay = function () {
+       return 0;
+};
+
+/**
+ * Get the number of milliseconds to wait after the ‘hold’ process has finished before
+ * executing the ‘teardown’ process.
+ *
+ * @param {OO.ui.Window} win Window being closed
+ * @param {Object} [data] Window closing data
+ * @return {number} Milliseconds to wait
+ */
+OO.ui.WindowManager.prototype.getTeardownDelay = function () {
+       return this.modal ? 250 : 0;
+};
+
+/**
+ * Get a window by its symbolic name.
+ *
+ * If the window is not yet instantiated and its symbolic name is recognized by a factory, it will be
+ * instantiated and added to the window manager automatically. Please see the [OOjs UI documentation on MediaWiki][3]
+ * for more information about using factories.
+ * [3]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Window_managers
+ *
+ * @param {string} name Symbolic name of the window
+ * @return {jQuery.Promise} Promise resolved with matching window, or rejected with an OO.ui.Error
+ * @throws {Error} An error is thrown if the symbolic name is not recognized by the factory.
+ * @throws {Error} An error is thrown if the named window is not recognized as a managed window.
+ */
+OO.ui.WindowManager.prototype.getWindow = function ( name ) {
+       var deferred = $.Deferred(),
+               win = this.windows[ name ];
+
+       if ( !( win instanceof OO.ui.Window ) ) {
+               if ( this.factory ) {
+                       if ( !this.factory.lookup( name ) ) {
+                               deferred.reject( new OO.ui.Error(
+                                       'Cannot auto-instantiate window: symbolic name is unrecognized by the factory'
+                               ) );
+                       } else {
+                               win = this.factory.create( name );
+                               this.addWindows( [ win ] );
+                               deferred.resolve( win );
+                       }
+               } else {
+                       deferred.reject( new OO.ui.Error(
+                               'Cannot get unmanaged window: symbolic name unrecognized as a managed window'
+                       ) );
+               }
+       } else {
+               deferred.resolve( win );
+       }
+
+       return deferred.promise();
+};
+
+/**
+ * Get current window.
+ *
+ * @return {OO.ui.Window|null} Currently opening/opened/closing window
+ */
+OO.ui.WindowManager.prototype.getCurrentWindow = function () {
+       return this.currentWindow;
+};
+
+/**
+ * Open a window.
+ *
+ * @param {OO.ui.Window|string} win Window object or symbolic name of window to open
+ * @param {Object} [data] Window opening data
+ * @return {jQuery.Promise} An `opening` promise resolved when the window is done opening.
+ *  See {@link #event-opening 'opening' event}  for more information about `opening` promises.
+ * @fires opening
+ */
+OO.ui.WindowManager.prototype.openWindow = function ( win, data ) {
+       var manager = this,
+               opening = $.Deferred();
+
+       // Argument handling
+       if ( typeof win === 'string' ) {
+               return this.getWindow( win ).then( function ( win ) {
+                       return manager.openWindow( win, data );
+               } );
+       }
+
+       // Error handling
+       if ( !this.hasWindow( win ) ) {
+               opening.reject( new OO.ui.Error(
+                       'Cannot open window: window is not attached to manager'
+               ) );
+       } else if ( this.preparingToOpen || this.opening || this.opened ) {
+               opening.reject( new OO.ui.Error(
+                       'Cannot open window: another window is opening or open'
+               ) );
+       }
+
+       // Window opening
+       if ( opening.state() !== 'rejected' ) {
+               // If a window is currently closing, wait for it to complete
+               this.preparingToOpen = $.when( this.closing );
+               // Ensure handlers get called after preparingToOpen is set
+               this.preparingToOpen.done( function () {
+                       if ( manager.modal ) {
+                               manager.toggleGlobalEvents( true );
+                               manager.toggleAriaIsolation( true );
+                       }
+                       manager.currentWindow = win;
+                       manager.opening = opening;
+                       manager.preparingToOpen = null;
+                       manager.emit( 'opening', win, opening, data );
+                       setTimeout( function () {
+                               win.setup( data ).then( function () {
+                                       manager.updateWindowSize( win );
+                                       manager.opening.notify( { state: 'setup' } );
+                                       setTimeout( function () {
+                                               win.ready( data ).then( function () {
+                                                       manager.opening.notify( { state: 'ready' } );
+                                                       manager.opening = null;
+                                                       manager.opened = $.Deferred();
+                                                       opening.resolve( manager.opened.promise(), data );
+                                               }, function () {
+                                                       manager.opening = null;
+                                                       manager.opened = $.Deferred();
+                                                       opening.reject();
+                                                       manager.closeWindow( win );
+                                               } );
+                                       }, manager.getReadyDelay() );
+                               }, function () {
+                                       manager.opening = null;
+                                       manager.opened = $.Deferred();
+                                       opening.reject();
+                                       manager.closeWindow( win );
+                               } );
+                       }, manager.getSetupDelay() );
+               } );
+       }
+
+       return opening.promise();
+};
+
+/**
+ * Close a window.
+ *
+ * @param {OO.ui.Window|string} win Window object or symbolic name of window to close
+ * @param {Object} [data] Window closing data
+ * @return {jQuery.Promise} A `closing` promise resolved when the window is done closing.
+ *  See {@link #event-closing 'closing' event} for more information about closing promises.
+ * @throws {Error} An error is thrown if the window is not managed by the window manager.
+ * @fires closing
+ */
+OO.ui.WindowManager.prototype.closeWindow = function ( win, data ) {
+       var manager = this,
+               closing = $.Deferred(),
+               opened;
+
+       // Argument handling
+       if ( typeof win === 'string' ) {
+               win = this.windows[ win ];
+       } else if ( !this.hasWindow( win ) ) {
+               win = null;
+       }
+
+       // Error handling
+       if ( !win ) {
+               closing.reject( new OO.ui.Error(
+                       'Cannot close window: window is not attached to manager'
+               ) );
+       } else if ( win !== this.currentWindow ) {
+               closing.reject( new OO.ui.Error(
+                       'Cannot close window: window already closed with different data'
+               ) );
+       } else if ( this.preparingToClose || this.closing ) {
+               closing.reject( new OO.ui.Error(
+                       'Cannot close window: window already closing with different data'
+               ) );
+       }
+
+       // Window closing
+       if ( closing.state() !== 'rejected' ) {
+               // If the window is currently opening, close it when it's done
+               this.preparingToClose = $.when( this.opening );
+               // Ensure handlers get called after preparingToClose is set
+               this.preparingToClose.always( function () {
+                       manager.closing = closing;
+                       manager.preparingToClose = null;
+                       manager.emit( 'closing', win, closing, data );
+                       opened = manager.opened;
+                       manager.opened = null;
+                       opened.resolve( closing.promise(), data );
+                       setTimeout( function () {
+                               win.hold( data ).then( function () {
+                                       closing.notify( { state: 'hold' } );
+                                       setTimeout( function () {
+                                               win.teardown( data ).then( function () {
+                                                       closing.notify( { state: 'teardown' } );
+                                                       if ( manager.modal ) {
+                                                               manager.toggleGlobalEvents( false );
+                                                               manager.toggleAriaIsolation( false );
+                                                       }
+                                                       manager.closing = null;
+                                                       manager.currentWindow = null;
+                                                       closing.resolve( data );
+                                               } );
+                                       }, manager.getTeardownDelay() );
+                               } );
+                       }, manager.getHoldDelay() );
+               } );
+       }
+
+       return closing.promise();
+};
+
+/**
+ * Add windows to the window manager.
+ *
+ * Windows can be added by reference, symbolic name, or explicitly defined symbolic names.
+ * See the [OOjs ui documentation on MediaWiki] [2] for examples.
+ * [2]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Window_managers
+ *
+ * @param {Object.<string,OO.ui.Window>|OO.ui.Window[]} windows An array of window objects specified
+ *  by reference, symbolic name, or explicitly defined symbolic names.
+ * @throws {Error} An error is thrown if a window is added by symbolic name, but has neither an
+ *  explicit nor a statically configured symbolic name.
+ */
+OO.ui.WindowManager.prototype.addWindows = function ( windows ) {
+       var i, len, win, name, list;
+
+       if ( Array.isArray( windows ) ) {
+               // Convert to map of windows by looking up symbolic names from static configuration
+               list = {};
+               for ( i = 0, len = windows.length; i < len; i++ ) {
+                       name = windows[ i ].constructor.static.name;
+                       if ( typeof name !== 'string' ) {
+                               throw new Error( 'Cannot add window' );
+                       }
+                       list[ name ] = windows[ i ];
+               }
+       } else if ( OO.isPlainObject( windows ) ) {
+               list = windows;
+       }
+
+       // Add windows
+       for ( name in list ) {
+               win = list[ name ];
+               this.windows[ name ] = win.toggle( false );
+               this.$element.append( win.$element );
+               win.setManager( this );
+       }
+};
+
+/**
+ * Remove the specified windows from the windows manager.
+ *
+ * Windows will be closed before they are removed. If you wish to remove all windows, you may wish to use
+ * the #clearWindows method instead. If you no longer need the window manager and want to ensure that it no
+ * longer listens to events, use the #destroy method.
+ *
+ * @param {string[]} names Symbolic names of windows to remove
+ * @return {jQuery.Promise} Promise resolved when window is closed and removed
+ * @throws {Error} An error is thrown if the named windows are not managed by the window manager.
+ */
+OO.ui.WindowManager.prototype.removeWindows = function ( names ) {
+       var i, len, win, name, cleanupWindow,
+               manager = this,
+               promises = [],
+               cleanup = function ( name, win ) {
+                       delete manager.windows[ name ];
+                       win.$element.detach();
+               };
+
+       for ( i = 0, len = names.length; i < len; i++ ) {
+               name = names[ i ];
+               win = this.windows[ name ];
+               if ( !win ) {
+                       throw new Error( 'Cannot remove window' );
+               }
+               cleanupWindow = cleanup.bind( null, name, win );
+               promises.push( this.closeWindow( name ).then( cleanupWindow, cleanupWindow ) );
+       }
+
+       return $.when.apply( $, promises );
+};
+
+/**
+ * Remove all windows from the window manager.
+ *
+ * Windows will be closed before they are removed. Note that the window manager, though not in use, will still
+ * listen to events. If the window manager will not be used again, you may wish to use the #destroy method instead.
+ * To remove just a subset of windows, use the #removeWindows method.
+ *
+ * @return {jQuery.Promise} Promise resolved when all windows are closed and removed
+ */
+OO.ui.WindowManager.prototype.clearWindows = function () {
+       return this.removeWindows( Object.keys( this.windows ) );
+};
+
+/**
+ * Set dialog size. In general, this method should not be called directly.
+ *
+ * Fullscreen mode will be used if the dialog is too wide to fit in the screen.
+ *
+ * @chainable
+ */
+OO.ui.WindowManager.prototype.updateWindowSize = function ( win ) {
+       var isFullscreen;
+
+       // Bypass for non-current, and thus invisible, windows
+       if ( win !== this.currentWindow ) {
+               return;
+       }
+
+       isFullscreen = win.getSize() === 'full';
+
+       this.$element.toggleClass( 'oo-ui-windowManager-fullscreen', isFullscreen );
+       this.$element.toggleClass( 'oo-ui-windowManager-floating', !isFullscreen );
+       win.setDimensions( win.getSizeProperties() );
+
+       this.emit( 'resize', win );
+
+       return this;
+};
+
+/**
+ * Bind or unbind global events for scrolling.
+ *
+ * @private
+ * @param {boolean} [on] Bind global events
+ * @chainable
+ */
+OO.ui.WindowManager.prototype.toggleGlobalEvents = function ( on ) {
+       var scrollWidth, bodyMargin,
+               $body = $( this.getElementDocument().body ),
+               // We could have multiple window managers open so only modify
+               // the body css at the bottom of the stack
+               stackDepth = $body.data( 'windowManagerGlobalEvents' ) || 0 ;
+
+       on = on === undefined ? !!this.globalEvents : !!on;
+
+       if ( on ) {
+               if ( !this.globalEvents ) {
+                       $( this.getElementWindow() ).on( {
+                               // Start listening for top-level window dimension changes
+                               'orientationchange resize': this.onWindowResizeHandler
+                       } );
+                       if ( stackDepth === 0 ) {
+                               scrollWidth = window.innerWidth - document.documentElement.clientWidth;
+                               bodyMargin = parseFloat( $body.css( 'margin-right' ) ) || 0;
+                               $body.css( {
+                                       overflow: 'hidden',
+                                       'margin-right': bodyMargin + scrollWidth
+                               } );
+                       }
+                       stackDepth++;
+                       this.globalEvents = true;
+               }
+       } else if ( this.globalEvents ) {
+               $( this.getElementWindow() ).off( {
+                       // Stop listening for top-level window dimension changes
+                       'orientationchange resize': this.onWindowResizeHandler
+               } );
+               stackDepth--;
+               if ( stackDepth === 0 ) {
+                       $body.css( {
+                               overflow: '',
+                               'margin-right': ''
+                       } );
+               }
+               this.globalEvents = false;
+       }
+       $body.data( 'windowManagerGlobalEvents', stackDepth );
+
+       return this;
+};
+
+/**
+ * Toggle screen reader visibility of content other than the window manager.
+ *
+ * @private
+ * @param {boolean} [isolate] Make only the window manager visible to screen readers
+ * @chainable
+ */
+OO.ui.WindowManager.prototype.toggleAriaIsolation = function ( isolate ) {
+       isolate = isolate === undefined ? !this.$ariaHidden : !!isolate;
+
+       if ( isolate ) {
+               if ( !this.$ariaHidden ) {
+                       // Hide everything other than the window manager from screen readers
+                       this.$ariaHidden = $( 'body' )
+                               .children()
+                               .not( this.$element.parentsUntil( 'body' ).last() )
+                               .attr( 'aria-hidden', '' );
+               }
+       } else if ( this.$ariaHidden ) {
+               // Restore screen reader visibility
+               this.$ariaHidden.removeAttr( 'aria-hidden' );
+               this.$ariaHidden = null;
+       }
+
+       return this;
+};
+
+/**
+ * Destroy the window manager.
+ *
+ * Destroying the window manager ensures that it will no longer listen to events. If you would like to
+ * continue using the window manager, but wish to remove all windows from it, use the #clearWindows method
+ * instead.
+ */
+OO.ui.WindowManager.prototype.destroy = function () {
+       this.toggleGlobalEvents( false );
+       this.toggleAriaIsolation( false );
+       this.clearWindows();
+       this.$element.remove();
+};
+
+/**
+ * A window is a container for elements that are in a child frame. They are used with
+ * a window manager (OO.ui.WindowManager), which is used to open and close the window and control
+ * its presentation. The size of a window is specified using a symbolic name (e.g., ‘small’, ‘medium’,
+ * ‘large’), which is interpreted by the window manager. If the requested size is not recognized,
+ * the window manager will choose a sensible fallback.
+ *
+ * The lifecycle of a window has three primary stages (opening, opened, and closing) in which
+ * different processes are executed:
+ *
+ * **opening**: The opening stage begins when the window manager's {@link OO.ui.WindowManager#openWindow
+ * openWindow} or the window's {@link #open open} methods are used, and the window manager begins to open
+ * the window.
+ *
+ * - {@link #getSetupProcess} method is called and its result executed
+ * - {@link #getReadyProcess} method is called and its result executed
+ *
+ * **opened**: The window is now open
+ *
+ * **closing**: The closing stage begins when the window manager's
+ * {@link OO.ui.WindowManager#closeWindow closeWindow}
+ * or the window's {@link #close} methods are used, and the window manager begins to close the window.
+ *
+ * - {@link #getHoldProcess} method is called and its result executed
+ * - {@link #getTeardownProcess} method is called and its result executed. The window is now closed
+ *
+ * Each of the window's processes (setup, ready, hold, and teardown) can be extended in subclasses
+ * by overriding the window's #getSetupProcess, #getReadyProcess, #getHoldProcess and #getTeardownProcess
+ * methods. Note that each {@link OO.ui.Process process} is executed in series, so asynchronous
+ * processing can complete. Always assume window processes are executed asynchronously.
+ *
+ * For more information, please see the [OOjs UI documentation on MediaWiki] [1].
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Windows
+ *
+ * @abstract
+ * @class
+ * @extends OO.ui.Element
+ * @mixins OO.EventEmitter
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {string} [size] Symbolic name of the dialog size: `small`, `medium`, `large`, `larger` or
+ *  `full`.  If omitted, the value of the {@link #static-size static size} property will be used.
+ */
+OO.ui.Window = function OoUiWindow( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Parent constructor
+       OO.ui.Window.parent.call( this, config );
+
+       // Mixin constructors
+       OO.EventEmitter.call( this );
+
+       // Properties
+       this.manager = null;
+       this.size = config.size || this.constructor.static.size;
+       this.$frame = $( '<div>' );
+       this.$overlay = $( '<div>' );
+       this.$content = $( '<div>' );
+
+       this.$focusTrapBefore = $( '<div>' ).prop( 'tabIndex', 0 );
+       this.$focusTrapAfter = $( '<div>' ).prop( 'tabIndex', 0 );
+       this.$focusTraps = this.$focusTrapBefore.add( this.$focusTrapAfter );
+
+       // Initialization
+       this.$overlay.addClass( 'oo-ui-window-overlay' );
+       this.$content
+               .addClass( 'oo-ui-window-content' )
+               .attr( 'tabindex', 0 );
+       this.$frame
+               .addClass( 'oo-ui-window-frame' )
+               .append( this.$focusTrapBefore, this.$content, this.$focusTrapAfter );
+
+       this.$element
+               .addClass( 'oo-ui-window' )
+               .append( this.$frame, this.$overlay );
+
+       // Initially hidden - using #toggle may cause errors if subclasses override toggle with methods
+       // that reference properties not initialized at that time of parent class construction
+       // TODO: Find a better way to handle post-constructor setup
+       this.visible = false;
+       this.$element.addClass( 'oo-ui-element-hidden' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.Window, OO.ui.Element );
+OO.mixinClass( OO.ui.Window, OO.EventEmitter );
+
+/* Static Properties */
+
+/**
+ * Symbolic name of the window size: `small`, `medium`, `large`, `larger` or `full`.
+ *
+ * The static size is used if no #size is configured during construction.
+ *
+ * @static
+ * @inheritable
+ * @property {string}
+ */
+OO.ui.Window.static.size = 'medium';
+
+/* Methods */
+
+/**
+ * Handle mouse down events.
+ *
+ * @private
+ * @param {jQuery.Event} e Mouse down event
+ */
+OO.ui.Window.prototype.onMouseDown = function ( e ) {
+       // Prevent clicking on the click-block from stealing focus
+       if ( e.target === this.$element[ 0 ] ) {
+               return false;
+       }
+};
+
+/**
+ * Check if the window has been initialized.
+ *
+ * Initialization occurs when a window is added to a manager.
+ *
+ * @return {boolean} Window has been initialized
+ */
+OO.ui.Window.prototype.isInitialized = function () {
+       return !!this.manager;
+};
+
+/**
+ * Check if the window is visible.
+ *
+ * @return {boolean} Window is visible
+ */
+OO.ui.Window.prototype.isVisible = function () {
+       return this.visible;
+};
+
+/**
+ * Check if the window is opening.
+ *
+ * This method is a wrapper around the window manager's {@link OO.ui.WindowManager#isOpening isOpening}
+ * method.
+ *
+ * @return {boolean} Window is opening
+ */
+OO.ui.Window.prototype.isOpening = function () {
+       return this.manager.isOpening( this );
+};
+
+/**
+ * Check if the window is closing.
+ *
+ * This method is a wrapper around the window manager's {@link OO.ui.WindowManager#isClosing isClosing} method.
+ *
+ * @return {boolean} Window is closing
+ */
+OO.ui.Window.prototype.isClosing = function () {
+       return this.manager.isClosing( this );
+};
+
+/**
+ * Check if the window is opened.
+ *
+ * This method is a wrapper around the window manager's {@link OO.ui.WindowManager#isOpened isOpened} method.
+ *
+ * @return {boolean} Window is opened
+ */
+OO.ui.Window.prototype.isOpened = function () {
+       return this.manager.isOpened( this );
+};
+
+/**
+ * Get the window manager.
+ *
+ * All windows must be attached to a window manager, which is used to open
+ * and close the window and control its presentation.
+ *
+ * @return {OO.ui.WindowManager} Manager of window
+ */
+OO.ui.Window.prototype.getManager = function () {
+       return this.manager;
+};
+
+/**
+ * Get the symbolic name of the window size (e.g., `small` or `medium`).
+ *
+ * @return {string} Symbolic name of the size: `small`, `medium`, `large`, `larger`, `full`
+ */
+OO.ui.Window.prototype.getSize = function () {
+       var viewport = OO.ui.Element.static.getDimensions( this.getElementWindow() ),
+               sizes = this.manager.constructor.static.sizes,
+               size = this.size;
+
+       if ( !sizes[ size ] ) {
+               size = this.manager.constructor.static.defaultSize;
+       }
+       if ( size !== 'full' && viewport.rect.right - viewport.rect.left < sizes[ size ].width ) {
+               size = 'full';
+       }
+
+       return size;
+};
+
+/**
+ * Get the size properties associated with the current window size
+ *
+ * @return {Object} Size properties
+ */
+OO.ui.Window.prototype.getSizeProperties = function () {
+       return this.manager.constructor.static.sizes[ this.getSize() ];
+};
+
+/**
+ * Disable transitions on window's frame for the duration of the callback function, then enable them
+ * back.
+ *
+ * @private
+ * @param {Function} callback Function to call while transitions are disabled
+ */
+OO.ui.Window.prototype.withoutSizeTransitions = function ( callback ) {
+       // Temporarily resize the frame so getBodyHeight() can use scrollHeight measurements.
+       // Disable transitions first, otherwise we'll get values from when the window was animating.
+       var oldTransition,
+               styleObj = this.$frame[ 0 ].style;
+       oldTransition = styleObj.transition || styleObj.OTransition || styleObj.MsTransition ||
+               styleObj.MozTransition || styleObj.WebkitTransition;
+       styleObj.transition = styleObj.OTransition = styleObj.MsTransition =
+               styleObj.MozTransition = styleObj.WebkitTransition = 'none';
+       callback();
+       // Force reflow to make sure the style changes done inside callback really are not transitioned
+       this.$frame.height();
+       styleObj.transition = styleObj.OTransition = styleObj.MsTransition =
+               styleObj.MozTransition = styleObj.WebkitTransition = oldTransition;
+};
+
+/**
+ * Get the height of the full window contents (i.e., the window head, body and foot together).
+ *
+ * What consistitutes the head, body, and foot varies depending on the window type.
+ * A {@link OO.ui.MessageDialog message dialog} displays a title and message in its body,
+ * and any actions in the foot. A {@link OO.ui.ProcessDialog process dialog} displays a title
+ * and special actions in the head, and dialog content in the body.
+ *
+ * To get just the height of the dialog body, use the #getBodyHeight method.
+ *
+ * @return {number} The height of the window contents (the dialog head, body and foot) in pixels
+ */
+OO.ui.Window.prototype.getContentHeight = function () {
+       var bodyHeight,
+               win = this,
+               bodyStyleObj = this.$body[ 0 ].style,
+               frameStyleObj = this.$frame[ 0 ].style;
+
+       // Temporarily resize the frame so getBodyHeight() can use scrollHeight measurements.
+       // Disable transitions first, otherwise we'll get values from when the window was animating.
+       this.withoutSizeTransitions( function () {
+               var oldHeight = frameStyleObj.height,
+                       oldPosition = bodyStyleObj.position;
+               frameStyleObj.height = '1px';
+               // Force body to resize to new width
+               bodyStyleObj.position = 'relative';
+               bodyHeight = win.getBodyHeight();
+               frameStyleObj.height = oldHeight;
+               bodyStyleObj.position = oldPosition;
+       } );
+
+       return (
+               // Add buffer for border
+               ( this.$frame.outerHeight() - this.$frame.innerHeight() ) +
+               // Use combined heights of children
+               ( this.$head.outerHeight( true ) + bodyHeight + this.$foot.outerHeight( true ) )
+       );
+};
+
+/**
+ * Get the height of the window body.
+ *
+ * To get the height of the full window contents (the window body, head, and foot together),
+ * use #getContentHeight.
+ *
+ * When this function is called, the window will temporarily have been resized
+ * to height=1px, so .scrollHeight measurements can be taken accurately.
+ *
+ * @return {number} Height of the window body in pixels
+ */
+OO.ui.Window.prototype.getBodyHeight = function () {
+       return this.$body[ 0 ].scrollHeight;
+};
+
+/**
+ * Get the directionality of the frame (right-to-left or left-to-right).
+ *
+ * @return {string} Directionality: `'ltr'` or `'rtl'`
+ */
+OO.ui.Window.prototype.getDir = function () {
+       return OO.ui.Element.static.getDir( this.$content ) || 'ltr';
+};
+
+/**
+ * Get the 'setup' process.
+ *
+ * The setup process is used to set up a window for use in a particular context,
+ * based on the `data` argument. This method is called during the opening phase of the window’s
+ * lifecycle.
+ *
+ * Override this method to add additional steps to the ‘setup’ process the parent method provides
+ * using the {@link OO.ui.Process#first first} and {@link OO.ui.Process#next next} methods
+ * of OO.ui.Process.
+ *
+ * To add window content that persists between openings, you may wish to use the #initialize method
+ * instead.
+ *
+ * @param {Object} [data] Window opening data
+ * @return {OO.ui.Process} Setup process
+ */
+OO.ui.Window.prototype.getSetupProcess = function () {
+       return new OO.ui.Process();
+};
+
+/**
+ * Get the ‘ready’ process.
+ *
+ * The ready process is used to ready a window for use in a particular
+ * context, based on the `data` argument. This method is called during the opening phase of
+ * the window’s lifecycle, after the window has been {@link #getSetupProcess setup}.
+ *
+ * Override this method to add additional steps to the ‘ready’ process the parent method
+ * provides using the {@link OO.ui.Process#first first} and {@link OO.ui.Process#next next}
+ * methods of OO.ui.Process.
+ *
+ * @param {Object} [data] Window opening data
+ * @return {OO.ui.Process} Ready process
+ */
+OO.ui.Window.prototype.getReadyProcess = function () {
+       return new OO.ui.Process();
+};
+
+/**
+ * Get the 'hold' process.
+ *
+ * The hold proccess is used to keep a window from being used in a particular context,
+ * based on the `data` argument. This method is called during the closing phase of the window’s
+ * lifecycle.
+ *
+ * Override this method to add additional steps to the 'hold' process the parent method provides
+ * using the {@link OO.ui.Process#first first} and {@link OO.ui.Process#next next} methods
+ * of OO.ui.Process.
+ *
+ * @param {Object} [data] Window closing data
+ * @return {OO.ui.Process} Hold process
+ */
+OO.ui.Window.prototype.getHoldProcess = function () {
+       return new OO.ui.Process();
+};
+
+/**
+ * Get the ‘teardown’ process.
+ *
+ * The teardown process is used to teardown a window after use. During teardown,
+ * user interactions within the window are conveyed and the window is closed, based on the `data`
+ * argument. This method is called during the closing phase of the window’s lifecycle.
+ *
+ * Override this method to add additional steps to the ‘teardown’ process the parent method provides
+ * using the {@link OO.ui.Process#first first} and {@link OO.ui.Process#next next} methods
+ * of OO.ui.Process.
+ *
+ * @param {Object} [data] Window closing data
+ * @return {OO.ui.Process} Teardown process
+ */
+OO.ui.Window.prototype.getTeardownProcess = function () {
+       return new OO.ui.Process();
+};
+
+/**
+ * Set the window manager.
+ *
+ * This will cause the window to initialize. Calling it more than once will cause an error.
+ *
+ * @param {OO.ui.WindowManager} manager Manager for this window
+ * @throws {Error} An error is thrown if the method is called more than once
+ * @chainable
+ */
+OO.ui.Window.prototype.setManager = function ( manager ) {
+       if ( this.manager ) {
+               throw new Error( 'Cannot set window manager, window already has a manager' );
+       }
+
+       this.manager = manager;
+       this.initialize();
+
+       return this;
+};
+
+/**
+ * Set the window size by symbolic name (e.g., 'small' or 'medium')
+ *
+ * @param {string} size Symbolic name of size: `small`, `medium`, `large`, `larger` or
+ *  `full`
+ * @chainable
+ */
+OO.ui.Window.prototype.setSize = function ( size ) {
+       this.size = size;
+       this.updateSize();
+       return this;
+};
+
+/**
+ * Update the window size.
+ *
+ * @throws {Error} An error is thrown if the window is not attached to a window manager
+ * @chainable
+ */
+OO.ui.Window.prototype.updateSize = function () {
+       if ( !this.manager ) {
+               throw new Error( 'Cannot update window size, must be attached to a manager' );
+       }
+
+       this.manager.updateWindowSize( this );
+
+       return this;
+};
+
+/**
+ * Set window dimensions. This method is called by the {@link OO.ui.WindowManager window manager}
+ * when the window is opening. In general, setDimensions should not be called directly.
+ *
+ * To set the size of the window, use the #setSize method.
+ *
+ * @param {Object} dim CSS dimension properties
+ * @param {string|number} [dim.width] Width
+ * @param {string|number} [dim.minWidth] Minimum width
+ * @param {string|number} [dim.maxWidth] Maximum width
+ * @param {string|number} [dim.width] Height, omit to set based on height of contents
+ * @param {string|number} [dim.minWidth] Minimum height
+ * @param {string|number} [dim.maxWidth] Maximum height
+ * @chainable
+ */
+OO.ui.Window.prototype.setDimensions = function ( dim ) {
+       var height,
+               win = this,
+               styleObj = this.$frame[ 0 ].style;
+
+       // Calculate the height we need to set using the correct width
+       if ( dim.height === undefined ) {
+               this.withoutSizeTransitions( function () {
+                       var oldWidth = styleObj.width;
+                       win.$frame.css( 'width', dim.width || '' );
+                       height = win.getContentHeight();
+                       styleObj.width = oldWidth;
+               } );
+       } else {
+               height = dim.height;
+       }
+
+       this.$frame.css( {
+               width: dim.width || '',
+               minWidth: dim.minWidth || '',
+               maxWidth: dim.maxWidth || '',
+               height: height || '',
+               minHeight: dim.minHeight || '',
+               maxHeight: dim.maxHeight || ''
+       } );
+
+       return this;
+};
+
+/**
+ * Initialize window contents.
+ *
+ * Before the window is opened for the first time, #initialize is called so that content that
+ * persists between openings can be added to the window.
+ *
+ * To set up a window with new content each time the window opens, use #getSetupProcess.
+ *
+ * @throws {Error} An error is thrown if the window is not attached to a window manager
+ * @chainable
+ */
+OO.ui.Window.prototype.initialize = function () {
+       if ( !this.manager ) {
+               throw new Error( 'Cannot initialize window, must be attached to a manager' );
+       }
+
+       // Properties
+       this.$head = $( '<div>' );
+       this.$body = $( '<div>' );
+       this.$foot = $( '<div>' );
+       this.$document = $( this.getElementDocument() );
+
+       // Events
+       this.$element.on( 'mousedown', this.onMouseDown.bind( this ) );
+
+       // Initialization
+       this.$head.addClass( 'oo-ui-window-head' );
+       this.$body.addClass( 'oo-ui-window-body' );
+       this.$foot.addClass( 'oo-ui-window-foot' );
+       this.$content.append( this.$head, this.$body, this.$foot );
+
+       return this;
+};
+
+/**
+ * Called when someone tries to focus the hidden element at the end of the dialog.
+ * Sends focus back to the start of the dialog.
+ *
+ * @param {jQuery.Event} event Focus event
+ */
+OO.ui.Window.prototype.onFocusTrapFocused = function ( event ) {
+       if ( this.$focusTrapBefore.is( event.target ) ) {
+               OO.ui.findFocusable( this.$content, true ).focus();
+       } else {
+               // this.$content is the part of the focus cycle, and is the first focusable element
+               this.$content.focus();
+       }
+};
+
+/**
+ * Open the window.
+ *
+ * This method is a wrapper around a call to the window manager’s {@link OO.ui.WindowManager#openWindow openWindow}
+ * method, which returns a promise resolved when the window is done opening.
+ *
+ * To customize the window each time it opens, use #getSetupProcess or #getReadyProcess.
+ *
+ * @param {Object} [data] Window opening data
+ * @return {jQuery.Promise} Promise resolved with a value when the window is opened, or rejected
+ *  if the window fails to open. When the promise is resolved successfully, the first argument of the
+ *  value is a new promise, which is resolved when the window begins closing.
+ * @throws {Error} An error is thrown if the window is not attached to a window manager
+ */
+OO.ui.Window.prototype.open = function ( data ) {
+       if ( !this.manager ) {
+               throw new Error( 'Cannot open window, must be attached to a manager' );
+       }
+
+       return this.manager.openWindow( this, data );
+};
+
+/**
+ * Close the window.
+ *
+ * This method is a wrapper around a call to the window
+ * manager’s {@link OO.ui.WindowManager#closeWindow closeWindow} method,
+ * which returns a closing promise resolved when the window is done closing.
+ *
+ * The window's #getHoldProcess and #getTeardownProcess methods are called during the closing
+ * phase of the window’s lifecycle and can be used to specify closing behavior each time
+ * the window closes.
+ *
+ * @param {Object} [data] Window closing data
+ * @return {jQuery.Promise} Promise resolved when window is closed
+ * @throws {Error} An error is thrown if the window is not attached to a window manager
+ */
+OO.ui.Window.prototype.close = function ( data ) {
+       if ( !this.manager ) {
+               throw new Error( 'Cannot close window, must be attached to a manager' );
+       }
+
+       return this.manager.closeWindow( this, data );
+};
+
+/**
+ * Setup window.
+ *
+ * This is called by OO.ui.WindowManager during window opening, and should not be called directly
+ * by other systems.
+ *
+ * @param {Object} [data] Window opening data
+ * @return {jQuery.Promise} Promise resolved when window is setup
+ */
+OO.ui.Window.prototype.setup = function ( data ) {
+       var win = this;
+
+       this.toggle( true );
+
+       this.focusTrapHandler = OO.ui.bind( this.onFocusTrapFocused, this );
+       this.$focusTraps.on( 'focus', this.focusTrapHandler );
+
+       return this.getSetupProcess( data ).execute().then( function () {
+               // Force redraw by asking the browser to measure the elements' widths
+               win.$element.addClass( 'oo-ui-window-active oo-ui-window-setup' ).width();
+               win.$content.addClass( 'oo-ui-window-content-setup' ).width();
+       } );
+};
+
+/**
+ * Ready window.
+ *
+ * This is called by OO.ui.WindowManager during window opening, and should not be called directly
+ * by other systems.
+ *
+ * @param {Object} [data] Window opening data
+ * @return {jQuery.Promise} Promise resolved when window is ready
+ */
+OO.ui.Window.prototype.ready = function ( data ) {
+       var win = this;
+
+       this.$content.focus();
+       return this.getReadyProcess( data ).execute().then( function () {
+               // Force redraw by asking the browser to measure the elements' widths
+               win.$element.addClass( 'oo-ui-window-ready' ).width();
+               win.$content.addClass( 'oo-ui-window-content-ready' ).width();
+       } );
+};
+
+/**
+ * Hold window.
+ *
+ * This is called by OO.ui.WindowManager during window closing, and should not be called directly
+ * by other systems.
+ *
+ * @param {Object} [data] Window closing data
+ * @return {jQuery.Promise} Promise resolved when window is held
+ */
+OO.ui.Window.prototype.hold = function ( data ) {
+       var win = this;
+
+       return this.getHoldProcess( data ).execute().then( function () {
+               // Get the focused element within the window's content
+               var $focus = win.$content.find( OO.ui.Element.static.getDocument( win.$content ).activeElement );
+
+               // Blur the focused element
+               if ( $focus.length ) {
+                       $focus[ 0 ].blur();
+               }
+
+               // Force redraw by asking the browser to measure the elements' widths
+               win.$element.removeClass( 'oo-ui-window-ready' ).width();
+               win.$content.removeClass( 'oo-ui-window-content-ready' ).width();
+       } );
+};
+
+/**
+ * Teardown window.
+ *
+ * This is called by OO.ui.WindowManager during window closing, and should not be called directly
+ * by other systems.
+ *
+ * @param {Object} [data] Window closing data
+ * @return {jQuery.Promise} Promise resolved when window is torn down
+ */
+OO.ui.Window.prototype.teardown = function ( data ) {
+       var win = this;
+
+       return this.getTeardownProcess( data ).execute().then( function () {
+               // Force redraw by asking the browser to measure the elements' widths
+               win.$element.removeClass( 'oo-ui-window-active oo-ui-window-setup' ).width();
+               win.$content.removeClass( 'oo-ui-window-content-setup' ).width();
+               win.$focusTraps.off( 'focus', win.focusTrapHandler );
+               win.toggle( false );
+       } );
+};
+
+/**
+ * The Dialog class serves as the base class for the other types of dialogs.
+ * Unless extended to include controls, the rendered dialog box is a simple window
+ * that users can close by hitting the ‘Esc’ key. Dialog windows are used with OO.ui.WindowManager,
+ * which opens, closes, and controls the presentation of the window. See the
+ * [OOjs UI documentation on MediaWiki] [1] for more information.
+ *
+ *     @example
+ *     // A simple dialog window.
+ *     function MyDialog( config ) {
+ *         MyDialog.parent.call( this, config );
+ *     }
+ *     OO.inheritClass( MyDialog, OO.ui.Dialog );
+ *     MyDialog.prototype.initialize = function () {
+ *         MyDialog.parent.prototype.initialize.call( this );
+ *         this.content = new OO.ui.PanelLayout( { padded: true, expanded: false } );
+ *         this.content.$element.append( '<p>A simple dialog window. Press \'Esc\' to close.</p>' );
+ *         this.$body.append( this.content.$element );
+ *     };
+ *     MyDialog.prototype.getBodyHeight = function () {
+ *         return this.content.$element.outerHeight( true );
+ *     };
+ *     var myDialog = new MyDialog( {
+ *         size: 'medium'
+ *     } );
+ *     // Create and append a window manager, which opens and closes the window.
+ *     var windowManager = new OO.ui.WindowManager();
+ *     $( 'body' ).append( windowManager.$element );
+ *     windowManager.addWindows( [ myDialog ] );
+ *     // Open the window!
+ *     windowManager.openWindow( myDialog );
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Dialogs
+ *
+ * @abstract
+ * @class
+ * @extends OO.ui.Window
+ * @mixins OO.ui.mixin.PendingElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.Dialog = function OoUiDialog( config ) {
+       // Parent constructor
+       OO.ui.Dialog.parent.call( this, config );
+
+       // Mixin constructors
+       OO.ui.mixin.PendingElement.call( this );
+
+       // Properties
+       this.actions = new OO.ui.ActionSet();
+       this.attachedActions = [];
+       this.currentAction = null;
+       this.onDialogKeyDownHandler = this.onDialogKeyDown.bind( this );
+
+       // Events
+       this.actions.connect( this, {
+               click: 'onActionClick',
+               resize: 'onActionResize',
+               change: 'onActionsChange'
+       } );
+
+       // Initialization
+       this.$element
+               .addClass( 'oo-ui-dialog' )
+               .attr( 'role', 'dialog' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.Dialog, OO.ui.Window );
+OO.mixinClass( OO.ui.Dialog, OO.ui.mixin.PendingElement );
+
+/* Static Properties */
+
+/**
+ * Symbolic name of dialog.
+ *
+ * The dialog class must have a symbolic name in order to be registered with OO.Factory.
+ * Please see the [OOjs UI documentation on MediaWiki] [3] for more information.
+ *
+ * [3]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Window_managers
+ *
+ * @abstract
+ * @static
+ * @inheritable
+ * @property {string}
+ */
+OO.ui.Dialog.static.name = '';
+
+/**
+ * The dialog title.
+ *
+ * The title can be specified as a plaintext string, a {@link OO.ui.mixin.LabelElement Label} node, or a function
+ * that will produce a Label node or string. The title can also be specified with data passed to the
+ * constructor (see #getSetupProcess). In this case, the static value will be overridden.
+ *
+ * @abstract
+ * @static
+ * @inheritable
+ * @property {jQuery|string|Function}
+ */
+OO.ui.Dialog.static.title = '';
+
+/**
+ * An array of configured {@link OO.ui.ActionWidget action widgets}.
+ *
+ * Actions can also be specified with data passed to the constructor (see #getSetupProcess). In this case, the static
+ * value will be overridden.
+ *
+ * [2]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Process_Dialogs#Action_sets
+ *
+ * @static
+ * @inheritable
+ * @property {Object[]}
+ */
+OO.ui.Dialog.static.actions = [];
+
+/**
+ * Close the dialog when the 'Esc' key is pressed.
+ *
+ * @static
+ * @abstract
+ * @inheritable
+ * @property {boolean}
+ */
+OO.ui.Dialog.static.escapable = true;
+
+/* Methods */
+
+/**
+ * Handle frame document key down events.
+ *
+ * @private
+ * @param {jQuery.Event} e Key down event
+ */
+OO.ui.Dialog.prototype.onDialogKeyDown = function ( e ) {
+       if ( e.which === OO.ui.Keys.ESCAPE ) {
+               this.executeAction( '' );
+               e.preventDefault();
+               e.stopPropagation();
+       }
+};
+
+/**
+ * Handle action resized events.
+ *
+ * @private
+ * @param {OO.ui.ActionWidget} action Action that was resized
+ */
+OO.ui.Dialog.prototype.onActionResize = function () {
+       // Override in subclass
+};
+
+/**
+ * Handle action click events.
+ *
+ * @private
+ * @param {OO.ui.ActionWidget} action Action that was clicked
+ */
+OO.ui.Dialog.prototype.onActionClick = function ( action ) {
+       if ( !this.isPending() ) {
+               this.executeAction( action.getAction() );
+       }
+};
+
+/**
+ * Handle actions change event.
+ *
+ * @private
+ */
+OO.ui.Dialog.prototype.onActionsChange = function () {
+       this.detachActions();
+       if ( !this.isClosing() ) {
+               this.attachActions();
+       }
+};
+
+/**
+ * Get the set of actions used by the dialog.
+ *
+ * @return {OO.ui.ActionSet}
+ */
+OO.ui.Dialog.prototype.getActions = function () {
+       return this.actions;
+};
+
+/**
+ * Get a process for taking action.
+ *
+ * When you override this method, you can create a new OO.ui.Process and return it, or add additional
+ * accept steps to the process the parent method provides using the {@link OO.ui.Process#first 'first'}
+ * and {@link OO.ui.Process#next 'next'} methods of OO.ui.Process.
+ *
+ * @param {string} [action] Symbolic name of action
+ * @return {OO.ui.Process} Action process
+ */
+OO.ui.Dialog.prototype.getActionProcess = function ( action ) {
+       return new OO.ui.Process()
+               .next( function () {
+                       if ( !action ) {
+                               // An empty action always closes the dialog without data, which should always be
+                               // safe and make no changes
+                               this.close();
+                       }
+               }, this );
+};
+
+/**
+ * @inheritdoc
+ *
+ * @param {Object} [data] Dialog opening data
+ * @param {jQuery|string|Function|null} [data.title] Dialog title, omit to use
+ *  the {@link #static-title static title}
+ * @param {Object[]} [data.actions] List of configuration options for each
+ *   {@link OO.ui.ActionWidget action widget}, omit to use {@link #static-actions static actions}.
+ */
+OO.ui.Dialog.prototype.getSetupProcess = function ( data ) {
+       data = data || {};
+
+       // Parent method
+       return OO.ui.Dialog.parent.prototype.getSetupProcess.call( this, data )
+               .next( function () {
+                       var config = this.constructor.static,
+                               actions = data.actions !== undefined ? data.actions : config.actions;
+
+                       this.title.setLabel(
+                               data.title !== undefined ? data.title : this.constructor.static.title
+                       );
+                       this.actions.add( this.getActionWidgets( actions ) );
+
+                       if ( this.constructor.static.escapable ) {
+                               this.$element.on( 'keydown', this.onDialogKeyDownHandler );
+                       }
+               }, this );
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.Dialog.prototype.getTeardownProcess = function ( data ) {
+       // Parent method
+       return OO.ui.Dialog.parent.prototype.getTeardownProcess.call( this, data )
+               .first( function () {
+                       if ( this.constructor.static.escapable ) {
+                               this.$element.off( 'keydown', this.onDialogKeyDownHandler );
+                       }
+
+                       this.actions.clear();
+                       this.currentAction = null;
+               }, this );
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.Dialog.prototype.initialize = function () {
+       var titleId;
+
+       // Parent method
+       OO.ui.Dialog.parent.prototype.initialize.call( this );
+
+       titleId = OO.ui.generateElementId();
+
+       // Properties
+       this.title = new OO.ui.LabelWidget( {
+               id: titleId
+       } );
+
+       // Initialization
+       this.$content.addClass( 'oo-ui-dialog-content' );
+       this.$element.attr( 'aria-labelledby', titleId );
+       this.setPendingElement( this.$head );
+};
+
+/**
+ * Get action widgets from a list of configs
+ *
+ * @param {Object[]} actions Action widget configs
+ * @return {OO.ui.ActionWidget[]} Action widgets
+ */
+OO.ui.Dialog.prototype.getActionWidgets = function ( actions ) {
+       var i, len, widgets = [];
+       for ( i = 0, len = actions.length; i < len; i++ ) {
+               widgets.push(
+                       new OO.ui.ActionWidget( actions[ i ] )
+               );
+       }
+       return widgets;
+};
+
+/**
+ * Attach action actions.
+ *
+ * @protected
+ */
+OO.ui.Dialog.prototype.attachActions = function () {
+       // Remember the list of potentially attached actions
+       this.attachedActions = this.actions.get();
+};
+
+/**
+ * Detach action actions.
+ *
+ * @protected
+ * @chainable
+ */
+OO.ui.Dialog.prototype.detachActions = function () {
+       var i, len;
+
+       // Detach all actions that may have been previously attached
+       for ( i = 0, len = this.attachedActions.length; i < len; i++ ) {
+               this.attachedActions[ i ].$element.detach();
+       }
+       this.attachedActions = [];
+};
+
+/**
+ * Execute an action.
+ *
+ * @param {string} action Symbolic name of action to execute
+ * @return {jQuery.Promise} Promise resolved when action completes, rejected if it fails
+ */
+OO.ui.Dialog.prototype.executeAction = function ( action ) {
+       this.pushPending();
+       this.currentAction = action;
+       return this.getActionProcess( action ).execute()
+               .always( this.popPending.bind( this ) );
+};
+
+/**
+ * MessageDialogs display a confirmation or alert message. By default, the rendered dialog box
+ * consists of a header that contains the dialog title, a body with the message, and a footer that
+ * contains any {@link OO.ui.ActionWidget action widgets}. The MessageDialog class is the only type
+ * of {@link OO.ui.Dialog dialog} that is usually instantiated directly.
+ *
+ * There are two basic types of message dialogs, confirmation and alert:
+ *
+ * - **confirmation**: the dialog title describes what a progressive action will do and the message provides
+ *  more details about the consequences.
+ * - **alert**: the dialog title describes which event occurred and the message provides more information
+ *  about why the event occurred.
+ *
+ * The MessageDialog class specifies two actions: ‘accept’, the primary
+ * action (e.g., ‘ok’) and ‘reject,’ the safe action (e.g., ‘cancel’). Both will close the window,
+ * passing along the selected action.
+ *
+ * For more information and examples, please see the [OOjs UI documentation on MediaWiki][1].
+ *
+ *     @example
+ *     // Example: Creating and opening a message dialog window.
+ *     var messageDialog = new OO.ui.MessageDialog();
+ *
+ *     // Create and append a window manager.
+ *     var windowManager = new OO.ui.WindowManager();
+ *     $( 'body' ).append( windowManager.$element );
+ *     windowManager.addWindows( [ messageDialog ] );
+ *     // Open the window.
+ *     windowManager.openWindow( messageDialog, {
+ *         title: 'Basic message dialog',
+ *         message: 'This is the message'
+ *     } );
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Message_Dialogs
+ *
+ * @class
+ * @extends OO.ui.Dialog
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.MessageDialog = function OoUiMessageDialog( config ) {
+       // Parent constructor
+       OO.ui.MessageDialog.parent.call( this, config );
+
+       // Properties
+       this.verticalActionLayout = null;
+
+       // Initialization
+       this.$element.addClass( 'oo-ui-messageDialog' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.MessageDialog, OO.ui.Dialog );
+
+/* Static Properties */
+
+OO.ui.MessageDialog.static.name = 'message';
+
+OO.ui.MessageDialog.static.size = 'small';
+
+OO.ui.MessageDialog.static.verbose = false;
+
+/**
+ * Dialog title.
+ *
+ * The title of a confirmation dialog describes what a progressive action will do. The
+ * title of an alert dialog describes which event occurred.
+ *
+ * @static
+ * @inheritable
+ * @property {jQuery|string|Function|null}
+ */
+OO.ui.MessageDialog.static.title = null;
+
+/**
+ * The message displayed in the dialog body.
+ *
+ * A confirmation message describes the consequences of a progressive action. An alert
+ * message describes why an event occurred.
+ *
+ * @static
+ * @inheritable
+ * @property {jQuery|string|Function|null}
+ */
+OO.ui.MessageDialog.static.message = null;
+
+// Note that OO.ui.alert() and OO.ui.confirm() rely on these.
+OO.ui.MessageDialog.static.actions = [
+       { action: 'accept', label: OO.ui.deferMsg( 'ooui-dialog-message-accept' ), flags: 'primary' },
+       { action: 'reject', label: OO.ui.deferMsg( 'ooui-dialog-message-reject' ), flags: 'safe' }
+];
+
+/* Methods */
+
+/**
+ * @inheritdoc
+ */
+OO.ui.MessageDialog.prototype.setManager = function ( manager ) {
+       OO.ui.MessageDialog.parent.prototype.setManager.call( this, manager );
+
+       // Events
+       this.manager.connect( this, {
+               resize: 'onResize'
+       } );
+
+       return this;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.MessageDialog.prototype.onActionResize = function ( action ) {
+       this.fitActions();
+       return OO.ui.MessageDialog.parent.prototype.onActionResize.call( this, action );
+};
+
+/**
+ * Handle window resized events.
+ *
+ * @private
+ */
+OO.ui.MessageDialog.prototype.onResize = function () {
+       var dialog = this;
+       dialog.fitActions();
+       // Wait for CSS transition to finish and do it again :(
+       setTimeout( function () {
+               dialog.fitActions();
+       }, 300 );
+};
+
+/**
+ * Toggle action layout between vertical and horizontal.
+ *
+ * @private
+ * @param {boolean} [value] Layout actions vertically, omit to toggle
+ * @chainable
+ */
+OO.ui.MessageDialog.prototype.toggleVerticalActionLayout = function ( value ) {
+       value = value === undefined ? !this.verticalActionLayout : !!value;
+
+       if ( value !== this.verticalActionLayout ) {
+               this.verticalActionLayout = value;
+               this.$actions
+                       .toggleClass( 'oo-ui-messageDialog-actions-vertical', value )
+                       .toggleClass( 'oo-ui-messageDialog-actions-horizontal', !value );
+       }
+
+       return this;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.MessageDialog.prototype.getActionProcess = function ( action ) {
+       if ( action ) {
+               return new OO.ui.Process( function () {
+                       this.close( { action: action } );
+               }, this );
+       }
+       return OO.ui.MessageDialog.parent.prototype.getActionProcess.call( this, action );
+};
+
+/**
+ * @inheritdoc
+ *
+ * @param {Object} [data] Dialog opening data
+ * @param {jQuery|string|Function|null} [data.title] Description of the action being confirmed
+ * @param {jQuery|string|Function|null} [data.message] Description of the action's consequence
+ * @param {boolean} [data.verbose] Message is verbose and should be styled as a long message
+ * @param {Object[]} [data.actions] List of OO.ui.ActionOptionWidget configuration options for each
+ *   action item
+ */
+OO.ui.MessageDialog.prototype.getSetupProcess = function ( data ) {
+       data = data || {};
+
+       // Parent method
+       return OO.ui.MessageDialog.parent.prototype.getSetupProcess.call( this, data )
+               .next( function () {
+                       this.title.setLabel(
+                               data.title !== undefined ? data.title : this.constructor.static.title
+                       );
+                       this.message.setLabel(
+                               data.message !== undefined ? data.message : this.constructor.static.message
+                       );
+                       this.message.$element.toggleClass(
+                               'oo-ui-messageDialog-message-verbose',
+                               data.verbose !== undefined ? data.verbose : this.constructor.static.verbose
+                       );
+               }, this );
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.MessageDialog.prototype.getReadyProcess = function ( data ) {
+       data = data || {};
+
+       // Parent method
+       return OO.ui.MessageDialog.parent.prototype.getReadyProcess.call( this, data )
+               .next( function () {
+                       // Focus the primary action button
+                       var actions = this.actions.get();
+                       actions = actions.filter( function ( action ) {
+                               return action.getFlags().indexOf( 'primary' ) > -1;
+                       } );
+                       if ( actions.length > 0 ) {
+                               actions[ 0 ].$button.focus();
+                       }
+               }, this );
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.MessageDialog.prototype.getBodyHeight = function () {
+       var bodyHeight, oldOverflow,
+               $scrollable = this.container.$element;
+
+       oldOverflow = $scrollable[ 0 ].style.overflow;
+       $scrollable[ 0 ].style.overflow = 'hidden';
+
+       OO.ui.Element.static.reconsiderScrollbars( $scrollable[ 0 ] );
+
+       bodyHeight = this.text.$element.outerHeight( true );
+       $scrollable[ 0 ].style.overflow = oldOverflow;
+
+       return bodyHeight;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.MessageDialog.prototype.setDimensions = function ( dim ) {
+       var $scrollable = this.container.$element;
+       OO.ui.MessageDialog.parent.prototype.setDimensions.call( this, dim );
+
+       // Twiddle the overflow property, otherwise an unnecessary scrollbar will be produced.
+       // Need to do it after transition completes (250ms), add 50ms just in case.
+       setTimeout( function () {
+               var oldOverflow = $scrollable[ 0 ].style.overflow;
+               $scrollable[ 0 ].style.overflow = 'hidden';
+
+               OO.ui.Element.static.reconsiderScrollbars( $scrollable[ 0 ] );
+
+               $scrollable[ 0 ].style.overflow = oldOverflow;
+       }, 300 );
+
+       return this;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.MessageDialog.prototype.initialize = function () {
+       // Parent method
+       OO.ui.MessageDialog.parent.prototype.initialize.call( this );
+
+       // Properties
+       this.$actions = $( '<div>' );
+       this.container = new OO.ui.PanelLayout( {
+               scrollable: true, classes: [ 'oo-ui-messageDialog-container' ]
+       } );
+       this.text = new OO.ui.PanelLayout( {
+               padded: true, expanded: false, classes: [ 'oo-ui-messageDialog-text' ]
+       } );
+       this.message = new OO.ui.LabelWidget( {
+               classes: [ 'oo-ui-messageDialog-message' ]
+       } );
+
+       // Initialization
+       this.title.$element.addClass( 'oo-ui-messageDialog-title' );
+       this.$content.addClass( 'oo-ui-messageDialog-content' );
+       this.container.$element.append( this.text.$element );
+       this.text.$element.append( this.title.$element, this.message.$element );
+       this.$body.append( this.container.$element );
+       this.$actions.addClass( 'oo-ui-messageDialog-actions' );
+       this.$foot.append( this.$actions );
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.MessageDialog.prototype.attachActions = function () {
+       var i, len, other, special, others;
+
+       // Parent method
+       OO.ui.MessageDialog.parent.prototype.attachActions.call( this );
+
+       special = this.actions.getSpecial();
+       others = this.actions.getOthers();
+
+       if ( special.safe ) {
+               this.$actions.append( special.safe.$element );
+               special.safe.toggleFramed( false );
+       }
+       if ( others.length ) {
+               for ( i = 0, len = others.length; i < len; i++ ) {
+                       other = others[ i ];
+                       this.$actions.append( other.$element );
+                       other.toggleFramed( false );
+               }
+       }
+       if ( special.primary ) {
+               this.$actions.append( special.primary.$element );
+               special.primary.toggleFramed( false );
+       }
+
+       if ( !this.isOpening() ) {
+               // If the dialog is currently opening, this will be called automatically soon.
+               // This also calls #fitActions.
+               this.updateSize();
+       }
+};
+
+/**
+ * Fit action actions into columns or rows.
+ *
+ * Columns will be used if all labels can fit without overflow, otherwise rows will be used.
+ *
+ * @private
+ */
+OO.ui.MessageDialog.prototype.fitActions = function () {
+       var i, len, action,
+               previous = this.verticalActionLayout,
+               actions = this.actions.get();
+
+       // Detect clipping
+       this.toggleVerticalActionLayout( false );
+       for ( i = 0, len = actions.length; i < len; i++ ) {
+               action = actions[ i ];
+               if ( action.$element.innerWidth() < action.$label.outerWidth( true ) ) {
+                       this.toggleVerticalActionLayout( true );
+                       break;
+               }
+       }
+
+       // Move the body out of the way of the foot
+       this.$body.css( 'bottom', this.$foot.outerHeight( true ) );
+
+       if ( this.verticalActionLayout !== previous ) {
+               // We changed the layout, window height might need to be updated.
+               this.updateSize();
+       }
+};
+
+/**
+ * ProcessDialog windows encapsulate a {@link OO.ui.Process process} and all of the code necessary
+ * to complete it. If the process terminates with an error, a customizable {@link OO.ui.Error error
+ * interface} alerts users to the trouble, permitting the user to dismiss the error and try again when
+ * relevant. The ProcessDialog class is always extended and customized with the actions and content
+ * required for each process.
+ *
+ * The process dialog box consists of a header that visually represents the ‘working’ state of long
+ * processes with an animation. The header contains the dialog title as well as
+ * two {@link OO.ui.ActionWidget action widgets}:  a ‘safe’ action on the left (e.g., ‘Cancel’) and
+ * a ‘primary’ action on the right (e.g., ‘Done’).
+ *
+ * Like other windows, the process dialog is managed by a {@link OO.ui.WindowManager window manager}.
+ * Please see the [OOjs UI documentation on MediaWiki][1] for more information and examples.
+ *
+ *     @example
+ *     // Example: Creating and opening a process dialog window.
+ *     function MyProcessDialog( config ) {
+ *         MyProcessDialog.parent.call( this, config );
+ *     }
+ *     OO.inheritClass( MyProcessDialog, OO.ui.ProcessDialog );
+ *
+ *     MyProcessDialog.static.title = 'Process dialog';
+ *     MyProcessDialog.static.actions = [
+ *         { action: 'save', label: 'Done', flags: 'primary' },
+ *         { label: 'Cancel', flags: 'safe' }
+ *     ];
+ *
+ *     MyProcessDialog.prototype.initialize = function () {
+ *         MyProcessDialog.parent.prototype.initialize.apply( this, arguments );
+ *         this.content = new OO.ui.PanelLayout( { padded: true, expanded: false } );
+ *         this.content.$element.append( '<p>This is a process dialog window. The header contains the title and two buttons: \'Cancel\' (a safe action) on the left and \'Done\' (a primary action)  on the right.</p>' );
+ *         this.$body.append( this.content.$element );
+ *     };
+ *     MyProcessDialog.prototype.getActionProcess = function ( action ) {
+ *         var dialog = this;
+ *         if ( action ) {
+ *             return new OO.ui.Process( function () {
+ *                 dialog.close( { action: action } );
+ *             } );
+ *         }
+ *         return MyProcessDialog.parent.prototype.getActionProcess.call( this, action );
+ *     };
+ *
+ *     var windowManager = new OO.ui.WindowManager();
+ *     $( 'body' ).append( windowManager.$element );
+ *
+ *     var dialog = new MyProcessDialog();
+ *     windowManager.addWindows( [ dialog ] );
+ *     windowManager.openWindow( dialog );
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Process_Dialogs
+ *
+ * @abstract
+ * @class
+ * @extends OO.ui.Dialog
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.ProcessDialog = function OoUiProcessDialog( config ) {
+       // Parent constructor
+       OO.ui.ProcessDialog.parent.call( this, config );
+
+       // Properties
+       this.fitOnOpen = false;
+
+       // Initialization
+       this.$element.addClass( 'oo-ui-processDialog' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.ProcessDialog, OO.ui.Dialog );
+
+/* Methods */
+
+/**
+ * Handle dismiss button click events.
+ *
+ * Hides errors.
+ *
+ * @private
+ */
+OO.ui.ProcessDialog.prototype.onDismissErrorButtonClick = function () {
+       this.hideErrors();
+};
+
+/**
+ * Handle retry button click events.
+ *
+ * Hides errors and then tries again.
+ *
+ * @private
+ */
+OO.ui.ProcessDialog.prototype.onRetryButtonClick = function () {
+       this.hideErrors();
+       this.executeAction( this.currentAction );
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.ProcessDialog.prototype.onActionResize = function ( action ) {
+       if ( this.actions.isSpecial( action ) ) {
+               this.fitLabel();
+       }
+       return OO.ui.ProcessDialog.parent.prototype.onActionResize.call( this, action );
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.ProcessDialog.prototype.initialize = function () {
+       // Parent method
+       OO.ui.ProcessDialog.parent.prototype.initialize.call( this );
+
+       // Properties
+       this.$navigation = $( '<div>' );
+       this.$location = $( '<div>' );
+       this.$safeActions = $( '<div>' );
+       this.$primaryActions = $( '<div>' );
+       this.$otherActions = $( '<div>' );
+       this.dismissButton = new OO.ui.ButtonWidget( {
+               label: OO.ui.msg( 'ooui-dialog-process-dismiss' )
+       } );
+       this.retryButton = new OO.ui.ButtonWidget();
+       this.$errors = $( '<div>' );
+       this.$errorsTitle = $( '<div>' );
+
+       // Events
+       this.dismissButton.connect( this, { click: 'onDismissErrorButtonClick' } );
+       this.retryButton.connect( this, { click: 'onRetryButtonClick' } );
+
+       // Initialization
+       this.title.$element.addClass( 'oo-ui-processDialog-title' );
+       this.$location
+               .append( this.title.$element )
+               .addClass( 'oo-ui-processDialog-location' );
+       this.$safeActions.addClass( 'oo-ui-processDialog-actions-safe' );
+       this.$primaryActions.addClass( 'oo-ui-processDialog-actions-primary' );
+       this.$otherActions.addClass( 'oo-ui-processDialog-actions-other' );
+       this.$errorsTitle
+               .addClass( 'oo-ui-processDialog-errors-title' )
+               .text( OO.ui.msg( 'ooui-dialog-process-error' ) );
+       this.$errors
+               .addClass( 'oo-ui-processDialog-errors oo-ui-element-hidden' )
+               .append( this.$errorsTitle, this.dismissButton.$element, this.retryButton.$element );
+       this.$content
+               .addClass( 'oo-ui-processDialog-content' )
+               .append( this.$errors );
+       this.$navigation
+               .addClass( 'oo-ui-processDialog-navigation' )
+               .append( this.$safeActions, this.$location, this.$primaryActions );
+       this.$head.append( this.$navigation );
+       this.$foot.append( this.$otherActions );
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.ProcessDialog.prototype.getActionWidgets = function ( actions ) {
+       var i, len, widgets = [];
+       for ( i = 0, len = actions.length; i < len; i++ ) {
+               widgets.push(
+                       new OO.ui.ActionWidget( $.extend( { framed: true }, actions[ i ] ) )
+               );
+       }
+       return widgets;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.ProcessDialog.prototype.attachActions = function () {
+       var i, len, other, special, others;
+
+       // Parent method
+       OO.ui.ProcessDialog.parent.prototype.attachActions.call( this );
+
+       special = this.actions.getSpecial();
+       others = this.actions.getOthers();
+       if ( special.primary ) {
+               this.$primaryActions.append( special.primary.$element );
+       }
+       for ( i = 0, len = others.length; i < len; i++ ) {
+               other = others[ i ];
+               this.$otherActions.append( other.$element );
+       }
+       if ( special.safe ) {
+               this.$safeActions.append( special.safe.$element );
+       }
+
+       this.fitLabel();
+       this.$body.css( 'bottom', this.$foot.outerHeight( true ) );
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.ProcessDialog.prototype.executeAction = function ( action ) {
+       var process = this;
+       return OO.ui.ProcessDialog.parent.prototype.executeAction.call( this, action )
+               .fail( function ( errors ) {
+                       process.showErrors( errors || [] );
+               } );
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.ProcessDialog.prototype.setDimensions = function () {
+       // Parent method
+       OO.ui.ProcessDialog.parent.prototype.setDimensions.apply( this, arguments );
+
+       this.fitLabel();
+};
+
+/**
+ * Fit label between actions.
+ *
+ * @private
+ * @chainable
+ */
+OO.ui.ProcessDialog.prototype.fitLabel = function () {
+       var safeWidth, primaryWidth, biggerWidth, labelWidth, navigationWidth, leftWidth, rightWidth,
+               size = this.getSizeProperties();
+
+       if ( typeof size.width !== 'number' ) {
+               if ( this.isOpened() ) {
+                       navigationWidth = this.$head.width() - 20;
+               } else if ( this.isOpening() ) {
+                       if ( !this.fitOnOpen ) {
+                               // Size is relative and the dialog isn't open yet, so wait.
+                               this.manager.opening.done( this.fitLabel.bind( this ) );
+                               this.fitOnOpen = true;
+                       }
+                       return;
+               } else {
+                       return;
+               }
+       } else {
+               navigationWidth = size.width - 20;
+       }
+
+       safeWidth = this.$safeActions.is( ':visible' ) ? this.$safeActions.width() : 0;
+       primaryWidth = this.$primaryActions.is( ':visible' ) ? this.$primaryActions.width() : 0;
+       biggerWidth = Math.max( safeWidth, primaryWidth );
+
+       labelWidth = this.title.$element.width();
+
+       if ( 2 * biggerWidth + labelWidth < navigationWidth ) {
+               // We have enough space to center the label
+               leftWidth = rightWidth = biggerWidth;
+       } else {
+               // Let's hope we at least have enough space not to overlap, because we can't wrap the label…
+               if ( this.getDir() === 'ltr' ) {
+                       leftWidth = safeWidth;
+                       rightWidth = primaryWidth;
+               } else {
+                       leftWidth = primaryWidth;
+                       rightWidth = safeWidth;
+               }
+       }
+
+       this.$location.css( { paddingLeft: leftWidth, paddingRight: rightWidth } );
+
+       return this;
+};
+
+/**
+ * Handle errors that occurred during accept or reject processes.
+ *
+ * @private
+ * @param {OO.ui.Error[]|OO.ui.Error} errors Errors to be handled
+ */
+OO.ui.ProcessDialog.prototype.showErrors = function ( errors ) {
+       var i, len, $item, actions,
+               items = [],
+               abilities = {},
+               recoverable = true,
+               warning = false;
+
+       if ( errors instanceof OO.ui.Error ) {
+               errors = [ errors ];
+       }
+
+       for ( i = 0, len = errors.length; i < len; i++ ) {
+               if ( !errors[ i ].isRecoverable() ) {
+                       recoverable = false;
+               }
+               if ( errors[ i ].isWarning() ) {
+                       warning = true;
+               }
+               $item = $( '<div>' )
+                       .addClass( 'oo-ui-processDialog-error' )
+                       .append( errors[ i ].getMessage() );
+               items.push( $item[ 0 ] );
+       }
+       this.$errorItems = $( items );
+       if ( recoverable ) {
+               abilities[ this.currentAction ] = true;
+               // Copy the flags from the first matching action
+               actions = this.actions.get( { actions: this.currentAction } );
+               if ( actions.length ) {
+                       this.retryButton.clearFlags().setFlags( actions[ 0 ].getFlags() );
+               }
+       } else {
+               abilities[ this.currentAction ] = false;
+               this.actions.setAbilities( abilities );
+       }
+       if ( warning ) {
+               this.retryButton.setLabel( OO.ui.msg( 'ooui-dialog-process-continue' ) );
+       } else {
+               this.retryButton.setLabel( OO.ui.msg( 'ooui-dialog-process-retry' ) );
+       }
+       this.retryButton.toggle( recoverable );
+       this.$errorsTitle.after( this.$errorItems );
+       this.$errors.removeClass( 'oo-ui-element-hidden' ).scrollTop( 0 );
+};
+
+/**
+ * Hide errors.
+ *
+ * @private
+ */
+OO.ui.ProcessDialog.prototype.hideErrors = function () {
+       this.$errors.addClass( 'oo-ui-element-hidden' );
+       if ( this.$errorItems ) {
+               this.$errorItems.remove();
+               this.$errorItems = null;
+       }
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.ProcessDialog.prototype.getTeardownProcess = function ( data ) {
+       // Parent method
+       return OO.ui.ProcessDialog.parent.prototype.getTeardownProcess.call( this, data )
+               .first( function () {
+                       // Make sure to hide errors
+                       this.hideErrors();
+                       this.fitOnOpen = false;
+               }, this );
+};
+
+/**
+ * @class OO.ui
+ */
+
+/**
+ * Lazy-initialize and return a global OO.ui.WindowManager instance, used by OO.ui.alert and
+ * OO.ui.confirm.
+ *
+ * @private
+ * @return {OO.ui.WindowManager}
+ */
+OO.ui.getWindowManager = function () {
+       if ( !OO.ui.windowManager ) {
+               OO.ui.windowManager = new OO.ui.WindowManager();
+               $( 'body' ).append( OO.ui.windowManager.$element );
+               OO.ui.windowManager.addWindows( {
+                       messageDialog: new OO.ui.MessageDialog()
+               } );
+       }
+       return OO.ui.windowManager;
+};
+
+/**
+ * Display a quick modal alert dialog, using a OO.ui.MessageDialog. While the dialog is open, the
+ * rest of the page will be dimmed out and the user won't be able to interact with it. The dialog
+ * has only one action button, labelled "OK", clicking it will simply close the dialog.
+ *
+ * A window manager is created automatically when this function is called for the first time.
+ *
+ *     @example
+ *     OO.ui.alert( 'Something happened!' ).done( function () {
+ *         console.log( 'User closed the dialog.' );
+ *     } );
+ *
+ * @param {jQuery|string} text Message text to display
+ * @param {Object} [options] Additional options, see OO.ui.MessageDialog#getSetupProcess
+ * @return {jQuery.Promise} Promise resolved when the user closes the dialog
+ */
+OO.ui.alert = function ( text, options ) {
+       return OO.ui.getWindowManager().openWindow( 'messageDialog', $.extend( {
+               message: text,
+               verbose: true,
+               actions: [ OO.ui.MessageDialog.static.actions[ 0 ] ]
+       }, options ) ).then( function ( opened ) {
+               return opened.then( function ( closing ) {
+                       return closing.then( function () {
+                               return $.Deferred().resolve();
+                       } );
+               } );
+       } );
+};
+
+/**
+ * Display a quick modal confirmation dialog, using a OO.ui.MessageDialog. While the dialog is open,
+ * the rest of the page will be dimmed out and the user won't be able to interact with it. The
+ * dialog has two action buttons, one to confirm an operation (labelled "OK") and one to cancel it
+ * (labelled "Cancel").
+ *
+ * A window manager is created automatically when this function is called for the first time.
+ *
+ *     @example
+ *     OO.ui.confirm( 'Are you sure?' ).done( function ( confirmed ) {
+ *         if ( confirmed ) {
+ *             console.log( 'User clicked "OK"!' );
+ *         } else {
+ *             console.log( 'User clicked "Cancel" or closed the dialog.' );
+ *         }
+ *     } );
+ *
+ * @param {jQuery|string} text Message text to display
+ * @param {Object} [options] Additional options, see OO.ui.MessageDialog#getSetupProcess
+ * @return {jQuery.Promise} Promise resolved when the user closes the dialog. If the user chose to
+ *  confirm, the promise will resolve to boolean `true`; otherwise, it will resolve to boolean
+ *  `false`.
+ */
+OO.ui.confirm = function ( text, options ) {
+       return OO.ui.getWindowManager().openWindow( 'messageDialog', $.extend( {
+               message: text,
+               verbose: true
+       }, options ) ).then( function ( opened ) {
+               return opened.then( function ( closing ) {
+                       return closing.then( function ( data ) {
+                               return $.Deferred().resolve( !!( data && data.action === 'accept' ) );
+                       } );
+               } );
+       } );
+};
+
+}( OO ) );
diff --git a/resources/lib/oojs-ui/oojs-ui.js b/resources/lib/oojs-ui/oojs-ui.js
deleted file mode 100644 (file)
index 98d4709..0000000
+++ /dev/null
@@ -1,20149 +0,0 @@
-/*!
- * OOjs UI v0.15.1
- * https://www.mediawiki.org/wiki/OOjs_UI
- *
- * Copyright 2011–2016 OOjs UI Team and other contributors.
- * Released under the MIT license
- * http://oojs.mit-license.org
- *
- * Date: 2016-01-26T21:14:23Z
- */
-( function ( OO ) {
-
-'use strict';
-
-/**
- * Namespace for all classes, static methods and static properties.
- *
- * @class
- * @singleton
- */
-OO.ui = {};
-
-OO.ui.bind = $.proxy;
-
-/**
- * @property {Object}
- */
-OO.ui.Keys = {
-       UNDEFINED: 0,
-       BACKSPACE: 8,
-       DELETE: 46,
-       LEFT: 37,
-       RIGHT: 39,
-       UP: 38,
-       DOWN: 40,
-       ENTER: 13,
-       END: 35,
-       HOME: 36,
-       TAB: 9,
-       PAGEUP: 33,
-       PAGEDOWN: 34,
-       ESCAPE: 27,
-       SHIFT: 16,
-       SPACE: 32
-};
-
-/**
- * Constants for MouseEvent.which
- *
- * @property {Object}
- */
-OO.ui.MouseButtons = {
-       LEFT: 1,
-       MIDDLE: 2,
-       RIGHT: 3
-};
-
-/**
- * @property {Number}
- */
-OO.ui.elementId = 0;
-
-/**
- * Generate a unique ID for element
- *
- * @return {String} [id]
- */
-OO.ui.generateElementId = function () {
-       OO.ui.elementId += 1;
-       return 'oojsui-' + OO.ui.elementId;
-};
-
-/**
- * Check if an element is focusable.
- * Inspired from :focusable in jQueryUI v1.11.4 - 2015-04-14
- *
- * @param {jQuery} element Element to test
- * @return {boolean}
- */
-OO.ui.isFocusableElement = function ( $element ) {
-       var nodeName,
-               element = $element[ 0 ];
-
-       // Anything disabled is not focusable
-       if ( element.disabled ) {
-               return false;
-       }
-
-       // Check if the element is visible
-       if ( !(
-               // This is quicker than calling $element.is( ':visible' )
-               $.expr.filters.visible( element ) &&
-               // Check that all parents are visible
-               !$element.parents().addBack().filter( function () {
-                       return $.css( this, 'visibility' ) === 'hidden';
-               } ).length
-       ) ) {
-               return false;
-       }
-
-       // Check if the element is ContentEditable, which is the string 'true'
-       if ( element.contentEditable === 'true' ) {
-               return true;
-       }
-
-       // Anything with a non-negative numeric tabIndex is focusable.
-       // Use .prop to avoid browser bugs
-       if ( $element.prop( 'tabIndex' ) >= 0 ) {
-               return true;
-       }
-
-       // Some element types are naturally focusable
-       // (indexOf is much faster than regex in Chrome and about the
-       // same in FF: https://jsperf.com/regex-vs-indexof-array2)
-       nodeName = element.nodeName.toLowerCase();
-       if ( [ 'input', 'select', 'textarea', 'button', 'object' ].indexOf( nodeName ) !== -1 ) {
-               return true;
-       }
-
-       // Links and areas are focusable if they have an href
-       if ( ( nodeName === 'a' || nodeName === 'area' ) && $element.attr( 'href' ) !== undefined ) {
-               return true;
-       }
-
-       return false;
-};
-
-/**
- * Find a focusable child
- *
- * @param {jQuery} $container Container to search in
- * @param {boolean} [backwards] Search backwards
- * @return {jQuery} Focusable child, an empty jQuery object if none found
- */
-OO.ui.findFocusable = function ( $container, backwards ) {
-       var $focusable = $( [] ),
-               // $focusableCandidates is a superset of things that
-               // could get matched by isFocusableElement
-               $focusableCandidates = $container
-                       .find( 'input, select, textarea, button, object, a, area, [contenteditable], [tabindex]' );
-
-       if ( backwards ) {
-               $focusableCandidates = Array.prototype.reverse.call( $focusableCandidates );
-       }
-
-       $focusableCandidates.each( function () {
-               var $this = $( this );
-               if ( OO.ui.isFocusableElement( $this ) ) {
-                       $focusable = $this;
-                       return false;
-               }
-       } );
-       return $focusable;
-};
-
-/**
- * Get the user's language and any fallback languages.
- *
- * These language codes are used to localize user interface elements in the user's language.
- *
- * In environments that provide a localization system, this function should be overridden to
- * return the user's language(s). The default implementation returns English (en) only.
- *
- * @return {string[]} Language codes, in descending order of priority
- */
-OO.ui.getUserLanguages = function () {
-       return [ 'en' ];
-};
-
-/**
- * Get a value in an object keyed by language code.
- *
- * @param {Object.<string,Mixed>} obj Object keyed by language code
- * @param {string|null} [lang] Language code, if omitted or null defaults to any user language
- * @param {string} [fallback] Fallback code, used if no matching language can be found
- * @return {Mixed} Local value
- */
-OO.ui.getLocalValue = function ( obj, lang, fallback ) {
-       var i, len, langs;
-
-       // Requested language
-       if ( obj[ lang ] ) {
-               return obj[ lang ];
-       }
-       // Known user language
-       langs = OO.ui.getUserLanguages();
-       for ( i = 0, len = langs.length; i < len; i++ ) {
-               lang = langs[ i ];
-               if ( obj[ lang ] ) {
-                       return obj[ lang ];
-               }
-       }
-       // Fallback language
-       if ( obj[ fallback ] ) {
-               return obj[ fallback ];
-       }
-       // First existing language
-       for ( lang in obj ) {
-               return obj[ lang ];
-       }
-
-       return undefined;
-};
-
-/**
- * Check if a node is contained within another node
- *
- * Similar to jQuery#contains except a list of containers can be supplied
- * and a boolean argument allows you to include the container in the match list
- *
- * @param {HTMLElement|HTMLElement[]} containers Container node(s) to search in
- * @param {HTMLElement} contained Node to find
- * @param {boolean} [matchContainers] Include the container(s) in the list of nodes to match, otherwise only match descendants
- * @return {boolean} The node is in the list of target nodes
- */
-OO.ui.contains = function ( containers, contained, matchContainers ) {
-       var i;
-       if ( !Array.isArray( containers ) ) {
-               containers = [ containers ];
-       }
-       for ( i = containers.length - 1; i >= 0; i-- ) {
-               if ( ( matchContainers && contained === containers[ i ] ) || $.contains( containers[ i ], contained ) ) {
-                       return true;
-               }
-       }
-       return false;
-};
-
-/**
- * Return a function, that, as long as it continues to be invoked, will not
- * be triggered. The function will be called after it stops being called for
- * N milliseconds. If `immediate` is passed, trigger the function on the
- * leading edge, instead of the trailing.
- *
- * Ported from: http://underscorejs.org/underscore.js
- *
- * @param {Function} func
- * @param {number} wait
- * @param {boolean} immediate
- * @return {Function}
- */
-OO.ui.debounce = function ( func, wait, immediate ) {
-       var timeout;
-       return function () {
-               var context = this,
-                       args = arguments,
-                       later = function () {
-                               timeout = null;
-                               if ( !immediate ) {
-                                       func.apply( context, args );
-                               }
-                       };
-               if ( immediate && !timeout ) {
-                       func.apply( context, args );
-               }
-               clearTimeout( timeout );
-               timeout = setTimeout( later, wait );
-       };
-};
-
-/**
- * Proxy for `node.addEventListener( eventName, handler, true )`.
- *
- * @param {HTMLElement} node
- * @param {string} eventName
- * @param {Function} handler
- * @deprecated
- */
-OO.ui.addCaptureEventListener = function ( node, eventName, handler ) {
-       node.addEventListener( eventName, handler, true );
-};
-
-/**
- * Proxy for `node.removeEventListener( eventName, handler, true )`.
- *
- * @param {HTMLElement} node
- * @param {string} eventName
- * @param {Function} handler
- * @deprecated
- */
-OO.ui.removeCaptureEventListener = function ( node, eventName, handler ) {
-       node.removeEventListener( eventName, handler, true );
-};
-
-/**
- * Reconstitute a JavaScript object corresponding to a widget created by
- * the PHP implementation.
- *
- * This is an alias for `OO.ui.Element.static.infuse()`.
- *
- * @param {string|HTMLElement|jQuery} idOrNode
- *   A DOM id (if a string) or node for the widget to infuse.
- * @return {OO.ui.Element}
- *   The `OO.ui.Element` corresponding to this (infusable) document node.
- */
-OO.ui.infuse = function ( idOrNode ) {
-       return OO.ui.Element.static.infuse( idOrNode );
-};
-
-( function () {
-       /**
-        * Message store for the default implementation of OO.ui.msg
-        *
-        * Environments that provide a localization system should not use this, but should override
-        * OO.ui.msg altogether.
-        *
-        * @private
-        */
-       var messages = {
-               // Tool tip for a button that moves items in a list down one place
-               'ooui-outline-control-move-down': 'Move item down',
-               // Tool tip for a button that moves items in a list up one place
-               'ooui-outline-control-move-up': 'Move item up',
-               // Tool tip for a button that removes items from a list
-               'ooui-outline-control-remove': 'Remove item',
-               // Label for the toolbar group that contains a list of all other available tools
-               'ooui-toolbar-more': 'More',
-               // Label for the fake tool that expands the full list of tools in a toolbar group
-               'ooui-toolgroup-expand': 'More',
-               // Label for the fake tool that collapses the full list of tools in a toolbar group
-               'ooui-toolgroup-collapse': 'Fewer',
-               // Default label for the accept button of a confirmation dialog
-               'ooui-dialog-message-accept': 'OK',
-               // Default label for the reject button of a confirmation dialog
-               'ooui-dialog-message-reject': 'Cancel',
-               // Title for process dialog error description
-               'ooui-dialog-process-error': 'Something went wrong',
-               // Label for process dialog dismiss error button, visible when describing errors
-               'ooui-dialog-process-dismiss': 'Dismiss',
-               // Label for process dialog retry action button, visible when describing only recoverable errors
-               'ooui-dialog-process-retry': 'Try again',
-               // Label for process dialog retry action button, visible when describing only warnings
-               'ooui-dialog-process-continue': 'Continue',
-               // Label for the file selection widget's select file button
-               'ooui-selectfile-button-select': 'Select a file',
-               // Label for the file selection widget if file selection is not supported
-               'ooui-selectfile-not-supported': 'File selection is not supported',
-               // Label for the file selection widget when no file is currently selected
-               'ooui-selectfile-placeholder': 'No file is selected',
-               // Label for the file selection widget's drop target
-               'ooui-selectfile-dragdrop-placeholder': 'Drop file here'
-       };
-
-       /**
-        * Get a localized message.
-        *
-        * In environments that provide a localization system, this function should be overridden to
-        * return the message translated in the user's language. The default implementation always returns
-        * English messages.
-        *
-        * After the message key, message parameters may optionally be passed. In the default implementation,
-        * any occurrences of $1 are replaced with the first parameter, $2 with the second parameter, etc.
-        * Alternative implementations of OO.ui.msg may use any substitution system they like, as long as
-        * they support unnamed, ordered message parameters.
-        *
-        * @param {string} key Message key
-        * @param {Mixed...} [params] Message parameters
-        * @return {string} Translated message with parameters substituted
-        */
-       OO.ui.msg = function ( key ) {
-               var message = messages[ key ],
-                       params = Array.prototype.slice.call( arguments, 1 );
-               if ( typeof message === 'string' ) {
-                       // Perform $1 substitution
-                       message = message.replace( /\$(\d+)/g, function ( unused, n ) {
-                               var i = parseInt( n, 10 );
-                               return params[ i - 1 ] !== undefined ? params[ i - 1 ] : '$' + n;
-                       } );
-               } else {
-                       // Return placeholder if message not found
-                       message = '[' + key + ']';
-               }
-               return message;
-       };
-} )();
-
-/**
- * Package a message and arguments for deferred resolution.
- *
- * Use this when you are statically specifying a message and the message may not yet be present.
- *
- * @param {string} key Message key
- * @param {Mixed...} [params] Message parameters
- * @return {Function} Function that returns the resolved message when executed
- */
-OO.ui.deferMsg = function () {
-       var args = arguments;
-       return function () {
-               return OO.ui.msg.apply( OO.ui, args );
-       };
-};
-
-/**
- * Resolve a message.
- *
- * If the message is a function it will be executed, otherwise it will pass through directly.
- *
- * @param {Function|string} msg Deferred message, or message text
- * @return {string} Resolved message
- */
-OO.ui.resolveMsg = function ( msg ) {
-       if ( $.isFunction( msg ) ) {
-               return msg();
-       }
-       return msg;
-};
-
-/**
- * @param {string} url
- * @return {boolean}
- */
-OO.ui.isSafeUrl = function ( url ) {
-       // Keep this function in sync with php/Tag.php
-       var i, protocolWhitelist;
-
-       function stringStartsWith( haystack, needle ) {
-               return haystack.substr( 0, needle.length ) === needle;
-       }
-
-       protocolWhitelist = [
-               'bitcoin', 'ftp', 'ftps', 'geo', 'git', 'gopher', 'http', 'https', 'irc', 'ircs',
-               'magnet', 'mailto', 'mms', 'news', 'nntp', 'redis', 'sftp', 'sip', 'sips', 'sms', 'ssh',
-               'svn', 'tel', 'telnet', 'urn', 'worldwind', 'xmpp'
-       ];
-
-       if ( url === '' ) {
-               return true;
-       }
-
-       for ( i = 0; i < protocolWhitelist.length; i++ ) {
-               if ( stringStartsWith( url, protocolWhitelist[ i ] + ':' ) ) {
-                       return true;
-               }
-       }
-
-       // This matches '//' too
-       if ( stringStartsWith( url, '/' ) || stringStartsWith( url, './' ) ) {
-               return true;
-       }
-       if ( stringStartsWith( url, '?' ) || stringStartsWith( url, '#' ) ) {
-               return true;
-       }
-
-       return false;
-};
-
-/**
- * Lazy-initialize and return a global OO.ui.WindowManager instance, used by OO.ui.alert and
- * OO.ui.confirm.
- *
- * @private
- * @return {OO.ui.WindowManager}
- */
-OO.ui.getWindowManager = function () {
-       if ( !OO.ui.windowManager ) {
-               OO.ui.windowManager = new OO.ui.WindowManager();
-               $( 'body' ).append( OO.ui.windowManager.$element );
-               OO.ui.windowManager.addWindows( {
-                       messageDialog: new OO.ui.MessageDialog()
-               } );
-       }
-       return OO.ui.windowManager;
-};
-
-/**
- * Display a quick modal alert dialog, using a OO.ui.MessageDialog. While the dialog is open, the
- * rest of the page will be dimmed out and the user won't be able to interact with it. The dialog
- * has only one action button, labelled "OK", clicking it will simply close the dialog.
- *
- * A window manager is created automatically when this function is called for the first time.
- *
- *     @example
- *     OO.ui.alert( 'Something happened!' ).done( function () {
- *         console.log( 'User closed the dialog.' );
- *     } );
- *
- * @param {jQuery|string} text Message text to display
- * @param {Object} [options] Additional options, see OO.ui.MessageDialog#getSetupProcess
- * @return {jQuery.Promise} Promise resolved when the user closes the dialog
- */
-OO.ui.alert = function ( text, options ) {
-       return OO.ui.getWindowManager().openWindow( 'messageDialog', $.extend( {
-               message: text,
-               verbose: true,
-               actions: [ OO.ui.MessageDialog.static.actions[ 0 ] ]
-       }, options ) ).then( function ( opened ) {
-               return opened.then( function ( closing ) {
-                       return closing.then( function () {
-                               return $.Deferred().resolve();
-                       } );
-               } );
-       } );
-};
-
-/**
- * Display a quick modal confirmation dialog, using a OO.ui.MessageDialog. While the dialog is open,
- * the rest of the page will be dimmed out and the user won't be able to interact with it. The
- * dialog has two action buttons, one to confirm an operation (labelled "OK") and one to cancel it
- * (labelled "Cancel").
- *
- * A window manager is created automatically when this function is called for the first time.
- *
- *     @example
- *     OO.ui.confirm( 'Are you sure?' ).done( function ( confirmed ) {
- *         if ( confirmed ) {
- *             console.log( 'User clicked "OK"!' );
- *         } else {
- *             console.log( 'User clicked "Cancel" or closed the dialog.' );
- *         }
- *     } );
- *
- * @param {jQuery|string} text Message text to display
- * @param {Object} [options] Additional options, see OO.ui.MessageDialog#getSetupProcess
- * @return {jQuery.Promise} Promise resolved when the user closes the dialog. If the user chose to
- *  confirm, the promise will resolve to boolean `true`; otherwise, it will resolve to boolean
- *  `false`.
- */
-OO.ui.confirm = function ( text, options ) {
-       return OO.ui.getWindowManager().openWindow( 'messageDialog', $.extend( {
-               message: text,
-               verbose: true
-       }, options ) ).then( function ( opened ) {
-               return opened.then( function ( closing ) {
-                       return closing.then( function ( data ) {
-                               return $.Deferred().resolve( !!( data && data.action === 'accept' ) );
-                       } );
-               } );
-       } );
-};
-
-/*!
- * Mixin namespace.
- */
-
-/**
- * Namespace for OOjs UI mixins.
- *
- * Mixins are named according to the type of object they are intended to
- * be mixed in to.  For example, OO.ui.mixin.GroupElement is intended to be
- * mixed in to an instance of OO.ui.Element, and OO.ui.mixin.GroupWidget
- * is intended to be mixed in to an instance of OO.ui.Widget.
- *
- * @class
- * @singleton
- */
-OO.ui.mixin = {};
-
-/**
- * PendingElement is a mixin that is used to create elements that notify users that something is happening
- * and that they should wait before proceeding. The pending state is visually represented with a pending
- * texture that appears in the head of a pending {@link OO.ui.ProcessDialog process dialog} or in the input
- * field of a {@link OO.ui.TextInputWidget text input widget}.
- *
- * Currently, {@link OO.ui.ActionWidget Action widgets}, which mix in this class, can also be marked as pending, but only when
- * used in {@link OO.ui.MessageDialog message dialogs}. The behavior is not currently supported for action widgets used
- * in process dialogs.
- *
- *     @example
- *     function MessageDialog( config ) {
- *         MessageDialog.parent.call( this, config );
- *     }
- *     OO.inheritClass( MessageDialog, OO.ui.MessageDialog );
- *
- *     MessageDialog.static.actions = [
- *         { action: 'save', label: 'Done', flags: 'primary' },
- *         { label: 'Cancel', flags: 'safe' }
- *     ];
- *
- *     MessageDialog.prototype.initialize = function () {
- *         MessageDialog.parent.prototype.initialize.apply( this, arguments );
- *         this.content = new OO.ui.PanelLayout( { $: this.$, padded: true } );
- *         this.content.$element.append( '<p>Click the \'Done\' action widget to see its pending state. Note that action widgets can be marked pending in message dialogs but not process dialogs.</p>' );
- *         this.$body.append( this.content.$element );
- *     };
- *     MessageDialog.prototype.getBodyHeight = function () {
- *         return 100;
- *     }
- *     MessageDialog.prototype.getActionProcess = function ( action ) {
- *         var dialog = this;
- *         if ( action === 'save' ) {
- *             dialog.getActions().get({actions: 'save'})[0].pushPending();
- *             return new OO.ui.Process()
- *             .next( 1000 )
- *             .next( function () {
- *                 dialog.getActions().get({actions: 'save'})[0].popPending();
- *             } );
- *         }
- *         return MessageDialog.parent.prototype.getActionProcess.call( this, action );
- *     };
- *
- *     var windowManager = new OO.ui.WindowManager();
- *     $( 'body' ).append( windowManager.$element );
- *
- *     var dialog = new MessageDialog();
- *     windowManager.addWindows( [ dialog ] );
- *     windowManager.openWindow( dialog );
- *
- * @abstract
- * @class
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {jQuery} [$pending] Element to mark as pending, defaults to this.$element
- */
-OO.ui.mixin.PendingElement = function OoUiMixinPendingElement( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Properties
-       this.pending = 0;
-       this.$pending = null;
-
-       // Initialisation
-       this.setPendingElement( config.$pending || this.$element );
-};
-
-/* Setup */
-
-OO.initClass( OO.ui.mixin.PendingElement );
-
-/* Methods */
-
-/**
- * Set the pending element (and clean up any existing one).
- *
- * @param {jQuery} $pending The element to set to pending.
- */
-OO.ui.mixin.PendingElement.prototype.setPendingElement = function ( $pending ) {
-       if ( this.$pending ) {
-               this.$pending.removeClass( 'oo-ui-pendingElement-pending' );
-       }
-
-       this.$pending = $pending;
-       if ( this.pending > 0 ) {
-               this.$pending.addClass( 'oo-ui-pendingElement-pending' );
-       }
-};
-
-/**
- * Check if an element is pending.
- *
- * @return {boolean} Element is pending
- */
-OO.ui.mixin.PendingElement.prototype.isPending = function () {
-       return !!this.pending;
-};
-
-/**
- * Increase the pending counter. The pending state will remain active until the counter is zero
- * (i.e., the number of calls to #pushPending and #popPending is the same).
- *
- * @chainable
- */
-OO.ui.mixin.PendingElement.prototype.pushPending = function () {
-       if ( this.pending === 0 ) {
-               this.$pending.addClass( 'oo-ui-pendingElement-pending' );
-               this.updateThemeClasses();
-       }
-       this.pending++;
-
-       return this;
-};
-
-/**
- * Decrease the pending counter. The pending state will remain active until the counter is zero
- * (i.e., the number of calls to #pushPending and #popPending is the same).
- *
- * @chainable
- */
-OO.ui.mixin.PendingElement.prototype.popPending = function () {
-       if ( this.pending === 1 ) {
-               this.$pending.removeClass( 'oo-ui-pendingElement-pending' );
-               this.updateThemeClasses();
-       }
-       this.pending = Math.max( 0, this.pending - 1 );
-
-       return this;
-};
-
-/**
- * ActionSets manage the behavior of the {@link OO.ui.ActionWidget action widgets} that comprise them.
- * Actions can be made available for specific contexts (modes) and circumstances
- * (abilities). Action sets are primarily used with {@link OO.ui.Dialog Dialogs}.
- *
- * ActionSets contain two types of actions:
- *
- * - Special: Special actions are the first visible actions with special flags, such as 'safe' and 'primary', the default special flags. Additional special flags can be configured in subclasses with the static #specialFlags property.
- * - Other: Other actions include all non-special visible actions.
- *
- * Please see the [OOjs UI documentation on MediaWiki][1] for more information.
- *
- *     @example
- *     // Example: An action set used in a process dialog
- *     function MyProcessDialog( config ) {
- *         MyProcessDialog.parent.call( this, config );
- *     }
- *     OO.inheritClass( MyProcessDialog, OO.ui.ProcessDialog );
- *     MyProcessDialog.static.title = 'An action set in a process dialog';
- *     // An action set that uses modes ('edit' and 'help' mode, in this example).
- *     MyProcessDialog.static.actions = [
- *         { action: 'continue', modes: 'edit', label: 'Continue', flags: [ 'primary', 'constructive' ] },
- *         { action: 'help', modes: 'edit', label: 'Help' },
- *         { modes: 'edit', label: 'Cancel', flags: 'safe' },
- *         { action: 'back', modes: 'help', label: 'Back', flags: 'safe' }
- *     ];
- *
- *     MyProcessDialog.prototype.initialize = function () {
- *         MyProcessDialog.parent.prototype.initialize.apply( this, arguments );
- *         this.panel1 = new OO.ui.PanelLayout( { padded: true, expanded: false } );
- *         this.panel1.$element.append( '<p>This dialog uses an action set (continue, help, cancel, back) configured with modes. This is edit mode. Click \'help\' to see help mode.</p>' );
- *         this.panel2 = new OO.ui.PanelLayout( { padded: true, expanded: false } );
- *         this.panel2.$element.append( '<p>This is help mode. Only the \'back\' action widget is configured to be visible here. Click \'back\' to return to \'edit\' mode.</p>' );
- *         this.stackLayout = new OO.ui.StackLayout( {
- *             items: [ this.panel1, this.panel2 ]
- *         } );
- *         this.$body.append( this.stackLayout.$element );
- *     };
- *     MyProcessDialog.prototype.getSetupProcess = function ( data ) {
- *         return MyProcessDialog.parent.prototype.getSetupProcess.call( this, data )
- *             .next( function () {
- *                 this.actions.setMode( 'edit' );
- *             }, this );
- *     };
- *     MyProcessDialog.prototype.getActionProcess = function ( action ) {
- *         if ( action === 'help' ) {
- *             this.actions.setMode( 'help' );
- *             this.stackLayout.setItem( this.panel2 );
- *         } else if ( action === 'back' ) {
- *             this.actions.setMode( 'edit' );
- *             this.stackLayout.setItem( this.panel1 );
- *         } else if ( action === 'continue' ) {
- *             var dialog = this;
- *             return new OO.ui.Process( function () {
- *                 dialog.close();
- *             } );
- *         }
- *         return MyProcessDialog.parent.prototype.getActionProcess.call( this, action );
- *     };
- *     MyProcessDialog.prototype.getBodyHeight = function () {
- *         return this.panel1.$element.outerHeight( true );
- *     };
- *     var windowManager = new OO.ui.WindowManager();
- *     $( 'body' ).append( windowManager.$element );
- *     var dialog = new MyProcessDialog( {
- *         size: 'medium'
- *     } );
- *     windowManager.addWindows( [ dialog ] );
- *     windowManager.openWindow( dialog );
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Process_Dialogs#Action_sets
- *
- * @abstract
- * @class
- * @mixins OO.EventEmitter
- *
- * @constructor
- * @param {Object} [config] Configuration options
- */
-OO.ui.ActionSet = function OoUiActionSet( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Mixin constructors
-       OO.EventEmitter.call( this );
-
-       // Properties
-       this.list = [];
-       this.categories = {
-               actions: 'getAction',
-               flags: 'getFlags',
-               modes: 'getModes'
-       };
-       this.categorized = {};
-       this.special = {};
-       this.others = [];
-       this.organized = false;
-       this.changing = false;
-       this.changed = false;
-};
-
-/* Setup */
-
-OO.mixinClass( OO.ui.ActionSet, OO.EventEmitter );
-
-/* Static Properties */
-
-/**
- * Symbolic name of the flags used to identify special actions. Special actions are displayed in the
- *  header of a {@link OO.ui.ProcessDialog process dialog}.
- *  See the [OOjs UI documentation on MediaWiki][2] for more information and examples.
- *
- *  [2]:https://www.mediawiki.org/wiki/OOjs_UI/Windows/Process_Dialogs
- *
- * @abstract
- * @static
- * @inheritable
- * @property {string}
- */
-OO.ui.ActionSet.static.specialFlags = [ 'safe', 'primary' ];
-
-/* Events */
-
-/**
- * @event click
- *
- * A 'click' event is emitted when an action is clicked.
- *
- * @param {OO.ui.ActionWidget} action Action that was clicked
- */
-
-/**
- * @event resize
- *
- * A 'resize' event is emitted when an action widget is resized.
- *
- * @param {OO.ui.ActionWidget} action Action that was resized
- */
-
-/**
- * @event add
- *
- * An 'add' event is emitted when actions are {@link #method-add added} to the action set.
- *
- * @param {OO.ui.ActionWidget[]} added Actions added
- */
-
-/**
- * @event remove
- *
- * A 'remove' event is emitted when actions are {@link #method-remove removed}
- *  or {@link #clear cleared}.
- *
- * @param {OO.ui.ActionWidget[]} added Actions removed
- */
-
-/**
- * @event change
- *
- * A 'change' event is emitted when actions are {@link #method-add added}, {@link #clear cleared},
- * or {@link #method-remove removed} from the action set or when the {@link #setMode mode} is changed.
- *
- */
-
-/* Methods */
-
-/**
- * Handle action change events.
- *
- * @private
- * @fires change
- */
-OO.ui.ActionSet.prototype.onActionChange = function () {
-       this.organized = false;
-       if ( this.changing ) {
-               this.changed = true;
-       } else {
-               this.emit( 'change' );
-       }
-};
-
-/**
- * Check if an action is one of the special actions.
- *
- * @param {OO.ui.ActionWidget} action Action to check
- * @return {boolean} Action is special
- */
-OO.ui.ActionSet.prototype.isSpecial = function ( action ) {
-       var flag;
-
-       for ( flag in this.special ) {
-               if ( action === this.special[ flag ] ) {
-                       return true;
-               }
-       }
-
-       return false;
-};
-
-/**
- * Get action widgets based on the specified filter: ‘actions’, ‘flags’, ‘modes’, ‘visible’,
- *  or ‘disabled’.
- *
- * @param {Object} [filters] Filters to use, omit to get all actions
- * @param {string|string[]} [filters.actions] Actions that action widgets must have
- * @param {string|string[]} [filters.flags] Flags that action widgets must have (e.g., 'safe')
- * @param {string|string[]} [filters.modes] Modes that action widgets must have
- * @param {boolean} [filters.visible] Action widgets must be visible
- * @param {boolean} [filters.disabled] Action widgets must be disabled
- * @return {OO.ui.ActionWidget[]} Action widgets matching all criteria
- */
-OO.ui.ActionSet.prototype.get = function ( filters ) {
-       var i, len, list, category, actions, index, match, matches;
-
-       if ( filters ) {
-               this.organize();
-
-               // Collect category candidates
-               matches = [];
-               for ( category in this.categorized ) {
-                       list = filters[ category ];
-                       if ( list ) {
-                               if ( !Array.isArray( list ) ) {
-                                       list = [ list ];
-                               }
-                               for ( i = 0, len = list.length; i < len; i++ ) {
-                                       actions = this.categorized[ category ][ list[ i ] ];
-                                       if ( Array.isArray( actions ) ) {
-                                               matches.push.apply( matches, actions );
-                                       }
-                               }
-                       }
-               }
-               // Remove by boolean filters
-               for ( i = 0, len = matches.length; i < len; i++ ) {
-                       match = matches[ i ];
-                       if (
-                               ( filters.visible !== undefined && match.isVisible() !== filters.visible ) ||
-                               ( filters.disabled !== undefined && match.isDisabled() !== filters.disabled )
-                       ) {
-                               matches.splice( i, 1 );
-                               len--;
-                               i--;
-                       }
-               }
-               // Remove duplicates
-               for ( i = 0, len = matches.length; i < len; i++ ) {
-                       match = matches[ i ];
-                       index = matches.lastIndexOf( match );
-                       while ( index !== i ) {
-                               matches.splice( index, 1 );
-                               len--;
-                               index = matches.lastIndexOf( match );
-                       }
-               }
-               return matches;
-       }
-       return this.list.slice();
-};
-
-/**
- * Get 'special' actions.
- *
- * Special actions are the first visible action widgets with special flags, such as 'safe' and 'primary'.
- * Special flags can be configured in subclasses by changing the static #specialFlags property.
- *
- * @return {OO.ui.ActionWidget[]|null} 'Special' action widgets.
- */
-OO.ui.ActionSet.prototype.getSpecial = function () {
-       this.organize();
-       return $.extend( {}, this.special );
-};
-
-/**
- * Get 'other' actions.
- *
- * Other actions include all non-special visible action widgets.
- *
- * @return {OO.ui.ActionWidget[]} 'Other' action widgets
- */
-OO.ui.ActionSet.prototype.getOthers = function () {
-       this.organize();
-       return this.others.slice();
-};
-
-/**
- * Set the mode  (e.g., ‘edit’ or ‘view’). Only {@link OO.ui.ActionWidget#modes actions} configured
- * to be available in the specified mode will be made visible. All other actions will be hidden.
- *
- * @param {string} mode The mode. Only actions configured to be available in the specified
- *  mode will be made visible.
- * @chainable
- * @fires toggle
- * @fires change
- */
-OO.ui.ActionSet.prototype.setMode = function ( mode ) {
-       var i, len, action;
-
-       this.changing = true;
-       for ( i = 0, len = this.list.length; i < len; i++ ) {
-               action = this.list[ i ];
-               action.toggle( action.hasMode( mode ) );
-       }
-
-       this.organized = false;
-       this.changing = false;
-       this.emit( 'change' );
-
-       return this;
-};
-
-/**
- * Set the abilities of the specified actions.
- *
- * Action widgets that are configured with the specified actions will be enabled
- * or disabled based on the boolean values specified in the `actions`
- * parameter.
- *
- * @param {Object.<string,boolean>} actions A list keyed by action name with boolean
- *  values that indicate whether or not the action should be enabled.
- * @chainable
- */
-OO.ui.ActionSet.prototype.setAbilities = function ( actions ) {
-       var i, len, action, item;
-
-       for ( i = 0, len = this.list.length; i < len; i++ ) {
-               item = this.list[ i ];
-               action = item.getAction();
-               if ( actions[ action ] !== undefined ) {
-                       item.setDisabled( !actions[ action ] );
-               }
-       }
-
-       return this;
-};
-
-/**
- * Executes a function once per action.
- *
- * When making changes to multiple actions, use this method instead of iterating over the actions
- * manually to defer emitting a #change event until after all actions have been changed.
- *
- * @param {Object|null} actions Filters to use to determine which actions to iterate over; see #get
- * @param {Function} callback Callback to run for each action; callback is invoked with three
- *   arguments: the action, the action's index, the list of actions being iterated over
- * @chainable
- */
-OO.ui.ActionSet.prototype.forEach = function ( filter, callback ) {
-       this.changed = false;
-       this.changing = true;
-       this.get( filter ).forEach( callback );
-       this.changing = false;
-       if ( this.changed ) {
-               this.emit( 'change' );
-       }
-
-       return this;
-};
-
-/**
- * Add action widgets to the action set.
- *
- * @param {OO.ui.ActionWidget[]} actions Action widgets to add
- * @chainable
- * @fires add
- * @fires change
- */
-OO.ui.ActionSet.prototype.add = function ( actions ) {
-       var i, len, action;
-
-       this.changing = true;
-       for ( i = 0, len = actions.length; i < len; i++ ) {
-               action = actions[ i ];
-               action.connect( this, {
-                       click: [ 'emit', 'click', action ],
-                       resize: [ 'emit', 'resize', action ],
-                       toggle: [ 'onActionChange' ]
-               } );
-               this.list.push( action );
-       }
-       this.organized = false;
-       this.emit( 'add', actions );
-       this.changing = false;
-       this.emit( 'change' );
-
-       return this;
-};
-
-/**
- * Remove action widgets from the set.
- *
- * To remove all actions, you may wish to use the #clear method instead.
- *
- * @param {OO.ui.ActionWidget[]} actions Action widgets to remove
- * @chainable
- * @fires remove
- * @fires change
- */
-OO.ui.ActionSet.prototype.remove = function ( actions ) {
-       var i, len, index, action;
-
-       this.changing = true;
-       for ( i = 0, len = actions.length; i < len; i++ ) {
-               action = actions[ i ];
-               index = this.list.indexOf( action );
-               if ( index !== -1 ) {
-                       action.disconnect( this );
-                       this.list.splice( index, 1 );
-               }
-       }
-       this.organized = false;
-       this.emit( 'remove', actions );
-       this.changing = false;
-       this.emit( 'change' );
-
-       return this;
-};
-
-/**
- * Remove all action widets from the set.
- *
- * To remove only specified actions, use the {@link #method-remove remove} method instead.
- *
- * @chainable
- * @fires remove
- * @fires change
- */
-OO.ui.ActionSet.prototype.clear = function () {
-       var i, len, action,
-               removed = this.list.slice();
-
-       this.changing = true;
-       for ( i = 0, len = this.list.length; i < len; i++ ) {
-               action = this.list[ i ];
-               action.disconnect( this );
-       }
-
-       this.list = [];
-
-       this.organized = false;
-       this.emit( 'remove', removed );
-       this.changing = false;
-       this.emit( 'change' );
-
-       return this;
-};
-
-/**
- * Organize actions.
- *
- * This is called whenever organized information is requested. It will only reorganize the actions
- * if something has changed since the last time it ran.
- *
- * @private
- * @chainable
- */
-OO.ui.ActionSet.prototype.organize = function () {
-       var i, iLen, j, jLen, flag, action, category, list, item, special,
-               specialFlags = this.constructor.static.specialFlags;
-
-       if ( !this.organized ) {
-               this.categorized = {};
-               this.special = {};
-               this.others = [];
-               for ( i = 0, iLen = this.list.length; i < iLen; i++ ) {
-                       action = this.list[ i ];
-                       if ( action.isVisible() ) {
-                               // Populate categories
-                               for ( category in this.categories ) {
-                                       if ( !this.categorized[ category ] ) {
-                                               this.categorized[ category ] = {};
-                                       }
-                                       list = action[ this.categories[ category ] ]();
-                                       if ( !Array.isArray( list ) ) {
-                                               list = [ list ];
-                                       }
-                                       for ( j = 0, jLen = list.length; j < jLen; j++ ) {
-                                               item = list[ j ];
-                                               if ( !this.categorized[ category ][ item ] ) {
-                                                       this.categorized[ category ][ item ] = [];
-                                               }
-                                               this.categorized[ category ][ item ].push( action );
-                                       }
-                               }
-                               // Populate special/others
-                               special = false;
-                               for ( j = 0, jLen = specialFlags.length; j < jLen; j++ ) {
-                                       flag = specialFlags[ j ];
-                                       if ( !this.special[ flag ] && action.hasFlag( flag ) ) {
-                                               this.special[ flag ] = action;
-                                               special = true;
-                                               break;
-                                       }
-                               }
-                               if ( !special ) {
-                                       this.others.push( action );
-                               }
-                       }
-               }
-               this.organized = true;
-       }
-
-       return this;
-};
-
-/**
- * Each Element represents a rendering in the DOM—a button or an icon, for example, or anything
- * that is visible to a user. Unlike {@link OO.ui.Widget widgets}, plain elements usually do not have events
- * connected to them and can't be interacted with.
- *
- * @abstract
- * @class
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {string[]} [classes] The names of the CSS classes to apply to the element. CSS styles are added
- *  to the top level (e.g., the outermost div) of the element. See the [OOjs UI documentation on MediaWiki][2]
- *  for an example.
- *  [2]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Buttons_and_Switches#cssExample
- * @cfg {string} [id] The HTML id attribute used in the rendered tag.
- * @cfg {string} [text] Text to insert
- * @cfg {Array} [content] An array of content elements to append (after #text).
- *  Strings will be html-escaped; use an OO.ui.HtmlSnippet to append raw HTML.
- *  Instances of OO.ui.Element will have their $element appended.
- * @cfg {jQuery} [$content] Content elements to append (after #text).
- * @cfg {jQuery} [$element] Wrapper element. Defaults to a new element with #getTagName.
- * @cfg {Mixed} [data] Custom data of any type or combination of types (e.g., string, number, array, object).
- *  Data can also be specified with the #setData method.
- */
-OO.ui.Element = function OoUiElement( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Properties
-       this.$ = $;
-       this.visible = true;
-       this.data = config.data;
-       this.$element = config.$element ||
-               $( document.createElement( this.getTagName() ) );
-       this.elementGroup = null;
-       this.debouncedUpdateThemeClassesHandler = OO.ui.debounce( this.debouncedUpdateThemeClasses );
-
-       // Initialization
-       if ( Array.isArray( config.classes ) ) {
-               this.$element.addClass( config.classes.join( ' ' ) );
-       }
-       if ( config.id ) {
-               this.$element.attr( 'id', config.id );
-       }
-       if ( config.text ) {
-               this.$element.text( config.text );
-       }
-       if ( config.content ) {
-               // The `content` property treats plain strings as text; use an
-               // HtmlSnippet to append HTML content.  `OO.ui.Element`s get their
-               // appropriate $element appended.
-               this.$element.append( config.content.map( function ( v ) {
-                       if ( typeof v === 'string' ) {
-                               // Escape string so it is properly represented in HTML.
-                               return document.createTextNode( v );
-                       } else if ( v instanceof OO.ui.HtmlSnippet ) {
-                               // Bypass escaping.
-                               return v.toString();
-                       } else if ( v instanceof OO.ui.Element ) {
-                               return v.$element;
-                       }
-                       return v;
-               } ) );
-       }
-       if ( config.$content ) {
-               // The `$content` property treats plain strings as HTML.
-               this.$element.append( config.$content );
-       }
-};
-
-/* Setup */
-
-OO.initClass( OO.ui.Element );
-
-/* Static Properties */
-
-/**
- * The name of the HTML tag used by the element.
- *
- * The static value may be ignored if the #getTagName method is overridden.
- *
- * @static
- * @inheritable
- * @property {string}
- */
-OO.ui.Element.static.tagName = 'div';
-
-/* Static Methods */
-
-/**
- * Reconstitute a JavaScript object corresponding to a widget created
- * by the PHP implementation.
- *
- * @param {string|HTMLElement|jQuery} idOrNode
- *   A DOM id (if a string) or node for the widget to infuse.
- * @return {OO.ui.Element}
- *   The `OO.ui.Element` corresponding to this (infusable) document node.
- *   For `Tag` objects emitted on the HTML side (used occasionally for content)
- *   the value returned is a newly-created Element wrapping around the existing
- *   DOM node.
- */
-OO.ui.Element.static.infuse = function ( idOrNode ) {
-       var obj = OO.ui.Element.static.unsafeInfuse( idOrNode, false );
-       // Verify that the type matches up.
-       // FIXME: uncomment after T89721 is fixed (see T90929)
-       /*
-       if ( !( obj instanceof this['class'] ) ) {
-               throw new Error( 'Infusion type mismatch!' );
-       }
-       */
-       return obj;
-};
-
-/**
- * Implementation helper for `infuse`; skips the type check and has an
- * extra property so that only the top-level invocation touches the DOM.
- * @private
- * @param {string|HTMLElement|jQuery} idOrNode
- * @param {jQuery.Promise|boolean} domPromise A promise that will be resolved
- *     when the top-level widget of this infusion is inserted into DOM,
- *     replacing the original node; or false for top-level invocation.
- * @return {OO.ui.Element}
- */
-OO.ui.Element.static.unsafeInfuse = function ( idOrNode, domPromise ) {
-       // look for a cached result of a previous infusion.
-       var id, $elem, data, cls, parts, parent, obj, top, state;
-       if ( typeof idOrNode === 'string' ) {
-               id = idOrNode;
-               $elem = $( document.getElementById( id ) );
-       } else {
-               $elem = $( idOrNode );
-               id = $elem.attr( 'id' );
-       }
-       if ( !$elem.length ) {
-               throw new Error( 'Widget not found: ' + id );
-       }
-       data = $elem.data( 'ooui-infused' ) || $elem[ 0 ].oouiInfused;
-       if ( data ) {
-               // cached!
-               if ( data === true ) {
-                       throw new Error( 'Circular dependency! ' + id );
-               }
-               return data;
-       }
-       data = $elem.attr( 'data-ooui' );
-       if ( !data ) {
-               throw new Error( 'No infusion data found: ' + id );
-       }
-       try {
-               data = $.parseJSON( data );
-       } catch ( _ ) {
-               data = null;
-       }
-       if ( !( data && data._ ) ) {
-               throw new Error( 'No valid infusion data found: ' + id );
-       }
-       if ( data._ === 'Tag' ) {
-               // Special case: this is a raw Tag; wrap existing node, don't rebuild.
-               return new OO.ui.Element( { $element: $elem } );
-       }
-       parts = data._.split( '.' );
-       cls = OO.getProp.apply( OO, [ window ].concat( parts ) );
-       if ( cls === undefined ) {
-               // The PHP output might be old and not including the "OO.ui" prefix
-               // TODO: Remove this back-compat after next major release
-               cls = OO.getProp.apply( OO, [ OO.ui ].concat( parts ) );
-               if ( cls === undefined ) {
-                       throw new Error( 'Unknown widget type: id: ' + id + ', class: ' + data._ );
-               }
-       }
-
-       // Verify that we're creating an OO.ui.Element instance
-       parent = cls.parent;
-
-       while ( parent !== undefined ) {
-               if ( parent === OO.ui.Element ) {
-                       // Safe
-                       break;
-               }
-
-               parent = parent.parent;
-       }
-
-       if ( parent !== OO.ui.Element ) {
-               throw new Error( 'Unknown widget type: id: ' + id + ', class: ' + data._ );
-       }
-
-       if ( domPromise === false ) {
-               top = $.Deferred();
-               domPromise = top.promise();
-       }
-       $elem.data( 'ooui-infused', true ); // prevent loops
-       data.id = id; // implicit
-       data = OO.copy( data, null, function deserialize( value ) {
-               if ( OO.isPlainObject( value ) ) {
-                       if ( value.tag ) {
-                               return OO.ui.Element.static.unsafeInfuse( value.tag, domPromise );
-                       }
-                       if ( value.html ) {
-                               return new OO.ui.HtmlSnippet( value.html );
-                       }
-               }
-       } );
-       // allow widgets to reuse parts of the DOM
-       data = cls.static.reusePreInfuseDOM( $elem[ 0 ], data );
-       // pick up dynamic state, like focus, value of form inputs, scroll position, etc.
-       state = cls.static.gatherPreInfuseState( $elem[ 0 ], data );
-       // rebuild widget
-       // jscs:disable requireCapitalizedConstructors
-       obj = new cls( data );
-       // jscs:enable requireCapitalizedConstructors
-       // now replace old DOM with this new DOM.
-       if ( top ) {
-               // An efficient constructor might be able to reuse the entire DOM tree of the original element,
-               // so only mutate the DOM if we need to.
-               if ( $elem[ 0 ] !== obj.$element[ 0 ] ) {
-                       $elem.replaceWith( obj.$element );
-                       // This element is now gone from the DOM, but if anyone is holding a reference to it,
-                       // let's allow them to OO.ui.infuse() it and do what they expect (T105828).
-                       // Do not use jQuery.data(), as using it on detached nodes leaks memory in 1.x line by design.
-                       $elem[ 0 ].oouiInfused = obj;
-               }
-               top.resolve();
-       }
-       obj.$element.data( 'ooui-infused', obj );
-       // set the 'data-ooui' attribute so we can identify infused widgets
-       obj.$element.attr( 'data-ooui', '' );
-       // restore dynamic state after the new element is inserted into DOM
-       domPromise.done( obj.restorePreInfuseState.bind( obj, state ) );
-       return obj;
-};
-
-/**
- * Pick out parts of `node`'s DOM to be reused when infusing a widget.
- *
- * This method **must not** make any changes to the DOM, only find interesting pieces and add them
- * to `config` (which should then be returned). Actual DOM juggling should then be done by the
- * constructor, which will be given the enhanced config.
- *
- * @protected
- * @param {HTMLElement} node
- * @param {Object} config
- * @return {Object}
- */
-OO.ui.Element.static.reusePreInfuseDOM = function ( node, config ) {
-       return config;
-};
-
-/**
- * Gather the dynamic state (focus, value of form inputs, scroll position, etc.) of a HTML DOM node
- * (and its children) that represent an Element of the same class and the given configuration,
- * generated by the PHP implementation.
- *
- * This method is called just before `node` is detached from the DOM. The return value of this
- * function will be passed to #restorePreInfuseState after the newly created widget's #$element
- * is inserted into DOM to replace `node`.
- *
- * @protected
- * @param {HTMLElement} node
- * @param {Object} config
- * @return {Object}
- */
-OO.ui.Element.static.gatherPreInfuseState = function () {
-       return {};
-};
-
-/**
- * Get a jQuery function within a specific document.
- *
- * @static
- * @param {jQuery|HTMLElement|HTMLDocument|Window} context Context to bind the function to
- * @param {jQuery} [$iframe] HTML iframe element that contains the document, omit if document is
- *   not in an iframe
- * @return {Function} Bound jQuery function
- */
-OO.ui.Element.static.getJQuery = function ( context, $iframe ) {
-       function wrapper( selector ) {
-               return $( selector, wrapper.context );
-       }
-
-       wrapper.context = this.getDocument( context );
-
-       if ( $iframe ) {
-               wrapper.$iframe = $iframe;
-       }
-
-       return wrapper;
-};
-
-/**
- * Get the document of an element.
- *
- * @static
- * @param {jQuery|HTMLElement|HTMLDocument|Window} obj Object to get the document for
- * @return {HTMLDocument|null} Document object
- */
-OO.ui.Element.static.getDocument = function ( obj ) {
-       // jQuery - selections created "offscreen" won't have a context, so .context isn't reliable
-       return ( obj[ 0 ] && obj[ 0 ].ownerDocument ) ||
-               // Empty jQuery selections might have a context
-               obj.context ||
-               // HTMLElement
-               obj.ownerDocument ||
-               // Window
-               obj.document ||
-               // HTMLDocument
-               ( obj.nodeType === 9 && obj ) ||
-               null;
-};
-
-/**
- * Get the window of an element or document.
- *
- * @static
- * @param {jQuery|HTMLElement|HTMLDocument|Window} obj Context to get the window for
- * @return {Window} Window object
- */
-OO.ui.Element.static.getWindow = function ( obj ) {
-       var doc = this.getDocument( obj );
-       return doc.defaultView;
-};
-
-/**
- * Get the direction of an element or document.
- *
- * @static
- * @param {jQuery|HTMLElement|HTMLDocument|Window} obj Context to get the direction for
- * @return {string} Text direction, either 'ltr' or 'rtl'
- */
-OO.ui.Element.static.getDir = function ( obj ) {
-       var isDoc, isWin;
-
-       if ( obj instanceof jQuery ) {
-               obj = obj[ 0 ];
-       }
-       isDoc = obj.nodeType === 9;
-       isWin = obj.document !== undefined;
-       if ( isDoc || isWin ) {
-               if ( isWin ) {
-                       obj = obj.document;
-               }
-               obj = obj.body;
-       }
-       return $( obj ).css( 'direction' );
-};
-
-/**
- * Get the offset between two frames.
- *
- * TODO: Make this function not use recursion.
- *
- * @static
- * @param {Window} from Window of the child frame
- * @param {Window} [to=window] Window of the parent frame
- * @param {Object} [offset] Offset to start with, used internally
- * @return {Object} Offset object, containing left and top properties
- */
-OO.ui.Element.static.getFrameOffset = function ( from, to, offset ) {
-       var i, len, frames, frame, rect;
-
-       if ( !to ) {
-               to = window;
-       }
-       if ( !offset ) {
-               offset = { top: 0, left: 0 };
-       }
-       if ( from.parent === from ) {
-               return offset;
-       }
-
-       // Get iframe element
-       frames = from.parent.document.getElementsByTagName( 'iframe' );
-       for ( i = 0, len = frames.length; i < len; i++ ) {
-               if ( frames[ i ].contentWindow === from ) {
-                       frame = frames[ i ];
-                       break;
-               }
-       }
-
-       // Recursively accumulate offset values
-       if ( frame ) {
-               rect = frame.getBoundingClientRect();
-               offset.left += rect.left;
-               offset.top += rect.top;
-               if ( from !== to ) {
-                       this.getFrameOffset( from.parent, offset );
-               }
-       }
-       return offset;
-};
-
-/**
- * Get the offset between two elements.
- *
- * The two elements may be in a different frame, but in that case the frame $element is in must
- * be contained in the frame $anchor is in.
- *
- * @static
- * @param {jQuery} $element Element whose position to get
- * @param {jQuery} $anchor Element to get $element's position relative to
- * @return {Object} Translated position coordinates, containing top and left properties
- */
-OO.ui.Element.static.getRelativePosition = function ( $element, $anchor ) {
-       var iframe, iframePos,
-               pos = $element.offset(),
-               anchorPos = $anchor.offset(),
-               elementDocument = this.getDocument( $element ),
-               anchorDocument = this.getDocument( $anchor );
-
-       // If $element isn't in the same document as $anchor, traverse up
-       while ( elementDocument !== anchorDocument ) {
-               iframe = elementDocument.defaultView.frameElement;
-               if ( !iframe ) {
-                       throw new Error( '$element frame is not contained in $anchor frame' );
-               }
-               iframePos = $( iframe ).offset();
-               pos.left += iframePos.left;
-               pos.top += iframePos.top;
-               elementDocument = iframe.ownerDocument;
-       }
-       pos.left -= anchorPos.left;
-       pos.top -= anchorPos.top;
-       return pos;
-};
-
-/**
- * Get element border sizes.
- *
- * @static
- * @param {HTMLElement} el Element to measure
- * @return {Object} Dimensions object with `top`, `left`, `bottom` and `right` properties
- */
-OO.ui.Element.static.getBorders = function ( el ) {
-       var doc = el.ownerDocument,
-               win = doc.defaultView,
-               style = win.getComputedStyle( el, null ),
-               $el = $( el ),
-               top = parseFloat( style ? style.borderTopWidth : $el.css( 'borderTopWidth' ) ) || 0,
-               left = parseFloat( style ? style.borderLeftWidth : $el.css( 'borderLeftWidth' ) ) || 0,
-               bottom = parseFloat( style ? style.borderBottomWidth : $el.css( 'borderBottomWidth' ) ) || 0,
-               right = parseFloat( style ? style.borderRightWidth : $el.css( 'borderRightWidth' ) ) || 0;
-
-       return {
-               top: top,
-               left: left,
-               bottom: bottom,
-               right: right
-       };
-};
-
-/**
- * Get dimensions of an element or window.
- *
- * @static
- * @param {HTMLElement|Window} el Element to measure
- * @return {Object} Dimensions object with `borders`, `scroll`, `scrollbar` and `rect` properties
- */
-OO.ui.Element.static.getDimensions = function ( el ) {
-       var $el, $win,
-               doc = el.ownerDocument || el.document,
-               win = doc.defaultView;
-
-       if ( win === el || el === doc.documentElement ) {
-               $win = $( win );
-               return {
-                       borders: { top: 0, left: 0, bottom: 0, right: 0 },
-                       scroll: {
-                               top: $win.scrollTop(),
-                               left: $win.scrollLeft()
-                       },
-                       scrollbar: { right: 0, bottom: 0 },
-                       rect: {
-                               top: 0,
-                               left: 0,
-                               bottom: $win.innerHeight(),
-                               right: $win.innerWidth()
-                       }
-               };
-       } else {
-               $el = $( el );
-               return {
-                       borders: this.getBorders( el ),
-                       scroll: {
-                               top: $el.scrollTop(),
-                               left: $el.scrollLeft()
-                       },
-                       scrollbar: {
-                               right: $el.innerWidth() - el.clientWidth,
-                               bottom: $el.innerHeight() - el.clientHeight
-                       },
-                       rect: el.getBoundingClientRect()
-               };
-       }
-};
-
-/**
- * Get scrollable object parent
- *
- * documentElement can't be used to get or set the scrollTop
- * property on Blink. Changing and testing its value lets us
- * use 'body' or 'documentElement' based on what is working.
- *
- * https://code.google.com/p/chromium/issues/detail?id=303131
- *
- * @static
- * @param {HTMLElement} el Element to find scrollable parent for
- * @return {HTMLElement} Scrollable parent
- */
-OO.ui.Element.static.getRootScrollableElement = function ( el ) {
-       var scrollTop, body;
-
-       if ( OO.ui.scrollableElement === undefined ) {
-               body = el.ownerDocument.body;
-               scrollTop = body.scrollTop;
-               body.scrollTop = 1;
-
-               if ( body.scrollTop === 1 ) {
-                       body.scrollTop = scrollTop;
-                       OO.ui.scrollableElement = 'body';
-               } else {
-                       OO.ui.scrollableElement = 'documentElement';
-               }
-       }
-
-       return el.ownerDocument[ OO.ui.scrollableElement ];
-};
-
-/**
- * Get closest scrollable container.
- *
- * Traverses up until either a scrollable element or the root is reached, in which case the window
- * will be returned.
- *
- * @static
- * @param {HTMLElement} el Element to find scrollable container for
- * @param {string} [dimension] Dimension of scrolling to look for; `x`, `y` or omit for either
- * @return {HTMLElement} Closest scrollable container
- */
-OO.ui.Element.static.getClosestScrollableContainer = function ( el, dimension ) {
-       var i, val,
-               // props = [ 'overflow' ] doesn't work due to https://bugzilla.mozilla.org/show_bug.cgi?id=889091
-               props = [ 'overflow-x', 'overflow-y' ],
-               $parent = $( el ).parent();
-
-       if ( dimension === 'x' || dimension === 'y' ) {
-               props = [ 'overflow-' + dimension ];
-       }
-
-       while ( $parent.length ) {
-               if ( $parent[ 0 ] === this.getRootScrollableElement( el ) ) {
-                       return $parent[ 0 ];
-               }
-               i = props.length;
-               while ( i-- ) {
-                       val = $parent.css( props[ i ] );
-                       if ( val === 'auto' || val === 'scroll' ) {
-                               return $parent[ 0 ];
-                       }
-               }
-               $parent = $parent.parent();
-       }
-       return this.getDocument( el ).body;
-};
-
-/**
- * Scroll element into view.
- *
- * @static
- * @param {HTMLElement} el Element to scroll into view
- * @param {Object} [config] Configuration options
- * @param {string} [config.duration] jQuery animation duration value
- * @param {string} [config.direction] Scroll in only one direction, e.g. 'x' or 'y', omit
- *  to scroll in both directions
- * @param {Function} [config.complete] Function to call when scrolling completes
- */
-OO.ui.Element.static.scrollIntoView = function ( el, config ) {
-       var rel, anim, callback, sc, $sc, eld, scd, $win;
-
-       // Configuration initialization
-       config = config || {};
-
-       anim = {};
-       callback = typeof config.complete === 'function' && config.complete;
-       sc = this.getClosestScrollableContainer( el, config.direction );
-       $sc = $( sc );
-       eld = this.getDimensions( el );
-       scd = this.getDimensions( sc );
-       $win = $( this.getWindow( el ) );
-
-       // Compute the distances between the edges of el and the edges of the scroll viewport
-       if ( $sc.is( 'html, body' ) ) {
-               // If the scrollable container is the root, this is easy
-               rel = {
-                       top: eld.rect.top,
-                       bottom: $win.innerHeight() - eld.rect.bottom,
-                       left: eld.rect.left,
-                       right: $win.innerWidth() - eld.rect.right
-               };
-       } else {
-               // Otherwise, we have to subtract el's coordinates from sc's coordinates
-               rel = {
-                       top: eld.rect.top - ( scd.rect.top + scd.borders.top ),
-                       bottom: scd.rect.bottom - scd.borders.bottom - scd.scrollbar.bottom - eld.rect.bottom,
-                       left: eld.rect.left - ( scd.rect.left + scd.borders.left ),
-                       right: scd.rect.right - scd.borders.right - scd.scrollbar.right - eld.rect.right
-               };
-       }
-
-       if ( !config.direction || config.direction === 'y' ) {
-               if ( rel.top < 0 ) {
-                       anim.scrollTop = scd.scroll.top + rel.top;
-               } else if ( rel.top > 0 && rel.bottom < 0 ) {
-                       anim.scrollTop = scd.scroll.top + Math.min( rel.top, -rel.bottom );
-               }
-       }
-       if ( !config.direction || config.direction === 'x' ) {
-               if ( rel.left < 0 ) {
-                       anim.scrollLeft = scd.scroll.left + rel.left;
-               } else if ( rel.left > 0 && rel.right < 0 ) {
-                       anim.scrollLeft = scd.scroll.left + Math.min( rel.left, -rel.right );
-               }
-       }
-       if ( !$.isEmptyObject( anim ) ) {
-               $sc.stop( true ).animate( anim, config.duration || 'fast' );
-               if ( callback ) {
-                       $sc.queue( function ( next ) {
-                               callback();
-                               next();
-                       } );
-               }
-       } else {
-               if ( callback ) {
-                       callback();
-               }
-       }
-};
-
-/**
- * Force the browser to reconsider whether it really needs to render scrollbars inside the element
- * and reserve space for them, because it probably doesn't.
- *
- * Workaround primarily for <https://code.google.com/p/chromium/issues/detail?id=387290>, but also
- * similar bugs in other browsers. "Just" forcing a reflow is not sufficient in all cases, we need
- * to first actually detach (or hide, but detaching is simpler) all children, *then* force a reflow,
- * and then reattach (or show) them back.
- *
- * @static
- * @param {HTMLElement} el Element to reconsider the scrollbars on
- */
-OO.ui.Element.static.reconsiderScrollbars = function ( el ) {
-       var i, len, scrollLeft, scrollTop, nodes = [];
-       // Save scroll position
-       scrollLeft = el.scrollLeft;
-       scrollTop = el.scrollTop;
-       // Detach all children
-       while ( el.firstChild ) {
-               nodes.push( el.firstChild );
-               el.removeChild( el.firstChild );
-       }
-       // Force reflow
-       void el.offsetHeight;
-       // Reattach all children
-       for ( i = 0, len = nodes.length; i < len; i++ ) {
-               el.appendChild( nodes[ i ] );
-       }
-       // Restore scroll position (no-op if scrollbars disappeared)
-       el.scrollLeft = scrollLeft;
-       el.scrollTop = scrollTop;
-};
-
-/* Methods */
-
-/**
- * Toggle visibility of an element.
- *
- * @param {boolean} [show] Make element visible, omit to toggle visibility
- * @fires visible
- * @chainable
- */
-OO.ui.Element.prototype.toggle = function ( show ) {
-       show = show === undefined ? !this.visible : !!show;
-
-       if ( show !== this.isVisible() ) {
-               this.visible = show;
-               this.$element.toggleClass( 'oo-ui-element-hidden', !this.visible );
-               this.emit( 'toggle', show );
-       }
-
-       return this;
-};
-
-/**
- * Check if element is visible.
- *
- * @return {boolean} element is visible
- */
-OO.ui.Element.prototype.isVisible = function () {
-       return this.visible;
-};
-
-/**
- * Get element data.
- *
- * @return {Mixed} Element data
- */
-OO.ui.Element.prototype.getData = function () {
-       return this.data;
-};
-
-/**
- * Set element data.
- *
- * @param {Mixed} Element data
- * @chainable
- */
-OO.ui.Element.prototype.setData = function ( data ) {
-       this.data = data;
-       return this;
-};
-
-/**
- * Check if element supports one or more methods.
- *
- * @param {string|string[]} methods Method or list of methods to check
- * @return {boolean} All methods are supported
- */
-OO.ui.Element.prototype.supports = function ( methods ) {
-       var i, len,
-               support = 0;
-
-       methods = Array.isArray( methods ) ? methods : [ methods ];
-       for ( i = 0, len = methods.length; i < len; i++ ) {
-               if ( $.isFunction( this[ methods[ i ] ] ) ) {
-                       support++;
-               }
-       }
-
-       return methods.length === support;
-};
-
-/**
- * Update the theme-provided classes.
- *
- * @localdoc This is called in element mixins and widget classes any time state changes.
- *   Updating is debounced, minimizing overhead of changing multiple attributes and
- *   guaranteeing that theme updates do not occur within an element's constructor
- */
-OO.ui.Element.prototype.updateThemeClasses = function () {
-       this.debouncedUpdateThemeClassesHandler();
-};
-
-/**
- * @private
- * @localdoc This method is called directly from the QUnit tests instead of #updateThemeClasses, to
- *   make them synchronous.
- */
-OO.ui.Element.prototype.debouncedUpdateThemeClasses = function () {
-       OO.ui.theme.updateElementClasses( this );
-};
-
-/**
- * Get the HTML tag name.
- *
- * Override this method to base the result on instance information.
- *
- * @return {string} HTML tag name
- */
-OO.ui.Element.prototype.getTagName = function () {
-       return this.constructor.static.tagName;
-};
-
-/**
- * Check if the element is attached to the DOM
- * @return {boolean} The element is attached to the DOM
- */
-OO.ui.Element.prototype.isElementAttached = function () {
-       return $.contains( this.getElementDocument(), this.$element[ 0 ] );
-};
-
-/**
- * Get the DOM document.
- *
- * @return {HTMLDocument} Document object
- */
-OO.ui.Element.prototype.getElementDocument = function () {
-       // Don't cache this in other ways either because subclasses could can change this.$element
-       return OO.ui.Element.static.getDocument( this.$element );
-};
-
-/**
- * Get the DOM window.
- *
- * @return {Window} Window object
- */
-OO.ui.Element.prototype.getElementWindow = function () {
-       return OO.ui.Element.static.getWindow( this.$element );
-};
-
-/**
- * Get closest scrollable container.
- */
-OO.ui.Element.prototype.getClosestScrollableElementContainer = function () {
-       return OO.ui.Element.static.getClosestScrollableContainer( this.$element[ 0 ] );
-};
-
-/**
- * Get group element is in.
- *
- * @return {OO.ui.mixin.GroupElement|null} Group element, null if none
- */
-OO.ui.Element.prototype.getElementGroup = function () {
-       return this.elementGroup;
-};
-
-/**
- * Set group element is in.
- *
- * @param {OO.ui.mixin.GroupElement|null} group Group element, null if none
- * @chainable
- */
-OO.ui.Element.prototype.setElementGroup = function ( group ) {
-       this.elementGroup = group;
-       return this;
-};
-
-/**
- * Scroll element into view.
- *
- * @param {Object} [config] Configuration options
- */
-OO.ui.Element.prototype.scrollElementIntoView = function ( config ) {
-       return OO.ui.Element.static.scrollIntoView( this.$element[ 0 ], config );
-};
-
-/**
- * Restore the pre-infusion dynamic state for this widget.
- *
- * This method is called after #$element has been inserted into DOM. The parameter is the return
- * value of #gatherPreInfuseState.
- *
- * @protected
- * @param {Object} state
- */
-OO.ui.Element.prototype.restorePreInfuseState = function () {
-};
-
-/**
- * Layouts are containers for elements and are used to arrange other widgets of arbitrary type in a way
- * that is centrally controlled and can be updated dynamically. Layouts can be, and usually are, combined.
- * See {@link OO.ui.FieldsetLayout FieldsetLayout}, {@link OO.ui.FieldLayout FieldLayout}, {@link OO.ui.FormLayout FormLayout},
- * {@link OO.ui.PanelLayout PanelLayout}, {@link OO.ui.StackLayout StackLayout}, {@link OO.ui.PageLayout PageLayout},
- * {@link OO.ui.HorizontalLayout HorizontalLayout}, and {@link OO.ui.BookletLayout BookletLayout} for more information and examples.
- *
- * @abstract
- * @class
- * @extends OO.ui.Element
- * @mixins OO.EventEmitter
- *
- * @constructor
- * @param {Object} [config] Configuration options
- */
-OO.ui.Layout = function OoUiLayout( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Parent constructor
-       OO.ui.Layout.parent.call( this, config );
-
-       // Mixin constructors
-       OO.EventEmitter.call( this );
-
-       // Initialization
-       this.$element.addClass( 'oo-ui-layout' );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.Layout, OO.ui.Element );
-OO.mixinClass( OO.ui.Layout, OO.EventEmitter );
-
-/**
- * Widgets are compositions of one or more OOjs UI elements that users can both view
- * and interact with. All widgets can be configured and modified via a standard API,
- * and their state can change dynamically according to a model.
- *
- * @abstract
- * @class
- * @extends OO.ui.Element
- * @mixins OO.EventEmitter
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {boolean} [disabled=false] Disable the widget. Disabled widgets cannot be used and their
- *  appearance reflects this state.
- */
-OO.ui.Widget = function OoUiWidget( config ) {
-       // Initialize config
-       config = $.extend( { disabled: false }, config );
-
-       // Parent constructor
-       OO.ui.Widget.parent.call( this, config );
-
-       // Mixin constructors
-       OO.EventEmitter.call( this );
-
-       // Properties
-       this.disabled = null;
-       this.wasDisabled = null;
-
-       // Initialization
-       this.$element.addClass( 'oo-ui-widget' );
-       this.setDisabled( !!config.disabled );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.Widget, OO.ui.Element );
-OO.mixinClass( OO.ui.Widget, OO.EventEmitter );
-
-/* Static Properties */
-
-/**
- * Whether this widget will behave reasonably when wrapped in a HTML `<label>`. If this is true,
- * wrappers such as OO.ui.FieldLayout may use a `<label>` instead of implementing own label click
- * handling.
- *
- * @static
- * @inheritable
- * @property {boolean}
- */
-OO.ui.Widget.static.supportsSimpleLabel = false;
-
-/* Events */
-
-/**
- * @event disable
- *
- * A 'disable' event is emitted when the disabled state of the widget changes
- * (i.e. on disable **and** enable).
- *
- * @param {boolean} disabled Widget is disabled
- */
-
-/**
- * @event toggle
- *
- * A 'toggle' event is emitted when the visibility of the widget changes.
- *
- * @param {boolean} visible Widget is visible
- */
-
-/* Methods */
-
-/**
- * Check if the widget is disabled.
- *
- * @return {boolean} Widget is disabled
- */
-OO.ui.Widget.prototype.isDisabled = function () {
-       return this.disabled;
-};
-
-/**
- * Set the 'disabled' state of the widget.
- *
- * When a widget is disabled, it cannot be used and its appearance is updated to reflect this state.
- *
- * @param {boolean} disabled Disable widget
- * @chainable
- */
-OO.ui.Widget.prototype.setDisabled = function ( disabled ) {
-       var isDisabled;
-
-       this.disabled = !!disabled;
-       isDisabled = this.isDisabled();
-       if ( isDisabled !== this.wasDisabled ) {
-               this.$element.toggleClass( 'oo-ui-widget-disabled', isDisabled );
-               this.$element.toggleClass( 'oo-ui-widget-enabled', !isDisabled );
-               this.$element.attr( 'aria-disabled', isDisabled.toString() );
-               this.emit( 'disable', isDisabled );
-               this.updateThemeClasses();
-       }
-       this.wasDisabled = isDisabled;
-
-       return this;
-};
-
-/**
- * Update the disabled state, in case of changes in parent widget.
- *
- * @chainable
- */
-OO.ui.Widget.prototype.updateDisabled = function () {
-       this.setDisabled( this.disabled );
-       return this;
-};
-
-/**
- * A window is a container for elements that are in a child frame. They are used with
- * a window manager (OO.ui.WindowManager), which is used to open and close the window and control
- * its presentation. The size of a window is specified using a symbolic name (e.g., ‘small’, ‘medium’,
- * ‘large’), which is interpreted by the window manager. If the requested size is not recognized,
- * the window manager will choose a sensible fallback.
- *
- * The lifecycle of a window has three primary stages (opening, opened, and closing) in which
- * different processes are executed:
- *
- * **opening**: The opening stage begins when the window manager's {@link OO.ui.WindowManager#openWindow
- * openWindow} or the window's {@link #open open} methods are used, and the window manager begins to open
- * the window.
- *
- * - {@link #getSetupProcess} method is called and its result executed
- * - {@link #getReadyProcess} method is called and its result executed
- *
- * **opened**: The window is now open
- *
- * **closing**: The closing stage begins when the window manager's
- * {@link OO.ui.WindowManager#closeWindow closeWindow}
- * or the window's {@link #close} methods are used, and the window manager begins to close the window.
- *
- * - {@link #getHoldProcess} method is called and its result executed
- * - {@link #getTeardownProcess} method is called and its result executed. The window is now closed
- *
- * Each of the window's processes (setup, ready, hold, and teardown) can be extended in subclasses
- * by overriding the window's #getSetupProcess, #getReadyProcess, #getHoldProcess and #getTeardownProcess
- * methods. Note that each {@link OO.ui.Process process} is executed in series, so asynchronous
- * processing can complete. Always assume window processes are executed asynchronously.
- *
- * For more information, please see the [OOjs UI documentation on MediaWiki] [1].
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Windows
- *
- * @abstract
- * @class
- * @extends OO.ui.Element
- * @mixins OO.EventEmitter
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {string} [size] Symbolic name of the dialog size: `small`, `medium`, `large`, `larger` or
- *  `full`.  If omitted, the value of the {@link #static-size static size} property will be used.
- */
-OO.ui.Window = function OoUiWindow( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Parent constructor
-       OO.ui.Window.parent.call( this, config );
-
-       // Mixin constructors
-       OO.EventEmitter.call( this );
-
-       // Properties
-       this.manager = null;
-       this.size = config.size || this.constructor.static.size;
-       this.$frame = $( '<div>' );
-       this.$overlay = $( '<div>' );
-       this.$content = $( '<div>' );
-
-       this.$focusTrapBefore = $( '<div>' ).prop( 'tabIndex', 0 );
-       this.$focusTrapAfter = $( '<div>' ).prop( 'tabIndex', 0 );
-       this.$focusTraps = this.$focusTrapBefore.add( this.$focusTrapAfter );
-
-       // Initialization
-       this.$overlay.addClass( 'oo-ui-window-overlay' );
-       this.$content
-               .addClass( 'oo-ui-window-content' )
-               .attr( 'tabindex', 0 );
-       this.$frame
-               .addClass( 'oo-ui-window-frame' )
-               .append( this.$focusTrapBefore, this.$content, this.$focusTrapAfter );
-
-       this.$element
-               .addClass( 'oo-ui-window' )
-               .append( this.$frame, this.$overlay );
-
-       // Initially hidden - using #toggle may cause errors if subclasses override toggle with methods
-       // that reference properties not initialized at that time of parent class construction
-       // TODO: Find a better way to handle post-constructor setup
-       this.visible = false;
-       this.$element.addClass( 'oo-ui-element-hidden' );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.Window, OO.ui.Element );
-OO.mixinClass( OO.ui.Window, OO.EventEmitter );
-
-/* Static Properties */
-
-/**
- * Symbolic name of the window size: `small`, `medium`, `large`, `larger` or `full`.
- *
- * The static size is used if no #size is configured during construction.
- *
- * @static
- * @inheritable
- * @property {string}
- */
-OO.ui.Window.static.size = 'medium';
-
-/* Methods */
-
-/**
- * Handle mouse down events.
- *
- * @private
- * @param {jQuery.Event} e Mouse down event
- */
-OO.ui.Window.prototype.onMouseDown = function ( e ) {
-       // Prevent clicking on the click-block from stealing focus
-       if ( e.target === this.$element[ 0 ] ) {
-               return false;
-       }
-};
-
-/**
- * Check if the window has been initialized.
- *
- * Initialization occurs when a window is added to a manager.
- *
- * @return {boolean} Window has been initialized
- */
-OO.ui.Window.prototype.isInitialized = function () {
-       return !!this.manager;
-};
-
-/**
- * Check if the window is visible.
- *
- * @return {boolean} Window is visible
- */
-OO.ui.Window.prototype.isVisible = function () {
-       return this.visible;
-};
-
-/**
- * Check if the window is opening.
- *
- * This method is a wrapper around the window manager's {@link OO.ui.WindowManager#isOpening isOpening}
- * method.
- *
- * @return {boolean} Window is opening
- */
-OO.ui.Window.prototype.isOpening = function () {
-       return this.manager.isOpening( this );
-};
-
-/**
- * Check if the window is closing.
- *
- * This method is a wrapper around the window manager's {@link OO.ui.WindowManager#isClosing isClosing} method.
- *
- * @return {boolean} Window is closing
- */
-OO.ui.Window.prototype.isClosing = function () {
-       return this.manager.isClosing( this );
-};
-
-/**
- * Check if the window is opened.
- *
- * This method is a wrapper around the window manager's {@link OO.ui.WindowManager#isOpened isOpened} method.
- *
- * @return {boolean} Window is opened
- */
-OO.ui.Window.prototype.isOpened = function () {
-       return this.manager.isOpened( this );
-};
-
-/**
- * Get the window manager.
- *
- * All windows must be attached to a window manager, which is used to open
- * and close the window and control its presentation.
- *
- * @return {OO.ui.WindowManager} Manager of window
- */
-OO.ui.Window.prototype.getManager = function () {
-       return this.manager;
-};
-
-/**
- * Get the symbolic name of the window size (e.g., `small` or `medium`).
- *
- * @return {string} Symbolic name of the size: `small`, `medium`, `large`, `larger`, `full`
- */
-OO.ui.Window.prototype.getSize = function () {
-       var viewport = OO.ui.Element.static.getDimensions( this.getElementWindow() ),
-               sizes = this.manager.constructor.static.sizes,
-               size = this.size;
-
-       if ( !sizes[ size ] ) {
-               size = this.manager.constructor.static.defaultSize;
-       }
-       if ( size !== 'full' && viewport.rect.right - viewport.rect.left < sizes[ size ].width ) {
-               size = 'full';
-       }
-
-       return size;
-};
-
-/**
- * Get the size properties associated with the current window size
- *
- * @return {Object} Size properties
- */
-OO.ui.Window.prototype.getSizeProperties = function () {
-       return this.manager.constructor.static.sizes[ this.getSize() ];
-};
-
-/**
- * Disable transitions on window's frame for the duration of the callback function, then enable them
- * back.
- *
- * @private
- * @param {Function} callback Function to call while transitions are disabled
- */
-OO.ui.Window.prototype.withoutSizeTransitions = function ( callback ) {
-       // Temporarily resize the frame so getBodyHeight() can use scrollHeight measurements.
-       // Disable transitions first, otherwise we'll get values from when the window was animating.
-       var oldTransition,
-               styleObj = this.$frame[ 0 ].style;
-       oldTransition = styleObj.transition || styleObj.OTransition || styleObj.MsTransition ||
-               styleObj.MozTransition || styleObj.WebkitTransition;
-       styleObj.transition = styleObj.OTransition = styleObj.MsTransition =
-               styleObj.MozTransition = styleObj.WebkitTransition = 'none';
-       callback();
-       // Force reflow to make sure the style changes done inside callback really are not transitioned
-       this.$frame.height();
-       styleObj.transition = styleObj.OTransition = styleObj.MsTransition =
-               styleObj.MozTransition = styleObj.WebkitTransition = oldTransition;
-};
-
-/**
- * Get the height of the full window contents (i.e., the window head, body and foot together).
- *
- * What consistitutes the head, body, and foot varies depending on the window type.
- * A {@link OO.ui.MessageDialog message dialog} displays a title and message in its body,
- * and any actions in the foot. A {@link OO.ui.ProcessDialog process dialog} displays a title
- * and special actions in the head, and dialog content in the body.
- *
- * To get just the height of the dialog body, use the #getBodyHeight method.
- *
- * @return {number} The height of the window contents (the dialog head, body and foot) in pixels
- */
-OO.ui.Window.prototype.getContentHeight = function () {
-       var bodyHeight,
-               win = this,
-               bodyStyleObj = this.$body[ 0 ].style,
-               frameStyleObj = this.$frame[ 0 ].style;
-
-       // Temporarily resize the frame so getBodyHeight() can use scrollHeight measurements.
-       // Disable transitions first, otherwise we'll get values from when the window was animating.
-       this.withoutSizeTransitions( function () {
-               var oldHeight = frameStyleObj.height,
-                       oldPosition = bodyStyleObj.position;
-               frameStyleObj.height = '1px';
-               // Force body to resize to new width
-               bodyStyleObj.position = 'relative';
-               bodyHeight = win.getBodyHeight();
-               frameStyleObj.height = oldHeight;
-               bodyStyleObj.position = oldPosition;
-       } );
-
-       return (
-               // Add buffer for border
-               ( this.$frame.outerHeight() - this.$frame.innerHeight() ) +
-               // Use combined heights of children
-               ( this.$head.outerHeight( true ) + bodyHeight + this.$foot.outerHeight( true ) )
-       );
-};
-
-/**
- * Get the height of the window body.
- *
- * To get the height of the full window contents (the window body, head, and foot together),
- * use #getContentHeight.
- *
- * When this function is called, the window will temporarily have been resized
- * to height=1px, so .scrollHeight measurements can be taken accurately.
- *
- * @return {number} Height of the window body in pixels
- */
-OO.ui.Window.prototype.getBodyHeight = function () {
-       return this.$body[ 0 ].scrollHeight;
-};
-
-/**
- * Get the directionality of the frame (right-to-left or left-to-right).
- *
- * @return {string} Directionality: `'ltr'` or `'rtl'`
- */
-OO.ui.Window.prototype.getDir = function () {
-       return OO.ui.Element.static.getDir( this.$content ) || 'ltr';
-};
-
-/**
- * Get the 'setup' process.
- *
- * The setup process is used to set up a window for use in a particular context,
- * based on the `data` argument. This method is called during the opening phase of the window’s
- * lifecycle.
- *
- * Override this method to add additional steps to the ‘setup’ process the parent method provides
- * using the {@link OO.ui.Process#first first} and {@link OO.ui.Process#next next} methods
- * of OO.ui.Process.
- *
- * To add window content that persists between openings, you may wish to use the #initialize method
- * instead.
- *
- * @param {Object} [data] Window opening data
- * @return {OO.ui.Process} Setup process
- */
-OO.ui.Window.prototype.getSetupProcess = function () {
-       return new OO.ui.Process();
-};
-
-/**
- * Get the ‘ready’ process.
- *
- * The ready process is used to ready a window for use in a particular
- * context, based on the `data` argument. This method is called during the opening phase of
- * the window’s lifecycle, after the window has been {@link #getSetupProcess setup}.
- *
- * Override this method to add additional steps to the ‘ready’ process the parent method
- * provides using the {@link OO.ui.Process#first first} and {@link OO.ui.Process#next next}
- * methods of OO.ui.Process.
- *
- * @param {Object} [data] Window opening data
- * @return {OO.ui.Process} Ready process
- */
-OO.ui.Window.prototype.getReadyProcess = function () {
-       return new OO.ui.Process();
-};
-
-/**
- * Get the 'hold' process.
- *
- * The hold proccess is used to keep a window from being used in a particular context,
- * based on the `data` argument. This method is called during the closing phase of the window’s
- * lifecycle.
- *
- * Override this method to add additional steps to the 'hold' process the parent method provides
- * using the {@link OO.ui.Process#first first} and {@link OO.ui.Process#next next} methods
- * of OO.ui.Process.
- *
- * @param {Object} [data] Window closing data
- * @return {OO.ui.Process} Hold process
- */
-OO.ui.Window.prototype.getHoldProcess = function () {
-       return new OO.ui.Process();
-};
-
-/**
- * Get the ‘teardown’ process.
- *
- * The teardown process is used to teardown a window after use. During teardown,
- * user interactions within the window are conveyed and the window is closed, based on the `data`
- * argument. This method is called during the closing phase of the window’s lifecycle.
- *
- * Override this method to add additional steps to the ‘teardown’ process the parent method provides
- * using the {@link OO.ui.Process#first first} and {@link OO.ui.Process#next next} methods
- * of OO.ui.Process.
- *
- * @param {Object} [data] Window closing data
- * @return {OO.ui.Process} Teardown process
- */
-OO.ui.Window.prototype.getTeardownProcess = function () {
-       return new OO.ui.Process();
-};
-
-/**
- * Set the window manager.
- *
- * This will cause the window to initialize. Calling it more than once will cause an error.
- *
- * @param {OO.ui.WindowManager} manager Manager for this window
- * @throws {Error} An error is thrown if the method is called more than once
- * @chainable
- */
-OO.ui.Window.prototype.setManager = function ( manager ) {
-       if ( this.manager ) {
-               throw new Error( 'Cannot set window manager, window already has a manager' );
-       }
-
-       this.manager = manager;
-       this.initialize();
-
-       return this;
-};
-
-/**
- * Set the window size by symbolic name (e.g., 'small' or 'medium')
- *
- * @param {string} size Symbolic name of size: `small`, `medium`, `large`, `larger` or
- *  `full`
- * @chainable
- */
-OO.ui.Window.prototype.setSize = function ( size ) {
-       this.size = size;
-       this.updateSize();
-       return this;
-};
-
-/**
- * Update the window size.
- *
- * @throws {Error} An error is thrown if the window is not attached to a window manager
- * @chainable
- */
-OO.ui.Window.prototype.updateSize = function () {
-       if ( !this.manager ) {
-               throw new Error( 'Cannot update window size, must be attached to a manager' );
-       }
-
-       this.manager.updateWindowSize( this );
-
-       return this;
-};
-
-/**
- * Set window dimensions. This method is called by the {@link OO.ui.WindowManager window manager}
- * when the window is opening. In general, setDimensions should not be called directly.
- *
- * To set the size of the window, use the #setSize method.
- *
- * @param {Object} dim CSS dimension properties
- * @param {string|number} [dim.width] Width
- * @param {string|number} [dim.minWidth] Minimum width
- * @param {string|number} [dim.maxWidth] Maximum width
- * @param {string|number} [dim.width] Height, omit to set based on height of contents
- * @param {string|number} [dim.minWidth] Minimum height
- * @param {string|number} [dim.maxWidth] Maximum height
- * @chainable
- */
-OO.ui.Window.prototype.setDimensions = function ( dim ) {
-       var height,
-               win = this,
-               styleObj = this.$frame[ 0 ].style;
-
-       // Calculate the height we need to set using the correct width
-       if ( dim.height === undefined ) {
-               this.withoutSizeTransitions( function () {
-                       var oldWidth = styleObj.width;
-                       win.$frame.css( 'width', dim.width || '' );
-                       height = win.getContentHeight();
-                       styleObj.width = oldWidth;
-               } );
-       } else {
-               height = dim.height;
-       }
-
-       this.$frame.css( {
-               width: dim.width || '',
-               minWidth: dim.minWidth || '',
-               maxWidth: dim.maxWidth || '',
-               height: height || '',
-               minHeight: dim.minHeight || '',
-               maxHeight: dim.maxHeight || ''
-       } );
-
-       return this;
-};
-
-/**
- * Initialize window contents.
- *
- * Before the window is opened for the first time, #initialize is called so that content that
- * persists between openings can be added to the window.
- *
- * To set up a window with new content each time the window opens, use #getSetupProcess.
- *
- * @throws {Error} An error is thrown if the window is not attached to a window manager
- * @chainable
- */
-OO.ui.Window.prototype.initialize = function () {
-       if ( !this.manager ) {
-               throw new Error( 'Cannot initialize window, must be attached to a manager' );
-       }
-
-       // Properties
-       this.$head = $( '<div>' );
-       this.$body = $( '<div>' );
-       this.$foot = $( '<div>' );
-       this.$document = $( this.getElementDocument() );
-
-       // Events
-       this.$element.on( 'mousedown', this.onMouseDown.bind( this ) );
-
-       // Initialization
-       this.$head.addClass( 'oo-ui-window-head' );
-       this.$body.addClass( 'oo-ui-window-body' );
-       this.$foot.addClass( 'oo-ui-window-foot' );
-       this.$content.append( this.$head, this.$body, this.$foot );
-
-       return this;
-};
-
-/**
- * Called when someone tries to focus the hidden element at the end of the dialog.
- * Sends focus back to the start of the dialog.
- *
- * @param {jQuery.Event} event Focus event
- */
-OO.ui.Window.prototype.onFocusTrapFocused = function ( event ) {
-       if ( this.$focusTrapBefore.is( event.target ) ) {
-               OO.ui.findFocusable( this.$content, true ).focus();
-       } else {
-               // this.$content is the part of the focus cycle, and is the first focusable element
-               this.$content.focus();
-       }
-};
-
-/**
- * Open the window.
- *
- * This method is a wrapper around a call to the window manager’s {@link OO.ui.WindowManager#openWindow openWindow}
- * method, which returns a promise resolved when the window is done opening.
- *
- * To customize the window each time it opens, use #getSetupProcess or #getReadyProcess.
- *
- * @param {Object} [data] Window opening data
- * @return {jQuery.Promise} Promise resolved with a value when the window is opened, or rejected
- *  if the window fails to open. When the promise is resolved successfully, the first argument of the
- *  value is a new promise, which is resolved when the window begins closing.
- * @throws {Error} An error is thrown if the window is not attached to a window manager
- */
-OO.ui.Window.prototype.open = function ( data ) {
-       if ( !this.manager ) {
-               throw new Error( 'Cannot open window, must be attached to a manager' );
-       }
-
-       return this.manager.openWindow( this, data );
-};
-
-/**
- * Close the window.
- *
- * This method is a wrapper around a call to the window
- * manager’s {@link OO.ui.WindowManager#closeWindow closeWindow} method,
- * which returns a closing promise resolved when the window is done closing.
- *
- * The window's #getHoldProcess and #getTeardownProcess methods are called during the closing
- * phase of the window’s lifecycle and can be used to specify closing behavior each time
- * the window closes.
- *
- * @param {Object} [data] Window closing data
- * @return {jQuery.Promise} Promise resolved when window is closed
- * @throws {Error} An error is thrown if the window is not attached to a window manager
- */
-OO.ui.Window.prototype.close = function ( data ) {
-       if ( !this.manager ) {
-               throw new Error( 'Cannot close window, must be attached to a manager' );
-       }
-
-       return this.manager.closeWindow( this, data );
-};
-
-/**
- * Setup window.
- *
- * This is called by OO.ui.WindowManager during window opening, and should not be called directly
- * by other systems.
- *
- * @param {Object} [data] Window opening data
- * @return {jQuery.Promise} Promise resolved when window is setup
- */
-OO.ui.Window.prototype.setup = function ( data ) {
-       var win = this;
-
-       this.toggle( true );
-
-       this.focusTrapHandler = OO.ui.bind( this.onFocusTrapFocused, this );
-       this.$focusTraps.on( 'focus', this.focusTrapHandler );
-
-       return this.getSetupProcess( data ).execute().then( function () {
-               // Force redraw by asking the browser to measure the elements' widths
-               win.$element.addClass( 'oo-ui-window-active oo-ui-window-setup' ).width();
-               win.$content.addClass( 'oo-ui-window-content-setup' ).width();
-       } );
-};
-
-/**
- * Ready window.
- *
- * This is called by OO.ui.WindowManager during window opening, and should not be called directly
- * by other systems.
- *
- * @param {Object} [data] Window opening data
- * @return {jQuery.Promise} Promise resolved when window is ready
- */
-OO.ui.Window.prototype.ready = function ( data ) {
-       var win = this;
-
-       this.$content.focus();
-       return this.getReadyProcess( data ).execute().then( function () {
-               // Force redraw by asking the browser to measure the elements' widths
-               win.$element.addClass( 'oo-ui-window-ready' ).width();
-               win.$content.addClass( 'oo-ui-window-content-ready' ).width();
-       } );
-};
-
-/**
- * Hold window.
- *
- * This is called by OO.ui.WindowManager during window closing, and should not be called directly
- * by other systems.
- *
- * @param {Object} [data] Window closing data
- * @return {jQuery.Promise} Promise resolved when window is held
- */
-OO.ui.Window.prototype.hold = function ( data ) {
-       var win = this;
-
-       return this.getHoldProcess( data ).execute().then( function () {
-               // Get the focused element within the window's content
-               var $focus = win.$content.find( OO.ui.Element.static.getDocument( win.$content ).activeElement );
-
-               // Blur the focused element
-               if ( $focus.length ) {
-                       $focus[ 0 ].blur();
-               }
-
-               // Force redraw by asking the browser to measure the elements' widths
-               win.$element.removeClass( 'oo-ui-window-ready' ).width();
-               win.$content.removeClass( 'oo-ui-window-content-ready' ).width();
-       } );
-};
-
-/**
- * Teardown window.
- *
- * This is called by OO.ui.WindowManager during window closing, and should not be called directly
- * by other systems.
- *
- * @param {Object} [data] Window closing data
- * @return {jQuery.Promise} Promise resolved when window is torn down
- */
-OO.ui.Window.prototype.teardown = function ( data ) {
-       var win = this;
-
-       return this.getTeardownProcess( data ).execute().then( function () {
-               // Force redraw by asking the browser to measure the elements' widths
-               win.$element.removeClass( 'oo-ui-window-active oo-ui-window-setup' ).width();
-               win.$content.removeClass( 'oo-ui-window-content-setup' ).width();
-               win.$focusTraps.off( 'focus', win.focusTrapHandler );
-               win.toggle( false );
-       } );
-};
-
-/**
- * The Dialog class serves as the base class for the other types of dialogs.
- * Unless extended to include controls, the rendered dialog box is a simple window
- * that users can close by hitting the ‘Esc’ key. Dialog windows are used with OO.ui.WindowManager,
- * which opens, closes, and controls the presentation of the window. See the
- * [OOjs UI documentation on MediaWiki] [1] for more information.
- *
- *     @example
- *     // A simple dialog window.
- *     function MyDialog( config ) {
- *         MyDialog.parent.call( this, config );
- *     }
- *     OO.inheritClass( MyDialog, OO.ui.Dialog );
- *     MyDialog.prototype.initialize = function () {
- *         MyDialog.parent.prototype.initialize.call( this );
- *         this.content = new OO.ui.PanelLayout( { padded: true, expanded: false } );
- *         this.content.$element.append( '<p>A simple dialog window. Press \'Esc\' to close.</p>' );
- *         this.$body.append( this.content.$element );
- *     };
- *     MyDialog.prototype.getBodyHeight = function () {
- *         return this.content.$element.outerHeight( true );
- *     };
- *     var myDialog = new MyDialog( {
- *         size: 'medium'
- *     } );
- *     // Create and append a window manager, which opens and closes the window.
- *     var windowManager = new OO.ui.WindowManager();
- *     $( 'body' ).append( windowManager.$element );
- *     windowManager.addWindows( [ myDialog ] );
- *     // Open the window!
- *     windowManager.openWindow( myDialog );
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Dialogs
- *
- * @abstract
- * @class
- * @extends OO.ui.Window
- * @mixins OO.ui.mixin.PendingElement
- *
- * @constructor
- * @param {Object} [config] Configuration options
- */
-OO.ui.Dialog = function OoUiDialog( config ) {
-       // Parent constructor
-       OO.ui.Dialog.parent.call( this, config );
-
-       // Mixin constructors
-       OO.ui.mixin.PendingElement.call( this );
-
-       // Properties
-       this.actions = new OO.ui.ActionSet();
-       this.attachedActions = [];
-       this.currentAction = null;
-       this.onDialogKeyDownHandler = this.onDialogKeyDown.bind( this );
-
-       // Events
-       this.actions.connect( this, {
-               click: 'onActionClick',
-               resize: 'onActionResize',
-               change: 'onActionsChange'
-       } );
-
-       // Initialization
-       this.$element
-               .addClass( 'oo-ui-dialog' )
-               .attr( 'role', 'dialog' );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.Dialog, OO.ui.Window );
-OO.mixinClass( OO.ui.Dialog, OO.ui.mixin.PendingElement );
-
-/* Static Properties */
-
-/**
- * Symbolic name of dialog.
- *
- * The dialog class must have a symbolic name in order to be registered with OO.Factory.
- * Please see the [OOjs UI documentation on MediaWiki] [3] for more information.
- *
- * [3]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Window_managers
- *
- * @abstract
- * @static
- * @inheritable
- * @property {string}
- */
-OO.ui.Dialog.static.name = '';
-
-/**
- * The dialog title.
- *
- * The title can be specified as a plaintext string, a {@link OO.ui.mixin.LabelElement Label} node, or a function
- * that will produce a Label node or string. The title can also be specified with data passed to the
- * constructor (see #getSetupProcess). In this case, the static value will be overridden.
- *
- * @abstract
- * @static
- * @inheritable
- * @property {jQuery|string|Function}
- */
-OO.ui.Dialog.static.title = '';
-
-/**
- * An array of configured {@link OO.ui.ActionWidget action widgets}.
- *
- * Actions can also be specified with data passed to the constructor (see #getSetupProcess). In this case, the static
- * value will be overridden.
- *
- * [2]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Process_Dialogs#Action_sets
- *
- * @static
- * @inheritable
- * @property {Object[]}
- */
-OO.ui.Dialog.static.actions = [];
-
-/**
- * Close the dialog when the 'Esc' key is pressed.
- *
- * @static
- * @abstract
- * @inheritable
- * @property {boolean}
- */
-OO.ui.Dialog.static.escapable = true;
-
-/* Methods */
-
-/**
- * Handle frame document key down events.
- *
- * @private
- * @param {jQuery.Event} e Key down event
- */
-OO.ui.Dialog.prototype.onDialogKeyDown = function ( e ) {
-       if ( e.which === OO.ui.Keys.ESCAPE ) {
-               this.executeAction( '' );
-               e.preventDefault();
-               e.stopPropagation();
-       }
-};
-
-/**
- * Handle action resized events.
- *
- * @private
- * @param {OO.ui.ActionWidget} action Action that was resized
- */
-OO.ui.Dialog.prototype.onActionResize = function () {
-       // Override in subclass
-};
-
-/**
- * Handle action click events.
- *
- * @private
- * @param {OO.ui.ActionWidget} action Action that was clicked
- */
-OO.ui.Dialog.prototype.onActionClick = function ( action ) {
-       if ( !this.isPending() ) {
-               this.executeAction( action.getAction() );
-       }
-};
-
-/**
- * Handle actions change event.
- *
- * @private
- */
-OO.ui.Dialog.prototype.onActionsChange = function () {
-       this.detachActions();
-       if ( !this.isClosing() ) {
-               this.attachActions();
-       }
-};
-
-/**
- * Get the set of actions used by the dialog.
- *
- * @return {OO.ui.ActionSet}
- */
-OO.ui.Dialog.prototype.getActions = function () {
-       return this.actions;
-};
-
-/**
- * Get a process for taking action.
- *
- * When you override this method, you can create a new OO.ui.Process and return it, or add additional
- * accept steps to the process the parent method provides using the {@link OO.ui.Process#first 'first'}
- * and {@link OO.ui.Process#next 'next'} methods of OO.ui.Process.
- *
- * @param {string} [action] Symbolic name of action
- * @return {OO.ui.Process} Action process
- */
-OO.ui.Dialog.prototype.getActionProcess = function ( action ) {
-       return new OO.ui.Process()
-               .next( function () {
-                       if ( !action ) {
-                               // An empty action always closes the dialog without data, which should always be
-                               // safe and make no changes
-                               this.close();
-                       }
-               }, this );
-};
-
-/**
- * @inheritdoc
- *
- * @param {Object} [data] Dialog opening data
- * @param {jQuery|string|Function|null} [data.title] Dialog title, omit to use
- *  the {@link #static-title static title}
- * @param {Object[]} [data.actions] List of configuration options for each
- *   {@link OO.ui.ActionWidget action widget}, omit to use {@link #static-actions static actions}.
- */
-OO.ui.Dialog.prototype.getSetupProcess = function ( data ) {
-       data = data || {};
-
-       // Parent method
-       return OO.ui.Dialog.parent.prototype.getSetupProcess.call( this, data )
-               .next( function () {
-                       var config = this.constructor.static,
-                               actions = data.actions !== undefined ? data.actions : config.actions;
-
-                       this.title.setLabel(
-                               data.title !== undefined ? data.title : this.constructor.static.title
-                       );
-                       this.actions.add( this.getActionWidgets( actions ) );
-
-                       if ( this.constructor.static.escapable ) {
-                               this.$element.on( 'keydown', this.onDialogKeyDownHandler );
-                       }
-               }, this );
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.Dialog.prototype.getTeardownProcess = function ( data ) {
-       // Parent method
-       return OO.ui.Dialog.parent.prototype.getTeardownProcess.call( this, data )
-               .first( function () {
-                       if ( this.constructor.static.escapable ) {
-                               this.$element.off( 'keydown', this.onDialogKeyDownHandler );
-                       }
-
-                       this.actions.clear();
-                       this.currentAction = null;
-               }, this );
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.Dialog.prototype.initialize = function () {
-       var titleId;
-
-       // Parent method
-       OO.ui.Dialog.parent.prototype.initialize.call( this );
-
-       titleId = OO.ui.generateElementId();
-
-       // Properties
-       this.title = new OO.ui.LabelWidget( {
-               id: titleId
-       } );
-
-       // Initialization
-       this.$content.addClass( 'oo-ui-dialog-content' );
-       this.$element.attr( 'aria-labelledby', titleId );
-       this.setPendingElement( this.$head );
-};
-
-/**
- * Get action widgets from a list of configs
- *
- * @param {Object[]} actions Action widget configs
- * @return {OO.ui.ActionWidget[]} Action widgets
- */
-OO.ui.Dialog.prototype.getActionWidgets = function ( actions ) {
-       var i, len, widgets = [];
-       for ( i = 0, len = actions.length; i < len; i++ ) {
-               widgets.push(
-                       new OO.ui.ActionWidget( actions[ i ] )
-               );
-       }
-       return widgets;
-};
-
-/**
- * Attach action actions.
- *
- * @protected
- */
-OO.ui.Dialog.prototype.attachActions = function () {
-       // Remember the list of potentially attached actions
-       this.attachedActions = this.actions.get();
-};
-
-/**
- * Detach action actions.
- *
- * @protected
- * @chainable
- */
-OO.ui.Dialog.prototype.detachActions = function () {
-       var i, len;
-
-       // Detach all actions that may have been previously attached
-       for ( i = 0, len = this.attachedActions.length; i < len; i++ ) {
-               this.attachedActions[ i ].$element.detach();
-       }
-       this.attachedActions = [];
-};
-
-/**
- * Execute an action.
- *
- * @param {string} action Symbolic name of action to execute
- * @return {jQuery.Promise} Promise resolved when action completes, rejected if it fails
- */
-OO.ui.Dialog.prototype.executeAction = function ( action ) {
-       this.pushPending();
-       this.currentAction = action;
-       return this.getActionProcess( action ).execute()
-               .always( this.popPending.bind( this ) );
-};
-
-/**
- * Window managers are used to open and close {@link OO.ui.Window windows} and control their presentation.
- * Managed windows are mutually exclusive. If a new window is opened while a current window is opening
- * or is opened, the current window will be closed and any ongoing {@link OO.ui.Process process} will be cancelled. Windows
- * themselves are persistent and—rather than being torn down when closed—can be repopulated with the
- * pertinent data and reused.
- *
- * Over the lifecycle of a window, the window manager makes available three promises: `opening`,
- * `opened`, and `closing`, which represent the primary stages of the cycle:
- *
- * **Opening**: the opening stage begins when the window manager’s #openWindow or a window’s
- * {@link OO.ui.Window#open open} method is used, and the window manager begins to open the window.
- *
- * - an `opening` event is emitted with an `opening` promise
- * - the #getSetupDelay method is called and the returned value is used to time a pause in execution before
- *   the window’s {@link OO.ui.Window#getSetupProcess getSetupProcess} method is called on the
- *   window and its result executed
- * - a `setup` progress notification is emitted from the `opening` promise
- * - the #getReadyDelay method is called the returned value is used to time a pause in execution before
- *   the window’s {@link OO.ui.Window#getReadyProcess getReadyProcess} method is called on the
- *   window and its result executed
- * - a `ready` progress notification is emitted from the `opening` promise
- * - the `opening` promise is resolved with an `opened` promise
- *
- * **Opened**: the window is now open.
- *
- * **Closing**: the closing stage begins when the window manager's #closeWindow or the
- * window's {@link OO.ui.Window#close close} methods is used, and the window manager begins
- * to close the window.
- *
- * - the `opened` promise is resolved with `closing` promise and a `closing` event is emitted
- * - the #getHoldDelay method is called and the returned value is used to time a pause in execution before
- *   the window's {@link OO.ui.Window#getHoldProcess getHoldProces} method is called on the
- *   window and its result executed
- * - a `hold` progress notification is emitted from the `closing` promise
- * - the #getTeardownDelay() method is called and the returned value is used to time a pause in execution before
- *   the window's {@link OO.ui.Window#getTeardownProcess getTeardownProcess} method is called on the
- *   window and its result executed
- * - a `teardown` progress notification is emitted from the `closing` promise
- * - the `closing` promise is resolved. The window is now closed
- *
- * See the [OOjs UI documentation on MediaWiki][1] for more information.
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Window_managers
- *
- * @class
- * @extends OO.ui.Element
- * @mixins OO.EventEmitter
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {OO.Factory} [factory] Window factory to use for automatic instantiation
- *  Note that window classes that are instantiated with a factory must have
- *  a {@link OO.ui.Dialog#static-name static name} property that specifies a symbolic name.
- * @cfg {boolean} [modal=true] Prevent interaction outside the dialog
- */
-OO.ui.WindowManager = function OoUiWindowManager( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Parent constructor
-       OO.ui.WindowManager.parent.call( this, config );
-
-       // Mixin constructors
-       OO.EventEmitter.call( this );
-
-       // Properties
-       this.factory = config.factory;
-       this.modal = config.modal === undefined || !!config.modal;
-       this.windows = {};
-       this.opening = null;
-       this.opened = null;
-       this.closing = null;
-       this.preparingToOpen = null;
-       this.preparingToClose = null;
-       this.currentWindow = null;
-       this.globalEvents = false;
-       this.$ariaHidden = null;
-       this.onWindowResizeTimeout = null;
-       this.onWindowResizeHandler = this.onWindowResize.bind( this );
-       this.afterWindowResizeHandler = this.afterWindowResize.bind( this );
-
-       // Initialization
-       this.$element
-               .addClass( 'oo-ui-windowManager' )
-               .toggleClass( 'oo-ui-windowManager-modal', this.modal );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.WindowManager, OO.ui.Element );
-OO.mixinClass( OO.ui.WindowManager, OO.EventEmitter );
-
-/* Events */
-
-/**
- * An 'opening' event is emitted when the window begins to be opened.
- *
- * @event opening
- * @param {OO.ui.Window} win Window that's being opened
- * @param {jQuery.Promise} opening An `opening` promise resolved with a value when the window is opened successfully.
- *  When the `opening` promise is resolved, the first argument of the value is an 'opened' promise, the second argument
- *  is the opening data. The `opening` promise emits `setup` and `ready` notifications when those processes are complete.
- * @param {Object} data Window opening data
- */
-
-/**
- * A 'closing' event is emitted when the window begins to be closed.
- *
- * @event closing
- * @param {OO.ui.Window} win Window that's being closed
- * @param {jQuery.Promise} closing A `closing` promise is resolved with a value when the window
- *  is closed successfully. The promise emits `hold` and `teardown` notifications when those
- *  processes are complete. When the `closing` promise is resolved, the first argument of its value
- *  is the closing data.
- * @param {Object} data Window closing data
- */
-
-/**
- * A 'resize' event is emitted when a window is resized.
- *
- * @event resize
- * @param {OO.ui.Window} win Window that was resized
- */
-
-/* Static Properties */
-
-/**
- * Map of the symbolic name of each window size and its CSS properties.
- *
- * @static
- * @inheritable
- * @property {Object}
- */
-OO.ui.WindowManager.static.sizes = {
-       small: {
-               width: 300
-       },
-       medium: {
-               width: 500
-       },
-       large: {
-               width: 700
-       },
-       larger: {
-               width: 900
-       },
-       full: {
-               // These can be non-numeric because they are never used in calculations
-               width: '100%',
-               height: '100%'
-       }
-};
-
-/**
- * Symbolic name of the default window size.
- *
- * The default size is used if the window's requested size is not recognized.
- *
- * @static
- * @inheritable
- * @property {string}
- */
-OO.ui.WindowManager.static.defaultSize = 'medium';
-
-/* Methods */
-
-/**
- * Handle window resize events.
- *
- * @private
- * @param {jQuery.Event} e Window resize event
- */
-OO.ui.WindowManager.prototype.onWindowResize = function () {
-       clearTimeout( this.onWindowResizeTimeout );
-       this.onWindowResizeTimeout = setTimeout( this.afterWindowResizeHandler, 200 );
-};
-
-/**
- * Handle window resize events.
- *
- * @private
- * @param {jQuery.Event} e Window resize event
- */
-OO.ui.WindowManager.prototype.afterWindowResize = function () {
-       if ( this.currentWindow ) {
-               this.updateWindowSize( this.currentWindow );
-       }
-};
-
-/**
- * Check if window is opening.
- *
- * @return {boolean} Window is opening
- */
-OO.ui.WindowManager.prototype.isOpening = function ( win ) {
-       return win === this.currentWindow && !!this.opening && this.opening.state() === 'pending';
-};
-
-/**
- * Check if window is closing.
- *
- * @return {boolean} Window is closing
- */
-OO.ui.WindowManager.prototype.isClosing = function ( win ) {
-       return win === this.currentWindow && !!this.closing && this.closing.state() === 'pending';
-};
-
-/**
- * Check if window is opened.
- *
- * @return {boolean} Window is opened
- */
-OO.ui.WindowManager.prototype.isOpened = function ( win ) {
-       return win === this.currentWindow && !!this.opened && this.opened.state() === 'pending';
-};
-
-/**
- * Check if a window is being managed.
- *
- * @param {OO.ui.Window} win Window to check
- * @return {boolean} Window is being managed
- */
-OO.ui.WindowManager.prototype.hasWindow = function ( win ) {
-       var name;
-
-       for ( name in this.windows ) {
-               if ( this.windows[ name ] === win ) {
-                       return true;
-               }
-       }
-
-       return false;
-};
-
-/**
- * Get the number of milliseconds to wait after opening begins before executing the ‘setup’ process.
- *
- * @param {OO.ui.Window} win Window being opened
- * @param {Object} [data] Window opening data
- * @return {number} Milliseconds to wait
- */
-OO.ui.WindowManager.prototype.getSetupDelay = function () {
-       return 0;
-};
-
-/**
- * Get the number of milliseconds to wait after setup has finished before executing the ‘ready’ process.
- *
- * @param {OO.ui.Window} win Window being opened
- * @param {Object} [data] Window opening data
- * @return {number} Milliseconds to wait
- */
-OO.ui.WindowManager.prototype.getReadyDelay = function () {
-       return 0;
-};
-
-/**
- * Get the number of milliseconds to wait after closing has begun before executing the 'hold' process.
- *
- * @param {OO.ui.Window} win Window being closed
- * @param {Object} [data] Window closing data
- * @return {number} Milliseconds to wait
- */
-OO.ui.WindowManager.prototype.getHoldDelay = function () {
-       return 0;
-};
-
-/**
- * Get the number of milliseconds to wait after the ‘hold’ process has finished before
- * executing the ‘teardown’ process.
- *
- * @param {OO.ui.Window} win Window being closed
- * @param {Object} [data] Window closing data
- * @return {number} Milliseconds to wait
- */
-OO.ui.WindowManager.prototype.getTeardownDelay = function () {
-       return this.modal ? 250 : 0;
-};
-
-/**
- * Get a window by its symbolic name.
- *
- * If the window is not yet instantiated and its symbolic name is recognized by a factory, it will be
- * instantiated and added to the window manager automatically. Please see the [OOjs UI documentation on MediaWiki][3]
- * for more information about using factories.
- * [3]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Window_managers
- *
- * @param {string} name Symbolic name of the window
- * @return {jQuery.Promise} Promise resolved with matching window, or rejected with an OO.ui.Error
- * @throws {Error} An error is thrown if the symbolic name is not recognized by the factory.
- * @throws {Error} An error is thrown if the named window is not recognized as a managed window.
- */
-OO.ui.WindowManager.prototype.getWindow = function ( name ) {
-       var deferred = $.Deferred(),
-               win = this.windows[ name ];
-
-       if ( !( win instanceof OO.ui.Window ) ) {
-               if ( this.factory ) {
-                       if ( !this.factory.lookup( name ) ) {
-                               deferred.reject( new OO.ui.Error(
-                                       'Cannot auto-instantiate window: symbolic name is unrecognized by the factory'
-                               ) );
-                       } else {
-                               win = this.factory.create( name );
-                               this.addWindows( [ win ] );
-                               deferred.resolve( win );
-                       }
-               } else {
-                       deferred.reject( new OO.ui.Error(
-                               'Cannot get unmanaged window: symbolic name unrecognized as a managed window'
-                       ) );
-               }
-       } else {
-               deferred.resolve( win );
-       }
-
-       return deferred.promise();
-};
-
-/**
- * Get current window.
- *
- * @return {OO.ui.Window|null} Currently opening/opened/closing window
- */
-OO.ui.WindowManager.prototype.getCurrentWindow = function () {
-       return this.currentWindow;
-};
-
-/**
- * Open a window.
- *
- * @param {OO.ui.Window|string} win Window object or symbolic name of window to open
- * @param {Object} [data] Window opening data
- * @return {jQuery.Promise} An `opening` promise resolved when the window is done opening.
- *  See {@link #event-opening 'opening' event}  for more information about `opening` promises.
- * @fires opening
- */
-OO.ui.WindowManager.prototype.openWindow = function ( win, data ) {
-       var manager = this,
-               opening = $.Deferred();
-
-       // Argument handling
-       if ( typeof win === 'string' ) {
-               return this.getWindow( win ).then( function ( win ) {
-                       return manager.openWindow( win, data );
-               } );
-       }
-
-       // Error handling
-       if ( !this.hasWindow( win ) ) {
-               opening.reject( new OO.ui.Error(
-                       'Cannot open window: window is not attached to manager'
-               ) );
-       } else if ( this.preparingToOpen || this.opening || this.opened ) {
-               opening.reject( new OO.ui.Error(
-                       'Cannot open window: another window is opening or open'
-               ) );
-       }
-
-       // Window opening
-       if ( opening.state() !== 'rejected' ) {
-               // If a window is currently closing, wait for it to complete
-               this.preparingToOpen = $.when( this.closing );
-               // Ensure handlers get called after preparingToOpen is set
-               this.preparingToOpen.done( function () {
-                       if ( manager.modal ) {
-                               manager.toggleGlobalEvents( true );
-                               manager.toggleAriaIsolation( true );
-                       }
-                       manager.currentWindow = win;
-                       manager.opening = opening;
-                       manager.preparingToOpen = null;
-                       manager.emit( 'opening', win, opening, data );
-                       setTimeout( function () {
-                               win.setup( data ).then( function () {
-                                       manager.updateWindowSize( win );
-                                       manager.opening.notify( { state: 'setup' } );
-                                       setTimeout( function () {
-                                               win.ready( data ).then( function () {
-                                                       manager.opening.notify( { state: 'ready' } );
-                                                       manager.opening = null;
-                                                       manager.opened = $.Deferred();
-                                                       opening.resolve( manager.opened.promise(), data );
-                                               }, function () {
-                                                       manager.opening = null;
-                                                       manager.opened = $.Deferred();
-                                                       opening.reject();
-                                                       manager.closeWindow( win );
-                                               } );
-                                       }, manager.getReadyDelay() );
-                               }, function () {
-                                       manager.opening = null;
-                                       manager.opened = $.Deferred();
-                                       opening.reject();
-                                       manager.closeWindow( win );
-                               } );
-                       }, manager.getSetupDelay() );
-               } );
-       }
-
-       return opening.promise();
-};
-
-/**
- * Close a window.
- *
- * @param {OO.ui.Window|string} win Window object or symbolic name of window to close
- * @param {Object} [data] Window closing data
- * @return {jQuery.Promise} A `closing` promise resolved when the window is done closing.
- *  See {@link #event-closing 'closing' event} for more information about closing promises.
- * @throws {Error} An error is thrown if the window is not managed by the window manager.
- * @fires closing
- */
-OO.ui.WindowManager.prototype.closeWindow = function ( win, data ) {
-       var manager = this,
-               closing = $.Deferred(),
-               opened;
-
-       // Argument handling
-       if ( typeof win === 'string' ) {
-               win = this.windows[ win ];
-       } else if ( !this.hasWindow( win ) ) {
-               win = null;
-       }
-
-       // Error handling
-       if ( !win ) {
-               closing.reject( new OO.ui.Error(
-                       'Cannot close window: window is not attached to manager'
-               ) );
-       } else if ( win !== this.currentWindow ) {
-               closing.reject( new OO.ui.Error(
-                       'Cannot close window: window already closed with different data'
-               ) );
-       } else if ( this.preparingToClose || this.closing ) {
-               closing.reject( new OO.ui.Error(
-                       'Cannot close window: window already closing with different data'
-               ) );
-       }
-
-       // Window closing
-       if ( closing.state() !== 'rejected' ) {
-               // If the window is currently opening, close it when it's done
-               this.preparingToClose = $.when( this.opening );
-               // Ensure handlers get called after preparingToClose is set
-               this.preparingToClose.always( function () {
-                       manager.closing = closing;
-                       manager.preparingToClose = null;
-                       manager.emit( 'closing', win, closing, data );
-                       opened = manager.opened;
-                       manager.opened = null;
-                       opened.resolve( closing.promise(), data );
-                       setTimeout( function () {
-                               win.hold( data ).then( function () {
-                                       closing.notify( { state: 'hold' } );
-                                       setTimeout( function () {
-                                               win.teardown( data ).then( function () {
-                                                       closing.notify( { state: 'teardown' } );
-                                                       if ( manager.modal ) {
-                                                               manager.toggleGlobalEvents( false );
-                                                               manager.toggleAriaIsolation( false );
-                                                       }
-                                                       manager.closing = null;
-                                                       manager.currentWindow = null;
-                                                       closing.resolve( data );
-                                               } );
-                                       }, manager.getTeardownDelay() );
-                               } );
-                       }, manager.getHoldDelay() );
-               } );
-       }
-
-       return closing.promise();
-};
-
-/**
- * Add windows to the window manager.
- *
- * Windows can be added by reference, symbolic name, or explicitly defined symbolic names.
- * See the [OOjs ui documentation on MediaWiki] [2] for examples.
- * [2]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Window_managers
- *
- * @param {Object.<string,OO.ui.Window>|OO.ui.Window[]} windows An array of window objects specified
- *  by reference, symbolic name, or explicitly defined symbolic names.
- * @throws {Error} An error is thrown if a window is added by symbolic name, but has neither an
- *  explicit nor a statically configured symbolic name.
- */
-OO.ui.WindowManager.prototype.addWindows = function ( windows ) {
-       var i, len, win, name, list;
-
-       if ( Array.isArray( windows ) ) {
-               // Convert to map of windows by looking up symbolic names from static configuration
-               list = {};
-               for ( i = 0, len = windows.length; i < len; i++ ) {
-                       name = windows[ i ].constructor.static.name;
-                       if ( typeof name !== 'string' ) {
-                               throw new Error( 'Cannot add window' );
-                       }
-                       list[ name ] = windows[ i ];
-               }
-       } else if ( OO.isPlainObject( windows ) ) {
-               list = windows;
-       }
-
-       // Add windows
-       for ( name in list ) {
-               win = list[ name ];
-               this.windows[ name ] = win.toggle( false );
-               this.$element.append( win.$element );
-               win.setManager( this );
-       }
-};
-
-/**
- * Remove the specified windows from the windows manager.
- *
- * Windows will be closed before they are removed. If you wish to remove all windows, you may wish to use
- * the #clearWindows method instead. If you no longer need the window manager and want to ensure that it no
- * longer listens to events, use the #destroy method.
- *
- * @param {string[]} names Symbolic names of windows to remove
- * @return {jQuery.Promise} Promise resolved when window is closed and removed
- * @throws {Error} An error is thrown if the named windows are not managed by the window manager.
- */
-OO.ui.WindowManager.prototype.removeWindows = function ( names ) {
-       var i, len, win, name, cleanupWindow,
-               manager = this,
-               promises = [],
-               cleanup = function ( name, win ) {
-                       delete manager.windows[ name ];
-                       win.$element.detach();
-               };
-
-       for ( i = 0, len = names.length; i < len; i++ ) {
-               name = names[ i ];
-               win = this.windows[ name ];
-               if ( !win ) {
-                       throw new Error( 'Cannot remove window' );
-               }
-               cleanupWindow = cleanup.bind( null, name, win );
-               promises.push( this.closeWindow( name ).then( cleanupWindow, cleanupWindow ) );
-       }
-
-       return $.when.apply( $, promises );
-};
-
-/**
- * Remove all windows from the window manager.
- *
- * Windows will be closed before they are removed. Note that the window manager, though not in use, will still
- * listen to events. If the window manager will not be used again, you may wish to use the #destroy method instead.
- * To remove just a subset of windows, use the #removeWindows method.
- *
- * @return {jQuery.Promise} Promise resolved when all windows are closed and removed
- */
-OO.ui.WindowManager.prototype.clearWindows = function () {
-       return this.removeWindows( Object.keys( this.windows ) );
-};
-
-/**
- * Set dialog size. In general, this method should not be called directly.
- *
- * Fullscreen mode will be used if the dialog is too wide to fit in the screen.
- *
- * @chainable
- */
-OO.ui.WindowManager.prototype.updateWindowSize = function ( win ) {
-       var isFullscreen;
-
-       // Bypass for non-current, and thus invisible, windows
-       if ( win !== this.currentWindow ) {
-               return;
-       }
-
-       isFullscreen = win.getSize() === 'full';
-
-       this.$element.toggleClass( 'oo-ui-windowManager-fullscreen', isFullscreen );
-       this.$element.toggleClass( 'oo-ui-windowManager-floating', !isFullscreen );
-       win.setDimensions( win.getSizeProperties() );
-
-       this.emit( 'resize', win );
-
-       return this;
-};
-
-/**
- * Bind or unbind global events for scrolling.
- *
- * @private
- * @param {boolean} [on] Bind global events
- * @chainable
- */
-OO.ui.WindowManager.prototype.toggleGlobalEvents = function ( on ) {
-       var scrollWidth, bodyMargin,
-               $body = $( this.getElementDocument().body ),
-               // We could have multiple window managers open so only modify
-               // the body css at the bottom of the stack
-               stackDepth = $body.data( 'windowManagerGlobalEvents' ) || 0 ;
-
-       on = on === undefined ? !!this.globalEvents : !!on;
-
-       if ( on ) {
-               if ( !this.globalEvents ) {
-                       $( this.getElementWindow() ).on( {
-                               // Start listening for top-level window dimension changes
-                               'orientationchange resize': this.onWindowResizeHandler
-                       } );
-                       if ( stackDepth === 0 ) {
-                               scrollWidth = window.innerWidth - document.documentElement.clientWidth;
-                               bodyMargin = parseFloat( $body.css( 'margin-right' ) ) || 0;
-                               $body.css( {
-                                       overflow: 'hidden',
-                                       'margin-right': bodyMargin + scrollWidth
-                               } );
-                       }
-                       stackDepth++;
-                       this.globalEvents = true;
-               }
-       } else if ( this.globalEvents ) {
-               $( this.getElementWindow() ).off( {
-                       // Stop listening for top-level window dimension changes
-                       'orientationchange resize': this.onWindowResizeHandler
-               } );
-               stackDepth--;
-               if ( stackDepth === 0 ) {
-                       $body.css( {
-                               overflow: '',
-                               'margin-right': ''
-                       } );
-               }
-               this.globalEvents = false;
-       }
-       $body.data( 'windowManagerGlobalEvents', stackDepth );
-
-       return this;
-};
-
-/**
- * Toggle screen reader visibility of content other than the window manager.
- *
- * @private
- * @param {boolean} [isolate] Make only the window manager visible to screen readers
- * @chainable
- */
-OO.ui.WindowManager.prototype.toggleAriaIsolation = function ( isolate ) {
-       isolate = isolate === undefined ? !this.$ariaHidden : !!isolate;
-
-       if ( isolate ) {
-               if ( !this.$ariaHidden ) {
-                       // Hide everything other than the window manager from screen readers
-                       this.$ariaHidden = $( 'body' )
-                               .children()
-                               .not( this.$element.parentsUntil( 'body' ).last() )
-                               .attr( 'aria-hidden', '' );
-               }
-       } else if ( this.$ariaHidden ) {
-               // Restore screen reader visibility
-               this.$ariaHidden.removeAttr( 'aria-hidden' );
-               this.$ariaHidden = null;
-       }
-
-       return this;
-};
-
-/**
- * Destroy the window manager.
- *
- * Destroying the window manager ensures that it will no longer listen to events. If you would like to
- * continue using the window manager, but wish to remove all windows from it, use the #clearWindows method
- * instead.
- */
-OO.ui.WindowManager.prototype.destroy = function () {
-       this.toggleGlobalEvents( false );
-       this.toggleAriaIsolation( false );
-       this.clearWindows();
-       this.$element.remove();
-};
-
-/**
- * Errors contain a required message (either a string or jQuery selection) that is used to describe what went wrong
- * in a {@link OO.ui.Process process}. The error's #recoverable and #warning configurations are used to customize the
- * appearance and functionality of the error interface.
- *
- * The basic error interface contains a formatted error message as well as two buttons: 'Dismiss' and 'Try again' (i.e., the error
- * is 'recoverable' by default). If the error is not recoverable, the 'Try again' button will not be rendered and the widget
- * that initiated the failed process will be disabled.
- *
- * If the error is a warning, the error interface will include a 'Dismiss' and a 'Continue' button, which will try the
- * process again.
- *
- * For an example of error interfaces, please see the [OOjs UI documentation on MediaWiki][1].
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Process_Dialogs#Processes_and_errors
- *
- * @class
- *
- * @constructor
- * @param {string|jQuery} message Description of error
- * @param {Object} [config] Configuration options
- * @cfg {boolean} [recoverable=true] Error is recoverable.
- *  By default, errors are recoverable, and users can try the process again.
- * @cfg {boolean} [warning=false] Error is a warning.
- *  If the error is a warning, the error interface will include a
- *  'Dismiss' and a 'Continue' button. It is the responsibility of the developer to ensure that the warning
- *  is not triggered a second time if the user chooses to continue.
- */
-OO.ui.Error = function OoUiError( message, config ) {
-       // Allow passing positional parameters inside the config object
-       if ( OO.isPlainObject( message ) && config === undefined ) {
-               config = message;
-               message = config.message;
-       }
-
-       // Configuration initialization
-       config = config || {};
-
-       // Properties
-       this.message = message instanceof jQuery ? message : String( message );
-       this.recoverable = config.recoverable === undefined || !!config.recoverable;
-       this.warning = !!config.warning;
-};
-
-/* Setup */
-
-OO.initClass( OO.ui.Error );
-
-/* Methods */
-
-/**
- * Check if the error is recoverable.
- *
- * If the error is recoverable, users are able to try the process again.
- *
- * @return {boolean} Error is recoverable
- */
-OO.ui.Error.prototype.isRecoverable = function () {
-       return this.recoverable;
-};
-
-/**
- * Check if the error is a warning.
- *
- * If the error is a warning, the error interface will include a 'Dismiss' and a 'Continue' button.
- *
- * @return {boolean} Error is warning
- */
-OO.ui.Error.prototype.isWarning = function () {
-       return this.warning;
-};
-
-/**
- * Get error message as DOM nodes.
- *
- * @return {jQuery} Error message in DOM nodes
- */
-OO.ui.Error.prototype.getMessage = function () {
-       return this.message instanceof jQuery ?
-               this.message.clone() :
-               $( '<div>' ).text( this.message ).contents();
-};
-
-/**
- * Get the error message text.
- *
- * @return {string} Error message
- */
-OO.ui.Error.prototype.getMessageText = function () {
-       return this.message instanceof jQuery ? this.message.text() : this.message;
-};
-
-/**
- * Wraps an HTML snippet for use with configuration values which default
- * to strings.  This bypasses the default html-escaping done to string
- * values.
- *
- * @class
- *
- * @constructor
- * @param {string} [content] HTML content
- */
-OO.ui.HtmlSnippet = function OoUiHtmlSnippet( content ) {
-       // Properties
-       this.content = content;
-};
-
-/* Setup */
-
-OO.initClass( OO.ui.HtmlSnippet );
-
-/* Methods */
-
-/**
- * Render into HTML.
- *
- * @return {string} Unchanged HTML snippet.
- */
-OO.ui.HtmlSnippet.prototype.toString = function () {
-       return this.content;
-};
-
-/**
- * A Process is a list of steps that are called in sequence. The step can be a number, a jQuery promise,
- * or a function:
- *
- * - **number**: the process will wait for the specified number of milliseconds before proceeding.
- * - **promise**: the process will continue to the next step when the promise is successfully resolved
- *  or stop if the promise is rejected.
- * - **function**: the process will execute the function. The process will stop if the function returns
- *  either a boolean `false` or a promise that is rejected; if the function returns a number, the process
- *  will wait for that number of milliseconds before proceeding.
- *
- * If the process fails, an {@link OO.ui.Error error} is generated. Depending on how the error is
- * configured, users can dismiss the error and try the process again, or not. If a process is stopped,
- * its remaining steps will not be performed.
- *
- * @class
- *
- * @constructor
- * @param {number|jQuery.Promise|Function} step Number of miliseconds to wait before proceeding, promise
- *  that must be resolved before proceeding, or a function to execute. See #createStep for more information. see #createStep for more information
- * @param {Object} [context=null] Execution context of the function. The context is ignored if the step is
- *  a number or promise.
- * @return {Object} Step object, with `callback` and `context` properties
- */
-OO.ui.Process = function ( step, context ) {
-       // Properties
-       this.steps = [];
-
-       // Initialization
-       if ( step !== undefined ) {
-               this.next( step, context );
-       }
-};
-
-/* Setup */
-
-OO.initClass( OO.ui.Process );
-
-/* Methods */
-
-/**
- * Start the process.
- *
- * @return {jQuery.Promise} Promise that is resolved when all steps have successfully completed.
- *  If any of the steps return a promise that is rejected or a boolean false, this promise is rejected
- *  and any remaining steps are not performed.
- */
-OO.ui.Process.prototype.execute = function () {
-       var i, len, promise;
-
-       /**
-        * Continue execution.
-        *
-        * @ignore
-        * @param {Array} step A function and the context it should be called in
-        * @return {Function} Function that continues the process
-        */
-       function proceed( step ) {
-               return function () {
-                       // Execute step in the correct context
-                       var deferred,
-                               result = step.callback.call( step.context );
-
-                       if ( result === false ) {
-                               // Use rejected promise for boolean false results
-                               return $.Deferred().reject( [] ).promise();
-                       }
-                       if ( typeof result === 'number' ) {
-                               if ( result < 0 ) {
-                                       throw new Error( 'Cannot go back in time: flux capacitor is out of service' );
-                               }
-                               // Use a delayed promise for numbers, expecting them to be in milliseconds
-                               deferred = $.Deferred();
-                               setTimeout( deferred.resolve, result );
-                               return deferred.promise();
-                       }
-                       if ( result instanceof OO.ui.Error ) {
-                               // Use rejected promise for error
-                               return $.Deferred().reject( [ result ] ).promise();
-                       }
-                       if ( Array.isArray( result ) && result.length && result[ 0 ] instanceof OO.ui.Error ) {
-                               // Use rejected promise for list of errors
-                               return $.Deferred().reject( result ).promise();
-                       }
-                       // Duck-type the object to see if it can produce a promise
-                       if ( result && $.isFunction( result.promise ) ) {
-                               // Use a promise generated from the result
-                               return result.promise();
-                       }
-                       // Use resolved promise for other results
-                       return $.Deferred().resolve().promise();
-               };
-       }
-
-       if ( this.steps.length ) {
-               // Generate a chain reaction of promises
-               promise = proceed( this.steps[ 0 ] )();
-               for ( i = 1, len = this.steps.length; i < len; i++ ) {
-                       promise = promise.then( proceed( this.steps[ i ] ) );
-               }
-       } else {
-               promise = $.Deferred().resolve().promise();
-       }
-
-       return promise;
-};
-
-/**
- * Create a process step.
- *
- * @private
- * @param {number|jQuery.Promise|Function} step
- *
- * - Number of milliseconds to wait before proceeding
- * - Promise that must be resolved before proceeding
- * - Function to execute
- *   - If the function returns a boolean false the process will stop
- *   - If the function returns a promise, the process will continue to the next
- *     step when the promise is resolved or stop if the promise is rejected
- *   - If the function returns a number, the process will wait for that number of
- *     milliseconds before proceeding
- * @param {Object} [context=null] Execution context of the function. The context is
- *  ignored if the step is a number or promise.
- * @return {Object} Step object, with `callback` and `context` properties
- */
-OO.ui.Process.prototype.createStep = function ( step, context ) {
-       if ( typeof step === 'number' || $.isFunction( step.promise ) ) {
-               return {
-                       callback: function () {
-                               return step;
-                       },
-                       context: null
-               };
-       }
-       if ( $.isFunction( step ) ) {
-               return {
-                       callback: step,
-                       context: context
-               };
-       }
-       throw new Error( 'Cannot create process step: number, promise or function expected' );
-};
-
-/**
- * Add step to the beginning of the process.
- *
- * @inheritdoc #createStep
- * @return {OO.ui.Process} this
- * @chainable
- */
-OO.ui.Process.prototype.first = function ( step, context ) {
-       this.steps.unshift( this.createStep( step, context ) );
-       return this;
-};
-
-/**
- * Add step to the end of the process.
- *
- * @inheritdoc #createStep
- * @return {OO.ui.Process} this
- * @chainable
- */
-OO.ui.Process.prototype.next = function ( step, context ) {
-       this.steps.push( this.createStep( step, context ) );
-       return this;
-};
-
-/**
- * A ToolFactory creates tools on demand. All tools ({@link OO.ui.Tool Tools}, {@link OO.ui.PopupTool PopupTools},
- * and {@link OO.ui.ToolGroupTool ToolGroupTools}) must be registered with a tool factory. Tools are
- * registered by their symbolic name. See {@link OO.ui.Toolbar toolbars} for an example.
- *
- * For more information about toolbars in general, please see the [OOjs UI documentation on MediaWiki][1].
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Toolbars
- *
- * @class
- * @extends OO.Factory
- * @constructor
- */
-OO.ui.ToolFactory = function OoUiToolFactory() {
-       // Parent constructor
-       OO.ui.ToolFactory.parent.call( this );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.ToolFactory, OO.Factory );
-
-/* Methods */
-
-/**
- * Get tools from the factory
- *
- * @param {Array|string} [include] Included tools, see #extract for format
- * @param {Array|string} [exclude] Excluded tools, see #extract for format
- * @param {Array|string} [promote] Promoted tools, see #extract for format
- * @param {Array|string} [demote] Demoted tools, see #extract for format
- * @return {string[]} List of tools
- */
-OO.ui.ToolFactory.prototype.getTools = function ( include, exclude, promote, demote ) {
-       var i, len, included, promoted, demoted,
-               auto = [],
-               used = {};
-
-       // Collect included and not excluded tools
-       included = OO.simpleArrayDifference( this.extract( include ), this.extract( exclude ) );
-
-       // Promotion
-       promoted = this.extract( promote, used );
-       demoted = this.extract( demote, used );
-
-       // Auto
-       for ( i = 0, len = included.length; i < len; i++ ) {
-               if ( !used[ included[ i ] ] ) {
-                       auto.push( included[ i ] );
-               }
-       }
-
-       return promoted.concat( auto ).concat( demoted );
-};
-
-/**
- * Get a flat list of names from a list of names or groups.
- *
- * Normally, `collection` is an array of tool specifications. Tools can be specified in the
- * following ways:
- *
- * - To include an individual tool, use the symbolic name: `{ name: 'tool-name' }` or `'tool-name'`.
- * - To include all tools in a group, use the group name: `{ group: 'group-name' }`. (To assign the
- *   tool to a group, use OO.ui.Tool.static.group.)
- *
- * Alternatively, to include all tools that are not yet assigned to any other toolgroup, use the
- * catch-all selector `'*'`.
- *
- * If `used` is passed, tool names that appear as properties in this object will be considered
- * already assigned, and will not be returned even if specified otherwise. The tool names extracted
- * by this function call will be added as new properties in the object.
- *
- * @private
- * @param {Array|string} collection List of tools, see above
- * @param {Object} [used] Object containing information about used tools, see above
- * @return {string[]} List of extracted tool names
- */
-OO.ui.ToolFactory.prototype.extract = function ( collection, used ) {
-       var i, len, item, name, tool,
-               names = [];
-
-       if ( collection === '*' ) {
-               for ( name in this.registry ) {
-                       tool = this.registry[ name ];
-                       if (
-                               // Only add tools by group name when auto-add is enabled
-                               tool.static.autoAddToCatchall &&
-                               // Exclude already used tools
-                               ( !used || !used[ name ] )
-                       ) {
-                               names.push( name );
-                               if ( used ) {
-                                       used[ name ] = true;
-                               }
-                       }
-               }
-       } else if ( Array.isArray( collection ) ) {
-               for ( i = 0, len = collection.length; i < len; i++ ) {
-                       item = collection[ i ];
-                       // Allow plain strings as shorthand for named tools
-                       if ( typeof item === 'string' ) {
-                               item = { name: item };
-                       }
-                       if ( OO.isPlainObject( item ) ) {
-                               if ( item.group ) {
-                                       for ( name in this.registry ) {
-                                               tool = this.registry[ name ];
-                                               if (
-                                                       // Include tools with matching group
-                                                       tool.static.group === item.group &&
-                                                       // Only add tools by group name when auto-add is enabled
-                                                       tool.static.autoAddToGroup &&
-                                                       // Exclude already used tools
-                                                       ( !used || !used[ name ] )
-                                               ) {
-                                                       names.push( name );
-                                                       if ( used ) {
-                                                               used[ name ] = true;
-                                                       }
-                                               }
-                                       }
-                               // Include tools with matching name and exclude already used tools
-                               } else if ( item.name && ( !used || !used[ item.name ] ) ) {
-                                       names.push( item.name );
-                                       if ( used ) {
-                                               used[ item.name ] = true;
-                                       }
-                               }
-                       }
-               }
-       }
-       return names;
-};
-
-/**
- * ToolGroupFactories create {@link OO.ui.ToolGroup toolgroups} on demand. The toolgroup classes must
- * specify a symbolic name and be registered with the factory. The following classes are registered by
- * default:
- *
- * - {@link OO.ui.BarToolGroup BarToolGroups} (‘bar’)
- * - {@link OO.ui.MenuToolGroup MenuToolGroups} (‘menu’)
- * - {@link OO.ui.ListToolGroup ListToolGroups} (‘list’)
- *
- * See {@link OO.ui.Toolbar toolbars} for an example.
- *
- * For more information about toolbars in general, please see the [OOjs UI documentation on MediaWiki][1].
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Toolbars
- * @class
- * @extends OO.Factory
- * @constructor
- */
-OO.ui.ToolGroupFactory = function OoUiToolGroupFactory() {
-       var i, l, defaultClasses;
-       // Parent constructor
-       OO.Factory.call( this );
-
-       defaultClasses = this.constructor.static.getDefaultClasses();
-
-       // Register default toolgroups
-       for ( i = 0, l = defaultClasses.length; i < l; i++ ) {
-               this.register( defaultClasses[ i ] );
-       }
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.ToolGroupFactory, OO.Factory );
-
-/* Static Methods */
-
-/**
- * Get a default set of classes to be registered on construction.
- *
- * @return {Function[]} Default classes
- */
-OO.ui.ToolGroupFactory.static.getDefaultClasses = function () {
-       return [
-               OO.ui.BarToolGroup,
-               OO.ui.ListToolGroup,
-               OO.ui.MenuToolGroup
-       ];
-};
-
-/**
- * Theme logic.
- *
- * @abstract
- * @class
- *
- * @constructor
- * @param {Object} [config] Configuration options
- */
-OO.ui.Theme = function OoUiTheme( config ) {
-       // Configuration initialization
-       config = config || {};
-};
-
-/* Setup */
-
-OO.initClass( OO.ui.Theme );
-
-/* Methods */
-
-/**
- * Get a list of classes to be applied to a widget.
- *
- * The 'on' and 'off' lists combined MUST contain keys for all classes the theme adds or removes,
- * otherwise state transitions will not work properly.
- *
- * @param {OO.ui.Element} element Element for which to get classes
- * @return {Object.<string,string[]>} Categorized class names with `on` and `off` lists
- */
-OO.ui.Theme.prototype.getElementClasses = function () {
-       return { on: [], off: [] };
-};
-
-/**
- * Update CSS classes provided by the theme.
- *
- * For elements with theme logic hooks, this should be called any time there's a state change.
- *
- * @param {OO.ui.Element} element Element for which to update classes
- * @return {Object.<string,string[]>} Categorized class names with `on` and `off` lists
- */
-OO.ui.Theme.prototype.updateElementClasses = function ( element ) {
-       var $elements = $( [] ),
-               classes = this.getElementClasses( element );
-
-       if ( element.$icon ) {
-               $elements = $elements.add( element.$icon );
-       }
-       if ( element.$indicator ) {
-               $elements = $elements.add( element.$indicator );
-       }
-
-       $elements
-               .removeClass( classes.off.join( ' ' ) )
-               .addClass( classes.on.join( ' ' ) );
-};
-
-/**
- * RequestManager is a mixin that manages the lifecycle of a promise-backed request for a widget, such as
- * the {@link OO.ui.mixin.LookupElement}.
- *
- * @class
- * @abstract
- *
- * @constructor
- */
-OO.ui.mixin.RequestManager = function OoUiMixinRequestManager() {
-       this.requestCache = {};
-       this.requestQuery = null;
-       this.requestRequest = null;
-};
-
-/* Setup */
-
-OO.initClass( OO.ui.mixin.RequestManager );
-
-/**
- * Get request results for the current query.
- *
- * @return {jQuery.Promise} Promise object which will be passed response data as the first argument of
- *   the done event. If the request was aborted to make way for a subsequent request, this promise
- *   may not be rejected, depending on what jQuery feels like doing.
- */
-OO.ui.mixin.RequestManager.prototype.getRequestData = function () {
-       var widget = this,
-               value = this.getRequestQuery(),
-               deferred = $.Deferred(),
-               ourRequest;
-
-       this.abortRequest();
-       if ( Object.prototype.hasOwnProperty.call( this.requestCache, value ) ) {
-               deferred.resolve( this.requestCache[ value ] );
-       } else {
-               if ( this.pushPending ) {
-                       this.pushPending();
-               }
-               this.requestQuery = value;
-               ourRequest = this.requestRequest = this.getRequest();
-               ourRequest
-                       .always( function () {
-                               // We need to pop pending even if this is an old request, otherwise
-                               // the widget will remain pending forever.
-                               // TODO: this assumes that an aborted request will fail or succeed soon after
-                               // being aborted, or at least eventually. It would be nice if we could popPending()
-                               // at abort time, but only if we knew that we hadn't already called popPending()
-                               // for that request.
-                               if ( widget.popPending ) {
-                                       widget.popPending();
-                               }
-                       } )
-                       .done( function ( response ) {
-                               // If this is an old request (and aborting it somehow caused it to still succeed),
-                               // ignore its success completely
-                               if ( ourRequest === widget.requestRequest ) {
-                                       widget.requestQuery = null;
-                                       widget.requestRequest = null;
-                                       widget.requestCache[ value ] = widget.getRequestCacheDataFromResponse( response );
-                                       deferred.resolve( widget.requestCache[ value ] );
-                               }
-                       } )
-                       .fail( function () {
-                               // If this is an old request (or a request failing because it's being aborted),
-                               // ignore its failure completely
-                               if ( ourRequest === widget.requestRequest ) {
-                                       widget.requestQuery = null;
-                                       widget.requestRequest = null;
-                                       deferred.reject();
-                               }
-                       } );
-       }
-       return deferred.promise();
-};
-
-/**
- * Abort the currently pending request, if any.
- *
- * @private
- */
-OO.ui.mixin.RequestManager.prototype.abortRequest = function () {
-       var oldRequest = this.requestRequest;
-       if ( oldRequest ) {
-               // First unset this.requestRequest to the fail handler will notice
-               // that the request is no longer current
-               this.requestRequest = null;
-               this.requestQuery = null;
-               oldRequest.abort();
-       }
-};
-
-/**
- * Get the query to be made.
- *
- * @protected
- * @method
- * @abstract
- * @return {string} query to be used
- */
-OO.ui.mixin.RequestManager.prototype.getRequestQuery = null;
-
-/**
- * Get a new request object of the current query value.
- *
- * @protected
- * @method
- * @abstract
- * @return {jQuery.Promise} jQuery AJAX object, or promise object with an .abort() method
- */
-OO.ui.mixin.RequestManager.prototype.getRequest = null;
-
-/**
- * Pre-process data returned by the request from #getRequest.
- *
- * The return value of this function will be cached, and any further queries for the given value
- * will use the cache rather than doing API requests.
- *
- * @protected
- * @method
- * @abstract
- * @param {Mixed} response Response from server
- * @return {Mixed} Cached result data
- */
-OO.ui.mixin.RequestManager.prototype.getRequestCacheDataFromResponse = null;
-
-/**
- * The TabIndexedElement class is an attribute mixin used to add additional functionality to an
- * element created by another class. The mixin provides a ‘tabIndex’ property, which specifies the
- * order in which users will navigate through the focusable elements via the "tab" key.
- *
- *     @example
- *     // TabIndexedElement is mixed into the ButtonWidget class
- *     // to provide a tabIndex property.
- *     var button1 = new OO.ui.ButtonWidget( {
- *         label: 'fourth',
- *         tabIndex: 4
- *     } );
- *     var button2 = new OO.ui.ButtonWidget( {
- *         label: 'second',
- *         tabIndex: 2
- *     } );
- *     var button3 = new OO.ui.ButtonWidget( {
- *         label: 'third',
- *         tabIndex: 3
- *     } );
- *     var button4 = new OO.ui.ButtonWidget( {
- *         label: 'first',
- *         tabIndex: 1
- *     } );
- *     $( 'body' ).append( button1.$element, button2.$element, button3.$element, button4.$element );
- *
- * @abstract
- * @class
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {jQuery} [$tabIndexed] The element that should use the tabindex functionality. By default,
- *  the functionality is applied to the element created by the class ($element). If a different element is specified, the tabindex
- *  functionality will be applied to it instead.
- * @cfg {number|null} [tabIndex=0] Number that specifies the element’s position in the tab-navigation
- *  order (e.g., 1 for the first focusable element). Use 0 to use the default navigation order; use -1
- *  to remove the element from the tab-navigation flow.
- */
-OO.ui.mixin.TabIndexedElement = function OoUiMixinTabIndexedElement( config ) {
-       // Configuration initialization
-       config = $.extend( { tabIndex: 0 }, config );
-
-       // Properties
-       this.$tabIndexed = null;
-       this.tabIndex = null;
-
-       // Events
-       this.connect( this, { disable: 'onTabIndexedElementDisable' } );
-
-       // Initialization
-       this.setTabIndex( config.tabIndex );
-       this.setTabIndexedElement( config.$tabIndexed || this.$element );
-};
-
-/* Setup */
-
-OO.initClass( OO.ui.mixin.TabIndexedElement );
-
-/* Methods */
-
-/**
- * Set the element that should use the tabindex functionality.
- *
- * This method is used to retarget a tabindex mixin so that its functionality applies
- * to the specified element. If an element is currently using the functionality, the mixin’s
- * effect on that element is removed before the new element is set up.
- *
- * @param {jQuery} $tabIndexed Element that should use the tabindex functionality
- * @chainable
- */
-OO.ui.mixin.TabIndexedElement.prototype.setTabIndexedElement = function ( $tabIndexed ) {
-       var tabIndex = this.tabIndex;
-       // Remove attributes from old $tabIndexed
-       this.setTabIndex( null );
-       // Force update of new $tabIndexed
-       this.$tabIndexed = $tabIndexed;
-       this.tabIndex = tabIndex;
-       return this.updateTabIndex();
-};
-
-/**
- * Set the value of the tabindex.
- *
- * @param {number|null} tabIndex Tabindex value, or `null` for no tabindex
- * @chainable
- */
-OO.ui.mixin.TabIndexedElement.prototype.setTabIndex = function ( tabIndex ) {
-       tabIndex = typeof tabIndex === 'number' ? tabIndex : null;
-
-       if ( this.tabIndex !== tabIndex ) {
-               this.tabIndex = tabIndex;
-               this.updateTabIndex();
-       }
-
-       return this;
-};
-
-/**
- * Update the `tabindex` attribute, in case of changes to tab index or
- * disabled state.
- *
- * @private
- * @chainable
- */
-OO.ui.mixin.TabIndexedElement.prototype.updateTabIndex = function () {
-       if ( this.$tabIndexed ) {
-               if ( this.tabIndex !== null ) {
-                       // Do not index over disabled elements
-                       this.$tabIndexed.attr( {
-                               tabindex: this.isDisabled() ? -1 : this.tabIndex,
-                               // Support: ChromeVox and NVDA
-                               // These do not seem to inherit aria-disabled from parent elements
-                               'aria-disabled': this.isDisabled().toString()
-                       } );
-               } else {
-                       this.$tabIndexed.removeAttr( 'tabindex aria-disabled' );
-               }
-       }
-       return this;
-};
-
-/**
- * Handle disable events.
- *
- * @private
- * @param {boolean} disabled Element is disabled
- */
-OO.ui.mixin.TabIndexedElement.prototype.onTabIndexedElementDisable = function () {
-       this.updateTabIndex();
-};
-
-/**
- * Get the value of the tabindex.
- *
- * @return {number|null} Tabindex value
- */
-OO.ui.mixin.TabIndexedElement.prototype.getTabIndex = function () {
-       return this.tabIndex;
-};
-
-/**
- * ButtonElement is often mixed into other classes to generate a button, which is a clickable
- * interface element that can be configured with access keys for accessibility.
- * See the [OOjs UI documentation on MediaWiki] [1] for examples.
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Buttons_and_Switches#Buttons
- * @abstract
- * @class
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {jQuery} [$button] The button element created by the class.
- *  If this configuration is omitted, the button element will use a generated `<a>`.
- * @cfg {boolean} [framed=true] Render the button with a frame
- */
-OO.ui.mixin.ButtonElement = function OoUiMixinButtonElement( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Properties
-       this.$button = null;
-       this.framed = null;
-       this.active = false;
-       this.onMouseUpHandler = this.onMouseUp.bind( this );
-       this.onMouseDownHandler = this.onMouseDown.bind( this );
-       this.onKeyDownHandler = this.onKeyDown.bind( this );
-       this.onKeyUpHandler = this.onKeyUp.bind( this );
-       this.onClickHandler = this.onClick.bind( this );
-       this.onKeyPressHandler = this.onKeyPress.bind( this );
-
-       // Initialization
-       this.$element.addClass( 'oo-ui-buttonElement' );
-       this.toggleFramed( config.framed === undefined || config.framed );
-       this.setButtonElement( config.$button || $( '<a>' ) );
-};
-
-/* Setup */
-
-OO.initClass( OO.ui.mixin.ButtonElement );
-
-/* Static Properties */
-
-/**
- * Cancel mouse down events.
- *
- * This property is usually set to `true` to prevent the focus from changing when the button is clicked.
- * Classes such as {@link OO.ui.mixin.DraggableElement DraggableElement} and {@link OO.ui.ButtonOptionWidget ButtonOptionWidget}
- * use a value of `false` so that dragging behavior is possible and mousedown events can be handled by a
- * parent widget.
- *
- * @static
- * @inheritable
- * @property {boolean}
- */
-OO.ui.mixin.ButtonElement.static.cancelButtonMouseDownEvents = true;
-
-/* Events */
-
-/**
- * A 'click' event is emitted when the button element is clicked.
- *
- * @event click
- */
-
-/* Methods */
-
-/**
- * Set the button element.
- *
- * This method is used to retarget a button mixin so that its functionality applies to
- * the specified button element instead of the one created by the class. If a button element
- * is already set, the method will remove the mixin’s effect on that element.
- *
- * @param {jQuery} $button Element to use as button
- */
-OO.ui.mixin.ButtonElement.prototype.setButtonElement = function ( $button ) {
-       if ( this.$button ) {
-               this.$button
-                       .removeClass( 'oo-ui-buttonElement-button' )
-                       .removeAttr( 'role accesskey' )
-                       .off( {
-                               mousedown: this.onMouseDownHandler,
-                               keydown: this.onKeyDownHandler,
-                               click: this.onClickHandler,
-                               keypress: this.onKeyPressHandler
-                       } );
-       }
-
-       this.$button = $button
-               .addClass( 'oo-ui-buttonElement-button' )
-               .attr( { role: 'button' } )
-               .on( {
-                       mousedown: this.onMouseDownHandler,
-                       keydown: this.onKeyDownHandler,
-                       click: this.onClickHandler,
-                       keypress: this.onKeyPressHandler
-               } );
-};
-
-/**
- * Handles mouse down events.
- *
- * @protected
- * @param {jQuery.Event} e Mouse down event
- */
-OO.ui.mixin.ButtonElement.prototype.onMouseDown = function ( e ) {
-       if ( this.isDisabled() || e.which !== OO.ui.MouseButtons.LEFT ) {
-               return;
-       }
-       this.$element.addClass( 'oo-ui-buttonElement-pressed' );
-       // Run the mouseup handler no matter where the mouse is when the button is let go, so we can
-       // reliably remove the pressed class
-       this.getElementDocument().addEventListener( 'mouseup', this.onMouseUpHandler, true );
-       // Prevent change of focus unless specifically configured otherwise
-       if ( this.constructor.static.cancelButtonMouseDownEvents ) {
-               return false;
-       }
-};
-
-/**
- * Handles mouse up events.
- *
- * @protected
- * @param {jQuery.Event} e Mouse up event
- */
-OO.ui.mixin.ButtonElement.prototype.onMouseUp = function ( e ) {
-       if ( this.isDisabled() || e.which !== OO.ui.MouseButtons.LEFT ) {
-               return;
-       }
-       this.$element.removeClass( 'oo-ui-buttonElement-pressed' );
-       // Stop listening for mouseup, since we only needed this once
-       this.getElementDocument().removeEventListener( 'mouseup', this.onMouseUpHandler, true );
-};
-
-/**
- * Handles mouse click events.
- *
- * @protected
- * @param {jQuery.Event} e Mouse click event
- * @fires click
- */
-OO.ui.mixin.ButtonElement.prototype.onClick = function ( e ) {
-       if ( !this.isDisabled() && e.which === OO.ui.MouseButtons.LEFT ) {
-               if ( this.emit( 'click' ) ) {
-                       return false;
-               }
-       }
-};
-
-/**
- * Handles key down events.
- *
- * @protected
- * @param {jQuery.Event} e Key down event
- */
-OO.ui.mixin.ButtonElement.prototype.onKeyDown = function ( e ) {
-       if ( this.isDisabled() || ( e.which !== OO.ui.Keys.SPACE && e.which !== OO.ui.Keys.ENTER ) ) {
-               return;
-       }
-       this.$element.addClass( 'oo-ui-buttonElement-pressed' );
-       // Run the keyup handler no matter where the key is when the button is let go, so we can
-       // reliably remove the pressed class
-       this.getElementDocument().addEventListener( 'keyup', this.onKeyUpHandler, true );
-};
-
-/**
- * Handles key up events.
- *
- * @protected
- * @param {jQuery.Event} e Key up event
- */
-OO.ui.mixin.ButtonElement.prototype.onKeyUp = function ( e ) {
-       if ( this.isDisabled() || ( e.which !== OO.ui.Keys.SPACE && e.which !== OO.ui.Keys.ENTER ) ) {
-               return;
-       }
-       this.$element.removeClass( 'oo-ui-buttonElement-pressed' );
-       // Stop listening for keyup, since we only needed this once
-       this.getElementDocument().removeEventListener( 'keyup', this.onKeyUpHandler, true );
-};
-
-/**
- * Handles key press events.
- *
- * @protected
- * @param {jQuery.Event} e Key press event
- * @fires click
- */
-OO.ui.mixin.ButtonElement.prototype.onKeyPress = function ( e ) {
-       if ( !this.isDisabled() && ( e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER ) ) {
-               if ( this.emit( 'click' ) ) {
-                       return false;
-               }
-       }
-};
-
-/**
- * Check if button has a frame.
- *
- * @return {boolean} Button is framed
- */
-OO.ui.mixin.ButtonElement.prototype.isFramed = function () {
-       return this.framed;
-};
-
-/**
- * Render the button with or without a frame. Omit the `framed` parameter to toggle the button frame on and off.
- *
- * @param {boolean} [framed] Make button framed, omit to toggle
- * @chainable
- */
-OO.ui.mixin.ButtonElement.prototype.toggleFramed = function ( framed ) {
-       framed = framed === undefined ? !this.framed : !!framed;
-       if ( framed !== this.framed ) {
-               this.framed = framed;
-               this.$element
-                       .toggleClass( 'oo-ui-buttonElement-frameless', !framed )
-                       .toggleClass( 'oo-ui-buttonElement-framed', framed );
-               this.updateThemeClasses();
-       }
-
-       return this;
-};
-
-/**
- * Set the button's active state.
- *
- * The active state occurs when a {@link OO.ui.ButtonOptionWidget ButtonOptionWidget} or
- * a {@link OO.ui.ToggleButtonWidget ToggleButtonWidget} is pressed. This method does nothing
- * for other button types.
- *
- * @param {boolean} value Make button active
- * @chainable
- */
-OO.ui.mixin.ButtonElement.prototype.setActive = function ( value ) {
-       this.active = !!value;
-       this.$element.toggleClass( 'oo-ui-buttonElement-active', this.active );
-       return this;
-};
-
-/**
- * Check if the button is active
- *
- * @return {boolean} The button is active
- */
-OO.ui.mixin.ButtonElement.prototype.isActive = function () {
-       return this.active;
-};
-
-/**
- * Any OOjs UI widget that contains other widgets (such as {@link OO.ui.ButtonWidget buttons} or
- * {@link OO.ui.OptionWidget options}) mixes in GroupElement. Adding, removing, and clearing
- * items from the group is done through the interface the class provides.
- * For more information, please see the [OOjs UI documentation on MediaWiki] [1].
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Elements/Groups
- *
- * @abstract
- * @class
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {jQuery} [$group] The container element created by the class. If this configuration
- *  is omitted, the group element will use a generated `<div>`.
- */
-OO.ui.mixin.GroupElement = function OoUiMixinGroupElement( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Properties
-       this.$group = null;
-       this.items = [];
-       this.aggregateItemEvents = {};
-
-       // Initialization
-       this.setGroupElement( config.$group || $( '<div>' ) );
-};
-
-/* Methods */
-
-/**
- * Set the group element.
- *
- * If an element is already set, items will be moved to the new element.
- *
- * @param {jQuery} $group Element to use as group
- */
-OO.ui.mixin.GroupElement.prototype.setGroupElement = function ( $group ) {
-       var i, len;
-
-       this.$group = $group;
-       for ( i = 0, len = this.items.length; i < len; i++ ) {
-               this.$group.append( this.items[ i ].$element );
-       }
-};
-
-/**
- * Check if a group contains no items.
- *
- * @return {boolean} Group is empty
- */
-OO.ui.mixin.GroupElement.prototype.isEmpty = function () {
-       return !this.items.length;
-};
-
-/**
- * Get all items in the group.
- *
- * The method returns an array of item references (e.g., [button1, button2, button3]) and is useful
- * when synchronizing groups of items, or whenever the references are required (e.g., when removing items
- * from a group).
- *
- * @return {OO.ui.Element[]} An array of items.
- */
-OO.ui.mixin.GroupElement.prototype.getItems = function () {
-       return this.items.slice( 0 );
-};
-
-/**
- * Get an item by its data.
- *
- * Only the first item with matching data will be returned. To return all matching items,
- * use the #getItemsFromData method.
- *
- * @param {Object} data Item data to search for
- * @return {OO.ui.Element|null} Item with equivalent data, `null` if none exists
- */
-OO.ui.mixin.GroupElement.prototype.getItemFromData = function ( data ) {
-       var i, len, item,
-               hash = OO.getHash( data );
-
-       for ( i = 0, len = this.items.length; i < len; i++ ) {
-               item = this.items[ i ];
-               if ( hash === OO.getHash( item.getData() ) ) {
-                       return item;
-               }
-       }
-
-       return null;
-};
-
-/**
- * Get items by their data.
- *
- * All items with matching data will be returned. To return only the first match, use the #getItemFromData method instead.
- *
- * @param {Object} data Item data to search for
- * @return {OO.ui.Element[]} Items with equivalent data
- */
-OO.ui.mixin.GroupElement.prototype.getItemsFromData = function ( data ) {
-       var i, len, item,
-               hash = OO.getHash( data ),
-               items = [];
-
-       for ( i = 0, len = this.items.length; i < len; i++ ) {
-               item = this.items[ i ];
-               if ( hash === OO.getHash( item.getData() ) ) {
-                       items.push( item );
-               }
-       }
-
-       return items;
-};
-
-/**
- * Aggregate the events emitted by the group.
- *
- * When events are aggregated, the group will listen to all contained items for the event,
- * and then emit the event under a new name. The new event will contain an additional leading
- * parameter containing the item that emitted the original event. Other arguments emitted from
- * the original event are passed through.
- *
- * @param {Object.<string,string|null>} events An object keyed by the name of the event that should be
- *  aggregated  (e.g., ‘click’) and the value of the new name to use (e.g., ‘groupClick’).
- *  A `null` value will remove aggregated events.
-
- * @throws {Error} An error is thrown if aggregation already exists.
- */
-OO.ui.mixin.GroupElement.prototype.aggregate = function ( events ) {
-       var i, len, item, add, remove, itemEvent, groupEvent;
-
-       for ( itemEvent in events ) {
-               groupEvent = events[ itemEvent ];
-
-               // Remove existing aggregated event
-               if ( Object.prototype.hasOwnProperty.call( this.aggregateItemEvents, itemEvent ) ) {
-                       // Don't allow duplicate aggregations
-                       if ( groupEvent ) {
-                               throw new Error( 'Duplicate item event aggregation for ' + itemEvent );
-                       }
-                       // Remove event aggregation from existing items
-                       for ( i = 0, len = this.items.length; i < len; i++ ) {
-                               item = this.items[ i ];
-                               if ( item.connect && item.disconnect ) {
-                                       remove = {};
-                                       remove[ itemEvent ] = [ 'emit', this.aggregateItemEvents[ itemEvent ], item ];
-                                       item.disconnect( this, remove );
-                               }
-                       }
-                       // Prevent future items from aggregating event
-                       delete this.aggregateItemEvents[ itemEvent ];
-               }
-
-               // Add new aggregate event
-               if ( groupEvent ) {
-                       // Make future items aggregate event
-                       this.aggregateItemEvents[ itemEvent ] = groupEvent;
-                       // Add event aggregation to existing items
-                       for ( i = 0, len = this.items.length; i < len; i++ ) {
-                               item = this.items[ i ];
-                               if ( item.connect && item.disconnect ) {
-                                       add = {};
-                                       add[ itemEvent ] = [ 'emit', groupEvent, item ];
-                                       item.connect( this, add );
-                               }
-                       }
-               }
-       }
-};
-
-/**
- * Add items to the group.
- *
- * Items will be added to the end of the group array unless the optional `index` parameter specifies
- * a different insertion point. Adding an existing item will move it to the end of the array or the point specified by the `index`.
- *
- * @param {OO.ui.Element[]} items An array of items to add to the group
- * @param {number} [index] Index of the insertion point
- * @chainable
- */
-OO.ui.mixin.GroupElement.prototype.addItems = function ( items, index ) {
-       var i, len, item, event, events, currentIndex,
-               itemElements = [];
-
-       for ( i = 0, len = items.length; i < len; i++ ) {
-               item = items[ i ];
-
-               // Check if item exists then remove it first, effectively "moving" it
-               currentIndex = this.items.indexOf( item );
-               if ( currentIndex >= 0 ) {
-                       this.removeItems( [ item ] );
-                       // Adjust index to compensate for removal
-                       if ( currentIndex < index ) {
-                               index--;
-                       }
-               }
-               // Add the item
-               if ( item.connect && item.disconnect && !$.isEmptyObject( this.aggregateItemEvents ) ) {
-                       events = {};
-                       for ( event in this.aggregateItemEvents ) {
-                               events[ event ] = [ 'emit', this.aggregateItemEvents[ event ], item ];
-                       }
-                       item.connect( this, events );
-               }
-               item.setElementGroup( this );
-               itemElements.push( item.$element.get( 0 ) );
-       }
-
-       if ( index === undefined || index < 0 || index >= this.items.length ) {
-               this.$group.append( itemElements );
-               this.items.push.apply( this.items, items );
-       } else if ( index === 0 ) {
-               this.$group.prepend( itemElements );
-               this.items.unshift.apply( this.items, items );
-       } else {
-               this.items[ index ].$element.before( itemElements );
-               this.items.splice.apply( this.items, [ index, 0 ].concat( items ) );
-       }
-
-       return this;
-};
-
-/**
- * Remove the specified items from a group.
- *
- * Removed items are detached (not removed) from the DOM so that they may be reused.
- * To remove all items from a group, you may wish to use the #clearItems method instead.
- *
- * @param {OO.ui.Element[]} items An array of items to remove
- * @chainable
- */
-OO.ui.mixin.GroupElement.prototype.removeItems = function ( items ) {
-       var i, len, item, index, remove, itemEvent;
-
-       // Remove specific items
-       for ( i = 0, len = items.length; i < len; i++ ) {
-               item = items[ i ];
-               index = this.items.indexOf( item );
-               if ( index !== -1 ) {
-                       if (
-                               item.connect && item.disconnect &&
-                               !$.isEmptyObject( this.aggregateItemEvents )
-                       ) {
-                               remove = {};
-                               if ( Object.prototype.hasOwnProperty.call( this.aggregateItemEvents, itemEvent ) ) {
-                                       remove[ itemEvent ] = [ 'emit', this.aggregateItemEvents[ itemEvent ], item ];
-                               }
-                               item.disconnect( this, remove );
-                       }
-                       item.setElementGroup( null );
-                       this.items.splice( index, 1 );
-                       item.$element.detach();
-               }
-       }
-
-       return this;
-};
-
-/**
- * Clear all items from the group.
- *
- * Cleared items are detached from the DOM, not removed, so that they may be reused.
- * To remove only a subset of items from a group, use the #removeItems method.
- *
- * @chainable
- */
-OO.ui.mixin.GroupElement.prototype.clearItems = function () {
-       var i, len, item, remove, itemEvent;
-
-       // Remove all items
-       for ( i = 0, len = this.items.length; i < len; i++ ) {
-               item = this.items[ i ];
-               if (
-                       item.connect && item.disconnect &&
-                       !$.isEmptyObject( this.aggregateItemEvents )
-               ) {
-                       remove = {};
-                       if ( Object.prototype.hasOwnProperty.call( this.aggregateItemEvents, itemEvent ) ) {
-                               remove[ itemEvent ] = [ 'emit', this.aggregateItemEvents[ itemEvent ], item ];
-                       }
-                       item.disconnect( this, remove );
-               }
-               item.setElementGroup( null );
-               item.$element.detach();
-       }
-
-       this.items = [];
-       return this;
-};
-
-/**
- * DraggableElement is a mixin class used to create elements that can be clicked
- * and dragged by a mouse to a new position within a group. This class must be used
- * in conjunction with OO.ui.mixin.DraggableGroupElement, which provides a container for
- * the draggable elements.
- *
- * @abstract
- * @class
- *
- * @constructor
- */
-OO.ui.mixin.DraggableElement = function OoUiMixinDraggableElement() {
-       // Properties
-       this.index = null;
-
-       // Initialize and events
-       this.$element
-               .attr( 'draggable', true )
-               .addClass( 'oo-ui-draggableElement' )
-               .on( {
-                       dragstart: this.onDragStart.bind( this ),
-                       dragover: this.onDragOver.bind( this ),
-                       dragend: this.onDragEnd.bind( this ),
-                       drop: this.onDrop.bind( this )
-               } );
-};
-
-OO.initClass( OO.ui.mixin.DraggableElement );
-
-/* Events */
-
-/**
- * @event dragstart
- *
- * A dragstart event is emitted when the user clicks and begins dragging an item.
- * @param {OO.ui.mixin.DraggableElement} item The item the user has clicked and is dragging with the mouse.
- */
-
-/**
- * @event dragend
- * A dragend event is emitted when the user drags an item and releases the mouse,
- * thus terminating the drag operation.
- */
-
-/**
- * @event drop
- * A drop event is emitted when the user drags an item and then releases the mouse button
- * over a valid target.
- */
-
-/* Static Properties */
-
-/**
- * @inheritdoc OO.ui.mixin.ButtonElement
- */
-OO.ui.mixin.DraggableElement.static.cancelButtonMouseDownEvents = false;
-
-/* Methods */
-
-/**
- * Respond to dragstart event.
- *
- * @private
- * @param {jQuery.Event} event jQuery event
- * @fires dragstart
- */
-OO.ui.mixin.DraggableElement.prototype.onDragStart = function ( e ) {
-       var dataTransfer = e.originalEvent.dataTransfer;
-       // Define drop effect
-       dataTransfer.dropEffect = 'none';
-       dataTransfer.effectAllowed = 'move';
-       // Support: Firefox
-       // We must set up a dataTransfer data property or Firefox seems to
-       // ignore the fact the element is draggable.
-       try {
-               dataTransfer.setData( 'application-x/OOjs-UI-draggable', this.getIndex() );
-       } catch ( err ) {
-               // The above is only for Firefox. Move on if it fails.
-       }
-       // Add dragging class
-       this.$element.addClass( 'oo-ui-draggableElement-dragging' );
-       // Emit event
-       this.emit( 'dragstart', this );
-       return true;
-};
-
-/**
- * Respond to dragend event.
- *
- * @private
- * @fires dragend
- */
-OO.ui.mixin.DraggableElement.prototype.onDragEnd = function () {
-       this.$element.removeClass( 'oo-ui-draggableElement-dragging' );
-       this.emit( 'dragend' );
-};
-
-/**
- * Handle drop event.
- *
- * @private
- * @param {jQuery.Event} event jQuery event
- * @fires drop
- */
-OO.ui.mixin.DraggableElement.prototype.onDrop = function ( e ) {
-       e.preventDefault();
-       this.emit( 'drop', e );
-};
-
-/**
- * In order for drag/drop to work, the dragover event must
- * return false and stop propogation.
- *
- * @private
- */
-OO.ui.mixin.DraggableElement.prototype.onDragOver = function ( e ) {
-       e.preventDefault();
-};
-
-/**
- * Set item index.
- * Store it in the DOM so we can access from the widget drag event
- *
- * @private
- * @param {number} Item index
- */
-OO.ui.mixin.DraggableElement.prototype.setIndex = function ( index ) {
-       if ( this.index !== index ) {
-               this.index = index;
-               this.$element.data( 'index', index );
-       }
-};
-
-/**
- * Get item index
- *
- * @private
- * @return {number} Item index
- */
-OO.ui.mixin.DraggableElement.prototype.getIndex = function () {
-       return this.index;
-};
-
-/**
- * DraggableGroupElement is a mixin class used to create a group element to
- * contain draggable elements, which are items that can be clicked and dragged by a mouse.
- * The class is used with OO.ui.mixin.DraggableElement.
- *
- * @abstract
- * @class
- * @mixins OO.ui.mixin.GroupElement
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {string} [orientation] Item orientation: 'horizontal' or 'vertical'. The orientation
- *  should match the layout of the items. Items displayed in a single row
- *  or in several rows should use horizontal orientation. The vertical orientation should only be
- *  used when the items are displayed in a single column. Defaults to 'vertical'
- */
-OO.ui.mixin.DraggableGroupElement = function OoUiMixinDraggableGroupElement( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Parent constructor
-       OO.ui.mixin.GroupElement.call( this, config );
-
-       // Properties
-       this.orientation = config.orientation || 'vertical';
-       this.dragItem = null;
-       this.itemDragOver = null;
-       this.itemKeys = {};
-       this.sideInsertion = '';
-
-       // Events
-       this.aggregate( {
-               dragstart: 'itemDragStart',
-               dragend: 'itemDragEnd',
-               drop: 'itemDrop'
-       } );
-       this.connect( this, {
-               itemDragStart: 'onItemDragStart',
-               itemDrop: 'onItemDrop',
-               itemDragEnd: 'onItemDragEnd'
-       } );
-       this.$element.on( {
-               dragover: this.onDragOver.bind( this ),
-               dragleave: this.onDragLeave.bind( this )
-       } );
-
-       // Initialize
-       if ( Array.isArray( config.items ) ) {
-               this.addItems( config.items );
-       }
-       this.$placeholder = $( '<div>' )
-               .addClass( 'oo-ui-draggableGroupElement-placeholder' );
-       this.$element
-               .addClass( 'oo-ui-draggableGroupElement' )
-               .append( this.$status )
-               .toggleClass( 'oo-ui-draggableGroupElement-horizontal', this.orientation === 'horizontal' )
-               .prepend( this.$placeholder );
-};
-
-/* Setup */
-OO.mixinClass( OO.ui.mixin.DraggableGroupElement, OO.ui.mixin.GroupElement );
-
-/* Events */
-
-/**
- * A 'reorder' event is emitted when the order of items in the group changes.
- *
- * @event reorder
- * @param {OO.ui.mixin.DraggableElement} item Reordered item
- * @param {number} [newIndex] New index for the item
- */
-
-/* Methods */
-
-/**
- * Respond to item drag start event
- *
- * @private
- * @param {OO.ui.mixin.DraggableElement} item Dragged item
- */
-OO.ui.mixin.DraggableGroupElement.prototype.onItemDragStart = function ( item ) {
-       var i, len;
-
-       // Map the index of each object
-       for ( i = 0, len = this.items.length; i < len; i++ ) {
-               this.items[ i ].setIndex( i );
-       }
-
-       if ( this.orientation === 'horizontal' ) {
-               // Set the height of the indicator
-               this.$placeholder.css( {
-                       height: item.$element.outerHeight(),
-                       width: 2
-               } );
-       } else {
-               // Set the width of the indicator
-               this.$placeholder.css( {
-                       height: 2,
-                       width: item.$element.outerWidth()
-               } );
-       }
-       this.setDragItem( item );
-};
-
-/**
- * Respond to item drag end event
- *
- * @private
- */
-OO.ui.mixin.DraggableGroupElement.prototype.onItemDragEnd = function () {
-       this.unsetDragItem();
-       return false;
-};
-
-/**
- * Handle drop event and switch the order of the items accordingly
- *
- * @private
- * @param {OO.ui.mixin.DraggableElement} item Dropped item
- * @fires reorder
- */
-OO.ui.mixin.DraggableGroupElement.prototype.onItemDrop = function ( item ) {
-       var toIndex = item.getIndex();
-       // Check if the dropped item is from the current group
-       // TODO: Figure out a way to configure a list of legally droppable
-       // elements even if they are not yet in the list
-       if ( this.getDragItem() ) {
-               // If the insertion point is 'after', the insertion index
-               // is shifted to the right (or to the left in RTL, hence 'after')
-               if ( this.sideInsertion === 'after' ) {
-                       toIndex++;
-               }
-               // Emit change event
-               this.emit( 'reorder', this.getDragItem(), toIndex );
-       }
-       this.unsetDragItem();
-       // Return false to prevent propogation
-       return false;
-};
-
-/**
- * Handle dragleave event.
- *
- * @private
- */
-OO.ui.mixin.DraggableGroupElement.prototype.onDragLeave = function () {
-       // This means the item was dragged outside the widget
-       this.$placeholder
-               .css( 'left', 0 )
-               .addClass( 'oo-ui-element-hidden' );
-};
-
-/**
- * Respond to dragover event
- *
- * @private
- * @param {jQuery.Event} event Event details
- */
-OO.ui.mixin.DraggableGroupElement.prototype.onDragOver = function ( e ) {
-       var dragOverObj, $optionWidget, itemOffset, itemMidpoint, itemBoundingRect,
-               itemSize, cssOutput, dragPosition, itemIndex, itemPosition,
-               clientX = e.originalEvent.clientX,
-               clientY = e.originalEvent.clientY;
-
-       // Get the OptionWidget item we are dragging over
-       dragOverObj = this.getElementDocument().elementFromPoint( clientX, clientY );
-       $optionWidget = $( dragOverObj ).closest( '.oo-ui-draggableElement' );
-       if ( $optionWidget[ 0 ] ) {
-               itemOffset = $optionWidget.offset();
-               itemBoundingRect = $optionWidget[ 0 ].getBoundingClientRect();
-               itemPosition = $optionWidget.position();
-               itemIndex = $optionWidget.data( 'index' );
-       }
-
-       if (
-               itemOffset &&
-               this.isDragging() &&
-               itemIndex !== this.getDragItem().getIndex()
-       ) {
-               if ( this.orientation === 'horizontal' ) {
-                       // Calculate where the mouse is relative to the item width
-                       itemSize = itemBoundingRect.width;
-                       itemMidpoint = itemBoundingRect.left + itemSize / 2;
-                       dragPosition = clientX;
-                       // Which side of the item we hover over will dictate
-                       // where the placeholder will appear, on the left or
-                       // on the right
-                       cssOutput = {
-                               left: dragPosition < itemMidpoint ? itemPosition.left : itemPosition.left + itemSize,
-                               top: itemPosition.top
-                       };
-               } else {
-                       // Calculate where the mouse is relative to the item height
-                       itemSize = itemBoundingRect.height;
-                       itemMidpoint = itemBoundingRect.top + itemSize / 2;
-                       dragPosition = clientY;
-                       // Which side of the item we hover over will dictate
-                       // where the placeholder will appear, on the top or
-                       // on the bottom
-                       cssOutput = {
-                               top: dragPosition < itemMidpoint ? itemPosition.top : itemPosition.top + itemSize,
-                               left: itemPosition.left
-                       };
-               }
-               // Store whether we are before or after an item to rearrange
-               // For horizontal layout, we need to account for RTL, as this is flipped
-               if (  this.orientation === 'horizontal' && this.$element.css( 'direction' ) === 'rtl' ) {
-                       this.sideInsertion = dragPosition < itemMidpoint ? 'after' : 'before';
-               } else {
-                       this.sideInsertion = dragPosition < itemMidpoint ? 'before' : 'after';
-               }
-               // Add drop indicator between objects
-               this.$placeholder
-                       .css( cssOutput )
-                       .removeClass( 'oo-ui-element-hidden' );
-       } else {
-               // This means the item was dragged outside the widget
-               this.$placeholder
-                       .css( 'left', 0 )
-                       .addClass( 'oo-ui-element-hidden' );
-       }
-       // Prevent default
-       e.preventDefault();
-};
-
-/**
- * Set a dragged item
- *
- * @param {OO.ui.mixin.DraggableElement} item Dragged item
- */
-OO.ui.mixin.DraggableGroupElement.prototype.setDragItem = function ( item ) {
-       this.dragItem = item;
-};
-
-/**
- * Unset the current dragged item
- */
-OO.ui.mixin.DraggableGroupElement.prototype.unsetDragItem = function () {
-       this.dragItem = null;
-       this.itemDragOver = null;
-       this.$placeholder.addClass( 'oo-ui-element-hidden' );
-       this.sideInsertion = '';
-};
-
-/**
- * Get the item that is currently being dragged.
- *
- * @return {OO.ui.mixin.DraggableElement|null} The currently dragged item, or `null` if no item is being dragged
- */
-OO.ui.mixin.DraggableGroupElement.prototype.getDragItem = function () {
-       return this.dragItem;
-};
-
-/**
- * Check if an item in the group is currently being dragged.
- *
- * @return {Boolean} Item is being dragged
- */
-OO.ui.mixin.DraggableGroupElement.prototype.isDragging = function () {
-       return this.getDragItem() !== null;
-};
-
-/**
- * IconElement is often mixed into other classes to generate an icon.
- * Icons are graphics, about the size of normal text. They are used to aid the user
- * in locating a control or to convey information in a space-efficient way. See the
- * [OOjs UI documentation on MediaWiki] [1] for a list of icons
- * included in the library.
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Icons,_Indicators,_and_Labels#Icons
- *
- * @abstract
- * @class
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {jQuery} [$icon] The icon element created by the class. If this configuration is omitted,
- *  the icon element will use a generated `<span>`. To use a different HTML tag, or to specify that
- *  the icon element be set to an existing icon instead of the one generated by this class, set a
- *  value using a jQuery selection. For example:
- *
- *      // Use a <div> tag instead of a <span>
- *     $icon: $("<div>")
- *     // Use an existing icon element instead of the one generated by the class
- *     $icon: this.$element
- *     // Use an icon element from a child widget
- *     $icon: this.childwidget.$element
- * @cfg {Object|string} [icon=''] The symbolic name of the icon (e.g., ‘remove’ or ‘menu’), or a map of
- *  symbolic names.  A map is used for i18n purposes and contains a `default` icon
- *  name and additional names keyed by language code. The `default` name is used when no icon is keyed
- *  by the user's language.
- *
- *  Example of an i18n map:
- *
- *     { default: 'bold-a', en: 'bold-b', de: 'bold-f' }
- *  See the [OOjs UI documentation on MediaWiki] [2] for a list of icons included in the library.
- * [2]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Icons,_Indicators,_and_Labels#Icons
- * @cfg {string|Function} [iconTitle] A text string used as the icon title, or a function that returns title
- *  text. The icon title is displayed when users move the mouse over the icon.
- */
-OO.ui.mixin.IconElement = function OoUiMixinIconElement( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Properties
-       this.$icon = null;
-       this.icon = null;
-       this.iconTitle = null;
-
-       // Initialization
-       this.setIcon( config.icon || this.constructor.static.icon );
-       this.setIconTitle( config.iconTitle || this.constructor.static.iconTitle );
-       this.setIconElement( config.$icon || $( '<span>' ) );
-};
-
-/* Setup */
-
-OO.initClass( OO.ui.mixin.IconElement );
-
-/* Static Properties */
-
-/**
- * The symbolic name of the icon (e.g., ‘remove’ or ‘menu’), or a map of symbolic names. A map is used
- * for i18n purposes and contains a `default` icon name and additional names keyed by
- * language code. The `default` name is used when no icon is keyed by the user's language.
- *
- * Example of an i18n map:
- *
- *     { default: 'bold-a', en: 'bold-b', de: 'bold-f' }
- *
- * Note: the static property will be overridden if the #icon configuration is used.
- *
- * @static
- * @inheritable
- * @property {Object|string}
- */
-OO.ui.mixin.IconElement.static.icon = null;
-
-/**
- * The icon title, displayed when users move the mouse over the icon. The value can be text, a
- * function that returns title text, or `null` for no title.
- *
- * The static property will be overridden if the #iconTitle configuration is used.
- *
- * @static
- * @inheritable
- * @property {string|Function|null}
- */
-OO.ui.mixin.IconElement.static.iconTitle = null;
-
-/* Methods */
-
-/**
- * Set the icon element. This method is used to retarget an icon mixin so that its functionality
- * applies to the specified icon element instead of the one created by the class. If an icon
- * element is already set, the mixin’s effect on that element is removed. Generated CSS classes
- * and mixin methods will no longer affect the element.
- *
- * @param {jQuery} $icon Element to use as icon
- */
-OO.ui.mixin.IconElement.prototype.setIconElement = function ( $icon ) {
-       if ( this.$icon ) {
-               this.$icon
-                       .removeClass( 'oo-ui-iconElement-icon oo-ui-icon-' + this.icon )
-                       .removeAttr( 'title' );
-       }
-
-       this.$icon = $icon
-               .addClass( 'oo-ui-iconElement-icon' )
-               .toggleClass( 'oo-ui-icon-' + this.icon, !!this.icon );
-       if ( this.iconTitle !== null ) {
-               this.$icon.attr( 'title', this.iconTitle );
-       }
-
-       this.updateThemeClasses();
-};
-
-/**
- * Set icon by symbolic name (e.g., ‘remove’ or ‘menu’). Use `null` to remove an icon.
- * The icon parameter can also be set to a map of icon names. See the #icon config setting
- * for an example.
- *
- * @param {Object|string|null} icon A symbolic icon name, a {@link #icon map of icon names} keyed
- *  by language code, or `null` to remove the icon.
- * @chainable
- */
-OO.ui.mixin.IconElement.prototype.setIcon = function ( icon ) {
-       icon = OO.isPlainObject( icon ) ? OO.ui.getLocalValue( icon, null, 'default' ) : icon;
-       icon = typeof icon === 'string' && icon.trim().length ? icon.trim() : null;
-
-       if ( this.icon !== icon ) {
-               if ( this.$icon ) {
-                       if ( this.icon !== null ) {
-                               this.$icon.removeClass( 'oo-ui-icon-' + this.icon );
-                       }
-                       if ( icon !== null ) {
-                               this.$icon.addClass( 'oo-ui-icon-' + icon );
-                       }
-               }
-               this.icon = icon;
-       }
-
-       this.$element.toggleClass( 'oo-ui-iconElement', !!this.icon );
-       this.updateThemeClasses();
-
-       return this;
-};
-
-/**
- * Set the icon title. Use `null` to remove the title.
- *
- * @param {string|Function|null} iconTitle A text string used as the icon title,
- *  a function that returns title text, or `null` for no title.
- * @chainable
- */
-OO.ui.mixin.IconElement.prototype.setIconTitle = function ( iconTitle ) {
-       iconTitle = typeof iconTitle === 'function' ||
-               ( typeof iconTitle === 'string' && iconTitle.length ) ?
-                       OO.ui.resolveMsg( iconTitle ) : null;
-
-       if ( this.iconTitle !== iconTitle ) {
-               this.iconTitle = iconTitle;
-               if ( this.$icon ) {
-                       if ( this.iconTitle !== null ) {
-                               this.$icon.attr( 'title', iconTitle );
-                       } else {
-                               this.$icon.removeAttr( 'title' );
-                       }
-               }
-       }
-
-       return this;
-};
-
-/**
- * Get the symbolic name of the icon.
- *
- * @return {string} Icon name
- */
-OO.ui.mixin.IconElement.prototype.getIcon = function () {
-       return this.icon;
-};
-
-/**
- * Get the icon title. The title text is displayed when a user moves the mouse over the icon.
- *
- * @return {string} Icon title text
- */
-OO.ui.mixin.IconElement.prototype.getIconTitle = function () {
-       return this.iconTitle;
-};
-
-/**
- * IndicatorElement is often mixed into other classes to generate an indicator.
- * Indicators are small graphics that are generally used in two ways:
- *
- * - To draw attention to the status of an item. For example, an indicator might be
- *   used to show that an item in a list has errors that need to be resolved.
- * - To clarify the function of a control that acts in an exceptional way (a button
- *   that opens a menu instead of performing an action directly, for example).
- *
- * For a list of indicators included in the library, please see the
- * [OOjs UI documentation on MediaWiki] [1].
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Icons,_Indicators,_and_Labels#Indicators
- *
- * @abstract
- * @class
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {jQuery} [$indicator] The indicator element created by the class. If this
- *  configuration is omitted, the indicator element will use a generated `<span>`.
- * @cfg {string} [indicator] Symbolic name of the indicator (e.g., ‘alert’ or  ‘down’).
- *  See the [OOjs UI documentation on MediaWiki][2] for a list of indicators included
- *  in the library.
- * [2]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Icons,_Indicators,_and_Labels#Indicators
- * @cfg {string|Function} [indicatorTitle] A text string used as the indicator title,
- *  or a function that returns title text. The indicator title is displayed when users move
- *  the mouse over the indicator.
- */
-OO.ui.mixin.IndicatorElement = function OoUiMixinIndicatorElement( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Properties
-       this.$indicator = null;
-       this.indicator = null;
-       this.indicatorTitle = null;
-
-       // Initialization
-       this.setIndicator( config.indicator || this.constructor.static.indicator );
-       this.setIndicatorTitle( config.indicatorTitle || this.constructor.static.indicatorTitle );
-       this.setIndicatorElement( config.$indicator || $( '<span>' ) );
-};
-
-/* Setup */
-
-OO.initClass( OO.ui.mixin.IndicatorElement );
-
-/* Static Properties */
-
-/**
- * Symbolic name of the indicator (e.g., ‘alert’ or  ‘down’).
- * The static property will be overridden if the #indicator configuration is used.
- *
- * @static
- * @inheritable
- * @property {string|null}
- */
-OO.ui.mixin.IndicatorElement.static.indicator = null;
-
-/**
- * A text string used as the indicator title, a function that returns title text, or `null`
- * for no title. The static property will be overridden if the #indicatorTitle configuration is used.
- *
- * @static
- * @inheritable
- * @property {string|Function|null}
- */
-OO.ui.mixin.IndicatorElement.static.indicatorTitle = null;
-
-/* Methods */
-
-/**
- * Set the indicator element.
- *
- * If an element is already set, it will be cleaned up before setting up the new element.
- *
- * @param {jQuery} $indicator Element to use as indicator
- */
-OO.ui.mixin.IndicatorElement.prototype.setIndicatorElement = function ( $indicator ) {
-       if ( this.$indicator ) {
-               this.$indicator
-                       .removeClass( 'oo-ui-indicatorElement-indicator oo-ui-indicator-' + this.indicator )
-                       .removeAttr( 'title' );
-       }
-
-       this.$indicator = $indicator
-               .addClass( 'oo-ui-indicatorElement-indicator' )
-               .toggleClass( 'oo-ui-indicator-' + this.indicator, !!this.indicator );
-       if ( this.indicatorTitle !== null ) {
-               this.$indicator.attr( 'title', this.indicatorTitle );
-       }
-
-       this.updateThemeClasses();
-};
-
-/**
- * Set the indicator by its symbolic name: ‘alert’, ‘down’, ‘next’, ‘previous’, ‘required’, ‘up’. Use `null` to remove the indicator.
- *
- * @param {string|null} indicator Symbolic name of indicator, or `null` for no indicator
- * @chainable
- */
-OO.ui.mixin.IndicatorElement.prototype.setIndicator = function ( indicator ) {
-       indicator = typeof indicator === 'string' && indicator.length ? indicator.trim() : null;
-
-       if ( this.indicator !== indicator ) {
-               if ( this.$indicator ) {
-                       if ( this.indicator !== null ) {
-                               this.$indicator.removeClass( 'oo-ui-indicator-' + this.indicator );
-                       }
-                       if ( indicator !== null ) {
-                               this.$indicator.addClass( 'oo-ui-indicator-' + indicator );
-                       }
-               }
-               this.indicator = indicator;
-       }
-
-       this.$element.toggleClass( 'oo-ui-indicatorElement', !!this.indicator );
-       this.updateThemeClasses();
-
-       return this;
-};
-
-/**
- * Set the indicator title.
- *
- * The title is displayed when a user moves the mouse over the indicator.
- *
- * @param {string|Function|null} indicator Indicator title text, a function that returns text, or
- *   `null` for no indicator title
- * @chainable
- */
-OO.ui.mixin.IndicatorElement.prototype.setIndicatorTitle = function ( indicatorTitle ) {
-       indicatorTitle = typeof indicatorTitle === 'function' ||
-               ( typeof indicatorTitle === 'string' && indicatorTitle.length ) ?
-                       OO.ui.resolveMsg( indicatorTitle ) : null;
-
-       if ( this.indicatorTitle !== indicatorTitle ) {
-               this.indicatorTitle = indicatorTitle;
-               if ( this.$indicator ) {
-                       if ( this.indicatorTitle !== null ) {
-                               this.$indicator.attr( 'title', indicatorTitle );
-                       } else {
-                               this.$indicator.removeAttr( 'title' );
-                       }
-               }
-       }
-
-       return this;
-};
-
-/**
- * Get the symbolic name of the indicator (e.g., ‘alert’ or  ‘down’).
- *
- * @return {string} Symbolic name of indicator
- */
-OO.ui.mixin.IndicatorElement.prototype.getIndicator = function () {
-       return this.indicator;
-};
-
-/**
- * Get the indicator title.
- *
- * The title is displayed when a user moves the mouse over the indicator.
- *
- * @return {string} Indicator title text
- */
-OO.ui.mixin.IndicatorElement.prototype.getIndicatorTitle = function () {
-       return this.indicatorTitle;
-};
-
-/**
- * LabelElement is often mixed into other classes to generate a label, which
- * helps identify the function of an interface element.
- * See the [OOjs UI documentation on MediaWiki] [1] for more information.
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Icons,_Indicators,_and_Labels#Labels
- *
- * @abstract
- * @class
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {jQuery} [$label] The label element created by the class. If this
- *  configuration is omitted, the label element will use a generated `<span>`.
- * @cfg {jQuery|string|Function|OO.ui.HtmlSnippet} [label] The label text. The label can be specified
- *  as a plaintext string, a jQuery selection of elements, or a function that will produce a string
- *  in the future. See the [OOjs UI documentation on MediaWiki] [2] for examples.
- *  [2]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Icons,_Indicators,_and_Labels#Labels
- * @cfg {boolean} [autoFitLabel=true] Fit the label to the width of the parent element.
- *  The label will be truncated to fit if necessary.
- */
-OO.ui.mixin.LabelElement = function OoUiMixinLabelElement( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Properties
-       this.$label = null;
-       this.label = null;
-       this.autoFitLabel = config.autoFitLabel === undefined || !!config.autoFitLabel;
-
-       // Initialization
-       this.setLabel( config.label || this.constructor.static.label );
-       this.setLabelElement( config.$label || $( '<span>' ) );
-};
-
-/* Setup */
-
-OO.initClass( OO.ui.mixin.LabelElement );
-
-/* Events */
-
-/**
- * @event labelChange
- * @param {string} value
- */
-
-/* Static Properties */
-
-/**
- * The label text. The label can be specified as a plaintext string, a function that will
- * produce a string in the future, or `null` for no label. The static value will
- * be overridden if a label is specified with the #label config option.
- *
- * @static
- * @inheritable
- * @property {string|Function|null}
- */
-OO.ui.mixin.LabelElement.static.label = null;
-
-/* Methods */
-
-/**
- * Set the label element.
- *
- * If an element is already set, it will be cleaned up before setting up the new element.
- *
- * @param {jQuery} $label Element to use as label
- */
-OO.ui.mixin.LabelElement.prototype.setLabelElement = function ( $label ) {
-       if ( this.$label ) {
-               this.$label.removeClass( 'oo-ui-labelElement-label' ).empty();
-       }
-
-       this.$label = $label.addClass( 'oo-ui-labelElement-label' );
-       this.setLabelContent( this.label );
-};
-
-/**
- * Set the label.
- *
- * An empty string will result in the label being hidden. A string containing only whitespace will
- * be converted to a single `&nbsp;`.
- *
- * @param {jQuery|string|OO.ui.HtmlSnippet|Function|null} label Label nodes; text; a function that returns nodes or
- *  text; or null for no label
- * @chainable
- */
-OO.ui.mixin.LabelElement.prototype.setLabel = function ( label ) {
-       label = typeof label === 'function' ? OO.ui.resolveMsg( label ) : label;
-       label = ( ( typeof label === 'string' && label.length ) || label instanceof jQuery || label instanceof OO.ui.HtmlSnippet ) ? label : null;
-
-       this.$element.toggleClass( 'oo-ui-labelElement', !!label );
-
-       if ( this.label !== label ) {
-               if ( this.$label ) {
-                       this.setLabelContent( label );
-               }
-               this.label = label;
-               this.emit( 'labelChange' );
-       }
-
-       return this;
-};
-
-/**
- * Get the label.
- *
- * @return {jQuery|string|Function|null} Label nodes; text; a function that returns nodes or
- *  text; or null for no label
- */
-OO.ui.mixin.LabelElement.prototype.getLabel = function () {
-       return this.label;
-};
-
-/**
- * Fit the label.
- *
- * @chainable
- */
-OO.ui.mixin.LabelElement.prototype.fitLabel = function () {
-       if ( this.$label && this.$label.autoEllipsis && this.autoFitLabel ) {
-               this.$label.autoEllipsis( { hasSpan: false, tooltip: true } );
-       }
-
-       return this;
-};
-
-/**
- * Set the content of the label.
- *
- * Do not call this method until after the label element has been set by #setLabelElement.
- *
- * @private
- * @param {jQuery|string|Function|null} label Label nodes; text; a function that returns nodes or
- *  text; or null for no label
- */
-OO.ui.mixin.LabelElement.prototype.setLabelContent = function ( label ) {
-       if ( typeof label === 'string' ) {
-               if ( label.match( /^\s*$/ ) ) {
-                       // Convert whitespace only string to a single non-breaking space
-                       this.$label.html( '&nbsp;' );
-               } else {
-                       this.$label.text( label );
-               }
-       } else if ( label instanceof OO.ui.HtmlSnippet ) {
-               this.$label.html( label.toString() );
-       } else if ( label instanceof jQuery ) {
-               this.$label.empty().append( label );
-       } else {
-               this.$label.empty();
-       }
-};
-
-/**
- * LookupElement is a mixin that creates a {@link OO.ui.FloatingMenuSelectWidget menu} of suggested values for
- * a {@link OO.ui.TextInputWidget text input widget}. Suggested values are based on the characters the user types
- * into the text input field and, in general, the menu is only displayed when the user types. If a suggested value is chosen
- * from the lookup menu, that value becomes the value of the input field.
- *
- * Note that a new menu of suggested items is displayed when a value is chosen from the lookup menu. If this is
- * not the desired behavior, disable lookup menus with the #setLookupsDisabled method, then set the value, then
- * re-enable lookups.
- *
- * See the [OOjs UI demos][1] for an example.
- *
- * [1]: https://tools.wmflabs.org/oojs-ui/oojs-ui/demos/index.html#widgets-apex-vector-ltr
- *
- * @class
- * @abstract
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {jQuery} [$overlay] Overlay for the lookup menu; defaults to relative positioning
- * @cfg {jQuery} [$container=this.$element] The container element. The lookup menu is rendered beneath the specified element.
- * @cfg {boolean} [allowSuggestionsWhenEmpty=false] Request and display a lookup menu when the text input is empty.
- *  By default, the lookup menu is not generated and displayed until the user begins to type.
- * @cfg {boolean} [highlightFirst=true] Whether the first lookup result should be highlighted (so, that the user can
- *  take it over into the input with simply pressing return) automatically or not.
- */
-OO.ui.mixin.LookupElement = function OoUiMixinLookupElement( config ) {
-       // Configuration initialization
-       config = $.extend( { highlightFirst: true }, config );
-
-       // Mixin constructors
-       OO.ui.mixin.RequestManager.call( this, config );
-
-       // Properties
-       this.$overlay = config.$overlay || this.$element;
-       this.lookupMenu = new OO.ui.FloatingMenuSelectWidget( {
-               widget: this,
-               input: this,
-               $container: config.$container || this.$element
-       } );
-
-       this.allowSuggestionsWhenEmpty = config.allowSuggestionsWhenEmpty || false;
-
-       this.lookupsDisabled = false;
-       this.lookupInputFocused = false;
-       this.lookupHighlightFirstItem = config.highlightFirst;
-
-       // Events
-       this.$input.on( {
-               focus: this.onLookupInputFocus.bind( this ),
-               blur: this.onLookupInputBlur.bind( this ),
-               mousedown: this.onLookupInputMouseDown.bind( this )
-       } );
-       this.connect( this, { change: 'onLookupInputChange' } );
-       this.lookupMenu.connect( this, {
-               toggle: 'onLookupMenuToggle',
-               choose: 'onLookupMenuItemChoose'
-       } );
-
-       // Initialization
-       this.$element.addClass( 'oo-ui-lookupElement' );
-       this.lookupMenu.$element.addClass( 'oo-ui-lookupElement-menu' );
-       this.$overlay.append( this.lookupMenu.$element );
-};
-
-/* Setup */
-
-OO.mixinClass( OO.ui.mixin.LookupElement, OO.ui.mixin.RequestManager );
-
-/* Methods */
-
-/**
- * Handle input focus event.
- *
- * @protected
- * @param {jQuery.Event} e Input focus event
- */
-OO.ui.mixin.LookupElement.prototype.onLookupInputFocus = function () {
-       this.lookupInputFocused = true;
-       this.populateLookupMenu();
-};
-
-/**
- * Handle input blur event.
- *
- * @protected
- * @param {jQuery.Event} e Input blur event
- */
-OO.ui.mixin.LookupElement.prototype.onLookupInputBlur = function () {
-       this.closeLookupMenu();
-       this.lookupInputFocused = false;
-};
-
-/**
- * Handle input mouse down event.
- *
- * @protected
- * @param {jQuery.Event} e Input mouse down event
- */
-OO.ui.mixin.LookupElement.prototype.onLookupInputMouseDown = function () {
-       // Only open the menu if the input was already focused.
-       // This way we allow the user to open the menu again after closing it with Esc
-       // by clicking in the input. Opening (and populating) the menu when initially
-       // clicking into the input is handled by the focus handler.
-       if ( this.lookupInputFocused && !this.lookupMenu.isVisible() ) {
-               this.populateLookupMenu();
-       }
-};
-
-/**
- * Handle input change event.
- *
- * @protected
- * @param {string} value New input value
- */
-OO.ui.mixin.LookupElement.prototype.onLookupInputChange = function () {
-       if ( this.lookupInputFocused ) {
-               this.populateLookupMenu();
-       }
-};
-
-/**
- * Handle the lookup menu being shown/hidden.
- *
- * @protected
- * @param {boolean} visible Whether the lookup menu is now visible.
- */
-OO.ui.mixin.LookupElement.prototype.onLookupMenuToggle = function ( visible ) {
-       if ( !visible ) {
-               // When the menu is hidden, abort any active request and clear the menu.
-               // This has to be done here in addition to closeLookupMenu(), because
-               // MenuSelectWidget will close itself when the user presses Esc.
-               this.abortLookupRequest();
-               this.lookupMenu.clearItems();
-       }
-};
-
-/**
- * Handle menu item 'choose' event, updating the text input value to the value of the clicked item.
- *
- * @protected
- * @param {OO.ui.MenuOptionWidget} item Selected item
- */
-OO.ui.mixin.LookupElement.prototype.onLookupMenuItemChoose = function ( item ) {
-       this.setValue( item.getData() );
-};
-
-/**
- * Get lookup menu.
- *
- * @private
- * @return {OO.ui.FloatingMenuSelectWidget}
- */
-OO.ui.mixin.LookupElement.prototype.getLookupMenu = function () {
-       return this.lookupMenu;
-};
-
-/**
- * Disable or re-enable lookups.
- *
- * When lookups are disabled, calls to #populateLookupMenu will be ignored.
- *
- * @param {boolean} disabled Disable lookups
- */
-OO.ui.mixin.LookupElement.prototype.setLookupsDisabled = function ( disabled ) {
-       this.lookupsDisabled = !!disabled;
-};
-
-/**
- * Open the menu. If there are no entries in the menu, this does nothing.
- *
- * @private
- * @chainable
- */
-OO.ui.mixin.LookupElement.prototype.openLookupMenu = function () {
-       if ( !this.lookupMenu.isEmpty() ) {
-               this.lookupMenu.toggle( true );
-       }
-       return this;
-};
-
-/**
- * Close the menu, empty it, and abort any pending request.
- *
- * @private
- * @chainable
- */
-OO.ui.mixin.LookupElement.prototype.closeLookupMenu = function () {
-       this.lookupMenu.toggle( false );
-       this.abortLookupRequest();
-       this.lookupMenu.clearItems();
-       return this;
-};
-
-/**
- * Request menu items based on the input's current value, and when they arrive,
- * populate the menu with these items and show the menu.
- *
- * If lookups have been disabled with #setLookupsDisabled, this function does nothing.
- *
- * @private
- * @chainable
- */
-OO.ui.mixin.LookupElement.prototype.populateLookupMenu = function () {
-       var widget = this,
-               value = this.getValue();
-
-       if ( this.lookupsDisabled || this.isReadOnly() ) {
-               return;
-       }
-
-       // If the input is empty, clear the menu, unless suggestions when empty are allowed.
-       if ( !this.allowSuggestionsWhenEmpty && value === '' ) {
-               this.closeLookupMenu();
-       // Skip population if there is already a request pending for the current value
-       } else if ( value !== this.lookupQuery ) {
-               this.getLookupMenuItems()
-                       .done( function ( items ) {
-                               widget.lookupMenu.clearItems();
-                               if ( items.length ) {
-                                       widget.lookupMenu
-                                               .addItems( items )
-                                               .toggle( true );
-                                       widget.initializeLookupMenuSelection();
-                               } else {
-                                       widget.lookupMenu.toggle( false );
-                               }
-                       } )
-                       .fail( function () {
-                               widget.lookupMenu.clearItems();
-                       } );
-       }
-
-       return this;
-};
-
-/**
- * Highlight the first selectable item in the menu, if configured.
- *
- * @private
- * @chainable
- */
-OO.ui.mixin.LookupElement.prototype.initializeLookupMenuSelection = function () {
-       if ( this.lookupHighlightFirstItem && !this.lookupMenu.getSelectedItem() ) {
-               this.lookupMenu.highlightItem( this.lookupMenu.getFirstSelectableItem() );
-       }
-};
-
-/**
- * Get lookup menu items for the current query.
- *
- * @private
- * @return {jQuery.Promise} Promise object which will be passed menu items as the first argument of
- *   the done event. If the request was aborted to make way for a subsequent request, this promise
- *   will not be rejected: it will remain pending forever.
- */
-OO.ui.mixin.LookupElement.prototype.getLookupMenuItems = function () {
-       return this.getRequestData().then( function ( data ) {
-               return this.getLookupMenuOptionsFromData( data );
-       }.bind( this ) );
-};
-
-/**
- * Abort the currently pending lookup request, if any.
- *
- * @private
- */
-OO.ui.mixin.LookupElement.prototype.abortLookupRequest = function () {
-       this.abortRequest();
-};
-
-/**
- * Get a new request object of the current lookup query value.
- *
- * @protected
- * @method
- * @abstract
- * @return {jQuery.Promise} jQuery AJAX object, or promise object with an .abort() method
- */
-OO.ui.mixin.LookupElement.prototype.getLookupRequest = null;
-
-/**
- * Pre-process data returned by the request from #getLookupRequest.
- *
- * The return value of this function will be cached, and any further queries for the given value
- * will use the cache rather than doing API requests.
- *
- * @protected
- * @method
- * @abstract
- * @param {Mixed} response Response from server
- * @return {Mixed} Cached result data
- */
-OO.ui.mixin.LookupElement.prototype.getLookupCacheDataFromResponse = null;
-
-/**
- * Get a list of menu option widgets from the (possibly cached) data returned by
- * #getLookupCacheDataFromResponse.
- *
- * @protected
- * @method
- * @abstract
- * @param {Mixed} data Cached result data, usually an array
- * @return {OO.ui.MenuOptionWidget[]} Menu items
- */
-OO.ui.mixin.LookupElement.prototype.getLookupMenuOptionsFromData = null;
-
-/**
- * Set the read-only state of the widget.
- *
- * This will also disable/enable the lookups functionality.
- *
- * @param {boolean} readOnly Make input read-only
- * @chainable
- */
-OO.ui.mixin.LookupElement.prototype.setReadOnly = function ( readOnly ) {
-       // Parent method
-       // Note: Calling #setReadOnly this way assumes this is mixed into an OO.ui.TextInputWidget
-       OO.ui.TextInputWidget.prototype.setReadOnly.call( this, readOnly );
-
-       // During construction, #setReadOnly is called before the OO.ui.mixin.LookupElement constructor
-       if ( this.isReadOnly() && this.lookupMenu ) {
-               this.closeLookupMenu();
-       }
-
-       return this;
-};
-
-/**
- * @inheritdoc OO.ui.mixin.RequestManager
- */
-OO.ui.mixin.LookupElement.prototype.getRequestQuery = function () {
-       return this.getValue();
-};
-
-/**
- * @inheritdoc OO.ui.mixin.RequestManager
- */
-OO.ui.mixin.LookupElement.prototype.getRequest = function () {
-       return this.getLookupRequest();
-};
-
-/**
- * @inheritdoc OO.ui.mixin.RequestManager
- */
-OO.ui.mixin.LookupElement.prototype.getRequestCacheDataFromResponse = function ( response ) {
-       return this.getLookupCacheDataFromResponse( response );
-};
-
-/**
- * PopupElement is mixed into other classes to generate a {@link OO.ui.PopupWidget popup widget}.
- * A popup is a container for content. It is overlaid and positioned absolutely. By default, each
- * popup has an anchor, which is an arrow-like protrusion that points toward the popup’s origin.
- * See {@link OO.ui.PopupWidget PopupWidget} for an example.
- *
- * @abstract
- * @class
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {Object} [popup] Configuration to pass to popup
- * @cfg {boolean} [popup.autoClose=true] Popup auto-closes when it loses focus
- */
-OO.ui.mixin.PopupElement = function OoUiMixinPopupElement( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Properties
-       this.popup = new OO.ui.PopupWidget( $.extend(
-               { autoClose: true },
-               config.popup,
-               { $autoCloseIgnore: this.$element }
-       ) );
-};
-
-/* Methods */
-
-/**
- * Get popup.
- *
- * @return {OO.ui.PopupWidget} Popup widget
- */
-OO.ui.mixin.PopupElement.prototype.getPopup = function () {
-       return this.popup;
-};
-
-/**
- * The FlaggedElement class is an attribute mixin, meaning that it is used to add
- * additional functionality to an element created by another class. The class provides
- * a ‘flags’ property assigned the name (or an array of names) of styling flags,
- * which are used to customize the look and feel of a widget to better describe its
- * importance and functionality.
- *
- * The library currently contains the following styling flags for general use:
- *
- * - **progressive**:  Progressive styling is applied to convey that the widget will move the user forward in a process.
- * - **destructive**: Destructive styling is applied to convey that the widget will remove something.
- * - **constructive**: Constructive styling is applied to convey that the widget will create something.
- *
- * The flags affect the appearance of the buttons:
- *
- *     @example
- *     // FlaggedElement is mixed into ButtonWidget to provide styling flags
- *     var button1 = new OO.ui.ButtonWidget( {
- *         label: 'Constructive',
- *         flags: 'constructive'
- *     } );
- *     var button2 = new OO.ui.ButtonWidget( {
- *         label: 'Destructive',
- *         flags: 'destructive'
- *     } );
- *     var button3 = new OO.ui.ButtonWidget( {
- *         label: 'Progressive',
- *         flags: 'progressive'
- *     } );
- *     $( 'body' ).append( button1.$element, button2.$element, button3.$element );
- *
- * {@link OO.ui.ActionWidget ActionWidgets}, which are a special kind of button that execute an action, use these flags: **primary** and **safe**.
- * Please see the [OOjs UI documentation on MediaWiki] [1] for more information.
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Elements/Flagged
- *
- * @abstract
- * @class
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {string|string[]} [flags] The name or names of the flags (e.g., 'constructive' or 'primary') to apply.
- *  Please see the [OOjs UI documentation on MediaWiki] [2] for more information about available flags.
- *  [2]: https://www.mediawiki.org/wiki/OOjs_UI/Elements/Flagged
- * @cfg {jQuery} [$flagged] The flagged element. By default,
- *  the flagged functionality is applied to the element created by the class ($element).
- *  If a different element is specified, the flagged functionality will be applied to it instead.
- */
-OO.ui.mixin.FlaggedElement = function OoUiMixinFlaggedElement( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Properties
-       this.flags = {};
-       this.$flagged = null;
-
-       // Initialization
-       this.setFlags( config.flags );
-       this.setFlaggedElement( config.$flagged || this.$element );
-};
-
-/* Events */
-
-/**
- * @event flag
- * A flag event is emitted when the #clearFlags or #setFlags methods are used. The `changes`
- * parameter contains the name of each modified flag and indicates whether it was
- * added or removed.
- *
- * @param {Object.<string,boolean>} changes Object keyed by flag name. A Boolean `true` indicates
- * that the flag was added, `false` that the flag was removed.
- */
-
-/* Methods */
-
-/**
- * Set the flagged element.
- *
- * This method is used to retarget a flagged mixin so that its functionality applies to the specified element.
- * If an element is already set, the method will remove the mixin’s effect on that element.
- *
- * @param {jQuery} $flagged Element that should be flagged
- */
-OO.ui.mixin.FlaggedElement.prototype.setFlaggedElement = function ( $flagged ) {
-       var classNames = Object.keys( this.flags ).map( function ( flag ) {
-               return 'oo-ui-flaggedElement-' + flag;
-       } ).join( ' ' );
-
-       if ( this.$flagged ) {
-               this.$flagged.removeClass( classNames );
-       }
-
-       this.$flagged = $flagged.addClass( classNames );
-};
-
-/**
- * Check if the specified flag is set.
- *
- * @param {string} flag Name of flag
- * @return {boolean} The flag is set
- */
-OO.ui.mixin.FlaggedElement.prototype.hasFlag = function ( flag ) {
-       // This may be called before the constructor, thus before this.flags is set
-       return this.flags && ( flag in this.flags );
-};
-
-/**
- * Get the names of all flags set.
- *
- * @return {string[]} Flag names
- */
-OO.ui.mixin.FlaggedElement.prototype.getFlags = function () {
-       // This may be called before the constructor, thus before this.flags is set
-       return Object.keys( this.flags || {} );
-};
-
-/**
- * Clear all flags.
- *
- * @chainable
- * @fires flag
- */
-OO.ui.mixin.FlaggedElement.prototype.clearFlags = function () {
-       var flag, className,
-               changes = {},
-               remove = [],
-               classPrefix = 'oo-ui-flaggedElement-';
-
-       for ( flag in this.flags ) {
-               className = classPrefix + flag;
-               changes[ flag ] = false;
-               delete this.flags[ flag ];
-               remove.push( className );
-       }
-
-       if ( this.$flagged ) {
-               this.$flagged.removeClass( remove.join( ' ' ) );
-       }
-
-       this.updateThemeClasses();
-       this.emit( 'flag', changes );
-
-       return this;
-};
-
-/**
- * Add one or more flags.
- *
- * @param {string|string[]|Object.<string, boolean>} flags A flag name, an array of flag names,
- *  or an object keyed by flag name with a boolean value that indicates whether the flag should
- *  be added (`true`) or removed (`false`).
- * @chainable
- * @fires flag
- */
-OO.ui.mixin.FlaggedElement.prototype.setFlags = function ( flags ) {
-       var i, len, flag, className,
-               changes = {},
-               add = [],
-               remove = [],
-               classPrefix = 'oo-ui-flaggedElement-';
-
-       if ( typeof flags === 'string' ) {
-               className = classPrefix + flags;
-               // Set
-               if ( !this.flags[ flags ] ) {
-                       this.flags[ flags ] = true;
-                       add.push( className );
-               }
-       } else if ( Array.isArray( flags ) ) {
-               for ( i = 0, len = flags.length; i < len; i++ ) {
-                       flag = flags[ i ];
-                       className = classPrefix + flag;
-                       // Set
-                       if ( !this.flags[ flag ] ) {
-                               changes[ flag ] = true;
-                               this.flags[ flag ] = true;
-                               add.push( className );
-                       }
-               }
-       } else if ( OO.isPlainObject( flags ) ) {
-               for ( flag in flags ) {
-                       className = classPrefix + flag;
-                       if ( flags[ flag ] ) {
-                               // Set
-                               if ( !this.flags[ flag ] ) {
-                                       changes[ flag ] = true;
-                                       this.flags[ flag ] = true;
-                                       add.push( className );
-                               }
-                       } else {
-                               // Remove
-                               if ( this.flags[ flag ] ) {
-                                       changes[ flag ] = false;
-                                       delete this.flags[ flag ];
-                                       remove.push( className );
-                               }
-                       }
-               }
-       }
-
-       if ( this.$flagged ) {
-               this.$flagged
-                       .addClass( add.join( ' ' ) )
-                       .removeClass( remove.join( ' ' ) );
-       }
-
-       this.updateThemeClasses();
-       this.emit( 'flag', changes );
-
-       return this;
-};
-
-/**
- * TitledElement is mixed into other classes to provide a `title` attribute.
- * Titles are rendered by the browser and are made visible when the user moves
- * the mouse over the element. Titles are not visible on touch devices.
- *
- *     @example
- *     // TitledElement provides a 'title' attribute to the
- *     // ButtonWidget class
- *     var button = new OO.ui.ButtonWidget( {
- *         label: 'Button with Title',
- *         title: 'I am a button'
- *     } );
- *     $( 'body' ).append( button.$element );
- *
- * @abstract
- * @class
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {jQuery} [$titled] The element to which the `title` attribute is applied.
- *  If this config is omitted, the title functionality is applied to $element, the
- *  element created by the class.
- * @cfg {string|Function} [title] The title text or a function that returns text. If
- *  this config is omitted, the value of the {@link #static-title static title} property is used.
- */
-OO.ui.mixin.TitledElement = function OoUiMixinTitledElement( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Properties
-       this.$titled = null;
-       this.title = null;
-
-       // Initialization
-       this.setTitle( config.title || this.constructor.static.title );
-       this.setTitledElement( config.$titled || this.$element );
-};
-
-/* Setup */
-
-OO.initClass( OO.ui.mixin.TitledElement );
-
-/* Static Properties */
-
-/**
- * The title text, a function that returns text, or `null` for no title. The value of the static property
- * is overridden if the #title config option is used.
- *
- * @static
- * @inheritable
- * @property {string|Function|null}
- */
-OO.ui.mixin.TitledElement.static.title = null;
-
-/* Methods */
-
-/**
- * Set the titled element.
- *
- * This method is used to retarget a titledElement mixin so that its functionality applies to the specified element.
- * If an element is already set, the mixin’s effect on that element is removed before the new element is set up.
- *
- * @param {jQuery} $titled Element that should use the 'titled' functionality
- */
-OO.ui.mixin.TitledElement.prototype.setTitledElement = function ( $titled ) {
-       if ( this.$titled ) {
-               this.$titled.removeAttr( 'title' );
-       }
-
-       this.$titled = $titled;
-       if ( this.title ) {
-               this.$titled.attr( 'title', this.title );
-       }
-};
-
-/**
- * Set title.
- *
- * @param {string|Function|null} title Title text, a function that returns text, or `null` for no title
- * @chainable
- */
-OO.ui.mixin.TitledElement.prototype.setTitle = function ( title ) {
-       title = typeof title === 'function' ? OO.ui.resolveMsg( title ) : title;
-       title = ( typeof title === 'string' && title.length ) ? title : null;
-
-       if ( this.title !== title ) {
-               if ( this.$titled ) {
-                       if ( title !== null ) {
-                               this.$titled.attr( 'title', title );
-                       } else {
-                               this.$titled.removeAttr( 'title' );
-                       }
-               }
-               this.title = title;
-       }
-
-       return this;
-};
-
-/**
- * Get title.
- *
- * @return {string} Title string
- */
-OO.ui.mixin.TitledElement.prototype.getTitle = function () {
-       return this.title;
-};
-
-/**
- * Element that can be automatically clipped to visible boundaries.
- *
- * Whenever the element's natural height changes, you have to call
- * {@link OO.ui.mixin.ClippableElement#clip} to make sure it's still
- * clipping correctly.
- *
- * The dimensions of #$clippableContainer will be compared to the boundaries of the
- * nearest scrollable container. If #$clippableContainer is too tall and/or too wide,
- * then #$clippable will be given a fixed reduced height and/or width and will be made
- * scrollable. By default, #$clippable and #$clippableContainer are the same element,
- * but you can build a static footer by setting #$clippableContainer to an element that contains
- * #$clippable and the footer.
- *
- * @abstract
- * @class
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {jQuery} [$clippable] Node to clip, assigned to #$clippable, omit to use #$element
- * @cfg {jQuery} [$clippableContainer] Node to keep visible, assigned to #$clippableContainer,
- *   omit to use #$clippable
- */
-OO.ui.mixin.ClippableElement = function OoUiMixinClippableElement( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Properties
-       this.$clippable = null;
-       this.$clippableContainer = null;
-       this.clipping = false;
-       this.clippedHorizontally = false;
-       this.clippedVertically = false;
-       this.$clippableScrollableContainer = null;
-       this.$clippableScroller = null;
-       this.$clippableWindow = null;
-       this.idealWidth = null;
-       this.idealHeight = null;
-       this.onClippableScrollHandler = this.clip.bind( this );
-       this.onClippableWindowResizeHandler = this.clip.bind( this );
-
-       // Initialization
-       if ( config.$clippableContainer ) {
-               this.setClippableContainer( config.$clippableContainer );
-       }
-       this.setClippableElement( config.$clippable || this.$element );
-};
-
-/* Methods */
-
-/**
- * Set clippable element.
- *
- * If an element is already set, it will be cleaned up before setting up the new element.
- *
- * @param {jQuery} $clippable Element to make clippable
- */
-OO.ui.mixin.ClippableElement.prototype.setClippableElement = function ( $clippable ) {
-       if ( this.$clippable ) {
-               this.$clippable.removeClass( 'oo-ui-clippableElement-clippable' );
-               this.$clippable.css( { width: '', height: '', overflowX: '', overflowY: '' } );
-               OO.ui.Element.static.reconsiderScrollbars( this.$clippable[ 0 ] );
-       }
-
-       this.$clippable = $clippable.addClass( 'oo-ui-clippableElement-clippable' );
-       this.clip();
-};
-
-/**
- * Set clippable container.
- *
- * This is the container that will be measured when deciding whether to clip. When clipping,
- * #$clippable will be resized in order to keep the clippable container fully visible.
- *
- * If the clippable container is unset, #$clippable will be used.
- *
- * @param {jQuery|null} $clippableContainer Container to keep visible, or null to unset
- */
-OO.ui.mixin.ClippableElement.prototype.setClippableContainer = function ( $clippableContainer ) {
-       this.$clippableContainer = $clippableContainer;
-       if ( this.$clippable ) {
-               this.clip();
-       }
-};
-
-/**
- * Toggle clipping.
- *
- * Do not turn clipping on until after the element is attached to the DOM and visible.
- *
- * @param {boolean} [clipping] Enable clipping, omit to toggle
- * @chainable
- */
-OO.ui.mixin.ClippableElement.prototype.toggleClipping = function ( clipping ) {
-       clipping = clipping === undefined ? !this.clipping : !!clipping;
-
-       if ( this.clipping !== clipping ) {
-               this.clipping = clipping;
-               if ( clipping ) {
-                       this.$clippableScrollableContainer = $( this.getClosestScrollableElementContainer() );
-                       // If the clippable container is the root, we have to listen to scroll events and check
-                       // jQuery.scrollTop on the window because of browser inconsistencies
-                       this.$clippableScroller = this.$clippableScrollableContainer.is( 'html, body' ) ?
-                               $( OO.ui.Element.static.getWindow( this.$clippableScrollableContainer ) ) :
-                               this.$clippableScrollableContainer;
-                       this.$clippableScroller.on( 'scroll', this.onClippableScrollHandler );
-                       this.$clippableWindow = $( this.getElementWindow() )
-                               .on( 'resize', this.onClippableWindowResizeHandler );
-                       // Initial clip after visible
-                       this.clip();
-               } else {
-                       this.$clippable.css( { width: '', height: '', overflowX: '', overflowY: '' } );
-                       OO.ui.Element.static.reconsiderScrollbars( this.$clippable[ 0 ] );
-
-                       this.$clippableScrollableContainer = null;
-                       this.$clippableScroller.off( 'scroll', this.onClippableScrollHandler );
-                       this.$clippableScroller = null;
-                       this.$clippableWindow.off( 'resize', this.onClippableWindowResizeHandler );
-                       this.$clippableWindow = null;
-               }
-       }
-
-       return this;
-};
-
-/**
- * Check if the element will be clipped to fit the visible area of the nearest scrollable container.
- *
- * @return {boolean} Element will be clipped to the visible area
- */
-OO.ui.mixin.ClippableElement.prototype.isClipping = function () {
-       return this.clipping;
-};
-
-/**
- * Check if the bottom or right of the element is being clipped by the nearest scrollable container.
- *
- * @return {boolean} Part of the element is being clipped
- */
-OO.ui.mixin.ClippableElement.prototype.isClipped = function () {
-       return this.clippedHorizontally || this.clippedVertically;
-};
-
-/**
- * Check if the right of the element is being clipped by the nearest scrollable container.
- *
- * @return {boolean} Part of the element is being clipped
- */
-OO.ui.mixin.ClippableElement.prototype.isClippedHorizontally = function () {
-       return this.clippedHorizontally;
-};
-
-/**
- * Check if the bottom of the element is being clipped by the nearest scrollable container.
- *
- * @return {boolean} Part of the element is being clipped
- */
-OO.ui.mixin.ClippableElement.prototype.isClippedVertically = function () {
-       return this.clippedVertically;
-};
-
-/**
- * Set the ideal size. These are the dimensions the element will have when it's not being clipped.
- *
- * @param {number|string} [width] Width as a number of pixels or CSS string with unit suffix
- * @param {number|string} [height] Height as a number of pixels or CSS string with unit suffix
- */
-OO.ui.mixin.ClippableElement.prototype.setIdealSize = function ( width, height ) {
-       this.idealWidth = width;
-       this.idealHeight = height;
-
-       if ( !this.clipping ) {
-               // Update dimensions
-               this.$clippable.css( { width: width, height: height } );
-       }
-       // While clipping, idealWidth and idealHeight are not considered
-};
-
-/**
- * Clip element to visible boundaries and allow scrolling when needed. Call this method when
- * the element's natural height changes.
- *
- * Element will be clipped the bottom or right of the element is within 10px of the edge of, or
- * overlapped by, the visible area of the nearest scrollable container.
- *
- * @chainable
- */
-OO.ui.mixin.ClippableElement.prototype.clip = function () {
-       var $container, extraHeight, extraWidth, ccOffset,
-               $scrollableContainer, scOffset, scHeight, scWidth,
-               ccWidth, scrollerIsWindow, scrollTop, scrollLeft,
-               desiredWidth, desiredHeight, allotedWidth, allotedHeight,
-               naturalWidth, naturalHeight, clipWidth, clipHeight,
-               buffer = 7; // Chosen by fair dice roll
-
-       if ( !this.clipping ) {
-               // this.$clippableScrollableContainer and this.$clippableWindow are null, so the below will fail
-               return this;
-       }
-
-       $container = this.$clippableContainer || this.$clippable;
-       extraHeight = $container.outerHeight() - this.$clippable.outerHeight();
-       extraWidth = $container.outerWidth() - this.$clippable.outerWidth();
-       ccOffset = $container.offset();
-       $scrollableContainer = this.$clippableScrollableContainer.is( 'html, body' ) ?
-               this.$clippableWindow : this.$clippableScrollableContainer;
-       scOffset = $scrollableContainer.offset() || { top: 0, left: 0 };
-       scHeight = $scrollableContainer.innerHeight() - buffer;
-       scWidth = $scrollableContainer.innerWidth() - buffer;
-       ccWidth = $container.outerWidth() + buffer;
-       scrollerIsWindow = this.$clippableScroller[ 0 ] === this.$clippableWindow[ 0 ];
-       scrollTop = scrollerIsWindow ? this.$clippableScroller.scrollTop() : 0;
-       scrollLeft = scrollerIsWindow ? this.$clippableScroller.scrollLeft() : 0;
-       desiredWidth = ccOffset.left < 0 ?
-               ccWidth + ccOffset.left :
-               ( scOffset.left + scrollLeft + scWidth ) - ccOffset.left;
-       desiredHeight = ( scOffset.top + scrollTop + scHeight ) - ccOffset.top;
-       allotedWidth = Math.ceil( desiredWidth - extraWidth );
-       allotedHeight = Math.ceil( desiredHeight - extraHeight );
-       naturalWidth = this.$clippable.prop( 'scrollWidth' );
-       naturalHeight = this.$clippable.prop( 'scrollHeight' );
-       clipWidth = allotedWidth < naturalWidth;
-       clipHeight = allotedHeight < naturalHeight;
-
-       if ( clipWidth ) {
-               this.$clippable.css( { overflowX: 'scroll', width: Math.max( 0, allotedWidth ) } );
-       } else {
-               this.$clippable.css( { width: this.idealWidth ? this.idealWidth - extraWidth : '', overflowX: '' } );
-       }
-       if ( clipHeight ) {
-               this.$clippable.css( { overflowY: 'scroll', height: Math.max( 0, allotedHeight ) } );
-       } else {
-               this.$clippable.css( { height: this.idealHeight ? this.idealHeight - extraHeight : '', overflowY: '' } );
-       }
-
-       // If we stopped clipping in at least one of the dimensions
-       if ( ( this.clippedHorizontally && !clipWidth ) || ( this.clippedVertically && !clipHeight ) ) {
-               OO.ui.Element.static.reconsiderScrollbars( this.$clippable[ 0 ] );
-       }
-
-       this.clippedHorizontally = clipWidth;
-       this.clippedVertically = clipHeight;
-
-       return this;
-};
-
-/**
- * Element that will stick under a specified container, even when it is inserted elsewhere in the
- * document (for example, in a OO.ui.Window's $overlay).
- *
- * The elements's position is automatically calculated and maintained when window is resized or the
- * page is scrolled. If you reposition the container manually, you have to call #position to make
- * sure the element is still placed correctly.
- *
- * As positioning is only possible when both the element and the container are attached to the DOM
- * and visible, it's only done after you call #togglePositioning. You might want to do this inside
- * the #toggle method to display a floating popup, for example.
- *
- * @abstract
- * @class
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {jQuery} [$floatable] Node to position, assigned to #$floatable, omit to use #$element
- * @cfg {jQuery} [$floatableContainer] Node to position below
- */
-OO.ui.mixin.FloatableElement = function OoUiMixinFloatableElement( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Properties
-       this.$floatable = null;
-       this.$floatableContainer = null;
-       this.$floatableWindow = null;
-       this.$floatableClosestScrollable = null;
-       this.onFloatableScrollHandler = this.position.bind( this );
-       this.onFloatableWindowResizeHandler = this.position.bind( this );
-
-       // Initialization
-       this.setFloatableContainer( config.$floatableContainer );
-       this.setFloatableElement( config.$floatable || this.$element );
-};
-
-/* Methods */
-
-/**
- * Set floatable element.
- *
- * If an element is already set, it will be cleaned up before setting up the new element.
- *
- * @param {jQuery} $floatable Element to make floatable
- */
-OO.ui.mixin.FloatableElement.prototype.setFloatableElement = function ( $floatable ) {
-       if ( this.$floatable ) {
-               this.$floatable.removeClass( 'oo-ui-floatableElement-floatable' );
-               this.$floatable.css( { left: '', top: '' } );
-       }
-
-       this.$floatable = $floatable.addClass( 'oo-ui-floatableElement-floatable' );
-       this.position();
-};
-
-/**
- * Set floatable container.
- *
- * The element will be always positioned under the specified container.
- *
- * @param {jQuery|null} $floatableContainer Container to keep visible, or null to unset
- */
-OO.ui.mixin.FloatableElement.prototype.setFloatableContainer = function ( $floatableContainer ) {
-       this.$floatableContainer = $floatableContainer;
-       if ( this.$floatable ) {
-               this.position();
-       }
-};
-
-/**
- * Toggle positioning.
- *
- * Do not turn positioning on until after the element is attached to the DOM and visible.
- *
- * @param {boolean} [positioning] Enable positioning, omit to toggle
- * @chainable
- */
-OO.ui.mixin.FloatableElement.prototype.togglePositioning = function ( positioning ) {
-       var closestScrollableOfContainer, closestScrollableOfFloatable;
-
-       positioning = positioning === undefined ? !this.positioning : !!positioning;
-
-       if ( this.positioning !== positioning ) {
-               this.positioning = positioning;
-
-               closestScrollableOfContainer = OO.ui.Element.static.getClosestScrollableContainer( this.$floatableContainer[ 0 ] );
-               closestScrollableOfFloatable = OO.ui.Element.static.getClosestScrollableContainer( this.$floatable[ 0 ] );
-               if ( closestScrollableOfContainer !== closestScrollableOfFloatable ) {
-                       // If the scrollable is the root, we have to listen to scroll events
-                       // on the window because of browser inconsistencies (or do we? someone should verify this)
-                       if ( $( closestScrollableOfContainer ).is( 'html, body' ) ) {
-                               closestScrollableOfContainer = OO.ui.Element.static.getWindow( closestScrollableOfContainer );
-                       }
-               }
-
-               if ( positioning ) {
-                       this.$floatableWindow = $( this.getElementWindow() );
-                       this.$floatableWindow.on( 'resize', this.onFloatableWindowResizeHandler );
-
-                       if ( closestScrollableOfContainer !== closestScrollableOfFloatable ) {
-                               this.$floatableClosestScrollable = $( closestScrollableOfContainer );
-                               this.$floatableClosestScrollable.on( 'scroll', this.onFloatableScrollHandler );
-                       }
-
-                       // Initial position after visible
-                       this.position();
-               } else {
-                       if ( this.$floatableWindow ) {
-                               this.$floatableWindow.off( 'resize', this.onFloatableWindowResizeHandler );
-                               this.$floatableWindow = null;
-                       }
-
-                       if ( this.$floatableClosestScrollable ) {
-                               this.$floatableClosestScrollable.off( 'scroll', this.onFloatableScrollHandler );
-                               this.$floatableClosestScrollable = null;
-                       }
-
-                       this.$floatable.css( { left: '', top: '' } );
-               }
-       }
-
-       return this;
-};
-
-/**
- * Position the floatable below its container.
- *
- * This should only be done when both of them are attached to the DOM and visible.
- *
- * @chainable
- */
-OO.ui.mixin.FloatableElement.prototype.position = function () {
-       var pos;
-
-       if ( !this.positioning ) {
-               return this;
-       }
-
-       pos = OO.ui.Element.static.getRelativePosition( this.$floatableContainer, this.$floatable.offsetParent() );
-
-       // Position under container
-       pos.top += this.$floatableContainer.height();
-       this.$floatable.css( pos );
-
-       // We updated the position, so re-evaluate the clipping state.
-       // (ClippableElement does not listen to 'scroll' events on $floatableContainer's parent, and so
-       // will not notice the need to update itself.)
-       // TODO: This is terrible, we shouldn't need to know about ClippableElement at all here. Why does
-       // it not listen to the right events in the right places?
-       if ( this.clip ) {
-               this.clip();
-       }
-
-       return this;
-};
-
-/**
- * AccessKeyedElement is mixed into other classes to provide an `accesskey` attribute.
- * Accesskeys allow an user to go to a specific element by using
- * a shortcut combination of a browser specific keys + the key
- * set to the field.
- *
- *     @example
- *     // AccessKeyedElement provides an 'accesskey' attribute to the
- *     // ButtonWidget class
- *     var button = new OO.ui.ButtonWidget( {
- *         label: 'Button with Accesskey',
- *         accessKey: 'k'
- *     } );
- *     $( 'body' ).append( button.$element );
- *
- * @abstract
- * @class
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {jQuery} [$accessKeyed] The element to which the `accesskey` attribute is applied.
- *  If this config is omitted, the accesskey functionality is applied to $element, the
- *  element created by the class.
- * @cfg {string|Function} [accessKey] The key or a function that returns the key. If
- *  this config is omitted, no accesskey will be added.
- */
-OO.ui.mixin.AccessKeyedElement = function OoUiMixinAccessKeyedElement( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Properties
-       this.$accessKeyed = null;
-       this.accessKey = null;
-
-       // Initialization
-       this.setAccessKey( config.accessKey || null );
-       this.setAccessKeyedElement( config.$accessKeyed || this.$element );
-};
-
-/* Setup */
-
-OO.initClass( OO.ui.mixin.AccessKeyedElement );
-
-/* Static Properties */
-
-/**
- * The access key, a function that returns a key, or `null` for no accesskey.
- *
- * @static
- * @inheritable
- * @property {string|Function|null}
- */
-OO.ui.mixin.AccessKeyedElement.static.accessKey = null;
-
-/* Methods */
-
-/**
- * Set the accesskeyed element.
- *
- * This method is used to retarget a AccessKeyedElement mixin so that its functionality applies to the specified element.
- * If an element is already set, the mixin's effect on that element is removed before the new element is set up.
- *
- * @param {jQuery} $accessKeyed Element that should use the 'accesskeyes' functionality
- */
-OO.ui.mixin.AccessKeyedElement.prototype.setAccessKeyedElement = function ( $accessKeyed ) {
-       if ( this.$accessKeyed ) {
-               this.$accessKeyed.removeAttr( 'accesskey' );
-       }
-
-       this.$accessKeyed = $accessKeyed;
-       if ( this.accessKey ) {
-               this.$accessKeyed.attr( 'accesskey', this.accessKey );
-       }
-};
-
-/**
- * Set accesskey.
- *
- * @param {string|Function|null} accesskey Key, a function that returns a key, or `null` for no accesskey
- * @chainable
- */
-OO.ui.mixin.AccessKeyedElement.prototype.setAccessKey = function ( accessKey ) {
-       accessKey = typeof accessKey === 'string' ? OO.ui.resolveMsg( accessKey ) : null;
-
-       if ( this.accessKey !== accessKey ) {
-               if ( this.$accessKeyed ) {
-                       if ( accessKey !== null ) {
-                               this.$accessKeyed.attr( 'accesskey', accessKey );
-                       } else {
-                               this.$accessKeyed.removeAttr( 'accesskey' );
-                       }
-               }
-               this.accessKey = accessKey;
-       }
-
-       return this;
-};
-
-/**
- * Get accesskey.
- *
- * @return {string} accessKey string
- */
-OO.ui.mixin.AccessKeyedElement.prototype.getAccessKey = function () {
-       return this.accessKey;
-};
-
-/**
- * Tools, together with {@link OO.ui.ToolGroup toolgroups}, constitute {@link OO.ui.Toolbar toolbars}.
- * Each tool is configured with a static name, title, and icon and is customized with the command to carry
- * out when the tool is selected. Tools must also be registered with a {@link OO.ui.ToolFactory tool factory},
- * which creates the tools on demand.
- *
- * Every Tool subclass must implement two methods:
- *
- * - {@link #onUpdateState}
- * - {@link #onSelect}
- *
- * Tools are added to toolgroups ({@link OO.ui.ListToolGroup ListToolGroup},
- * {@link OO.ui.BarToolGroup BarToolGroup}, or {@link OO.ui.MenuToolGroup MenuToolGroup}), which determine how
- * the tool is displayed in the toolbar. See {@link OO.ui.Toolbar toolbars} for an example.
- *
- * For more information, please see the [OOjs UI documentation on MediaWiki][1].
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Toolbars
- *
- * @abstract
- * @class
- * @extends OO.ui.Widget
- * @mixins OO.ui.mixin.IconElement
- * @mixins OO.ui.mixin.FlaggedElement
- * @mixins OO.ui.mixin.TabIndexedElement
- *
- * @constructor
- * @param {OO.ui.ToolGroup} toolGroup
- * @param {Object} [config] Configuration options
- * @cfg {string|Function} [title] Title text or a function that returns text. If this config is omitted, the value of
- *  the {@link #static-title static title} property is used.
- *
- *  The title is used in different ways depending on the type of toolgroup that contains the tool. The
- *  title is used as a tooltip if the tool is part of a {@link OO.ui.BarToolGroup bar} toolgroup, or as the label text if the tool is
- *  part of a {@link OO.ui.ListToolGroup list} or {@link OO.ui.MenuToolGroup menu} toolgroup.
- *
- *  For bar toolgroups, a description of the accelerator key is appended to the title if an accelerator key
- *  is associated with an action by the same name as the tool and accelerator functionality has been added to the application.
- *  To add accelerator key functionality, you must subclass OO.ui.Toolbar and override the {@link OO.ui.Toolbar#getToolAccelerator getToolAccelerator} method.
- */
-OO.ui.Tool = function OoUiTool( toolGroup, config ) {
-       // Allow passing positional parameters inside the config object
-       if ( OO.isPlainObject( toolGroup ) && config === undefined ) {
-               config = toolGroup;
-               toolGroup = config.toolGroup;
-       }
-
-       // Configuration initialization
-       config = config || {};
-
-       // Parent constructor
-       OO.ui.Tool.parent.call( this, config );
-
-       // Properties
-       this.toolGroup = toolGroup;
-       this.toolbar = this.toolGroup.getToolbar();
-       this.active = false;
-       this.$title = $( '<span>' );
-       this.$accel = $( '<span>' );
-       this.$link = $( '<a>' );
-       this.title = null;
-
-       // Mixin constructors
-       OO.ui.mixin.IconElement.call( this, config );
-       OO.ui.mixin.FlaggedElement.call( this, config );
-       OO.ui.mixin.TabIndexedElement.call( this, $.extend( {}, config, { $tabIndexed: this.$link } ) );
-
-       // Events
-       this.toolbar.connect( this, { updateState: 'onUpdateState' } );
-
-       // Initialization
-       this.$title.addClass( 'oo-ui-tool-title' );
-       this.$accel
-               .addClass( 'oo-ui-tool-accel' )
-               .prop( {
-                       // This may need to be changed if the key names are ever localized,
-                       // but for now they are essentially written in English
-                       dir: 'ltr',
-                       lang: 'en'
-               } );
-       this.$link
-               .addClass( 'oo-ui-tool-link' )
-               .append( this.$icon, this.$title, this.$accel )
-               .attr( 'role', 'button' );
-       this.$element
-               .data( 'oo-ui-tool', this )
-               .addClass(
-                       'oo-ui-tool ' + 'oo-ui-tool-name-' +
-                       this.constructor.static.name.replace( /^([^\/]+)\/([^\/]+).*$/, '$1-$2' )
-               )
-               .toggleClass( 'oo-ui-tool-with-label', this.constructor.static.displayBothIconAndLabel )
-               .append( this.$link );
-       this.setTitle( config.title || this.constructor.static.title );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.Tool, OO.ui.Widget );
-OO.mixinClass( OO.ui.Tool, OO.ui.mixin.IconElement );
-OO.mixinClass( OO.ui.Tool, OO.ui.mixin.FlaggedElement );
-OO.mixinClass( OO.ui.Tool, OO.ui.mixin.TabIndexedElement );
-
-/* Static Properties */
-
-/**
- * @static
- * @inheritdoc
- */
-OO.ui.Tool.static.tagName = 'span';
-
-/**
- * Symbolic name of tool.
- *
- * The symbolic name is used internally to register the tool with a {@link OO.ui.ToolFactory ToolFactory}. It can
- * also be used when adding tools to toolgroups.
- *
- * @abstract
- * @static
- * @inheritable
- * @property {string}
- */
-OO.ui.Tool.static.name = '';
-
-/**
- * Symbolic name of the group.
- *
- * The group name is used to associate tools with each other so that they can be selected later by
- * a {@link OO.ui.ToolGroup toolgroup}.
- *
- * @abstract
- * @static
- * @inheritable
- * @property {string}
- */
-OO.ui.Tool.static.group = '';
-
-/**
- * Tool title text or a function that returns title text. The value of the static property is overridden if the #title config option is used.
- *
- * @abstract
- * @static
- * @inheritable
- * @property {string|Function}
- */
-OO.ui.Tool.static.title = '';
-
-/**
- * Display both icon and label when the tool is used in a {@link OO.ui.BarToolGroup bar} toolgroup.
- * Normally only the icon is displayed, or only the label if no icon is given.
- *
- * @static
- * @inheritable
- * @property {boolean}
- */
-OO.ui.Tool.static.displayBothIconAndLabel = false;
-
-/**
- * Add tool to catch-all groups automatically.
- *
- * A catch-all group, which contains all tools that do not currently belong to a toolgroup,
- * can be included in a toolgroup using the wildcard selector, an asterisk (*).
- *
- * @static
- * @inheritable
- * @property {boolean}
- */
-OO.ui.Tool.static.autoAddToCatchall = true;
-
-/**
- * Add tool to named groups automatically.
- *
- * By default, tools that are configured with a static ‘group’ property are added
- * to that group and will be selected when the symbolic name of the group is specified (e.g., when
- * toolgroups include tools by group name).
- *
- * @static
- * @property {boolean}
- * @inheritable
- */
-OO.ui.Tool.static.autoAddToGroup = true;
-
-/**
- * Check if this tool is compatible with given data.
- *
- * This is a stub that can be overridden to provide support for filtering tools based on an
- * arbitrary piece of information  (e.g., where the cursor is in a document). The implementation
- * must also call this method so that the compatibility check can be performed.
- *
- * @static
- * @inheritable
- * @param {Mixed} data Data to check
- * @return {boolean} Tool can be used with data
- */
-OO.ui.Tool.static.isCompatibleWith = function () {
-       return false;
-};
-
-/* Methods */
-
-/**
- * Handle the toolbar state being updated. This method is called when the
- * {@link OO.ui.Toolbar#event-updateState 'updateState' event} is emitted on the
- * {@link OO.ui.Toolbar Toolbar} that uses this tool, and should set the state of this tool
- * depending on application state (usually by calling #setDisabled to enable or disable the tool,
- * or #setActive to mark is as currently in-use or not).
- *
- * This is an abstract method that must be overridden in a concrete subclass.
- *
- * @method
- * @protected
- * @abstract
- */
-OO.ui.Tool.prototype.onUpdateState = null;
-
-/**
- * Handle the tool being selected. This method is called when the user triggers this tool,
- * usually by clicking on its label/icon.
- *
- * This is an abstract method that must be overridden in a concrete subclass.
- *
- * @method
- * @protected
- * @abstract
- */
-OO.ui.Tool.prototype.onSelect = null;
-
-/**
- * Check if the tool is active.
- *
- * Tools become active when their #onSelect or #onUpdateState handlers change them to appear pressed
- * with the #setActive method. Additional CSS is applied to the tool to reflect the active state.
- *
- * @return {boolean} Tool is active
- */
-OO.ui.Tool.prototype.isActive = function () {
-       return this.active;
-};
-
-/**
- * Make the tool appear active or inactive.
- *
- * This method should be called within #onSelect or #onUpdateState event handlers to make the tool
- * appear pressed or not.
- *
- * @param {boolean} state Make tool appear active
- */
-OO.ui.Tool.prototype.setActive = function ( state ) {
-       this.active = !!state;
-       if ( this.active ) {
-               this.$element.addClass( 'oo-ui-tool-active' );
-       } else {
-               this.$element.removeClass( 'oo-ui-tool-active' );
-       }
-};
-
-/**
- * Set the tool #title.
- *
- * @param {string|Function} title Title text or a function that returns text
- * @chainable
- */
-OO.ui.Tool.prototype.setTitle = function ( title ) {
-       this.title = OO.ui.resolveMsg( title );
-       this.updateTitle();
-       return this;
-};
-
-/**
- * Get the tool #title.
- *
- * @return {string} Title text
- */
-OO.ui.Tool.prototype.getTitle = function () {
-       return this.title;
-};
-
-/**
- * Get the tool's symbolic name.
- *
- * @return {string} Symbolic name of tool
- */
-OO.ui.Tool.prototype.getName = function () {
-       return this.constructor.static.name;
-};
-
-/**
- * Update the title.
- */
-OO.ui.Tool.prototype.updateTitle = function () {
-       var titleTooltips = this.toolGroup.constructor.static.titleTooltips,
-               accelTooltips = this.toolGroup.constructor.static.accelTooltips,
-               accel = this.toolbar.getToolAccelerator( this.constructor.static.name ),
-               tooltipParts = [];
-
-       this.$title.text( this.title );
-       this.$accel.text( accel );
-
-       if ( titleTooltips && typeof this.title === 'string' && this.title.length ) {
-               tooltipParts.push( this.title );
-       }
-       if ( accelTooltips && typeof accel === 'string' && accel.length ) {
-               tooltipParts.push( accel );
-       }
-       if ( tooltipParts.length ) {
-               this.$link.attr( 'title', tooltipParts.join( ' ' ) );
-       } else {
-               this.$link.removeAttr( 'title' );
-       }
-};
-
-/**
- * Destroy tool.
- *
- * Destroying the tool removes all event handlers and the tool’s DOM elements.
- * Call this method whenever you are done using a tool.
- */
-OO.ui.Tool.prototype.destroy = function () {
-       this.toolbar.disconnect( this );
-       this.$element.remove();
-};
-
-/**
- * Toolbars are complex interface components that permit users to easily access a variety
- * of {@link OO.ui.Tool tools} (e.g., formatting commands) and actions, which are additional commands that are
- * part of the toolbar, but not configured as tools.
- *
- * Individual tools are customized and then registered with a {@link OO.ui.ToolFactory tool factory}, which creates
- * the tools on demand. Each tool has a symbolic name (used when registering the tool), a title (e.g., ‘Insert
- * image’), and an icon.
- *
- * Individual tools are organized in {@link OO.ui.ToolGroup toolgroups}, which can be {@link OO.ui.MenuToolGroup menus}
- * of tools, {@link OO.ui.ListToolGroup lists} of tools, or a single {@link OO.ui.BarToolGroup bar} of tools.
- * The arrangement and order of the toolgroups is customized when the toolbar is set up. Tools can be presented in
- * any order, but each can only appear once in the toolbar.
- *
- * The toolbar can be synchronized with the state of the external "application", like a text
- * editor's editing area, marking tools as active/inactive (e.g. a 'bold' tool would be shown as
- * active when the text cursor was inside bolded text) or enabled/disabled (e.g. a table caption
- * tool would be disabled while the user is not editing a table). A state change is signalled by
- * emitting the {@link #event-updateState 'updateState' event}, which calls Tools'
- * {@link OO.ui.Tool#onUpdateState onUpdateState method}.
- *
- * The following is an example of a basic toolbar.
- *
- *     @example
- *     // Example of a toolbar
- *     // Create the toolbar
- *     var toolFactory = new OO.ui.ToolFactory();
- *     var toolGroupFactory = new OO.ui.ToolGroupFactory();
- *     var toolbar = new OO.ui.Toolbar( toolFactory, toolGroupFactory );
- *
- *     // We will be placing status text in this element when tools are used
- *     var $area = $( '<p>' ).text( 'Toolbar example' );
- *
- *     // Define the tools that we're going to place in our toolbar
- *
- *     // Create a class inheriting from OO.ui.Tool
- *     function SearchTool() {
- *         SearchTool.parent.apply( this, arguments );
- *     }
- *     OO.inheritClass( SearchTool, OO.ui.Tool );
- *     // Each tool must have a 'name' (used as an internal identifier, see later) and at least one
- *     // of 'icon' and 'title' (displayed icon and text).
- *     SearchTool.static.name = 'search';
- *     SearchTool.static.icon = 'search';
- *     SearchTool.static.title = 'Search...';
- *     // Defines the action that will happen when this tool is selected (clicked).
- *     SearchTool.prototype.onSelect = function () {
- *         $area.text( 'Search tool clicked!' );
- *         // Never display this tool as "active" (selected).
- *         this.setActive( false );
- *     };
- *     SearchTool.prototype.onUpdateState = function () {};
- *     // Make this tool available in our toolFactory and thus our toolbar
- *     toolFactory.register( SearchTool );
- *
- *     // Register two more tools, nothing interesting here
- *     function SettingsTool() {
- *         SettingsTool.parent.apply( this, arguments );
- *     }
- *     OO.inheritClass( SettingsTool, OO.ui.Tool );
- *     SettingsTool.static.name = 'settings';
- *     SettingsTool.static.icon = 'settings';
- *     SettingsTool.static.title = 'Change settings';
- *     SettingsTool.prototype.onSelect = function () {
- *         $area.text( 'Settings tool clicked!' );
- *         this.setActive( false );
- *     };
- *     SettingsTool.prototype.onUpdateState = function () {};
- *     toolFactory.register( SettingsTool );
- *
- *     // Register two more tools, nothing interesting here
- *     function StuffTool() {
- *         StuffTool.parent.apply( this, arguments );
- *     }
- *     OO.inheritClass( StuffTool, OO.ui.Tool );
- *     StuffTool.static.name = 'stuff';
- *     StuffTool.static.icon = 'ellipsis';
- *     StuffTool.static.title = 'More stuff';
- *     StuffTool.prototype.onSelect = function () {
- *         $area.text( 'More stuff tool clicked!' );
- *         this.setActive( false );
- *     };
- *     StuffTool.prototype.onUpdateState = function () {};
- *     toolFactory.register( StuffTool );
- *
- *     // This is a PopupTool. Rather than having a custom 'onSelect' action, it will display a
- *     // little popup window (a PopupWidget).
- *     function HelpTool( toolGroup, config ) {
- *         OO.ui.PopupTool.call( this, toolGroup, $.extend( { popup: {
- *             padded: true,
- *             label: 'Help',
- *             head: true
- *         } }, config ) );
- *         this.popup.$body.append( '<p>I am helpful!</p>' );
- *     }
- *     OO.inheritClass( HelpTool, OO.ui.PopupTool );
- *     HelpTool.static.name = 'help';
- *     HelpTool.static.icon = 'help';
- *     HelpTool.static.title = 'Help';
- *     toolFactory.register( HelpTool );
- *
- *     // Finally define which tools and in what order appear in the toolbar. Each tool may only be
- *     // used once (but not all defined tools must be used).
- *     toolbar.setup( [
- *         {
- *             // 'bar' tool groups display tools' icons only, side-by-side.
- *             type: 'bar',
- *             include: [ 'search', 'help' ]
- *         },
- *         {
- *             // 'list' tool groups display both the titles and icons, in a dropdown list.
- *             type: 'list',
- *             indicator: 'down',
- *             label: 'More',
- *             include: [ 'settings', 'stuff' ]
- *         }
- *         // Note how the tools themselves are toolgroup-agnostic - the same tool can be displayed
- *         // either in a 'list' or a 'bar'. There is a 'menu' tool group too, not showcased here,
- *         // since it's more complicated to use. (See the next example snippet on this page.)
- *     ] );
- *
- *     // Create some UI around the toolbar and place it in the document
- *     var frame = new OO.ui.PanelLayout( {
- *         expanded: false,
- *         framed: true
- *     } );
- *     var contentFrame = new OO.ui.PanelLayout( {
- *         expanded: false,
- *         padded: true
- *     } );
- *     frame.$element.append(
- *         toolbar.$element,
- *         contentFrame.$element.append( $area )
- *     );
- *     $( 'body' ).append( frame.$element );
- *
- *     // Here is where the toolbar is actually built. This must be done after inserting it into the
- *     // document.
- *     toolbar.initialize();
- *     toolbar.emit( 'updateState' );
- *
- * The following example extends the previous one to illustrate 'menu' toolgroups and the usage of
- * {@link #event-updateState 'updateState' event}.
- *
- *     @example
- *     // Create the toolbar
- *     var toolFactory = new OO.ui.ToolFactory();
- *     var toolGroupFactory = new OO.ui.ToolGroupFactory();
- *     var toolbar = new OO.ui.Toolbar( toolFactory, toolGroupFactory );
- *
- *     // We will be placing status text in this element when tools are used
- *     var $area = $( '<p>' ).text( 'Toolbar example' );
- *
- *     // Define the tools that we're going to place in our toolbar
- *
- *     // Create a class inheriting from OO.ui.Tool
- *     function SearchTool() {
- *         SearchTool.parent.apply( this, arguments );
- *     }
- *     OO.inheritClass( SearchTool, OO.ui.Tool );
- *     // Each tool must have a 'name' (used as an internal identifier, see later) and at least one
- *     // of 'icon' and 'title' (displayed icon and text).
- *     SearchTool.static.name = 'search';
- *     SearchTool.static.icon = 'search';
- *     SearchTool.static.title = 'Search...';
- *     // Defines the action that will happen when this tool is selected (clicked).
- *     SearchTool.prototype.onSelect = function () {
- *         $area.text( 'Search tool clicked!' );
- *         // Never display this tool as "active" (selected).
- *         this.setActive( false );
- *     };
- *     SearchTool.prototype.onUpdateState = function () {};
- *     // Make this tool available in our toolFactory and thus our toolbar
- *     toolFactory.register( SearchTool );
- *
- *     // Register two more tools, nothing interesting here
- *     function SettingsTool() {
- *         SettingsTool.parent.apply( this, arguments );
- *         this.reallyActive = false;
- *     }
- *     OO.inheritClass( SettingsTool, OO.ui.Tool );
- *     SettingsTool.static.name = 'settings';
- *     SettingsTool.static.icon = 'settings';
- *     SettingsTool.static.title = 'Change settings';
- *     SettingsTool.prototype.onSelect = function () {
- *         $area.text( 'Settings tool clicked!' );
- *         // Toggle the active state on each click
- *         this.reallyActive = !this.reallyActive;
- *         this.setActive( this.reallyActive );
- *         // To update the menu label
- *         this.toolbar.emit( 'updateState' );
- *     };
- *     SettingsTool.prototype.onUpdateState = function () {};
- *     toolFactory.register( SettingsTool );
- *
- *     // Register two more tools, nothing interesting here
- *     function StuffTool() {
- *         StuffTool.parent.apply( this, arguments );
- *         this.reallyActive = false;
- *     }
- *     OO.inheritClass( StuffTool, OO.ui.Tool );
- *     StuffTool.static.name = 'stuff';
- *     StuffTool.static.icon = 'ellipsis';
- *     StuffTool.static.title = 'More stuff';
- *     StuffTool.prototype.onSelect = function () {
- *         $area.text( 'More stuff tool clicked!' );
- *         // Toggle the active state on each click
- *         this.reallyActive = !this.reallyActive;
- *         this.setActive( this.reallyActive );
- *         // To update the menu label
- *         this.toolbar.emit( 'updateState' );
- *     };
- *     StuffTool.prototype.onUpdateState = function () {};
- *     toolFactory.register( StuffTool );
- *
- *     // This is a PopupTool. Rather than having a custom 'onSelect' action, it will display a
- *     // little popup window (a PopupWidget). 'onUpdateState' is also already implemented.
- *     function HelpTool( toolGroup, config ) {
- *         OO.ui.PopupTool.call( this, toolGroup, $.extend( { popup: {
- *             padded: true,
- *             label: 'Help',
- *             head: true
- *         } }, config ) );
- *         this.popup.$body.append( '<p>I am helpful!</p>' );
- *     }
- *     OO.inheritClass( HelpTool, OO.ui.PopupTool );
- *     HelpTool.static.name = 'help';
- *     HelpTool.static.icon = 'help';
- *     HelpTool.static.title = 'Help';
- *     toolFactory.register( HelpTool );
- *
- *     // Finally define which tools and in what order appear in the toolbar. Each tool may only be
- *     // used once (but not all defined tools must be used).
- *     toolbar.setup( [
- *         {
- *             // 'bar' tool groups display tools' icons only, side-by-side.
- *             type: 'bar',
- *             include: [ 'search', 'help' ]
- *         },
- *         {
- *             // 'menu' tool groups display both the titles and icons, in a dropdown menu.
- *             // Menu label indicates which items are selected.
- *             type: 'menu',
- *             indicator: 'down',
- *             include: [ 'settings', 'stuff' ]
- *         }
- *     ] );
- *
- *     // Create some UI around the toolbar and place it in the document
- *     var frame = new OO.ui.PanelLayout( {
- *         expanded: false,
- *         framed: true
- *     } );
- *     var contentFrame = new OO.ui.PanelLayout( {
- *         expanded: false,
- *         padded: true
- *     } );
- *     frame.$element.append(
- *         toolbar.$element,
- *         contentFrame.$element.append( $area )
- *     );
- *     $( 'body' ).append( frame.$element );
- *
- *     // Here is where the toolbar is actually built. This must be done after inserting it into the
- *     // document.
- *     toolbar.initialize();
- *     toolbar.emit( 'updateState' );
- *
- * @class
- * @extends OO.ui.Element
- * @mixins OO.EventEmitter
- * @mixins OO.ui.mixin.GroupElement
- *
- * @constructor
- * @param {OO.ui.ToolFactory} toolFactory Factory for creating tools
- * @param {OO.ui.ToolGroupFactory} toolGroupFactory Factory for creating toolgroups
- * @param {Object} [config] Configuration options
- * @cfg {boolean} [actions] Add an actions section to the toolbar. Actions are commands that are included
- *  in the toolbar, but are not configured as tools. By default, actions are displayed on the right side of
- *  the toolbar.
- * @cfg {boolean} [shadow] Add a shadow below the toolbar.
- */
-OO.ui.Toolbar = function OoUiToolbar( toolFactory, toolGroupFactory, config ) {
-       // Allow passing positional parameters inside the config object
-       if ( OO.isPlainObject( toolFactory ) && config === undefined ) {
-               config = toolFactory;
-               toolFactory = config.toolFactory;
-               toolGroupFactory = config.toolGroupFactory;
-       }
-
-       // Configuration initialization
-       config = config || {};
-
-       // Parent constructor
-       OO.ui.Toolbar.parent.call( this, config );
-
-       // Mixin constructors
-       OO.EventEmitter.call( this );
-       OO.ui.mixin.GroupElement.call( this, config );
-
-       // Properties
-       this.toolFactory = toolFactory;
-       this.toolGroupFactory = toolGroupFactory;
-       this.groups = [];
-       this.tools = {};
-       this.$bar = $( '<div>' );
-       this.$actions = $( '<div>' );
-       this.initialized = false;
-       this.onWindowResizeHandler = this.onWindowResize.bind( this );
-
-       // Events
-       this.$element
-               .add( this.$bar ).add( this.$group ).add( this.$actions )
-               .on( 'mousedown keydown', this.onPointerDown.bind( this ) );
-
-       // Initialization
-       this.$group.addClass( 'oo-ui-toolbar-tools' );
-       if ( config.actions ) {
-               this.$bar.append( this.$actions.addClass( 'oo-ui-toolbar-actions' ) );
-       }
-       this.$bar
-               .addClass( 'oo-ui-toolbar-bar' )
-               .append( this.$group, '<div style="clear:both"></div>' );
-       if ( config.shadow ) {
-               this.$bar.append( '<div class="oo-ui-toolbar-shadow"></div>' );
-       }
-       this.$element.addClass( 'oo-ui-toolbar' ).append( this.$bar );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.Toolbar, OO.ui.Element );
-OO.mixinClass( OO.ui.Toolbar, OO.EventEmitter );
-OO.mixinClass( OO.ui.Toolbar, OO.ui.mixin.GroupElement );
-
-/* Events */
-
-/**
- * @event updateState
- *
- * An 'updateState' event must be emitted on the Toolbar (by calling `toolbar.emit( 'updateState' )`)
- * every time the state of the application using the toolbar changes, and an update to the state of
- * tools is required.
- *
- * @param {Mixed...} data Application-defined parameters
- */
-
-/* Methods */
-
-/**
- * Get the tool factory.
- *
- * @return {OO.ui.ToolFactory} Tool factory
- */
-OO.ui.Toolbar.prototype.getToolFactory = function () {
-       return this.toolFactory;
-};
-
-/**
- * Get the toolgroup factory.
- *
- * @return {OO.Factory} Toolgroup factory
- */
-OO.ui.Toolbar.prototype.getToolGroupFactory = function () {
-       return this.toolGroupFactory;
-};
-
-/**
- * Handles mouse down events.
- *
- * @private
- * @param {jQuery.Event} e Mouse down event
- */
-OO.ui.Toolbar.prototype.onPointerDown = function ( e ) {
-       var $closestWidgetToEvent = $( e.target ).closest( '.oo-ui-widget' ),
-               $closestWidgetToToolbar = this.$element.closest( '.oo-ui-widget' );
-       if ( !$closestWidgetToEvent.length || $closestWidgetToEvent[ 0 ] === $closestWidgetToToolbar[ 0 ] ) {
-               return false;
-       }
-};
-
-/**
- * Handle window resize event.
- *
- * @private
- * @param {jQuery.Event} e Window resize event
- */
-OO.ui.Toolbar.prototype.onWindowResize = function () {
-       this.$element.toggleClass(
-               'oo-ui-toolbar-narrow',
-               this.$bar.width() <= this.narrowThreshold
-       );
-};
-
-/**
- * Sets up handles and preloads required information for the toolbar to work.
- * This must be called after it is attached to a visible document and before doing anything else.
- */
-OO.ui.Toolbar.prototype.initialize = function () {
-       if ( !this.initialized ) {
-               this.initialized = true;
-               this.narrowThreshold = this.$group.width() + this.$actions.width();
-               $( this.getElementWindow() ).on( 'resize', this.onWindowResizeHandler );
-               this.onWindowResize();
-       }
-};
-
-/**
- * Set up the toolbar.
- *
- * The toolbar is set up with a list of toolgroup configurations that specify the type of
- * toolgroup ({@link OO.ui.BarToolGroup bar}, {@link OO.ui.MenuToolGroup menu}, or {@link OO.ui.ListToolGroup list})
- * to add and which tools to include, exclude, promote, or demote within that toolgroup. Please
- * see {@link OO.ui.ToolGroup toolgroups} for more information about including tools in toolgroups.
- *
- * @param {Object.<string,Array>} groups List of toolgroup configurations
- * @param {Array|string} [groups.include] Tools to include in the toolgroup
- * @param {Array|string} [groups.exclude] Tools to exclude from the toolgroup
- * @param {Array|string} [groups.promote] Tools to promote to the beginning of the toolgroup
- * @param {Array|string} [groups.demote] Tools to demote to the end of the toolgroup
- */
-OO.ui.Toolbar.prototype.setup = function ( groups ) {
-       var i, len, type, group,
-               items = [],
-               defaultType = 'bar';
-
-       // Cleanup previous groups
-       this.reset();
-
-       // Build out new groups
-       for ( i = 0, len = groups.length; i < len; i++ ) {
-               group = groups[ i ];
-               if ( group.include === '*' ) {
-                       // Apply defaults to catch-all groups
-                       if ( group.type === undefined ) {
-                               group.type = 'list';
-                       }
-                       if ( group.label === undefined ) {
-                               group.label = OO.ui.msg( 'ooui-toolbar-more' );
-                       }
-               }
-               // Check type has been registered
-               type = this.getToolGroupFactory().lookup( group.type ) ? group.type : defaultType;
-               items.push(
-                       this.getToolGroupFactory().create( type, this, group )
-               );
-       }
-       this.addItems( items );
-};
-
-/**
- * Remove all tools and toolgroups from the toolbar.
- */
-OO.ui.Toolbar.prototype.reset = function () {
-       var i, len;
-
-       this.groups = [];
-       this.tools = {};
-       for ( i = 0, len = this.items.length; i < len; i++ ) {
-               this.items[ i ].destroy();
-       }
-       this.clearItems();
-};
-
-/**
- * Destroy the toolbar.
- *
- * Destroying the toolbar removes all event handlers and DOM elements that constitute the toolbar. Call
- * this method whenever you are done using a toolbar.
- */
-OO.ui.Toolbar.prototype.destroy = function () {
-       $( this.getElementWindow() ).off( 'resize', this.onWindowResizeHandler );
-       this.reset();
-       this.$element.remove();
-};
-
-/**
- * Check if the tool is available.
- *
- * Available tools are ones that have not yet been added to the toolbar.
- *
- * @param {string} name Symbolic name of tool
- * @return {boolean} Tool is available
- */
-OO.ui.Toolbar.prototype.isToolAvailable = function ( name ) {
-       return !this.tools[ name ];
-};
-
-/**
- * Prevent tool from being used again.
- *
- * @param {OO.ui.Tool} tool Tool to reserve
- */
-OO.ui.Toolbar.prototype.reserveTool = function ( tool ) {
-       this.tools[ tool.getName() ] = tool;
-};
-
-/**
- * Allow tool to be used again.
- *
- * @param {OO.ui.Tool} tool Tool to release
- */
-OO.ui.Toolbar.prototype.releaseTool = function ( tool ) {
-       delete this.tools[ tool.getName() ];
-};
-
-/**
- * Get accelerator label for tool.
- *
- * The OOjs UI library does not contain an accelerator system, but this is the hook for one. To
- * use an accelerator system, subclass the toolbar and override this method, which is meant to return a label
- * that describes the accelerator keys for the tool passed (by symbolic name) to the method.
- *
- * @param {string} name Symbolic name of tool
- * @return {string|undefined} Tool accelerator label if available
- */
-OO.ui.Toolbar.prototype.getToolAccelerator = function () {
-       return undefined;
-};
-
-/**
- * ToolGroups are collections of {@link OO.ui.Tool tools} that are used in a {@link OO.ui.Toolbar toolbar}.
- * The type of toolgroup ({@link OO.ui.ListToolGroup list}, {@link OO.ui.BarToolGroup bar}, or {@link OO.ui.MenuToolGroup menu})
- * to which a tool belongs determines how the tool is arranged and displayed in the toolbar. Toolgroups
- * themselves are created on demand with a {@link OO.ui.ToolGroupFactory toolgroup factory}.
- *
- * Toolgroups can contain individual tools, groups of tools, or all available tools, as specified
- * using the `include` config option. See OO.ui.ToolFactory#extract on documentation of the format.
- * The options `exclude`, `promote`, and `demote` support the same formats.
- *
- * See {@link OO.ui.Toolbar toolbars} for a full example. For more information about toolbars in general,
- * please see the [OOjs UI documentation on MediaWiki][1].
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Toolbars
- *
- * @abstract
- * @class
- * @extends OO.ui.Widget
- * @mixins OO.ui.mixin.GroupElement
- *
- * @constructor
- * @param {OO.ui.Toolbar} toolbar
- * @param {Object} [config] Configuration options
- * @cfg {Array|string} [include] List of tools to include in the toolgroup, see above.
- * @cfg {Array|string} [exclude] List of tools to exclude from the toolgroup, see above.
- * @cfg {Array|string} [promote] List of tools to promote to the beginning of the toolgroup, see above.
- * @cfg {Array|string} [demote] List of tools to demote to the end of the toolgroup, see above.
- *  This setting is particularly useful when tools have been added to the toolgroup
- *  en masse (e.g., via the catch-all selector).
- */
-OO.ui.ToolGroup = function OoUiToolGroup( toolbar, config ) {
-       // Allow passing positional parameters inside the config object
-       if ( OO.isPlainObject( toolbar ) && config === undefined ) {
-               config = toolbar;
-               toolbar = config.toolbar;
-       }
-
-       // Configuration initialization
-       config = config || {};
-
-       // Parent constructor
-       OO.ui.ToolGroup.parent.call( this, config );
-
-       // Mixin constructors
-       OO.ui.mixin.GroupElement.call( this, config );
-
-       // Properties
-       this.toolbar = toolbar;
-       this.tools = {};
-       this.pressed = null;
-       this.autoDisabled = false;
-       this.include = config.include || [];
-       this.exclude = config.exclude || [];
-       this.promote = config.promote || [];
-       this.demote = config.demote || [];
-       this.onCapturedMouseKeyUpHandler = this.onCapturedMouseKeyUp.bind( this );
-
-       // Events
-       this.$element.on( {
-               mousedown: this.onMouseKeyDown.bind( this ),
-               mouseup: this.onMouseKeyUp.bind( this ),
-               keydown: this.onMouseKeyDown.bind( this ),
-               keyup: this.onMouseKeyUp.bind( this ),
-               focus: this.onMouseOverFocus.bind( this ),
-               blur: this.onMouseOutBlur.bind( this ),
-               mouseover: this.onMouseOverFocus.bind( this ),
-               mouseout: this.onMouseOutBlur.bind( this )
-       } );
-       this.toolbar.getToolFactory().connect( this, { register: 'onToolFactoryRegister' } );
-       this.aggregate( { disable: 'itemDisable' } );
-       this.connect( this, { itemDisable: 'updateDisabled' } );
-
-       // Initialization
-       this.$group.addClass( 'oo-ui-toolGroup-tools' );
-       this.$element
-               .addClass( 'oo-ui-toolGroup' )
-               .append( this.$group );
-       this.populate();
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.ToolGroup, OO.ui.Widget );
-OO.mixinClass( OO.ui.ToolGroup, OO.ui.mixin.GroupElement );
-
-/* Events */
-
-/**
- * @event update
- */
-
-/* Static Properties */
-
-/**
- * Show labels in tooltips.
- *
- * @static
- * @inheritable
- * @property {boolean}
- */
-OO.ui.ToolGroup.static.titleTooltips = false;
-
-/**
- * Show acceleration labels in tooltips.
- *
- * Note: The OOjs UI library does not include an accelerator system, but does contain
- * a hook for one. To use an accelerator system, subclass the {@link OO.ui.Toolbar toolbar} and
- * override the {@link OO.ui.Toolbar#getToolAccelerator getToolAccelerator} method, which is
- * meant to return a label that describes the accelerator keys for a given tool (e.g., 'Ctrl + M').
- *
- * @static
- * @inheritable
- * @property {boolean}
- */
-OO.ui.ToolGroup.static.accelTooltips = false;
-
-/**
- * Automatically disable the toolgroup when all tools are disabled
- *
- * @static
- * @inheritable
- * @property {boolean}
- */
-OO.ui.ToolGroup.static.autoDisable = true;
-
-/* Methods */
-
-/**
- * @inheritdoc
- */
-OO.ui.ToolGroup.prototype.isDisabled = function () {
-       return this.autoDisabled || OO.ui.ToolGroup.parent.prototype.isDisabled.apply( this, arguments );
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.ToolGroup.prototype.updateDisabled = function () {
-       var i, item, allDisabled = true;
-
-       if ( this.constructor.static.autoDisable ) {
-               for ( i = this.items.length - 1; i >= 0; i-- ) {
-                       item = this.items[ i ];
-                       if ( !item.isDisabled() ) {
-                               allDisabled = false;
-                               break;
-                       }
-               }
-               this.autoDisabled = allDisabled;
-       }
-       OO.ui.ToolGroup.parent.prototype.updateDisabled.apply( this, arguments );
-};
-
-/**
- * Handle mouse down and key down events.
- *
- * @protected
- * @param {jQuery.Event} e Mouse down or key down event
- */
-OO.ui.ToolGroup.prototype.onMouseKeyDown = function ( e ) {
-       if (
-               !this.isDisabled() &&
-               ( e.which === OO.ui.MouseButtons.LEFT || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
-       ) {
-               this.pressed = this.getTargetTool( e );
-               if ( this.pressed ) {
-                       this.pressed.setActive( true );
-                       this.getElementDocument().addEventListener( 'mouseup', this.onCapturedMouseKeyUpHandler, true );
-                       this.getElementDocument().addEventListener( 'keyup', this.onCapturedMouseKeyUpHandler, true );
-               }
-               return false;
-       }
-};
-
-/**
- * Handle captured mouse up and key up events.
- *
- * @protected
- * @param {Event} e Mouse up or key up event
- */
-OO.ui.ToolGroup.prototype.onCapturedMouseKeyUp = function ( e ) {
-       this.getElementDocument().removeEventListener( 'mouseup', this.onCapturedMouseKeyUpHandler, true );
-       this.getElementDocument().removeEventListener( 'keyup', this.onCapturedMouseKeyUpHandler, true );
-       // onMouseKeyUp may be called a second time, depending on where the mouse is when the button is
-       // released, but since `this.pressed` will no longer be true, the second call will be ignored.
-       this.onMouseKeyUp( e );
-};
-
-/**
- * Handle mouse up and key up events.
- *
- * @protected
- * @param {jQuery.Event} e Mouse up or key up event
- */
-OO.ui.ToolGroup.prototype.onMouseKeyUp = function ( e ) {
-       var tool = this.getTargetTool( e );
-
-       if (
-               !this.isDisabled() && this.pressed && this.pressed === tool &&
-               ( e.which === OO.ui.MouseButtons.LEFT || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
-       ) {
-               this.pressed.onSelect();
-               this.pressed = null;
-               return false;
-       }
-
-       this.pressed = null;
-};
-
-/**
- * Handle mouse over and focus events.
- *
- * @protected
- * @param {jQuery.Event} e Mouse over or focus event
- */
-OO.ui.ToolGroup.prototype.onMouseOverFocus = function ( e ) {
-       var tool = this.getTargetTool( e );
-
-       if ( this.pressed && this.pressed === tool ) {
-               this.pressed.setActive( true );
-       }
-};
-
-/**
- * Handle mouse out and blur events.
- *
- * @protected
- * @param {jQuery.Event} e Mouse out or blur event
- */
-OO.ui.ToolGroup.prototype.onMouseOutBlur = function ( e ) {
-       var tool = this.getTargetTool( e );
-
-       if ( this.pressed && this.pressed === tool ) {
-               this.pressed.setActive( false );
-       }
-};
-
-/**
- * Get the closest tool to a jQuery.Event.
- *
- * Only tool links are considered, which prevents other elements in the tool such as popups from
- * triggering tool group interactions.
- *
- * @private
- * @param {jQuery.Event} e
- * @return {OO.ui.Tool|null} Tool, `null` if none was found
- */
-OO.ui.ToolGroup.prototype.getTargetTool = function ( e ) {
-       var tool,
-               $item = $( e.target ).closest( '.oo-ui-tool-link' );
-
-       if ( $item.length ) {
-               tool = $item.parent().data( 'oo-ui-tool' );
-       }
-
-       return tool && !tool.isDisabled() ? tool : null;
-};
-
-/**
- * Handle tool registry register events.
- *
- * If a tool is registered after the group is created, we must repopulate the list to account for:
- *
- * - a tool being added that may be included
- * - a tool already included being overridden
- *
- * @protected
- * @param {string} name Symbolic name of tool
- */
-OO.ui.ToolGroup.prototype.onToolFactoryRegister = function () {
-       this.populate();
-};
-
-/**
- * Get the toolbar that contains the toolgroup.
- *
- * @return {OO.ui.Toolbar} Toolbar that contains the toolgroup
- */
-OO.ui.ToolGroup.prototype.getToolbar = function () {
-       return this.toolbar;
-};
-
-/**
- * Add and remove tools based on configuration.
- */
-OO.ui.ToolGroup.prototype.populate = function () {
-       var i, len, name, tool,
-               toolFactory = this.toolbar.getToolFactory(),
-               names = {},
-               add = [],
-               remove = [],
-               list = this.toolbar.getToolFactory().getTools(
-                       this.include, this.exclude, this.promote, this.demote
-               );
-
-       // Build a list of needed tools
-       for ( i = 0, len = list.length; i < len; i++ ) {
-               name = list[ i ];
-               if (
-                       // Tool exists
-                       toolFactory.lookup( name ) &&
-                       // Tool is available or is already in this group
-                       ( this.toolbar.isToolAvailable( name ) || this.tools[ name ] )
-               ) {
-                       // Hack to prevent infinite recursion via ToolGroupTool. We need to reserve the tool before
-                       // creating it, but we can't call reserveTool() yet because we haven't created the tool.
-                       this.toolbar.tools[ name ] = true;
-                       tool = this.tools[ name ];
-                       if ( !tool ) {
-                               // Auto-initialize tools on first use
-                               this.tools[ name ] = tool = toolFactory.create( name, this );
-                               tool.updateTitle();
-                       }
-                       this.toolbar.reserveTool( tool );
-                       add.push( tool );
-                       names[ name ] = true;
-               }
-       }
-       // Remove tools that are no longer needed
-       for ( name in this.tools ) {
-               if ( !names[ name ] ) {
-                       this.tools[ name ].destroy();
-                       this.toolbar.releaseTool( this.tools[ name ] );
-                       remove.push( this.tools[ name ] );
-                       delete this.tools[ name ];
-               }
-       }
-       if ( remove.length ) {
-               this.removeItems( remove );
-       }
-       // Update emptiness state
-       if ( add.length ) {
-               this.$element.removeClass( 'oo-ui-toolGroup-empty' );
-       } else {
-               this.$element.addClass( 'oo-ui-toolGroup-empty' );
-       }
-       // Re-add tools (moving existing ones to new locations)
-       this.addItems( add );
-       // Disabled state may depend on items
-       this.updateDisabled();
-};
-
-/**
- * Destroy toolgroup.
- */
-OO.ui.ToolGroup.prototype.destroy = function () {
-       var name;
-
-       this.clearItems();
-       this.toolbar.getToolFactory().disconnect( this );
-       for ( name in this.tools ) {
-               this.toolbar.releaseTool( this.tools[ name ] );
-               this.tools[ name ].disconnect( this ).destroy();
-               delete this.tools[ name ];
-       }
-       this.$element.remove();
-};
-
-/**
- * MessageDialogs display a confirmation or alert message. By default, the rendered dialog box
- * consists of a header that contains the dialog title, a body with the message, and a footer that
- * contains any {@link OO.ui.ActionWidget action widgets}. The MessageDialog class is the only type
- * of {@link OO.ui.Dialog dialog} that is usually instantiated directly.
- *
- * There are two basic types of message dialogs, confirmation and alert:
- *
- * - **confirmation**: the dialog title describes what a progressive action will do and the message provides
- *  more details about the consequences.
- * - **alert**: the dialog title describes which event occurred and the message provides more information
- *  about why the event occurred.
- *
- * The MessageDialog class specifies two actions: ‘accept’, the primary
- * action (e.g., ‘ok’) and ‘reject,’ the safe action (e.g., ‘cancel’). Both will close the window,
- * passing along the selected action.
- *
- * For more information and examples, please see the [OOjs UI documentation on MediaWiki][1].
- *
- *     @example
- *     // Example: Creating and opening a message dialog window.
- *     var messageDialog = new OO.ui.MessageDialog();
- *
- *     // Create and append a window manager.
- *     var windowManager = new OO.ui.WindowManager();
- *     $( 'body' ).append( windowManager.$element );
- *     windowManager.addWindows( [ messageDialog ] );
- *     // Open the window.
- *     windowManager.openWindow( messageDialog, {
- *         title: 'Basic message dialog',
- *         message: 'This is the message'
- *     } );
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Message_Dialogs
- *
- * @class
- * @extends OO.ui.Dialog
- *
- * @constructor
- * @param {Object} [config] Configuration options
- */
-OO.ui.MessageDialog = function OoUiMessageDialog( config ) {
-       // Parent constructor
-       OO.ui.MessageDialog.parent.call( this, config );
-
-       // Properties
-       this.verticalActionLayout = null;
-
-       // Initialization
-       this.$element.addClass( 'oo-ui-messageDialog' );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.MessageDialog, OO.ui.Dialog );
-
-/* Static Properties */
-
-OO.ui.MessageDialog.static.name = 'message';
-
-OO.ui.MessageDialog.static.size = 'small';
-
-OO.ui.MessageDialog.static.verbose = false;
-
-/**
- * Dialog title.
- *
- * The title of a confirmation dialog describes what a progressive action will do. The
- * title of an alert dialog describes which event occurred.
- *
- * @static
- * @inheritable
- * @property {jQuery|string|Function|null}
- */
-OO.ui.MessageDialog.static.title = null;
-
-/**
- * The message displayed in the dialog body.
- *
- * A confirmation message describes the consequences of a progressive action. An alert
- * message describes why an event occurred.
- *
- * @static
- * @inheritable
- * @property {jQuery|string|Function|null}
- */
-OO.ui.MessageDialog.static.message = null;
-
-// Note that OO.ui.alert() and OO.ui.confirm() rely on these.
-OO.ui.MessageDialog.static.actions = [
-       { action: 'accept', label: OO.ui.deferMsg( 'ooui-dialog-message-accept' ), flags: 'primary' },
-       { action: 'reject', label: OO.ui.deferMsg( 'ooui-dialog-message-reject' ), flags: 'safe' }
-];
-
-/* Methods */
-
-/**
- * @inheritdoc
- */
-OO.ui.MessageDialog.prototype.setManager = function ( manager ) {
-       OO.ui.MessageDialog.parent.prototype.setManager.call( this, manager );
-
-       // Events
-       this.manager.connect( this, {
-               resize: 'onResize'
-       } );
-
-       return this;
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.MessageDialog.prototype.onActionResize = function ( action ) {
-       this.fitActions();
-       return OO.ui.MessageDialog.parent.prototype.onActionResize.call( this, action );
-};
-
-/**
- * Handle window resized events.
- *
- * @private
- */
-OO.ui.MessageDialog.prototype.onResize = function () {
-       var dialog = this;
-       dialog.fitActions();
-       // Wait for CSS transition to finish and do it again :(
-       setTimeout( function () {
-               dialog.fitActions();
-       }, 300 );
-};
-
-/**
- * Toggle action layout between vertical and horizontal.
- *
- * @private
- * @param {boolean} [value] Layout actions vertically, omit to toggle
- * @chainable
- */
-OO.ui.MessageDialog.prototype.toggleVerticalActionLayout = function ( value ) {
-       value = value === undefined ? !this.verticalActionLayout : !!value;
-
-       if ( value !== this.verticalActionLayout ) {
-               this.verticalActionLayout = value;
-               this.$actions
-                       .toggleClass( 'oo-ui-messageDialog-actions-vertical', value )
-                       .toggleClass( 'oo-ui-messageDialog-actions-horizontal', !value );
-       }
-
-       return this;
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.MessageDialog.prototype.getActionProcess = function ( action ) {
-       if ( action ) {
-               return new OO.ui.Process( function () {
-                       this.close( { action: action } );
-               }, this );
-       }
-       return OO.ui.MessageDialog.parent.prototype.getActionProcess.call( this, action );
-};
-
-/**
- * @inheritdoc
- *
- * @param {Object} [data] Dialog opening data
- * @param {jQuery|string|Function|null} [data.title] Description of the action being confirmed
- * @param {jQuery|string|Function|null} [data.message] Description of the action's consequence
- * @param {boolean} [data.verbose] Message is verbose and should be styled as a long message
- * @param {Object[]} [data.actions] List of OO.ui.ActionOptionWidget configuration options for each
- *   action item
- */
-OO.ui.MessageDialog.prototype.getSetupProcess = function ( data ) {
-       data = data || {};
-
-       // Parent method
-       return OO.ui.MessageDialog.parent.prototype.getSetupProcess.call( this, data )
-               .next( function () {
-                       this.title.setLabel(
-                               data.title !== undefined ? data.title : this.constructor.static.title
-                       );
-                       this.message.setLabel(
-                               data.message !== undefined ? data.message : this.constructor.static.message
-                       );
-                       this.message.$element.toggleClass(
-                               'oo-ui-messageDialog-message-verbose',
-                               data.verbose !== undefined ? data.verbose : this.constructor.static.verbose
-                       );
-               }, this );
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.MessageDialog.prototype.getReadyProcess = function ( data ) {
-       data = data || {};
-
-       // Parent method
-       return OO.ui.MessageDialog.parent.prototype.getReadyProcess.call( this, data )
-               .next( function () {
-                       // Focus the primary action button
-                       var actions = this.actions.get();
-                       actions = actions.filter( function ( action ) {
-                               return action.getFlags().indexOf( 'primary' ) > -1;
-                       } );
-                       if ( actions.length > 0 ) {
-                               actions[ 0 ].$button.focus();
-                       }
-               }, this );
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.MessageDialog.prototype.getBodyHeight = function () {
-       var bodyHeight, oldOverflow,
-               $scrollable = this.container.$element;
-
-       oldOverflow = $scrollable[ 0 ].style.overflow;
-       $scrollable[ 0 ].style.overflow = 'hidden';
-
-       OO.ui.Element.static.reconsiderScrollbars( $scrollable[ 0 ] );
-
-       bodyHeight = this.text.$element.outerHeight( true );
-       $scrollable[ 0 ].style.overflow = oldOverflow;
-
-       return bodyHeight;
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.MessageDialog.prototype.setDimensions = function ( dim ) {
-       var $scrollable = this.container.$element;
-       OO.ui.MessageDialog.parent.prototype.setDimensions.call( this, dim );
-
-       // Twiddle the overflow property, otherwise an unnecessary scrollbar will be produced.
-       // Need to do it after transition completes (250ms), add 50ms just in case.
-       setTimeout( function () {
-               var oldOverflow = $scrollable[ 0 ].style.overflow;
-               $scrollable[ 0 ].style.overflow = 'hidden';
-
-               OO.ui.Element.static.reconsiderScrollbars( $scrollable[ 0 ] );
-
-               $scrollable[ 0 ].style.overflow = oldOverflow;
-       }, 300 );
-
-       return this;
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.MessageDialog.prototype.initialize = function () {
-       // Parent method
-       OO.ui.MessageDialog.parent.prototype.initialize.call( this );
-
-       // Properties
-       this.$actions = $( '<div>' );
-       this.container = new OO.ui.PanelLayout( {
-               scrollable: true, classes: [ 'oo-ui-messageDialog-container' ]
-       } );
-       this.text = new OO.ui.PanelLayout( {
-               padded: true, expanded: false, classes: [ 'oo-ui-messageDialog-text' ]
-       } );
-       this.message = new OO.ui.LabelWidget( {
-               classes: [ 'oo-ui-messageDialog-message' ]
-       } );
-
-       // Initialization
-       this.title.$element.addClass( 'oo-ui-messageDialog-title' );
-       this.$content.addClass( 'oo-ui-messageDialog-content' );
-       this.container.$element.append( this.text.$element );
-       this.text.$element.append( this.title.$element, this.message.$element );
-       this.$body.append( this.container.$element );
-       this.$actions.addClass( 'oo-ui-messageDialog-actions' );
-       this.$foot.append( this.$actions );
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.MessageDialog.prototype.attachActions = function () {
-       var i, len, other, special, others;
-
-       // Parent method
-       OO.ui.MessageDialog.parent.prototype.attachActions.call( this );
-
-       special = this.actions.getSpecial();
-       others = this.actions.getOthers();
-
-       if ( special.safe ) {
-               this.$actions.append( special.safe.$element );
-               special.safe.toggleFramed( false );
-       }
-       if ( others.length ) {
-               for ( i = 0, len = others.length; i < len; i++ ) {
-                       other = others[ i ];
-                       this.$actions.append( other.$element );
-                       other.toggleFramed( false );
-               }
-       }
-       if ( special.primary ) {
-               this.$actions.append( special.primary.$element );
-               special.primary.toggleFramed( false );
-       }
-
-       if ( !this.isOpening() ) {
-               // If the dialog is currently opening, this will be called automatically soon.
-               // This also calls #fitActions.
-               this.updateSize();
-       }
-};
-
-/**
- * Fit action actions into columns or rows.
- *
- * Columns will be used if all labels can fit without overflow, otherwise rows will be used.
- *
- * @private
- */
-OO.ui.MessageDialog.prototype.fitActions = function () {
-       var i, len, action,
-               previous = this.verticalActionLayout,
-               actions = this.actions.get();
-
-       // Detect clipping
-       this.toggleVerticalActionLayout( false );
-       for ( i = 0, len = actions.length; i < len; i++ ) {
-               action = actions[ i ];
-               if ( action.$element.innerWidth() < action.$label.outerWidth( true ) ) {
-                       this.toggleVerticalActionLayout( true );
-                       break;
-               }
-       }
-
-       // Move the body out of the way of the foot
-       this.$body.css( 'bottom', this.$foot.outerHeight( true ) );
-
-       if ( this.verticalActionLayout !== previous ) {
-               // We changed the layout, window height might need to be updated.
-               this.updateSize();
-       }
-};
-
-/**
- * ProcessDialog windows encapsulate a {@link OO.ui.Process process} and all of the code necessary
- * to complete it. If the process terminates with an error, a customizable {@link OO.ui.Error error
- * interface} alerts users to the trouble, permitting the user to dismiss the error and try again when
- * relevant. The ProcessDialog class is always extended and customized with the actions and content
- * required for each process.
- *
- * The process dialog box consists of a header that visually represents the ‘working’ state of long
- * processes with an animation. The header contains the dialog title as well as
- * two {@link OO.ui.ActionWidget action widgets}:  a ‘safe’ action on the left (e.g., ‘Cancel’) and
- * a ‘primary’ action on the right (e.g., ‘Done’).
- *
- * Like other windows, the process dialog is managed by a {@link OO.ui.WindowManager window manager}.
- * Please see the [OOjs UI documentation on MediaWiki][1] for more information and examples.
- *
- *     @example
- *     // Example: Creating and opening a process dialog window.
- *     function MyProcessDialog( config ) {
- *         MyProcessDialog.parent.call( this, config );
- *     }
- *     OO.inheritClass( MyProcessDialog, OO.ui.ProcessDialog );
- *
- *     MyProcessDialog.static.title = 'Process dialog';
- *     MyProcessDialog.static.actions = [
- *         { action: 'save', label: 'Done', flags: 'primary' },
- *         { label: 'Cancel', flags: 'safe' }
- *     ];
- *
- *     MyProcessDialog.prototype.initialize = function () {
- *         MyProcessDialog.parent.prototype.initialize.apply( this, arguments );
- *         this.content = new OO.ui.PanelLayout( { padded: true, expanded: false } );
- *         this.content.$element.append( '<p>This is a process dialog window. The header contains the title and two buttons: \'Cancel\' (a safe action) on the left and \'Done\' (a primary action)  on the right.</p>' );
- *         this.$body.append( this.content.$element );
- *     };
- *     MyProcessDialog.prototype.getActionProcess = function ( action ) {
- *         var dialog = this;
- *         if ( action ) {
- *             return new OO.ui.Process( function () {
- *                 dialog.close( { action: action } );
- *             } );
- *         }
- *         return MyProcessDialog.parent.prototype.getActionProcess.call( this, action );
- *     };
- *
- *     var windowManager = new OO.ui.WindowManager();
- *     $( 'body' ).append( windowManager.$element );
- *
- *     var dialog = new MyProcessDialog();
- *     windowManager.addWindows( [ dialog ] );
- *     windowManager.openWindow( dialog );
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Process_Dialogs
- *
- * @abstract
- * @class
- * @extends OO.ui.Dialog
- *
- * @constructor
- * @param {Object} [config] Configuration options
- */
-OO.ui.ProcessDialog = function OoUiProcessDialog( config ) {
-       // Parent constructor
-       OO.ui.ProcessDialog.parent.call( this, config );
-
-       // Properties
-       this.fitOnOpen = false;
-
-       // Initialization
-       this.$element.addClass( 'oo-ui-processDialog' );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.ProcessDialog, OO.ui.Dialog );
-
-/* Methods */
-
-/**
- * Handle dismiss button click events.
- *
- * Hides errors.
- *
- * @private
- */
-OO.ui.ProcessDialog.prototype.onDismissErrorButtonClick = function () {
-       this.hideErrors();
-};
-
-/**
- * Handle retry button click events.
- *
- * Hides errors and then tries again.
- *
- * @private
- */
-OO.ui.ProcessDialog.prototype.onRetryButtonClick = function () {
-       this.hideErrors();
-       this.executeAction( this.currentAction );
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.ProcessDialog.prototype.onActionResize = function ( action ) {
-       if ( this.actions.isSpecial( action ) ) {
-               this.fitLabel();
-       }
-       return OO.ui.ProcessDialog.parent.prototype.onActionResize.call( this, action );
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.ProcessDialog.prototype.initialize = function () {
-       // Parent method
-       OO.ui.ProcessDialog.parent.prototype.initialize.call( this );
-
-       // Properties
-       this.$navigation = $( '<div>' );
-       this.$location = $( '<div>' );
-       this.$safeActions = $( '<div>' );
-       this.$primaryActions = $( '<div>' );
-       this.$otherActions = $( '<div>' );
-       this.dismissButton = new OO.ui.ButtonWidget( {
-               label: OO.ui.msg( 'ooui-dialog-process-dismiss' )
-       } );
-       this.retryButton = new OO.ui.ButtonWidget();
-       this.$errors = $( '<div>' );
-       this.$errorsTitle = $( '<div>' );
-
-       // Events
-       this.dismissButton.connect( this, { click: 'onDismissErrorButtonClick' } );
-       this.retryButton.connect( this, { click: 'onRetryButtonClick' } );
-
-       // Initialization
-       this.title.$element.addClass( 'oo-ui-processDialog-title' );
-       this.$location
-               .append( this.title.$element )
-               .addClass( 'oo-ui-processDialog-location' );
-       this.$safeActions.addClass( 'oo-ui-processDialog-actions-safe' );
-       this.$primaryActions.addClass( 'oo-ui-processDialog-actions-primary' );
-       this.$otherActions.addClass( 'oo-ui-processDialog-actions-other' );
-       this.$errorsTitle
-               .addClass( 'oo-ui-processDialog-errors-title' )
-               .text( OO.ui.msg( 'ooui-dialog-process-error' ) );
-       this.$errors
-               .addClass( 'oo-ui-processDialog-errors oo-ui-element-hidden' )
-               .append( this.$errorsTitle, this.dismissButton.$element, this.retryButton.$element );
-       this.$content
-               .addClass( 'oo-ui-processDialog-content' )
-               .append( this.$errors );
-       this.$navigation
-               .addClass( 'oo-ui-processDialog-navigation' )
-               .append( this.$safeActions, this.$location, this.$primaryActions );
-       this.$head.append( this.$navigation );
-       this.$foot.append( this.$otherActions );
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.ProcessDialog.prototype.getActionWidgets = function ( actions ) {
-       var i, len, widgets = [];
-       for ( i = 0, len = actions.length; i < len; i++ ) {
-               widgets.push(
-                       new OO.ui.ActionWidget( $.extend( { framed: true }, actions[ i ] ) )
-               );
-       }
-       return widgets;
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.ProcessDialog.prototype.attachActions = function () {
-       var i, len, other, special, others;
-
-       // Parent method
-       OO.ui.ProcessDialog.parent.prototype.attachActions.call( this );
-
-       special = this.actions.getSpecial();
-       others = this.actions.getOthers();
-       if ( special.primary ) {
-               this.$primaryActions.append( special.primary.$element );
-       }
-       for ( i = 0, len = others.length; i < len; i++ ) {
-               other = others[ i ];
-               this.$otherActions.append( other.$element );
-       }
-       if ( special.safe ) {
-               this.$safeActions.append( special.safe.$element );
-       }
-
-       this.fitLabel();
-       this.$body.css( 'bottom', this.$foot.outerHeight( true ) );
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.ProcessDialog.prototype.executeAction = function ( action ) {
-       var process = this;
-       return OO.ui.ProcessDialog.parent.prototype.executeAction.call( this, action )
-               .fail( function ( errors ) {
-                       process.showErrors( errors || [] );
-               } );
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.ProcessDialog.prototype.setDimensions = function () {
-       // Parent method
-       OO.ui.ProcessDialog.parent.prototype.setDimensions.apply( this, arguments );
-
-       this.fitLabel();
-};
-
-/**
- * Fit label between actions.
- *
- * @private
- * @chainable
- */
-OO.ui.ProcessDialog.prototype.fitLabel = function () {
-       var safeWidth, primaryWidth, biggerWidth, labelWidth, navigationWidth, leftWidth, rightWidth,
-               size = this.getSizeProperties();
-
-       if ( typeof size.width !== 'number' ) {
-               if ( this.isOpened() ) {
-                       navigationWidth = this.$head.width() - 20;
-               } else if ( this.isOpening() ) {
-                       if ( !this.fitOnOpen ) {
-                               // Size is relative and the dialog isn't open yet, so wait.
-                               this.manager.opening.done( this.fitLabel.bind( this ) );
-                               this.fitOnOpen = true;
-                       }
-                       return;
-               } else {
-                       return;
-               }
-       } else {
-               navigationWidth = size.width - 20;
-       }
-
-       safeWidth = this.$safeActions.is( ':visible' ) ? this.$safeActions.width() : 0;
-       primaryWidth = this.$primaryActions.is( ':visible' ) ? this.$primaryActions.width() : 0;
-       biggerWidth = Math.max( safeWidth, primaryWidth );
-
-       labelWidth = this.title.$element.width();
-
-       if ( 2 * biggerWidth + labelWidth < navigationWidth ) {
-               // We have enough space to center the label
-               leftWidth = rightWidth = biggerWidth;
-       } else {
-               // Let's hope we at least have enough space not to overlap, because we can't wrap the label…
-               if ( this.getDir() === 'ltr' ) {
-                       leftWidth = safeWidth;
-                       rightWidth = primaryWidth;
-               } else {
-                       leftWidth = primaryWidth;
-                       rightWidth = safeWidth;
-               }
-       }
-
-       this.$location.css( { paddingLeft: leftWidth, paddingRight: rightWidth } );
-
-       return this;
-};
-
-/**
- * Handle errors that occurred during accept or reject processes.
- *
- * @private
- * @param {OO.ui.Error[]|OO.ui.Error} errors Errors to be handled
- */
-OO.ui.ProcessDialog.prototype.showErrors = function ( errors ) {
-       var i, len, $item, actions,
-               items = [],
-               abilities = {},
-               recoverable = true,
-               warning = false;
-
-       if ( errors instanceof OO.ui.Error ) {
-               errors = [ errors ];
-       }
-
-       for ( i = 0, len = errors.length; i < len; i++ ) {
-               if ( !errors[ i ].isRecoverable() ) {
-                       recoverable = false;
-               }
-               if ( errors[ i ].isWarning() ) {
-                       warning = true;
-               }
-               $item = $( '<div>' )
-                       .addClass( 'oo-ui-processDialog-error' )
-                       .append( errors[ i ].getMessage() );
-               items.push( $item[ 0 ] );
-       }
-       this.$errorItems = $( items );
-       if ( recoverable ) {
-               abilities[ this.currentAction ] = true;
-               // Copy the flags from the first matching action
-               actions = this.actions.get( { actions: this.currentAction } );
-               if ( actions.length ) {
-                       this.retryButton.clearFlags().setFlags( actions[ 0 ].getFlags() );
-               }
-       } else {
-               abilities[ this.currentAction ] = false;
-               this.actions.setAbilities( abilities );
-       }
-       if ( warning ) {
-               this.retryButton.setLabel( OO.ui.msg( 'ooui-dialog-process-continue' ) );
-       } else {
-               this.retryButton.setLabel( OO.ui.msg( 'ooui-dialog-process-retry' ) );
-       }
-       this.retryButton.toggle( recoverable );
-       this.$errorsTitle.after( this.$errorItems );
-       this.$errors.removeClass( 'oo-ui-element-hidden' ).scrollTop( 0 );
-};
-
-/**
- * Hide errors.
- *
- * @private
- */
-OO.ui.ProcessDialog.prototype.hideErrors = function () {
-       this.$errors.addClass( 'oo-ui-element-hidden' );
-       if ( this.$errorItems ) {
-               this.$errorItems.remove();
-               this.$errorItems = null;
-       }
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.ProcessDialog.prototype.getTeardownProcess = function ( data ) {
-       // Parent method
-       return OO.ui.ProcessDialog.parent.prototype.getTeardownProcess.call( this, data )
-               .first( function () {
-                       // Make sure to hide errors
-                       this.hideErrors();
-                       this.fitOnOpen = false;
-               }, this );
-};
-
-/**
- * FieldLayouts are used with OO.ui.FieldsetLayout. Each FieldLayout requires a field-widget,
- * which is a widget that is specified by reference before any optional configuration settings.
- *
- * Field layouts can be configured with help text and/or labels. Labels are aligned in one of four ways:
- *
- * - **left**: The label is placed before the field-widget and aligned with the left margin.
- *   A left-alignment is used for forms with many fields.
- * - **right**: The label is placed before the field-widget and aligned to the right margin.
- *   A right-alignment is used for long but familiar forms which users tab through,
- *   verifying the current field with a quick glance at the label.
- * - **top**: The label is placed above the field-widget. A top-alignment is used for brief forms
- *   that users fill out from top to bottom.
- * - **inline**: The label is placed after the field-widget and aligned to the left.
- *   An inline-alignment is best used with checkboxes or radio buttons.
- *
- * Help text is accessed via a help icon that appears in the upper right corner of the rendered field layout.
- * Please see the [OOjs UI documentation on MediaWiki] [1] for examples and more information.
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Layouts/Fields_and_Fieldsets
- * @class
- * @extends OO.ui.Layout
- * @mixins OO.ui.mixin.LabelElement
- * @mixins OO.ui.mixin.TitledElement
- *
- * @constructor
- * @param {OO.ui.Widget} fieldWidget Field widget
- * @param {Object} [config] Configuration options
- * @cfg {string} [align='left'] Alignment of the label: 'left', 'right', 'top' or 'inline'
- * @cfg {Array} [errors] Error messages about the widget, which will be displayed below the widget.
- *  The array may contain strings or OO.ui.HtmlSnippet instances.
- * @cfg {Array} [notices] Notices about the widget, which will be displayed below the widget.
- *  The array may contain strings or OO.ui.HtmlSnippet instances.
- * @cfg {string|OO.ui.HtmlSnippet} [help] Help text. When help text is specified, a "help" icon will appear
- *  in the upper-right corner of the rendered field; clicking it will display the text in a popup.
- *  For important messages, you are advised to use `notices`, as they are always shown.
- *
- * @throws {Error} An error is thrown if no widget is specified
- */
-OO.ui.FieldLayout = function OoUiFieldLayout( fieldWidget, config ) {
-       var hasInputWidget, div;
-
-       // Allow passing positional parameters inside the config object
-       if ( OO.isPlainObject( fieldWidget ) && config === undefined ) {
-               config = fieldWidget;
-               fieldWidget = config.fieldWidget;
-       }
-
-       // Make sure we have required constructor arguments
-       if ( fieldWidget === undefined ) {
-               throw new Error( 'Widget not found' );
-       }
-
-       hasInputWidget = fieldWidget.constructor.static.supportsSimpleLabel;
-
-       // Configuration initialization
-       config = $.extend( { align: 'left' }, config );
-
-       // Parent constructor
-       OO.ui.FieldLayout.parent.call( this, config );
-
-       // Mixin constructors
-       OO.ui.mixin.LabelElement.call( this, config );
-       OO.ui.mixin.TitledElement.call( this, $.extend( {}, config, { $titled: this.$label } ) );
-
-       // Properties
-       this.fieldWidget = fieldWidget;
-       this.errors = [];
-       this.notices = [];
-       this.$field = $( '<div>' );
-       this.$messages = $( '<ul>' );
-       this.$body = $( '<' + ( hasInputWidget ? 'label' : 'div' ) + '>' );
-       this.align = null;
-       if ( config.help ) {
-               this.popupButtonWidget = new OO.ui.PopupButtonWidget( {
-                       classes: [ 'oo-ui-fieldLayout-help' ],
-                       framed: false,
-                       icon: 'info'
-               } );
-
-               div = $( '<div>' );
-               if ( config.help instanceof OO.ui.HtmlSnippet ) {
-                       div.html( config.help.toString() );
-               } else {
-                       div.text( config.help );
-               }
-               this.popupButtonWidget.getPopup().$body.append(
-                       div.addClass( 'oo-ui-fieldLayout-help-content' )
-               );
-               this.$help = this.popupButtonWidget.$element;
-       } else {
-               this.$help = $( [] );
-       }
-
-       // Events
-       if ( hasInputWidget ) {
-               this.$label.on( 'click', this.onLabelClick.bind( this ) );
-       }
-       this.fieldWidget.connect( this, { disable: 'onFieldDisable' } );
-
-       // Initialization
-       this.$element
-               .addClass( 'oo-ui-fieldLayout' )
-               .append( this.$help, this.$body );
-       this.$body.addClass( 'oo-ui-fieldLayout-body' );
-       this.$messages.addClass( 'oo-ui-fieldLayout-messages' );
-       this.$field
-               .addClass( 'oo-ui-fieldLayout-field' )
-               .toggleClass( 'oo-ui-fieldLayout-disable', this.fieldWidget.isDisabled() )
-               .append( this.fieldWidget.$element );
-
-       this.setErrors( config.errors || [] );
-       this.setNotices( config.notices || [] );
-       this.setAlignment( config.align );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.FieldLayout, OO.ui.Layout );
-OO.mixinClass( OO.ui.FieldLayout, OO.ui.mixin.LabelElement );
-OO.mixinClass( OO.ui.FieldLayout, OO.ui.mixin.TitledElement );
-
-/* Methods */
-
-/**
- * Handle field disable events.
- *
- * @private
- * @param {boolean} value Field is disabled
- */
-OO.ui.FieldLayout.prototype.onFieldDisable = function ( value ) {
-       this.$element.toggleClass( 'oo-ui-fieldLayout-disabled', value );
-};
-
-/**
- * Handle label mouse click events.
- *
- * @private
- * @param {jQuery.Event} e Mouse click event
- */
-OO.ui.FieldLayout.prototype.onLabelClick = function () {
-       this.fieldWidget.simulateLabelClick();
-       return false;
-};
-
-/**
- * Get the widget contained by the field.
- *
- * @return {OO.ui.Widget} Field widget
- */
-OO.ui.FieldLayout.prototype.getField = function () {
-       return this.fieldWidget;
-};
-
-/**
- * @protected
- * @param {string} kind 'error' or 'notice'
- * @param {string|OO.ui.HtmlSnippet} text
- * @return {jQuery}
- */
-OO.ui.FieldLayout.prototype.makeMessage = function ( kind, text ) {
-       var $listItem, $icon, message;
-       $listItem = $( '<li>' );
-       if ( kind === 'error' ) {
-               $icon = new OO.ui.IconWidget( { icon: 'alert', flags: [ 'warning' ] } ).$element;
-       } else if ( kind === 'notice' ) {
-               $icon = new OO.ui.IconWidget( { icon: 'info' } ).$element;
-       } else {
-               $icon = '';
-       }
-       message = new OO.ui.LabelWidget( { label: text } );
-       $listItem
-               .append( $icon, message.$element )
-               .addClass( 'oo-ui-fieldLayout-messages-' + kind );
-       return $listItem;
-};
-
-/**
- * Set the field alignment mode.
- *
- * @private
- * @param {string} value Alignment mode, either 'left', 'right', 'top' or 'inline'
- * @chainable
- */
-OO.ui.FieldLayout.prototype.setAlignment = function ( value ) {
-       if ( value !== this.align ) {
-               // Default to 'left'
-               if ( [ 'left', 'right', 'top', 'inline' ].indexOf( value ) === -1 ) {
-                       value = 'left';
-               }
-               // Reorder elements
-               if ( value === 'inline' ) {
-                       this.$body.append( this.$field, this.$label );
-               } else {
-                       this.$body.append( this.$label, this.$field );
-               }
-               // Set classes. The following classes can be used here:
-               // * oo-ui-fieldLayout-align-left
-               // * oo-ui-fieldLayout-align-right
-               // * oo-ui-fieldLayout-align-top
-               // * oo-ui-fieldLayout-align-inline
-               if ( this.align ) {
-                       this.$element.removeClass( 'oo-ui-fieldLayout-align-' + this.align );
-               }
-               this.$element.addClass( 'oo-ui-fieldLayout-align-' + value );
-               this.align = value;
-       }
-
-       return this;
-};
-
-/**
- * Set the list of error messages.
- *
- * @param {Array} errors Error messages about the widget, which will be displayed below the widget.
- *  The array may contain strings or OO.ui.HtmlSnippet instances.
- * @chainable
- */
-OO.ui.FieldLayout.prototype.setErrors = function ( errors ) {
-       this.errors = errors.slice();
-       this.updateMessages();
-       return this;
-};
-
-/**
- * Set the list of notice messages.
- *
- * @param {Array} notices Notices about the widget, which will be displayed below the widget.
- *  The array may contain strings or OO.ui.HtmlSnippet instances.
- * @chainable
- */
-OO.ui.FieldLayout.prototype.setNotices = function ( notices ) {
-       this.notices = notices.slice();
-       this.updateMessages();
-       return this;
-};
-
-/**
- * Update the rendering of error and notice messages.
- *
- * @private
- */
-OO.ui.FieldLayout.prototype.updateMessages = function () {
-       var i;
-       this.$messages.empty();
-
-       if ( this.errors.length || this.notices.length ) {
-               this.$body.after( this.$messages );
-       } else {
-               this.$messages.remove();
-               return;
-       }
-
-       for ( i = 0; i < this.notices.length; i++ ) {
-               this.$messages.append( this.makeMessage( 'notice', this.notices[ i ] ) );
-       }
-       for ( i = 0; i < this.errors.length; i++ ) {
-               this.$messages.append( this.makeMessage( 'error', this.errors[ i ] ) );
-       }
-};
-
-/**
- * ActionFieldLayouts are used with OO.ui.FieldsetLayout. The layout consists of a field-widget, a button,
- * and an optional label and/or help text. The field-widget (e.g., a {@link OO.ui.TextInputWidget TextInputWidget}),
- * is required and is specified before any optional configuration settings.
- *
- * Labels can be aligned in one of four ways:
- *
- * - **left**: The label is placed before the field-widget and aligned with the left margin.
- *   A left-alignment is used for forms with many fields.
- * - **right**: The label is placed before the field-widget and aligned to the right margin.
- *   A right-alignment is used for long but familiar forms which users tab through,
- *   verifying the current field with a quick glance at the label.
- * - **top**: The label is placed above the field-widget. A top-alignment is used for brief forms
- *   that users fill out from top to bottom.
- * - **inline**: The label is placed after the field-widget and aligned to the left.
- *   An inline-alignment is best used with checkboxes or radio buttons.
- *
- * Help text is accessed via a help icon that appears in the upper right corner of the rendered field layout when help
- * text is specified.
- *
- *     @example
- *     // Example of an ActionFieldLayout
- *     var actionFieldLayout = new OO.ui.ActionFieldLayout(
- *         new OO.ui.TextInputWidget( {
- *             placeholder: 'Field widget'
- *         } ),
- *         new OO.ui.ButtonWidget( {
- *             label: 'Button'
- *         } ),
- *         {
- *             label: 'An ActionFieldLayout. This label is aligned top',
- *             align: 'top',
- *             help: 'This is help text'
- *         }
- *     );
- *
- *     $( 'body' ).append( actionFieldLayout.$element );
- *
- * @class
- * @extends OO.ui.FieldLayout
- *
- * @constructor
- * @param {OO.ui.Widget} fieldWidget Field widget
- * @param {OO.ui.ButtonWidget} buttonWidget Button widget
- */
-OO.ui.ActionFieldLayout = function OoUiActionFieldLayout( fieldWidget, buttonWidget, config ) {
-       // Allow passing positional parameters inside the config object
-       if ( OO.isPlainObject( fieldWidget ) && config === undefined ) {
-               config = fieldWidget;
-               fieldWidget = config.fieldWidget;
-               buttonWidget = config.buttonWidget;
-       }
-
-       // Parent constructor
-       OO.ui.ActionFieldLayout.parent.call( this, fieldWidget, config );
-
-       // Properties
-       this.buttonWidget = buttonWidget;
-       this.$button = $( '<div>' );
-       this.$input = $( '<div>' );
-
-       // Initialization
-       this.$element
-               .addClass( 'oo-ui-actionFieldLayout' );
-       this.$button
-               .addClass( 'oo-ui-actionFieldLayout-button' )
-               .append( this.buttonWidget.$element );
-       this.$input
-               .addClass( 'oo-ui-actionFieldLayout-input' )
-               .append( this.fieldWidget.$element );
-       this.$field
-               .append( this.$input, this.$button );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.ActionFieldLayout, OO.ui.FieldLayout );
-
-/**
- * FieldsetLayouts are composed of one or more {@link OO.ui.FieldLayout FieldLayouts},
- * which each contain an individual widget and, optionally, a label. Each Fieldset can be
- * configured with a label as well. For more information and examples,
- * please see the [OOjs UI documentation on MediaWiki][1].
- *
- *     @example
- *     // Example of a fieldset layout
- *     var input1 = new OO.ui.TextInputWidget( {
- *         placeholder: 'A text input field'
- *     } );
- *
- *     var input2 = new OO.ui.TextInputWidget( {
- *         placeholder: 'A text input field'
- *     } );
- *
- *     var fieldset = new OO.ui.FieldsetLayout( {
- *         label: 'Example of a fieldset layout'
- *     } );
- *
- *     fieldset.addItems( [
- *         new OO.ui.FieldLayout( input1, {
- *             label: 'Field One'
- *         } ),
- *         new OO.ui.FieldLayout( input2, {
- *             label: 'Field Two'
- *         } )
- *     ] );
- *     $( 'body' ).append( fieldset.$element );
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Layouts/Fields_and_Fieldsets
- *
- * @class
- * @extends OO.ui.Layout
- * @mixins OO.ui.mixin.IconElement
- * @mixins OO.ui.mixin.LabelElement
- * @mixins OO.ui.mixin.GroupElement
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {OO.ui.FieldLayout[]} [items] An array of fields to add to the fieldset. See OO.ui.FieldLayout for more information about fields.
- */
-OO.ui.FieldsetLayout = function OoUiFieldsetLayout( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Parent constructor
-       OO.ui.FieldsetLayout.parent.call( this, config );
-
-       // Mixin constructors
-       OO.ui.mixin.IconElement.call( this, config );
-       OO.ui.mixin.LabelElement.call( this, config );
-       OO.ui.mixin.GroupElement.call( this, config );
-
-       if ( config.help ) {
-               this.popupButtonWidget = new OO.ui.PopupButtonWidget( {
-                       classes: [ 'oo-ui-fieldsetLayout-help' ],
-                       framed: false,
-                       icon: 'info'
-               } );
-
-               this.popupButtonWidget.getPopup().$body.append(
-                       $( '<div>' )
-                               .text( config.help )
-                               .addClass( 'oo-ui-fieldsetLayout-help-content' )
-               );
-               this.$help = this.popupButtonWidget.$element;
-       } else {
-               this.$help = $( [] );
-       }
-
-       // Initialization
-       this.$element
-               .addClass( 'oo-ui-fieldsetLayout' )
-               .prepend( this.$help, this.$icon, this.$label, this.$group );
-       if ( Array.isArray( config.items ) ) {
-               this.addItems( config.items );
-       }
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.FieldsetLayout, OO.ui.Layout );
-OO.mixinClass( OO.ui.FieldsetLayout, OO.ui.mixin.IconElement );
-OO.mixinClass( OO.ui.FieldsetLayout, OO.ui.mixin.LabelElement );
-OO.mixinClass( OO.ui.FieldsetLayout, OO.ui.mixin.GroupElement );
-
-/**
- * FormLayouts are used to wrap {@link OO.ui.FieldsetLayout FieldsetLayouts} when you intend to use browser-based
- * form submission for the fields instead of handling them in JavaScript. Form layouts can be configured with an
- * HTML form action, an encoding type, and a method using the #action, #enctype, and #method configs, respectively.
- * See the [OOjs UI documentation on MediaWiki] [1] for more information and examples.
- *
- * Only widgets from the {@link OO.ui.InputWidget InputWidget} family support form submission. It
- * includes standard form elements like {@link OO.ui.CheckboxInputWidget checkboxes}, {@link
- * OO.ui.RadioInputWidget radio buttons} and {@link OO.ui.TextInputWidget text fields}, as well as
- * some fancier controls. Some controls have both regular and InputWidget variants, for example
- * OO.ui.DropdownWidget and OO.ui.DropdownInputWidget – only the latter support form submission and
- * often have simplified APIs to match the capabilities of HTML forms.
- * See the [OOjs UI Inputs documentation on MediaWiki] [2] for more information about InputWidgets.
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Layouts/Forms
- * [2]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Inputs
- *
- *     @example
- *     // Example of a form layout that wraps a fieldset layout
- *     var input1 = new OO.ui.TextInputWidget( {
- *         placeholder: 'Username'
- *     } );
- *     var input2 = new OO.ui.TextInputWidget( {
- *         placeholder: 'Password',
- *         type: 'password'
- *     } );
- *     var submit = new OO.ui.ButtonInputWidget( {
- *         label: 'Submit'
- *     } );
- *
- *     var fieldset = new OO.ui.FieldsetLayout( {
- *         label: 'A form layout'
- *     } );
- *     fieldset.addItems( [
- *         new OO.ui.FieldLayout( input1, {
- *             label: 'Username',
- *             align: 'top'
- *         } ),
- *         new OO.ui.FieldLayout( input2, {
- *             label: 'Password',
- *             align: 'top'
- *         } ),
- *         new OO.ui.FieldLayout( submit )
- *     ] );
- *     var form = new OO.ui.FormLayout( {
- *         items: [ fieldset ],
- *         action: '/api/formhandler',
- *         method: 'get'
- *     } )
- *     $( 'body' ).append( form.$element );
- *
- * @class
- * @extends OO.ui.Layout
- * @mixins OO.ui.mixin.GroupElement
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {string} [method] HTML form `method` attribute
- * @cfg {string} [action] HTML form `action` attribute
- * @cfg {string} [enctype] HTML form `enctype` attribute
- * @cfg {OO.ui.FieldsetLayout[]} [items] Fieldset layouts to add to the form layout.
- */
-OO.ui.FormLayout = function OoUiFormLayout( config ) {
-       var action;
-
-       // Configuration initialization
-       config = config || {};
-
-       // Parent constructor
-       OO.ui.FormLayout.parent.call( this, config );
-
-       // Mixin constructors
-       OO.ui.mixin.GroupElement.call( this, $.extend( {}, config, { $group: this.$element } ) );
-
-       // Events
-       this.$element.on( 'submit', this.onFormSubmit.bind( this ) );
-
-       // Make sure the action is safe
-       action = config.action;
-       if ( action !== undefined && !OO.ui.isSafeUrl( action ) ) {
-               action = './' + action;
-       }
-
-       // Initialization
-       this.$element
-               .addClass( 'oo-ui-formLayout' )
-               .attr( {
-                       method: config.method,
-                       action: action,
-                       enctype: config.enctype
-               } );
-       if ( Array.isArray( config.items ) ) {
-               this.addItems( config.items );
-       }
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.FormLayout, OO.ui.Layout );
-OO.mixinClass( OO.ui.FormLayout, OO.ui.mixin.GroupElement );
-
-/* Events */
-
-/**
- * A 'submit' event is emitted when the form is submitted.
- *
- * @event submit
- */
-
-/* Static Properties */
-
-OO.ui.FormLayout.static.tagName = 'form';
-
-/* Methods */
-
-/**
- * Handle form submit events.
- *
- * @private
- * @param {jQuery.Event} e Submit event
- * @fires submit
- */
-OO.ui.FormLayout.prototype.onFormSubmit = function () {
-       if ( this.emit( 'submit' ) ) {
-               return false;
-       }
-};
-
-/**
- * MenuLayouts combine a menu and a content {@link OO.ui.PanelLayout panel}. The menu is positioned relative to the content (after, before, top, or bottom)
- * and its size is customized with the #menuSize config. The content area will fill all remaining space.
- *
- *     @example
- *     var menuLayout = new OO.ui.MenuLayout( {
- *         position: 'top'
- *     } ),
- *         menuPanel = new OO.ui.PanelLayout( { padded: true, expanded: true, scrollable: true } ),
- *         contentPanel = new OO.ui.PanelLayout( { padded: true, expanded: true, scrollable: true } ),
- *         select = new OO.ui.SelectWidget( {
- *             items: [
- *                 new OO.ui.OptionWidget( {
- *                     data: 'before',
- *                     label: 'Before',
- *                 } ),
- *                 new OO.ui.OptionWidget( {
- *                     data: 'after',
- *                     label: 'After',
- *                 } ),
- *                 new OO.ui.OptionWidget( {
- *                     data: 'top',
- *                     label: 'Top',
- *                 } ),
- *                 new OO.ui.OptionWidget( {
- *                     data: 'bottom',
- *                     label: 'Bottom',
- *                 } )
- *              ]
- *         } ).on( 'select', function ( item ) {
- *            menuLayout.setMenuPosition( item.getData() );
- *         } );
- *
- *     menuLayout.$menu.append(
- *         menuPanel.$element.append( '<b>Menu panel</b>', select.$element )
- *     );
- *     menuLayout.$content.append(
- *         contentPanel.$element.append( '<b>Content panel</b>', '<p>Note that the menu is positioned relative to the content panel: top, bottom, after, before.</p>')
- *     );
- *     $( 'body' ).append( menuLayout.$element );
- *
- * If menu size needs to be overridden, it can be accomplished using CSS similar to the snippet
- * below. MenuLayout's CSS will override the appropriate values with 'auto' or '0' to display the
- * menu correctly. If `menuPosition` is known beforehand, CSS rules corresponding to other positions
- * may be omitted.
- *
- *     .oo-ui-menuLayout-menu {
- *         height: 200px;
- *         width: 200px;
- *     }
- *     .oo-ui-menuLayout-content {
- *         top: 200px;
- *         left: 200px;
- *         right: 200px;
- *         bottom: 200px;
- *     }
- *
- * @class
- * @extends OO.ui.Layout
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {boolean} [showMenu=true] Show menu
- * @cfg {string} [menuPosition='before'] Position of menu: `top`, `after`, `bottom` or `before`
- */
-OO.ui.MenuLayout = function OoUiMenuLayout( config ) {
-       // Configuration initialization
-       config = $.extend( {
-               showMenu: true,
-               menuPosition: 'before'
-       }, config );
-
-       // Parent constructor
-       OO.ui.MenuLayout.parent.call( this, config );
-
-       /**
-        * Menu DOM node
-        *
-        * @property {jQuery}
-        */
-       this.$menu = $( '<div>' );
-       /**
-        * Content DOM node
-        *
-        * @property {jQuery}
-        */
-       this.$content = $( '<div>' );
-
-       // Initialization
-       this.$menu
-               .addClass( 'oo-ui-menuLayout-menu' );
-       this.$content.addClass( 'oo-ui-menuLayout-content' );
-       this.$element
-               .addClass( 'oo-ui-menuLayout' )
-               .append( this.$content, this.$menu );
-       this.setMenuPosition( config.menuPosition );
-       this.toggleMenu( config.showMenu );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.MenuLayout, OO.ui.Layout );
-
-/* Methods */
-
-/**
- * Toggle menu.
- *
- * @param {boolean} showMenu Show menu, omit to toggle
- * @chainable
- */
-OO.ui.MenuLayout.prototype.toggleMenu = function ( showMenu ) {
-       showMenu = showMenu === undefined ? !this.showMenu : !!showMenu;
-
-       if ( this.showMenu !== showMenu ) {
-               this.showMenu = showMenu;
-               this.$element
-                       .toggleClass( 'oo-ui-menuLayout-showMenu', this.showMenu )
-                       .toggleClass( 'oo-ui-menuLayout-hideMenu', !this.showMenu );
-       }
-
-       return this;
-};
-
-/**
- * Check if menu is visible
- *
- * @return {boolean} Menu is visible
- */
-OO.ui.MenuLayout.prototype.isMenuVisible = function () {
-       return this.showMenu;
-};
-
-/**
- * Set menu position.
- *
- * @param {string} position Position of menu, either `top`, `after`, `bottom` or `before`
- * @throws {Error} If position value is not supported
- * @chainable
- */
-OO.ui.MenuLayout.prototype.setMenuPosition = function ( position ) {
-       this.$element.removeClass( 'oo-ui-menuLayout-' + this.menuPosition );
-       this.menuPosition = position;
-       this.$element.addClass( 'oo-ui-menuLayout-' + position );
-
-       return this;
-};
-
-/**
- * Get menu position.
- *
- * @return {string} Menu position
- */
-OO.ui.MenuLayout.prototype.getMenuPosition = function () {
-       return this.menuPosition;
-};
-
-/**
- * BookletLayouts contain {@link OO.ui.PageLayout page layouts} as well as
- * an {@link OO.ui.OutlineSelectWidget outline} that allows users to easily navigate
- * through the pages and select which one to display. By default, only one page is
- * displayed at a time and the outline is hidden. When a user navigates to a new page,
- * the booklet layout automatically focuses on the first focusable element, unless the
- * default setting is changed. Optionally, booklets can be configured to show
- * {@link OO.ui.OutlineControlsWidget controls} for adding, moving, and removing items.
- *
- *     @example
- *     // Example of a BookletLayout that contains two PageLayouts.
- *
- *     function PageOneLayout( name, config ) {
- *         PageOneLayout.parent.call( this, name, config );
- *         this.$element.append( '<p>First page</p><p>(This booklet has an outline, displayed on the left)</p>' );
- *     }
- *     OO.inheritClass( PageOneLayout, OO.ui.PageLayout );
- *     PageOneLayout.prototype.setupOutlineItem = function () {
- *         this.outlineItem.setLabel( 'Page One' );
- *     };
- *
- *     function PageTwoLayout( name, config ) {
- *         PageTwoLayout.parent.call( this, name, config );
- *         this.$element.append( '<p>Second page</p>' );
- *     }
- *     OO.inheritClass( PageTwoLayout, OO.ui.PageLayout );
- *     PageTwoLayout.prototype.setupOutlineItem = function () {
- *         this.outlineItem.setLabel( 'Page Two' );
- *     };
- *
- *     var page1 = new PageOneLayout( 'one' ),
- *         page2 = new PageTwoLayout( 'two' );
- *
- *     var booklet = new OO.ui.BookletLayout( {
- *         outlined: true
- *     } );
- *
- *     booklet.addPages ( [ page1, page2 ] );
- *     $( 'body' ).append( booklet.$element );
- *
- * @class
- * @extends OO.ui.MenuLayout
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {boolean} [continuous=false] Show all pages, one after another
- * @cfg {boolean} [autoFocus=true] Focus on the first focusable element when a new page is displayed.
- * @cfg {boolean} [outlined=false] Show the outline. The outline is used to navigate through the pages of the booklet.
- * @cfg {boolean} [editable=false] Show controls for adding, removing and reordering pages
- */
-OO.ui.BookletLayout = function OoUiBookletLayout( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Parent constructor
-       OO.ui.BookletLayout.parent.call( this, config );
-
-       // Properties
-       this.currentPageName = null;
-       this.pages = {};
-       this.ignoreFocus = false;
-       this.stackLayout = new OO.ui.StackLayout( { continuous: !!config.continuous } );
-       this.$content.append( this.stackLayout.$element );
-       this.autoFocus = config.autoFocus === undefined || !!config.autoFocus;
-       this.outlineVisible = false;
-       this.outlined = !!config.outlined;
-       if ( this.outlined ) {
-               this.editable = !!config.editable;
-               this.outlineControlsWidget = null;
-               this.outlineSelectWidget = new OO.ui.OutlineSelectWidget();
-               this.outlinePanel = new OO.ui.PanelLayout( { scrollable: true } );
-               this.$menu.append( this.outlinePanel.$element );
-               this.outlineVisible = true;
-               if ( this.editable ) {
-                       this.outlineControlsWidget = new OO.ui.OutlineControlsWidget(
-                               this.outlineSelectWidget
-                       );
-               }
-       }
-       this.toggleMenu( this.outlined );
-
-       // Events
-       this.stackLayout.connect( this, { set: 'onStackLayoutSet' } );
-       if ( this.outlined ) {
-               this.outlineSelectWidget.connect( this, { select: 'onOutlineSelectWidgetSelect' } );
-               this.scrolling = false;
-               this.stackLayout.connect( this, { visibleItemChange: 'onStackLayoutVisibleItemChange' } );
-       }
-       if ( this.autoFocus ) {
-               // Event 'focus' does not bubble, but 'focusin' does
-               this.stackLayout.$element.on( 'focusin', this.onStackLayoutFocus.bind( this ) );
-       }
-
-       // Initialization
-       this.$element.addClass( 'oo-ui-bookletLayout' );
-       this.stackLayout.$element.addClass( 'oo-ui-bookletLayout-stackLayout' );
-       if ( this.outlined ) {
-               this.outlinePanel.$element
-                       .addClass( 'oo-ui-bookletLayout-outlinePanel' )
-                       .append( this.outlineSelectWidget.$element );
-               if ( this.editable ) {
-                       this.outlinePanel.$element
-                               .addClass( 'oo-ui-bookletLayout-outlinePanel-editable' )
-                               .append( this.outlineControlsWidget.$element );
-               }
-       }
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.BookletLayout, OO.ui.MenuLayout );
-
-/* Events */
-
-/**
- * A 'set' event is emitted when a page is {@link #setPage set} to be displayed by the booklet layout.
- * @event set
- * @param {OO.ui.PageLayout} page Current page
- */
-
-/**
- * An 'add' event is emitted when pages are {@link #addPages added} to the booklet layout.
- *
- * @event add
- * @param {OO.ui.PageLayout[]} page Added pages
- * @param {number} index Index pages were added at
- */
-
-/**
- * A 'remove' event is emitted when pages are {@link #clearPages cleared} or
- * {@link #removePages removed} from the booklet.
- *
- * @event remove
- * @param {OO.ui.PageLayout[]} pages Removed pages
- */
-
-/* Methods */
-
-/**
- * Handle stack layout focus.
- *
- * @private
- * @param {jQuery.Event} e Focusin event
- */
-OO.ui.BookletLayout.prototype.onStackLayoutFocus = function ( e ) {
-       var name, $target;
-
-       // Find the page that an element was focused within
-       $target = $( e.target ).closest( '.oo-ui-pageLayout' );
-       for ( name in this.pages ) {
-               // Check for page match, exclude current page to find only page changes
-               if ( this.pages[ name ].$element[ 0 ] === $target[ 0 ] && name !== this.currentPageName ) {
-                       this.setPage( name );
-                       break;
-               }
-       }
-};
-
-/**
- * Handle visibleItemChange events from the stackLayout
- *
- * The next visible page is set as the current page by selecting it
- * in the outline
- *
- * @param {OO.ui.PageLayout} page The next visible page in the layout
- */
-OO.ui.BookletLayout.prototype.onStackLayoutVisibleItemChange = function ( page ) {
-       // Set a flag to so that the resulting call to #onStackLayoutSet doesn't
-       // try and scroll the item into view again.
-       this.scrolling = true;
-       this.outlineSelectWidget.selectItemByData( page.getName() );
-       this.scrolling = false;
-};
-
-/**
- * Handle stack layout set events.
- *
- * @private
- * @param {OO.ui.PanelLayout|null} page The page panel that is now the current panel
- */
-OO.ui.BookletLayout.prototype.onStackLayoutSet = function ( page ) {
-       var layout = this;
-       if ( !this.scrolling && page ) {
-               page.scrollElementIntoView( { complete: function () {
-                       if ( layout.autoFocus ) {
-                               layout.focus();
-                       }
-               } } );
-       }
-};
-
-/**
- * Focus the first input in the current page.
- *
- * If no page is selected, the first selectable page will be selected.
- * If the focus is already in an element on the current page, nothing will happen.
- * @param {number} [itemIndex] A specific item to focus on
- */
-OO.ui.BookletLayout.prototype.focus = function ( itemIndex ) {
-       var page,
-               items = this.stackLayout.getItems();
-
-       if ( itemIndex !== undefined && items[ itemIndex ] ) {
-               page = items[ itemIndex ];
-       } else {
-               page = this.stackLayout.getCurrentItem();
-       }
-
-       if ( !page && this.outlined ) {
-               this.selectFirstSelectablePage();
-               page = this.stackLayout.getCurrentItem();
-       }
-       if ( !page ) {
-               return;
-       }
-       // Only change the focus if is not already in the current page
-       if ( !OO.ui.contains( page.$element[ 0 ], this.getElementDocument().activeElement, true ) ) {
-               page.focus();
-       }
-};
-
-/**
- * Find the first focusable input in the booklet layout and focus
- * on it.
- */
-OO.ui.BookletLayout.prototype.focusFirstFocusable = function () {
-       OO.ui.findFocusable( this.stackLayout.$element ).focus();
-};
-
-/**
- * Handle outline widget select events.
- *
- * @private
- * @param {OO.ui.OptionWidget|null} item Selected item
- */
-OO.ui.BookletLayout.prototype.onOutlineSelectWidgetSelect = function ( item ) {
-       if ( item ) {
-               this.setPage( item.getData() );
-       }
-};
-
-/**
- * Check if booklet has an outline.
- *
- * @return {boolean} Booklet has an outline
- */
-OO.ui.BookletLayout.prototype.isOutlined = function () {
-       return this.outlined;
-};
-
-/**
- * Check if booklet has editing controls.
- *
- * @return {boolean} Booklet is editable
- */
-OO.ui.BookletLayout.prototype.isEditable = function () {
-       return this.editable;
-};
-
-/**
- * Check if booklet has a visible outline.
- *
- * @return {boolean} Outline is visible
- */
-OO.ui.BookletLayout.prototype.isOutlineVisible = function () {
-       return this.outlined && this.outlineVisible;
-};
-
-/**
- * Hide or show the outline.
- *
- * @param {boolean} [show] Show outline, omit to invert current state
- * @chainable
- */
-OO.ui.BookletLayout.prototype.toggleOutline = function ( show ) {
-       if ( this.outlined ) {
-               show = show === undefined ? !this.outlineVisible : !!show;
-               this.outlineVisible = show;
-               this.toggleMenu( show );
-       }
-
-       return this;
-};
-
-/**
- * Get the page closest to the specified page.
- *
- * @param {OO.ui.PageLayout} page Page to use as a reference point
- * @return {OO.ui.PageLayout|null} Page closest to the specified page
- */
-OO.ui.BookletLayout.prototype.getClosestPage = function ( page ) {
-       var next, prev, level,
-               pages = this.stackLayout.getItems(),
-               index = pages.indexOf( page );
-
-       if ( index !== -1 ) {
-               next = pages[ index + 1 ];
-               prev = pages[ index - 1 ];
-               // Prefer adjacent pages at the same level
-               if ( this.outlined ) {
-                       level = this.outlineSelectWidget.getItemFromData( page.getName() ).getLevel();
-                       if (
-                               prev &&
-                               level === this.outlineSelectWidget.getItemFromData( prev.getName() ).getLevel()
-                       ) {
-                               return prev;
-                       }
-                       if (
-                               next &&
-                               level === this.outlineSelectWidget.getItemFromData( next.getName() ).getLevel()
-                       ) {
-                               return next;
-                       }
-               }
-       }
-       return prev || next || null;
-};
-
-/**
- * Get the outline widget.
- *
- * If the booklet is not outlined, the method will return `null`.
- *
- * @return {OO.ui.OutlineSelectWidget|null} Outline widget, or null if the booklet is not outlined
- */
-OO.ui.BookletLayout.prototype.getOutline = function () {
-       return this.outlineSelectWidget;
-};
-
-/**
- * Get the outline controls widget.
- *
- * If the outline is not editable, the method will return `null`.
- *
- * @return {OO.ui.OutlineControlsWidget|null} The outline controls widget.
- */
-OO.ui.BookletLayout.prototype.getOutlineControls = function () {
-       return this.outlineControlsWidget;
-};
-
-/**
- * Get a page by its symbolic name.
- *
- * @param {string} name Symbolic name of page
- * @return {OO.ui.PageLayout|undefined} Page, if found
- */
-OO.ui.BookletLayout.prototype.getPage = function ( name ) {
-       return this.pages[ name ];
-};
-
-/**
- * Get the current page.
- *
- * @return {OO.ui.PageLayout|undefined} Current page, if found
- */
-OO.ui.BookletLayout.prototype.getCurrentPage = function () {
-       var name = this.getCurrentPageName();
-       return name ? this.getPage( name ) : undefined;
-};
-
-/**
- * Get the symbolic name of the current page.
- *
- * @return {string|null} Symbolic name of the current page
- */
-OO.ui.BookletLayout.prototype.getCurrentPageName = function () {
-       return this.currentPageName;
-};
-
-/**
- * Add pages to the booklet layout
- *
- * When pages are added with the same names as existing pages, the existing pages will be
- * automatically removed before the new pages are added.
- *
- * @param {OO.ui.PageLayout[]} pages Pages to add
- * @param {number} index Index of the insertion point
- * @fires add
- * @chainable
- */
-OO.ui.BookletLayout.prototype.addPages = function ( pages, index ) {
-       var i, len, name, page, item, currentIndex,
-               stackLayoutPages = this.stackLayout.getItems(),
-               remove = [],
-               items = [];
-
-       // Remove pages with same names
-       for ( i = 0, len = pages.length; i < len; i++ ) {
-               page = pages[ i ];
-               name = page.getName();
-
-               if ( Object.prototype.hasOwnProperty.call( this.pages, name ) ) {
-                       // Correct the insertion index
-                       currentIndex = stackLayoutPages.indexOf( this.pages[ name ] );
-                       if ( currentIndex !== -1 && currentIndex + 1 < index ) {
-                               index--;
-                       }
-                       remove.push( this.pages[ name ] );
-               }
-       }
-       if ( remove.length ) {
-               this.removePages( remove );
-       }
-
-       // Add new pages
-       for ( i = 0, len = pages.length; i < len; i++ ) {
-               page = pages[ i ];
-               name = page.getName();
-               this.pages[ page.getName() ] = page;
-               if ( this.outlined ) {
-                       item = new OO.ui.OutlineOptionWidget( { data: name } );
-                       page.setOutlineItem( item );
-                       items.push( item );
-               }
-       }
-
-       if ( this.outlined && items.length ) {
-               this.outlineSelectWidget.addItems( items, index );
-               this.selectFirstSelectablePage();
-       }
-       this.stackLayout.addItems( pages, index );
-       this.emit( 'add', pages, index );
-
-       return this;
-};
-
-/**
- * Remove the specified pages from the booklet layout.
- *
- * To remove all pages from the booklet, you may wish to use the #clearPages method instead.
- *
- * @param {OO.ui.PageLayout[]} pages An array of pages to remove
- * @fires remove
- * @chainable
- */
-OO.ui.BookletLayout.prototype.removePages = function ( pages ) {
-       var i, len, name, page,
-               items = [];
-
-       for ( i = 0, len = pages.length; i < len; i++ ) {
-               page = pages[ i ];
-               name = page.getName();
-               delete this.pages[ name ];
-               if ( this.outlined ) {
-                       items.push( this.outlineSelectWidget.getItemFromData( name ) );
-                       page.setOutlineItem( null );
-               }
-       }
-       if ( this.outlined && items.length ) {
-               this.outlineSelectWidget.removeItems( items );
-               this.selectFirstSelectablePage();
-       }
-       this.stackLayout.removeItems( pages );
-       this.emit( 'remove', pages );
-
-       return this;
-};
-
-/**
- * Clear all pages from the booklet layout.
- *
- * To remove only a subset of pages from the booklet, use the #removePages method.
- *
- * @fires remove
- * @chainable
- */
-OO.ui.BookletLayout.prototype.clearPages = function () {
-       var i, len,
-               pages = this.stackLayout.getItems();
-
-       this.pages = {};
-       this.currentPageName = null;
-       if ( this.outlined ) {
-               this.outlineSelectWidget.clearItems();
-               for ( i = 0, len = pages.length; i < len; i++ ) {
-                       pages[ i ].setOutlineItem( null );
-               }
-       }
-       this.stackLayout.clearItems();
-
-       this.emit( 'remove', pages );
-
-       return this;
-};
-
-/**
- * Set the current page by symbolic name.
- *
- * @fires set
- * @param {string} name Symbolic name of page
- */
-OO.ui.BookletLayout.prototype.setPage = function ( name ) {
-       var selectedItem,
-               $focused,
-               page = this.pages[ name ],
-               previousPage = this.currentPageName && this.pages[ this.currentPageName ];
-
-       if ( name !== this.currentPageName ) {
-               if ( this.outlined ) {
-                       selectedItem = this.outlineSelectWidget.getSelectedItem();
-                       if ( selectedItem && selectedItem.getData() !== name ) {
-                               this.outlineSelectWidget.selectItemByData( name );
-                       }
-               }
-               if ( page ) {
-                       if ( previousPage ) {
-                               previousPage.setActive( false );
-                               // Blur anything focused if the next page doesn't have anything focusable.
-                               // This is not needed if the next page has something focusable (because once it is focused
-                               // this blur happens automatically). If the layout is non-continuous, this check is
-                               // meaningless because the next page is not visible yet and thus can't hold focus.
-                               if (
-                                       this.autoFocus &&
-                                       this.stackLayout.continuous &&
-                                       OO.ui.findFocusable( page.$element ).length !== 0
-                               ) {
-                                       $focused = previousPage.$element.find( ':focus' );
-                                       if ( $focused.length ) {
-                                               $focused[ 0 ].blur();
-                                       }
-                               }
-                       }
-                       this.currentPageName = name;
-                       page.setActive( true );
-                       this.stackLayout.setItem( page );
-                       if ( !this.stackLayout.continuous && previousPage ) {
-                               // This should not be necessary, since any inputs on the previous page should have been
-                               // blurred when it was hidden, but browsers are not very consistent about this.
-                               $focused = previousPage.$element.find( ':focus' );
-                               if ( $focused.length ) {
-                                       $focused[ 0 ].blur();
-                               }
-                       }
-                       this.emit( 'set', page );
-               }
-       }
-};
-
-/**
- * Select the first selectable page.
- *
- * @chainable
- */
-OO.ui.BookletLayout.prototype.selectFirstSelectablePage = function () {
-       if ( !this.outlineSelectWidget.getSelectedItem() ) {
-               this.outlineSelectWidget.selectItem( this.outlineSelectWidget.getFirstSelectableItem() );
-       }
-
-       return this;
-};
-
-/**
- * IndexLayouts contain {@link OO.ui.CardLayout card layouts} as well as
- * {@link OO.ui.TabSelectWidget tabs} that allow users to easily navigate through the cards and
- * select which one to display. By default, only one card is displayed at a time. When a user
- * navigates to a new card, the index layout automatically focuses on the first focusable element,
- * unless the default setting is changed.
- *
- * TODO: This class is similar to BookletLayout, we may want to refactor to reduce duplication
- *
- *     @example
- *     // Example of a IndexLayout that contains two CardLayouts.
- *
- *     function CardOneLayout( name, config ) {
- *         CardOneLayout.parent.call( this, name, config );
- *         this.$element.append( '<p>First card</p>' );
- *     }
- *     OO.inheritClass( CardOneLayout, OO.ui.CardLayout );
- *     CardOneLayout.prototype.setupTabItem = function () {
- *         this.tabItem.setLabel( 'Card one' );
- *     };
- *
- *     var card1 = new CardOneLayout( 'one' ),
- *         card2 = new CardLayout( 'two', { label: 'Card two' } );
- *
- *     card2.$element.append( '<p>Second card</p>' );
- *
- *     var index = new OO.ui.IndexLayout();
- *
- *     index.addCards ( [ card1, card2 ] );
- *     $( 'body' ).append( index.$element );
- *
- * @class
- * @extends OO.ui.MenuLayout
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {boolean} [continuous=false] Show all cards, one after another
- * @cfg {boolean} [expanded=true] Expand the content panel to fill the entire parent element.
- * @cfg {boolean} [autoFocus=true] Focus on the first focusable element when a new card is displayed.
- */
-OO.ui.IndexLayout = function OoUiIndexLayout( config ) {
-       // Configuration initialization
-       config = $.extend( {}, config, { menuPosition: 'top' } );
-
-       // Parent constructor
-       OO.ui.IndexLayout.parent.call( this, config );
-
-       // Properties
-       this.currentCardName = null;
-       this.cards = {};
-       this.ignoreFocus = false;
-       this.stackLayout = new OO.ui.StackLayout( {
-               continuous: !!config.continuous,
-               expanded: config.expanded
-       } );
-       this.$content.append( this.stackLayout.$element );
-       this.autoFocus = config.autoFocus === undefined || !!config.autoFocus;
-
-       this.tabSelectWidget = new OO.ui.TabSelectWidget();
-       this.tabPanel = new OO.ui.PanelLayout();
-       this.$menu.append( this.tabPanel.$element );
-
-       this.toggleMenu( true );
-
-       // Events
-       this.stackLayout.connect( this, { set: 'onStackLayoutSet' } );
-       this.tabSelectWidget.connect( this, { select: 'onTabSelectWidgetSelect' } );
-       if ( this.autoFocus ) {
-               // Event 'focus' does not bubble, but 'focusin' does
-               this.stackLayout.$element.on( 'focusin', this.onStackLayoutFocus.bind( this ) );
-       }
-
-       // Initialization
-       this.$element.addClass( 'oo-ui-indexLayout' );
-       this.stackLayout.$element.addClass( 'oo-ui-indexLayout-stackLayout' );
-       this.tabPanel.$element
-               .addClass( 'oo-ui-indexLayout-tabPanel' )
-               .append( this.tabSelectWidget.$element );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.IndexLayout, OO.ui.MenuLayout );
-
-/* Events */
-
-/**
- * A 'set' event is emitted when a card is {@link #setCard set} to be displayed by the index layout.
- * @event set
- * @param {OO.ui.CardLayout} card Current card
- */
-
-/**
- * An 'add' event is emitted when cards are {@link #addCards added} to the index layout.
- *
- * @event add
- * @param {OO.ui.CardLayout[]} card Added cards
- * @param {number} index Index cards were added at
- */
-
-/**
- * A 'remove' event is emitted when cards are {@link #clearCards cleared} or
- * {@link #removeCards removed} from the index.
- *
- * @event remove
- * @param {OO.ui.CardLayout[]} cards Removed cards
- */
-
-/* Methods */
-
-/**
- * Handle stack layout focus.
- *
- * @private
- * @param {jQuery.Event} e Focusin event
- */
-OO.ui.IndexLayout.prototype.onStackLayoutFocus = function ( e ) {
-       var name, $target;
-
-       // Find the card that an element was focused within
-       $target = $( e.target ).closest( '.oo-ui-cardLayout' );
-       for ( name in this.cards ) {
-               // Check for card match, exclude current card to find only card changes
-               if ( this.cards[ name ].$element[ 0 ] === $target[ 0 ] && name !== this.currentCardName ) {
-                       this.setCard( name );
-                       break;
-               }
-       }
-};
-
-/**
- * Handle stack layout set events.
- *
- * @private
- * @param {OO.ui.PanelLayout|null} card The card panel that is now the current panel
- */
-OO.ui.IndexLayout.prototype.onStackLayoutSet = function ( card ) {
-       var layout = this;
-       if ( card ) {
-               card.scrollElementIntoView( { complete: function () {
-                       if ( layout.autoFocus ) {
-                               layout.focus();
-                       }
-               } } );
-       }
-};
-
-/**
- * Focus the first input in the current card.
- *
- * If no card is selected, the first selectable card will be selected.
- * If the focus is already in an element on the current card, nothing will happen.
- * @param {number} [itemIndex] A specific item to focus on
- */
-OO.ui.IndexLayout.prototype.focus = function ( itemIndex ) {
-       var card,
-               items = this.stackLayout.getItems();
-
-       if ( itemIndex !== undefined && items[ itemIndex ] ) {
-               card = items[ itemIndex ];
-       } else {
-               card = this.stackLayout.getCurrentItem();
-       }
-
-       if ( !card ) {
-               this.selectFirstSelectableCard();
-               card = this.stackLayout.getCurrentItem();
-       }
-       if ( !card ) {
-               return;
-       }
-       // Only change the focus if is not already in the current page
-       if ( !OO.ui.contains( card.$element[ 0 ], this.getElementDocument().activeElement, true ) ) {
-               card.focus();
-       }
-};
-
-/**
- * Find the first focusable input in the index layout and focus
- * on it.
- */
-OO.ui.IndexLayout.prototype.focusFirstFocusable = function () {
-       OO.ui.findFocusable( this.stackLayout.$element ).focus();
-};
-
-/**
- * Handle tab widget select events.
- *
- * @private
- * @param {OO.ui.OptionWidget|null} item Selected item
- */
-OO.ui.IndexLayout.prototype.onTabSelectWidgetSelect = function ( item ) {
-       if ( item ) {
-               this.setCard( item.getData() );
-       }
-};
-
-/**
- * Get the card closest to the specified card.
- *
- * @param {OO.ui.CardLayout} card Card to use as a reference point
- * @return {OO.ui.CardLayout|null} Card closest to the specified card
- */
-OO.ui.IndexLayout.prototype.getClosestCard = function ( card ) {
-       var next, prev, level,
-               cards = this.stackLayout.getItems(),
-               index = cards.indexOf( card );
-
-       if ( index !== -1 ) {
-               next = cards[ index + 1 ];
-               prev = cards[ index - 1 ];
-               // Prefer adjacent cards at the same level
-               level = this.tabSelectWidget.getItemFromData( card.getName() ).getLevel();
-               if (
-                       prev &&
-                       level === this.tabSelectWidget.getItemFromData( prev.getName() ).getLevel()
-               ) {
-                       return prev;
-               }
-               if (
-                       next &&
-                       level === this.tabSelectWidget.getItemFromData( next.getName() ).getLevel()
-               ) {
-                       return next;
-               }
-       }
-       return prev || next || null;
-};
-
-/**
- * Get the tabs widget.
- *
- * @return {OO.ui.TabSelectWidget} Tabs widget
- */
-OO.ui.IndexLayout.prototype.getTabs = function () {
-       return this.tabSelectWidget;
-};
-
-/**
- * Get a card by its symbolic name.
- *
- * @param {string} name Symbolic name of card
- * @return {OO.ui.CardLayout|undefined} Card, if found
- */
-OO.ui.IndexLayout.prototype.getCard = function ( name ) {
-       return this.cards[ name ];
-};
-
-/**
- * Get the current card.
- *
- * @return {OO.ui.CardLayout|undefined} Current card, if found
- */
-OO.ui.IndexLayout.prototype.getCurrentCard = function () {
-       var name = this.getCurrentCardName();
-       return name ? this.getCard( name ) : undefined;
-};
-
-/**
- * Get the symbolic name of the current card.
- *
- * @return {string|null} Symbolic name of the current card
- */
-OO.ui.IndexLayout.prototype.getCurrentCardName = function () {
-       return this.currentCardName;
-};
-
-/**
- * Add cards to the index layout
- *
- * When cards are added with the same names as existing cards, the existing cards will be
- * automatically removed before the new cards are added.
- *
- * @param {OO.ui.CardLayout[]} cards Cards to add
- * @param {number} index Index of the insertion point
- * @fires add
- * @chainable
- */
-OO.ui.IndexLayout.prototype.addCards = function ( cards, index ) {
-       var i, len, name, card, item, currentIndex,
-               stackLayoutCards = this.stackLayout.getItems(),
-               remove = [],
-               items = [];
-
-       // Remove cards with same names
-       for ( i = 0, len = cards.length; i < len; i++ ) {
-               card = cards[ i ];
-               name = card.getName();
-
-               if ( Object.prototype.hasOwnProperty.call( this.cards, name ) ) {
-                       // Correct the insertion index
-                       currentIndex = stackLayoutCards.indexOf( this.cards[ name ] );
-                       if ( currentIndex !== -1 && currentIndex + 1 < index ) {
-                               index--;
-                       }
-                       remove.push( this.cards[ name ] );
-               }
-       }
-       if ( remove.length ) {
-               this.removeCards( remove );
-       }
-
-       // Add new cards
-       for ( i = 0, len = cards.length; i < len; i++ ) {
-               card = cards[ i ];
-               name = card.getName();
-               this.cards[ card.getName() ] = card;
-               item = new OO.ui.TabOptionWidget( { data: name } );
-               card.setTabItem( item );
-               items.push( item );
-       }
-
-       if ( items.length ) {
-               this.tabSelectWidget.addItems( items, index );
-               this.selectFirstSelectableCard();
-       }
-       this.stackLayout.addItems( cards, index );
-       this.emit( 'add', cards, index );
-
-       return this;
-};
-
-/**
- * Remove the specified cards from the index layout.
- *
- * To remove all cards from the index, you may wish to use the #clearCards method instead.
- *
- * @param {OO.ui.CardLayout[]} cards An array of cards to remove
- * @fires remove
- * @chainable
- */
-OO.ui.IndexLayout.prototype.removeCards = function ( cards ) {
-       var i, len, name, card,
-               items = [];
-
-       for ( i = 0, len = cards.length; i < len; i++ ) {
-               card = cards[ i ];
-               name = card.getName();
-               delete this.cards[ name ];
-               items.push( this.tabSelectWidget.getItemFromData( name ) );
-               card.setTabItem( null );
-       }
-       if ( items.length ) {
-               this.tabSelectWidget.removeItems( items );
-               this.selectFirstSelectableCard();
-       }
-       this.stackLayout.removeItems( cards );
-       this.emit( 'remove', cards );
-
-       return this;
-};
-
-/**
- * Clear all cards from the index layout.
- *
- * To remove only a subset of cards from the index, use the #removeCards method.
- *
- * @fires remove
- * @chainable
- */
-OO.ui.IndexLayout.prototype.clearCards = function () {
-       var i, len,
-               cards = this.stackLayout.getItems();
-
-       this.cards = {};
-       this.currentCardName = null;
-       this.tabSelectWidget.clearItems();
-       for ( i = 0, len = cards.length; i < len; i++ ) {
-               cards[ i ].setTabItem( null );
-       }
-       this.stackLayout.clearItems();
-
-       this.emit( 'remove', cards );
-
-       return this;
-};
-
-/**
- * Set the current card by symbolic name.
- *
- * @fires set
- * @param {string} name Symbolic name of card
- */
-OO.ui.IndexLayout.prototype.setCard = function ( name ) {
-       var selectedItem,
-               $focused,
-               card = this.cards[ name ],
-               previousCard = this.currentCardName && this.cards[ this.currentCardName ];
-
-       if ( name !== this.currentCardName ) {
-               selectedItem = this.tabSelectWidget.getSelectedItem();
-               if ( selectedItem && selectedItem.getData() !== name ) {
-                       this.tabSelectWidget.selectItemByData( name );
-               }
-               if ( card ) {
-                       if ( previousCard ) {
-                               previousCard.setActive( false );
-                               // Blur anything focused if the next card doesn't have anything focusable.
-                               // This is not needed if the next card has something focusable (because once it is focused
-                               // this blur happens automatically). If the layout is non-continuous, this check is
-                               // meaningless because the next card is not visible yet and thus can't hold focus.
-                               if (
-                                       this.autoFocus &&
-                                       this.stackLayout.continuous &&
-                                       OO.ui.findFocusable( card.$element ).length !== 0
-                               ) {
-                                       $focused = previousCard.$element.find( ':focus' );
-                                       if ( $focused.length ) {
-                                               $focused[ 0 ].blur();
-                                       }
-                               }
-                       }
-                       this.currentCardName = name;
-                       card.setActive( true );
-                       this.stackLayout.setItem( card );
-                       if ( !this.stackLayout.continuous && previousCard ) {
-                               // This should not be necessary, since any inputs on the previous card should have been
-                               // blurred when it was hidden, but browsers are not very consistent about this.
-                               $focused = previousCard.$element.find( ':focus' );
-                               if ( $focused.length ) {
-                                       $focused[ 0 ].blur();
-                               }
-                       }
-                       this.emit( 'set', card );
-               }
-       }
-};
-
-/**
- * Select the first selectable card.
- *
- * @chainable
- */
-OO.ui.IndexLayout.prototype.selectFirstSelectableCard = function () {
-       if ( !this.tabSelectWidget.getSelectedItem() ) {
-               this.tabSelectWidget.selectItem( this.tabSelectWidget.getFirstSelectableItem() );
-       }
-
-       return this;
-};
-
-/**
- * PanelLayouts expand to cover the entire area of their parent. They can be configured with scrolling, padding,
- * and a frame, and are often used together with {@link OO.ui.StackLayout StackLayouts}.
- *
- *     @example
- *     // Example of a panel layout
- *     var panel = new OO.ui.PanelLayout( {
- *         expanded: false,
- *         framed: true,
- *         padded: true,
- *         $content: $( '<p>A panel layout with padding and a frame.</p>' )
- *     } );
- *     $( 'body' ).append( panel.$element );
- *
- * @class
- * @extends OO.ui.Layout
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {boolean} [scrollable=false] Allow vertical scrolling
- * @cfg {boolean} [padded=false] Add padding between the content and the edges of the panel.
- * @cfg {boolean} [expanded=true] Expand the panel to fill the entire parent element.
- * @cfg {boolean} [framed=false] Render the panel with a frame to visually separate it from outside content.
- */
-OO.ui.PanelLayout = function OoUiPanelLayout( config ) {
-       // Configuration initialization
-       config = $.extend( {
-               scrollable: false,
-               padded: false,
-               expanded: true,
-               framed: false
-       }, config );
-
-       // Parent constructor
-       OO.ui.PanelLayout.parent.call( this, config );
-
-       // Initialization
-       this.$element.addClass( 'oo-ui-panelLayout' );
-       if ( config.scrollable ) {
-               this.$element.addClass( 'oo-ui-panelLayout-scrollable' );
-       }
-       if ( config.padded ) {
-               this.$element.addClass( 'oo-ui-panelLayout-padded' );
-       }
-       if ( config.expanded ) {
-               this.$element.addClass( 'oo-ui-panelLayout-expanded' );
-       }
-       if ( config.framed ) {
-               this.$element.addClass( 'oo-ui-panelLayout-framed' );
-       }
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.PanelLayout, OO.ui.Layout );
-
-/* Methods */
-
-/**
- * Focus the panel layout
- *
- * The default implementation just focuses the first focusable element in the panel
- */
-OO.ui.PanelLayout.prototype.focus = function () {
-       OO.ui.findFocusable( this.$element ).focus();
-};
-
-/**
- * CardLayouts are used within {@link OO.ui.IndexLayout index layouts} to create cards that users can select and display
- * from the index's optional {@link OO.ui.TabSelectWidget tab} navigation. Cards are usually not instantiated directly,
- * rather extended to include the required content and functionality.
- *
- * Each card must have a unique symbolic name, which is passed to the constructor. In addition, the card's tab
- * item is customized (with a label) using the #setupTabItem method. See
- * {@link OO.ui.IndexLayout IndexLayout} for an example.
- *
- * @class
- * @extends OO.ui.PanelLayout
- *
- * @constructor
- * @param {string} name Unique symbolic name of card
- * @param {Object} [config] Configuration options
- * @cfg {jQuery|string|Function|OO.ui.HtmlSnippet} [label] Label for card's tab
- */
-OO.ui.CardLayout = function OoUiCardLayout( name, config ) {
-       // Allow passing positional parameters inside the config object
-       if ( OO.isPlainObject( name ) && config === undefined ) {
-               config = name;
-               name = config.name;
-       }
-
-       // Configuration initialization
-       config = $.extend( { scrollable: true }, config );
-
-       // Parent constructor
-       OO.ui.CardLayout.parent.call( this, config );
-
-       // Properties
-       this.name = name;
-       this.label = config.label;
-       this.tabItem = null;
-       this.active = false;
-
-       // Initialization
-       this.$element.addClass( 'oo-ui-cardLayout' );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.CardLayout, OO.ui.PanelLayout );
-
-/* Events */
-
-/**
- * An 'active' event is emitted when the card becomes active. Cards become active when they are
- * shown in a index layout that is configured to display only one card at a time.
- *
- * @event active
- * @param {boolean} active Card is active
- */
-
-/* Methods */
-
-/**
- * Get the symbolic name of the card.
- *
- * @return {string} Symbolic name of card
- */
-OO.ui.CardLayout.prototype.getName = function () {
-       return this.name;
-};
-
-/**
- * Check if card is active.
- *
- * Cards become active when they are shown in a {@link OO.ui.IndexLayout index layout} that is configured to display
- * only one card at a time. Additional CSS is applied to the card's tab item to reflect the active state.
- *
- * @return {boolean} Card is active
- */
-OO.ui.CardLayout.prototype.isActive = function () {
-       return this.active;
-};
-
-/**
- * Get tab item.
- *
- * The tab item allows users to access the card from the index's tab
- * navigation. The tab item itself can be customized (with a label, level, etc.) using the #setupTabItem method.
- *
- * @return {OO.ui.TabOptionWidget|null} Tab option widget
- */
-OO.ui.CardLayout.prototype.getTabItem = function () {
-       return this.tabItem;
-};
-
-/**
- * Set or unset the tab item.
- *
- * Specify a {@link OO.ui.TabOptionWidget tab option} to set it,
- * or `null` to clear the tab item. To customize the tab item itself (e.g., to set a label or tab
- * level), use #setupTabItem instead of this method.
- *
- * @param {OO.ui.TabOptionWidget|null} tabItem Tab option widget, null to clear
- * @chainable
- */
-OO.ui.CardLayout.prototype.setTabItem = function ( tabItem ) {
-       this.tabItem = tabItem || null;
-       if ( tabItem ) {
-               this.setupTabItem();
-       }
-       return this;
-};
-
-/**
- * Set up the tab item.
- *
- * Use this method to customize the tab item (e.g., to add a label or tab level). To set or unset
- * the tab item itself (with a {@link OO.ui.TabOptionWidget tab option} or `null`), use
- * the #setTabItem method instead.
- *
- * @param {OO.ui.TabOptionWidget} tabItem Tab option widget to set up
- * @chainable
- */
-OO.ui.CardLayout.prototype.setupTabItem = function () {
-       if ( this.label ) {
-               this.tabItem.setLabel( this.label );
-       }
-       return this;
-};
-
-/**
- * Set the card to its 'active' state.
- *
- * Cards become active when they are shown in a index layout that is configured to display only one card at a time. Additional
- * CSS is applied to the tab item to reflect the card's active state. Outside of the index
- * context, setting the active state on a card does nothing.
- *
- * @param {boolean} value Card is active
- * @fires active
- */
-OO.ui.CardLayout.prototype.setActive = function ( active ) {
-       active = !!active;
-
-       if ( active !== this.active ) {
-               this.active = active;
-               this.$element.toggleClass( 'oo-ui-cardLayout-active', this.active );
-               this.emit( 'active', this.active );
-       }
-};
-
-/**
- * PageLayouts are used within {@link OO.ui.BookletLayout booklet layouts} to create pages that users can select and display
- * from the booklet's optional {@link OO.ui.OutlineSelectWidget outline} navigation. Pages are usually not instantiated directly,
- * rather extended to include the required content and functionality.
- *
- * Each page must have a unique symbolic name, which is passed to the constructor. In addition, the page's outline
- * item is customized (with a label, outline level, etc.) using the #setupOutlineItem method. See
- * {@link OO.ui.BookletLayout BookletLayout} for an example.
- *
- * @class
- * @extends OO.ui.PanelLayout
- *
- * @constructor
- * @param {string} name Unique symbolic name of page
- * @param {Object} [config] Configuration options
- */
-OO.ui.PageLayout = function OoUiPageLayout( name, config ) {
-       // Allow passing positional parameters inside the config object
-       if ( OO.isPlainObject( name ) && config === undefined ) {
-               config = name;
-               name = config.name;
-       }
-
-       // Configuration initialization
-       config = $.extend( { scrollable: true }, config );
-
-       // Parent constructor
-       OO.ui.PageLayout.parent.call( this, config );
-
-       // Properties
-       this.name = name;
-       this.outlineItem = null;
-       this.active = false;
-
-       // Initialization
-       this.$element.addClass( 'oo-ui-pageLayout' );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.PageLayout, OO.ui.PanelLayout );
-
-/* Events */
-
-/**
- * An 'active' event is emitted when the page becomes active. Pages become active when they are
- * shown in a booklet layout that is configured to display only one page at a time.
- *
- * @event active
- * @param {boolean} active Page is active
- */
-
-/* Methods */
-
-/**
- * Get the symbolic name of the page.
- *
- * @return {string} Symbolic name of page
- */
-OO.ui.PageLayout.prototype.getName = function () {
-       return this.name;
-};
-
-/**
- * Check if page is active.
- *
- * Pages become active when they are shown in a {@link OO.ui.BookletLayout booklet layout} that is configured to display
- * only one page at a time. Additional CSS is applied to the page's outline item to reflect the active state.
- *
- * @return {boolean} Page is active
- */
-OO.ui.PageLayout.prototype.isActive = function () {
-       return this.active;
-};
-
-/**
- * Get outline item.
- *
- * The outline item allows users to access the page from the booklet's outline
- * navigation. The outline item itself can be customized (with a label, level, etc.) using the #setupOutlineItem method.
- *
- * @return {OO.ui.OutlineOptionWidget|null} Outline option widget
- */
-OO.ui.PageLayout.prototype.getOutlineItem = function () {
-       return this.outlineItem;
-};
-
-/**
- * Set or unset the outline item.
- *
- * Specify an {@link OO.ui.OutlineOptionWidget outline option} to set it,
- * or `null` to clear the outline item. To customize the outline item itself (e.g., to set a label or outline
- * level), use #setupOutlineItem instead of this method.
- *
- * @param {OO.ui.OutlineOptionWidget|null} outlineItem Outline option widget, null to clear
- * @chainable
- */
-OO.ui.PageLayout.prototype.setOutlineItem = function ( outlineItem ) {
-       this.outlineItem = outlineItem || null;
-       if ( outlineItem ) {
-               this.setupOutlineItem();
-       }
-       return this;
-};
-
-/**
- * Set up the outline item.
- *
- * Use this method to customize the outline item (e.g., to add a label or outline level). To set or unset
- * the outline item itself (with an {@link OO.ui.OutlineOptionWidget outline option} or `null`), use
- * the #setOutlineItem method instead.
- *
- * @param {OO.ui.OutlineOptionWidget} outlineItem Outline option widget to set up
- * @chainable
- */
-OO.ui.PageLayout.prototype.setupOutlineItem = function () {
-       return this;
-};
-
-/**
- * Set the page to its 'active' state.
- *
- * Pages become active when they are shown in a booklet layout that is configured to display only one page at a time. Additional
- * CSS is applied to the outline item to reflect the page's active state. Outside of the booklet
- * context, setting the active state on a page does nothing.
- *
- * @param {boolean} value Page is active
- * @fires active
- */
-OO.ui.PageLayout.prototype.setActive = function ( active ) {
-       active = !!active;
-
-       if ( active !== this.active ) {
-               this.active = active;
-               this.$element.toggleClass( 'oo-ui-pageLayout-active', active );
-               this.emit( 'active', this.active );
-       }
-};
-
-/**
- * StackLayouts contain a series of {@link OO.ui.PanelLayout panel layouts}. By default, only one panel is displayed
- * at a time, though the stack layout can also be configured to show all contained panels, one after another,
- * by setting the #continuous option to 'true'.
- *
- *     @example
- *     // A stack layout with two panels, configured to be displayed continously
- *     var myStack = new OO.ui.StackLayout( {
- *         items: [
- *             new OO.ui.PanelLayout( {
- *                 $content: $( '<p>Panel One</p>' ),
- *                 padded: true,
- *                 framed: true
- *             } ),
- *             new OO.ui.PanelLayout( {
- *                 $content: $( '<p>Panel Two</p>' ),
- *                 padded: true,
- *                 framed: true
- *             } )
- *         ],
- *         continuous: true
- *     } );
- *     $( 'body' ).append( myStack.$element );
- *
- * @class
- * @extends OO.ui.PanelLayout
- * @mixins OO.ui.mixin.GroupElement
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {boolean} [continuous=false] Show all panels, one after another. By default, only one panel is displayed at a time.
- * @cfg {OO.ui.Layout[]} [items] Panel layouts to add to the stack layout.
- */
-OO.ui.StackLayout = function OoUiStackLayout( config ) {
-       // Configuration initialization
-       config = $.extend( { scrollable: true }, config );
-
-       // Parent constructor
-       OO.ui.StackLayout.parent.call( this, config );
-
-       // Mixin constructors
-       OO.ui.mixin.GroupElement.call( this, $.extend( {}, config, { $group: this.$element } ) );
-
-       // Properties
-       this.currentItem = null;
-       this.continuous = !!config.continuous;
-
-       // Initialization
-       this.$element.addClass( 'oo-ui-stackLayout' );
-       if ( this.continuous ) {
-               this.$element.addClass( 'oo-ui-stackLayout-continuous' );
-               this.$element.on( 'scroll', OO.ui.debounce( this.onScroll.bind( this ), 250 ) );
-       }
-       if ( Array.isArray( config.items ) ) {
-               this.addItems( config.items );
-       }
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.StackLayout, OO.ui.PanelLayout );
-OO.mixinClass( OO.ui.StackLayout, OO.ui.mixin.GroupElement );
-
-/* Events */
-
-/**
- * A 'set' event is emitted when panels are {@link #addItems added}, {@link #removeItems removed},
- * {@link #clearItems cleared} or {@link #setItem displayed}.
- *
- * @event set
- * @param {OO.ui.Layout|null} item Current panel or `null` if no panel is shown
- */
-
-/**
- * When used in continuous mode, this event is emitted when the user scrolls down
- * far enough such that currentItem is no longer visible.
- *
- * @event visibleItemChange
- * @param {OO.ui.PanelLayout} panel The next visible item in the layout
- */
-
-/* Methods */
-
-/**
- * Handle scroll events from the layout element
- *
- * @param {jQuery.Event} e
- * @fires visibleItemChange
- */
-OO.ui.StackLayout.prototype.onScroll = function () {
-       var currentRect,
-               len = this.items.length,
-               currentIndex = this.items.indexOf( this.currentItem ),
-               newIndex = currentIndex,
-               containerRect = this.$element[ 0 ].getBoundingClientRect();
-
-       if ( !containerRect || ( !containerRect.top && !containerRect.bottom ) ) {
-               // Can't get bounding rect, possibly not attached.
-               return;
-       }
-
-       function getRect( item ) {
-               return item.$element[ 0 ].getBoundingClientRect();
-       }
-
-       function isVisible( item ) {
-               var rect = getRect( item );
-               return rect.bottom > containerRect.top && rect.top < containerRect.bottom;
-       }
-
-       currentRect = getRect( this.currentItem );
-
-       if ( currentRect.bottom < containerRect.top ) {
-               // Scrolled down past current item
-               while ( ++newIndex < len ) {
-                       if ( isVisible( this.items[ newIndex ] ) ) {
-                               break;
-                       }
-               }
-       } else if ( currentRect.top > containerRect.bottom ) {
-               // Scrolled up past current item
-               while ( --newIndex >= 0 ) {
-                       if ( isVisible( this.items[ newIndex ] ) ) {
-                               break;
-                       }
-               }
-       }
-
-       if ( newIndex !== currentIndex ) {
-               this.emit( 'visibleItemChange', this.items[ newIndex ] );
-       }
-};
-
-/**
- * Get the current panel.
- *
- * @return {OO.ui.Layout|null}
- */
-OO.ui.StackLayout.prototype.getCurrentItem = function () {
-       return this.currentItem;
-};
-
-/**
- * Unset the current item.
- *
- * @private
- * @param {OO.ui.StackLayout} layout
- * @fires set
- */
-OO.ui.StackLayout.prototype.unsetCurrentItem = function () {
-       var prevItem = this.currentItem;
-       if ( prevItem === null ) {
-               return;
-       }
-
-       this.currentItem = null;
-       this.emit( 'set', null );
-};
-
-/**
- * Add panel layouts to the stack layout.
- *
- * Panels will be added to the end of the stack layout array unless the optional index parameter specifies a different
- * insertion point. Adding a panel that is already in the stack will move it to the end of the array or the point specified
- * by the index.
- *
- * @param {OO.ui.Layout[]} items Panels to add
- * @param {number} [index] Index of the insertion point
- * @chainable
- */
-OO.ui.StackLayout.prototype.addItems = function ( items, index ) {
-       // Update the visibility
-       this.updateHiddenState( items, this.currentItem );
-
-       // Mixin method
-       OO.ui.mixin.GroupElement.prototype.addItems.call( this, items, index );
-
-       if ( !this.currentItem && items.length ) {
-               this.setItem( items[ 0 ] );
-       }
-
-       return this;
-};
-
-/**
- * Remove the specified panels from the stack layout.
- *
- * Removed panels are detached from the DOM, not removed, so that they may be reused. To remove all panels,
- * you may wish to use the #clearItems method instead.
- *
- * @param {OO.ui.Layout[]} items Panels to remove
- * @chainable
- * @fires set
- */
-OO.ui.StackLayout.prototype.removeItems = function ( items ) {
-       // Mixin method
-       OO.ui.mixin.GroupElement.prototype.removeItems.call( this, items );
-
-       if ( items.indexOf( this.currentItem ) !== -1 ) {
-               if ( this.items.length ) {
-                       this.setItem( this.items[ 0 ] );
-               } else {
-                       this.unsetCurrentItem();
-               }
-       }
-
-       return this;
-};
-
-/**
- * Clear all panels from the stack layout.
- *
- * Cleared panels are detached from the DOM, not removed, so that they may be reused. To remove only
- * a subset of panels, use the #removeItems method.
- *
- * @chainable
- * @fires set
- */
-OO.ui.StackLayout.prototype.clearItems = function () {
-       this.unsetCurrentItem();
-       OO.ui.mixin.GroupElement.prototype.clearItems.call( this );
-
-       return this;
-};
-
-/**
- * Show the specified panel.
- *
- * If another panel is currently displayed, it will be hidden.
- *
- * @param {OO.ui.Layout} item Panel to show
- * @chainable
- * @fires set
- */
-OO.ui.StackLayout.prototype.setItem = function ( item ) {
-       if ( item !== this.currentItem ) {
-               this.updateHiddenState( this.items, item );
-
-               if ( this.items.indexOf( item ) !== -1 ) {
-                       this.currentItem = item;
-                       this.emit( 'set', item );
-               } else {
-                       this.unsetCurrentItem();
-               }
-       }
-
-       return this;
-};
-
-/**
- * Update the visibility of all items in case of non-continuous view.
- *
- * Ensure all items are hidden except for the selected one.
- * This method does nothing when the stack is continuous.
- *
- * @private
- * @param {OO.ui.Layout[]} items Item list iterate over
- * @param {OO.ui.Layout} [selectedItem] Selected item to show
- */
-OO.ui.StackLayout.prototype.updateHiddenState = function ( items, selectedItem ) {
-       var i, len;
-
-       if ( !this.continuous ) {
-               for ( i = 0, len = items.length; i < len; i++ ) {
-                       if ( !selectedItem || selectedItem !== items[ i ] ) {
-                               items[ i ].$element.addClass( 'oo-ui-element-hidden' );
-                       }
-               }
-               if ( selectedItem ) {
-                       selectedItem.$element.removeClass( 'oo-ui-element-hidden' );
-               }
-       }
-};
-
-/**
- * HorizontalLayout arranges its contents in a single line (using `display: inline-block` for its
- * items), with small margins between them. Convenient when you need to put a number of block-level
- * widgets on a single line next to each other.
- *
- * Note that inline elements, such as OO.ui.ButtonWidgets, do not need this wrapper.
- *
- *     @example
- *     // HorizontalLayout with a text input and a label
- *     var layout = new OO.ui.HorizontalLayout( {
- *       items: [
- *         new OO.ui.LabelWidget( { label: 'Label' } ),
- *         new OO.ui.TextInputWidget( { value: 'Text' } )
- *       ]
- *     } );
- *     $( 'body' ).append( layout.$element );
- *
- * @class
- * @extends OO.ui.Layout
- * @mixins OO.ui.mixin.GroupElement
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {OO.ui.Widget[]|OO.ui.Layout[]} [items] Widgets or other layouts to add to the layout.
- */
-OO.ui.HorizontalLayout = function OoUiHorizontalLayout( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Parent constructor
-       OO.ui.HorizontalLayout.parent.call( this, config );
-
-       // Mixin constructors
-       OO.ui.mixin.GroupElement.call( this, $.extend( {}, config, { $group: this.$element } ) );
-
-       // Initialization
-       this.$element.addClass( 'oo-ui-horizontalLayout' );
-       if ( Array.isArray( config.items ) ) {
-               this.addItems( config.items );
-       }
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.HorizontalLayout, OO.ui.Layout );
-OO.mixinClass( OO.ui.HorizontalLayout, OO.ui.mixin.GroupElement );
-
-/**
- * BarToolGroups are one of three types of {@link OO.ui.ToolGroup toolgroups} that are used to
- * create {@link OO.ui.Toolbar toolbars} (the other types of groups are {@link OO.ui.MenuToolGroup MenuToolGroup}
- * and {@link OO.ui.ListToolGroup ListToolGroup}). The {@link OO.ui.Tool tools} in a BarToolGroup are
- * displayed by icon in a single row. The title of the tool is displayed when users move the mouse over
- * the tool.
- *
- * BarToolGroups are created by a {@link OO.ui.ToolGroupFactory tool group factory} when the toolbar is
- * set up.
- *
- *     @example
- *     // Example of a BarToolGroup with two tools
- *     var toolFactory = new OO.ui.ToolFactory();
- *     var toolGroupFactory = new OO.ui.ToolGroupFactory();
- *     var toolbar = new OO.ui.Toolbar( toolFactory, toolGroupFactory );
- *
- *     // We will be placing status text in this element when tools are used
- *     var $area = $( '<p>' ).text( 'Example of a BarToolGroup with two tools.' );
- *
- *     // Define the tools that we're going to place in our toolbar
- *
- *     // Create a class inheriting from OO.ui.Tool
- *     function SearchTool() {
- *         SearchTool.parent.apply( this, arguments );
- *     }
- *     OO.inheritClass( SearchTool, OO.ui.Tool );
- *     // Each tool must have a 'name' (used as an internal identifier, see later) and at least one
- *     // of 'icon' and 'title' (displayed icon and text).
- *     SearchTool.static.name = 'search';
- *     SearchTool.static.icon = 'search';
- *     SearchTool.static.title = 'Search...';
- *     // Defines the action that will happen when this tool is selected (clicked).
- *     SearchTool.prototype.onSelect = function () {
- *         $area.text( 'Search tool clicked!' );
- *         // Never display this tool as "active" (selected).
- *         this.setActive( false );
- *     };
- *     SearchTool.prototype.onUpdateState = function () {};
- *     // Make this tool available in our toolFactory and thus our toolbar
- *     toolFactory.register( SearchTool );
- *
- *     // This is a PopupTool. Rather than having a custom 'onSelect' action, it will display a
- *     // little popup window (a PopupWidget).
- *     function HelpTool( toolGroup, config ) {
- *         OO.ui.PopupTool.call( this, toolGroup, $.extend( { popup: {
- *             padded: true,
- *             label: 'Help',
- *             head: true
- *         } }, config ) );
- *         this.popup.$body.append( '<p>I am helpful!</p>' );
- *     }
- *     OO.inheritClass( HelpTool, OO.ui.PopupTool );
- *     HelpTool.static.name = 'help';
- *     HelpTool.static.icon = 'help';
- *     HelpTool.static.title = 'Help';
- *     toolFactory.register( HelpTool );
- *
- *     // Finally define which tools and in what order appear in the toolbar. Each tool may only be
- *     // used once (but not all defined tools must be used).
- *     toolbar.setup( [
- *         {
- *             // 'bar' tool groups display tools by icon only
- *             type: 'bar',
- *             include: [ 'search', 'help' ]
- *         }
- *     ] );
- *
- *     // Create some UI around the toolbar and place it in the document
- *     var frame = new OO.ui.PanelLayout( {
- *         expanded: false,
- *         framed: true
- *     } );
- *     var contentFrame = new OO.ui.PanelLayout( {
- *         expanded: false,
- *         padded: true
- *     } );
- *     frame.$element.append(
- *         toolbar.$element,
- *         contentFrame.$element.append( $area )
- *     );
- *     $( 'body' ).append( frame.$element );
- *
- *     // Here is where the toolbar is actually built. This must be done after inserting it into the
- *     // document.
- *     toolbar.initialize();
- *
- * For more information about how to add tools to a bar tool group, please see {@link OO.ui.ToolGroup toolgroup}.
- * For more information about toolbars in general, please see the [OOjs UI documentation on MediaWiki][1].
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Toolbars
- *
- * @class
- * @extends OO.ui.ToolGroup
- *
- * @constructor
- * @param {OO.ui.Toolbar} toolbar
- * @param {Object} [config] Configuration options
- */
-OO.ui.BarToolGroup = function OoUiBarToolGroup( toolbar, config ) {
-       // Allow passing positional parameters inside the config object
-       if ( OO.isPlainObject( toolbar ) && config === undefined ) {
-               config = toolbar;
-               toolbar = config.toolbar;
-       }
-
-       // Parent constructor
-       OO.ui.BarToolGroup.parent.call( this, toolbar, config );
-
-       // Initialization
-       this.$element.addClass( 'oo-ui-barToolGroup' );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.BarToolGroup, OO.ui.ToolGroup );
-
-/* Static Properties */
-
-OO.ui.BarToolGroup.static.titleTooltips = true;
-
-OO.ui.BarToolGroup.static.accelTooltips = true;
-
-OO.ui.BarToolGroup.static.name = 'bar';
-
-/**
- * PopupToolGroup is an abstract base class used by both {@link OO.ui.MenuToolGroup MenuToolGroup}
- * and {@link OO.ui.ListToolGroup ListToolGroup} to provide a popup--an overlaid menu or list of tools with an
- * optional icon and label. This class can be used for other base classes that also use this functionality.
- *
- * @abstract
- * @class
- * @extends OO.ui.ToolGroup
- * @mixins OO.ui.mixin.IconElement
- * @mixins OO.ui.mixin.IndicatorElement
- * @mixins OO.ui.mixin.LabelElement
- * @mixins OO.ui.mixin.TitledElement
- * @mixins OO.ui.mixin.ClippableElement
- * @mixins OO.ui.mixin.TabIndexedElement
- *
- * @constructor
- * @param {OO.ui.Toolbar} toolbar
- * @param {Object} [config] Configuration options
- * @cfg {string} [header] Text to display at the top of the popup
- */
-OO.ui.PopupToolGroup = function OoUiPopupToolGroup( toolbar, config ) {
-       // Allow passing positional parameters inside the config object
-       if ( OO.isPlainObject( toolbar ) && config === undefined ) {
-               config = toolbar;
-               toolbar = config.toolbar;
-       }
-
-       // Configuration initialization
-       config = config || {};
-
-       // Parent constructor
-       OO.ui.PopupToolGroup.parent.call( this, toolbar, config );
-
-       // Properties
-       this.active = false;
-       this.dragging = false;
-       this.onBlurHandler = this.onBlur.bind( this );
-       this.$handle = $( '<span>' );
-
-       // Mixin constructors
-       OO.ui.mixin.IconElement.call( this, config );
-       OO.ui.mixin.IndicatorElement.call( this, config );
-       OO.ui.mixin.LabelElement.call( this, config );
-       OO.ui.mixin.TitledElement.call( this, config );
-       OO.ui.mixin.ClippableElement.call( this, $.extend( {}, config, { $clippable: this.$group } ) );
-       OO.ui.mixin.TabIndexedElement.call( this, $.extend( {}, config, { $tabIndexed: this.$handle } ) );
-
-       // Events
-       this.$handle.on( {
-               keydown: this.onHandleMouseKeyDown.bind( this ),
-               keyup: this.onHandleMouseKeyUp.bind( this ),
-               mousedown: this.onHandleMouseKeyDown.bind( this ),
-               mouseup: this.onHandleMouseKeyUp.bind( this )
-       } );
-
-       // Initialization
-       this.$handle
-               .addClass( 'oo-ui-popupToolGroup-handle' )
-               .append( this.$icon, this.$label, this.$indicator );
-       // If the pop-up should have a header, add it to the top of the toolGroup.
-       // Note: If this feature is useful for other widgets, we could abstract it into an
-       // OO.ui.HeaderedElement mixin constructor.
-       if ( config.header !== undefined ) {
-               this.$group
-                       .prepend( $( '<span>' )
-                               .addClass( 'oo-ui-popupToolGroup-header' )
-                               .text( config.header )
-                       );
-       }
-       this.$element
-               .addClass( 'oo-ui-popupToolGroup' )
-               .prepend( this.$handle );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.PopupToolGroup, OO.ui.ToolGroup );
-OO.mixinClass( OO.ui.PopupToolGroup, OO.ui.mixin.IconElement );
-OO.mixinClass( OO.ui.PopupToolGroup, OO.ui.mixin.IndicatorElement );
-OO.mixinClass( OO.ui.PopupToolGroup, OO.ui.mixin.LabelElement );
-OO.mixinClass( OO.ui.PopupToolGroup, OO.ui.mixin.TitledElement );
-OO.mixinClass( OO.ui.PopupToolGroup, OO.ui.mixin.ClippableElement );
-OO.mixinClass( OO.ui.PopupToolGroup, OO.ui.mixin.TabIndexedElement );
-
-/* Methods */
-
-/**
- * @inheritdoc
- */
-OO.ui.PopupToolGroup.prototype.setDisabled = function () {
-       // Parent method
-       OO.ui.PopupToolGroup.parent.prototype.setDisabled.apply( this, arguments );
-
-       if ( this.isDisabled() && this.isElementAttached() ) {
-               this.setActive( false );
-       }
-};
-
-/**
- * Handle focus being lost.
- *
- * The event is actually generated from a mouseup/keyup, so it is not a normal blur event object.
- *
- * @protected
- * @param {jQuery.Event} e Mouse up or key up event
- */
-OO.ui.PopupToolGroup.prototype.onBlur = function ( e ) {
-       // Only deactivate when clicking outside the dropdown element
-       if ( $( e.target ).closest( '.oo-ui-popupToolGroup' )[ 0 ] !== this.$element[ 0 ] ) {
-               this.setActive( false );
-       }
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.PopupToolGroup.prototype.onMouseKeyUp = function ( e ) {
-       // Only close toolgroup when a tool was actually selected
-       if (
-               !this.isDisabled() && this.pressed && this.pressed === this.getTargetTool( e ) &&
-               ( e.which === OO.ui.MouseButtons.LEFT || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
-       ) {
-               this.setActive( false );
-       }
-       return OO.ui.PopupToolGroup.parent.prototype.onMouseKeyUp.call( this, e );
-};
-
-/**
- * Handle mouse up and key up events.
- *
- * @protected
- * @param {jQuery.Event} e Mouse up or key up event
- */
-OO.ui.PopupToolGroup.prototype.onHandleMouseKeyUp = function ( e ) {
-       if (
-               !this.isDisabled() &&
-               ( e.which === OO.ui.MouseButtons.LEFT || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
-       ) {
-               return false;
-       }
-};
-
-/**
- * Handle mouse down and key down events.
- *
- * @protected
- * @param {jQuery.Event} e Mouse down or key down event
- */
-OO.ui.PopupToolGroup.prototype.onHandleMouseKeyDown = function ( e ) {
-       if (
-               !this.isDisabled() &&
-               ( e.which === OO.ui.MouseButtons.LEFT || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
-       ) {
-               this.setActive( !this.active );
-               return false;
-       }
-};
-
-/**
- * Switch into 'active' mode.
- *
- * When active, the popup is visible. A mouseup event anywhere in the document will trigger
- * deactivation.
- */
-OO.ui.PopupToolGroup.prototype.setActive = function ( value ) {
-       var containerWidth, containerLeft;
-       value = !!value;
-       if ( this.active !== value ) {
-               this.active = value;
-               if ( value ) {
-                       this.getElementDocument().addEventListener( 'mouseup', this.onBlurHandler, true );
-                       this.getElementDocument().addEventListener( 'keyup', this.onBlurHandler, true );
-
-                       this.$clippable.css( 'left', '' );
-                       // Try anchoring the popup to the left first
-                       this.$element.addClass( 'oo-ui-popupToolGroup-active oo-ui-popupToolGroup-left' );
-                       this.toggleClipping( true );
-                       if ( this.isClippedHorizontally() ) {
-                               // Anchoring to the left caused the popup to clip, so anchor it to the right instead
-                               this.toggleClipping( false );
-                               this.$element
-                                       .removeClass( 'oo-ui-popupToolGroup-left' )
-                                       .addClass( 'oo-ui-popupToolGroup-right' );
-                               this.toggleClipping( true );
-                       }
-                       if ( this.isClippedHorizontally() ) {
-                               // Anchoring to the right also caused the popup to clip, so just make it fill the container
-                               containerWidth = this.$clippableScrollableContainer.width();
-                               containerLeft = this.$clippableScrollableContainer.offset().left;
-
-                               this.toggleClipping( false );
-                               this.$element.removeClass( 'oo-ui-popupToolGroup-right' );
-
-                               this.$clippable.css( {
-                                       left: -( this.$element.offset().left - containerLeft ),
-                                       width: containerWidth
-                               } );
-                       }
-               } else {
-                       this.getElementDocument().removeEventListener( 'mouseup', this.onBlurHandler, true );
-                       this.getElementDocument().removeEventListener( 'keyup', this.onBlurHandler, true );
-                       this.$element.removeClass(
-                               'oo-ui-popupToolGroup-active oo-ui-popupToolGroup-left  oo-ui-popupToolGroup-right'
-                       );
-                       this.toggleClipping( false );
-               }
-       }
-};
-
-/**
- * ListToolGroups are one of three types of {@link OO.ui.ToolGroup toolgroups} that are used to
- * create {@link OO.ui.Toolbar toolbars} (the other types of groups are {@link OO.ui.MenuToolGroup MenuToolGroup}
- * and {@link OO.ui.BarToolGroup BarToolGroup}). The {@link OO.ui.Tool tools} in a ListToolGroup are displayed
- * by label in a dropdown menu. The title of the tool is used as the label text. The menu itself can be configured
- * with a label, icon, indicator, header, and title.
- *
- * ListToolGroups can be configured to be expanded and collapsed. Collapsed lists will have a ‘More’ option that
- * users can select to see the full list of tools. If a collapsed toolgroup is expanded, a ‘Fewer’ option permits
- * users to collapse the list again.
- *
- * ListToolGroups are created by a {@link OO.ui.ToolGroupFactory toolgroup factory} when the toolbar is set up. The factory
- * requires the ListToolGroup's symbolic name, 'list', which is specified along with the other configurations. For more
- * information about how to add tools to a ListToolGroup, please see {@link OO.ui.ToolGroup toolgroup}.
- *
- *     @example
- *     // Example of a ListToolGroup
- *     var toolFactory = new OO.ui.ToolFactory();
- *     var toolGroupFactory = new OO.ui.ToolGroupFactory();
- *     var toolbar = new OO.ui.Toolbar( toolFactory, toolGroupFactory );
- *
- *     // Configure and register two tools
- *     function SettingsTool() {
- *         SettingsTool.parent.apply( this, arguments );
- *     }
- *     OO.inheritClass( SettingsTool, OO.ui.Tool );
- *     SettingsTool.static.name = 'settings';
- *     SettingsTool.static.icon = 'settings';
- *     SettingsTool.static.title = 'Change settings';
- *     SettingsTool.prototype.onSelect = function () {
- *         this.setActive( false );
- *     };
- *     SettingsTool.prototype.onUpdateState = function () {};
- *     toolFactory.register( SettingsTool );
- *     // Register two more tools, nothing interesting here
- *     function StuffTool() {
- *         StuffTool.parent.apply( this, arguments );
- *     }
- *     OO.inheritClass( StuffTool, OO.ui.Tool );
- *     StuffTool.static.name = 'stuff';
- *     StuffTool.static.icon = 'search';
- *     StuffTool.static.title = 'Change the world';
- *     StuffTool.prototype.onSelect = function () {
- *         this.setActive( false );
- *     };
- *     StuffTool.prototype.onUpdateState = function () {};
- *     toolFactory.register( StuffTool );
- *     toolbar.setup( [
- *         {
- *             // Configurations for list toolgroup.
- *             type: 'list',
- *             label: 'ListToolGroup',
- *             indicator: 'down',
- *             icon: 'ellipsis',
- *             title: 'This is the title, displayed when user moves the mouse over the list toolgroup',
- *             header: 'This is the header',
- *             include: [ 'settings', 'stuff' ],
- *             allowCollapse: ['stuff']
- *         }
- *     ] );
- *
- *     // Create some UI around the toolbar and place it in the document
- *     var frame = new OO.ui.PanelLayout( {
- *         expanded: false,
- *         framed: true
- *     } );
- *     frame.$element.append(
- *         toolbar.$element
- *     );
- *     $( 'body' ).append( frame.$element );
- *     // Build the toolbar. This must be done after the toolbar has been appended to the document.
- *     toolbar.initialize();
- *
- * For more information about toolbars in general, please see the [OOjs UI documentation on MediaWiki][1].
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Toolbars
- *
- * @class
- * @extends OO.ui.PopupToolGroup
- *
- * @constructor
- * @param {OO.ui.Toolbar} toolbar
- * @param {Object} [config] Configuration options
- * @cfg {Array} [allowCollapse] Allow the specified tools to be collapsed. By default, collapsible tools
- *  will only be displayed if users click the ‘More’ option displayed at the bottom of the list. If
- *  the list is expanded, a ‘Fewer’ option permits users to collapse the list again. Any tools that
- *  are included in the toolgroup, but are not designated as collapsible, will always be displayed.
- *  To open a collapsible list in its expanded state, set #expanded to 'true'.
- * @cfg {Array} [forceExpand] Expand the specified tools. All other tools will be designated as collapsible.
- *  Unless #expanded is set to true, the collapsible tools will be collapsed when the list is first opened.
- * @cfg {boolean} [expanded=false] Expand collapsible tools. This config is only relevant if tools have
- *  been designated as collapsible. When expanded is set to true, all tools in the group will be displayed
- *  when the list is first opened. Users can collapse the list with a ‘Fewer’ option at the bottom.
- */
-OO.ui.ListToolGroup = function OoUiListToolGroup( toolbar, config ) {
-       // Allow passing positional parameters inside the config object
-       if ( OO.isPlainObject( toolbar ) && config === undefined ) {
-               config = toolbar;
-               toolbar = config.toolbar;
-       }
-
-       // Configuration initialization
-       config = config || {};
-
-       // Properties (must be set before parent constructor, which calls #populate)
-       this.allowCollapse = config.allowCollapse;
-       this.forceExpand = config.forceExpand;
-       this.expanded = config.expanded !== undefined ? config.expanded : false;
-       this.collapsibleTools = [];
-
-       // Parent constructor
-       OO.ui.ListToolGroup.parent.call( this, toolbar, config );
-
-       // Initialization
-       this.$element.addClass( 'oo-ui-listToolGroup' );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.ListToolGroup, OO.ui.PopupToolGroup );
-
-/* Static Properties */
-
-OO.ui.ListToolGroup.static.name = 'list';
-
-/* Methods */
-
-/**
- * @inheritdoc
- */
-OO.ui.ListToolGroup.prototype.populate = function () {
-       var i, len, allowCollapse = [];
-
-       OO.ui.ListToolGroup.parent.prototype.populate.call( this );
-
-       // Update the list of collapsible tools
-       if ( this.allowCollapse !== undefined ) {
-               allowCollapse = this.allowCollapse;
-       } else if ( this.forceExpand !== undefined ) {
-               allowCollapse = OO.simpleArrayDifference( Object.keys( this.tools ), this.forceExpand );
-       }
-
-       this.collapsibleTools = [];
-       for ( i = 0, len = allowCollapse.length; i < len; i++ ) {
-               if ( this.tools[ allowCollapse[ i ] ] !== undefined ) {
-                       this.collapsibleTools.push( this.tools[ allowCollapse[ i ] ] );
-               }
-       }
-
-       // Keep at the end, even when tools are added
-       this.$group.append( this.getExpandCollapseTool().$element );
-
-       this.getExpandCollapseTool().toggle( this.collapsibleTools.length !== 0 );
-       this.updateCollapsibleState();
-};
-
-OO.ui.ListToolGroup.prototype.getExpandCollapseTool = function () {
-       var ExpandCollapseTool;
-       if ( this.expandCollapseTool === undefined ) {
-               ExpandCollapseTool = function () {
-                       ExpandCollapseTool.parent.apply( this, arguments );
-               };
-
-               OO.inheritClass( ExpandCollapseTool, OO.ui.Tool );
-
-               ExpandCollapseTool.prototype.onSelect = function () {
-                       this.toolGroup.expanded = !this.toolGroup.expanded;
-                       this.toolGroup.updateCollapsibleState();
-                       this.setActive( false );
-               };
-               ExpandCollapseTool.prototype.onUpdateState = function () {
-                       // Do nothing. Tool interface requires an implementation of this function.
-               };
-
-               ExpandCollapseTool.static.name = 'more-fewer';
-
-               this.expandCollapseTool = new ExpandCollapseTool( this );
-       }
-       return this.expandCollapseTool;
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.ListToolGroup.prototype.onMouseKeyUp = function ( e ) {
-       // Do not close the popup when the user wants to show more/fewer tools
-       if (
-               $( e.target ).closest( '.oo-ui-tool-name-more-fewer' ).length &&
-               ( e.which === OO.ui.MouseButtons.LEFT || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
-       ) {
-               // HACK: Prevent the popup list from being hidden. Skip the PopupToolGroup implementation (which
-               // hides the popup list when a tool is selected) and call ToolGroup's implementation directly.
-               return OO.ui.ListToolGroup.parent.parent.prototype.onMouseKeyUp.call( this, e );
-       } else {
-               return OO.ui.ListToolGroup.parent.prototype.onMouseKeyUp.call( this, e );
-       }
-};
-
-OO.ui.ListToolGroup.prototype.updateCollapsibleState = function () {
-       var i, len;
-
-       this.getExpandCollapseTool()
-               .setIcon( this.expanded ? 'collapse' : 'expand' )
-               .setTitle( OO.ui.msg( this.expanded ? 'ooui-toolgroup-collapse' : 'ooui-toolgroup-expand' ) );
-
-       for ( i = 0, len = this.collapsibleTools.length; i < len; i++ ) {
-               this.collapsibleTools[ i ].toggle( this.expanded );
-       }
-};
-
-/**
- * MenuToolGroups are one of three types of {@link OO.ui.ToolGroup toolgroups} that are used to
- * create {@link OO.ui.Toolbar toolbars} (the other types of groups are {@link OO.ui.BarToolGroup BarToolGroup}
- * and {@link OO.ui.ListToolGroup ListToolGroup}). MenuToolGroups contain selectable {@link OO.ui.Tool tools},
- * which are displayed by label in a dropdown menu. The tool's title is used as the label text, and the
- * menu label is updated to reflect which tool or tools are currently selected. If no tools are selected,
- * the menu label is empty. The menu can be configured with an indicator, icon, title, and/or header.
- *
- * MenuToolGroups are created by a {@link OO.ui.ToolGroupFactory tool group factory} when the toolbar
- * is set up.
- *
- *     @example
- *     // Example of a MenuToolGroup
- *     var toolFactory = new OO.ui.ToolFactory();
- *     var toolGroupFactory = new OO.ui.ToolGroupFactory();
- *     var toolbar = new OO.ui.Toolbar( toolFactory, toolGroupFactory );
- *
- *     // We will be placing status text in this element when tools are used
- *     var $area = $( '<p>' ).text( 'An example of a MenuToolGroup. Select a tool from the dropdown menu.' );
- *
- *     // Define the tools that we're going to place in our toolbar
- *
- *     function SettingsTool() {
- *         SettingsTool.parent.apply( this, arguments );
- *         this.reallyActive = false;
- *     }
- *     OO.inheritClass( SettingsTool, OO.ui.Tool );
- *     SettingsTool.static.name = 'settings';
- *     SettingsTool.static.icon = 'settings';
- *     SettingsTool.static.title = 'Change settings';
- *     SettingsTool.prototype.onSelect = function () {
- *         $area.text( 'Settings tool clicked!' );
- *         // Toggle the active state on each click
- *         this.reallyActive = !this.reallyActive;
- *         this.setActive( this.reallyActive );
- *         // To update the menu label
- *         this.toolbar.emit( 'updateState' );
- *     };
- *     SettingsTool.prototype.onUpdateState = function () {};
- *     toolFactory.register( SettingsTool );
- *
- *     function StuffTool() {
- *         StuffTool.parent.apply( this, arguments );
- *         this.reallyActive = false;
- *     }
- *     OO.inheritClass( StuffTool, OO.ui.Tool );
- *     StuffTool.static.name = 'stuff';
- *     StuffTool.static.icon = 'ellipsis';
- *     StuffTool.static.title = 'More stuff';
- *     StuffTool.prototype.onSelect = function () {
- *         $area.text( 'More stuff tool clicked!' );
- *         // Toggle the active state on each click
- *         this.reallyActive = !this.reallyActive;
- *         this.setActive( this.reallyActive );
- *         // To update the menu label
- *         this.toolbar.emit( 'updateState' );
- *     };
- *     StuffTool.prototype.onUpdateState = function () {};
- *     toolFactory.register( StuffTool );
- *
- *     // Finally define which tools and in what order appear in the toolbar. Each tool may only be
- *     // used once (but not all defined tools must be used).
- *     toolbar.setup( [
- *         {
- *             type: 'menu',
- *             header: 'This is the (optional) header',
- *             title: 'This is the (optional) title',
- *             indicator: 'down',
- *             include: [ 'settings', 'stuff' ]
- *         }
- *     ] );
- *
- *     // Create some UI around the toolbar and place it in the document
- *     var frame = new OO.ui.PanelLayout( {
- *         expanded: false,
- *         framed: true
- *     } );
- *     var contentFrame = new OO.ui.PanelLayout( {
- *         expanded: false,
- *         padded: true
- *     } );
- *     frame.$element.append(
- *         toolbar.$element,
- *         contentFrame.$element.append( $area )
- *     );
- *     $( 'body' ).append( frame.$element );
- *
- *     // Here is where the toolbar is actually built. This must be done after inserting it into the
- *     // document.
- *     toolbar.initialize();
- *     toolbar.emit( 'updateState' );
- *
- * For more information about how to add tools to a MenuToolGroup, please see {@link OO.ui.ToolGroup toolgroup}.
- * For more information about toolbars in general, please see the [OOjs UI documentation on MediaWiki] [1].
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Toolbars
- *
- * @class
- * @extends OO.ui.PopupToolGroup
- *
- * @constructor
- * @param {OO.ui.Toolbar} toolbar
- * @param {Object} [config] Configuration options
- */
-OO.ui.MenuToolGroup = function OoUiMenuToolGroup( toolbar, config ) {
-       // Allow passing positional parameters inside the config object
-       if ( OO.isPlainObject( toolbar ) && config === undefined ) {
-               config = toolbar;
-               toolbar = config.toolbar;
-       }
-
-       // Configuration initialization
-       config = config || {};
-
-       // Parent constructor
-       OO.ui.MenuToolGroup.parent.call( this, toolbar, config );
-
-       // Events
-       this.toolbar.connect( this, { updateState: 'onUpdateState' } );
-
-       // Initialization
-       this.$element.addClass( 'oo-ui-menuToolGroup' );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.MenuToolGroup, OO.ui.PopupToolGroup );
-
-/* Static Properties */
-
-OO.ui.MenuToolGroup.static.name = 'menu';
-
-/* Methods */
-
-/**
- * Handle the toolbar state being updated.
- *
- * When the state changes, the title of each active item in the menu will be joined together and
- * used as a label for the group. The label will be empty if none of the items are active.
- *
- * @private
- */
-OO.ui.MenuToolGroup.prototype.onUpdateState = function () {
-       var name,
-               labelTexts = [];
-
-       for ( name in this.tools ) {
-               if ( this.tools[ name ].isActive() ) {
-                       labelTexts.push( this.tools[ name ].getTitle() );
-               }
-       }
-
-       this.setLabel( labelTexts.join( ', ' ) || ' ' );
-};
-
-/**
- * Popup tools open a popup window when they are selected from the {@link OO.ui.Toolbar toolbar}. Each popup tool is configured
- * with a static name, title, and icon, as well with as any popup configurations. Unlike other tools, popup tools do not require that developers specify
- * an #onSelect or #onUpdateState method, as these methods have been implemented already.
- *
- *     // Example of a popup tool. When selected, a popup tool displays
- *     // a popup window.
- *     function HelpTool( toolGroup, config ) {
- *        OO.ui.PopupTool.call( this, toolGroup, $.extend( { popup: {
- *            padded: true,
- *            label: 'Help',
- *            head: true
- *        } }, config ) );
- *        this.popup.$body.append( '<p>I am helpful!</p>' );
- *     };
- *     OO.inheritClass( HelpTool, OO.ui.PopupTool );
- *     HelpTool.static.name = 'help';
- *     HelpTool.static.icon = 'help';
- *     HelpTool.static.title = 'Help';
- *     toolFactory.register( HelpTool );
- *
- * For an example of a toolbar that contains a popup tool, see {@link OO.ui.Toolbar toolbars}. For more information about
- * toolbars in genreral, please see the [OOjs UI documentation on MediaWiki][1].
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Toolbars
- *
- * @abstract
- * @class
- * @extends OO.ui.Tool
- * @mixins OO.ui.mixin.PopupElement
- *
- * @constructor
- * @param {OO.ui.ToolGroup} toolGroup
- * @param {Object} [config] Configuration options
- */
-OO.ui.PopupTool = function OoUiPopupTool( toolGroup, config ) {
-       // Allow passing positional parameters inside the config object
-       if ( OO.isPlainObject( toolGroup ) && config === undefined ) {
-               config = toolGroup;
-               toolGroup = config.toolGroup;
-       }
-
-       // Parent constructor
-       OO.ui.PopupTool.parent.call( this, toolGroup, config );
-
-       // Mixin constructors
-       OO.ui.mixin.PopupElement.call( this, config );
-
-       // Initialization
-       this.$element
-               .addClass( 'oo-ui-popupTool' )
-               .append( this.popup.$element );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.PopupTool, OO.ui.Tool );
-OO.mixinClass( OO.ui.PopupTool, OO.ui.mixin.PopupElement );
-
-/* Methods */
-
-/**
- * Handle the tool being selected.
- *
- * @inheritdoc
- */
-OO.ui.PopupTool.prototype.onSelect = function () {
-       if ( !this.isDisabled() ) {
-               this.popup.toggle();
-       }
-       this.setActive( false );
-       return false;
-};
-
-/**
- * Handle the toolbar state being updated.
- *
- * @inheritdoc
- */
-OO.ui.PopupTool.prototype.onUpdateState = function () {
-       this.setActive( false );
-};
-
-/**
- * A ToolGroupTool is a special sort of tool that can contain other {@link OO.ui.Tool tools}
- * and {@link OO.ui.ToolGroup toolgroups}. The ToolGroupTool was specifically designed to be used
- * inside a {@link OO.ui.BarToolGroup bar} toolgroup to provide access to additional tools from
- * the bar item. Included tools will be displayed in a dropdown {@link OO.ui.ListToolGroup list}
- * when the ToolGroupTool is selected.
- *
- *     // Example: ToolGroupTool with two nested tools, 'setting1' and 'setting2', defined elsewhere.
- *
- *     function SettingsTool() {
- *         SettingsTool.parent.apply( this, arguments );
- *     };
- *     OO.inheritClass( SettingsTool, OO.ui.ToolGroupTool );
- *     SettingsTool.static.name = 'settings';
- *     SettingsTool.static.title = 'Change settings';
- *     SettingsTool.static.groupConfig = {
- *         icon: 'settings',
- *         label: 'ToolGroupTool',
- *         include: [  'setting1', 'setting2'  ]
- *     };
- *     toolFactory.register( SettingsTool );
- *
- * For more information, please see the [OOjs UI documentation on MediaWiki][1].
- *
- * Please note that this implementation is subject to change per [T74159] [2].
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Toolbars#ToolGroupTool
- * [2]: https://phabricator.wikimedia.org/T74159
- *
- * @abstract
- * @class
- * @extends OO.ui.Tool
- *
- * @constructor
- * @param {OO.ui.ToolGroup} toolGroup
- * @param {Object} [config] Configuration options
- */
-OO.ui.ToolGroupTool = function OoUiToolGroupTool( toolGroup, config ) {
-       // Allow passing positional parameters inside the config object
-       if ( OO.isPlainObject( toolGroup ) && config === undefined ) {
-               config = toolGroup;
-               toolGroup = config.toolGroup;
-       }
-
-       // Parent constructor
-       OO.ui.ToolGroupTool.parent.call( this, toolGroup, config );
-
-       // Properties
-       this.innerToolGroup = this.createGroup( this.constructor.static.groupConfig );
-
-       // Events
-       this.innerToolGroup.connect( this, { disable: 'onToolGroupDisable' } );
-
-       // Initialization
-       this.$link.remove();
-       this.$element
-               .addClass( 'oo-ui-toolGroupTool' )
-               .append( this.innerToolGroup.$element );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.ToolGroupTool, OO.ui.Tool );
-
-/* Static Properties */
-
-/**
- * Toolgroup configuration.
- *
- * The toolgroup configuration consists of the tools to include, as well as an icon and label
- * to use for the bar item. Tools can be included by symbolic name, group, or with the
- * wildcard selector. Please see {@link OO.ui.ToolGroup toolgroup} for more information.
- *
- * @property {Object.<string,Array>}
- */
-OO.ui.ToolGroupTool.static.groupConfig = {};
-
-/* Methods */
-
-/**
- * Handle the tool being selected.
- *
- * @inheritdoc
- */
-OO.ui.ToolGroupTool.prototype.onSelect = function () {
-       this.innerToolGroup.setActive( !this.innerToolGroup.active );
-       return false;
-};
-
-/**
- * Synchronize disabledness state of the tool with the inner toolgroup.
- *
- * @private
- * @param {boolean} disabled Element is disabled
- */
-OO.ui.ToolGroupTool.prototype.onToolGroupDisable = function ( disabled ) {
-       this.setDisabled( disabled );
-};
-
-/**
- * Handle the toolbar state being updated.
- *
- * @inheritdoc
- */
-OO.ui.ToolGroupTool.prototype.onUpdateState = function () {
-       this.setActive( false );
-};
-
-/**
- * Build a {@link OO.ui.ToolGroup toolgroup} from the specified configuration.
- *
- * @param {Object.<string,Array>} group Toolgroup configuration. Please see {@link OO.ui.ToolGroup toolgroup} for
- *  more information.
- * @return {OO.ui.ListToolGroup}
- */
-OO.ui.ToolGroupTool.prototype.createGroup = function ( group ) {
-       if ( group.include === '*' ) {
-               // Apply defaults to catch-all groups
-               if ( group.label === undefined ) {
-                       group.label = OO.ui.msg( 'ooui-toolbar-more' );
-               }
-       }
-
-       return this.toolbar.getToolGroupFactory().create( 'list', this.toolbar, group );
-};
-
-/**
- * Mixin for OO.ui.Widget subclasses to provide OO.ui.mixin.GroupElement.
- *
- * Use together with OO.ui.mixin.ItemWidget to make disabled state inheritable.
- *
- * @private
- * @abstract
- * @class
- * @extends OO.ui.mixin.GroupElement
- *
- * @constructor
- * @param {Object} [config] Configuration options
- */
-OO.ui.mixin.GroupWidget = function OoUiMixinGroupWidget( config ) {
-       // Parent constructor
-       OO.ui.mixin.GroupWidget.parent.call( this, config );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.mixin.GroupWidget, OO.ui.mixin.GroupElement );
-
-/* Methods */
-
-/**
- * Set the disabled state of the widget.
- *
- * This will also update the disabled state of child widgets.
- *
- * @param {boolean} disabled Disable widget
- * @chainable
- */
-OO.ui.mixin.GroupWidget.prototype.setDisabled = function ( disabled ) {
-       var i, len;
-
-       // Parent method
-       // Note: Calling #setDisabled this way assumes this is mixed into an OO.ui.Widget
-       OO.ui.Widget.prototype.setDisabled.call( this, disabled );
-
-       // During construction, #setDisabled is called before the OO.ui.mixin.GroupElement constructor
-       if ( this.items ) {
-               for ( i = 0, len = this.items.length; i < len; i++ ) {
-                       this.items[ i ].updateDisabled();
-               }
-       }
-
-       return this;
-};
-
-/**
- * Mixin for widgets used as items in widgets that mix in OO.ui.mixin.GroupWidget.
- *
- * Item widgets have a reference to a OO.ui.mixin.GroupWidget while they are attached to the group. This
- * allows bidirectional communication.
- *
- * Use together with OO.ui.mixin.GroupWidget to make disabled state inheritable.
- *
- * @private
- * @abstract
- * @class
- *
- * @constructor
- */
-OO.ui.mixin.ItemWidget = function OoUiMixinItemWidget() {
-       //
-};
-
-/* Methods */
-
-/**
- * Check if widget is disabled.
- *
- * Checks parent if present, making disabled state inheritable.
- *
- * @return {boolean} Widget is disabled
- */
-OO.ui.mixin.ItemWidget.prototype.isDisabled = function () {
-       return this.disabled ||
-               ( this.elementGroup instanceof OO.ui.Widget && this.elementGroup.isDisabled() );
-};
-
-/**
- * Set group element is in.
- *
- * @param {OO.ui.mixin.GroupElement|null} group Group element, null if none
- * @chainable
- */
-OO.ui.mixin.ItemWidget.prototype.setElementGroup = function ( group ) {
-       // Parent method
-       // Note: Calling #setElementGroup this way assumes this is mixed into an OO.ui.Element
-       OO.ui.Element.prototype.setElementGroup.call( this, group );
-
-       // Initialize item disabled states
-       this.updateDisabled();
-
-       return this;
-};
-
-/**
- * OutlineControlsWidget is a set of controls for an {@link OO.ui.OutlineSelectWidget outline select widget}.
- * Controls include moving items up and down, removing items, and adding different kinds of items.
- *
- * **Currently, this class is only used by {@link OO.ui.BookletLayout booklet layouts}.**
- *
- * @class
- * @extends OO.ui.Widget
- * @mixins OO.ui.mixin.GroupElement
- * @mixins OO.ui.mixin.IconElement
- *
- * @constructor
- * @param {OO.ui.OutlineSelectWidget} outline Outline to control
- * @param {Object} [config] Configuration options
- * @cfg {Object} [abilities] List of abilties
- * @cfg {boolean} [abilities.move=true] Allow moving movable items
- * @cfg {boolean} [abilities.remove=true] Allow removing removable items
- */
-OO.ui.OutlineControlsWidget = function OoUiOutlineControlsWidget( outline, config ) {
-       // Allow passing positional parameters inside the config object
-       if ( OO.isPlainObject( outline ) && config === undefined ) {
-               config = outline;
-               outline = config.outline;
-       }
-
-       // Configuration initialization
-       config = $.extend( { icon: 'add' }, config );
-
-       // Parent constructor
-       OO.ui.OutlineControlsWidget.parent.call( this, config );
-
-       // Mixin constructors
-       OO.ui.mixin.GroupElement.call( this, config );
-       OO.ui.mixin.IconElement.call( this, config );
-
-       // Properties
-       this.outline = outline;
-       this.$movers = $( '<div>' );
-       this.upButton = new OO.ui.ButtonWidget( {
-               framed: false,
-               icon: 'collapse',
-               title: OO.ui.msg( 'ooui-outline-control-move-up' )
-       } );
-       this.downButton = new OO.ui.ButtonWidget( {
-               framed: false,
-               icon: 'expand',
-               title: OO.ui.msg( 'ooui-outline-control-move-down' )
-       } );
-       this.removeButton = new OO.ui.ButtonWidget( {
-               framed: false,
-               icon: 'remove',
-               title: OO.ui.msg( 'ooui-outline-control-remove' )
-       } );
-       this.abilities = { move: true, remove: true };
-
-       // Events
-       outline.connect( this, {
-               select: 'onOutlineChange',
-               add: 'onOutlineChange',
-               remove: 'onOutlineChange'
-       } );
-       this.upButton.connect( this, { click: [ 'emit', 'move', -1 ] } );
-       this.downButton.connect( this, { click: [ 'emit', 'move', 1 ] } );
-       this.removeButton.connect( this, { click: [ 'emit', 'remove' ] } );
-
-       // Initialization
-       this.$element.addClass( 'oo-ui-outlineControlsWidget' );
-       this.$group.addClass( 'oo-ui-outlineControlsWidget-items' );
-       this.$movers
-               .addClass( 'oo-ui-outlineControlsWidget-movers' )
-               .append( this.removeButton.$element, this.upButton.$element, this.downButton.$element );
-       this.$element.append( this.$icon, this.$group, this.$movers );
-       this.setAbilities( config.abilities || {} );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.OutlineControlsWidget, OO.ui.Widget );
-OO.mixinClass( OO.ui.OutlineControlsWidget, OO.ui.mixin.GroupElement );
-OO.mixinClass( OO.ui.OutlineControlsWidget, OO.ui.mixin.IconElement );
-
-/* Events */
-
-/**
- * @event move
- * @param {number} places Number of places to move
- */
-
-/**
- * @event remove
- */
-
-/* Methods */
-
-/**
- * Set abilities.
- *
- * @param {Object} abilities List of abilties
- * @param {boolean} [abilities.move] Allow moving movable items
- * @param {boolean} [abilities.remove] Allow removing removable items
- */
-OO.ui.OutlineControlsWidget.prototype.setAbilities = function ( abilities ) {
-       var ability;
-
-       for ( ability in this.abilities ) {
-               if ( abilities[ ability ] !== undefined ) {
-                       this.abilities[ ability ] = !!abilities[ ability ];
-               }
-       }
-
-       this.onOutlineChange();
-};
-
-/**
- * @private
- * Handle outline change events.
- */
-OO.ui.OutlineControlsWidget.prototype.onOutlineChange = function () {
-       var i, len, firstMovable, lastMovable,
-               items = this.outline.getItems(),
-               selectedItem = this.outline.getSelectedItem(),
-               movable = this.abilities.move && selectedItem && selectedItem.isMovable(),
-               removable = this.abilities.remove && selectedItem && selectedItem.isRemovable();
-
-       if ( movable ) {
-               i = -1;
-               len = items.length;
-               while ( ++i < len ) {
-                       if ( items[ i ].isMovable() ) {
-                               firstMovable = items[ i ];
-                               break;
-                       }
-               }
-               i = len;
-               while ( i-- ) {
-                       if ( items[ i ].isMovable() ) {
-                               lastMovable = items[ i ];
-                               break;
-                       }
-               }
-       }
-       this.upButton.setDisabled( !movable || selectedItem === firstMovable );
-       this.downButton.setDisabled( !movable || selectedItem === lastMovable );
-       this.removeButton.setDisabled( !removable );
-};
-
-/**
- * ToggleWidget implements basic behavior of widgets with an on/off state.
- * Please see OO.ui.ToggleButtonWidget and OO.ui.ToggleSwitchWidget for examples.
- *
- * @abstract
- * @class
- * @extends OO.ui.Widget
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {boolean} [value=false] The toggle’s initial on/off state.
- *  By default, the toggle is in the 'off' state.
- */
-OO.ui.ToggleWidget = function OoUiToggleWidget( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Parent constructor
-       OO.ui.ToggleWidget.parent.call( this, config );
-
-       // Properties
-       this.value = null;
-
-       // Initialization
-       this.$element.addClass( 'oo-ui-toggleWidget' );
-       this.setValue( !!config.value );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.ToggleWidget, OO.ui.Widget );
-
-/* Events */
-
-/**
- * @event change
- *
- * A change event is emitted when the on/off state of the toggle changes.
- *
- * @param {boolean} value Value representing the new state of the toggle
- */
-
-/* Methods */
-
-/**
- * Get the value representing the toggle’s state.
- *
- * @return {boolean} The on/off state of the toggle
- */
-OO.ui.ToggleWidget.prototype.getValue = function () {
-       return this.value;
-};
-
-/**
- * Set the state of the toggle: `true` for 'on', `false' for 'off'.
- *
- * @param {boolean} value The state of the toggle
- * @fires change
- * @chainable
- */
-OO.ui.ToggleWidget.prototype.setValue = function ( value ) {
-       value = !!value;
-       if ( this.value !== value ) {
-               this.value = value;
-               this.emit( 'change', value );
-               this.$element.toggleClass( 'oo-ui-toggleWidget-on', value );
-               this.$element.toggleClass( 'oo-ui-toggleWidget-off', !value );
-               this.$element.attr( 'aria-checked', value.toString() );
-       }
-       return this;
-};
-
-/**
- * A ButtonGroupWidget groups related buttons and is used together with OO.ui.ButtonWidget and
- * its subclasses. Each button in a group is addressed by a unique reference. Buttons can be added,
- * removed, and cleared from the group.
- *
- *     @example
- *     // Example: A ButtonGroupWidget with two buttons
- *     var button1 = new OO.ui.PopupButtonWidget( {
- *         label: 'Select a category',
- *         icon: 'menu',
- *         popup: {
- *             $content: $( '<p>List of categories...</p>' ),
- *             padded: true,
- *             align: 'left'
- *         }
- *     } );
- *     var button2 = new OO.ui.ButtonWidget( {
- *         label: 'Add item'
- *     });
- *     var buttonGroup = new OO.ui.ButtonGroupWidget( {
- *         items: [button1, button2]
- *     } );
- *     $( 'body' ).append( buttonGroup.$element );
- *
- * @class
- * @extends OO.ui.Widget
- * @mixins OO.ui.mixin.GroupElement
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {OO.ui.ButtonWidget[]} [items] Buttons to add
- */
-OO.ui.ButtonGroupWidget = function OoUiButtonGroupWidget( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Parent constructor
-       OO.ui.ButtonGroupWidget.parent.call( this, config );
-
-       // Mixin constructors
-       OO.ui.mixin.GroupElement.call( this, $.extend( {}, config, { $group: this.$element } ) );
-
-       // Initialization
-       this.$element.addClass( 'oo-ui-buttonGroupWidget' );
-       if ( Array.isArray( config.items ) ) {
-               this.addItems( config.items );
-       }
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.ButtonGroupWidget, OO.ui.Widget );
-OO.mixinClass( OO.ui.ButtonGroupWidget, OO.ui.mixin.GroupElement );
-
-/**
- * ButtonWidget is a generic widget for buttons. A wide variety of looks,
- * feels, and functionality can be customized via the class’s configuration options
- * and methods. Please see the [OOjs UI documentation on MediaWiki] [1] for more information
- * and examples.
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Buttons_and_Switches
- *
- *     @example
- *     // A button widget
- *     var button = new OO.ui.ButtonWidget( {
- *         label: 'Button with Icon',
- *         icon: 'remove',
- *         iconTitle: 'Remove'
- *     } );
- *     $( 'body' ).append( button.$element );
- *
- * NOTE: HTML form buttons should use the OO.ui.ButtonInputWidget class.
- *
- * @class
- * @extends OO.ui.Widget
- * @mixins OO.ui.mixin.ButtonElement
- * @mixins OO.ui.mixin.IconElement
- * @mixins OO.ui.mixin.IndicatorElement
- * @mixins OO.ui.mixin.LabelElement
- * @mixins OO.ui.mixin.TitledElement
- * @mixins OO.ui.mixin.FlaggedElement
- * @mixins OO.ui.mixin.TabIndexedElement
- * @mixins OO.ui.mixin.AccessKeyedElement
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {string} [href] Hyperlink to visit when the button is clicked.
- * @cfg {string} [target] The frame or window in which to open the hyperlink.
- * @cfg {boolean} [noFollow] Search engine traversal hint (default: true)
- */
-OO.ui.ButtonWidget = function OoUiButtonWidget( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Parent constructor
-       OO.ui.ButtonWidget.parent.call( this, config );
-
-       // Mixin constructors
-       OO.ui.mixin.ButtonElement.call( this, config );
-       OO.ui.mixin.IconElement.call( this, config );
-       OO.ui.mixin.IndicatorElement.call( this, config );
-       OO.ui.mixin.LabelElement.call( this, config );
-       OO.ui.mixin.TitledElement.call( this, $.extend( {}, config, { $titled: this.$button } ) );
-       OO.ui.mixin.FlaggedElement.call( this, config );
-       OO.ui.mixin.TabIndexedElement.call( this, $.extend( {}, config, { $tabIndexed: this.$button } ) );
-       OO.ui.mixin.AccessKeyedElement.call( this, $.extend( {}, config, { $accessKeyed: this.$button } ) );
-
-       // Properties
-       this.href = null;
-       this.target = null;
-       this.noFollow = false;
-
-       // Events
-       this.connect( this, { disable: 'onDisable' } );
-
-       // Initialization
-       this.$button.append( this.$icon, this.$label, this.$indicator );
-       this.$element
-               .addClass( 'oo-ui-buttonWidget' )
-               .append( this.$button );
-       this.setHref( config.href );
-       this.setTarget( config.target );
-       this.setNoFollow( config.noFollow );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.ButtonWidget, OO.ui.Widget );
-OO.mixinClass( OO.ui.ButtonWidget, OO.ui.mixin.ButtonElement );
-OO.mixinClass( OO.ui.ButtonWidget, OO.ui.mixin.IconElement );
-OO.mixinClass( OO.ui.ButtonWidget, OO.ui.mixin.IndicatorElement );
-OO.mixinClass( OO.ui.ButtonWidget, OO.ui.mixin.LabelElement );
-OO.mixinClass( OO.ui.ButtonWidget, OO.ui.mixin.TitledElement );
-OO.mixinClass( OO.ui.ButtonWidget, OO.ui.mixin.FlaggedElement );
-OO.mixinClass( OO.ui.ButtonWidget, OO.ui.mixin.TabIndexedElement );
-OO.mixinClass( OO.ui.ButtonWidget, OO.ui.mixin.AccessKeyedElement );
-
-/* Methods */
-
-/**
- * @inheritdoc
- */
-OO.ui.ButtonWidget.prototype.onMouseDown = function ( e ) {
-       if ( !this.isDisabled() ) {
-               // Remove the tab-index while the button is down to prevent the button from stealing focus
-               this.$button.removeAttr( 'tabindex' );
-       }
-
-       return OO.ui.mixin.ButtonElement.prototype.onMouseDown.call( this, e );
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.ButtonWidget.prototype.onMouseUp = function ( e ) {
-       if ( !this.isDisabled() ) {
-               // Restore the tab-index after the button is up to restore the button's accessibility
-               this.$button.attr( 'tabindex', this.tabIndex );
-       }
-
-       return OO.ui.mixin.ButtonElement.prototype.onMouseUp.call( this, e );
-};
-
-/**
- * Get hyperlink location.
- *
- * @return {string} Hyperlink location
- */
-OO.ui.ButtonWidget.prototype.getHref = function () {
-       return this.href;
-};
-
-/**
- * Get hyperlink target.
- *
- * @return {string} Hyperlink target
- */
-OO.ui.ButtonWidget.prototype.getTarget = function () {
-       return this.target;
-};
-
-/**
- * Get search engine traversal hint.
- *
- * @return {boolean} Whether search engines should avoid traversing this hyperlink
- */
-OO.ui.ButtonWidget.prototype.getNoFollow = function () {
-       return this.noFollow;
-};
-
-/**
- * Set hyperlink location.
- *
- * @param {string|null} href Hyperlink location, null to remove
- */
-OO.ui.ButtonWidget.prototype.setHref = function ( href ) {
-       href = typeof href === 'string' ? href : null;
-       if ( href !== null && !OO.ui.isSafeUrl( href ) ) {
-               href = './' + href;
-       }
-
-       if ( href !== this.href ) {
-               this.href = href;
-               this.updateHref();
-       }
-
-       return this;
-};
-
-/**
- * Update the `href` attribute, in case of changes to href or
- * disabled state.
- *
- * @private
- * @chainable
- */
-OO.ui.ButtonWidget.prototype.updateHref = function () {
-       if ( this.href !== null && !this.isDisabled() ) {
-               this.$button.attr( 'href', this.href );
-       } else {
-               this.$button.removeAttr( 'href' );
-       }
-
-       return this;
-};
-
-/**
- * Handle disable events.
- *
- * @private
- * @param {boolean} disabled Element is disabled
- */
-OO.ui.ButtonWidget.prototype.onDisable = function () {
-       this.updateHref();
-};
-
-/**
- * Set hyperlink target.
- *
- * @param {string|null} target Hyperlink target, null to remove
- */
-OO.ui.ButtonWidget.prototype.setTarget = function ( target ) {
-       target = typeof target === 'string' ? target : null;
-
-       if ( target !== this.target ) {
-               this.target = target;
-               if ( target !== null ) {
-                       this.$button.attr( 'target', target );
-               } else {
-                       this.$button.removeAttr( 'target' );
-               }
-       }
-
-       return this;
-};
-
-/**
- * Set search engine traversal hint.
- *
- * @param {boolean} noFollow True if search engines should avoid traversing this hyperlink
- */
-OO.ui.ButtonWidget.prototype.setNoFollow = function ( noFollow ) {
-       noFollow = typeof noFollow === 'boolean' ? noFollow : true;
-
-       if ( noFollow !== this.noFollow ) {
-               this.noFollow = noFollow;
-               if ( noFollow ) {
-                       this.$button.attr( 'rel', 'nofollow' );
-               } else {
-                       this.$button.removeAttr( 'rel' );
-               }
-       }
-
-       return this;
-};
-
-/**
- * An ActionWidget is a {@link OO.ui.ButtonWidget button widget} that executes an action.
- * Action widgets are used with OO.ui.ActionSet, which manages the behavior and availability
- * of the actions.
- *
- * Both actions and action sets are primarily used with {@link OO.ui.Dialog Dialogs}.
- * Please see the [OOjs UI documentation on MediaWiki] [1] for more information
- * and examples.
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Process_Dialogs#Action_sets
- *
- * @class
- * @extends OO.ui.ButtonWidget
- * @mixins OO.ui.mixin.PendingElement
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {string} [action] Symbolic name of the action (e.g., ‘continue’ or ‘cancel’).
- * @cfg {string[]} [modes] Symbolic names of the modes (e.g., ‘edit’ or ‘read’) in which the action
- *  should be made available. See the action set's {@link OO.ui.ActionSet#setMode setMode} method
- *  for more information about setting modes.
- * @cfg {boolean} [framed=false] Render the action button with a frame
- */
-OO.ui.ActionWidget = function OoUiActionWidget( config ) {
-       // Configuration initialization
-       config = $.extend( { framed: false }, config );
-
-       // Parent constructor
-       OO.ui.ActionWidget.parent.call( this, config );
-
-       // Mixin constructors
-       OO.ui.mixin.PendingElement.call( this, config );
-
-       // Properties
-       this.action = config.action || '';
-       this.modes = config.modes || [];
-       this.width = 0;
-       this.height = 0;
-
-       // Initialization
-       this.$element.addClass( 'oo-ui-actionWidget' );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.ActionWidget, OO.ui.ButtonWidget );
-OO.mixinClass( OO.ui.ActionWidget, OO.ui.mixin.PendingElement );
-
-/* Events */
-
-/**
- * A resize event is emitted when the size of the widget changes.
- *
- * @event resize
- */
-
-/* Methods */
-
-/**
- * Check if the action is configured to be available in the specified `mode`.
- *
- * @param {string} mode Name of mode
- * @return {boolean} The action is configured with the mode
- */
-OO.ui.ActionWidget.prototype.hasMode = function ( mode ) {
-       return this.modes.indexOf( mode ) !== -1;
-};
-
-/**
- * Get the symbolic name of the action (e.g., ‘continue’ or ‘cancel’).
- *
- * @return {string}
- */
-OO.ui.ActionWidget.prototype.getAction = function () {
-       return this.action;
-};
-
-/**
- * Get the symbolic name of the mode or modes for which the action is configured to be available.
- *
- * The current mode is set with the action set's {@link OO.ui.ActionSet#setMode setMode} method.
- * Only actions that are configured to be avaiable in the current mode will be visible. All other actions
- * are hidden.
- *
- * @return {string[]}
- */
-OO.ui.ActionWidget.prototype.getModes = function () {
-       return this.modes.slice();
-};
-
-/**
- * Emit a resize event if the size has changed.
- *
- * @private
- * @chainable
- */
-OO.ui.ActionWidget.prototype.propagateResize = function () {
-       var width, height;
-
-       if ( this.isElementAttached() ) {
-               width = this.$element.width();
-               height = this.$element.height();
-
-               if ( width !== this.width || height !== this.height ) {
-                       this.width = width;
-                       this.height = height;
-                       this.emit( 'resize' );
-               }
-       }
-
-       return this;
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.ActionWidget.prototype.setIcon = function () {
-       // Mixin method
-       OO.ui.mixin.IconElement.prototype.setIcon.apply( this, arguments );
-       this.propagateResize();
-
-       return this;
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.ActionWidget.prototype.setLabel = function () {
-       // Mixin method
-       OO.ui.mixin.LabelElement.prototype.setLabel.apply( this, arguments );
-       this.propagateResize();
-
-       return this;
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.ActionWidget.prototype.setFlags = function () {
-       // Mixin method
-       OO.ui.mixin.FlaggedElement.prototype.setFlags.apply( this, arguments );
-       this.propagateResize();
-
-       return this;
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.ActionWidget.prototype.clearFlags = function () {
-       // Mixin method
-       OO.ui.mixin.FlaggedElement.prototype.clearFlags.apply( this, arguments );
-       this.propagateResize();
-
-       return this;
-};
-
-/**
- * Toggle the visibility of the action button.
- *
- * @param {boolean} [show] Show button, omit to toggle visibility
- * @chainable
- */
-OO.ui.ActionWidget.prototype.toggle = function () {
-       // Parent method
-       OO.ui.ActionWidget.parent.prototype.toggle.apply( this, arguments );
-       this.propagateResize();
-
-       return this;
-};
-
-/**
- * PopupButtonWidgets toggle the visibility of a contained {@link OO.ui.PopupWidget PopupWidget},
- * which is used to display additional information or options.
- *
- *     @example
- *     // Example of a popup button.
- *     var popupButton = new OO.ui.PopupButtonWidget( {
- *         label: 'Popup button with options',
- *         icon: 'menu',
- *         popup: {
- *             $content: $( '<p>Additional options here.</p>' ),
- *             padded: true,
- *             align: 'force-left'
- *         }
- *     } );
- *     // Append the button to the DOM.
- *     $( 'body' ).append( popupButton.$element );
- *
- * @class
- * @extends OO.ui.ButtonWidget
- * @mixins OO.ui.mixin.PopupElement
- *
- * @constructor
- * @param {Object} [config] Configuration options
- */
-OO.ui.PopupButtonWidget = function OoUiPopupButtonWidget( config ) {
-       // Parent constructor
-       OO.ui.PopupButtonWidget.parent.call( this, config );
-
-       // Mixin constructors
-       OO.ui.mixin.PopupElement.call( this, config );
-
-       // Events
-       this.connect( this, { click: 'onAction' } );
-
-       // Initialization
-       this.$element
-               .addClass( 'oo-ui-popupButtonWidget' )
-               .attr( 'aria-haspopup', 'true' )
-               .append( this.popup.$element );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.PopupButtonWidget, OO.ui.ButtonWidget );
-OO.mixinClass( OO.ui.PopupButtonWidget, OO.ui.mixin.PopupElement );
-
-/* Methods */
-
-/**
- * Handle the button action being triggered.
- *
- * @private
- */
-OO.ui.PopupButtonWidget.prototype.onAction = function () {
-       this.popup.toggle();
-};
-
-/**
- * ToggleButtons are buttons that have a state (‘on’ or ‘off’) that is represented by a
- * Boolean value. Like other {@link OO.ui.ButtonWidget buttons}, toggle buttons can be
- * configured with {@link OO.ui.mixin.IconElement icons}, {@link OO.ui.mixin.IndicatorElement indicators},
- * {@link OO.ui.mixin.TitledElement titles}, {@link OO.ui.mixin.FlaggedElement styling flags},
- * and {@link OO.ui.mixin.LabelElement labels}. Please see
- * the [OOjs UI documentation][1] on MediaWiki for more information.
- *
- *     @example
- *     // Toggle buttons in the 'off' and 'on' state.
- *     var toggleButton1 = new OO.ui.ToggleButtonWidget( {
- *         label: 'Toggle Button off'
- *     } );
- *     var toggleButton2 = new OO.ui.ToggleButtonWidget( {
- *         label: 'Toggle Button on',
- *         value: true
- *     } );
- *     // Append the buttons to the DOM.
- *     $( 'body' ).append( toggleButton1.$element, toggleButton2.$element );
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Buttons_and_Switches#Toggle_buttons
- *
- * @class
- * @extends OO.ui.ToggleWidget
- * @mixins OO.ui.mixin.ButtonElement
- * @mixins OO.ui.mixin.IconElement
- * @mixins OO.ui.mixin.IndicatorElement
- * @mixins OO.ui.mixin.LabelElement
- * @mixins OO.ui.mixin.TitledElement
- * @mixins OO.ui.mixin.FlaggedElement
- * @mixins OO.ui.mixin.TabIndexedElement
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {boolean} [value=false] The toggle button’s initial on/off
- *  state. By default, the button is in the 'off' state.
- */
-OO.ui.ToggleButtonWidget = function OoUiToggleButtonWidget( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Parent constructor
-       OO.ui.ToggleButtonWidget.parent.call( this, config );
-
-       // Mixin constructors
-       OO.ui.mixin.ButtonElement.call( this, config );
-       OO.ui.mixin.IconElement.call( this, config );
-       OO.ui.mixin.IndicatorElement.call( this, config );
-       OO.ui.mixin.LabelElement.call( this, config );
-       OO.ui.mixin.TitledElement.call( this, $.extend( {}, config, { $titled: this.$button } ) );
-       OO.ui.mixin.FlaggedElement.call( this, config );
-       OO.ui.mixin.TabIndexedElement.call( this, $.extend( {}, config, { $tabIndexed: this.$button } ) );
-
-       // Events
-       this.connect( this, { click: 'onAction' } );
-
-       // Initialization
-       this.$button.append( this.$icon, this.$label, this.$indicator );
-       this.$element
-               .addClass( 'oo-ui-toggleButtonWidget' )
-               .append( this.$button );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.ToggleButtonWidget, OO.ui.ToggleWidget );
-OO.mixinClass( OO.ui.ToggleButtonWidget, OO.ui.mixin.ButtonElement );
-OO.mixinClass( OO.ui.ToggleButtonWidget, OO.ui.mixin.IconElement );
-OO.mixinClass( OO.ui.ToggleButtonWidget, OO.ui.mixin.IndicatorElement );
-OO.mixinClass( OO.ui.ToggleButtonWidget, OO.ui.mixin.LabelElement );
-OO.mixinClass( OO.ui.ToggleButtonWidget, OO.ui.mixin.TitledElement );
-OO.mixinClass( OO.ui.ToggleButtonWidget, OO.ui.mixin.FlaggedElement );
-OO.mixinClass( OO.ui.ToggleButtonWidget, OO.ui.mixin.TabIndexedElement );
-
-/* Methods */
-
-/**
- * Handle the button action being triggered.
- *
- * @private
- */
-OO.ui.ToggleButtonWidget.prototype.onAction = function () {
-       this.setValue( !this.value );
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.ToggleButtonWidget.prototype.setValue = function ( value ) {
-       value = !!value;
-       if ( value !== this.value ) {
-               // Might be called from parent constructor before ButtonElement constructor
-               if ( this.$button ) {
-                       this.$button.attr( 'aria-pressed', value.toString() );
-               }
-               this.setActive( value );
-       }
-
-       // Parent method
-       OO.ui.ToggleButtonWidget.parent.prototype.setValue.call( this, value );
-
-       return this;
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.ToggleButtonWidget.prototype.setButtonElement = function ( $button ) {
-       if ( this.$button ) {
-               this.$button.removeAttr( 'aria-pressed' );
-       }
-       OO.ui.mixin.ButtonElement.prototype.setButtonElement.call( this, $button );
-       this.$button.attr( 'aria-pressed', this.value.toString() );
-};
-
-/**
- * CapsuleMultiSelectWidgets are something like a {@link OO.ui.ComboBoxInputWidget combo box widget}
- * that allows for selecting multiple values.
- *
- * For more information about menus and options, please see the [OOjs UI documentation on MediaWiki][1].
- *
- *     @example
- *     // Example: A CapsuleMultiSelectWidget.
- *     var capsule = new OO.ui.CapsuleMultiSelectWidget( {
- *         label: 'CapsuleMultiSelectWidget',
- *         selected: [ 'Option 1', 'Option 3' ],
- *         menu: {
- *             items: [
- *                 new OO.ui.MenuOptionWidget( {
- *                     data: 'Option 1',
- *                     label: 'Option One'
- *                 } ),
- *                 new OO.ui.MenuOptionWidget( {
- *                     data: 'Option 2',
- *                     label: 'Option Two'
- *                 } ),
- *                 new OO.ui.MenuOptionWidget( {
- *                     data: 'Option 3',
- *                     label: 'Option Three'
- *                 } ),
- *                 new OO.ui.MenuOptionWidget( {
- *                     data: 'Option 4',
- *                     label: 'Option Four'
- *                 } ),
- *                 new OO.ui.MenuOptionWidget( {
- *                     data: 'Option 5',
- *                     label: 'Option Five'
- *                 } )
- *             ]
- *         }
- *     } );
- *     $( 'body' ).append( capsule.$element );
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options#Menu_selects_and_options
- *
- * @class
- * @extends OO.ui.Widget
- * @mixins OO.ui.mixin.TabIndexedElement
- * @mixins OO.ui.mixin.GroupElement
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {boolean} [allowArbitrary=false] Allow data items to be added even if not present in the menu.
- * @cfg {Object} [menu] Configuration options to pass to the {@link OO.ui.MenuSelectWidget menu select widget}.
- * @cfg {Object} [popup] Configuration options to pass to the {@link OO.ui.PopupWidget popup widget}.
- *  If specified, this popup will be shown instead of the menu (but the menu
- *  will still be used for item labels and allowArbitrary=false). The widgets
- *  in the popup should use this.addItemsFromData() or this.addItems() as necessary.
- * @cfg {jQuery} [$overlay] Render the menu or popup into a separate layer.
- *  This configuration is useful in cases where the expanded menu is larger than
- *  its containing `<div>`. The specified overlay layer is usually on top of
- *  the containing `<div>` and has a larger area. By default, the menu uses
- *  relative positioning.
- */
-OO.ui.CapsuleMultiSelectWidget = function OoUiCapsuleMultiSelectWidget( config ) {
-       var $tabFocus;
-
-       // Configuration initialization
-       config = config || {};
-
-       // Parent constructor
-       OO.ui.CapsuleMultiSelectWidget.parent.call( this, config );
-
-       // Properties (must be set before mixin constructor calls)
-       this.$input = config.popup ? null : $( '<input>' );
-       this.$handle = $( '<div>' );
-
-       // Mixin constructors
-       OO.ui.mixin.GroupElement.call( this, config );
-       if ( config.popup ) {
-               config.popup = $.extend( {}, config.popup, {
-                       align: 'forwards',
-                       anchor: false
-               } );
-               OO.ui.mixin.PopupElement.call( this, config );
-               $tabFocus = $( '<span>' );
-               OO.ui.mixin.TabIndexedElement.call( this, $.extend( {}, config, { $tabIndexed: $tabFocus } ) );
-       } else {
-               this.popup = null;
-               $tabFocus = null;
-               OO.ui.mixin.TabIndexedElement.call( this, $.extend( {}, config, { $tabIndexed: this.$input } ) );
-       }
-       OO.ui.mixin.IndicatorElement.call( this, config );
-       OO.ui.mixin.IconElement.call( this, config );
-
-       // Properties
-       this.$content = $( '<div>' );
-       this.allowArbitrary = !!config.allowArbitrary;
-       this.$overlay = config.$overlay || this.$element;
-       this.menu = new OO.ui.FloatingMenuSelectWidget( $.extend(
-               {
-                       widget: this,
-                       $input: this.$input,
-                       $container: this.$element,
-                       filterFromInput: true,
-                       disabled: this.isDisabled()
-               },
-               config.menu
-       ) );
-
-       // Events
-       if ( this.popup ) {
-               $tabFocus.on( {
-                       focus: this.onFocusForPopup.bind( this )
-               } );
-               this.popup.$element.on( 'focusout', this.onPopupFocusOut.bind( this ) );
-               if ( this.popup.$autoCloseIgnore ) {
-                       this.popup.$autoCloseIgnore.on( 'focusout', this.onPopupFocusOut.bind( this ) );
-               }
-               this.popup.connect( this, {
-                       toggle: function ( visible ) {
-                               $tabFocus.toggle( !visible );
-                       }
-               } );
-       } else {
-               this.$input.on( {
-                       focus: this.onInputFocus.bind( this ),
-                       blur: this.onInputBlur.bind( this ),
-                       'propertychange change click mouseup keydown keyup input cut paste select focus':
-                               OO.ui.debounce( this.updateInputSize.bind( this ) ),
-                       keydown: this.onKeyDown.bind( this ),
-                       keypress: this.onKeyPress.bind( this )
-               } );
-       }
-       this.menu.connect( this, {
-               choose: 'onMenuChoose',
-               add: 'onMenuItemsChange',
-               remove: 'onMenuItemsChange'
-       } );
-       this.$handle.on( {
-               mousedown: this.onMouseDown.bind( this )
-       } );
-
-       // Initialization
-       if ( this.$input ) {
-               this.$input.prop( 'disabled', this.isDisabled() );
-               this.$input.attr( {
-                       role: 'combobox',
-                       'aria-autocomplete': 'list'
-               } );
-               this.updateInputSize();
-       }
-       if ( config.data ) {
-               this.setItemsFromData( config.data );
-       }
-       this.$content.addClass( 'oo-ui-capsuleMultiSelectWidget-content' )
-               .append( this.$group );
-       this.$group.addClass( 'oo-ui-capsuleMultiSelectWidget-group' );
-       this.$handle.addClass( 'oo-ui-capsuleMultiSelectWidget-handle' )
-               .append( this.$indicator, this.$icon, this.$content );
-       this.$element.addClass( 'oo-ui-capsuleMultiSelectWidget' )
-               .append( this.$handle );
-       if ( this.popup ) {
-               this.$content.append( $tabFocus );
-               this.$overlay.append( this.popup.$element );
-       } else {
-               this.$content.append( this.$input );
-               this.$overlay.append( this.menu.$element );
-       }
-       this.onMenuItemsChange();
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.CapsuleMultiSelectWidget, OO.ui.Widget );
-OO.mixinClass( OO.ui.CapsuleMultiSelectWidget, OO.ui.mixin.GroupElement );
-OO.mixinClass( OO.ui.CapsuleMultiSelectWidget, OO.ui.mixin.PopupElement );
-OO.mixinClass( OO.ui.CapsuleMultiSelectWidget, OO.ui.mixin.TabIndexedElement );
-OO.mixinClass( OO.ui.CapsuleMultiSelectWidget, OO.ui.mixin.IndicatorElement );
-OO.mixinClass( OO.ui.CapsuleMultiSelectWidget, OO.ui.mixin.IconElement );
-
-/* Events */
-
-/**
- * @event change
- *
- * A change event is emitted when the set of selected items changes.
- *
- * @param {Mixed[]} datas Data of the now-selected items
- */
-
-/* Methods */
-
-/**
- * Construct a OO.ui.CapsuleItemWidget (or a subclass thereof) from given label and data.
- *
- * @protected
- * @param {Mixed} data Custom data of any type.
- * @param {string} label The label text.
- * @return {OO.ui.CapsuleItemWidget}
- */
-OO.ui.CapsuleMultiSelectWidget.prototype.createItemWidget = function ( data, label ) {
-       return new OO.ui.CapsuleItemWidget( { data: data, label: label } );
-};
-
-/**
- * Get the data of the items in the capsule
- * @return {Mixed[]}
- */
-OO.ui.CapsuleMultiSelectWidget.prototype.getItemsData = function () {
-       return $.map( this.getItems(), function ( e ) { return e.data; } );
-};
-
-/**
- * Set the items in the capsule by providing data
- * @chainable
- * @param {Mixed[]} datas
- * @return {OO.ui.CapsuleMultiSelectWidget}
- */
-OO.ui.CapsuleMultiSelectWidget.prototype.setItemsFromData = function ( datas ) {
-       var widget = this,
-               menu = this.menu,
-               items = this.getItems();
-
-       $.each( datas, function ( i, data ) {
-               var j, label,
-                       item = menu.getItemFromData( data );
-
-               if ( item ) {
-                       label = item.label;
-               } else if ( widget.allowArbitrary ) {
-                       label = String( data );
-               } else {
-                       return;
-               }
-
-               item = null;
-               for ( j = 0; j < items.length; j++ ) {
-                       if ( items[ j ].data === data && items[ j ].label === label ) {
-                               item = items[ j ];
-                               items.splice( j, 1 );
-                               break;
-                       }
-               }
-               if ( !item ) {
-                       item = widget.createItemWidget( data, label );
-               }
-               widget.addItems( [ item ], i );
-       } );
-
-       if ( items.length ) {
-               widget.removeItems( items );
-       }
-
-       return this;
-};
-
-/**
- * Add items to the capsule by providing their data
- * @chainable
- * @param {Mixed[]} datas
- * @return {OO.ui.CapsuleMultiSelectWidget}
- */
-OO.ui.CapsuleMultiSelectWidget.prototype.addItemsFromData = function ( datas ) {
-       var widget = this,
-               menu = this.menu,
-               items = [];
-
-       $.each( datas, function ( i, data ) {
-               var item;
-
-               if ( !widget.getItemFromData( data ) ) {
-                       item = menu.getItemFromData( data );
-                       if ( item ) {
-                               items.push( widget.createItemWidget( data, item.label ) );
-                       } else if ( widget.allowArbitrary ) {
-                               items.push( widget.createItemWidget( data, String( data ) ) );
-                       }
-               }
-       } );
-
-       if ( items.length ) {
-               this.addItems( items );
-       }
-
-       return this;
-};
-
-/**
- * Remove items by data
- * @chainable
- * @param {Mixed[]} datas
- * @return {OO.ui.CapsuleMultiSelectWidget}
- */
-OO.ui.CapsuleMultiSelectWidget.prototype.removeItemsFromData = function ( datas ) {
-       var widget = this,
-               items = [];
-
-       $.each( datas, function ( i, data ) {
-               var item = widget.getItemFromData( data );
-               if ( item ) {
-                       items.push( item );
-               }
-       } );
-
-       if ( items.length ) {
-               this.removeItems( items );
-       }
-
-       return this;
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.CapsuleMultiSelectWidget.prototype.addItems = function ( items ) {
-       var same, i, l,
-               oldItems = this.items.slice();
-
-       OO.ui.mixin.GroupElement.prototype.addItems.call( this, items );
-
-       if ( this.items.length !== oldItems.length ) {
-               same = false;
-       } else {
-               same = true;
-               for ( i = 0, l = oldItems.length; same && i < l; i++ ) {
-                       same = same && this.items[ i ] === oldItems[ i ];
-               }
-       }
-       if ( !same ) {
-               this.emit( 'change', this.getItemsData() );
-               this.menu.position();
-       }
-
-       return this;
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.CapsuleMultiSelectWidget.prototype.removeItems = function ( items ) {
-       var same, i, l,
-               oldItems = this.items.slice();
-
-       OO.ui.mixin.GroupElement.prototype.removeItems.call( this, items );
-
-       if ( this.items.length !== oldItems.length ) {
-               same = false;
-       } else {
-               same = true;
-               for ( i = 0, l = oldItems.length; same && i < l; i++ ) {
-                       same = same && this.items[ i ] === oldItems[ i ];
-               }
-       }
-       if ( !same ) {
-               this.emit( 'change', this.getItemsData() );
-               this.menu.position();
-       }
-
-       return this;
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.CapsuleMultiSelectWidget.prototype.clearItems = function () {
-       if ( this.items.length ) {
-               OO.ui.mixin.GroupElement.prototype.clearItems.call( this );
-               this.emit( 'change', this.getItemsData() );
-               this.menu.position();
-       }
-       return this;
-};
-
-/**
- * Get the capsule widget's menu.
- * @return {OO.ui.MenuSelectWidget} Menu widget
- */
-OO.ui.CapsuleMultiSelectWidget.prototype.getMenu = function () {
-       return this.menu;
-};
-
-/**
- * Handle focus events
- *
- * @private
- * @param {jQuery.Event} event
- */
-OO.ui.CapsuleMultiSelectWidget.prototype.onInputFocus = function () {
-       if ( !this.isDisabled() ) {
-               this.menu.toggle( true );
-       }
-};
-
-/**
- * Handle blur events
- *
- * @private
- * @param {jQuery.Event} event
- */
-OO.ui.CapsuleMultiSelectWidget.prototype.onInputBlur = function () {
-       if ( this.allowArbitrary && this.$input.val().trim() !== '' ) {
-               this.addItemsFromData( [ this.$input.val() ] );
-       }
-       this.clearInput();
-};
-
-/**
- * Handle focus events
- *
- * @private
- * @param {jQuery.Event} event
- */
-OO.ui.CapsuleMultiSelectWidget.prototype.onFocusForPopup = function () {
-       if ( !this.isDisabled() ) {
-               this.popup.setSize( this.$handle.width() );
-               this.popup.toggle( true );
-               this.popup.$element.find( '*' )
-                       .filter( function () { return OO.ui.isFocusableElement( $( this ), true ); } )
-                       .first()
-                       .focus();
-       }
-};
-
-/**
- * Handles popup focus out events.
- *
- * @private
- * @param {Event} e Focus out event
- */
-OO.ui.CapsuleMultiSelectWidget.prototype.onPopupFocusOut = function () {
-       var widget = this.popup;
-
-       setTimeout( function () {
-               if (
-                       widget.isVisible() &&
-                       !OO.ui.contains( widget.$element[ 0 ], document.activeElement, true ) &&
-                       ( !widget.$autoCloseIgnore || !widget.$autoCloseIgnore.has( document.activeElement ).length )
-               ) {
-                       widget.toggle( false );
-               }
-       } );
-};
-
-/**
- * Handle mouse down events.
- *
- * @private
- * @param {jQuery.Event} e Mouse down event
- */
-OO.ui.CapsuleMultiSelectWidget.prototype.onMouseDown = function ( e ) {
-       if ( e.which === OO.ui.MouseButtons.LEFT ) {
-               this.focus();
-               return false;
-       } else {
-               this.updateInputSize();
-       }
-};
-
-/**
- * Handle key press events.
- *
- * @private
- * @param {jQuery.Event} e Key press event
- */
-OO.ui.CapsuleMultiSelectWidget.prototype.onKeyPress = function ( e ) {
-       var item;
-
-       if ( !this.isDisabled() ) {
-               if ( e.which === OO.ui.Keys.ESCAPE ) {
-                       this.clearInput();
-                       return false;
-               }
-
-               if ( !this.popup ) {
-                       this.menu.toggle( true );
-                       if ( e.which === OO.ui.Keys.ENTER ) {
-                               item = this.menu.getItemFromLabel( this.$input.val(), true );
-                               if ( item ) {
-                                       this.addItemsFromData( [ item.data ] );
-                                       this.clearInput();
-                               } else if ( this.allowArbitrary && this.$input.val().trim() !== '' ) {
-                                       this.addItemsFromData( [ this.$input.val() ] );
-                                       this.clearInput();
-                               }
-                               return false;
-                       }
-
-                       // Make sure the input gets resized.
-                       setTimeout( this.updateInputSize.bind( this ), 0 );
-               }
-       }
-};
-
-/**
- * Handle key down events.
- *
- * @private
- * @param {jQuery.Event} e Key down event
- */
-OO.ui.CapsuleMultiSelectWidget.prototype.onKeyDown = function ( e ) {
-       if ( !this.isDisabled() ) {
-               // 'keypress' event is not triggered for Backspace
-               if ( e.keyCode === OO.ui.Keys.BACKSPACE && this.$input.val() === '' ) {
-                       if ( this.items.length ) {
-                               this.removeItems( this.items.slice( -1 ) );
-                       }
-                       return false;
-               }
-       }
-};
-
-/**
- * Update the dimensions of the text input field to encompass all available area.
- *
- * @private
- * @param {jQuery.Event} e Event of some sort
- */
-OO.ui.CapsuleMultiSelectWidget.prototype.updateInputSize = function () {
-       var $lastItem, direction, contentWidth, currentWidth, bestWidth;
-       if ( !this.isDisabled() ) {
-               this.$input.css( 'width', '1em' );
-               $lastItem = this.$group.children().last();
-               direction = OO.ui.Element.static.getDir( this.$handle );
-               contentWidth = this.$input[ 0 ].scrollWidth;
-               currentWidth = this.$input.width();
-
-               if ( contentWidth < currentWidth ) {
-                       // All is fine, don't perform expensive calculations
-                       return;
-               }
-
-               if ( !$lastItem.length ) {
-                       bestWidth = this.$content.innerWidth();
-               } else {
-                       bestWidth = direction === 'ltr' ?
-                               this.$content.innerWidth() - $lastItem.position().left - $lastItem.outerWidth() :
-                               $lastItem.position().left;
-               }
-               // Some safety margin for sanity, because I *really* don't feel like finding out where the few
-               // pixels this is off by are coming from.
-               bestWidth -= 10;
-               if ( contentWidth > bestWidth ) {
-                       // This will result in the input getting shifted to the next line
-                       bestWidth = this.$content.innerWidth() - 10;
-               }
-               this.$input.width( Math.floor( bestWidth ) );
-
-               this.menu.position();
-       }
-};
-
-/**
- * Handle menu choose events.
- *
- * @private
- * @param {OO.ui.OptionWidget} item Chosen item
- */
-OO.ui.CapsuleMultiSelectWidget.prototype.onMenuChoose = function ( item ) {
-       if ( item && item.isVisible() ) {
-               this.addItemsFromData( [ item.getData() ] );
-               this.clearInput();
-       }
-};
-
-/**
- * Handle menu item change events.
- *
- * @private
- */
-OO.ui.CapsuleMultiSelectWidget.prototype.onMenuItemsChange = function () {
-       this.setItemsFromData( this.getItemsData() );
-       this.$element.toggleClass( 'oo-ui-capsuleMultiSelectWidget-empty', this.menu.isEmpty() );
-};
-
-/**
- * Clear the input field
- * @private
- */
-OO.ui.CapsuleMultiSelectWidget.prototype.clearInput = function () {
-       if ( this.$input ) {
-               this.$input.val( '' );
-               this.updateInputSize();
-       }
-       if ( this.popup ) {
-               this.popup.toggle( false );
-       }
-       this.menu.toggle( false );
-       this.menu.selectItem();
-       this.menu.highlightItem();
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.CapsuleMultiSelectWidget.prototype.setDisabled = function ( disabled ) {
-       var i, len;
-
-       // Parent method
-       OO.ui.CapsuleMultiSelectWidget.parent.prototype.setDisabled.call( this, disabled );
-
-       if ( this.$input ) {
-               this.$input.prop( 'disabled', this.isDisabled() );
-       }
-       if ( this.menu ) {
-               this.menu.setDisabled( this.isDisabled() );
-       }
-       if ( this.popup ) {
-               this.popup.setDisabled( this.isDisabled() );
-       }
-
-       if ( this.items ) {
-               for ( i = 0, len = this.items.length; i < len; i++ ) {
-                       this.items[ i ].updateDisabled();
-               }
-       }
-
-       return this;
-};
-
-/**
- * Focus the widget
- * @chainable
- * @return {OO.ui.CapsuleMultiSelectWidget}
- */
-OO.ui.CapsuleMultiSelectWidget.prototype.focus = function () {
-       if ( !this.isDisabled() ) {
-               if ( this.popup ) {
-                       this.popup.setSize( this.$handle.width() );
-                       this.popup.toggle( true );
-                       this.popup.$element.find( '*' )
-                               .filter( function () { return OO.ui.isFocusableElement( $( this ), true ); } )
-                               .first()
-                               .focus();
-               } else {
-                       this.updateInputSize();
-                       this.menu.toggle( true );
-                       this.$input.focus();
-               }
-       }
-       return this;
-};
-
-/**
- * CapsuleItemWidgets are used within a {@link OO.ui.CapsuleMultiSelectWidget
- * CapsuleMultiSelectWidget} to display the selected items.
- *
- * @class
- * @extends OO.ui.Widget
- * @mixins OO.ui.mixin.ItemWidget
- * @mixins OO.ui.mixin.IndicatorElement
- * @mixins OO.ui.mixin.LabelElement
- * @mixins OO.ui.mixin.FlaggedElement
- * @mixins OO.ui.mixin.TabIndexedElement
- *
- * @constructor
- * @param {Object} [config] Configuration options
- */
-OO.ui.CapsuleItemWidget = function OoUiCapsuleItemWidget( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Parent constructor
-       OO.ui.CapsuleItemWidget.parent.call( this, config );
-
-       // Properties (must be set before mixin constructor calls)
-       this.$indicator = $( '<span>' );
-
-       // Mixin constructors
-       OO.ui.mixin.ItemWidget.call( this );
-       OO.ui.mixin.IndicatorElement.call( this, $.extend( {}, config, { $indicator: this.$indicator, indicator: 'clear' } ) );
-       OO.ui.mixin.LabelElement.call( this, config );
-       OO.ui.mixin.FlaggedElement.call( this, config );
-       OO.ui.mixin.TabIndexedElement.call( this, $.extend( {}, config, { $tabIndexed: this.$indicator } ) );
-
-       // Events
-       this.$indicator.on( {
-               keydown: this.onCloseKeyDown.bind( this ),
-               click: this.onCloseClick.bind( this )
-       } );
-
-       // Initialization
-       this.$element
-               .addClass( 'oo-ui-capsuleItemWidget' )
-               .append( this.$indicator, this.$label );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.CapsuleItemWidget, OO.ui.Widget );
-OO.mixinClass( OO.ui.CapsuleItemWidget, OO.ui.mixin.ItemWidget );
-OO.mixinClass( OO.ui.CapsuleItemWidget, OO.ui.mixin.IndicatorElement );
-OO.mixinClass( OO.ui.CapsuleItemWidget, OO.ui.mixin.LabelElement );
-OO.mixinClass( OO.ui.CapsuleItemWidget, OO.ui.mixin.FlaggedElement );
-OO.mixinClass( OO.ui.CapsuleItemWidget, OO.ui.mixin.TabIndexedElement );
-
-/* Methods */
-
-/**
- * Handle close icon clicks
- * @param {jQuery.Event} event
- */
-OO.ui.CapsuleItemWidget.prototype.onCloseClick = function () {
-       var element = this.getElementGroup();
-
-       if ( !this.isDisabled() && element && $.isFunction( element.removeItems ) ) {
-               element.removeItems( [ this ] );
-               element.focus();
-       }
-};
-
-/**
- * Handle close keyboard events
- * @param {jQuery.Event} event Key down event
- */
-OO.ui.CapsuleItemWidget.prototype.onCloseKeyDown = function ( e ) {
-       if ( !this.isDisabled() && $.isFunction( this.getElementGroup().removeItems ) ) {
-               switch ( e.which ) {
-                       case OO.ui.Keys.ENTER:
-                       case OO.ui.Keys.BACKSPACE:
-                       case OO.ui.Keys.SPACE:
-                               this.getElementGroup().removeItems( [ this ] );
-                               return false;
-               }
-       }
-};
-
-/**
- * DropdownWidgets are not menus themselves, rather they contain a menu of options created with
- * OO.ui.MenuOptionWidget. The DropdownWidget takes care of opening and displaying the menu so that
- * users can interact with it.
- *
- * If you want to use this within a HTML form, such as a OO.ui.FormLayout, use
- * OO.ui.DropdownInputWidget instead.
- *
- *     @example
- *     // Example: A DropdownWidget with a menu that contains three options
- *     var dropDown = new OO.ui.DropdownWidget( {
- *         label: 'Dropdown menu: Select a menu option',
- *         menu: {
- *             items: [
- *                 new OO.ui.MenuOptionWidget( {
- *                     data: 'a',
- *                     label: 'First'
- *                 } ),
- *                 new OO.ui.MenuOptionWidget( {
- *                     data: 'b',
- *                     label: 'Second'
- *                 } ),
- *                 new OO.ui.MenuOptionWidget( {
- *                     data: 'c',
- *                     label: 'Third'
- *                 } )
- *             ]
- *         }
- *     } );
- *
- *     $( 'body' ).append( dropDown.$element );
- *
- *     dropDown.getMenu().selectItemByData( 'b' );
- *
- *     dropDown.getMenu().getSelectedItem().getData(); // returns 'b'
- *
- * For more information, please see the [OOjs UI documentation on MediaWiki] [1].
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options#Menu_selects_and_options
- *
- * @class
- * @extends OO.ui.Widget
- * @mixins OO.ui.mixin.IconElement
- * @mixins OO.ui.mixin.IndicatorElement
- * @mixins OO.ui.mixin.LabelElement
- * @mixins OO.ui.mixin.TitledElement
- * @mixins OO.ui.mixin.TabIndexedElement
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {Object} [menu] Configuration options to pass to {@link OO.ui.FloatingMenuSelectWidget menu select widget}
- * @cfg {jQuery} [$overlay] Render the menu into a separate layer. This configuration is useful in cases where
- *  the expanded menu is larger than its containing `<div>`. The specified overlay layer is usually on top of the
- *  containing `<div>` and has a larger area. By default, the menu uses relative positioning.
- */
-OO.ui.DropdownWidget = function OoUiDropdownWidget( config ) {
-       // Configuration initialization
-       config = $.extend( { indicator: 'down' }, config );
-
-       // Parent constructor
-       OO.ui.DropdownWidget.parent.call( this, config );
-
-       // Properties (must be set before TabIndexedElement constructor call)
-       this.$handle = this.$( '<span>' );
-       this.$overlay = config.$overlay || this.$element;
-
-       // Mixin constructors
-       OO.ui.mixin.IconElement.call( this, config );
-       OO.ui.mixin.IndicatorElement.call( this, config );
-       OO.ui.mixin.LabelElement.call( this, config );
-       OO.ui.mixin.TitledElement.call( this, $.extend( {}, config, { $titled: this.$label } ) );
-       OO.ui.mixin.TabIndexedElement.call( this, $.extend( {}, config, { $tabIndexed: this.$handle } ) );
-
-       // Properties
-       this.menu = new OO.ui.FloatingMenuSelectWidget( $.extend( {
-               widget: this,
-               $container: this.$element
-       }, config.menu ) );
-
-       // Events
-       this.$handle.on( {
-               click: this.onClick.bind( this ),
-               keypress: this.onKeyPress.bind( this )
-       } );
-       this.menu.connect( this, { select: 'onMenuSelect' } );
-
-       // Initialization
-       this.$handle
-               .addClass( 'oo-ui-dropdownWidget-handle' )
-               .append( this.$icon, this.$label, this.$indicator );
-       this.$element
-               .addClass( 'oo-ui-dropdownWidget' )
-               .append( this.$handle );
-       this.$overlay.append( this.menu.$element );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.DropdownWidget, OO.ui.Widget );
-OO.mixinClass( OO.ui.DropdownWidget, OO.ui.mixin.IconElement );
-OO.mixinClass( OO.ui.DropdownWidget, OO.ui.mixin.IndicatorElement );
-OO.mixinClass( OO.ui.DropdownWidget, OO.ui.mixin.LabelElement );
-OO.mixinClass( OO.ui.DropdownWidget, OO.ui.mixin.TitledElement );
-OO.mixinClass( OO.ui.DropdownWidget, OO.ui.mixin.TabIndexedElement );
-
-/* Methods */
-
-/**
- * Get the menu.
- *
- * @return {OO.ui.MenuSelectWidget} Menu of widget
- */
-OO.ui.DropdownWidget.prototype.getMenu = function () {
-       return this.menu;
-};
-
-/**
- * Handles menu select events.
- *
- * @private
- * @param {OO.ui.MenuOptionWidget} item Selected menu item
- */
-OO.ui.DropdownWidget.prototype.onMenuSelect = function ( item ) {
-       var selectedLabel;
-
-       if ( !item ) {
-               this.setLabel( null );
-               return;
-       }
-
-       selectedLabel = item.getLabel();
-
-       // If the label is a DOM element, clone it, because setLabel will append() it
-       if ( selectedLabel instanceof jQuery ) {
-               selectedLabel = selectedLabel.clone();
-       }
-
-       this.setLabel( selectedLabel );
-};
-
-/**
- * Handle mouse click events.
- *
- * @private
- * @param {jQuery.Event} e Mouse click event
- */
-OO.ui.DropdownWidget.prototype.onClick = function ( e ) {
-       if ( !this.isDisabled() && e.which === OO.ui.MouseButtons.LEFT ) {
-               this.menu.toggle();
-       }
-       return false;
-};
-
-/**
- * Handle key press events.
- *
- * @private
- * @param {jQuery.Event} e Key press event
- */
-OO.ui.DropdownWidget.prototype.onKeyPress = function ( e ) {
-       if ( !this.isDisabled() &&
-               ( ( e.which === OO.ui.Keys.SPACE && !this.menu.isVisible() ) || e.which === OO.ui.Keys.ENTER )
-       ) {
-               this.menu.toggle();
-               return false;
-       }
-};
-
-/**
- * SelectFileWidgets allow for selecting files, using the HTML5 File API. These
- * widgets can be configured with {@link OO.ui.mixin.IconElement icons} and {@link
- * OO.ui.mixin.IndicatorElement indicators}.
- * Please see the [OOjs UI documentation on MediaWiki] [1] for more information and examples.
- *
- *     @example
- *     // Example of a file select widget
- *     var selectFile = new OO.ui.SelectFileWidget();
- *     $( 'body' ).append( selectFile.$element );
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets
- *
- * @class
- * @extends OO.ui.Widget
- * @mixins OO.ui.mixin.IconElement
- * @mixins OO.ui.mixin.IndicatorElement
- * @mixins OO.ui.mixin.PendingElement
- * @mixins OO.ui.mixin.LabelElement
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {string[]|null} [accept=null] MIME types to accept. null accepts all types.
- * @cfg {string} [placeholder] Text to display when no file is selected.
- * @cfg {string} [notsupported] Text to display when file support is missing in the browser.
- * @cfg {boolean} [droppable=true] Whether to accept files by drag and drop.
- * @cfg {boolean} [showDropTarget=false] Whether to show a drop target. Requires droppable to be true.
- * @cfg {boolean} [dragDropUI=false] Deprecated alias for showDropTarget
- */
-OO.ui.SelectFileWidget = function OoUiSelectFileWidget( config ) {
-       var dragHandler;
-
-       // TODO: Remove in next release
-       if ( config && config.dragDropUI ) {
-               config.showDropTarget = true;
-       }
-
-       // Configuration initialization
-       config = $.extend( {
-               accept: null,
-               placeholder: OO.ui.msg( 'ooui-selectfile-placeholder' ),
-               notsupported: OO.ui.msg( 'ooui-selectfile-not-supported' ),
-               droppable: true,
-               showDropTarget: false
-       }, config );
-
-       // Parent constructor
-       OO.ui.SelectFileWidget.parent.call( this, config );
-
-       // Mixin constructors
-       OO.ui.mixin.IconElement.call( this, config );
-       OO.ui.mixin.IndicatorElement.call( this, config );
-       OO.ui.mixin.PendingElement.call( this, $.extend( {}, config, { $pending: this.$info } ) );
-       OO.ui.mixin.LabelElement.call( this, $.extend( {}, config, { autoFitLabel: true } ) );
-
-       // Properties
-       this.$info = $( '<span>' );
-
-       // Properties
-       this.showDropTarget = config.showDropTarget;
-       this.isSupported = this.constructor.static.isSupported();
-       this.currentFile = null;
-       if ( Array.isArray( config.accept ) ) {
-               this.accept = config.accept;
-       } else {
-               this.accept = null;
-       }
-       this.placeholder = config.placeholder;
-       this.notsupported = config.notsupported;
-       this.onFileSelectedHandler = this.onFileSelected.bind( this );
-
-       this.selectButton = new OO.ui.ButtonWidget( {
-               classes: [ 'oo-ui-selectFileWidget-selectButton' ],
-               label: OO.ui.msg( 'ooui-selectfile-button-select' ),
-               disabled: this.disabled || !this.isSupported
-       } );
-
-       this.clearButton = new OO.ui.ButtonWidget( {
-               classes: [ 'oo-ui-selectFileWidget-clearButton' ],
-               framed: false,
-               icon: 'remove',
-               disabled: this.disabled
-       } );
-
-       // Events
-       this.selectButton.$button.on( {
-               keypress: this.onKeyPress.bind( this )
-       } );
-       this.clearButton.connect( this, {
-               click: 'onClearClick'
-       } );
-       if ( config.droppable ) {
-               dragHandler = this.onDragEnterOrOver.bind( this );
-               this.$element.on( {
-                       dragenter: dragHandler,
-                       dragover: dragHandler,
-                       dragleave: this.onDragLeave.bind( this ),
-                       drop: this.onDrop.bind( this )
-               } );
-       }
-
-       // Initialization
-       this.addInput();
-       this.updateUI();
-       this.$label.addClass( 'oo-ui-selectFileWidget-label' );
-       this.$info
-               .addClass( 'oo-ui-selectFileWidget-info' )
-               .append( this.$icon, this.$label, this.clearButton.$element, this.$indicator );
-       this.$element
-               .addClass( 'oo-ui-selectFileWidget' )
-               .append( this.$info, this.selectButton.$element );
-       if ( config.droppable && config.showDropTarget ) {
-               this.$dropTarget = $( '<div>' )
-                       .addClass( 'oo-ui-selectFileWidget-dropTarget' )
-                       .text( OO.ui.msg( 'ooui-selectfile-dragdrop-placeholder' ) )
-                       .on( {
-                               click: this.onDropTargetClick.bind( this )
-                       } );
-               this.$element.prepend( this.$dropTarget );
-       }
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.SelectFileWidget, OO.ui.Widget );
-OO.mixinClass( OO.ui.SelectFileWidget, OO.ui.mixin.IconElement );
-OO.mixinClass( OO.ui.SelectFileWidget, OO.ui.mixin.IndicatorElement );
-OO.mixinClass( OO.ui.SelectFileWidget, OO.ui.mixin.PendingElement );
-OO.mixinClass( OO.ui.SelectFileWidget, OO.ui.mixin.LabelElement );
-
-/* Static Properties */
-
-/**
- * Check if this widget is supported
- *
- * @static
- * @return {boolean}
- */
-OO.ui.SelectFileWidget.static.isSupported = function () {
-       var $input;
-       if ( OO.ui.SelectFileWidget.static.isSupportedCache === null ) {
-               $input = $( '<input type="file">' );
-               OO.ui.SelectFileWidget.static.isSupportedCache = $input[ 0 ].files !== undefined;
-       }
-       return OO.ui.SelectFileWidget.static.isSupportedCache;
-};
-
-OO.ui.SelectFileWidget.static.isSupportedCache = null;
-
-/* Events */
-
-/**
- * @event change
- *
- * A change event is emitted when the on/off state of the toggle changes.
- *
- * @param {File|null} value New value
- */
-
-/* Methods */
-
-/**
- * Get the current value of the field
- *
- * @return {File|null}
- */
-OO.ui.SelectFileWidget.prototype.getValue = function () {
-       return this.currentFile;
-};
-
-/**
- * Set the current value of the field
- *
- * @param {File|null} file File to select
- */
-OO.ui.SelectFileWidget.prototype.setValue = function ( file ) {
-       if ( this.currentFile !== file ) {
-               this.currentFile = file;
-               this.updateUI();
-               this.emit( 'change', this.currentFile );
-       }
-};
-
-/**
- * Focus the widget.
- *
- * Focusses the select file button.
- *
- * @chainable
- */
-OO.ui.SelectFileWidget.prototype.focus = function () {
-       this.selectButton.$button[ 0 ].focus();
-       return this;
-};
-
-/**
- * Update the user interface when a file is selected or unselected
- *
- * @protected
- */
-OO.ui.SelectFileWidget.prototype.updateUI = function () {
-       var $label;
-       if ( !this.isSupported ) {
-               this.$element.addClass( 'oo-ui-selectFileWidget-notsupported' );
-               this.$element.removeClass( 'oo-ui-selectFileWidget-empty' );
-               this.setLabel( this.notsupported );
-       } else {
-               this.$element.addClass( 'oo-ui-selectFileWidget-supported' );
-               if ( this.currentFile ) {
-                       this.$element.removeClass( 'oo-ui-selectFileWidget-empty' );
-                       $label = $( [] );
-                       $label = $label.add(
-                               $( '<span>' )
-                                       .addClass( 'oo-ui-selectFileWidget-fileName' )
-                                       .text( this.currentFile.name )
-                       );
-                       if ( this.currentFile.type !== '' ) {
-                               $label = $label.add(
-                                       $( '<span>' )
-                                               .addClass( 'oo-ui-selectFileWidget-fileType' )
-                                               .text( this.currentFile.type )
-                               );
-                       }
-                       this.setLabel( $label );
-               } else {
-                       this.$element.addClass( 'oo-ui-selectFileWidget-empty' );
-                       this.setLabel( this.placeholder );
-               }
-       }
-};
-
-/**
- * Add the input to the widget
- *
- * @private
- */
-OO.ui.SelectFileWidget.prototype.addInput = function () {
-       if ( this.$input ) {
-               this.$input.remove();
-       }
-
-       if ( !this.isSupported ) {
-               this.$input = null;
-               return;
-       }
-
-       this.$input = $( '<input type="file">' );
-       this.$input.on( 'change', this.onFileSelectedHandler );
-       this.$input.attr( {
-               tabindex: -1
-       } );
-       if ( this.accept ) {
-               this.$input.attr( 'accept', this.accept.join( ', ' ) );
-       }
-       this.selectButton.$button.append( this.$input );
-};
-
-/**
- * Determine if we should accept this file
- *
- * @private
- * @param {string} File MIME type
- * @return {boolean}
- */
-OO.ui.SelectFileWidget.prototype.isAllowedType = function ( mimeType ) {
-       var i, mimeTest;
-
-       if ( !this.accept || !mimeType ) {
-               return true;
-       }
-
-       for ( i = 0; i < this.accept.length; i++ ) {
-               mimeTest = this.accept[ i ];
-               if ( mimeTest === mimeType ) {
-                       return true;
-               } else if ( mimeTest.substr( -2 ) === '/*' ) {
-                       mimeTest = mimeTest.substr( 0, mimeTest.length - 1 );
-                       if ( mimeType.substr( 0, mimeTest.length ) === mimeTest ) {
-                               return true;
-                       }
-               }
-       }
-
-       return false;
-};
-
-/**
- * Handle file selection from the input
- *
- * @private
- * @param {jQuery.Event} e
- */
-OO.ui.SelectFileWidget.prototype.onFileSelected = function ( e ) {
-       var file = OO.getProp( e.target, 'files', 0 ) || null;
-
-       if ( file && !this.isAllowedType( file.type ) ) {
-               file = null;
-       }
-
-       this.setValue( file );
-       this.addInput();
-};
-
-/**
- * Handle clear button click events.
- *
- * @private
- */
-OO.ui.SelectFileWidget.prototype.onClearClick = function () {
-       this.setValue( null );
-       return false;
-};
-
-/**
- * Handle key press events.
- *
- * @private
- * @param {jQuery.Event} e Key press event
- */
-OO.ui.SelectFileWidget.prototype.onKeyPress = function ( e ) {
-       if ( this.isSupported && !this.isDisabled() && this.$input &&
-               ( e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
-       ) {
-               this.$input.click();
-               return false;
-       }
-};
-
-/**
- * Handle drop target click events.
- *
- * @private
- * @param {jQuery.Event} e Key press event
- */
-OO.ui.SelectFileWidget.prototype.onDropTargetClick = function () {
-       if ( this.isSupported && !this.isDisabled() && this.$input ) {
-               this.$input.click();
-               return false;
-       }
-};
-
-/**
- * Handle drag enter and over events
- *
- * @private
- * @param {jQuery.Event} e Drag event
- */
-OO.ui.SelectFileWidget.prototype.onDragEnterOrOver = function ( e ) {
-       var itemOrFile,
-               droppableFile = false,
-               dt = e.originalEvent.dataTransfer;
-
-       e.preventDefault();
-       e.stopPropagation();
-
-       if ( this.isDisabled() || !this.isSupported ) {
-               this.$element.removeClass( 'oo-ui-selectFileWidget-canDrop' );
-               dt.dropEffect = 'none';
-               return false;
-       }
-
-       // DataTransferItem and File both have a type property, but in Chrome files
-       // have no information at this point.
-       itemOrFile = OO.getProp( dt, 'items', 0 ) || OO.getProp( dt, 'files', 0 );
-       if ( itemOrFile ) {
-               if ( this.isAllowedType( itemOrFile.type ) ) {
-                       droppableFile = true;
-               }
-       // dt.types is Array-like, but not an Array
-       } else if ( Array.prototype.indexOf.call( OO.getProp( dt, 'types' ) || [], 'Files' ) !== -1 ) {
-               // File information is not available at this point for security so just assume
-               // it is acceptable for now.
-               // https://bugzilla.mozilla.org/show_bug.cgi?id=640534
-               droppableFile = true;
-       }
-
-       this.$element.toggleClass( 'oo-ui-selectFileWidget-canDrop', droppableFile );
-       if ( !droppableFile ) {
-               dt.dropEffect = 'none';
-       }
-
-       return false;
-};
-
-/**
- * Handle drag leave events
- *
- * @private
- * @param {jQuery.Event} e Drag event
- */
-OO.ui.SelectFileWidget.prototype.onDragLeave = function () {
-       this.$element.removeClass( 'oo-ui-selectFileWidget-canDrop' );
-};
-
-/**
- * Handle drop events
- *
- * @private
- * @param {jQuery.Event} e Drop event
- */
-OO.ui.SelectFileWidget.prototype.onDrop = function ( e ) {
-       var file = null,
-               dt = e.originalEvent.dataTransfer;
-
-       e.preventDefault();
-       e.stopPropagation();
-       this.$element.removeClass( 'oo-ui-selectFileWidget-canDrop' );
-
-       if ( this.isDisabled() || !this.isSupported ) {
-               return false;
-       }
-
-       file = OO.getProp( dt, 'files', 0 );
-       if ( file && !this.isAllowedType( file.type ) ) {
-               file = null;
-       }
-       if ( file ) {
-               this.setValue( file );
-       }
-
-       return false;
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.SelectFileWidget.prototype.setDisabled = function ( disabled ) {
-       OO.ui.SelectFileWidget.parent.prototype.setDisabled.call( this, disabled );
-       if ( this.selectButton ) {
-               this.selectButton.setDisabled( disabled );
-       }
-       if ( this.clearButton ) {
-               this.clearButton.setDisabled( disabled );
-       }
-       return this;
-};
-
-/**
- * IconWidget is a generic widget for {@link OO.ui.mixin.IconElement icons}. In general, IconWidgets should be used with OO.ui.LabelWidget,
- * which creates a label that identifies the icon’s function. See the [OOjs UI documentation on MediaWiki] [1]
- * for a list of icons included in the library.
- *
- *     @example
- *     // An icon widget with a label
- *     var myIcon = new OO.ui.IconWidget( {
- *         icon: 'help',
- *         iconTitle: 'Help'
- *      } );
- *      // Create a label.
- *      var iconLabel = new OO.ui.LabelWidget( {
- *          label: 'Help'
- *      } );
- *      $( 'body' ).append( myIcon.$element, iconLabel.$element );
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Icons,_Indicators,_and_Labels#Icons
- *
- * @class
- * @extends OO.ui.Widget
- * @mixins OO.ui.mixin.IconElement
- * @mixins OO.ui.mixin.TitledElement
- * @mixins OO.ui.mixin.FlaggedElement
- *
- * @constructor
- * @param {Object} [config] Configuration options
- */
-OO.ui.IconWidget = function OoUiIconWidget( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Parent constructor
-       OO.ui.IconWidget.parent.call( this, config );
-
-       // Mixin constructors
-       OO.ui.mixin.IconElement.call( this, $.extend( {}, config, { $icon: this.$element } ) );
-       OO.ui.mixin.TitledElement.call( this, $.extend( {}, config, { $titled: this.$element } ) );
-       OO.ui.mixin.FlaggedElement.call( this, $.extend( {}, config, { $flagged: this.$element } ) );
-
-       // Initialization
-       this.$element.addClass( 'oo-ui-iconWidget' );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.IconWidget, OO.ui.Widget );
-OO.mixinClass( OO.ui.IconWidget, OO.ui.mixin.IconElement );
-OO.mixinClass( OO.ui.IconWidget, OO.ui.mixin.TitledElement );
-OO.mixinClass( OO.ui.IconWidget, OO.ui.mixin.FlaggedElement );
-
-/* Static Properties */
-
-OO.ui.IconWidget.static.tagName = 'span';
-
-/**
- * IndicatorWidgets create indicators, which are small graphics that are generally used to draw
- * attention to the status of an item or to clarify the function of a control. For a list of
- * indicators included in the library, please see the [OOjs UI documentation on MediaWiki][1].
- *
- *     @example
- *     // Example of an indicator widget
- *     var indicator1 = new OO.ui.IndicatorWidget( {
- *         indicator: 'alert'
- *     } );
- *
- *     // Create a fieldset layout to add a label
- *     var fieldset = new OO.ui.FieldsetLayout();
- *     fieldset.addItems( [
- *         new OO.ui.FieldLayout( indicator1, { label: 'An alert indicator:' } )
- *     ] );
- *     $( 'body' ).append( fieldset.$element );
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Icons,_Indicators,_and_Labels#Indicators
- *
- * @class
- * @extends OO.ui.Widget
- * @mixins OO.ui.mixin.IndicatorElement
- * @mixins OO.ui.mixin.TitledElement
- *
- * @constructor
- * @param {Object} [config] Configuration options
- */
-OO.ui.IndicatorWidget = function OoUiIndicatorWidget( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Parent constructor
-       OO.ui.IndicatorWidget.parent.call( this, config );
-
-       // Mixin constructors
-       OO.ui.mixin.IndicatorElement.call( this, $.extend( {}, config, { $indicator: this.$element } ) );
-       OO.ui.mixin.TitledElement.call( this, $.extend( {}, config, { $titled: this.$element } ) );
-
-       // Initialization
-       this.$element.addClass( 'oo-ui-indicatorWidget' );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.IndicatorWidget, OO.ui.Widget );
-OO.mixinClass( OO.ui.IndicatorWidget, OO.ui.mixin.IndicatorElement );
-OO.mixinClass( OO.ui.IndicatorWidget, OO.ui.mixin.TitledElement );
-
-/* Static Properties */
-
-OO.ui.IndicatorWidget.static.tagName = 'span';
-
-/**
- * InputWidget is the base class for all input widgets, which
- * include {@link OO.ui.TextInputWidget text inputs}, {@link OO.ui.CheckboxInputWidget checkbox inputs},
- * {@link OO.ui.RadioInputWidget radio inputs}, and {@link OO.ui.ButtonInputWidget button inputs}.
- * See the [OOjs UI documentation on MediaWiki] [1] for more information and examples.
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Inputs
- *
- * @abstract
- * @class
- * @extends OO.ui.Widget
- * @mixins OO.ui.mixin.FlaggedElement
- * @mixins OO.ui.mixin.TabIndexedElement
- * @mixins OO.ui.mixin.TitledElement
- * @mixins OO.ui.mixin.AccessKeyedElement
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {string} [name=''] The value of the input’s HTML `name` attribute.
- * @cfg {string} [value=''] The value of the input.
- * @cfg {string} [dir] The directionality of the input (ltr/rtl).
- * @cfg {Function} [inputFilter] The name of an input filter function. Input filters modify the value of an input
- *  before it is accepted.
- */
-OO.ui.InputWidget = function OoUiInputWidget( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Parent constructor
-       OO.ui.InputWidget.parent.call( this, config );
-
-       // Properties
-       this.$input = this.getInputElement( config );
-       this.value = '';
-       this.inputFilter = config.inputFilter;
-
-       // Mixin constructors
-       OO.ui.mixin.FlaggedElement.call( this, config );
-       OO.ui.mixin.TabIndexedElement.call( this, $.extend( {}, config, { $tabIndexed: this.$input } ) );
-       OO.ui.mixin.TitledElement.call( this, $.extend( {}, config, { $titled: this.$input } ) );
-       OO.ui.mixin.AccessKeyedElement.call( this, $.extend( {}, config, { $accessKeyed: this.$input } ) );
-
-       // Events
-       this.$input.on( 'keydown mouseup cut paste change input select', this.onEdit.bind( this ) );
-
-       // Initialization
-       this.$input
-               .addClass( 'oo-ui-inputWidget-input' )
-               .attr( 'name', config.name )
-               .prop( 'disabled', this.isDisabled() );
-       this.$element
-               .addClass( 'oo-ui-inputWidget' )
-               .append( this.$input );
-       this.setValue( config.value );
-       this.setAccessKey( config.accessKey );
-       if ( config.dir ) {
-               this.setDir( config.dir );
-       }
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.InputWidget, OO.ui.Widget );
-OO.mixinClass( OO.ui.InputWidget, OO.ui.mixin.FlaggedElement );
-OO.mixinClass( OO.ui.InputWidget, OO.ui.mixin.TabIndexedElement );
-OO.mixinClass( OO.ui.InputWidget, OO.ui.mixin.TitledElement );
-OO.mixinClass( OO.ui.InputWidget, OO.ui.mixin.AccessKeyedElement );
-
-/* Static Properties */
-
-OO.ui.InputWidget.static.supportsSimpleLabel = true;
-
-/* Static Methods */
-
-/**
- * @inheritdoc
- */
-OO.ui.InputWidget.static.reusePreInfuseDOM = function ( node, config ) {
-       config = OO.ui.InputWidget.parent.static.reusePreInfuseDOM( node, config );
-       // Reusing $input lets browsers preserve inputted values across page reloads (T114134)
-       config.$input = $( node ).find( '.oo-ui-inputWidget-input' );
-       return config;
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.InputWidget.static.gatherPreInfuseState = function ( node, config ) {
-       var state = OO.ui.InputWidget.parent.static.gatherPreInfuseState( node, config );
-       state.value = config.$input.val();
-       // Might be better in TabIndexedElement, but it's awkward to do there because mixins are awkward
-       state.focus = config.$input.is( ':focus' );
-       return state;
-};
-
-/* Events */
-
-/**
- * @event change
- *
- * A change event is emitted when the value of the input changes.
- *
- * @param {string} value
- */
-
-/* Methods */
-
-/**
- * Get input element.
- *
- * Subclasses of OO.ui.InputWidget use the `config` parameter to produce different elements in
- * different circumstances. The element must have a `value` property (like form elements).
- *
- * @protected
- * @param {Object} config Configuration options
- * @return {jQuery} Input element
- */
-OO.ui.InputWidget.prototype.getInputElement = function ( config ) {
-       // See #reusePreInfuseDOM about config.$input
-       return config.$input || $( '<input>' );
-};
-
-/**
- * Handle potentially value-changing events.
- *
- * @private
- * @param {jQuery.Event} e Key down, mouse up, cut, paste, change, input, or select event
- */
-OO.ui.InputWidget.prototype.onEdit = function () {
-       var widget = this;
-       if ( !this.isDisabled() ) {
-               // Allow the stack to clear so the value will be updated
-               setTimeout( function () {
-                       widget.setValue( widget.$input.val() );
-               } );
-       }
-};
-
-/**
- * Get the value of the input.
- *
- * @return {string} Input value
- */
-OO.ui.InputWidget.prototype.getValue = function () {
-       // Resynchronize our internal data with DOM data. Other scripts executing on the page can modify
-       // it, and we won't know unless they're kind enough to trigger a 'change' event.
-       var value = this.$input.val();
-       if ( this.value !== value ) {
-               this.setValue( value );
-       }
-       return this.value;
-};
-
-/**
- * Set the directionality of the input, either RTL (right-to-left) or LTR (left-to-right).
- *
- * @deprecated since v0.13.1, use #setDir directly
- * @param {boolean} isRTL Directionality is right-to-left
- * @chainable
- */
-OO.ui.InputWidget.prototype.setRTL = function ( isRTL ) {
-       this.setDir( isRTL ? 'rtl' : 'ltr' );
-       return this;
-};
-
-/**
- * Set the directionality of the input.
- *
- * @param {string} dir Text directionality: 'ltr', 'rtl' or 'auto'
- * @chainable
- */
-OO.ui.InputWidget.prototype.setDir = function ( dir ) {
-       this.$input.prop( 'dir', dir );
-       return this;
-};
-
-/**
- * Set the value of the input.
- *
- * @param {string} value New value
- * @fires change
- * @chainable
- */
-OO.ui.InputWidget.prototype.setValue = function ( value ) {
-       value = this.cleanUpValue( value );
-       // Update the DOM if it has changed. Note that with cleanUpValue, it
-       // is possible for the DOM value to change without this.value changing.
-       if ( this.$input.val() !== value ) {
-               this.$input.val( value );
-       }
-       if ( this.value !== value ) {
-               this.value = value;
-               this.emit( 'change', this.value );
-       }
-       return this;
-};
-
-/**
- * Set the input's access key.
- * FIXME: This is the same code as in OO.ui.mixin.ButtonElement, maybe find a better place for it?
- *
- * @param {string} accessKey Input's access key, use empty string to remove
- * @chainable
- */
-OO.ui.InputWidget.prototype.setAccessKey = function ( accessKey ) {
-       accessKey = typeof accessKey === 'string' && accessKey.length ? accessKey : null;
-
-       if ( this.accessKey !== accessKey ) {
-               if ( this.$input ) {
-                       if ( accessKey !== null ) {
-                               this.$input.attr( 'accesskey', accessKey );
-                       } else {
-                               this.$input.removeAttr( 'accesskey' );
-                       }
-               }
-               this.accessKey = accessKey;
-       }
-
-       return this;
-};
-
-/**
- * Clean up incoming value.
- *
- * Ensures value is a string, and converts undefined and null to empty string.
- *
- * @private
- * @param {string} value Original value
- * @return {string} Cleaned up value
- */
-OO.ui.InputWidget.prototype.cleanUpValue = function ( value ) {
-       if ( value === undefined || value === null ) {
-               return '';
-       } else if ( this.inputFilter ) {
-               return this.inputFilter( String( value ) );
-       } else {
-               return String( value );
-       }
-};
-
-/**
- * Simulate the behavior of clicking on a label bound to this input. This method is only called by
- * {@link OO.ui.LabelWidget LabelWidget} and {@link OO.ui.FieldLayout FieldLayout}. It should not be
- * called directly.
- */
-OO.ui.InputWidget.prototype.simulateLabelClick = function () {
-       if ( !this.isDisabled() ) {
-               if ( this.$input.is( ':checkbox, :radio' ) ) {
-                       this.$input.click();
-               }
-               if ( this.$input.is( ':input' ) ) {
-                       this.$input[ 0 ].focus();
-               }
-       }
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.InputWidget.prototype.setDisabled = function ( state ) {
-       OO.ui.InputWidget.parent.prototype.setDisabled.call( this, state );
-       if ( this.$input ) {
-               this.$input.prop( 'disabled', this.isDisabled() );
-       }
-       return this;
-};
-
-/**
- * Focus the input.
- *
- * @chainable
- */
-OO.ui.InputWidget.prototype.focus = function () {
-       this.$input[ 0 ].focus();
-       return this;
-};
-
-/**
- * Blur the input.
- *
- * @chainable
- */
-OO.ui.InputWidget.prototype.blur = function () {
-       this.$input[ 0 ].blur();
-       return this;
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.InputWidget.prototype.restorePreInfuseState = function ( state ) {
-       OO.ui.InputWidget.parent.prototype.restorePreInfuseState.call( this, state );
-       if ( state.value !== undefined && state.value !== this.getValue() ) {
-               this.setValue( state.value );
-       }
-       if ( state.focus ) {
-               this.focus();
-       }
-};
-
-/**
- * ButtonInputWidget is used to submit HTML forms and is intended to be used within
- * a OO.ui.FormLayout. If you do not need the button to work with HTML forms, you probably
- * want to use OO.ui.ButtonWidget instead. Button input widgets can be rendered as either an
- * HTML `<button/>` (the default) or an HTML `<input/>` tags. See the
- * [OOjs UI documentation on MediaWiki] [1] for more information.
- *
- *     @example
- *     // A ButtonInputWidget rendered as an HTML button, the default.
- *     var button = new OO.ui.ButtonInputWidget( {
- *         label: 'Input button',
- *         icon: 'check',
- *         value: 'check'
- *     } );
- *     $( 'body' ).append( button.$element );
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Inputs#Button_inputs
- *
- * @class
- * @extends OO.ui.InputWidget
- * @mixins OO.ui.mixin.ButtonElement
- * @mixins OO.ui.mixin.IconElement
- * @mixins OO.ui.mixin.IndicatorElement
- * @mixins OO.ui.mixin.LabelElement
- * @mixins OO.ui.mixin.TitledElement
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {string} [type='button'] The value of the HTML `'type'` attribute: 'button', 'submit' or 'reset'.
- * @cfg {boolean} [useInputTag=false] Use an `<input/>` tag instead of a `<button/>` tag, the default.
- *  Widgets configured to be an `<input/>` do not support {@link #icon icons} and {@link #indicator indicators},
- *  non-plaintext {@link #label labels}, or {@link #value values}. In general, useInputTag should only
- *  be set to `true` when there’s need to support IE6 in a form with multiple buttons.
- */
-OO.ui.ButtonInputWidget = function OoUiButtonInputWidget( config ) {
-       // Configuration initialization
-       config = $.extend( { type: 'button', useInputTag: false }, config );
-
-       // Properties (must be set before parent constructor, which calls #setValue)
-       this.useInputTag = config.useInputTag;
-
-       // Parent constructor
-       OO.ui.ButtonInputWidget.parent.call( this, config );
-
-       // Mixin constructors
-       OO.ui.mixin.ButtonElement.call( this, $.extend( {}, config, { $button: this.$input } ) );
-       OO.ui.mixin.IconElement.call( this, config );
-       OO.ui.mixin.IndicatorElement.call( this, config );
-       OO.ui.mixin.LabelElement.call( this, config );
-       OO.ui.mixin.TitledElement.call( this, $.extend( {}, config, { $titled: this.$input } ) );
-
-       // Initialization
-       if ( !config.useInputTag ) {
-               this.$input.append( this.$icon, this.$label, this.$indicator );
-       }
-       this.$element.addClass( 'oo-ui-buttonInputWidget' );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.ButtonInputWidget, OO.ui.InputWidget );
-OO.mixinClass( OO.ui.ButtonInputWidget, OO.ui.mixin.ButtonElement );
-OO.mixinClass( OO.ui.ButtonInputWidget, OO.ui.mixin.IconElement );
-OO.mixinClass( OO.ui.ButtonInputWidget, OO.ui.mixin.IndicatorElement );
-OO.mixinClass( OO.ui.ButtonInputWidget, OO.ui.mixin.LabelElement );
-OO.mixinClass( OO.ui.ButtonInputWidget, OO.ui.mixin.TitledElement );
-
-/* Static Properties */
-
-/**
- * Disable generating `<label>` elements for buttons. One would very rarely need additional label
- * for a button, and it's already a big clickable target, and it causes unexpected rendering.
- */
-OO.ui.ButtonInputWidget.static.supportsSimpleLabel = false;
-
-/* Methods */
-
-/**
- * @inheritdoc
- * @protected
- */
-OO.ui.ButtonInputWidget.prototype.getInputElement = function ( config ) {
-       var type;
-       // See InputWidget#reusePreInfuseDOM about config.$input
-       if ( config.$input ) {
-               return config.$input.empty();
-       }
-       type = [ 'button', 'submit', 'reset' ].indexOf( config.type ) !== -1 ? config.type : 'button';
-       return $( '<' + ( config.useInputTag ? 'input' : 'button' ) + ' type="' + type + '">' );
-};
-
-/**
- * Set label value.
- *
- * If #useInputTag is `true`, the label is set as the `value` of the `<input/>` tag.
- *
- * @param {jQuery|string|Function|null} label Label nodes, text, a function that returns nodes or
- *  text, or `null` for no label
- * @chainable
- */
-OO.ui.ButtonInputWidget.prototype.setLabel = function ( label ) {
-       OO.ui.mixin.LabelElement.prototype.setLabel.call( this, label );
-
-       if ( this.useInputTag ) {
-               if ( typeof label === 'function' ) {
-                       label = OO.ui.resolveMsg( label );
-               }
-               if ( label instanceof jQuery ) {
-                       label = label.text();
-               }
-               if ( !label ) {
-                       label = '';
-               }
-               this.$input.val( label );
-       }
-
-       return this;
-};
-
-/**
- * Set the value of the input.
- *
- * This method is disabled for button inputs configured as {@link #useInputTag <input/> tags}, as
- * they do not support {@link #value values}.
- *
- * @param {string} value New value
- * @chainable
- */
-OO.ui.ButtonInputWidget.prototype.setValue = function ( value ) {
-       if ( !this.useInputTag ) {
-               OO.ui.ButtonInputWidget.parent.prototype.setValue.call( this, value );
-       }
-       return this;
-};
-
-/**
- * CheckboxInputWidgets, like HTML checkboxes, can be selected and/or configured with a value.
- * Note that these {@link OO.ui.InputWidget input widgets} are best laid out
- * in {@link OO.ui.FieldLayout field layouts} that use the {@link OO.ui.FieldLayout#align inline}
- * alignment. For more information, please see the [OOjs UI documentation on MediaWiki][1].
- *
- * This widget can be used inside a HTML form, such as a OO.ui.FormLayout.
- *
- *     @example
- *     // An example of selected, unselected, and disabled checkbox inputs
- *     var checkbox1=new OO.ui.CheckboxInputWidget( {
- *          value: 'a',
- *          selected: true
- *     } );
- *     var checkbox2=new OO.ui.CheckboxInputWidget( {
- *         value: 'b'
- *     } );
- *     var checkbox3=new OO.ui.CheckboxInputWidget( {
- *         value:'c',
- *         disabled: true
- *     } );
- *     // Create a fieldset layout with fields for each checkbox.
- *     var fieldset = new OO.ui.FieldsetLayout( {
- *         label: 'Checkboxes'
- *     } );
- *     fieldset.addItems( [
- *         new OO.ui.FieldLayout( checkbox1, { label: 'Selected checkbox', align: 'inline' } ),
- *         new OO.ui.FieldLayout( checkbox2, { label: 'Unselected checkbox', align: 'inline' } ),
- *         new OO.ui.FieldLayout( checkbox3, { label: 'Disabled checkbox', align: 'inline' } ),
- *     ] );
- *     $( 'body' ).append( fieldset.$element );
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Inputs
- *
- * @class
- * @extends OO.ui.InputWidget
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {boolean} [selected=false] Select the checkbox initially. By default, the checkbox is not selected.
- */
-OO.ui.CheckboxInputWidget = function OoUiCheckboxInputWidget( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Parent constructor
-       OO.ui.CheckboxInputWidget.parent.call( this, config );
-
-       // Initialization
-       this.$element
-               .addClass( 'oo-ui-checkboxInputWidget' )
-               // Required for pretty styling in MediaWiki theme
-               .append( $( '<span>' ) );
-       this.setSelected( config.selected !== undefined ? config.selected : false );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.CheckboxInputWidget, OO.ui.InputWidget );
-
-/* Static Methods */
-
-/**
- * @inheritdoc
- */
-OO.ui.CheckboxInputWidget.static.gatherPreInfuseState = function ( node, config ) {
-       var state = OO.ui.CheckboxInputWidget.parent.static.gatherPreInfuseState( node, config );
-       state.checked = config.$input.prop( 'checked' );
-       return state;
-};
-
-/* Methods */
-
-/**
- * @inheritdoc
- * @protected
- */
-OO.ui.CheckboxInputWidget.prototype.getInputElement = function () {
-       return $( '<input type="checkbox" />' );
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.CheckboxInputWidget.prototype.onEdit = function () {
-       var widget = this;
-       if ( !this.isDisabled() ) {
-               // Allow the stack to clear so the value will be updated
-               setTimeout( function () {
-                       widget.setSelected( widget.$input.prop( 'checked' ) );
-               } );
-       }
-};
-
-/**
- * Set selection state of this checkbox.
- *
- * @param {boolean} state `true` for selected
- * @chainable
- */
-OO.ui.CheckboxInputWidget.prototype.setSelected = function ( state ) {
-       state = !!state;
-       if ( this.selected !== state ) {
-               this.selected = state;
-               this.$input.prop( 'checked', this.selected );
-               this.emit( 'change', this.selected );
-       }
-       return this;
-};
-
-/**
- * Check if this checkbox is selected.
- *
- * @return {boolean} Checkbox is selected
- */
-OO.ui.CheckboxInputWidget.prototype.isSelected = function () {
-       // Resynchronize our internal data with DOM data. Other scripts executing on the page can modify
-       // it, and we won't know unless they're kind enough to trigger a 'change' event.
-       var selected = this.$input.prop( 'checked' );
-       if ( this.selected !== selected ) {
-               this.setSelected( selected );
-       }
-       return this.selected;
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.CheckboxInputWidget.prototype.restorePreInfuseState = function ( state ) {
-       OO.ui.CheckboxInputWidget.parent.prototype.restorePreInfuseState.call( this, state );
-       if ( state.checked !== undefined && state.checked !== this.isSelected() ) {
-               this.setSelected( state.checked );
-       }
-};
-
-/**
- * DropdownInputWidget is a {@link OO.ui.DropdownWidget DropdownWidget} intended to be used
- * within a HTML form, such as a OO.ui.FormLayout. The selected value is synchronized with the value
- * of a hidden HTML `input` tag. Please see the [OOjs UI documentation on MediaWiki][1] for
- * more information about input widgets.
- *
- * A DropdownInputWidget always has a value (one of the options is always selected), unless there
- * are no options. If no `value` configuration option is provided, the first option is selected.
- * If you need a state representing no value (no option being selected), use a DropdownWidget.
- *
- * This and OO.ui.RadioSelectInputWidget support the same configuration options.
- *
- *     @example
- *     // Example: A DropdownInputWidget with three options
- *     var dropdownInput = new OO.ui.DropdownInputWidget( {
- *         options: [
- *             { data: 'a', label: 'First' },
- *             { data: 'b', label: 'Second'},
- *             { data: 'c', label: 'Third' }
- *         ]
- *     } );
- *     $( 'body' ).append( dropdownInput.$element );
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Inputs
- *
- * @class
- * @extends OO.ui.InputWidget
- * @mixins OO.ui.mixin.TitledElement
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {Object[]} [options=[]] Array of menu options in the format `{ data: …, label: … }`
- * @cfg {Object} [dropdown] Configuration options for {@link OO.ui.DropdownWidget DropdownWidget}
- */
-OO.ui.DropdownInputWidget = function OoUiDropdownInputWidget( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Properties (must be done before parent constructor which calls #setDisabled)
-       this.dropdownWidget = new OO.ui.DropdownWidget( config.dropdown );
-
-       // Parent constructor
-       OO.ui.DropdownInputWidget.parent.call( this, config );
-
-       // Mixin constructors
-       OO.ui.mixin.TitledElement.call( this, config );
-
-       // Events
-       this.dropdownWidget.getMenu().connect( this, { select: 'onMenuSelect' } );
-
-       // Initialization
-       this.setOptions( config.options || [] );
-       this.$element
-               .addClass( 'oo-ui-dropdownInputWidget' )
-               .append( this.dropdownWidget.$element );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.DropdownInputWidget, OO.ui.InputWidget );
-OO.mixinClass( OO.ui.DropdownInputWidget, OO.ui.mixin.TitledElement );
-
-/* Methods */
-
-/**
- * @inheritdoc
- * @protected
- */
-OO.ui.DropdownInputWidget.prototype.getInputElement = function ( config ) {
-       // See InputWidget#reusePreInfuseDOM about config.$input
-       if ( config.$input ) {
-               return config.$input.addClass( 'oo-ui-element-hidden' );
-       }
-       return $( '<input type="hidden">' );
-};
-
-/**
- * Handles menu select events.
- *
- * @private
- * @param {OO.ui.MenuOptionWidget} item Selected menu item
- */
-OO.ui.DropdownInputWidget.prototype.onMenuSelect = function ( item ) {
-       this.setValue( item.getData() );
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.DropdownInputWidget.prototype.setValue = function ( value ) {
-       value = this.cleanUpValue( value );
-       this.dropdownWidget.getMenu().selectItemByData( value );
-       OO.ui.DropdownInputWidget.parent.prototype.setValue.call( this, value );
-       return this;
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.DropdownInputWidget.prototype.setDisabled = function ( state ) {
-       this.dropdownWidget.setDisabled( state );
-       OO.ui.DropdownInputWidget.parent.prototype.setDisabled.call( this, state );
-       return this;
-};
-
-/**
- * Set the options available for this input.
- *
- * @param {Object[]} options Array of menu options in the format `{ data: …, label: … }`
- * @chainable
- */
-OO.ui.DropdownInputWidget.prototype.setOptions = function ( options ) {
-       var
-               value = this.getValue(),
-               widget = this;
-
-       // Rebuild the dropdown menu
-       this.dropdownWidget.getMenu()
-               .clearItems()
-               .addItems( options.map( function ( opt ) {
-                       var optValue = widget.cleanUpValue( opt.data );
-                       return new OO.ui.MenuOptionWidget( {
-                               data: optValue,
-                               label: opt.label !== undefined ? opt.label : optValue
-                       } );
-               } ) );
-
-       // Restore the previous value, or reset to something sensible
-       if ( this.dropdownWidget.getMenu().getItemFromData( value ) ) {
-               // Previous value is still available, ensure consistency with the dropdown
-               this.setValue( value );
-       } else {
-               // No longer valid, reset
-               if ( options.length ) {
-                       this.setValue( options[ 0 ].data );
-               }
-       }
-
-       return this;
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.DropdownInputWidget.prototype.focus = function () {
-       this.dropdownWidget.getMenu().toggle( true );
-       return this;
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.DropdownInputWidget.prototype.blur = function () {
-       this.dropdownWidget.getMenu().toggle( false );
-       return this;
-};
-
-/**
- * RadioInputWidget creates a single radio button. Because radio buttons are usually used as a set,
- * in most cases you will want to use a {@link OO.ui.RadioSelectWidget radio select}
- * with {@link OO.ui.RadioOptionWidget radio options} instead of this class. For more information,
- * please see the [OOjs UI documentation on MediaWiki][1].
- *
- * This widget can be used inside a HTML form, such as a OO.ui.FormLayout.
- *
- *     @example
- *     // An example of selected, unselected, and disabled radio inputs
- *     var radio1 = new OO.ui.RadioInputWidget( {
- *         value: 'a',
- *         selected: true
- *     } );
- *     var radio2 = new OO.ui.RadioInputWidget( {
- *         value: 'b'
- *     } );
- *     var radio3 = new OO.ui.RadioInputWidget( {
- *         value: 'c',
- *         disabled: true
- *     } );
- *     // Create a fieldset layout with fields for each radio button.
- *     var fieldset = new OO.ui.FieldsetLayout( {
- *         label: 'Radio inputs'
- *     } );
- *     fieldset.addItems( [
- *         new OO.ui.FieldLayout( radio1, { label: 'Selected', align: 'inline' } ),
- *         new OO.ui.FieldLayout( radio2, { label: 'Unselected', align: 'inline' } ),
- *         new OO.ui.FieldLayout( radio3, { label: 'Disabled', align: 'inline' } ),
- *     ] );
- *     $( 'body' ).append( fieldset.$element );
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Inputs
- *
- * @class
- * @extends OO.ui.InputWidget
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {boolean} [selected=false] Select the radio button initially. By default, the radio button is not selected.
- */
-OO.ui.RadioInputWidget = function OoUiRadioInputWidget( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Parent constructor
-       OO.ui.RadioInputWidget.parent.call( this, config );
-
-       // Initialization
-       this.$element
-               .addClass( 'oo-ui-radioInputWidget' )
-               // Required for pretty styling in MediaWiki theme
-               .append( $( '<span>' ) );
-       this.setSelected( config.selected !== undefined ? config.selected : false );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.RadioInputWidget, OO.ui.InputWidget );
-
-/* Static Methods */
-
-/**
- * @inheritdoc
- */
-OO.ui.RadioInputWidget.static.gatherPreInfuseState = function ( node, config ) {
-       var state = OO.ui.RadioInputWidget.parent.static.gatherPreInfuseState( node, config );
-       state.checked = config.$input.prop( 'checked' );
-       return state;
-};
-
-/* Methods */
-
-/**
- * @inheritdoc
- * @protected
- */
-OO.ui.RadioInputWidget.prototype.getInputElement = function () {
-       return $( '<input type="radio" />' );
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.RadioInputWidget.prototype.onEdit = function () {
-       // RadioInputWidget doesn't track its state.
-};
-
-/**
- * Set selection state of this radio button.
- *
- * @param {boolean} state `true` for selected
- * @chainable
- */
-OO.ui.RadioInputWidget.prototype.setSelected = function ( state ) {
-       // RadioInputWidget doesn't track its state.
-       this.$input.prop( 'checked', state );
-       return this;
-};
-
-/**
- * Check if this radio button is selected.
- *
- * @return {boolean} Radio is selected
- */
-OO.ui.RadioInputWidget.prototype.isSelected = function () {
-       return this.$input.prop( 'checked' );
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.RadioInputWidget.prototype.restorePreInfuseState = function ( state ) {
-       OO.ui.RadioInputWidget.parent.prototype.restorePreInfuseState.call( this, state );
-       if ( state.checked !== undefined && state.checked !== this.isSelected() ) {
-               this.setSelected( state.checked );
-       }
-};
-
-/**
- * RadioSelectInputWidget is a {@link OO.ui.RadioSelectWidget RadioSelectWidget} intended to be used
- * within a HTML form, such as a OO.ui.FormLayout. The selected value is synchronized with the value
- * of a hidden HTML `input` tag. Please see the [OOjs UI documentation on MediaWiki][1] for
- * more information about input widgets.
- *
- * This and OO.ui.DropdownInputWidget support the same configuration options.
- *
- *     @example
- *     // Example: A RadioSelectInputWidget with three options
- *     var radioSelectInput = new OO.ui.RadioSelectInputWidget( {
- *         options: [
- *             { data: 'a', label: 'First' },
- *             { data: 'b', label: 'Second'},
- *             { data: 'c', label: 'Third' }
- *         ]
- *     } );
- *     $( 'body' ).append( radioSelectInput.$element );
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Inputs
- *
- * @class
- * @extends OO.ui.InputWidget
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {Object[]} [options=[]] Array of menu options in the format `{ data: …, label: … }`
- */
-OO.ui.RadioSelectInputWidget = function OoUiRadioSelectInputWidget( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Properties (must be done before parent constructor which calls #setDisabled)
-       this.radioSelectWidget = new OO.ui.RadioSelectWidget();
-
-       // Parent constructor
-       OO.ui.RadioSelectInputWidget.parent.call( this, config );
-
-       // Events
-       this.radioSelectWidget.connect( this, { select: 'onMenuSelect' } );
-
-       // Initialization
-       this.setOptions( config.options || [] );
-       this.$element
-               .addClass( 'oo-ui-radioSelectInputWidget' )
-               .append( this.radioSelectWidget.$element );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.RadioSelectInputWidget, OO.ui.InputWidget );
-
-/* Static Properties */
-
-OO.ui.RadioSelectInputWidget.static.supportsSimpleLabel = false;
-
-/* Static Methods */
-
-/**
- * @inheritdoc
- */
-OO.ui.RadioSelectInputWidget.static.gatherPreInfuseState = function ( node, config ) {
-       var state = OO.ui.RadioSelectInputWidget.parent.static.gatherPreInfuseState( node, config );
-       state.value = $( node ).find( '.oo-ui-radioInputWidget .oo-ui-inputWidget-input:checked' ).val();
-       return state;
-};
-
-/* Methods */
-
-/**
- * @inheritdoc
- * @protected
- */
-OO.ui.RadioSelectInputWidget.prototype.getInputElement = function () {
-       return $( '<input type="hidden">' );
-};
-
-/**
- * Handles menu select events.
- *
- * @private
- * @param {OO.ui.RadioOptionWidget} item Selected menu item
- */
-OO.ui.RadioSelectInputWidget.prototype.onMenuSelect = function ( item ) {
-       this.setValue( item.getData() );
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.RadioSelectInputWidget.prototype.setValue = function ( value ) {
-       value = this.cleanUpValue( value );
-       this.radioSelectWidget.selectItemByData( value );
-       OO.ui.RadioSelectInputWidget.parent.prototype.setValue.call( this, value );
-       return this;
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.RadioSelectInputWidget.prototype.setDisabled = function ( state ) {
-       this.radioSelectWidget.setDisabled( state );
-       OO.ui.RadioSelectInputWidget.parent.prototype.setDisabled.call( this, state );
-       return this;
-};
-
-/**
- * Set the options available for this input.
- *
- * @param {Object[]} options Array of menu options in the format `{ data: …, label: … }`
- * @chainable
- */
-OO.ui.RadioSelectInputWidget.prototype.setOptions = function ( options ) {
-       var
-               value = this.getValue(),
-               widget = this;
-
-       // Rebuild the radioSelect menu
-       this.radioSelectWidget
-               .clearItems()
-               .addItems( options.map( function ( opt ) {
-                       var optValue = widget.cleanUpValue( opt.data );
-                       return new OO.ui.RadioOptionWidget( {
-                               data: optValue,
-                               label: opt.label !== undefined ? opt.label : optValue
-                       } );
-               } ) );
-
-       // Restore the previous value, or reset to something sensible
-       if ( this.radioSelectWidget.getItemFromData( value ) ) {
-               // Previous value is still available, ensure consistency with the radioSelect
-               this.setValue( value );
-       } else {
-               // No longer valid, reset
-               if ( options.length ) {
-                       this.setValue( options[ 0 ].data );
-               }
-       }
-
-       return this;
-};
-
-/**
- * TextInputWidgets, like HTML text inputs, can be configured with options that customize the
- * size of the field as well as its presentation. In addition, these widgets can be configured
- * with {@link OO.ui.mixin.IconElement icons}, {@link OO.ui.mixin.IndicatorElement indicators}, an optional
- * validation-pattern (used to determine if an input value is valid or not) and an input filter,
- * which modifies incoming values rather than validating them.
- * Please see the [OOjs UI documentation on MediaWiki] [1] for more information and examples.
- *
- * This widget can be used inside a HTML form, such as a OO.ui.FormLayout.
- *
- *     @example
- *     // Example of a text input widget
- *     var textInput = new OO.ui.TextInputWidget( {
- *         value: 'Text input'
- *     } )
- *     $( 'body' ).append( textInput.$element );
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Inputs
- *
- * @class
- * @extends OO.ui.InputWidget
- * @mixins OO.ui.mixin.IconElement
- * @mixins OO.ui.mixin.IndicatorElement
- * @mixins OO.ui.mixin.PendingElement
- * @mixins OO.ui.mixin.LabelElement
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {string} [type='text'] The value of the HTML `type` attribute: 'text', 'password', 'search',
- *  'email' or 'url'. Ignored if `multiline` is true.
- *
- *  Some values of `type` result in additional behaviors:
- *
- *  - `search`: implies `icon: 'search'` and `indicator: 'clear'`; when clicked, the indicator
- *    empties the text field
- * @cfg {string} [placeholder] Placeholder text
- * @cfg {boolean} [autofocus=false] Use an HTML `autofocus` attribute to
- *  instruct the browser to focus this widget.
- * @cfg {boolean} [readOnly=false] Prevent changes to the value of the text input.
- * @cfg {number} [maxLength] Maximum number of characters allowed in the input.
- * @cfg {boolean} [multiline=false] Allow multiple lines of text
- * @cfg {number} [rows] If multiline, number of visible lines in textarea. If used with `autosize`,
- *  specifies minimum number of rows to display.
- * @cfg {boolean} [autosize=false] Automatically resize the text input to fit its content.
- *  Use the #maxRows config to specify a maximum number of displayed rows.
- * @cfg {boolean} [maxRows] Maximum number of rows to display when #autosize is set to true.
- *  Defaults to the maximum of `10` and `2 * rows`, or `10` if `rows` isn't provided.
- * @cfg {string} [labelPosition='after'] The position of the inline label relative to that of
- *  the value or placeholder text: `'before'` or `'after'`
- * @cfg {boolean} [required=false] Mark the field as required. Implies `indicator: 'required'`.
- * @cfg {boolean} [autocomplete=true] Should the browser support autocomplete for this field
- * @cfg {RegExp|Function|string} [validate] Validation pattern: when string, a symbolic name of a
- *  pattern defined by the class: 'non-empty' (the value cannot be an empty string) or 'integer'
- *  (the value must contain only numbers); when RegExp, a regular expression that must match the
- *  value for it to be considered valid; when Function, a function receiving the value as parameter
- *  that must return true, or promise resolving to true, for it to be considered valid.
- */
-OO.ui.TextInputWidget = function OoUiTextInputWidget( config ) {
-       // Configuration initialization
-       config = $.extend( {
-               type: 'text',
-               labelPosition: 'after'
-       }, config );
-       if ( config.type === 'search' ) {
-               if ( config.icon === undefined ) {
-                       config.icon = 'search';
-               }
-               // indicator: 'clear' is set dynamically later, depending on value
-       }
-       if ( config.required ) {
-               if ( config.indicator === undefined ) {
-                       config.indicator = 'required';
-               }
-       }
-
-       // Parent constructor
-       OO.ui.TextInputWidget.parent.call( this, config );
-
-       // Mixin constructors
-       OO.ui.mixin.IconElement.call( this, config );
-       OO.ui.mixin.IndicatorElement.call( this, config );
-       OO.ui.mixin.PendingElement.call( this, $.extend( {}, config, { $pending: this.$input } ) );
-       OO.ui.mixin.LabelElement.call( this, config );
-
-       // Properties
-       this.type = this.getSaneType( config );
-       this.readOnly = false;
-       this.multiline = !!config.multiline;
-       this.autosize = !!config.autosize;
-       this.minRows = config.rows !== undefined ? config.rows : '';
-       this.maxRows = config.maxRows || Math.max( 2 * ( this.minRows || 0 ), 10 );
-       this.validate = null;
-       this.styleHeight = null;
-       this.scrollWidth = null;
-
-       // Clone for resizing
-       if ( this.autosize ) {
-               this.$clone = this.$input
-                       .clone()
-                       .insertAfter( this.$input )
-                       .attr( 'aria-hidden', 'true' )
-                       .addClass( 'oo-ui-element-hidden' );
-       }
-
-       this.setValidation( config.validate );
-       this.setLabelPosition( config.labelPosition );
-
-       // Events
-       this.$input.on( {
-               keypress: this.onKeyPress.bind( this ),
-               blur: this.onBlur.bind( this )
-       } );
-       this.$input.one( {
-               focus: this.onElementAttach.bind( this )
-       } );
-       this.$icon.on( 'mousedown', this.onIconMouseDown.bind( this ) );
-       this.$indicator.on( 'mousedown', this.onIndicatorMouseDown.bind( this ) );
-       this.on( 'labelChange', this.updatePosition.bind( this ) );
-       this.connect( this, {
-               change: 'onChange',
-               disable: 'onDisable'
-       } );
-
-       // Initialization
-       this.$element
-               .addClass( 'oo-ui-textInputWidget oo-ui-textInputWidget-type-' + this.type )
-               .append( this.$icon, this.$indicator );
-       this.setReadOnly( !!config.readOnly );
-       this.updateSearchIndicator();
-       if ( config.placeholder ) {
-               this.$input.attr( 'placeholder', config.placeholder );
-       }
-       if ( config.maxLength !== undefined ) {
-               this.$input.attr( 'maxlength', config.maxLength );
-       }
-       if ( config.autofocus ) {
-               this.$input.attr( 'autofocus', 'autofocus' );
-       }
-       if ( config.required ) {
-               this.$input.attr( 'required', 'required' );
-               this.$input.attr( 'aria-required', 'true' );
-       }
-       if ( config.autocomplete === false ) {
-               this.$input.attr( 'autocomplete', 'off' );
-               // Turning off autocompletion also disables "form caching" when the user navigates to a
-               // different page and then clicks "Back". Re-enable it when leaving. Borrowed from jQuery UI.
-               $( window ).on( {
-                       beforeunload: function () {
-                               this.$input.removeAttr( 'autocomplete' );
-                       }.bind( this ),
-                       pageshow: function () {
-                               // Browsers don't seem to actually fire this event on "Back", they instead just reload the
-                               // whole page... it shouldn't hurt, though.
-                               this.$input.attr( 'autocomplete', 'off' );
-                       }.bind( this )
-               } );
-       }
-       if ( this.multiline && config.rows ) {
-               this.$input.attr( 'rows', config.rows );
-       }
-       if ( this.label || config.autosize ) {
-               this.installParentChangeDetector();
-       }
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.TextInputWidget, OO.ui.InputWidget );
-OO.mixinClass( OO.ui.TextInputWidget, OO.ui.mixin.IconElement );
-OO.mixinClass( OO.ui.TextInputWidget, OO.ui.mixin.IndicatorElement );
-OO.mixinClass( OO.ui.TextInputWidget, OO.ui.mixin.PendingElement );
-OO.mixinClass( OO.ui.TextInputWidget, OO.ui.mixin.LabelElement );
-
-/* Static Properties */
-
-OO.ui.TextInputWidget.static.validationPatterns = {
-       'non-empty': /.+/,
-       integer: /^\d+$/
-};
-
-/* Static Methods */
-
-/**
- * @inheritdoc
- */
-OO.ui.TextInputWidget.static.gatherPreInfuseState = function ( node, config ) {
-       var state = OO.ui.TextInputWidget.parent.static.gatherPreInfuseState( node, config );
-       if ( config.multiline ) {
-               state.scrollTop = config.$input.scrollTop();
-       }
-       return state;
-};
-
-/* Events */
-
-/**
- * An `enter` event is emitted when the user presses 'enter' inside the text box.
- *
- * Not emitted if the input is multiline.
- *
- * @event enter
- */
-
-/**
- * A `resize` event is emitted when autosize is set and the widget resizes
- *
- * @event resize
- */
-
-/* Methods */
-
-/**
- * Handle icon mouse down events.
- *
- * @private
- * @param {jQuery.Event} e Mouse down event
- * @fires icon
- */
-OO.ui.TextInputWidget.prototype.onIconMouseDown = function ( e ) {
-       if ( e.which === OO.ui.MouseButtons.LEFT ) {
-               this.$input[ 0 ].focus();
-               return false;
-       }
-};
-
-/**
- * Handle indicator mouse down events.
- *
- * @private
- * @param {jQuery.Event} e Mouse down event
- * @fires indicator
- */
-OO.ui.TextInputWidget.prototype.onIndicatorMouseDown = function ( e ) {
-       if ( e.which === OO.ui.MouseButtons.LEFT ) {
-               if ( this.type === 'search' ) {
-                       // Clear the text field
-                       this.setValue( '' );
-               }
-               this.$input[ 0 ].focus();
-               return false;
-       }
-};
-
-/**
- * Handle key press events.
- *
- * @private
- * @param {jQuery.Event} e Key press event
- * @fires enter If enter key is pressed and input is not multiline
- */
-OO.ui.TextInputWidget.prototype.onKeyPress = function ( e ) {
-       if ( e.which === OO.ui.Keys.ENTER && !this.multiline ) {
-               this.emit( 'enter', e );
-       }
-};
-
-/**
- * Handle blur events.
- *
- * @private
- * @param {jQuery.Event} e Blur event
- */
-OO.ui.TextInputWidget.prototype.onBlur = function () {
-       this.setValidityFlag();
-};
-
-/**
- * Handle element attach events.
- *
- * @private
- * @param {jQuery.Event} e Element attach event
- */
-OO.ui.TextInputWidget.prototype.onElementAttach = function () {
-       // Any previously calculated size is now probably invalid if we reattached elsewhere
-       this.valCache = null;
-       this.adjustSize();
-       this.positionLabel();
-};
-
-/**
- * Handle change events.
- *
- * @param {string} value
- * @private
- */
-OO.ui.TextInputWidget.prototype.onChange = function () {
-       this.updateSearchIndicator();
-       this.setValidityFlag();
-       this.adjustSize();
-};
-
-/**
- * Handle disable events.
- *
- * @param {boolean} disabled Element is disabled
- * @private
- */
-OO.ui.TextInputWidget.prototype.onDisable = function () {
-       this.updateSearchIndicator();
-};
-
-/**
- * Check if the input is {@link #readOnly read-only}.
- *
- * @return {boolean}
- */
-OO.ui.TextInputWidget.prototype.isReadOnly = function () {
-       return this.readOnly;
-};
-
-/**
- * Set the {@link #readOnly read-only} state of the input.
- *
- * @param {boolean} state Make input read-only
- * @chainable
- */
-OO.ui.TextInputWidget.prototype.setReadOnly = function ( state ) {
-       this.readOnly = !!state;
-       this.$input.prop( 'readOnly', this.readOnly );
-       this.updateSearchIndicator();
-       return this;
-};
-
-/**
- * Support function for making #onElementAttach work across browsers.
- *
- * This whole function could be replaced with one line of code using the DOMNodeInsertedIntoDocument
- * event, but it's not supported by Firefox and allegedly deprecated, so we only use it as fallback.
- *
- * Due to MutationObserver performance woes, #onElementAttach is only somewhat reliably called the
- * first time that the element gets attached to the documented.
- */
-OO.ui.TextInputWidget.prototype.installParentChangeDetector = function () {
-       var mutationObserver, onRemove, topmostNode, fakeParentNode,
-               MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver,
-               widget = this;
-
-       if ( MutationObserver ) {
-               // The new way. If only it wasn't so ugly.
-
-               if ( this.$element.closest( 'html' ).length ) {
-                       // Widget is attached already, do nothing. This breaks the functionality of this function when
-                       // the widget is detached and reattached. Alas, doing this correctly with MutationObserver
-                       // would require observation of the whole document, which would hurt performance of other,
-                       // more important code.
-                       return;
-               }
-
-               // Find topmost node in the tree
-               topmostNode = this.$element[ 0 ];
-               while ( topmostNode.parentNode ) {
-                       topmostNode = topmostNode.parentNode;
-               }
-
-               // We have no way to detect the $element being attached somewhere without observing the entire
-               // DOM with subtree modifications, which would hurt performance. So we cheat: we hook to the
-               // parent node of $element, and instead detect when $element is removed from it (and thus
-               // probably attached somewhere else). If there is no parent, we create a "fake" one. If it
-               // doesn't get attached, we end up back here and create the parent.
-
-               mutationObserver = new MutationObserver( function ( mutations ) {
-                       var i, j, removedNodes;
-                       for ( i = 0; i < mutations.length; i++ ) {
-                               removedNodes = mutations[ i ].removedNodes;
-                               for ( j = 0; j < removedNodes.length; j++ ) {
-                                       if ( removedNodes[ j ] === topmostNode ) {
-                                               setTimeout( onRemove, 0 );
-                                               return;
-                                       }
-                               }
-                       }
-               } );
-
-               onRemove = function () {
-                       // If the node was attached somewhere else, report it
-                       if ( widget.$element.closest( 'html' ).length ) {
-                               widget.onElementAttach();
-                       }
-                       mutationObserver.disconnect();
-                       widget.installParentChangeDetector();
-               };
-
-               // Create a fake parent and observe it
-               fakeParentNode = $( '<div>' ).append( topmostNode )[ 0 ];
-               mutationObserver.observe( fakeParentNode, { childList: true } );
-       } else {
-               // Using the DOMNodeInsertedIntoDocument event is much nicer and less magical, and works for
-               // detachment and reattachment, but it's not supported by Firefox and allegedly deprecated.
-               this.$element.on( 'DOMNodeInsertedIntoDocument', this.onElementAttach.bind( this ) );
-       }
-};
-
-/**
- * Automatically adjust the size of the text input.
- *
- * This only affects #multiline inputs that are {@link #autosize autosized}.
- *
- * @chainable
- * @fires resize
- */
-OO.ui.TextInputWidget.prototype.adjustSize = function () {
-       var scrollHeight, innerHeight, outerHeight, maxInnerHeight, measurementError,
-               idealHeight, newHeight, scrollWidth, property;
-
-       if ( this.multiline && this.$input.val() !== this.valCache ) {
-               if ( this.autosize ) {
-                       this.$clone
-                               .val( this.$input.val() )
-                               .attr( 'rows', this.minRows )
-                               // Set inline height property to 0 to measure scroll height
-                               .css( 'height', 0 );
-
-                       this.$clone.removeClass( 'oo-ui-element-hidden' );
-
-                       this.valCache = this.$input.val();
-
-                       scrollHeight = this.$clone[ 0 ].scrollHeight;
-
-                       // Remove inline height property to measure natural heights
-                       this.$clone.css( 'height', '' );
-                       innerHeight = this.$clone.innerHeight();
-                       outerHeight = this.$clone.outerHeight();
-
-                       // Measure max rows height
-                       this.$clone
-                               .attr( 'rows', this.maxRows )
-                               .css( 'height', 'auto' )
-                               .val( '' );
-                       maxInnerHeight = this.$clone.innerHeight();
-
-                       // Difference between reported innerHeight and scrollHeight with no scrollbars present
-                       // Equals 1 on Blink-based browsers and 0 everywhere else
-                       measurementError = maxInnerHeight - this.$clone[ 0 ].scrollHeight;
-                       idealHeight = Math.min( maxInnerHeight, scrollHeight + measurementError );
-
-                       this.$clone.addClass( 'oo-ui-element-hidden' );
-
-                       // Only apply inline height when expansion beyond natural height is needed
-                       // Use the difference between the inner and outer height as a buffer
-                       newHeight = idealHeight > innerHeight ? idealHeight + ( outerHeight - innerHeight ) : '';
-                       if ( newHeight !== this.styleHeight ) {
-                               this.$input.css( 'height', newHeight );
-                               this.styleHeight = newHeight;
-                               this.emit( 'resize' );
-                       }
-               }
-               scrollWidth = this.$input[ 0 ].offsetWidth - this.$input[ 0 ].clientWidth;
-               if ( scrollWidth !== this.scrollWidth ) {
-                       property = this.$element.css( 'direction' ) === 'rtl' ? 'left' : 'right';
-                       // Reset
-                       this.$label.css( { right: '', left: '' } );
-                       this.$indicator.css( { right: '', left: '' } );
-
-                       if ( scrollWidth ) {
-                               this.$indicator.css( property, scrollWidth );
-                               if ( this.labelPosition === 'after' ) {
-                                       this.$label.css( property, scrollWidth );
-                               }
-                       }
-
-                       this.scrollWidth = scrollWidth;
-                       this.positionLabel();
-               }
-       }
-       return this;
-};
-
-/**
- * @inheritdoc
- * @protected
- */
-OO.ui.TextInputWidget.prototype.getInputElement = function ( config ) {
-       return config.multiline ?
-               $( '<textarea>' ) :
-               $( '<input type="' + this.getSaneType( config ) + '" />' );
-};
-
-/**
- * Get sanitized value for 'type' for given config.
- *
- * @param {Object} config Configuration options
- * @return {string|null}
- * @private
- */
-OO.ui.TextInputWidget.prototype.getSaneType = function ( config ) {
-       var type = [ 'text', 'password', 'search', 'email', 'url' ].indexOf( config.type ) !== -1 ?
-               config.type :
-               'text';
-       return config.multiline ? 'multiline' : type;
-};
-
-/**
- * Check if the input supports multiple lines.
- *
- * @return {boolean}
- */
-OO.ui.TextInputWidget.prototype.isMultiline = function () {
-       return !!this.multiline;
-};
-
-/**
- * Check if the input automatically adjusts its size.
- *
- * @return {boolean}
- */
-OO.ui.TextInputWidget.prototype.isAutosizing = function () {
-       return !!this.autosize;
-};
-
-/**
- * Focus the input and select a specified range within the text.
- *
- * @param {number} from Select from offset
- * @param {number} [to] Select to offset, defaults to from
- * @chainable
- */
-OO.ui.TextInputWidget.prototype.selectRange = function ( from, to ) {
-       var isBackwards, start, end,
-               input = this.$input[ 0 ];
-
-       to = to || from;
-
-       isBackwards = to < from;
-       start = isBackwards ? to : from;
-       end = isBackwards ? from : to;
-
-       this.focus();
-
-       input.setSelectionRange( start, end, isBackwards ? 'backward' : 'forward' );
-       return this;
-};
-
-/**
- * Get an object describing the current selection range in a directional manner
- *
- * @return {Object} Object containing 'from' and 'to' offsets
- */
-OO.ui.TextInputWidget.prototype.getRange = function () {
-       var input = this.$input[ 0 ],
-               start = input.selectionStart,
-               end = input.selectionEnd,
-               isBackwards = input.selectionDirection === 'backward';
-
-       return {
-               from: isBackwards ? end : start,
-               to: isBackwards ? start : end
-       };
-};
-
-/**
- * Get the length of the text input value.
- *
- * This could differ from the length of #getValue if the
- * value gets filtered
- *
- * @return {number} Input length
- */
-OO.ui.TextInputWidget.prototype.getInputLength = function () {
-       return this.$input[ 0 ].value.length;
-};
-
-/**
- * Focus the input and select the entire text.
- *
- * @chainable
- */
-OO.ui.TextInputWidget.prototype.select = function () {
-       return this.selectRange( 0, this.getInputLength() );
-};
-
-/**
- * Focus the input and move the cursor to the start.
- *
- * @chainable
- */
-OO.ui.TextInputWidget.prototype.moveCursorToStart = function () {
-       return this.selectRange( 0 );
-};
-
-/**
- * Focus the input and move the cursor to the end.
- *
- * @chainable
- */
-OO.ui.TextInputWidget.prototype.moveCursorToEnd = function () {
-       return this.selectRange( this.getInputLength() );
-};
-
-/**
- * Insert new content into the input.
- *
- * @param {string} content Content to be inserted
- * @chainable
- */
-OO.ui.TextInputWidget.prototype.insertContent = function ( content ) {
-       var start, end,
-               range = this.getRange(),
-               value = this.getValue();
-
-       start = Math.min( range.from, range.to );
-       end = Math.max( range.from, range.to );
-
-       this.setValue( value.slice( 0, start ) + content + value.slice( end ) );
-       this.selectRange( start + content.length );
-       return this;
-};
-
-/**
- * Insert new content either side of a selection.
- *
- * @param {string} pre Content to be inserted before the selection
- * @param {string} post Content to be inserted after the selection
- * @chainable
- */
-OO.ui.TextInputWidget.prototype.encapsulateContent = function ( pre, post ) {
-       var start, end,
-               range = this.getRange(),
-               offset = pre.length;
-
-       start = Math.min( range.from, range.to );
-       end = Math.max( range.from, range.to );
-
-       this.selectRange( start ).insertContent( pre );
-       this.selectRange( offset + end ).insertContent( post );
-
-       this.selectRange( offset + start, offset + end );
-       return this;
-};
-
-/**
- * Set the validation pattern.
- *
- * The validation pattern is either a regular expression, a function, or the symbolic name of a
- * pattern defined by the class: 'non-empty' (the value cannot be an empty string) or 'integer' (the
- * value must contain only numbers).
- *
- * @param {RegExp|Function|string|null} validate Regular expression, function, or the symbolic name
- *  of a pattern (either ‘integer’ or ‘non-empty’) defined by the class.
- */
-OO.ui.TextInputWidget.prototype.setValidation = function ( validate ) {
-       if ( validate instanceof RegExp || validate instanceof Function ) {
-               this.validate = validate;
-       } else {
-               this.validate = this.constructor.static.validationPatterns[ validate ] || /.*/;
-       }
-};
-
-/**
- * Sets the 'invalid' flag appropriately.
- *
- * @param {boolean} [isValid] Optionally override validation result
- */
-OO.ui.TextInputWidget.prototype.setValidityFlag = function ( isValid ) {
-       var widget = this,
-               setFlag = function ( valid ) {
-                       if ( !valid ) {
-                               widget.$input.attr( 'aria-invalid', 'true' );
-                       } else {
-                               widget.$input.removeAttr( 'aria-invalid' );
-                       }
-                       widget.setFlags( { invalid: !valid } );
-               };
-
-       if ( isValid !== undefined ) {
-               setFlag( isValid );
-       } else {
-               this.getValidity().then( function () {
-                       setFlag( true );
-               }, function () {
-                       setFlag( false );
-               } );
-       }
-};
-
-/**
- * Check if a value is valid.
- *
- * This method returns a promise that resolves with a boolean `true` if the current value is
- * considered valid according to the supplied {@link #validate validation pattern}.
- *
- * @deprecated
- * @return {jQuery.Promise} A promise that resolves to a boolean `true` if the value is valid.
- */
-OO.ui.TextInputWidget.prototype.isValid = function () {
-       var result;
-
-       if ( this.validate instanceof Function ) {
-               result = this.validate( this.getValue() );
-               if ( result && $.isFunction( result.promise ) ) {
-                       return result.promise();
-               } else {
-                       return $.Deferred().resolve( !!result ).promise();
-               }
-       } else {
-               return $.Deferred().resolve( !!this.getValue().match( this.validate ) ).promise();
-       }
-};
-
-/**
- * Get the validity of current value.
- *
- * This method returns a promise that resolves if the value is valid and rejects if
- * it isn't. Uses the {@link #validate validation pattern}  to check for validity.
- *
- * @return {jQuery.Promise} A promise that resolves if the value is valid, rejects if not.
- */
-OO.ui.TextInputWidget.prototype.getValidity = function () {
-       var result;
-
-       function rejectOrResolve( valid ) {
-               if ( valid ) {
-                       return $.Deferred().resolve().promise();
-               } else {
-                       return $.Deferred().reject().promise();
-               }
-       }
-
-       if ( this.validate instanceof Function ) {
-               result = this.validate( this.getValue() );
-               if ( result && $.isFunction( result.promise ) ) {
-                       return result.promise().then( function ( valid ) {
-                               return rejectOrResolve( valid );
-                       } );
-               } else {
-                       return rejectOrResolve( result );
-               }
-       } else {
-               return rejectOrResolve( this.getValue().match( this.validate ) );
-       }
-};
-
-/**
- * Set the position of the inline label relative to that of the value: `‘before’` or `‘after’`.
- *
- * @param {string} labelPosition Label position, 'before' or 'after'
- * @chainable
- */
-OO.ui.TextInputWidget.prototype.setLabelPosition = function ( labelPosition ) {
-       this.labelPosition = labelPosition;
-       this.updatePosition();
-       return this;
-};
-
-/**
- * Update the position of the inline label.
- *
- * This method is called by #setLabelPosition, and can also be called on its own if
- * something causes the label to be mispositioned.
- *
- * @chainable
- */
-OO.ui.TextInputWidget.prototype.updatePosition = function () {
-       var after = this.labelPosition === 'after';
-
-       this.$element
-               .toggleClass( 'oo-ui-textInputWidget-labelPosition-after', !!this.label && after )
-               .toggleClass( 'oo-ui-textInputWidget-labelPosition-before', !!this.label && !after );
-
-       this.valCache = null;
-       this.scrollWidth = null;
-       this.adjustSize();
-       this.positionLabel();
-
-       return this;
-};
-
-/**
- * Update the 'clear' indicator displayed on type: 'search' text fields, hiding it when the field is
- * already empty or when it's not editable.
- */
-OO.ui.TextInputWidget.prototype.updateSearchIndicator = function () {
-       if ( this.type === 'search' ) {
-               if ( this.getValue() === '' || this.isDisabled() || this.isReadOnly() ) {
-                       this.setIndicator( null );
-               } else {
-                       this.setIndicator( 'clear' );
-               }
-       }
-};
-
-/**
- * Position the label by setting the correct padding on the input.
- *
- * @private
- * @chainable
- */
-OO.ui.TextInputWidget.prototype.positionLabel = function () {
-       var after, rtl, property;
-       // Clear old values
-       this.$input
-               // Clear old values if present
-               .css( {
-                       'padding-right': '',
-                       'padding-left': ''
-               } );
-
-       if ( this.label ) {
-               this.$element.append( this.$label );
-       } else {
-               this.$label.detach();
-               return;
-       }
-
-       after = this.labelPosition === 'after';
-       rtl = this.$element.css( 'direction' ) === 'rtl';
-       property = after === rtl ? 'padding-left' : 'padding-right';
-
-       this.$input.css( property, this.$label.outerWidth( true ) + ( after ? this.scrollWidth : 0 ) );
-
-       return this;
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.TextInputWidget.prototype.restorePreInfuseState = function ( state ) {
-       OO.ui.TextInputWidget.parent.prototype.restorePreInfuseState.call( this, state );
-       if ( state.scrollTop !== undefined ) {
-               this.$input.scrollTop( state.scrollTop );
-       }
-};
-
-/**
- * ComboBoxInputWidgets combine a {@link OO.ui.TextInputWidget text input} (where a value
- * can be entered manually) and a {@link OO.ui.MenuSelectWidget menu of options} (from which
- * a value can be chosen instead). Users can choose options from the combo box in one of two ways:
- *
- * - by typing a value in the text input field. If the value exactly matches the value of a menu
- *   option, that option will appear to be selected.
- * - by choosing a value from the menu. The value of the chosen option will then appear in the text
- *   input field.
- *
- * This widget can be used inside a HTML form, such as a OO.ui.FormLayout.
- *
- * For more information about menus and options, please see the [OOjs UI documentation on MediaWiki][1].
- *
- *     @example
- *     // Example: A ComboBoxInputWidget.
- *     var comboBox = new OO.ui.ComboBoxInputWidget( {
- *         label: 'ComboBoxInputWidget',
- *         value: 'Option 1',
- *         menu: {
- *             items: [
- *                 new OO.ui.MenuOptionWidget( {
- *                     data: 'Option 1',
- *                     label: 'Option One'
- *                 } ),
- *                 new OO.ui.MenuOptionWidget( {
- *                     data: 'Option 2',
- *                     label: 'Option Two'
- *                 } ),
- *                 new OO.ui.MenuOptionWidget( {
- *                     data: 'Option 3',
- *                     label: 'Option Three'
- *                 } ),
- *                 new OO.ui.MenuOptionWidget( {
- *                     data: 'Option 4',
- *                     label: 'Option Four'
- *                 } ),
- *                 new OO.ui.MenuOptionWidget( {
- *                     data: 'Option 5',
- *                     label: 'Option Five'
- *                 } )
- *             ]
- *         }
- *     } );
- *     $( 'body' ).append( comboBox.$element );
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options#Menu_selects_and_options
- *
- * @class
- * @extends OO.ui.TextInputWidget
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {Object[]} [options=[]] Array of menu options in the format `{ data: …, label: … }`
- * @cfg {Object} [menu] Configuration options to pass to the {@link OO.ui.FloatingMenuSelectWidget menu select widget}.
- * @cfg {jQuery} [$overlay] Render the menu into a separate layer. This configuration is useful in cases where
- *  the expanded menu is larger than its containing `<div>`. The specified overlay layer is usually on top of the
- *  containing `<div>` and has a larger area. By default, the menu uses relative positioning.
- */
-OO.ui.ComboBoxInputWidget = function OoUiComboBoxInputWidget( config ) {
-       // Configuration initialization
-       config = $.extend( {
-               indicator: 'down'
-       }, config );
-       // For backwards-compatibility with ComboBoxWidget config
-       $.extend( config, config.input );
-
-       // Parent constructor
-       OO.ui.ComboBoxInputWidget.parent.call( this, config );
-
-       // Properties
-       this.$overlay = config.$overlay || this.$element;
-       this.menu = new OO.ui.FloatingMenuSelectWidget( $.extend(
-               {
-                       widget: this,
-                       input: this,
-                       $container: this.$element,
-                       disabled: this.isDisabled()
-               },
-               config.menu
-       ) );
-       // For backwards-compatibility with ComboBoxWidget
-       this.input = this;
-
-       // Events
-       this.$indicator.on( {
-               click: this.onIndicatorClick.bind( this ),
-               keypress: this.onIndicatorKeyPress.bind( this )
-       } );
-       this.connect( this, {
-               change: 'onInputChange',
-               enter: 'onInputEnter'
-       } );
-       this.menu.connect( this, {
-               choose: 'onMenuChoose',
-               add: 'onMenuItemsChange',
-               remove: 'onMenuItemsChange'
-       } );
-
-       // Initialization
-       this.$input.attr( {
-               role: 'combobox',
-               'aria-autocomplete': 'list'
-       } );
-       // Do not override options set via config.menu.items
-       if ( config.options !== undefined ) {
-               this.setOptions( config.options );
-       }
-       // Extra class for backwards-compatibility with ComboBoxWidget
-       this.$element.addClass( 'oo-ui-comboBoxInputWidget oo-ui-comboBoxWidget' );
-       this.$overlay.append( this.menu.$element );
-       this.onMenuItemsChange();
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.ComboBoxInputWidget, OO.ui.TextInputWidget );
-
-/* Methods */
-
-/**
- * Get the combobox's menu.
- * @return {OO.ui.FloatingMenuSelectWidget} Menu widget
- */
-OO.ui.ComboBoxInputWidget.prototype.getMenu = function () {
-       return this.menu;
-};
-
-/**
- * Get the combobox's text input widget.
- * @return {OO.ui.TextInputWidget} Text input widget
- */
-OO.ui.ComboBoxInputWidget.prototype.getInput = function () {
-       return this;
-};
-
-/**
- * Handle input change events.
- *
- * @private
- * @param {string} value New value
- */
-OO.ui.ComboBoxInputWidget.prototype.onInputChange = function ( value ) {
-       var match = this.menu.getItemFromData( value );
-
-       this.menu.selectItem( match );
-       if ( this.menu.getHighlightedItem() ) {
-               this.menu.highlightItem( match );
-       }
-
-       if ( !this.isDisabled() ) {
-               this.menu.toggle( true );
-       }
-};
-
-/**
- * Handle mouse click events.
- *
- * @private
- * @param {jQuery.Event} e Mouse click event
- */
-OO.ui.ComboBoxInputWidget.prototype.onIndicatorClick = function ( e ) {
-       if ( !this.isDisabled() && e.which === OO.ui.MouseButtons.LEFT ) {
-               this.menu.toggle();
-               this.$input[ 0 ].focus();
-       }
-       return false;
-};
-
-/**
- * Handle key press events.
- *
- * @private
- * @param {jQuery.Event} e Key press event
- */
-OO.ui.ComboBoxInputWidget.prototype.onIndicatorKeyPress = function ( e ) {
-       if ( !this.isDisabled() && ( e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER ) ) {
-               this.menu.toggle();
-               this.$input[ 0 ].focus();
-               return false;
-       }
-};
-
-/**
- * Handle input enter events.
- *
- * @private
- */
-OO.ui.ComboBoxInputWidget.prototype.onInputEnter = function () {
-       if ( !this.isDisabled() ) {
-               this.menu.toggle( false );
-       }
-};
-
-/**
- * Handle menu choose events.
- *
- * @private
- * @param {OO.ui.OptionWidget} item Chosen item
- */
-OO.ui.ComboBoxInputWidget.prototype.onMenuChoose = function ( item ) {
-       this.setValue( item.getData() );
-};
-
-/**
- * Handle menu item change events.
- *
- * @private
- */
-OO.ui.ComboBoxInputWidget.prototype.onMenuItemsChange = function () {
-       var match = this.menu.getItemFromData( this.getValue() );
-       this.menu.selectItem( match );
-       if ( this.menu.getHighlightedItem() ) {
-               this.menu.highlightItem( match );
-       }
-       this.$element.toggleClass( 'oo-ui-comboBoxInputWidget-empty', this.menu.isEmpty() );
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.ComboBoxInputWidget.prototype.setDisabled = function ( disabled ) {
-       // Parent method
-       OO.ui.ComboBoxInputWidget.parent.prototype.setDisabled.call( this, disabled );
-
-       if ( this.menu ) {
-               this.menu.setDisabled( this.isDisabled() );
-       }
-
-       return this;
-};
-
-/**
- * Set the options available for this input.
- *
- * @param {Object[]} options Array of menu options in the format `{ data: …, label: … }`
- * @chainable
- */
-OO.ui.ComboBoxInputWidget.prototype.setOptions = function ( options ) {
-       this.getMenu()
-               .clearItems()
-               .addItems( options.map( function ( opt ) {
-                       return new OO.ui.MenuOptionWidget( {
-                               data: opt.data,
-                               label: opt.label !== undefined ? opt.label : opt.data
-                       } );
-               } ) );
-
-       return this;
-};
-
-/**
- * @class
- * @deprecated Use OO.ui.ComboBoxInputWidget instead.
- */
-OO.ui.ComboBoxWidget = OO.ui.ComboBoxInputWidget;
-
-/**
- * LabelWidgets help identify the function of interface elements. Each LabelWidget can
- * be configured with a `label` option that is set to a string, a label node, or a function:
- *
- * - String: a plaintext string
- * - jQuery selection: a jQuery selection, used for anything other than a plaintext label, e.g., a
- *   label that includes a link or special styling, such as a gray color or additional graphical elements.
- * - Function: a function that will produce a string in the future. Functions are used
- *   in cases where the value of the label is not currently defined.
- *
- * In addition, the LabelWidget can be associated with an {@link OO.ui.InputWidget input widget}, which
- * will come into focus when the label is clicked.
- *
- *     @example
- *     // Examples of LabelWidgets
- *     var label1 = new OO.ui.LabelWidget( {
- *         label: 'plaintext label'
- *     } );
- *     var label2 = new OO.ui.LabelWidget( {
- *         label: $( '<a href="default.html">jQuery label</a>' )
- *     } );
- *     // Create a fieldset layout with fields for each example
- *     var fieldset = new OO.ui.FieldsetLayout();
- *     fieldset.addItems( [
- *         new OO.ui.FieldLayout( label1 ),
- *         new OO.ui.FieldLayout( label2 )
- *     ] );
- *     $( 'body' ).append( fieldset.$element );
- *
- * @class
- * @extends OO.ui.Widget
- * @mixins OO.ui.mixin.LabelElement
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {OO.ui.InputWidget} [input] {@link OO.ui.InputWidget Input widget} that uses the label.
- *  Clicking the label will focus the specified input field.
- */
-OO.ui.LabelWidget = function OoUiLabelWidget( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Parent constructor
-       OO.ui.LabelWidget.parent.call( this, config );
-
-       // Mixin constructors
-       OO.ui.mixin.LabelElement.call( this, $.extend( {}, config, { $label: this.$element } ) );
-       OO.ui.mixin.TitledElement.call( this, config );
-
-       // Properties
-       this.input = config.input;
-
-       // Events
-       if ( this.input instanceof OO.ui.InputWidget ) {
-               this.$element.on( 'click', this.onClick.bind( this ) );
-       }
-
-       // Initialization
-       this.$element.addClass( 'oo-ui-labelWidget' );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.LabelWidget, OO.ui.Widget );
-OO.mixinClass( OO.ui.LabelWidget, OO.ui.mixin.LabelElement );
-OO.mixinClass( OO.ui.LabelWidget, OO.ui.mixin.TitledElement );
-
-/* Static Properties */
-
-OO.ui.LabelWidget.static.tagName = 'span';
-
-/* Methods */
-
-/**
- * Handles label mouse click events.
- *
- * @private
- * @param {jQuery.Event} e Mouse click event
- */
-OO.ui.LabelWidget.prototype.onClick = function () {
-       this.input.simulateLabelClick();
-       return false;
-};
-
-/**
- * OptionWidgets are special elements that can be selected and configured with data. The
- * data is often unique for each option, but it does not have to be. OptionWidgets are used
- * with OO.ui.SelectWidget to create a selection of mutually exclusive options. For more information
- * and examples, please see the [OOjs UI documentation on MediaWiki][1].
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options
- *
- * @class
- * @extends OO.ui.Widget
- * @mixins OO.ui.mixin.LabelElement
- * @mixins OO.ui.mixin.FlaggedElement
- *
- * @constructor
- * @param {Object} [config] Configuration options
- */
-OO.ui.OptionWidget = function OoUiOptionWidget( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Parent constructor
-       OO.ui.OptionWidget.parent.call( this, config );
-
-       // Mixin constructors
-       OO.ui.mixin.ItemWidget.call( this );
-       OO.ui.mixin.LabelElement.call( this, config );
-       OO.ui.mixin.FlaggedElement.call( this, config );
-
-       // Properties
-       this.selected = false;
-       this.highlighted = false;
-       this.pressed = false;
-
-       // Initialization
-       this.$element
-               .data( 'oo-ui-optionWidget', this )
-               .attr( 'role', 'option' )
-               .attr( 'aria-selected', 'false' )
-               .addClass( 'oo-ui-optionWidget' )
-               .append( this.$label );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.OptionWidget, OO.ui.Widget );
-OO.mixinClass( OO.ui.OptionWidget, OO.ui.mixin.ItemWidget );
-OO.mixinClass( OO.ui.OptionWidget, OO.ui.mixin.LabelElement );
-OO.mixinClass( OO.ui.OptionWidget, OO.ui.mixin.FlaggedElement );
-
-/* Static Properties */
-
-OO.ui.OptionWidget.static.selectable = true;
-
-OO.ui.OptionWidget.static.highlightable = true;
-
-OO.ui.OptionWidget.static.pressable = true;
-
-OO.ui.OptionWidget.static.scrollIntoViewOnSelect = false;
-
-/* Methods */
-
-/**
- * Check if the option can be selected.
- *
- * @return {boolean} Item is selectable
- */
-OO.ui.OptionWidget.prototype.isSelectable = function () {
-       return this.constructor.static.selectable && !this.isDisabled() && this.isVisible();
-};
-
-/**
- * Check if the option can be highlighted. A highlight indicates that the option
- * may be selected when a user presses enter or clicks. Disabled items cannot
- * be highlighted.
- *
- * @return {boolean} Item is highlightable
- */
-OO.ui.OptionWidget.prototype.isHighlightable = function () {
-       return this.constructor.static.highlightable && !this.isDisabled() && this.isVisible();
-};
-
-/**
- * Check if the option can be pressed. The pressed state occurs when a user mouses
- * down on an item, but has not yet let go of the mouse.
- *
- * @return {boolean} Item is pressable
- */
-OO.ui.OptionWidget.prototype.isPressable = function () {
-       return this.constructor.static.pressable && !this.isDisabled() && this.isVisible();
-};
-
-/**
- * Check if the option is selected.
- *
- * @return {boolean} Item is selected
- */
-OO.ui.OptionWidget.prototype.isSelected = function () {
-       return this.selected;
-};
-
-/**
- * Check if the option is highlighted. A highlight indicates that the
- * item may be selected when a user presses enter or clicks.
- *
- * @return {boolean} Item is highlighted
- */
-OO.ui.OptionWidget.prototype.isHighlighted = function () {
-       return this.highlighted;
-};
-
-/**
- * Check if the option is pressed. The pressed state occurs when a user mouses
- * down on an item, but has not yet let go of the mouse. The item may appear
- * selected, but it will not be selected until the user releases the mouse.
- *
- * @return {boolean} Item is pressed
- */
-OO.ui.OptionWidget.prototype.isPressed = function () {
-       return this.pressed;
-};
-
-/**
- * Set the option’s selected state. In general, all modifications to the selection
- * should be handled by the SelectWidget’s {@link OO.ui.SelectWidget#selectItem selectItem( [item] )}
- * method instead of this method.
- *
- * @param {boolean} [state=false] Select option
- * @chainable
- */
-OO.ui.OptionWidget.prototype.setSelected = function ( state ) {
-       if ( this.constructor.static.selectable ) {
-               this.selected = !!state;
-               this.$element
-                       .toggleClass( 'oo-ui-optionWidget-selected', state )
-                       .attr( 'aria-selected', state.toString() );
-               if ( state && this.constructor.static.scrollIntoViewOnSelect ) {
-                       this.scrollElementIntoView();
-               }
-               this.updateThemeClasses();
-       }
-       return this;
-};
-
-/**
- * Set the option’s highlighted state. In general, all programmatic
- * modifications to the highlight should be handled by the
- * SelectWidget’s {@link OO.ui.SelectWidget#highlightItem highlightItem( [item] )}
- * method instead of this method.
- *
- * @param {boolean} [state=false] Highlight option
- * @chainable
- */
-OO.ui.OptionWidget.prototype.setHighlighted = function ( state ) {
-       if ( this.constructor.static.highlightable ) {
-               this.highlighted = !!state;
-               this.$element.toggleClass( 'oo-ui-optionWidget-highlighted', state );
-               this.updateThemeClasses();
-       }
-       return this;
-};
-
-/**
- * Set the option’s pressed state. In general, all
- * programmatic modifications to the pressed state should be handled by the
- * SelectWidget’s {@link OO.ui.SelectWidget#pressItem pressItem( [item] )}
- * method instead of this method.
- *
- * @param {boolean} [state=false] Press option
- * @chainable
- */
-OO.ui.OptionWidget.prototype.setPressed = function ( state ) {
-       if ( this.constructor.static.pressable ) {
-               this.pressed = !!state;
-               this.$element.toggleClass( 'oo-ui-optionWidget-pressed', state );
-               this.updateThemeClasses();
-       }
-       return this;
-};
-
-/**
- * DecoratedOptionWidgets are {@link OO.ui.OptionWidget options} that can be configured
- * with an {@link OO.ui.mixin.IconElement icon} and/or {@link OO.ui.mixin.IndicatorElement indicator}.
- * This class is used with OO.ui.SelectWidget to create a selection of mutually exclusive
- * options. For more information about options and selects, please see the
- * [OOjs UI documentation on MediaWiki][1].
- *
- *     @example
- *     // Decorated options in a select widget
- *     var select = new OO.ui.SelectWidget( {
- *         items: [
- *             new OO.ui.DecoratedOptionWidget( {
- *                 data: 'a',
- *                 label: 'Option with icon',
- *                 icon: 'help'
- *             } ),
- *             new OO.ui.DecoratedOptionWidget( {
- *                 data: 'b',
- *                 label: 'Option with indicator',
- *                 indicator: 'next'
- *             } )
- *         ]
- *     } );
- *     $( 'body' ).append( select.$element );
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options
- *
- * @class
- * @extends OO.ui.OptionWidget
- * @mixins OO.ui.mixin.IconElement
- * @mixins OO.ui.mixin.IndicatorElement
- *
- * @constructor
- * @param {Object} [config] Configuration options
- */
-OO.ui.DecoratedOptionWidget = function OoUiDecoratedOptionWidget( config ) {
-       // Parent constructor
-       OO.ui.DecoratedOptionWidget.parent.call( this, config );
-
-       // Mixin constructors
-       OO.ui.mixin.IconElement.call( this, config );
-       OO.ui.mixin.IndicatorElement.call( this, config );
-
-       // Initialization
-       this.$element
-               .addClass( 'oo-ui-decoratedOptionWidget' )
-               .prepend( this.$icon )
-               .append( this.$indicator );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.DecoratedOptionWidget, OO.ui.OptionWidget );
-OO.mixinClass( OO.ui.DecoratedOptionWidget, OO.ui.mixin.IconElement );
-OO.mixinClass( OO.ui.DecoratedOptionWidget, OO.ui.mixin.IndicatorElement );
-
-/**
- * ButtonOptionWidget is a special type of {@link OO.ui.mixin.ButtonElement button element} that
- * can be selected and configured with data. The class is
- * used with OO.ui.ButtonSelectWidget to create a selection of button options. Please see the
- * [OOjs UI documentation on MediaWiki] [1] for more information.
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options#Button_selects_and_options
- *
- * @class
- * @extends OO.ui.DecoratedOptionWidget
- * @mixins OO.ui.mixin.ButtonElement
- * @mixins OO.ui.mixin.TabIndexedElement
- * @mixins OO.ui.mixin.TitledElement
- *
- * @constructor
- * @param {Object} [config] Configuration options
- */
-OO.ui.ButtonOptionWidget = function OoUiButtonOptionWidget( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Parent constructor
-       OO.ui.ButtonOptionWidget.parent.call( this, config );
-
-       // Mixin constructors
-       OO.ui.mixin.ButtonElement.call( this, config );
-       OO.ui.mixin.TitledElement.call( this, $.extend( {}, config, { $titled: this.$button } ) );
-       OO.ui.mixin.TabIndexedElement.call( this, $.extend( {}, config, {
-               $tabIndexed: this.$button,
-               tabIndex: -1
-       } ) );
-
-       // Initialization
-       this.$element.addClass( 'oo-ui-buttonOptionWidget' );
-       this.$button.append( this.$element.contents() );
-       this.$element.append( this.$button );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.ButtonOptionWidget, OO.ui.DecoratedOptionWidget );
-OO.mixinClass( OO.ui.ButtonOptionWidget, OO.ui.mixin.ButtonElement );
-OO.mixinClass( OO.ui.ButtonOptionWidget, OO.ui.mixin.TitledElement );
-OO.mixinClass( OO.ui.ButtonOptionWidget, OO.ui.mixin.TabIndexedElement );
-
-/* Static Properties */
-
-// Allow button mouse down events to pass through so they can be handled by the parent select widget
-OO.ui.ButtonOptionWidget.static.cancelButtonMouseDownEvents = false;
-
-OO.ui.ButtonOptionWidget.static.highlightable = false;
-
-/* Methods */
-
-/**
- * @inheritdoc
- */
-OO.ui.ButtonOptionWidget.prototype.setSelected = function ( state ) {
-       OO.ui.ButtonOptionWidget.parent.prototype.setSelected.call( this, state );
-
-       if ( this.constructor.static.selectable ) {
-               this.setActive( state );
-       }
-
-       return this;
-};
-
-/**
- * RadioOptionWidget is an option widget that looks like a radio button.
- * The class is used with OO.ui.RadioSelectWidget to create a selection of radio options.
- * Please see the [OOjs UI documentation on MediaWiki] [1] for more information.
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options#Button_selects_and_option
- *
- * @class
- * @extends OO.ui.OptionWidget
- *
- * @constructor
- * @param {Object} [config] Configuration options
- */
-OO.ui.RadioOptionWidget = function OoUiRadioOptionWidget( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Properties (must be done before parent constructor which calls #setDisabled)
-       this.radio = new OO.ui.RadioInputWidget( { value: config.data, tabIndex: -1 } );
-
-       // Parent constructor
-       OO.ui.RadioOptionWidget.parent.call( this, config );
-
-       // Events
-       this.radio.$input.on( 'focus', this.onInputFocus.bind( this ) );
-
-       // Initialization
-       // Remove implicit role, we're handling it ourselves
-       this.radio.$input.attr( 'role', 'presentation' );
-       this.$element
-               .addClass( 'oo-ui-radioOptionWidget' )
-               .attr( 'role', 'radio' )
-               .attr( 'aria-checked', 'false' )
-               .removeAttr( 'aria-selected' )
-               .prepend( this.radio.$element );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.RadioOptionWidget, OO.ui.OptionWidget );
-
-/* Static Properties */
-
-OO.ui.RadioOptionWidget.static.highlightable = false;
-
-OO.ui.RadioOptionWidget.static.scrollIntoViewOnSelect = true;
-
-OO.ui.RadioOptionWidget.static.pressable = false;
-
-OO.ui.RadioOptionWidget.static.tagName = 'label';
-
-/* Methods */
-
-/**
- * @param {jQuery.Event} e Focus event
- * @private
- */
-OO.ui.RadioOptionWidget.prototype.onInputFocus = function () {
-       this.radio.$input.blur();
-       this.$element.parent().focus();
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.RadioOptionWidget.prototype.setSelected = function ( state ) {
-       OO.ui.RadioOptionWidget.parent.prototype.setSelected.call( this, state );
-
-       this.radio.setSelected( state );
-       this.$element
-               .attr( 'aria-checked', state.toString() )
-               .removeAttr( 'aria-selected' );
-
-       return this;
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.RadioOptionWidget.prototype.setDisabled = function ( disabled ) {
-       OO.ui.RadioOptionWidget.parent.prototype.setDisabled.call( this, disabled );
-
-       this.radio.setDisabled( this.isDisabled() );
-
-       return this;
-};
-
-/**
- * MenuOptionWidget is an option widget that looks like a menu item. The class is used with
- * OO.ui.MenuSelectWidget to create a menu of mutually exclusive options. Please see
- * the [OOjs UI documentation on MediaWiki] [1] for more information.
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options#Menu_selects_and_options
- *
- * @class
- * @extends OO.ui.DecoratedOptionWidget
- *
- * @constructor
- * @param {Object} [config] Configuration options
- */
-OO.ui.MenuOptionWidget = function OoUiMenuOptionWidget( config ) {
-       // Configuration initialization
-       config = $.extend( { icon: 'check' }, config );
-
-       // Parent constructor
-       OO.ui.MenuOptionWidget.parent.call( this, config );
-
-       // Initialization
-       this.$element
-               .attr( 'role', 'menuitem' )
-               .addClass( 'oo-ui-menuOptionWidget' );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.MenuOptionWidget, OO.ui.DecoratedOptionWidget );
-
-/* Static Properties */
-
-OO.ui.MenuOptionWidget.static.scrollIntoViewOnSelect = true;
-
-/**
- * MenuSectionOptionWidgets are used inside {@link OO.ui.MenuSelectWidget menu select widgets} to group one or more related
- * {@link OO.ui.MenuOptionWidget menu options}. MenuSectionOptionWidgets cannot be highlighted or selected.
- *
- *     @example
- *     var myDropdown = new OO.ui.DropdownWidget( {
- *         menu: {
- *             items: [
- *                 new OO.ui.MenuSectionOptionWidget( {
- *                     label: 'Dogs'
- *                 } ),
- *                 new OO.ui.MenuOptionWidget( {
- *                     data: 'corgi',
- *                     label: 'Welsh Corgi'
- *                 } ),
- *                 new OO.ui.MenuOptionWidget( {
- *                     data: 'poodle',
- *                     label: 'Standard Poodle'
- *                 } ),
- *                 new OO.ui.MenuSectionOptionWidget( {
- *                     label: 'Cats'
- *                 } ),
- *                 new OO.ui.MenuOptionWidget( {
- *                     data: 'lion',
- *                     label: 'Lion'
- *                 } )
- *             ]
- *         }
- *     } );
- *     $( 'body' ).append( myDropdown.$element );
- *
- * @class
- * @extends OO.ui.DecoratedOptionWidget
- *
- * @constructor
- * @param {Object} [config] Configuration options
- */
-OO.ui.MenuSectionOptionWidget = function OoUiMenuSectionOptionWidget( config ) {
-       // Parent constructor
-       OO.ui.MenuSectionOptionWidget.parent.call( this, config );
-
-       // Initialization
-       this.$element.addClass( 'oo-ui-menuSectionOptionWidget' );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.MenuSectionOptionWidget, OO.ui.DecoratedOptionWidget );
-
-/* Static Properties */
-
-OO.ui.MenuSectionOptionWidget.static.selectable = false;
-
-OO.ui.MenuSectionOptionWidget.static.highlightable = false;
-
-/**
- * OutlineOptionWidget is an item in an {@link OO.ui.OutlineSelectWidget OutlineSelectWidget}.
- *
- * Currently, this class is only used by {@link OO.ui.BookletLayout booklet layouts}, which contain
- * {@link OO.ui.PageLayout page layouts}. See {@link OO.ui.BookletLayout BookletLayout}
- * for an example.
- *
- * @class
- * @extends OO.ui.DecoratedOptionWidget
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {number} [level] Indentation level
- * @cfg {boolean} [movable] Allow modification from {@link OO.ui.OutlineControlsWidget outline controls}.
- */
-OO.ui.OutlineOptionWidget = function OoUiOutlineOptionWidget( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Parent constructor
-       OO.ui.OutlineOptionWidget.parent.call( this, config );
-
-       // Properties
-       this.level = 0;
-       this.movable = !!config.movable;
-       this.removable = !!config.removable;
-
-       // Initialization
-       this.$element.addClass( 'oo-ui-outlineOptionWidget' );
-       this.setLevel( config.level );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.OutlineOptionWidget, OO.ui.DecoratedOptionWidget );
-
-/* Static Properties */
-
-OO.ui.OutlineOptionWidget.static.highlightable = false;
-
-OO.ui.OutlineOptionWidget.static.scrollIntoViewOnSelect = true;
-
-OO.ui.OutlineOptionWidget.static.levelClass = 'oo-ui-outlineOptionWidget-level-';
-
-OO.ui.OutlineOptionWidget.static.levels = 3;
-
-/* Methods */
-
-/**
- * Check if item is movable.
- *
- * Movability is used by {@link OO.ui.OutlineControlsWidget outline controls}.
- *
- * @return {boolean} Item is movable
- */
-OO.ui.OutlineOptionWidget.prototype.isMovable = function () {
-       return this.movable;
-};
-
-/**
- * Check if item is removable.
- *
- * Removability is used by {@link OO.ui.OutlineControlsWidget outline controls}.
- *
- * @return {boolean} Item is removable
- */
-OO.ui.OutlineOptionWidget.prototype.isRemovable = function () {
-       return this.removable;
-};
-
-/**
- * Get indentation level.
- *
- * @return {number} Indentation level
- */
-OO.ui.OutlineOptionWidget.prototype.getLevel = function () {
-       return this.level;
-};
-
-/**
- * Set movability.
- *
- * Movability is used by {@link OO.ui.OutlineControlsWidget outline controls}.
- *
- * @param {boolean} movable Item is movable
- * @chainable
- */
-OO.ui.OutlineOptionWidget.prototype.setMovable = function ( movable ) {
-       this.movable = !!movable;
-       this.updateThemeClasses();
-       return this;
-};
-
-/**
- * Set removability.
- *
- * Removability is used by {@link OO.ui.OutlineControlsWidget outline controls}.
- *
- * @param {boolean} removable Item is removable
- * @chainable
- */
-OO.ui.OutlineOptionWidget.prototype.setRemovable = function ( removable ) {
-       this.removable = !!removable;
-       this.updateThemeClasses();
-       return this;
-};
-
-/**
- * Set indentation level.
- *
- * @param {number} [level=0] Indentation level, in the range of [0,#maxLevel]
- * @chainable
- */
-OO.ui.OutlineOptionWidget.prototype.setLevel = function ( level ) {
-       var levels = this.constructor.static.levels,
-               levelClass = this.constructor.static.levelClass,
-               i = levels;
-
-       this.level = level ? Math.max( 0, Math.min( levels - 1, level ) ) : 0;
-       while ( i-- ) {
-               if ( this.level === i ) {
-                       this.$element.addClass( levelClass + i );
-               } else {
-                       this.$element.removeClass( levelClass + i );
-               }
-       }
-       this.updateThemeClasses();
-
-       return this;
-};
-
-/**
- * TabOptionWidget is an item in a {@link OO.ui.TabSelectWidget TabSelectWidget}.
- *
- * Currently, this class is only used by {@link OO.ui.IndexLayout index layouts}, which contain
- * {@link OO.ui.CardLayout card layouts}. See {@link OO.ui.IndexLayout IndexLayout}
- * for an example.
- *
- * @class
- * @extends OO.ui.OptionWidget
- *
- * @constructor
- * @param {Object} [config] Configuration options
- */
-OO.ui.TabOptionWidget = function OoUiTabOptionWidget( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Parent constructor
-       OO.ui.TabOptionWidget.parent.call( this, config );
-
-       // Initialization
-       this.$element.addClass( 'oo-ui-tabOptionWidget' );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.TabOptionWidget, OO.ui.OptionWidget );
-
-/* Static Properties */
-
-OO.ui.TabOptionWidget.static.highlightable = false;
-
-/**
- * PopupWidget is a container for content. The popup is overlaid and positioned absolutely.
- * By default, each popup has an anchor that points toward its origin.
- * Please see the [OOjs UI documentation on Mediawiki] [1] for more information and examples.
- *
- *     @example
- *     // A popup widget.
- *     var popup = new OO.ui.PopupWidget( {
- *         $content: $( '<p>Hi there!</p>' ),
- *         padded: true,
- *         width: 300
- *     } );
- *
- *     $( 'body' ).append( popup.$element );
- *     // To display the popup, toggle the visibility to 'true'.
- *     popup.toggle( true );
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Popups
- *
- * @class
- * @extends OO.ui.Widget
- * @mixins OO.ui.mixin.LabelElement
- * @mixins OO.ui.mixin.ClippableElement
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {number} [width=320] Width of popup in pixels
- * @cfg {number} [height] Height of popup in pixels. Omit to use the automatic height.
- * @cfg {boolean} [anchor=true] Show anchor pointing to origin of popup
- * @cfg {string} [align='center'] Alignment of the popup: `center`, `force-left`, `force-right`, `backwards` or `forwards`.
- *  If the popup is forced-left the popup body is leaning towards the left. For force-right alignment, the body of the
- *  popup is leaning towards the right of the screen.
- *  Using 'backwards' is a logical direction which will result in the popup leaning towards the beginning of the sentence
- *  in the given language, which means it will flip to the correct positioning in right-to-left languages.
- *  Using 'forward' will also result in a logical alignment where the body of the popup leans towards the end of the
- *  sentence in the given language.
- * @cfg {jQuery} [$container] Constrain the popup to the boundaries of the specified container.
- *  See the [OOjs UI docs on MediaWiki][3] for an example.
- *  [3]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Popups#containerExample
- * @cfg {number} [containerPadding=10] Padding between the popup and its container, specified as a number of pixels.
- * @cfg {jQuery} [$content] Content to append to the popup's body
- * @cfg {jQuery} [$footer] Content to append to the popup's footer
- * @cfg {boolean} [autoClose=false] Automatically close the popup when it loses focus.
- * @cfg {jQuery} [$autoCloseIgnore] Elements that will not close the popup when clicked.
- *  This config option is only relevant if #autoClose is set to `true`. See the [OOjs UI docs on MediaWiki][2]
- *  for an example.
- *  [2]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Popups#autocloseExample
- * @cfg {boolean} [head] Show a popup header that contains a #label (if specified) and close
- *  button.
- * @cfg {boolean} [padded] Add padding to the popup's body
- */
-OO.ui.PopupWidget = function OoUiPopupWidget( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Parent constructor
-       OO.ui.PopupWidget.parent.call( this, config );
-
-       // Properties (must be set before ClippableElement constructor call)
-       this.$body = $( '<div>' );
-       this.$popup = $( '<div>' );
-
-       // Mixin constructors
-       OO.ui.mixin.LabelElement.call( this, config );
-       OO.ui.mixin.ClippableElement.call( this, $.extend( {}, config, {
-               $clippable: this.$body,
-               $clippableContainer: this.$popup
-       } ) );
-
-       // Properties
-       this.$head = $( '<div>' );
-       this.$footer = $( '<div>' );
-       this.$anchor = $( '<div>' );
-       // If undefined, will be computed lazily in updateDimensions()
-       this.$container = config.$container;
-       this.containerPadding = config.containerPadding !== undefined ? config.containerPadding : 10;
-       this.autoClose = !!config.autoClose;
-       this.$autoCloseIgnore = config.$autoCloseIgnore;
-       this.transitionTimeout = null;
-       this.anchor = null;
-       this.width = config.width !== undefined ? config.width : 320;
-       this.height = config.height !== undefined ? config.height : null;
-       this.setAlignment( config.align );
-       this.closeButton = new OO.ui.ButtonWidget( { framed: false, icon: 'close' } );
-       this.onMouseDownHandler = this.onMouseDown.bind( this );
-       this.onDocumentKeyDownHandler = this.onDocumentKeyDown.bind( this );
-
-       // Events
-       this.closeButton.connect( this, { click: 'onCloseButtonClick' } );
-
-       // Initialization
-       this.toggleAnchor( config.anchor === undefined || config.anchor );
-       this.$body.addClass( 'oo-ui-popupWidget-body' );
-       this.$anchor.addClass( 'oo-ui-popupWidget-anchor' );
-       this.$head
-               .addClass( 'oo-ui-popupWidget-head' )
-               .append( this.$label, this.closeButton.$element );
-       this.$footer.addClass( 'oo-ui-popupWidget-footer' );
-       if ( !config.head ) {
-               this.$head.addClass( 'oo-ui-element-hidden' );
-       }
-       if ( !config.$footer ) {
-               this.$footer.addClass( 'oo-ui-element-hidden' );
-       }
-       this.$popup
-               .addClass( 'oo-ui-popupWidget-popup' )
-               .append( this.$head, this.$body, this.$footer );
-       this.$element
-               .addClass( 'oo-ui-popupWidget' )
-               .append( this.$popup, this.$anchor );
-       // Move content, which was added to #$element by OO.ui.Widget, to the body
-       if ( config.$content instanceof jQuery ) {
-               this.$body.append( config.$content );
-       }
-       if ( config.$footer instanceof jQuery ) {
-               this.$footer.append( config.$footer );
-       }
-       if ( config.padded ) {
-               this.$body.addClass( 'oo-ui-popupWidget-body-padded' );
-       }
-
-       // Initially hidden - using #toggle may cause errors if subclasses override toggle with methods
-       // that reference properties not initialized at that time of parent class construction
-       // TODO: Find a better way to handle post-constructor setup
-       this.visible = false;
-       this.$element.addClass( 'oo-ui-element-hidden' );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.PopupWidget, OO.ui.Widget );
-OO.mixinClass( OO.ui.PopupWidget, OO.ui.mixin.LabelElement );
-OO.mixinClass( OO.ui.PopupWidget, OO.ui.mixin.ClippableElement );
-
-/* Methods */
-
-/**
- * Handles mouse down events.
- *
- * @private
- * @param {MouseEvent} e Mouse down event
- */
-OO.ui.PopupWidget.prototype.onMouseDown = function ( e ) {
-       if (
-               this.isVisible() &&
-               !$.contains( this.$element[ 0 ], e.target ) &&
-               ( !this.$autoCloseIgnore || !this.$autoCloseIgnore.has( e.target ).length )
-       ) {
-               this.toggle( false );
-       }
-};
-
-/**
- * Bind mouse down listener.
- *
- * @private
- */
-OO.ui.PopupWidget.prototype.bindMouseDownListener = function () {
-       // Capture clicks outside popup
-       this.getElementWindow().addEventListener( 'mousedown', this.onMouseDownHandler, true );
-};
-
-/**
- * Handles close button click events.
- *
- * @private
- */
-OO.ui.PopupWidget.prototype.onCloseButtonClick = function () {
-       if ( this.isVisible() ) {
-               this.toggle( false );
-       }
-};
-
-/**
- * Unbind mouse down listener.
- *
- * @private
- */
-OO.ui.PopupWidget.prototype.unbindMouseDownListener = function () {
-       this.getElementWindow().removeEventListener( 'mousedown', this.onMouseDownHandler, true );
-};
-
-/**
- * Handles key down events.
- *
- * @private
- * @param {KeyboardEvent} e Key down event
- */
-OO.ui.PopupWidget.prototype.onDocumentKeyDown = function ( e ) {
-       if (
-               e.which === OO.ui.Keys.ESCAPE &&
-               this.isVisible()
-       ) {
-               this.toggle( false );
-               e.preventDefault();
-               e.stopPropagation();
-       }
-};
-
-/**
- * Bind key down listener.
- *
- * @private
- */
-OO.ui.PopupWidget.prototype.bindKeyDownListener = function () {
-       this.getElementWindow().addEventListener( 'keydown', this.onDocumentKeyDownHandler, true );
-};
-
-/**
- * Unbind key down listener.
- *
- * @private
- */
-OO.ui.PopupWidget.prototype.unbindKeyDownListener = function () {
-       this.getElementWindow().removeEventListener( 'keydown', this.onDocumentKeyDownHandler, true );
-};
-
-/**
- * Show, hide, or toggle the visibility of the anchor.
- *
- * @param {boolean} [show] Show anchor, omit to toggle
- */
-OO.ui.PopupWidget.prototype.toggleAnchor = function ( show ) {
-       show = show === undefined ? !this.anchored : !!show;
-
-       if ( this.anchored !== show ) {
-               if ( show ) {
-                       this.$element.addClass( 'oo-ui-popupWidget-anchored' );
-               } else {
-                       this.$element.removeClass( 'oo-ui-popupWidget-anchored' );
-               }
-               this.anchored = show;
-       }
-};
-
-/**
- * Check if the anchor is visible.
- *
- * @return {boolean} Anchor is visible
- */
-OO.ui.PopupWidget.prototype.hasAnchor = function () {
-       return this.anchor;
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.PopupWidget.prototype.toggle = function ( show ) {
-       var change;
-       show = show === undefined ? !this.isVisible() : !!show;
-
-       change = show !== this.isVisible();
-
-       // Parent method
-       OO.ui.PopupWidget.parent.prototype.toggle.call( this, show );
-
-       if ( change ) {
-               if ( show ) {
-                       if ( this.autoClose ) {
-                               this.bindMouseDownListener();
-                               this.bindKeyDownListener();
-                       }
-                       this.updateDimensions();
-                       this.toggleClipping( true );
-               } else {
-                       this.toggleClipping( false );
-                       if ( this.autoClose ) {
-                               this.unbindMouseDownListener();
-                               this.unbindKeyDownListener();
-                       }
-               }
-       }
-
-       return this;
-};
-
-/**
- * Set the size of the popup.
- *
- * Changing the size may also change the popup's position depending on the alignment.
- *
- * @param {number} width Width in pixels
- * @param {number} height Height in pixels
- * @param {boolean} [transition=false] Use a smooth transition
- * @chainable
- */
-OO.ui.PopupWidget.prototype.setSize = function ( width, height, transition ) {
-       this.width = width;
-       this.height = height !== undefined ? height : null;
-       if ( this.isVisible() ) {
-               this.updateDimensions( transition );
-       }
-};
-
-/**
- * Update the size and position.
- *
- * Only use this to keep the popup properly anchored. Use #setSize to change the size, and this will
- * be called automatically.
- *
- * @param {boolean} [transition=false] Use a smooth transition
- * @chainable
- */
-OO.ui.PopupWidget.prototype.updateDimensions = function ( transition ) {
-       var popupOffset, originOffset, containerLeft, containerWidth, containerRight,
-               popupLeft, popupRight, overlapLeft, overlapRight, anchorWidth,
-               align = this.align,
-               widget = this;
-
-       if ( !this.$container ) {
-               // Lazy-initialize $container if not specified in constructor
-               this.$container = $( this.getClosestScrollableElementContainer() );
-       }
-
-       // Set height and width before measuring things, since it might cause our measurements
-       // to change (e.g. due to scrollbars appearing or disappearing)
-       this.$popup.css( {
-               width: this.width,
-               height: this.height !== null ? this.height : 'auto'
-       } );
-
-       // If we are in RTL, we need to flip the alignment, unless it is center
-       if ( align === 'forwards' || align === 'backwards' ) {
-               if ( this.$container.css( 'direction' ) === 'rtl' ) {
-                       align = ( { forwards: 'force-left', backwards: 'force-right' } )[ this.align ];
-               } else {
-                       align = ( { forwards: 'force-right', backwards: 'force-left' } )[ this.align ];
-               }
-
-       }
-
-       // Compute initial popupOffset based on alignment
-       popupOffset = this.width * ( { 'force-left': -1, center: -0.5, 'force-right': 0 } )[ align ];
-
-       // Figure out if this will cause the popup to go beyond the edge of the container
-       originOffset = this.$element.offset().left;
-       containerLeft = this.$container.offset().left;
-       containerWidth = this.$container.innerWidth();
-       containerRight = containerLeft + containerWidth;
-       popupLeft = popupOffset - this.containerPadding;
-       popupRight = popupOffset + this.containerPadding + this.width + this.containerPadding;
-       overlapLeft = ( originOffset + popupLeft ) - containerLeft;
-       overlapRight = containerRight - ( originOffset + popupRight );
-
-       // Adjust offset to make the popup not go beyond the edge, if needed
-       if ( overlapRight < 0 ) {
-               popupOffset += overlapRight;
-       } else if ( overlapLeft < 0 ) {
-               popupOffset -= overlapLeft;
-       }
-
-       // Adjust offset to avoid anchor being rendered too close to the edge
-       // $anchor.width() doesn't work with the pure CSS anchor (returns 0)
-       // TODO: Find a measurement that works for CSS anchors and image anchors
-       anchorWidth = this.$anchor[ 0 ].scrollWidth * 2;
-       if ( popupOffset + this.width < anchorWidth ) {
-               popupOffset = anchorWidth - this.width;
-       } else if ( -popupOffset < anchorWidth ) {
-               popupOffset = -anchorWidth;
-       }
-
-       // Prevent transition from being interrupted
-       clearTimeout( this.transitionTimeout );
-       if ( transition ) {
-               // Enable transition
-               this.$element.addClass( 'oo-ui-popupWidget-transitioning' );
-       }
-
-       // Position body relative to anchor
-       this.$popup.css( 'margin-left', popupOffset );
-
-       if ( transition ) {
-               // Prevent transitioning after transition is complete
-               this.transitionTimeout = setTimeout( function () {
-                       widget.$element.removeClass( 'oo-ui-popupWidget-transitioning' );
-               }, 200 );
-       } else {
-               // Prevent transitioning immediately
-               this.$element.removeClass( 'oo-ui-popupWidget-transitioning' );
-       }
-
-       // Reevaluate clipping state since we've relocated and resized the popup
-       this.clip();
-
-       return this;
-};
-
-/**
- * Set popup alignment
- * @param {string} align Alignment of the popup, `center`, `force-left`, `force-right`,
- *  `backwards` or `forwards`.
- */
-OO.ui.PopupWidget.prototype.setAlignment = function ( align ) {
-       // Validate alignment and transform deprecated values
-       if ( [ 'left', 'right', 'force-left', 'force-right', 'backwards', 'forwards', 'center' ].indexOf( align ) > -1 ) {
-               this.align = { left: 'force-right', right: 'force-left' }[ align ] || align;
-       } else {
-               this.align = 'center';
-       }
-};
-
-/**
- * Get popup alignment
- * @return {string} align Alignment of the popup, `center`, `force-left`, `force-right`,
- *  `backwards` or `forwards`.
- */
-OO.ui.PopupWidget.prototype.getAlignment = function () {
-       return this.align;
-};
-
-/**
- * Progress bars visually display the status of an operation, such as a download,
- * and can be either determinate or indeterminate:
- *
- * - **determinate** process bars show the percent of an operation that is complete.
- *
- * - **indeterminate** process bars use a visual display of motion to indicate that an operation
- *   is taking place. Because the extent of an indeterminate operation is unknown, the bar does
- *   not use percentages.
- *
- * The value of the `progress` configuration determines whether the bar is determinate or indeterminate.
- *
- *     @example
- *     // Examples of determinate and indeterminate progress bars.
- *     var progressBar1 = new OO.ui.ProgressBarWidget( {
- *         progress: 33
- *     } );
- *     var progressBar2 = new OO.ui.ProgressBarWidget();
- *
- *     // Create a FieldsetLayout to layout progress bars
- *     var fieldset = new OO.ui.FieldsetLayout;
- *     fieldset.addItems( [
- *        new OO.ui.FieldLayout( progressBar1, {label: 'Determinate', align: 'top'}),
- *        new OO.ui.FieldLayout( progressBar2, {label: 'Indeterminate', align: 'top'})
- *     ] );
- *     $( 'body' ).append( fieldset.$element );
- *
- * @class
- * @extends OO.ui.Widget
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {number|boolean} [progress=false] The type of progress bar (determinate or indeterminate).
- *  To create a determinate progress bar, specify a number that reflects the initial percent complete.
- *  By default, the progress bar is indeterminate.
- */
-OO.ui.ProgressBarWidget = function OoUiProgressBarWidget( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Parent constructor
-       OO.ui.ProgressBarWidget.parent.call( this, config );
-
-       // Properties
-       this.$bar = $( '<div>' );
-       this.progress = null;
-
-       // Initialization
-       this.setProgress( config.progress !== undefined ? config.progress : false );
-       this.$bar.addClass( 'oo-ui-progressBarWidget-bar' );
-       this.$element
-               .attr( {
-                       role: 'progressbar',
-                       'aria-valuemin': 0,
-                       'aria-valuemax': 100
-               } )
-               .addClass( 'oo-ui-progressBarWidget' )
-               .append( this.$bar );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.ProgressBarWidget, OO.ui.Widget );
-
-/* Static Properties */
-
-OO.ui.ProgressBarWidget.static.tagName = 'div';
-
-/* Methods */
-
-/**
- * Get the percent of the progress that has been completed. Indeterminate progresses will return `false`.
- *
- * @return {number|boolean} Progress percent
- */
-OO.ui.ProgressBarWidget.prototype.getProgress = function () {
-       return this.progress;
-};
-
-/**
- * Set the percent of the process completed or `false` for an indeterminate process.
- *
- * @param {number|boolean} progress Progress percent or `false` for indeterminate
- */
-OO.ui.ProgressBarWidget.prototype.setProgress = function ( progress ) {
-       this.progress = progress;
-
-       if ( progress !== false ) {
-               this.$bar.css( 'width', this.progress + '%' );
-               this.$element.attr( 'aria-valuenow', this.progress );
-       } else {
-               this.$bar.css( 'width', '' );
-               this.$element.removeAttr( 'aria-valuenow' );
-       }
-       this.$element.toggleClass( 'oo-ui-progressBarWidget-indeterminate', !progress );
-};
-
-/**
- * SearchWidgets combine a {@link OO.ui.TextInputWidget text input field}, where users can type a search query,
- * and a menu of search results, which is displayed beneath the query
- * field. Unlike {@link OO.ui.mixin.LookupElement lookup menus}, search result menus are always visible to the user.
- * Users can choose an item from the menu or type a query into the text field to search for a matching result item.
- * In general, search widgets are used inside a separate {@link OO.ui.Dialog dialog} window.
- *
- * Each time the query is changed, the search result menu is cleared and repopulated. Please see
- * the [OOjs UI demos][1] for an example.
- *
- * [1]: https://tools.wmflabs.org/oojs-ui/oojs-ui/demos/#dialogs-mediawiki-vector-ltr
- *
- * @class
- * @extends OO.ui.Widget
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {string|jQuery} [placeholder] Placeholder text for query input
- * @cfg {string} [value] Initial query value
- */
-OO.ui.SearchWidget = function OoUiSearchWidget( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Parent constructor
-       OO.ui.SearchWidget.parent.call( this, config );
-
-       // Properties
-       this.query = new OO.ui.TextInputWidget( {
-               icon: 'search',
-               placeholder: config.placeholder,
-               value: config.value
-       } );
-       this.results = new OO.ui.SelectWidget();
-       this.$query = $( '<div>' );
-       this.$results = $( '<div>' );
-
-       // Events
-       this.query.connect( this, {
-               change: 'onQueryChange',
-               enter: 'onQueryEnter'
-       } );
-       this.query.$input.on( 'keydown', this.onQueryKeydown.bind( this ) );
-
-       // Initialization
-       this.$query
-               .addClass( 'oo-ui-searchWidget-query' )
-               .append( this.query.$element );
-       this.$results
-               .addClass( 'oo-ui-searchWidget-results' )
-               .append( this.results.$element );
-       this.$element
-               .addClass( 'oo-ui-searchWidget' )
-               .append( this.$results, this.$query );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.SearchWidget, OO.ui.Widget );
-
-/* Methods */
-
-/**
- * Handle query key down events.
- *
- * @private
- * @param {jQuery.Event} e Key down event
- */
-OO.ui.SearchWidget.prototype.onQueryKeydown = function ( e ) {
-       var highlightedItem, nextItem,
-               dir = e.which === OO.ui.Keys.DOWN ? 1 : ( e.which === OO.ui.Keys.UP ? -1 : 0 );
-
-       if ( dir ) {
-               highlightedItem = this.results.getHighlightedItem();
-               if ( !highlightedItem ) {
-                       highlightedItem = this.results.getSelectedItem();
-               }
-               nextItem = this.results.getRelativeSelectableItem( highlightedItem, dir );
-               this.results.highlightItem( nextItem );
-               nextItem.scrollElementIntoView();
-       }
-};
-
-/**
- * Handle select widget select events.
- *
- * Clears existing results. Subclasses should repopulate items according to new query.
- *
- * @private
- * @param {string} value New value
- */
-OO.ui.SearchWidget.prototype.onQueryChange = function () {
-       // Reset
-       this.results.clearItems();
-};
-
-/**
- * Handle select widget enter key events.
- *
- * Chooses highlighted item.
- *
- * @private
- * @param {string} value New value
- */
-OO.ui.SearchWidget.prototype.onQueryEnter = function () {
-       var highlightedItem = this.results.getHighlightedItem();
-       if ( highlightedItem ) {
-               this.results.chooseItem( highlightedItem );
-       }
-};
-
-/**
- * Get the query input.
- *
- * @return {OO.ui.TextInputWidget} Query input
- */
-OO.ui.SearchWidget.prototype.getQuery = function () {
-       return this.query;
-};
-
-/**
- * Get the search results menu.
- *
- * @return {OO.ui.SelectWidget} Menu of search results
- */
-OO.ui.SearchWidget.prototype.getResults = function () {
-       return this.results;
-};
-
-/**
- * A SelectWidget is of a generic selection of options. The OOjs UI library contains several types of
- * select widgets, including {@link OO.ui.ButtonSelectWidget button selects},
- * {@link OO.ui.RadioSelectWidget radio selects}, and {@link OO.ui.MenuSelectWidget
- * menu selects}.
- *
- * This class should be used together with OO.ui.OptionWidget or OO.ui.DecoratedOptionWidget. For more
- * information, please see the [OOjs UI documentation on MediaWiki][1].
- *
- *     @example
- *     // Example of a select widget with three options
- *     var select = new OO.ui.SelectWidget( {
- *         items: [
- *             new OO.ui.OptionWidget( {
- *                 data: 'a',
- *                 label: 'Option One',
- *             } ),
- *             new OO.ui.OptionWidget( {
- *                 data: 'b',
- *                 label: 'Option Two',
- *             } ),
- *             new OO.ui.OptionWidget( {
- *                 data: 'c',
- *                 label: 'Option Three',
- *             } )
- *         ]
- *     } );
- *     $( 'body' ).append( select.$element );
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options
- *
- * @abstract
- * @class
- * @extends OO.ui.Widget
- * @mixins OO.ui.mixin.GroupWidget
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {OO.ui.OptionWidget[]} [items] An array of options to add to the select.
- *  Options are created with {@link OO.ui.OptionWidget OptionWidget} classes. See
- *  the [OOjs UI documentation on MediaWiki] [2] for examples.
- *  [2]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options
- */
-OO.ui.SelectWidget = function OoUiSelectWidget( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Parent constructor
-       OO.ui.SelectWidget.parent.call( this, config );
-
-       // Mixin constructors
-       OO.ui.mixin.GroupWidget.call( this, $.extend( {}, config, { $group: this.$element } ) );
-
-       // Properties
-       this.pressed = false;
-       this.selecting = null;
-       this.onMouseUpHandler = this.onMouseUp.bind( this );
-       this.onMouseMoveHandler = this.onMouseMove.bind( this );
-       this.onKeyDownHandler = this.onKeyDown.bind( this );
-       this.onKeyPressHandler = this.onKeyPress.bind( this );
-       this.keyPressBuffer = '';
-       this.keyPressBufferTimer = null;
-
-       // Events
-       this.connect( this, {
-               toggle: 'onToggle'
-       } );
-       this.$element.on( {
-               mousedown: this.onMouseDown.bind( this ),
-               mouseover: this.onMouseOver.bind( this ),
-               mouseleave: this.onMouseLeave.bind( this )
-       } );
-
-       // Initialization
-       this.$element
-               .addClass( 'oo-ui-selectWidget oo-ui-selectWidget-depressed' )
-               .attr( 'role', 'listbox' );
-       if ( Array.isArray( config.items ) ) {
-               this.addItems( config.items );
-       }
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.SelectWidget, OO.ui.Widget );
-
-// Need to mixin base class as well
-OO.mixinClass( OO.ui.SelectWidget, OO.ui.mixin.GroupElement );
-OO.mixinClass( OO.ui.SelectWidget, OO.ui.mixin.GroupWidget );
-
-/* Static */
-OO.ui.SelectWidget.static.passAllFilter = function () {
-       return true;
-};
-
-/* Events */
-
-/**
- * @event highlight
- *
- * A `highlight` event is emitted when the highlight is changed with the #highlightItem method.
- *
- * @param {OO.ui.OptionWidget|null} item Highlighted item
- */
-
-/**
- * @event press
- *
- * A `press` event is emitted when the #pressItem method is used to programmatically modify the
- * pressed state of an option.
- *
- * @param {OO.ui.OptionWidget|null} item Pressed item
- */
-
-/**
- * @event select
- *
- * A `select` event is emitted when the selection is modified programmatically with the #selectItem method.
- *
- * @param {OO.ui.OptionWidget|null} item Selected item
- */
-
-/**
- * @event choose
- * A `choose` event is emitted when an item is chosen with the #chooseItem method.
- * @param {OO.ui.OptionWidget} item Chosen item
- */
-
-/**
- * @event add
- *
- * An `add` event is emitted when options are added to the select with the #addItems method.
- *
- * @param {OO.ui.OptionWidget[]} items Added items
- * @param {number} index Index of insertion point
- */
-
-/**
- * @event remove
- *
- * A `remove` event is emitted when options are removed from the select with the #clearItems
- * or #removeItems methods.
- *
- * @param {OO.ui.OptionWidget[]} items Removed items
- */
-
-/* Methods */
-
-/**
- * Handle mouse down events.
- *
- * @private
- * @param {jQuery.Event} e Mouse down event
- */
-OO.ui.SelectWidget.prototype.onMouseDown = function ( e ) {
-       var item;
-
-       if ( !this.isDisabled() && e.which === OO.ui.MouseButtons.LEFT ) {
-               this.togglePressed( true );
-               item = this.getTargetItem( e );
-               if ( item && item.isSelectable() ) {
-                       this.pressItem( item );
-                       this.selecting = item;
-                       this.getElementDocument().addEventListener( 'mouseup', this.onMouseUpHandler, true );
-                       this.getElementDocument().addEventListener( 'mousemove', this.onMouseMoveHandler, true );
-               }
-       }
-       return false;
-};
-
-/**
- * Handle mouse up events.
- *
- * @private
- * @param {jQuery.Event} e Mouse up event
- */
-OO.ui.SelectWidget.prototype.onMouseUp = function ( e ) {
-       var item;
-
-       this.togglePressed( false );
-       if ( !this.selecting ) {
-               item = this.getTargetItem( e );
-               if ( item && item.isSelectable() ) {
-                       this.selecting = item;
-               }
-       }
-       if ( !this.isDisabled() && e.which === OO.ui.MouseButtons.LEFT && this.selecting ) {
-               this.pressItem( null );
-               this.chooseItem( this.selecting );
-               this.selecting = null;
-       }
-
-       this.getElementDocument().removeEventListener( 'mouseup', this.onMouseUpHandler, true );
-       this.getElementDocument().removeEventListener( 'mousemove', this.onMouseMoveHandler, true );
-
-       return false;
-};
-
-/**
- * Handle mouse move events.
- *
- * @private
- * @param {jQuery.Event} e Mouse move event
- */
-OO.ui.SelectWidget.prototype.onMouseMove = function ( e ) {
-       var item;
-
-       if ( !this.isDisabled() && this.pressed ) {
-               item = this.getTargetItem( e );
-               if ( item && item !== this.selecting && item.isSelectable() ) {
-                       this.pressItem( item );
-                       this.selecting = item;
-               }
-       }
-       return false;
-};
-
-/**
- * Handle mouse over events.
- *
- * @private
- * @param {jQuery.Event} e Mouse over event
- */
-OO.ui.SelectWidget.prototype.onMouseOver = function ( e ) {
-       var item;
-
-       if ( !this.isDisabled() ) {
-               item = this.getTargetItem( e );
-               this.highlightItem( item && item.isHighlightable() ? item : null );
-       }
-       return false;
-};
-
-/**
- * Handle mouse leave events.
- *
- * @private
- * @param {jQuery.Event} e Mouse over event
- */
-OO.ui.SelectWidget.prototype.onMouseLeave = function () {
-       if ( !this.isDisabled() ) {
-               this.highlightItem( null );
-       }
-       return false;
-};
-
-/**
- * Handle key down events.
- *
- * @protected
- * @param {jQuery.Event} e Key down event
- */
-OO.ui.SelectWidget.prototype.onKeyDown = function ( e ) {
-       var nextItem,
-               handled = false,
-               currentItem = this.getHighlightedItem() || this.getSelectedItem();
-
-       if ( !this.isDisabled() && this.isVisible() ) {
-               switch ( e.keyCode ) {
-                       case OO.ui.Keys.ENTER:
-                               if ( currentItem && currentItem.constructor.static.highlightable ) {
-                                       // Was only highlighted, now let's select it. No-op if already selected.
-                                       this.chooseItem( currentItem );
-                                       handled = true;
-                               }
-                               break;
-                       case OO.ui.Keys.UP:
-                       case OO.ui.Keys.LEFT:
-                               this.clearKeyPressBuffer();
-                               nextItem = this.getRelativeSelectableItem( currentItem, -1 );
-                               handled = true;
-                               break;
-                       case OO.ui.Keys.DOWN:
-                       case OO.ui.Keys.RIGHT:
-                               this.clearKeyPressBuffer();
-                               nextItem = this.getRelativeSelectableItem( currentItem, 1 );
-                               handled = true;
-                               break;
-                       case OO.ui.Keys.ESCAPE:
-                       case OO.ui.Keys.TAB:
-                               if ( currentItem && currentItem.constructor.static.highlightable ) {
-                                       currentItem.setHighlighted( false );
-                               }
-                               this.unbindKeyDownListener();
-                               this.unbindKeyPressListener();
-                               // Don't prevent tabbing away / defocusing
-                               handled = false;
-                               break;
-               }
-
-               if ( nextItem ) {
-                       if ( nextItem.constructor.static.highlightable ) {
-                               this.highlightItem( nextItem );
-                       } else {
-                               this.chooseItem( nextItem );
-                       }
-                       nextItem.scrollElementIntoView();
-               }
-
-               if ( handled ) {
-                       // Can't just return false, because e is not always a jQuery event
-                       e.preventDefault();
-                       e.stopPropagation();
-               }
-       }
-};
-
-/**
- * Bind key down listener.
- *
- * @protected
- */
-OO.ui.SelectWidget.prototype.bindKeyDownListener = function () {
-       this.getElementWindow().addEventListener( 'keydown', this.onKeyDownHandler, true );
-};
-
-/**
- * Unbind key down listener.
- *
- * @protected
- */
-OO.ui.SelectWidget.prototype.unbindKeyDownListener = function () {
-       this.getElementWindow().removeEventListener( 'keydown', this.onKeyDownHandler, true );
-};
-
-/**
- * Clear the key-press buffer
- *
- * @protected
- */
-OO.ui.SelectWidget.prototype.clearKeyPressBuffer = function () {
-       if ( this.keyPressBufferTimer ) {
-               clearTimeout( this.keyPressBufferTimer );
-               this.keyPressBufferTimer = null;
-       }
-       this.keyPressBuffer = '';
-};
-
-/**
- * Handle key press events.
- *
- * @protected
- * @param {jQuery.Event} e Key press event
- */
-OO.ui.SelectWidget.prototype.onKeyPress = function ( e ) {
-       var c, filter, item;
-
-       if ( !e.charCode ) {
-               if ( e.keyCode === OO.ui.Keys.BACKSPACE && this.keyPressBuffer !== '' ) {
-                       this.keyPressBuffer = this.keyPressBuffer.substr( 0, this.keyPressBuffer.length - 1 );
-                       return false;
-               }
-               return;
-       }
-       if ( String.fromCodePoint ) {
-               c = String.fromCodePoint( e.charCode );
-       } else {
-               c = String.fromCharCode( e.charCode );
-       }
-
-       if ( this.keyPressBufferTimer ) {
-               clearTimeout( this.keyPressBufferTimer );
-       }
-       this.keyPressBufferTimer = setTimeout( this.clearKeyPressBuffer.bind( this ), 1500 );
-
-       item = this.getHighlightedItem() || this.getSelectedItem();
-
-       if ( this.keyPressBuffer === c ) {
-               // Common (if weird) special case: typing "xxxx" will cycle through all
-               // the items beginning with "x".
-               if ( item ) {
-                       item = this.getRelativeSelectableItem( item, 1 );
-               }
-       } else {
-               this.keyPressBuffer += c;
-       }
-
-       filter = this.getItemMatcher( this.keyPressBuffer, false );
-       if ( !item || !filter( item ) ) {
-               item = this.getRelativeSelectableItem( item, 1, filter );
-       }
-       if ( item ) {
-               if ( item.constructor.static.highlightable ) {
-                       this.highlightItem( item );
-               } else {
-                       this.chooseItem( item );
-               }
-               item.scrollElementIntoView();
-       }
-
-       return false;
-};
-
-/**
- * Get a matcher for the specific string
- *
- * @protected
- * @param {string} s String to match against items
- * @param {boolean} [exact=false] Only accept exact matches
- * @return {Function} function ( OO.ui.OptionItem ) => boolean
- */
-OO.ui.SelectWidget.prototype.getItemMatcher = function ( s, exact ) {
-       var re;
-
-       if ( s.normalize ) {
-               s = s.normalize();
-       }
-       s = exact ? s.trim() : s.replace( /^\s+/, '' );
-       re = '^\\s*' + s.replace( /([\\{}()|.?*+\-\^$\[\]])/g, '\\$1' ).replace( /\s+/g, '\\s+' );
-       if ( exact ) {
-               re += '\\s*$';
-       }
-       re = new RegExp( re, 'i' );
-       return function ( item ) {
-               var l = item.getLabel();
-               if ( typeof l !== 'string' ) {
-                       l = item.$label.text();
-               }
-               if ( l.normalize ) {
-                       l = l.normalize();
-               }
-               return re.test( l );
-       };
-};
-
-/**
- * Bind key press listener.
- *
- * @protected
- */
-OO.ui.SelectWidget.prototype.bindKeyPressListener = function () {
-       this.getElementWindow().addEventListener( 'keypress', this.onKeyPressHandler, true );
-};
-
-/**
- * Unbind key down listener.
- *
- * If you override this, be sure to call this.clearKeyPressBuffer() from your
- * implementation.
- *
- * @protected
- */
-OO.ui.SelectWidget.prototype.unbindKeyPressListener = function () {
-       this.getElementWindow().removeEventListener( 'keypress', this.onKeyPressHandler, true );
-       this.clearKeyPressBuffer();
-};
-
-/**
- * Visibility change handler
- *
- * @protected
- * @param {boolean} visible
- */
-OO.ui.SelectWidget.prototype.onToggle = function ( visible ) {
-       if ( !visible ) {
-               this.clearKeyPressBuffer();
-       }
-};
-
-/**
- * Get the closest item to a jQuery.Event.
- *
- * @private
- * @param {jQuery.Event} e
- * @return {OO.ui.OptionWidget|null} Outline item widget, `null` if none was found
- */
-OO.ui.SelectWidget.prototype.getTargetItem = function ( e ) {
-       return $( e.target ).closest( '.oo-ui-optionWidget' ).data( 'oo-ui-optionWidget' ) || null;
-};
-
-/**
- * Get selected item.
- *
- * @return {OO.ui.OptionWidget|null} Selected item, `null` if no item is selected
- */
-OO.ui.SelectWidget.prototype.getSelectedItem = function () {
-       var i, len;
-
-       for ( i = 0, len = this.items.length; i < len; i++ ) {
-               if ( this.items[ i ].isSelected() ) {
-                       return this.items[ i ];
-               }
-       }
-       return null;
-};
-
-/**
- * Get highlighted item.
- *
- * @return {OO.ui.OptionWidget|null} Highlighted item, `null` if no item is highlighted
- */
-OO.ui.SelectWidget.prototype.getHighlightedItem = function () {
-       var i, len;
-
-       for ( i = 0, len = this.items.length; i < len; i++ ) {
-               if ( this.items[ i ].isHighlighted() ) {
-                       return this.items[ i ];
-               }
-       }
-       return null;
-};
-
-/**
- * Toggle pressed state.
- *
- * Press is a state that occurs when a user mouses down on an item, but
- * has not yet let go of the mouse. The item may appear selected, but it will not be selected
- * until the user releases the mouse.
- *
- * @param {boolean} pressed An option is being pressed
- */
-OO.ui.SelectWidget.prototype.togglePressed = function ( pressed ) {
-       if ( pressed === undefined ) {
-               pressed = !this.pressed;
-       }
-       if ( pressed !== this.pressed ) {
-               this.$element
-                       .toggleClass( 'oo-ui-selectWidget-pressed', pressed )
-                       .toggleClass( 'oo-ui-selectWidget-depressed', !pressed );
-               this.pressed = pressed;
-       }
-};
-
-/**
- * Highlight an option. If the `item` param is omitted, no options will be highlighted
- * and any existing highlight will be removed. The highlight is mutually exclusive.
- *
- * @param {OO.ui.OptionWidget} [item] Item to highlight, omit for no highlight
- * @fires highlight
- * @chainable
- */
-OO.ui.SelectWidget.prototype.highlightItem = function ( item ) {
-       var i, len, highlighted,
-               changed = false;
-
-       for ( i = 0, len = this.items.length; i < len; i++ ) {
-               highlighted = this.items[ i ] === item;
-               if ( this.items[ i ].isHighlighted() !== highlighted ) {
-                       this.items[ i ].setHighlighted( highlighted );
-                       changed = true;
-               }
-       }
-       if ( changed ) {
-               this.emit( 'highlight', item );
-       }
-
-       return this;
-};
-
-/**
- * Fetch an item by its label.
- *
- * @param {string} label Label of the item to select.
- * @param {boolean} [prefix=false] Allow a prefix match, if only a single item matches
- * @return {OO.ui.Element|null} Item with equivalent label, `null` if none exists
- */
-OO.ui.SelectWidget.prototype.getItemFromLabel = function ( label, prefix ) {
-       var i, item, found,
-               len = this.items.length,
-               filter = this.getItemMatcher( label, true );
-
-       for ( i = 0; i < len; i++ ) {
-               item = this.items[ i ];
-               if ( item instanceof OO.ui.OptionWidget && item.isSelectable() && filter( item ) ) {
-                       return item;
-               }
-       }
-
-       if ( prefix ) {
-               found = null;
-               filter = this.getItemMatcher( label, false );
-               for ( i = 0; i < len; i++ ) {
-                       item = this.items[ i ];
-                       if ( item instanceof OO.ui.OptionWidget && item.isSelectable() && filter( item ) ) {
-                               if ( found ) {
-                                       return null;
-                               }
-                               found = item;
-                       }
-               }
-               if ( found ) {
-                       return found;
-               }
-       }
-
-       return null;
-};
-
-/**
- * Programmatically select an option by its label. If the item does not exist,
- * all options will be deselected.
- *
- * @param {string} [label] Label of the item to select.
- * @param {boolean} [prefix=false] Allow a prefix match, if only a single item matches
- * @fires select
- * @chainable
- */
-OO.ui.SelectWidget.prototype.selectItemByLabel = function ( label, prefix ) {
-       var itemFromLabel = this.getItemFromLabel( label, !!prefix );
-       if ( label === undefined || !itemFromLabel ) {
-               return this.selectItem();
-       }
-       return this.selectItem( itemFromLabel );
-};
-
-/**
- * Programmatically select an option by its data. If the `data` parameter is omitted,
- * or if the item does not exist, all options will be deselected.
- *
- * @param {Object|string} [data] Value of the item to select, omit to deselect all
- * @fires select
- * @chainable
- */
-OO.ui.SelectWidget.prototype.selectItemByData = function ( data ) {
-       var itemFromData = this.getItemFromData( data );
-       if ( data === undefined || !itemFromData ) {
-               return this.selectItem();
-       }
-       return this.selectItem( itemFromData );
-};
-
-/**
- * Programmatically select an option by its reference. If the `item` parameter is omitted,
- * all options will be deselected.
- *
- * @param {OO.ui.OptionWidget} [item] Item to select, omit to deselect all
- * @fires select
- * @chainable
- */
-OO.ui.SelectWidget.prototype.selectItem = function ( item ) {
-       var i, len, selected,
-               changed = false;
-
-       for ( i = 0, len = this.items.length; i < len; i++ ) {
-               selected = this.items[ i ] === item;
-               if ( this.items[ i ].isSelected() !== selected ) {
-                       this.items[ i ].setSelected( selected );
-                       changed = true;
-               }
-       }
-       if ( changed ) {
-               this.emit( 'select', item );
-       }
-
-       return this;
-};
-
-/**
- * Press an item.
- *
- * Press is a state that occurs when a user mouses down on an item, but has not
- * yet let go of the mouse. The item may appear selected, but it will not be selected until the user
- * releases the mouse.
- *
- * @param {OO.ui.OptionWidget} [item] Item to press, omit to depress all
- * @fires press
- * @chainable
- */
-OO.ui.SelectWidget.prototype.pressItem = function ( item ) {
-       var i, len, pressed,
-               changed = false;
-
-       for ( i = 0, len = this.items.length; i < len; i++ ) {
-               pressed = this.items[ i ] === item;
-               if ( this.items[ i ].isPressed() !== pressed ) {
-                       this.items[ i ].setPressed( pressed );
-                       changed = true;
-               }
-       }
-       if ( changed ) {
-               this.emit( 'press', item );
-       }
-
-       return this;
-};
-
-/**
- * Choose an item.
- *
- * Note that ‘choose’ should never be modified programmatically. A user can choose
- * an option with the keyboard or mouse and it becomes selected. To select an item programmatically,
- * use the #selectItem method.
- *
- * This method is identical to #selectItem, but may vary in subclasses that take additional action
- * when users choose an item with the keyboard or mouse.
- *
- * @param {OO.ui.OptionWidget} item Item to choose
- * @fires choose
- * @chainable
- */
-OO.ui.SelectWidget.prototype.chooseItem = function ( item ) {
-       if ( item ) {
-               this.selectItem( item );
-               this.emit( 'choose', item );
-       }
-
-       return this;
-};
-
-/**
- * Get an option by its position relative to the specified item (or to the start of the option array,
- * if item is `null`). The direction in which to search through the option array is specified with a
- * number: -1 for reverse (the default) or 1 for forward. The method will return an option, or
- * `null` if there are no options in the array.
- *
- * @param {OO.ui.OptionWidget|null} item Item to describe the start position, or `null` to start at the beginning of the array.
- * @param {number} direction Direction to move in: -1 to move backward, 1 to move forward
- * @param {Function} filter Only consider items for which this function returns
- *  true. Function takes an OO.ui.OptionWidget and returns a boolean.
- * @return {OO.ui.OptionWidget|null} Item at position, `null` if there are no items in the select
- */
-OO.ui.SelectWidget.prototype.getRelativeSelectableItem = function ( item, direction, filter ) {
-       var currentIndex, nextIndex, i,
-               increase = direction > 0 ? 1 : -1,
-               len = this.items.length;
-
-       if ( !$.isFunction( filter ) ) {
-               filter = OO.ui.SelectWidget.static.passAllFilter;
-       }
-
-       if ( item instanceof OO.ui.OptionWidget ) {
-               currentIndex = this.items.indexOf( item );
-               nextIndex = ( currentIndex + increase + len ) % len;
-       } else {
-               // If no item is selected and moving forward, start at the beginning.
-               // If moving backward, start at the end.
-               nextIndex = direction > 0 ? 0 : len - 1;
-       }
-
-       for ( i = 0; i < len; i++ ) {
-               item = this.items[ nextIndex ];
-               if ( item instanceof OO.ui.OptionWidget && item.isSelectable() && filter( item ) ) {
-                       return item;
-               }
-               nextIndex = ( nextIndex + increase + len ) % len;
-       }
-       return null;
-};
-
-/**
- * Get the next selectable item or `null` if there are no selectable items.
- * Disabled options and menu-section markers and breaks are not selectable.
- *
- * @return {OO.ui.OptionWidget|null} Item, `null` if there aren't any selectable items
- */
-OO.ui.SelectWidget.prototype.getFirstSelectableItem = function () {
-       var i, len, item;
-
-       for ( i = 0, len = this.items.length; i < len; i++ ) {
-               item = this.items[ i ];
-               if ( item instanceof OO.ui.OptionWidget && item.isSelectable() ) {
-                       return item;
-               }
-       }
-
-       return null;
-};
-
-/**
- * Add an array of options to the select. Optionally, an index number can be used to
- * specify an insertion point.
- *
- * @param {OO.ui.OptionWidget[]} items Items to add
- * @param {number} [index] Index to insert items after
- * @fires add
- * @chainable
- */
-OO.ui.SelectWidget.prototype.addItems = function ( items, index ) {
-       // Mixin method
-       OO.ui.mixin.GroupWidget.prototype.addItems.call( this, items, index );
-
-       // Always provide an index, even if it was omitted
-       this.emit( 'add', items, index === undefined ? this.items.length - items.length - 1 : index );
-
-       return this;
-};
-
-/**
- * Remove the specified array of options from the select. Options will be detached
- * from the DOM, not removed, so they can be reused later. To remove all options from
- * the select, you may wish to use the #clearItems method instead.
- *
- * @param {OO.ui.OptionWidget[]} items Items to remove
- * @fires remove
- * @chainable
- */
-OO.ui.SelectWidget.prototype.removeItems = function ( items ) {
-       var i, len, item;
-
-       // Deselect items being removed
-       for ( i = 0, len = items.length; i < len; i++ ) {
-               item = items[ i ];
-               if ( item.isSelected() ) {
-                       this.selectItem( null );
-               }
-       }
-
-       // Mixin method
-       OO.ui.mixin.GroupWidget.prototype.removeItems.call( this, items );
-
-       this.emit( 'remove', items );
-
-       return this;
-};
-
-/**
- * Clear all options from the select. Options will be detached from the DOM, not removed,
- * so that they can be reused later. To remove a subset of options from the select, use
- * the #removeItems method.
- *
- * @fires remove
- * @chainable
- */
-OO.ui.SelectWidget.prototype.clearItems = function () {
-       var items = this.items.slice();
-
-       // Mixin method
-       OO.ui.mixin.GroupWidget.prototype.clearItems.call( this );
-
-       // Clear selection
-       this.selectItem( null );
-
-       this.emit( 'remove', items );
-
-       return this;
-};
-
-/**
- * ButtonSelectWidget is a {@link OO.ui.SelectWidget select widget} that contains
- * button options and is used together with
- * OO.ui.ButtonOptionWidget. The ButtonSelectWidget provides an interface for
- * highlighting, choosing, and selecting mutually exclusive options. Please see
- * the [OOjs UI documentation on MediaWiki] [1] for more information.
- *
- *     @example
- *     // Example: A ButtonSelectWidget that contains three ButtonOptionWidgets
- *     var option1 = new OO.ui.ButtonOptionWidget( {
- *         data: 1,
- *         label: 'Option 1',
- *         title: 'Button option 1'
- *     } );
- *
- *     var option2 = new OO.ui.ButtonOptionWidget( {
- *         data: 2,
- *         label: 'Option 2',
- *         title: 'Button option 2'
- *     } );
- *
- *     var option3 = new OO.ui.ButtonOptionWidget( {
- *         data: 3,
- *         label: 'Option 3',
- *         title: 'Button option 3'
- *     } );
- *
- *     var buttonSelect=new OO.ui.ButtonSelectWidget( {
- *         items: [ option1, option2, option3 ]
- *     } );
- *     $( 'body' ).append( buttonSelect.$element );
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options
- *
- * @class
- * @extends OO.ui.SelectWidget
- * @mixins OO.ui.mixin.TabIndexedElement
- *
- * @constructor
- * @param {Object} [config] Configuration options
- */
-OO.ui.ButtonSelectWidget = function OoUiButtonSelectWidget( config ) {
-       // Parent constructor
-       OO.ui.ButtonSelectWidget.parent.call( this, config );
-
-       // Mixin constructors
-       OO.ui.mixin.TabIndexedElement.call( this, config );
-
-       // Events
-       this.$element.on( {
-               focus: this.bindKeyDownListener.bind( this ),
-               blur: this.unbindKeyDownListener.bind( this )
-       } );
-
-       // Initialization
-       this.$element.addClass( 'oo-ui-buttonSelectWidget' );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.ButtonSelectWidget, OO.ui.SelectWidget );
-OO.mixinClass( OO.ui.ButtonSelectWidget, OO.ui.mixin.TabIndexedElement );
-
-/**
- * RadioSelectWidget is a {@link OO.ui.SelectWidget select widget} that contains radio
- * options and is used together with OO.ui.RadioOptionWidget. The RadioSelectWidget provides
- * an interface for adding, removing and selecting options.
- * Please see the [OOjs UI documentation on MediaWiki][1] for more information.
- *
- * If you want to use this within a HTML form, such as a OO.ui.FormLayout, use
- * OO.ui.RadioSelectInputWidget instead.
- *
- *     @example
- *     // A RadioSelectWidget with RadioOptions.
- *     var option1 = new OO.ui.RadioOptionWidget( {
- *         data: 'a',
- *         label: 'Selected radio option'
- *     } );
- *
- *     var option2 = new OO.ui.RadioOptionWidget( {
- *         data: 'b',
- *         label: 'Unselected radio option'
- *     } );
- *
- *     var radioSelect=new OO.ui.RadioSelectWidget( {
- *         items: [ option1, option2 ]
- *      } );
- *
- *     // Select 'option 1' using the RadioSelectWidget's selectItem() method.
- *     radioSelect.selectItem( option1 );
- *
- *     $( 'body' ).append( radioSelect.$element );
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options
-
- *
- * @class
- * @extends OO.ui.SelectWidget
- * @mixins OO.ui.mixin.TabIndexedElement
- *
- * @constructor
- * @param {Object} [config] Configuration options
- */
-OO.ui.RadioSelectWidget = function OoUiRadioSelectWidget( config ) {
-       // Parent constructor
-       OO.ui.RadioSelectWidget.parent.call( this, config );
-
-       // Mixin constructors
-       OO.ui.mixin.TabIndexedElement.call( this, config );
-
-       // Events
-       this.$element.on( {
-               focus: this.bindKeyDownListener.bind( this ),
-               blur: this.unbindKeyDownListener.bind( this )
-       } );
-
-       // Initialization
-       this.$element
-               .addClass( 'oo-ui-radioSelectWidget' )
-               .attr( 'role', 'radiogroup' );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.RadioSelectWidget, OO.ui.SelectWidget );
-OO.mixinClass( OO.ui.RadioSelectWidget, OO.ui.mixin.TabIndexedElement );
-
-/**
- * MenuSelectWidget is a {@link OO.ui.SelectWidget select widget} that contains options and
- * is used together with OO.ui.MenuOptionWidget. It is designed be used as part of another widget.
- * See {@link OO.ui.DropdownWidget DropdownWidget}, {@link OO.ui.ComboBoxInputWidget ComboBoxInputWidget},
- * and {@link OO.ui.mixin.LookupElement LookupElement} for examples of widgets that contain menus.
- * MenuSelectWidgets themselves are not instantiated directly, rather subclassed
- * and customized to be opened, closed, and displayed as needed.
- *
- * By default, menus are clipped to the visible viewport and are not visible when a user presses the
- * mouse outside the menu.
- *
- * Menus also have support for keyboard interaction:
- *
- * - Enter/Return key: choose and select a menu option
- * - Up-arrow key: highlight the previous menu option
- * - Down-arrow key: highlight the next menu option
- * - Esc key: hide the menu
- *
- * Please see the [OOjs UI documentation on MediaWiki][1] for more information.
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options
- *
- * @class
- * @extends OO.ui.SelectWidget
- * @mixins OO.ui.mixin.ClippableElement
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {OO.ui.TextInputWidget} [input] Text input used to implement option highlighting for menu items that match
- *  the text the user types. This config is used by {@link OO.ui.ComboBoxInputWidget ComboBoxInputWidget}
- *  and {@link OO.ui.mixin.LookupElement LookupElement}
- * @cfg {jQuery} [$input] Text input used to implement option highlighting for menu items that match
- *  the text the user types. This config is used by {@link OO.ui.CapsuleMultiSelectWidget CapsuleMultiSelectWidget}
- * @cfg {OO.ui.Widget} [widget] Widget associated with the menu's active state. If the user clicks the mouse
- *  anywhere on the page outside of this widget, the menu is hidden. For example, if there is a button
- *  that toggles the menu's visibility on click, the menu will be hidden then re-shown when the user clicks
- *  that button, unless the button (or its parent widget) is passed in here.
- * @cfg {boolean} [autoHide=true] Hide the menu when the mouse is pressed outside the menu.
- * @cfg {boolean} [filterFromInput=false] Filter the displayed options from the input
- */
-OO.ui.MenuSelectWidget = function OoUiMenuSelectWidget( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Parent constructor
-       OO.ui.MenuSelectWidget.parent.call( this, config );
-
-       // Mixin constructors
-       OO.ui.mixin.ClippableElement.call( this, $.extend( {}, config, { $clippable: this.$group } ) );
-
-       // Properties
-       this.newItems = null;
-       this.autoHide = config.autoHide === undefined || !!config.autoHide;
-       this.filterFromInput = !!config.filterFromInput;
-       this.$input = config.$input ? config.$input : config.input ? config.input.$input : null;
-       this.$widget = config.widget ? config.widget.$element : null;
-       this.onDocumentMouseDownHandler = this.onDocumentMouseDown.bind( this );
-       this.onInputEditHandler = OO.ui.debounce( this.updateItemVisibility.bind( this ), 100 );
-
-       // Initialization
-       this.$element
-               .addClass( 'oo-ui-menuSelectWidget' )
-               .attr( 'role', 'menu' );
-
-       // Initially hidden - using #toggle may cause errors if subclasses override toggle with methods
-       // that reference properties not initialized at that time of parent class construction
-       // TODO: Find a better way to handle post-constructor setup
-       this.visible = false;
-       this.$element.addClass( 'oo-ui-element-hidden' );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.MenuSelectWidget, OO.ui.SelectWidget );
-OO.mixinClass( OO.ui.MenuSelectWidget, OO.ui.mixin.ClippableElement );
-
-/* Methods */
-
-/**
- * Handles document mouse down events.
- *
- * @protected
- * @param {jQuery.Event} e Key down event
- */
-OO.ui.MenuSelectWidget.prototype.onDocumentMouseDown = function ( e ) {
-       if (
-               !OO.ui.contains( this.$element[ 0 ], e.target, true ) &&
-               ( !this.$widget || !OO.ui.contains( this.$widget[ 0 ], e.target, true ) )
-       ) {
-               this.toggle( false );
-       }
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.MenuSelectWidget.prototype.onKeyDown = function ( e ) {
-       var currentItem = this.getHighlightedItem() || this.getSelectedItem();
-
-       if ( !this.isDisabled() && this.isVisible() ) {
-               switch ( e.keyCode ) {
-                       case OO.ui.Keys.LEFT:
-                       case OO.ui.Keys.RIGHT:
-                               // Do nothing if a text field is associated, arrow keys will be handled natively
-                               if ( !this.$input ) {
-                                       OO.ui.MenuSelectWidget.parent.prototype.onKeyDown.call( this, e );
-                               }
-                               break;
-                       case OO.ui.Keys.ESCAPE:
-                       case OO.ui.Keys.TAB:
-                               if ( currentItem ) {
-                                       currentItem.setHighlighted( false );
-                               }
-                               this.toggle( false );
-                               // Don't prevent tabbing away, prevent defocusing
-                               if ( e.keyCode === OO.ui.Keys.ESCAPE ) {
-                                       e.preventDefault();
-                                       e.stopPropagation();
-                               }
-                               break;
-                       default:
-                               OO.ui.MenuSelectWidget.parent.prototype.onKeyDown.call( this, e );
-                               return;
-               }
-       }
-};
-
-/**
- * Update menu item visibility after input changes.
- * @protected
- */
-OO.ui.MenuSelectWidget.prototype.updateItemVisibility = function () {
-       var i, item,
-               len = this.items.length,
-               showAll = !this.isVisible(),
-               filter = showAll ? null : this.getItemMatcher( this.$input.val() );
-
-       for ( i = 0; i < len; i++ ) {
-               item = this.items[ i ];
-               if ( item instanceof OO.ui.OptionWidget ) {
-                       item.toggle( showAll || filter( item ) );
-               }
-       }
-
-       // Reevaluate clipping
-       this.clip();
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.MenuSelectWidget.prototype.bindKeyDownListener = function () {
-       if ( this.$input ) {
-               this.$input.on( 'keydown', this.onKeyDownHandler );
-       } else {
-               OO.ui.MenuSelectWidget.parent.prototype.bindKeyDownListener.call( this );
-       }
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.MenuSelectWidget.prototype.unbindKeyDownListener = function () {
-       if ( this.$input ) {
-               this.$input.off( 'keydown', this.onKeyDownHandler );
-       } else {
-               OO.ui.MenuSelectWidget.parent.prototype.unbindKeyDownListener.call( this );
-       }
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.MenuSelectWidget.prototype.bindKeyPressListener = function () {
-       if ( this.$input ) {
-               if ( this.filterFromInput ) {
-                       this.$input.on( 'keydown mouseup cut paste change input select', this.onInputEditHandler );
-               }
-       } else {
-               OO.ui.MenuSelectWidget.parent.prototype.bindKeyPressListener.call( this );
-       }
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.MenuSelectWidget.prototype.unbindKeyPressListener = function () {
-       if ( this.$input ) {
-               if ( this.filterFromInput ) {
-                       this.$input.off( 'keydown mouseup cut paste change input select', this.onInputEditHandler );
-                       this.updateItemVisibility();
-               }
-       } else {
-               OO.ui.MenuSelectWidget.parent.prototype.unbindKeyPressListener.call( this );
-       }
-};
-
-/**
- * Choose an item.
- *
- * When a user chooses an item, the menu is closed.
- *
- * Note that ‘choose’ should never be modified programmatically. A user can choose an option with the keyboard
- * or mouse and it becomes selected. To select an item programmatically, use the #selectItem method.
- * @param {OO.ui.OptionWidget} item Item to choose
- * @chainable
- */
-OO.ui.MenuSelectWidget.prototype.chooseItem = function ( item ) {
-       OO.ui.MenuSelectWidget.parent.prototype.chooseItem.call( this, item );
-       this.toggle( false );
-       return this;
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.MenuSelectWidget.prototype.addItems = function ( items, index ) {
-       var i, len, item;
-
-       // Parent method
-       OO.ui.MenuSelectWidget.parent.prototype.addItems.call( this, items, index );
-
-       // Auto-initialize
-       if ( !this.newItems ) {
-               this.newItems = [];
-       }
-
-       for ( i = 0, len = items.length; i < len; i++ ) {
-               item = items[ i ];
-               if ( this.isVisible() ) {
-                       // Defer fitting label until item has been attached
-                       item.fitLabel();
-               } else {
-                       this.newItems.push( item );
-               }
-       }
-
-       // Reevaluate clipping
-       this.clip();
-
-       return this;
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.MenuSelectWidget.prototype.removeItems = function ( items ) {
-       // Parent method
-       OO.ui.MenuSelectWidget.parent.prototype.removeItems.call( this, items );
-
-       // Reevaluate clipping
-       this.clip();
-
-       return this;
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.MenuSelectWidget.prototype.clearItems = function () {
-       // Parent method
-       OO.ui.MenuSelectWidget.parent.prototype.clearItems.call( this );
-
-       // Reevaluate clipping
-       this.clip();
-
-       return this;
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.MenuSelectWidget.prototype.toggle = function ( visible ) {
-       var i, len, change;
-
-       visible = ( visible === undefined ? !this.visible : !!visible ) && !!this.items.length;
-       change = visible !== this.isVisible();
-
-       // Parent method
-       OO.ui.MenuSelectWidget.parent.prototype.toggle.call( this, visible );
-
-       if ( change ) {
-               if ( visible ) {
-                       this.bindKeyDownListener();
-                       this.bindKeyPressListener();
-
-                       if ( this.newItems && this.newItems.length ) {
-                               for ( i = 0, len = this.newItems.length; i < len; i++ ) {
-                                       this.newItems[ i ].fitLabel();
-                               }
-                               this.newItems = null;
-                       }
-                       this.toggleClipping( true );
-
-                       // Auto-hide
-                       if ( this.autoHide ) {
-                               this.getElementDocument().addEventListener( 'mousedown', this.onDocumentMouseDownHandler, true );
-                       }
-               } else {
-                       this.unbindKeyDownListener();
-                       this.unbindKeyPressListener();
-                       this.getElementDocument().removeEventListener( 'mousedown', this.onDocumentMouseDownHandler, true );
-                       this.toggleClipping( false );
-               }
-       }
-
-       return this;
-};
-
-/**
- * FloatingMenuSelectWidget is a menu that will stick under a specified
- * container, even when it is inserted elsewhere in the document (for example,
- * in a OO.ui.Window's $overlay). This is sometimes necessary to prevent the
- * menu from being clipped too aggresively.
- *
- * The menu's position is automatically calculated and maintained when the menu
- * is toggled or the window is resized.
- *
- * See OO.ui.ComboBoxInputWidget for an example of a widget that uses this class.
- *
- * @class
- * @extends OO.ui.MenuSelectWidget
- * @mixins OO.ui.mixin.FloatableElement
- *
- * @constructor
- * @param {OO.ui.Widget} [inputWidget] Widget to provide the menu for.
- *   Deprecated, omit this parameter and specify `$container` instead.
- * @param {Object} [config] Configuration options
- * @cfg {jQuery} [$container=inputWidget.$element] Element to render menu under
- */
-OO.ui.FloatingMenuSelectWidget = function OoUiFloatingMenuSelectWidget( inputWidget, config ) {
-       // Allow 'inputWidget' parameter and config for backwards compatibility
-       if ( OO.isPlainObject( inputWidget ) && config === undefined ) {
-               config = inputWidget;
-               inputWidget = config.inputWidget;
-       }
-
-       // Configuration initialization
-       config = config || {};
-
-       // Parent constructor
-       OO.ui.FloatingMenuSelectWidget.parent.call( this, config );
-
-       // Properties (must be set before mixin constructors)
-       this.inputWidget = inputWidget; // For backwards compatibility
-       this.$container = config.$container || this.inputWidget.$element;
-
-       // Mixins constructors
-       OO.ui.mixin.FloatableElement.call( this, $.extend( {}, config, { $floatableContainer: this.$container } ) );
-
-       // Initialization
-       this.$element.addClass( 'oo-ui-floatingMenuSelectWidget' );
-       // For backwards compatibility
-       this.$element.addClass( 'oo-ui-textInputMenuSelectWidget' );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.FloatingMenuSelectWidget, OO.ui.MenuSelectWidget );
-OO.mixinClass( OO.ui.FloatingMenuSelectWidget, OO.ui.mixin.FloatableElement );
-
-// For backwards compatibility
-OO.ui.TextInputMenuSelectWidget = OO.ui.FloatingMenuSelectWidget;
-
-/* Methods */
-
-/**
- * @inheritdoc
- */
-OO.ui.FloatingMenuSelectWidget.prototype.toggle = function ( visible ) {
-       var change;
-       visible = visible === undefined ? !this.isVisible() : !!visible;
-       change = visible !== this.isVisible();
-
-       if ( change && visible ) {
-               // Make sure the width is set before the parent method runs.
-               this.setIdealSize( this.$container.width() );
-       }
-
-       // Parent method
-       // This will call this.clip(), which is nonsensical since we're not positioned yet...
-       OO.ui.FloatingMenuSelectWidget.parent.prototype.toggle.call( this, visible );
-
-       if ( change ) {
-               this.togglePositioning( this.isVisible() );
-       }
-
-       return this;
-};
-
-/**
- * OutlineSelectWidget is a structured list that contains {@link OO.ui.OutlineOptionWidget outline options}
- * A set of controls can be provided with an {@link OO.ui.OutlineControlsWidget outline controls} widget.
- *
- * **Currently, this class is only used by {@link OO.ui.BookletLayout booklet layouts}.**
- *
- * @class
- * @extends OO.ui.SelectWidget
- * @mixins OO.ui.mixin.TabIndexedElement
- *
- * @constructor
- * @param {Object} [config] Configuration options
- */
-OO.ui.OutlineSelectWidget = function OoUiOutlineSelectWidget( config ) {
-       // Parent constructor
-       OO.ui.OutlineSelectWidget.parent.call( this, config );
-
-       // Mixin constructors
-       OO.ui.mixin.TabIndexedElement.call( this, config );
-
-       // Events
-       this.$element.on( {
-               focus: this.bindKeyDownListener.bind( this ),
-               blur: this.unbindKeyDownListener.bind( this )
-       } );
-
-       // Initialization
-       this.$element.addClass( 'oo-ui-outlineSelectWidget' );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.OutlineSelectWidget, OO.ui.SelectWidget );
-OO.mixinClass( OO.ui.OutlineSelectWidget, OO.ui.mixin.TabIndexedElement );
-
-/**
- * TabSelectWidget is a list that contains {@link OO.ui.TabOptionWidget tab options}
- *
- * **Currently, this class is only used by {@link OO.ui.IndexLayout index layouts}.**
- *
- * @class
- * @extends OO.ui.SelectWidget
- * @mixins OO.ui.mixin.TabIndexedElement
- *
- * @constructor
- * @param {Object} [config] Configuration options
- */
-OO.ui.TabSelectWidget = function OoUiTabSelectWidget( config ) {
-       // Parent constructor
-       OO.ui.TabSelectWidget.parent.call( this, config );
-
-       // Mixin constructors
-       OO.ui.mixin.TabIndexedElement.call( this, config );
-
-       // Events
-       this.$element.on( {
-               focus: this.bindKeyDownListener.bind( this ),
-               blur: this.unbindKeyDownListener.bind( this )
-       } );
-
-       // Initialization
-       this.$element.addClass( 'oo-ui-tabSelectWidget' );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.TabSelectWidget, OO.ui.SelectWidget );
-OO.mixinClass( OO.ui.TabSelectWidget, OO.ui.mixin.TabIndexedElement );
-
-/**
- * NumberInputWidgets combine a {@link OO.ui.TextInputWidget text input} (where a value
- * can be entered manually) and two {@link OO.ui.ButtonWidget button widgets}
- * (to adjust the value in increments) to allow the user to enter a number.
- *
- *     @example
- *     // Example: A NumberInputWidget.
- *     var numberInput = new OO.ui.NumberInputWidget( {
- *         label: 'NumberInputWidget',
- *         input: { value: 5, min: 1, max: 10 }
- *     } );
- *     $( 'body' ).append( numberInput.$element );
- *
- * @class
- * @extends OO.ui.Widget
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {Object} [input] Configuration options to pass to the {@link OO.ui.TextInputWidget text input widget}.
- * @cfg {Object} [minusButton] Configuration options to pass to the {@link OO.ui.ButtonWidget decrementing button widget}.
- * @cfg {Object} [plusButton] Configuration options to pass to the {@link OO.ui.ButtonWidget incrementing button widget}.
- * @cfg {boolean} [isInteger=false] Whether the field accepts only integer values.
- * @cfg {number} [min=-Infinity] Minimum allowed value
- * @cfg {number} [max=Infinity] Maximum allowed value
- * @cfg {number} [step=1] Delta when using the buttons or up/down arrow keys
- * @cfg {number|null} [pageStep] Delta when using the page-up/page-down keys. Defaults to 10 times #step.
- */
-OO.ui.NumberInputWidget = function OoUiNumberInputWidget( config ) {
-       // Configuration initialization
-       config = $.extend( {
-               isInteger: false,
-               min: -Infinity,
-               max: Infinity,
-               step: 1,
-               pageStep: null
-       }, config );
-
-       // Parent constructor
-       OO.ui.NumberInputWidget.parent.call( this, config );
-
-       // Properties
-       this.input = new OO.ui.TextInputWidget( $.extend(
-               {
-                       disabled: this.isDisabled()
-               },
-               config.input
-       ) );
-       this.minusButton = new OO.ui.ButtonWidget( $.extend(
-               {
-                       disabled: this.isDisabled(),
-                       tabIndex: -1
-               },
-               config.minusButton,
-               {
-                       classes: [ 'oo-ui-numberInputWidget-minusButton' ],
-                       label: '−'
-               }
-       ) );
-       this.plusButton = new OO.ui.ButtonWidget( $.extend(
-               {
-                       disabled: this.isDisabled(),
-                       tabIndex: -1
-               },
-               config.plusButton,
-               {
-                       classes: [ 'oo-ui-numberInputWidget-plusButton' ],
-                       label: '+'
-               }
-       ) );
-
-       // Events
-       this.input.connect( this, {
-               change: this.emit.bind( this, 'change' ),
-               enter: this.emit.bind( this, 'enter' )
-       } );
-       this.input.$input.on( {
-               keydown: this.onKeyDown.bind( this ),
-               'wheel mousewheel DOMMouseScroll': this.onWheel.bind( this )
-       } );
-       this.plusButton.connect( this, {
-               click: [ 'onButtonClick', +1 ]
-       } );
-       this.minusButton.connect( this, {
-               click: [ 'onButtonClick', -1 ]
-       } );
-
-       // Initialization
-       this.setIsInteger( !!config.isInteger );
-       this.setRange( config.min, config.max );
-       this.setStep( config.step, config.pageStep );
-
-       this.$field = $( '<div>' ).addClass( 'oo-ui-numberInputWidget-field' )
-               .append(
-                       this.minusButton.$element,
-                       this.input.$element,
-                       this.plusButton.$element
-               );
-       this.$element.addClass( 'oo-ui-numberInputWidget' ).append( this.$field );
-       this.input.setValidation( this.validateNumber.bind( this ) );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.NumberInputWidget, OO.ui.Widget );
-
-/* Events */
-
-/**
- * A `change` event is emitted when the value of the input changes.
- *
- * @event change
- */
-
-/**
- * An `enter` event is emitted when the user presses 'enter' inside the text box.
- *
- * @event enter
- */
-
-/* Methods */
-
-/**
- * Set whether only integers are allowed
- * @param {boolean} flag
- */
-OO.ui.NumberInputWidget.prototype.setIsInteger = function ( flag ) {
-       this.isInteger = !!flag;
-       this.input.setValidityFlag();
-};
-
-/**
- * Get whether only integers are allowed
- * @return {boolean} Flag value
- */
-OO.ui.NumberInputWidget.prototype.getIsInteger = function () {
-       return this.isInteger;
-};
-
-/**
- * Set the range of allowed values
- * @param {number} min Minimum allowed value
- * @param {number} max Maximum allowed value
- */
-OO.ui.NumberInputWidget.prototype.setRange = function ( min, max ) {
-       if ( min > max ) {
-               throw new Error( 'Minimum (' + min + ') must not be greater than maximum (' + max + ')' );
-       }
-       this.min = min;
-       this.max = max;
-       this.input.setValidityFlag();
-};
-
-/**
- * Get the current range
- * @return {number[]} Minimum and maximum values
- */
-OO.ui.NumberInputWidget.prototype.getRange = function () {
-       return [ this.min, this.max ];
-};
-
-/**
- * Set the stepping deltas
- * @param {number} step Normal step
- * @param {number|null} pageStep Page step. If null, 10 * step will be used.
- */
-OO.ui.NumberInputWidget.prototype.setStep = function ( step, pageStep ) {
-       if ( step <= 0 ) {
-               throw new Error( 'Step value must be positive' );
-       }
-       if ( pageStep === null ) {
-               pageStep = step * 10;
-       } else if ( pageStep <= 0 ) {
-               throw new Error( 'Page step value must be positive' );
-       }
-       this.step = step;
-       this.pageStep = pageStep;
-};
-
-/**
- * Get the current stepping values
- * @return {number[]} Step and page step
- */
-OO.ui.NumberInputWidget.prototype.getStep = function () {
-       return [ this.step, this.pageStep ];
-};
-
-/**
- * Get the current value of the widget
- * @return {string}
- */
-OO.ui.NumberInputWidget.prototype.getValue = function () {
-       return this.input.getValue();
-};
-
-/**
- * Get the current value of the widget as a number
- * @return {number} May be NaN, or an invalid number
- */
-OO.ui.NumberInputWidget.prototype.getNumericValue = function () {
-       return +this.input.getValue();
-};
-
-/**
- * Set the value of the widget
- * @param {string} value Invalid values are allowed
- */
-OO.ui.NumberInputWidget.prototype.setValue = function ( value ) {
-       this.input.setValue( value );
-};
-
-/**
- * Adjust the value of the widget
- * @param {number} delta Adjustment amount
- */
-OO.ui.NumberInputWidget.prototype.adjustValue = function ( delta ) {
-       var n, v = this.getNumericValue();
-
-       delta = +delta;
-       if ( isNaN( delta ) || !isFinite( delta ) ) {
-               throw new Error( 'Delta must be a finite number' );
-       }
-
-       if ( isNaN( v ) ) {
-               n = 0;
-       } else {
-               n = v + delta;
-               n = Math.max( Math.min( n, this.max ), this.min );
-               if ( this.isInteger ) {
-                       n = Math.round( n );
-               }
-       }
-
-       if ( n !== v ) {
-               this.setValue( n );
-       }
-};
-
-/**
- * Validate input
- * @private
- * @param {string} value Field value
- * @return {boolean}
- */
-OO.ui.NumberInputWidget.prototype.validateNumber = function ( value ) {
-       var n = +value;
-       if ( isNaN( n ) || !isFinite( n ) ) {
-               return false;
-       }
-
-       /*jshint bitwise: false */
-       if ( this.isInteger && ( n | 0 ) !== n ) {
-               return false;
-       }
-       /*jshint bitwise: true */
-
-       if ( n < this.min || n > this.max ) {
-               return false;
-       }
-
-       return true;
-};
-
-/**
- * Handle mouse click events.
- *
- * @private
- * @param {number} dir +1 or -1
- */
-OO.ui.NumberInputWidget.prototype.onButtonClick = function ( dir ) {
-       this.adjustValue( dir * this.step );
-};
-
-/**
- * Handle mouse wheel events.
- *
- * @private
- * @param {jQuery.Event} event
- */
-OO.ui.NumberInputWidget.prototype.onWheel = function ( event ) {
-       var delta = 0;
-
-       // Standard 'wheel' event
-       if ( event.originalEvent.deltaMode !== undefined ) {
-               this.sawWheelEvent = true;
-       }
-       if ( event.originalEvent.deltaY ) {
-               delta = -event.originalEvent.deltaY;
-       } else if ( event.originalEvent.deltaX ) {
-               delta = event.originalEvent.deltaX;
-       }
-
-       // Non-standard events
-       if ( !this.sawWheelEvent ) {
-               if ( event.originalEvent.wheelDeltaX ) {
-                       delta = -event.originalEvent.wheelDeltaX;
-               } else if ( event.originalEvent.wheelDeltaY ) {
-                       delta = event.originalEvent.wheelDeltaY;
-               } else if ( event.originalEvent.wheelDelta ) {
-                       delta = event.originalEvent.wheelDelta;
-               } else if ( event.originalEvent.detail ) {
-                       delta = -event.originalEvent.detail;
-               }
-       }
-
-       if ( delta ) {
-               delta = delta < 0 ? -1 : 1;
-               this.adjustValue( delta * this.step );
-       }
-
-       return false;
-};
-
-/**
- * Handle key down events.
- *
- * @private
- * @param {jQuery.Event} e Key down event
- */
-OO.ui.NumberInputWidget.prototype.onKeyDown = function ( e ) {
-       if ( !this.isDisabled() ) {
-               switch ( e.which ) {
-                       case OO.ui.Keys.UP:
-                               this.adjustValue( this.step );
-                               return false;
-                       case OO.ui.Keys.DOWN:
-                               this.adjustValue( -this.step );
-                               return false;
-                       case OO.ui.Keys.PAGEUP:
-                               this.adjustValue( this.pageStep );
-                               return false;
-                       case OO.ui.Keys.PAGEDOWN:
-                               this.adjustValue( -this.pageStep );
-                               return false;
-               }
-       }
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.NumberInputWidget.prototype.setDisabled = function ( disabled ) {
-       // Parent method
-       OO.ui.NumberInputWidget.parent.prototype.setDisabled.call( this, disabled );
-
-       if ( this.input ) {
-               this.input.setDisabled( this.isDisabled() );
-       }
-       if ( this.minusButton ) {
-               this.minusButton.setDisabled( this.isDisabled() );
-       }
-       if ( this.plusButton ) {
-               this.plusButton.setDisabled( this.isDisabled() );
-       }
-
-       return this;
-};
-
-/**
- * ToggleSwitches are switches that slide on and off. Their state is represented by a Boolean
- * value (`true` for ‘on’, and `false` otherwise, the default). The ‘off’ state is represented
- * visually by a slider in the leftmost position.
- *
- *     @example
- *     // Toggle switches in the 'off' and 'on' position.
- *     var toggleSwitch1 = new OO.ui.ToggleSwitchWidget();
- *     var toggleSwitch2 = new OO.ui.ToggleSwitchWidget( {
- *         value: true
- *     } );
- *
- *     // Create a FieldsetLayout to layout and label switches
- *     var fieldset = new OO.ui.FieldsetLayout( {
- *        label: 'Toggle switches'
- *     } );
- *     fieldset.addItems( [
- *         new OO.ui.FieldLayout( toggleSwitch1, { label: 'Off', align: 'top' } ),
- *         new OO.ui.FieldLayout( toggleSwitch2, { label: 'On', align: 'top' } )
- *     ] );
- *     $( 'body' ).append( fieldset.$element );
- *
- * @class
- * @extends OO.ui.ToggleWidget
- * @mixins OO.ui.mixin.TabIndexedElement
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {boolean} [value=false] The toggle switch’s initial on/off state.
- *  By default, the toggle switch is in the 'off' position.
- */
-OO.ui.ToggleSwitchWidget = function OoUiToggleSwitchWidget( config ) {
-       // Parent constructor
-       OO.ui.ToggleSwitchWidget.parent.call( this, config );
-
-       // Mixin constructors
-       OO.ui.mixin.TabIndexedElement.call( this, config );
-
-       // Properties
-       this.dragging = false;
-       this.dragStart = null;
-       this.sliding = false;
-       this.$glow = $( '<span>' );
-       this.$grip = $( '<span>' );
-
-       // Events
-       this.$element.on( {
-               click: this.onClick.bind( this ),
-               keypress: this.onKeyPress.bind( this )
-       } );
-
-       // Initialization
-       this.$glow.addClass( 'oo-ui-toggleSwitchWidget-glow' );
-       this.$grip.addClass( 'oo-ui-toggleSwitchWidget-grip' );
-       this.$element
-               .addClass( 'oo-ui-toggleSwitchWidget' )
-               .attr( 'role', 'checkbox' )
-               .append( this.$glow, this.$grip );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.ToggleSwitchWidget, OO.ui.ToggleWidget );
-OO.mixinClass( OO.ui.ToggleSwitchWidget, OO.ui.mixin.TabIndexedElement );
-
-/* Methods */
-
-/**
- * Handle mouse click events.
- *
- * @private
- * @param {jQuery.Event} e Mouse click event
- */
-OO.ui.ToggleSwitchWidget.prototype.onClick = function ( e ) {
-       if ( !this.isDisabled() && e.which === OO.ui.MouseButtons.LEFT ) {
-               this.setValue( !this.value );
-       }
-       return false;
-};
-
-/**
- * Handle key press events.
- *
- * @private
- * @param {jQuery.Event} e Key press event
- */
-OO.ui.ToggleSwitchWidget.prototype.onKeyPress = function ( e ) {
-       if ( !this.isDisabled() && ( e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER ) ) {
-               this.setValue( !this.value );
-               return false;
-       }
-};
-
-}( OO ) );
index 0886fa6..c565256 100644 (file)
@@ -5,6 +5,10 @@
                "articleRedirect": { "file": {
                        "ltr": "images/icons/articleRedirect-ltr.svg",
                        "rtl": "images/icons/articleRedirect-rtl.svg"
+               } },
+               "upload": { "file": {
+                       "ltr": "images/icons/upload-ltr.svg",
+                       "rtl": "images/icons/upload-rtl.svg"
                } }
        }
 }
index ab04d36..aae5201 100644 (file)
@@ -5,6 +5,10 @@
                "alignCentre": { "file": "images/icons/align-center.svg" },
                "alignLeft": { "file": "images/icons/align-float-left.svg" },
                "alignRight": { "file": "images/icons/align-float-right.svg" },
+               "attachment": { "file": {
+                       "ltr": "images/icons/attachment-ltr.svg",
+                       "rtl": "images/icons/attachment-rtl.svg"
+               } },
                "calendar": { "file": {
                        "ltr": "images/icons/calendar-ltr.svg",
                        "rtl": "images/icons/calendar-rtl.svg"
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/attachment-ltr.png b/resources/lib/oojs-ui/themes/apex/images/icons/attachment-ltr.png
new file mode 100644 (file)
index 0000000..cbcd675
Binary files /dev/null and b/resources/lib/oojs-ui/themes/apex/images/icons/attachment-ltr.png differ
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/attachment-ltr.svg b/resources/lib/oojs-ui/themes/apex/images/icons/attachment-ltr.svg
new file mode 100644 (file)
index 0000000..74a4d64
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="-293 385 24 24">
+    <path d="M-274.3 390.9c-1.6-1.6-4.3-1.5-5.8.1l.2.2c.5.5 1.3.7 2.1.4.8-.3 1.7-.1 2.4.6 1 .9.9 2.4 0 3.4l-7.1 7.1c-.9 1-2.4.9-3.4 0s-.9-2.4 0-3.4l4.4-4.4c.3-.3.9-.5 1.3-.1s.2 1-.1 1.3l-3.4 3.4c-.6.6-.6 1.7.1 2.3l4.3-4.3c.8-.8 1.1-1.8.9-2.7-.2-.9-.9-1.6-1.7-1.9-.9-.2-1.9 0-2.6.7l-4.4 4.4c-1.6 1.6-1.6 4.3.1 5.8 1.5 1.6 4.3 1.5 5.8-.1l7-7c.8-.8 1.2-1.9 1.2-3s-.5-2.1-1.3-2.8c-.7-.7.8.7 0 0-.7-.7.8.7 0 0-.7-.7.8.7 0 0-.7-.7.8.7 0 0-.7-.7.8.7 0 0-.7-.7.8.7 0 0-1.5-1.6.8.7 0 0z"/>
+</svg>
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/attachment-rtl.png b/resources/lib/oojs-ui/themes/apex/images/icons/attachment-rtl.png
new file mode 100644 (file)
index 0000000..43a0161
Binary files /dev/null and b/resources/lib/oojs-ui/themes/apex/images/icons/attachment-rtl.png differ
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/attachment-rtl.svg b/resources/lib/oojs-ui/themes/apex/images/icons/attachment-rtl.svg
new file mode 100644 (file)
index 0000000..f53f5a4
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="-119 70 24 24">
+    <path d="M-113.3 75.6c-.8.7.7-.7 0 0-.8.7.7-.7 0 0-.8.7.7-.7 0 0-.8.7.7-.7 0 0-.8.7.7-.7 0 0-.8.7.7-.7 0 0-.8.7-1.3 1.7-1.3 2.8 0 1.1.4 2.2 1.2 3l7 7c1.5 1.6 4.3 1.7 5.8.1 1.7-1.5 1.7-4.2.1-5.8l-4.4-4.4c-.7-.7-1.7-.9-2.6-.7-.8.3-1.5 1-1.7 1.9-.2.9.1 1.9.9 2.7l4.3 4.3c.7-.6.7-1.7.1-2.3l-3.4-3.4c-.3-.3-.5-.9-.1-1.3s1-.2 1.3.1l4.4 4.4c.9 1 1 2.5 0 3.4s-2.5 1-3.4 0l-7.1-7.1c-.9-1-1-2.5 0-3.4.7-.7 1.6-.9 2.4-.6.8.3 1.6.1 2.1-.4l.2-.2c-1.5-1.6-4.2-1.8-5.8-.1-.8.7 1.5-1.7 0 0z"/>
+</svg>
index bdd9abe..e27be3c 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/apex/images/icons/language-ltr.png and b/resources/lib/oojs-ui/themes/apex/images/icons/language-ltr.png differ
index 4f1fc10..d64b734 100644 (file)
@@ -1,7 +1,10 @@
 <?xml version="1.0" encoding="utf-8"?>
 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
-    <g id="translation">
-        <path id="english" d="M14.34 9l-3.53 10h2.064l.72-2.406h3.624l.72 2.406H20L16.465 9h-2.12zm1.065 1.53L16.75 15h-2.69z"/>
-        <path id="chinese" d="M8.97 4.22c-.43.29-.88.616-1.25.874l.186.312c.14.194.275.393.407.594H4.47v1.47h1.593c.43 1.41 1.11 2.624 2.03 3.624-1.008.664-2.192 1.248-3.624 1.75L4 13c.317.487.714.976 1.03 1.375l.25-.094c1.593-.59 2.91-1.263 4.032-2.06.818.63 1.71 1.16 2.657 1.595l.56-1.624a13.21 13.21 0 0 1-1.908-1.063c.284-.28.59-.634.906-1.156.46-.716.776-1.57 1-2.5h1.657V6h-4.063c-.283-.552-.596-1.083-.97-1.53l-.186-.25zM7.72 7.47h3.186c-.32 1.075-.83 1.937-1.53 2.624-.713-.705-1.26-1.568-1.657-2.625zm6.31 5.31l-.467 1.658c.292-.514.577-1.075.812-1.532l-.344-.125z"/>
+    <g id="A">
+        <path d="M18.738 15.673l1.137 3.15h1.575L17.775 7.448h-2.188l-3.85 11.375h1.575l1.05-3.15h4.375zM16.55 8.76l1.837 5.427h-3.675l1.838-5.425z"/>
+    </g>
+    <g id="文">
+        <path d="M8.325 6.573h.787l-.875-1.75h-1.75l.438.875a1.56 1.56 0 0 0 1.4.875z"/>
+        <path d="m 9.202,12.874 c 0.7,0.525 1.486,0.963 2.45,1.225 l -0.438,1.31 A 9.17,9.17 0 0 1 8.151,13.835 c -1.49,1.137 -3.063,1.837 -4.813,2.363 L 2.9,14.885 C 4.386,14.36 5.874,13.835 7.1,12.872 5.962,11.648 5.174,10.335 4.65,8.758 l -1.663,0 0,-1.31 10.85,0 -0.438,1.312 -1.75,0 c -0.308,1.33 -1.255,2.957 -2.45,4.114 z m 1.05,-4.114 -4.114,0 c 0.35,1.226 1.138,2.363 2.013,3.238 0.926,-1 1.617,-1.957 2.1,-3.237 z"/>
     </g>
 </svg>
index ed64644..a7fd0cd 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/apex/images/icons/language-rtl.png and b/resources/lib/oojs-ui/themes/apex/images/icons/language-rtl.png differ
index 081252e..cf6c1d5 100644 (file)
@@ -1,7 +1,10 @@
 <?xml version="1.0" encoding="utf-8"?>
 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
-    <g id="translation">
-        <path id="english" d="M7.53 9L4 19h2.063l.72-2.406h3.624l.72 2.406h2.062L9.65 9H7.53zm1.064 1.53L9.938 15H7.25z"/>
-        <path id="chinese" d="M14.594 4.22c-.43.29-.88.616-1.25.874l.187.312c.14.194.28.393.41.594h-3.843v1.47h1.594c.43 1.41 1.11 2.624 2.03 3.624-.662.437-1.413.82-2.25 1.187l.563 1.567a15.882 15.882 0 0 0 2.908-1.625 13.82 13.82 0 0 0 3.97 2.125l.28.094c.293-.514.578-1.075.813-1.532l-.375-.125c-1.38-.49-2.49-1.05-3.375-1.654.284-.28.59-.635.906-1.157.46-.717.775-1.572 1-2.5h1.656V6H15.75c-.283-.552-.596-1.083-.97-1.53l-.186-.25zm-1.25 3.25h3.187c-.315 1.075-.825 1.937-1.53 2.624-.71-.705-1.26-1.568-1.653-2.625zM9.97 12.874L9.624 13c.196.3.406.594.625.875l-.28-1z"/>
+    <g id="A">
+        <path d="M5.612 15.673l-1.137 3.15H2.9L6.575 7.448h2.188l3.85 11.375h-1.575l-1.05-3.15H5.613zM7.8 8.76l-1.837 5.427h3.675L7.8 8.762z" id="path5"/>
+    </g>
+    <g id="文">
+        <path d="M16.384 6.573h.787l-.873-1.75h-1.75l.438.875c.26.535.805.874 1.4.875z" id="path7"/>
+        <path d="M15.15 12.874c-.7.525-1.486.963-2.45 1.225l.438 1.31a9.17 9.17 0 0 0 3.063-1.575c1.49 1.137 3.064 1.837 4.814 2.363l.438-1.313c-1.486-.525-2.974-1.05-4.2-2.013 1.138-1.224 1.926-2.537 2.45-4.114h1.663v-1.31h-10.85l.438 1.312h1.75c.308 1.33 1.255 2.957 2.45 4.114zM14.1 8.76h4.114c-.35 1.226-1.138 2.363-2.013 3.238-.925-1-1.616-1.957-2.1-3.237z" id="path11-7"/>
     </g>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/upload-ltr.png b/resources/lib/oojs-ui/themes/apex/images/icons/upload-ltr.png
new file mode 100644 (file)
index 0000000..fa2da24
Binary files /dev/null and b/resources/lib/oojs-ui/themes/apex/images/icons/upload-ltr.png differ
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/upload-ltr.svg b/resources/lib/oojs-ui/themes/apex/images/icons/upload-ltr.svg
new file mode 100644 (file)
index 0000000..dc4676f
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <path d="M10 13c0 1.7 1.3 3 3 3V9h3l-4.5-5L7 9h3v4zm7 0v5H7c-.6 0-1-.4-1-1v-4H4v4c0 1.9 1.3 3 3 3h12v-7h-2z"/>
+</svg>
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/upload-rtl.png b/resources/lib/oojs-ui/themes/apex/images/icons/upload-rtl.png
new file mode 100644 (file)
index 0000000..1ac6106
Binary files /dev/null and b/resources/lib/oojs-ui/themes/apex/images/icons/upload-rtl.png differ
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/upload-rtl.svg b/resources/lib/oojs-ui/themes/apex/images/icons/upload-rtl.svg
new file mode 100644 (file)
index 0000000..ed3fe77
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <path d="M13 13c0 1.7-1.3 3-3 3V9H7l4.5-5L16 9h-3v4zm-7 0v5h10c.6 0 1-.4 1-1v-4h2v4c0 1.9-1.3 3-3 3H4v-7h2z"/>
+</svg>
index c855f16..4db45f4 100644 (file)
@@ -4,7 +4,7 @@
        "intro": "@import '../../../../src/styles/common';",
        "variants": {
                "invert": {
-                       "color": "#FFFFFF",
+                       "color": "#ffffff",
                        "global": true
                }
        },
index 04c5299..79f644e 100644 (file)
@@ -4,7 +4,7 @@
        "intro": "@import '../../../../src/styles/common';",
        "variants": {
                "invert": {
-                       "color": "#FFFFFF",
+                       "color": "#ffffff",
                        "global": true
                }
        },
index f5b6693..0c5f6f9 100644 (file)
@@ -4,7 +4,7 @@
        "intro": "@import '../../../../src/styles/common';",
        "variants": {
                "invert": {
-                       "color": "#FFFFFF",
+                       "color": "#ffffff",
                        "global": true
                }
        },
index dbb3411..b03950c 100644 (file)
@@ -4,7 +4,7 @@
        "intro": "@import '../../../../src/styles/common';",
        "variants": {
                "invert": {
-                       "color": "#FFFFFF",
+                       "color": "#ffffff",
                        "global": true
                }
        },
                "alignCentre": { "file": "images/icons/align-center.svg" },
                "alignLeft": { "file": "images/icons/align-float-left.svg" },
                "alignRight": { "file": "images/icons/align-float-right.svg" },
+               "attachment": { "file": {
+                       "ltr": "images/icons/attachment-ltr.svg",
+                       "rtl": "images/icons/attachment-rtl.svg"
+               } },
                "calendar": { "file": {
                        "ltr": "images/icons/calendar-ltr.svg",
                        "rtl": "images/icons/calendar-rtl.svg"
index 2a383df..ffa8905 100644 (file)
@@ -4,20 +4,20 @@
        "intro": "@import '../../../../src/styles/common';",
        "variants": {
                "invert": {
-                       "color": "#FFFFFF",
+                       "color": "#ffffff",
                        "global": true
                },
                "progressive": {
-                       "color": "#347BFF"
+                       "color": "#347bff"
                },
                "constructive": {
-                       "color": "#00AF89"
+                       "color": "#00af89"
                },
                "destructive": {
-                       "color": "#D11D13"
+                       "color": "#d11d13"
                },
                "warning": {
-                       "color": "#FF5D00"
+                       "color": "#ff5d00"
                }
        },
        "images": {
index a8ace26..489d6ca 100644 (file)
@@ -4,7 +4,7 @@
        "intro": "@import '../../../../src/styles/common';",
        "variants": {
                "invert": {
-                       "color": "#FFFFFF",
+                       "color": "#ffffff",
                        "global": true
                }
        },
index f7d63a1..28188ea 100644 (file)
@@ -4,7 +4,7 @@
        "intro": "@import '../../../../src/styles/common';",
        "variants": {
                "invert": {
-                       "color": "#FFFFFF",
+                       "color": "#ffffff",
                        "global": true
                }
        },
index d5c0662..c3263e2 100644 (file)
@@ -4,7 +4,7 @@
        "intro": "@import '../../../../src/styles/common';",
        "variants": {
                "invert": {
-                       "color": "#FFFFFF",
+                       "color": "#ffffff",
                        "global": true
                }
        },
index 47282d9..b713146 100644 (file)
@@ -4,20 +4,20 @@
        "intro": "@import '../../../../src/styles/common';",
        "variants": {
                "invert": {
-                       "color": "#FFFFFF",
+                       "color": "#ffffff",
                        "global": true
                },
                "progressive": {
-                       "color": "#347BFF"
+                       "color": "#347bff"
                },
                "constructive": {
-                       "color": "#00AF89"
+                       "color": "#00af89"
                },
                "destructive": {
-                       "color": "#D11D13"
+                       "color": "#d11d13"
                },
                "warning": {
-                       "color": "#FF5D00"
+                       "color": "#ff5d00"
                }
        },
        "images": {
index 197989b..c53946f 100644 (file)
@@ -4,7 +4,7 @@
        "intro": "@import '../../../../src/styles/common';",
        "variants": {
                "invert": {
-                       "color": "#FFFFFF",
+                       "color": "#ffffff",
                        "global": true
                }
        },
index ddd95ad..809bc6a 100644 (file)
@@ -4,7 +4,7 @@
        "intro": "@import '../../../../src/styles/common';",
        "variants": {
                "invert": {
-                       "color": "#FFFFFF",
+                       "color": "#ffffff",
                        "global": true
                }
        },
index cec844c..7029bc2 100644 (file)
@@ -4,20 +4,20 @@
        "intro": "@import '../../../../src/styles/common';",
        "variants": {
                "invert": {
-                       "color": "#FFFFFF",
+                       "color": "#ffffff",
                        "global": true
                },
                "progressive": {
-                       "color": "#347BFF"
+                       "color": "#347bff"
                },
                "constructive": {
-                       "color": "#00AF89"
+                       "color": "#00af89"
                },
                "destructive": {
-                       "color": "#D11D13"
+                       "color": "#d11d13"
                },
                "warning": {
-                       "color": "#FF5D00"
+                       "color": "#ff5d00"
                }
        },
        "images": {
index 2a3a3c5..c216c37 100644 (file)
@@ -4,7 +4,7 @@
        "intro": "@import '../../../../src/styles/common';",
        "variants": {
                "invert": {
-                       "color": "#FFFFFF",
+                       "color": "#ffffff",
                        "global": true
                }
        },
index 3e74ac6..cf19c6c 100644 (file)
@@ -4,7 +4,7 @@
        "intro": "@import '../../../../src/styles/common';",
        "variants": {
                "invert": {
-                       "color": "#FFFFFF",
+                       "color": "#ffffff",
                        "global": true
                }
        },
index 0c3b4eb..c332e3c 100644 (file)
@@ -4,20 +4,20 @@
        "intro": "@import '../../../../src/styles/common';",
        "variants": {
                "invert": {
-                       "color": "#FFFFFF",
+                       "color": "#ffffff",
                        "global": true
                },
                "progressive": {
-                       "color": "#347BFF"
+                       "color": "#347bff"
                },
                "constructive": {
-                       "color": "#00AF89"
+                       "color": "#00af89"
                },
                "destructive": {
-                       "color": "#D11D13"
+                       "color": "#d11d13"
                },
                "warning": {
-                       "color": "#FF5D00"
+                       "color": "#ff5d00"
                }
        },
        "images": {
index dab0bea..0bd0e73 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #00AF89 }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #00af89 }</style>
     <g id="add">
         <path id="plus" d="M13 6h-2v5H6v2h5v5h2v-5h5v-2h-5z"/>
     </g>
index 35322d0..bedfe5a 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="add">
         <path id="plus" d="M13 6h-2v5H6v2h5v5h2v-5h5v-2h-5z"/>
     </g>
index fad6a26..9cf7fab 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M20 13.44v-2.88l-1.8-.3c-.1-.397-.3-.794-.6-1.39l1.1-1.49-2.1-2.088-1.5 1.093c-.5-.298-1-.497-1.4-.596L13.5 4h-2.9l-.3 1.79c-.5.098-.9.297-1.4.595L7.4 5.292 5.3 7.38l1 1.49c-.3.496-.4.894-.6 1.39l-1.7.2v2.882l1.8.298c.1.497.3.894.6 1.39l-1 1.492 2.1 2.087 1.5-1c.4.2.9.395 1.4.594l.3 1.79h3l.3-1.79c.5-.1.9-.298 1.4-.596l1.5 1.092 2.1-2.08-1.1-1.49c.3-.496.5-.993.6-1.39l1.5-.3zm-8 1.492c-1.7 0-3-1.292-3-2.982 0-1.69 1.3-2.98 3-2.98s3 1.29 3 2.98-1.3 2.982-3 2.982z"/>
 </svg>
index 55621b9..74bb91d 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="alert">
         <path id="point" d="M11 16h2v2h-2z"/>
         <path id="stroke" d="M13.516 10h-3L11 15h2z"/>
index bdf0ac2..c5c5687 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FF5D00 }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ff5d00 }</style>
     <g id="alert">
         <path id="point" d="M11 16h2v2h-2z"/>
         <path id="stroke" d="M13.516 10h-3L11 15h2z"/>
index fef1152..197e037 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M9 9h6c.554 0 1 .446 1 1v5c0 .554-.446 1-1 1H9c-.554 0-1-.446-1-1v-5c0-.554.446-1 1-1zm-5.5 9h17a.5.5 0 0 1 0 1h-17a.5.5 0 0 1 0-1zm0-12h17a.5.5 0 0 1 0 1h-17a.5.5 0 0 1 0-1z" id="align-center"/>
 </svg>
index 2b0bc65..663ba94 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M4 9h6c.554 0 1 .446 1 1v5c0 .554-.446 1-1 1H4c-.554 0-1-.446-1-1v-5c0-.554.446-1 1-1zm9.5 0h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1 0-1zm0 3h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1 0-1zm0 3h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1 0-1zm-10-9h17a.5.5 0 0 1 0 1h-17a.5.5 0 0 1 0-1zm0 12h17a.5.5 0 0 1 0 1h-17a.5.5 0 0 1 0-1z" id="align-float-left"/>
 </svg>
index d126163..41be069 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M20 9h-6c-.554 0-1 .446-1 1v5c0 .554.446 1 1 1h6c.554 0 1-.446 1-1v-5c0-.554-.446-1-1-1zm-9.5 0h-7a.5.5 0 0 0 0 1h7a.5.5 0 0 0 0-1zm0 3h-7a.5.5 0 0 0 0 1h7a.5.5 0 0 0 0-1zm0 3h-7a.5.5 0 0 0 0 1h7a.5.5 0 0 0 0-1zm10-9h-17a.5.5 0 0 0 0 1h17a.5.5 0 0 0 0-1zm0 12h-17a.5.5 0 0 0 0 1h17a.5.5 0 0 0 0-1z" id="align-float-right"/>
 </svg>
index 0351e89..3dc5125 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M13.3 6.3l6.3 5.7-6.3 5.7v-3.8H12c-3.2 0-6.3 1.3-7.6 3.8 0-4.7 2.8-7.6 7.9-7.6h.9V6.3z"/>
 </svg>
index 3bb7096..0e2c2f3 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M10.7 6.3L4.4 12l6.3 5.7v-3.8H12c3.2 0 6.3 1.3 7.6 3.8 0-4.7-2.8-7.6-7.9-7.6h-.9V6.3z"/>
 </svg>
index 6dacb76..3c5c48b 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M16 12H6c-1.7 0-3 1.3-3 3h13v3l5-4.5L16 9v3z"/>
 </svg>
index 269de66..da0c6ec 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M8 12h10c1.7 0 3 1.3 3 3H8v3l-5-4.5L8 9v3z"/>
 </svg>
index 19df2c0..80e7992 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M12 10h4V5h-4v5zm-5 2h9v-1H7v1zm0 2h9v-1H7v1zm0 2h9v-1H7v1zm4-9H7v1h4V7zm0 2H7v1h4V9zm0-4H7v1h4V5zM5 3h13v16H8c-1.7 0-3-1.3-3-3V3z"/>
 </svg>
index 0fba841..f0a4a6a 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M11 10H7V5h4v5zm5 2H7v-1h9v1zm0 2H7v-1h9v1zm0 2H7v-1h9v1zm-4-9h4v1h-4V7zm0 2h4v1h-4V9zm0-4h4v1h-4V5zm6-2H5v16h10c1.7 0 3-1.3 3-3V3z"/>
 </svg>
index b1636c1..77febef 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M21 11l-6 7-4-4-1 1 5 5 7-8z"/>
     <path d="M17 14V3H4v13c0 1.7 1.3 3 3 3h5l-3-3H6v-1h2.6l1-1H6v-1h9v1h-2l1 1h2l1-1zM6 5h4v1H6V5zm0 2h4v1H6V7zm0 2h4v1H6V9zm9 3H6v-1h9v1zm-4-2V5h4v5h-4z"/>
 </svg>
index d331123..c80c83f 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M5 11l6 7 4-4 1 1-5 5-7-8z"/>
     <path d="M9 14V3h13v13c0 1.7-1.3 3-3 3h-5l3-3h3v-1h-2.6l-1-1H20v-1h-9v1h2l-1 1h-2l-1-1zm11-9h-4v1h4V5zm0 2h-4v1h4V7zm0 2h-4v1h4V9zm-9 3h9v-1h-9v1zm4-2V5h-4v5h4z"/>
 </svg>
index 5317700..0ed1d31 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="article-redirect">
         <path id="arrow" d="M18.1 14.2L23 18l-4.9 4.8v-2.2c-1.7 0-2.9-.2-4.3-1.2-1.2-.8-2.5-2.6-2.3-4.1 1.4 1 2.9 1.5 4.4 1.5.7 0 1.4-.1 2.1-.3l.1-2.3"/>
         <path id="page" d="M5 3v13c0 1.7 1.3 3 3 3h3.375c-.157-.205-.3-.43-.438-.656-.42-.688-.77-1.483-.843-2.344H7v-1h3.125l.125-1H7v-1h3.375l.03-.188.283.188H16v1h-3.906l.22.156c.523.375 1.065.64 1.592.844H16v.406c.208-.013.418-.07.625-.094.068-1.294.125-3.874.125-3.874l1.25.968V3H5zm2 2h4v1H7V5zm5 0h4v5h-4V5zM7 7h4v1H7V7zm0 2h4v1H7V9zm0 2h9v1H7v-1z"/>
index 97bae5a..1a5b22a 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="article-redirect">
         <path id="arrow" d="M5.9 14.2L1 18l4.9 4.8v-2.2c1.7 0 2.9-.2 4.3-1.2 1.2-.8 2.5-2.6 2.3-4.1-1.4 1-2.9 1.5-4.4 1.5-.7 0-1.4-.1-2.1-.3l-.1-2.3"/>
         <path id="page" d="M19 3v13c0 1.7-1.3 3-3 3h-3.375c.157-.205.3-.43.438-.656.42-.688.77-1.483.843-2.344H17v-1h-3.125l-.125-1H17v-1h-3.375l-.03-.188-.283.188H8v1h3.906l-.22.156a7.097 7.097 0 0 1-1.592.844H8v.406c-.208-.013-.418-.07-.625-.094a178.903 178.903 0 0 1-.125-3.874L6 12.405V3zm-2 2h-4v1h4zm-5 0H8v5h4zm5 2h-4v1h4zm0 2h-4v1h4zm0 2H8v1h9z"/>
index 545ea93..de091d9 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M19.1 18.5c.6-.7.9-1.5.9-2.5 0-2.2-1.8-4-4-4s-4 1.8-4 4 1.8 4 4 4c.7 0 1.3-.1 1.8-.4l2.7 2.7 1.1-1.1-2.5-2.7zm-3.1-.3c-1.2 0-2.2-1-2.2-2.3 0-1.2 1-2.2 2.2-2.2 1.2 0 2.3 1 2.3 2.2-.1 1.3-1.1 2.3-2.3 2.3zM11.8 13c.3-.4.6-.7 1-1H7v-1h9s1.2 0 2 .6V3H5v13c0 1.7 1.3 3 3 3h3.8c-.6-.8-1-1.9-1-3H7v-1h3.9l.3-1H7v-1h4.8zm.2-8h4v5h-4V5zM7 5h4v1H7V5zm0 2h4v1H7V7zm0 2h4v1H7V9z"/>
 </svg>
index b93fd7c..abb7ce7 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M7.5 18.5c-.6-.7-.9-1.5-.9-2.5 0-2.2 1.8-4 4-4s4 1.8 4 4-1.8 4-4 4c-.7 0-1.3-.1-1.8-.4l-2.7 2.7L5 21.2l2.5-2.7zm3.1-.3c1.2 0 2.2-1 2.2-2.3 0-1.2-1-2.2-2.2-2.2-1.2 0-2.3 1-2.3 2.2.1 1.3 1.1 2.3 2.3 2.3zm4.2-5.2c-.3-.4-.6-.7-1-1h5.8v-1h-9s-1.2 0-2 .6V3h13v13c0 1.7-1.3 3-3 3h-3.8c.6-.8 1-1.9 1-3h3.8v-1h-3.9l-.3-1h4.2v-1h-4.8zm-.2-8h-4v5h4V5zm5 0h-4v1h4V5zm0 2h-4v1h4V7zm0 2h-4v1h4V9z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/attachment-ltr-invert.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/attachment-ltr-invert.png
new file mode 100644 (file)
index 0000000..a77a23e
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/attachment-ltr-invert.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/attachment-ltr-invert.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/attachment-ltr-invert.svg
new file mode 100644 (file)
index 0000000..67c3ae9
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="-293 385 24 24"><style>* { fill: #ffffff }</style>
+    <path d="M-274.3 390.9c-1.6-1.6-4.3-1.5-5.8.1l.2.2c.5.5 1.3.7 2.1.4.8-.3 1.7-.1 2.4.6 1 .9.9 2.4 0 3.4l-7.1 7.1c-.9 1-2.4.9-3.4 0s-.9-2.4 0-3.4l4.4-4.4c.3-.3.9-.5 1.3-.1s.2 1-.1 1.3l-3.4 3.4c-.6.6-.6 1.7.1 2.3l4.3-4.3c.8-.8 1.1-1.8.9-2.7-.2-.9-.9-1.6-1.7-1.9-.9-.2-1.9 0-2.6.7l-4.4 4.4c-1.6 1.6-1.6 4.3.1 5.8 1.5 1.6 4.3 1.5 5.8-.1l7-7c.8-.8 1.2-1.9 1.2-3s-.5-2.1-1.3-2.8c-.7-.7.8.7 0 0-.7-.7.8.7 0 0-.7-.7.8.7 0 0-.7-.7.8.7 0 0-.7-.7.8.7 0 0-.7-.7.8.7 0 0-1.5-1.6.8.7 0 0z"/>
+</svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/attachment-ltr.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/attachment-ltr.png
new file mode 100644 (file)
index 0000000..cbcd675
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/attachment-ltr.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/attachment-ltr.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/attachment-ltr.svg
new file mode 100644 (file)
index 0000000..74a4d64
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="-293 385 24 24">
+    <path d="M-274.3 390.9c-1.6-1.6-4.3-1.5-5.8.1l.2.2c.5.5 1.3.7 2.1.4.8-.3 1.7-.1 2.4.6 1 .9.9 2.4 0 3.4l-7.1 7.1c-.9 1-2.4.9-3.4 0s-.9-2.4 0-3.4l4.4-4.4c.3-.3.9-.5 1.3-.1s.2 1-.1 1.3l-3.4 3.4c-.6.6-.6 1.7.1 2.3l4.3-4.3c.8-.8 1.1-1.8.9-2.7-.2-.9-.9-1.6-1.7-1.9-.9-.2-1.9 0-2.6.7l-4.4 4.4c-1.6 1.6-1.6 4.3.1 5.8 1.5 1.6 4.3 1.5 5.8-.1l7-7c.8-.8 1.2-1.9 1.2-3s-.5-2.1-1.3-2.8c-.7-.7.8.7 0 0-.7-.7.8.7 0 0-.7-.7.8.7 0 0-.7-.7.8.7 0 0-.7-.7.8.7 0 0-.7-.7.8.7 0 0-1.5-1.6.8.7 0 0z"/>
+</svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/attachment-rtl-invert.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/attachment-rtl-invert.png
new file mode 100644 (file)
index 0000000..e2e2e02
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/attachment-rtl-invert.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/attachment-rtl-invert.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/attachment-rtl-invert.svg
new file mode 100644 (file)
index 0000000..61e11d0
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="-119 70 24 24"><style>* { fill: #ffffff }</style>
+    <path d="M-113.3 75.6c-.8.7.7-.7 0 0-.8.7.7-.7 0 0-.8.7.7-.7 0 0-.8.7.7-.7 0 0-.8.7.7-.7 0 0-.8.7.7-.7 0 0-.8.7-1.3 1.7-1.3 2.8 0 1.1.4 2.2 1.2 3l7 7c1.5 1.6 4.3 1.7 5.8.1 1.7-1.5 1.7-4.2.1-5.8l-4.4-4.4c-.7-.7-1.7-.9-2.6-.7-.8.3-1.5 1-1.7 1.9-.2.9.1 1.9.9 2.7l4.3 4.3c.7-.6.7-1.7.1-2.3l-3.4-3.4c-.3-.3-.5-.9-.1-1.3s1-.2 1.3.1l4.4 4.4c.9 1 1 2.5 0 3.4s-2.5 1-3.4 0l-7.1-7.1c-.9-1-1-2.5 0-3.4.7-.7 1.6-.9 2.4-.6.8.3 1.6.1 2.1-.4l.2-.2c-1.5-1.6-4.2-1.8-5.8-.1-.8.7 1.5-1.7 0 0z"/>
+</svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/attachment-rtl.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/attachment-rtl.png
new file mode 100644 (file)
index 0000000..43a0161
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/attachment-rtl.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/attachment-rtl.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/attachment-rtl.svg
new file mode 100644 (file)
index 0000000..f53f5a4
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="-119 70 24 24">
+    <path d="M-113.3 75.6c-.8.7.7-.7 0 0-.8.7.7-.7 0 0-.8.7.7-.7 0 0-.8.7.7-.7 0 0-.8.7.7-.7 0 0-.8.7.7-.7 0 0-.8.7-1.3 1.7-1.3 2.8 0 1.1.4 2.2 1.2 3l7 7c1.5 1.6 4.3 1.7 5.8.1 1.7-1.5 1.7-4.2.1-5.8l-4.4-4.4c-.7-.7-1.7-.9-2.6-.7-.8.3-1.5 1-1.7 1.9-.2.9.1 1.9.9 2.7l4.3 4.3c.7-.6.7-1.7.1-2.3l-3.4-3.4c-.3-.3-.5-.9-.1-1.3s1-.2 1.3.1l4.4 4.4c.9 1 1 2.5 0 3.4s-2.5 1-3.4 0l-7.1-7.1c-.9-1-1-2.5 0-3.4.7-.7 1.6-.9 2.4-.6.8.3 1.6.1 2.1-.4l.2-.2c-1.5-1.6-4.2-1.8-5.8-.1-.8.7 1.5-1.7 0 0z"/>
+</svg>
index 13f6ede..7ed3635 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M17.5 14V9c0-3-2.3-5-5.5-5S6.5 6 6.5 9v5c0 2 0 3-2 3v1h15v-1c-2 0-2-1-2-3zM12 20H9c0 1 1.6 2 3 2s3-1 3-2h-3z"/>
 </svg>
index 7dfcb1c..c032294 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M17.8 14.7l1.7-4.7c1-2.8-.5-5.5-3.5-6.6s-5.9 0-6.9 2.8l-1.7 4.7c-.7 1.9-1 2.8-2.9 2.1l-.3 1 14.1 5.1.3-.9c-1.9-.7-1.5-1.6-.8-3.5zM12 19.8l-2.8-1c-.3.9.8 2.4 2.1 2.9s3.2.1 3.5-.9l-2.8-1z"/>
 </svg>
index 83ed027..34ec94b 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M6.21 14.7L4.51 10c-1-2.8.5-5.5 3.5-6.6 3-1.1 5.9 0 6.9 2.8l1.7 4.7c.7 1.9 1 2.8 2.9 2.1l.3 1-14.1 5.1-.3-.9c1.9-.7 1.5-1.6.8-3.5zm5.8 5.1l2.8-1c.3.9-.8 2.4-2.1 2.9s-3.2.1-3.5-.9l2.8-1z"/>
 </svg>
index 4d8c673..d463577 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M12 4c-4.4 0-8 3.6-8 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm4 12l-3-2-1 4-1-4-3 2 2-3-4-1 4-1-2-3 3 2 1-4 1 4 3-2-2 3 4 1-4 1 2 3z"/>
 </svg>
index 5058629..2b8ee7a 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M15.3 14.7C16.1 10.9 14.7 4 12 4c-2.7 0-4.2 6.7-3.4 10.5L7 18h2.7l.3 1h4c.2-.3.1-.5.3-1H17l-1.7-3.3zM12 10c-.8 0-1.5-.7-1.5-1.5S11.2 7 12 7s1.5.7 1.5 1.5S12.8 10 12 10zm2 10c0 1.1-2 2-2 2s-2-.9-2-2"/>
 </svg>
index 9b3cdeb..41d644a 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M12.666 6h-1.372l-4.48 12H8.52l1.493-4h4l1.507 4h1.666l-4.52-12zm-2.28 7l1.617-4.333L13.637 13h-3.25z" id="a"/>
     <g id="up">
         <path id="arrow" d="M15.5 9h7L19 3z"/>
index 3d00d67..4101ddf 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M12.666 6h-1.372l-4.48 12H8.52l1.493-4h4l1.507 4h1.666l-4.52-12zm-2.28 7l1.617-4.333L13.637 13h-3.25z" id="a"/>
     <g id="up">
         <path id="arrow" d="M1.5 9h7L5 3z"/>
index 973a140..2cf60bc 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #D11D13 }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #d11d13 }</style>
     <path d="M12 4c-4.4 0-8 3.6-8 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm5 9H7v-2h10v2z"/>
 </svg>
index 551069e..a2f916e 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M12 4c-4.4 0-8 3.6-8 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm5 9H7v-2h10v2z"/>
 </svg>
index 2848a10..1d41a44 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M17 11v2h-2l3.6 3.6c.9-1.3 1.4-2.9 1.4-4.6 0-4.4-3.6-8-8-8-1.7 0-3.3.5-4.6 1.4L13 11h4zM4 4L3 5l2.4 2.4C4.5 8.7 4 10.3 4 12c0 4.4 3.6 8 8 8 1.7 0 3.3-.5 4.6-1.4L19 21l1-1L4 4zm3 9v-2h2l2 2H7z"/>
 </svg>
index 92994a6..ca592ed 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M7 11v2h2l-3.6 3.6C4.5 15.3 4 13.7 4 12c0-4.4 3.6-8 8-8 1.7 0 3.3.5 4.6 1.4L11 11H7zm13-7l1 1-2.4 2.4c.9 1.3 1.4 2.9 1.4 4.6 0 4.4-3.6 8-8 8-1.7 0-3.3-.5-4.6-1.4L5 21l-1-1L20 4zm-3 9v-2h-2l-2 2h4z"/>
 </svg>
index 97f3d2e..519f850 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M16 18h3L14 6h-3L6 18h3l1.25-3h4.5L16 18zm-4.917-5L12.5 9.6l1.417 3.4h-2.834z" id="bold-a"/>
 </svg>
index 2e11204..e43c4b5 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="bold-arab-ain">
         <path id="arab-ain" d="M9.337 13.616c0 1.35 1.386 2.1 4.16 2.258l2.186-.03.318.045c-.03.12-.25.34-.66.65l-.09.06c-1.233.93-2.42 1.394-3.56 1.394-1.14 0-2.043-.33-2.71-.99-.65-.66-.973-1.56-.973-2.7.006-1.353.567-2.572 1.685-3.657v-.044l-.606-.55a.952.952 0 0 1-.222-.63c0-.49.24-1.11.72-1.863.65-1.045 1.302-1.565 1.957-1.56.886.005 1.618.42 2.194 1.246.325.48-.03.55-1.064.22-.843-.33-1.528-.05-2.055.826l.016.074 1.125.866.05.005c1.405-.497 2.42-.74 3.044-.725-.06.116-.14.36-.244.732a27.75 27.75 0 0 1-.304.982l-.125.372-.386.05c-1.743.24-2.992.716-3.745 1.43-.465.463-.7.972-.704 1.524"/>
     </g>
index e55b63f..7f24cbc 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="bold-arab-dad">
         <path id="arab-dad" d="M16.41 8.232l-1.675-.665L15.43 6l1.687.64-.707 1.592m.775 3.078c-.51-.286-1-.427-1.476-.423-.478 0-.99.205-1.54.616l-.505.38.006.024c1.085.066 1.935.1 2.55.1h.315c.57-.022.994-.065 1.278-.132-.067-.17-.275-.36-.625-.566h-.006M10.38 14.6c-.016-.905-.33-1.87-.937-2.9l1.294-1.73.118.15c.267.337.504.925.713 1.767l.064.05c.496-.007.942-.17 1.338-.484v-.006l1.732-1.53c.68-.6 1.282-.9 1.807-.9.383.003.85.194 1.394.57.55.378.884.697 1 .96.063.15.094.385.094.71 0 .694-.11 1.227-.33 1.596-.193.31-.474.555-.845.734-.438.208-1.55.312-3.333.312-.8 0-1.794-.02-2.982-.064l-.142.43c-.254.67-.463 1.112-.625 1.323-.724.937-1.785 1.405-3.182 1.405-1.71-.006-2.56-.92-2.56-2.74.003-.94.278-1.814.824-2.618.15-.216.298-.367.444-.454.225-.133.288-.09.188.124-.396.862-.596 1.548-.6 2.058.008 1.177.752 1.768 2.232 1.772 1.038-.004 1.803-.182 2.295-.535"/>
     </g>
index 416e0f5..3ad81f5 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="bold-armn-to">
         <path id="armn-to" d="M13.86 16.257c.124 0 .254-.026.39-.078.135-.06.257-.15.367-.28a1.43 1.43 0 0 0 .273-.517c.073-.214.11-.48.11-.798V13h-1.14c-.14 0-.284.026-.43.078a.905.905 0 0 0-.383.258c-.11.125-.2.294-.274.508-.067.213-.1.487-.1.82 0 .34.035.47.108.695.08.21.18.39.29.53.12.13.25.23.39.29.14.05.276.07.406.07m-2.97-7.84a2.67 2.67 0 0 0-.975.45 2.1 2.1 0 0 0-.672.813c-.16.342-.242.78-.242 1.31V18H6v-7.188c0-.776.15-1.455.453-2.04a4.227 4.227 0 0 1 1.234-1.467c.52-.39 1.13-.685 1.83-.883a8.114 8.114 0 0 1 2.225-.297c.526 0 1.04.044 1.54.133.504.088.98.22 1.43.398.447.172.858.388 1.233.65.375.26.698.564.97.913.275.34.49.73.64 1.17.15.43.226 1.09.226 1.61h1.36v2.04h-1.36v1.6c0 .58-.102 1.09-.31 1.54-.21.44-.49.81-.844 1.11-.35.302-.834.53-1.297.687-.465.15-.954.226-1.47.226-.51 0-.997-.08-1.46-.235a3.46 3.46 0 0 1-1.22-.703 3.452 3.452 0 0 1-.836-1.174c-.203-.472-.304-1.027-.304-1.662s.1-1.18.32-1.64c.21-.46.49-.684.85-.976.35-.297.76-.513 1.22-.648.452-.14.93-.21 1.43-.21h1.13c-.01-.49-.04-1.044-.24-1.36a2.26 2.26 0 0 0-.77-.767 3.234 3.234 0 0 0-.986-.427c-.375-.09-.578-.094-1.1-.094-.52 0-.64.02-1.01.102z"/>
     </g>
index 78ab202..2ba09bc 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="bold-b">
         <path id="b" d="M7 18h6c2 0 4-1 4-3 0-1.064.01-1.975-1.99-3 2-.975 1.99-1.935 1.99-3 0-2-2-3-4-3H7v12zm7-8c0 1 0 1-2 1h-2V8h2c2 0 2 0 2 1v1zm-2 6h-2v-3h2c2 0 2 0 2 1v1s0 1-2 1z"/>
     </g>
index 60b6416..3a6ec97 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="bold-cyrl-be">
         <path id="cyrl-be" d="M7 6h9v2h-6v3h2.65c.892 0 1.632.11 2.22.327.587.218 1.087.622 1.5 1.21.42.59.63 1.188.63 1.98 0 .812-.21 1.397-.63 1.976-.418.578-.897.974-1.436 1.187-.533.213-1.295.32-2.286.32h-5.65m4.768-2c.75 0 1.28-.05 1.584-.12.305-.077.57-.247.792-.51.23-.26.343-.472.343-.854 0-.557-.2-.868-.596-1.12-.4-.255-1.07-.397-2.02-.397H10v3"/>
     </g>
index 740dcee..490c615 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="bold-cyrl-te">
         <path id="te" d="M11 18V8H7V6h11v2h-4v10"/>
     </g>
index f0edc48..91f80dc 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="bold-cyrl-zhe">
         <path id="cyrl-zhe" d="M13 6v5.154c.328-.033.537-.18.705-.447.168-.266.4-.873.698-1.82.39-1.242.79-2.034 1.197-2.375.403-.336 1.075-.504 2.014-.504L18 6v1.78l-.386-.008c-.4 0-.69.062-.878.187-.186.112-.337.3-.452.55-.115.25-.286.76-.512 1.533-.12.41-.25.755-.392 1.032-.137.275-.383.536-.738.78.44.156.8.465 1.084.926.288.455.603 1.103.944 1.943L18 18h-2.314l-1.17-3.08-.113-.253-.24-.56c-.247-.57-.45-.933-.61-1.09A.726.726 0 0 0 13 12.78V18h-2v-5.22c-.226 0-.382.077-.546.23-.164.15-.368.517-.612 1.097l-.246.56-.113.253L8.313 18H6l1.33-3.267c.327-.808.635-1.447.923-1.92.293-.476.663-.793 1.11-.95-.355-.244-.603-.5-.745-.772a6.357 6.357 0 0 1-.392-1.04c-.222-.76-.39-1.26-.505-1.52-.11-.25-.26-.44-.45-.57-.18-.12-.49-.18-.912-.18H6V6l.386.008c.953 0 1.63.17 2.034.512.4.347.79 1.136 1.177 2.366.3.954.534 1.564.698 1.83.168.26.377.405.705.438V6.002"/>
     </g>
index 14884f7..1954e93 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="bold-f">
         <path id="f" d="M16 8V6H8v12h3v-5h4v-2h-4V8z"/>
     </g>
index 2f359c7..03f9519 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="bold-g">
         <path id="g" d="M12 14v-2h5v4.203c-.497.475-1.22.894-2.166 1.26A7.994 7.994 0 0 1 11.97 18c-1.23 0-2.303-.253-3.217-.76a4.908 4.908 0 0 1-2.062-2.185A7.008 7.008 0 0 1 6 11.96c0-1.208.26-2.282.77-3.222.518-.94 1.27-1.66 2.26-2.16.754-.386 1.693-.58 2.816-.58 1.46 0 2.6.304 3.418.91.825.603 1.354 1.436 1.59 2.502l-2.36.435a2.433 2.433 0 0 0-.94-1.346c-.454-.34-1.022-.5-1.707-.5-1.038 0-1.864.32-2.48.97-.61.65-.914 1.61-.914 2.89 0 1.375.31 2.41.93 3.1.62.687 1.434 1.03 2.44 1.03.497 0 .995-.095 1.49-.285.505-.196 1.334-.57 1.69-.846v-.868"/>
     </g>
index 391127f..290fd4c 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="bold-geor-man">
         <path id="geor-man" d="M13.832 14.06c0-1.714-.394-2.572-1.182-2.572-.868 0-1.302.78-1.302 2.338-.01 1.624.42 2.436 1.295 2.436.793 0 1.19-.734 1.19-2.2m2.167 0C16 16.686 14.884 18 12.65 18 10.218 18 9 16.614 9 13.84c0-2.737 1.217-4.105 3.65-4.105.842 0 1.183.63 1.183.63v-1.58c0-.788-.45-1.183-1.347-1.183-.572 0-.858.374-.858 1.123h-2.34C9.29 6.908 10.35 6 12.462 6 14.83 6 16.01 6.946 16 8.84"/>
     </g>
index 2041445..356145b 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="bold-l">
         <path id="l" d="M8 18V6h3v10h5v2"/>
     </g>
index 667bec4..82e1c0b 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="bold-n">
         <path id="n" d="M7 18V6h3l4 8V6h3v12h-3l-4-8v8H7"/>
     </g>
index cb1dd89..18aef7b 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="bold-v">
         <path id="v" d="M10.5 18L6 6h3l3 8 3-8h3l-4.5 12"/>
     </g>
index 9ff43d3..c383e61 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M15 7c-1.7 0-3 1.3-3 3 0-1.7-1.3-3-3-3H3v13h6c1.7 0 3 1 3 2 0-1 1.3-2 3-2h6V7h-6zm5 12h-5c-1.7 0-2 .4-2 .4v-8.9C13 9.1 14.1 8 15.5 8H20v11z"/>
 </svg>
index 6b07962..390902f 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M9 7c1.7 0 3 1.3 3 3 0-1.7 1.3-3 3-3h6v13h-6c-1.7 0-3 1-3 2 0-1-1.3-2-3-2H3V7h6zM4 19h5c1.7 0 2 .4 2 .4v-8.9C11 9.1 9.9 8 8.5 8H4v11z"/>
 </svg>
index bf39564..ea474cb 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M15 5H8c-1.1 0-2 .9-2 2v3h3v11l4-3 4 3V7c0-1.1-.9-2-2-2zM9 9H7V7c0-.6.4-1 1-1h1v3z"/>
 </svg>
index 729b8c2..9049881 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M8 5h7c1.1 0 2 .9 2 2v3h-3v11l-4-3-4 3V7c0-1.1.9-2 2-2zm6 4h2V7c0-.6-.4-1-1-1h-1v3z"/>
 </svg>
index ae38327..28fac21 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M18.1 5.1c0 .3-.1.6-.3.9l-1.4 1.4-.9-.8 2.2-2.2c.3.1.4.4.4.7zm-.5 5.3h3.2c0 .3-.1.6-.4.9-.3.3-.5.4-.8.4h-2v-1.3zm-6.2-5V2.2c.3 0 .6.1.9.4.3.3.4.5.4.8v2h-1.3zm6.4 11.7c-.3 0-.6-.1-.8-.3l-1.4-1.4.8-.8 2.2 2.2c-.2.2-.5.3-.8.3zM6.2 4.9c.3 0 .6.1.8.3l1.4 1.4-.8.9-2.2-2.3c.2-.2.5-.3.8-.3zm5.2 11.7h1.2v3.2c-.3 0-.6-.1-.9-.4-.3-.3-.4-.5-.4-.8l.1-2zm-7-6.2h2v1.2H3.2c0-.3.1-.6.4-.9.3-.3.5-.3.8-.3zM6.2 16l1.4-1.4.8.8-2.2 2.2c-.2-.2-.3-.5-.3-.8 0-.3.1-.6.3-.8zM12 8c1.7 0 3 1.3 3 3s-1.3 3-3 3-3-1.3-3-3 1.3-3 3-3m0-1c-2.2 0-4 1.8-4 4s1.8 4 4 4 4-1.8 4-4-1.8-4-4-4z"/>
     <path d="M12 8c1.7 0 3 1.3 3 3s-1.3 3-3 3-3-1.3-3-3 1.3-3 3-3m0-1c-2.2 0-4 1.8-4 4s1.8 4 4 4 4-1.8 4-4-1.8-4-4-4z"/>
 </svg>
index 762e641..85c7273 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M3 6v11c0 1.7 1.3 3 3 3h15V6H3zm2.5 1C6.3 7 7 7.7 7 8.5S6.3 10 5.5 10 4 9.3 4 8.5 4.7 7 5.5 7zM20 19H6c-1.1 0-2-.9-2-2v-6h16v8z"/>
 </svg>
index d6378bf..38778ec 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M21 6v11c0 1.7-1.3 3-3 3H3V6h18zm-2.5 1c-.8 0-1.5.7-1.5 1.5s.7 1.5 1.5 1.5S20 9.3 20 8.5 19.3 7 18.5 7zM4 19h14c1.1 0 2-.9 2-2v-6H4v8z"/>
 </svg>
index 5fb8675..f820d7a 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M4 5v10c0 1.7 1.3 3 3 3h14V8c0-1.7-1.3-3-3-3H4zm2 1a1 1 0 1 1 0 2 1 1 0 0 1 0-2zm4 0a1 1 0 1 1 0 2 1 1 0 0 1 0-2zm4 0a1 1 0 1 1 0 2 1 1 0 0 1 0-2zm4 0a1 1 0 1 1 0 2 1 1 0 0 1 0-2zM5 9h3v2H5V9zm4 0h3v2H9V9zm4 0h3v2h-3V9zm4 0h3v2h-3V9zM5 12h3v2H5v-2zm4 0h3v2H9v-2zm4 0h3v2h-3v-2zm4 0h3v2h-3v-2zM5 15h3v2H7c-1.195 0-2-.805-2-2zm4 0h3v2H9v-2zm4 0h3v2h-3v-2zm4 0h3v2h-3v-2z"/>
 </svg>
index 8f06883..e430f0b 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M21 5v10c0 1.7-1.3 3-3 3H4V8c0-1.7 1.3-3 3-3h14zm-2 1a1 1 0 1 0 0 2 1 1 0 0 0 0-2zm-4 0a1 1 0 1 0 0 2 1 1 0 0 0 0-2zm-4 0a1 1 0 1 0 0 2 1 1 0 0 0 0-2zM7 6a1 1 0 1 0 0 2 1 1 0 0 0 0-2zm13 3h-3v2h3V9zm-4 0h-3v2h3V9zm-4 0H9v2h3V9zM8 9H5v2h3V9zm12 3h-3v2h3v-2zm-4 0h-3v2h3v-2zm-4 0H9v2h3v-2zm-4 0H5v2h3v-2zm12 3h-3v2h1c1.195 0 2-.805 2-2zm-4 0h-3v2h3v-2zm-4 0H9v2h3v-2zm-4 0H5v2h3v-2z"/>
 </svg>
index 919dba4..5970559 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #D11D13 }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #d11d13 }</style>
     <g id="cancel">
         <path id="circle-with-strike" d="M12 5.022a6.98 6.98 0 0 0-.003 13.956 6.98 6.98 0 0 0-.002-13.956zM6.885 12c0-1.092.572-3.25.93-2.93l7.113 7.114c.487.525-1.838.93-2.93.93A5.113 5.113 0 0 1 6.884 12zm9.298 2.93L9.07 7.815c-.445-.483 1.837-.93 2.93-.93a5.112 5.112 0 0 1 5.114 5.113c0 1.092-.364 3.542-.93 2.93z"/>
     </g>
index 2bf3370..553e9f6 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="cancel">
         <path id="circle-with-strike" d="M12 5.022a6.98 6.98 0 0 0-.003 13.956 6.98 6.98 0 0 0-.002-13.956zM6.885 12c0-1.092.572-3.25.93-2.93l7.113 7.114c.487.525-1.838.93-2.93.93A5.113 5.113 0 0 1 6.884 12zm9.298 2.93L9.07 7.815c-.445-.483 1.837-.93 2.93-.93a5.112 5.112 0 0 1 5.114 5.113c0 1.092-.364 3.542-.93 2.93z"/>
     </g>
index b9b6b3a..28490a3 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M7 13.1l8.9 8.9c.8-.8.8-2 0-2.8l-6.1-6.1 6-6.1c.8-.8.8-2 0-2.8L7 13.1z"/>
 </svg>
index 21c2a33..1a3447e 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M16.5 13.1L7.6 22c-.8-.8-.8-2 0-2.8l6.1-6.1-6-6.1c-.8-.8-.8-2 0-2.8l8.8 8.9z"/>
 </svg>
index 5e3cfcf..04e6e5e 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M12 16l8.9-8.9c-.8-.8-2-.8-2.8 0L12 13.2l-6.1-6c-.8-.8-2-.8-2.8 0L12 16z"/>
 </svg>
index 3cc20fd..2cbec64 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M12 6.5l8.9 8.9c-.8.8-2 .8-2.8 0L12 9.3l-6.1 6c-.8.8-2 .8-2.8 0L12 6.5z"/>
 </svg>
index 4bc0ba2..e39d780 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="regular-expression">
         <path id="upper-case" d="M7.53 7L4 17h2.063l.72-2.406h3.624l.72 2.406h2.062L9.65 7h-2.12zm1.064 1.53L9.938 13H7.25l1.344-4.47z"/>
         <path id="lower-case" d="M18.55 17l-.184-1.035h-.055c-.35.44-.71.747-1.08.92-.37.167-.85.25-1.44.25-.564 0-.955-.208-1.377-.625-.42-.418-.627-1.012-.627-1.784 0-.808.283-1.403.846-1.784.568-.386 1.193-.607 2.208-.64l1.322-.04v-.335c0-.772-.396-1.158-1.187-1.158-.61 0-1.325.18-2.147.55l-.688-1.4c.877-.46 1.85-.69 2.916-.69 1.024 0 1.59.22 2.134.662.545.445.818 1.12.818 2.03V17h-1.45m-.394-3.527l-.802.027c-.604.018-1.054.127-1.35.327-.294.2-.442.504-.442.912 0 .58.336.87 1.008.87.48 0 .865-.137 1.152-.414.29-.277.436-.645.436-1.103v-.627"/>
index 7dbd0ac..07a5614 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #00AF89 }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #00af89 }</style>
     <path d="M17 7.5L9.5 15 6 11.5 4.5 13l5 5L20 7.5c-.706-.706-2.294-.706-3 0z" id="check"/>
 </svg>
index 41e7160..eb495e4 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #D11D13 }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #d11d13 }</style>
     <path d="M17 7.5L9.5 15 6 11.5 4.5 13l5 5L20 7.5c-.706-.706-2.294-.706-3 0z" id="check"/>
 </svg>
index 0f4ef78..1c198c5 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M17 7.5L9.5 15 6 11.5 4.5 13l5 5L20 7.5c-.706-.706-2.294-.706-3 0z" id="check"/>
 </svg>
index fd08148..3084e5a 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #347BFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #347bff }</style>
     <path d="M17 7.5L9.5 15 6 11.5 4.5 13l5 5L20 7.5c-.706-.706-2.294-.706-3 0z" id="check"/>
 </svg>
index f1291f9..b96e771 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #00AF89 }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #00af89 }</style>
     <circle cx="12" cy="12" r="6"/>
 </svg>
index 7cae5db..ddc7b85 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <circle cx="12" cy="12" r="6"/>
 </svg>
index 40d3aab..a6bf1ce 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M7 12h9v-1H7v1zm0 2h9v-1H7v1zm0 2h9v-1H7v1zm4-9H7v1h4V7zm0 2H7v1h4V9zm0-4H7v1h4V5zm5-2h2v16H8c-1.7 0-3-1.3-3-3V3h8v7l1.5-2 1.5 2V3z"/>
 </svg>
index c0cd449..72472b6 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M16 12H7v-1h9v1zm0 2H7v-1h9v1zm0 2H7v-1h9v1zm-4-9h4v1h-4V7zm0 2h4v1h-4V9zm0-4h4v1h-4V5zM7 3H5v16h10c1.7 0 3-1.3 3-3V3h-8v7L8.5 8 7 10V3z"/>
 </svg>
index 0738205..7a6b1c0 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="clear">
         <path id="circle-with-cross" d="M12 5c-4.4 0-8 3.6-8 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm4 11l-1 1-3-3-3 3-1-1 3-3-3-3 1-1 3 3 3-3 1 1-3 3 3 3z"/>
     </g>
index a134c65..6eb6e0a 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M12 5c-4.4 0-8 3.6-8 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm3 12l-4-3V8h2v5l1.7 1.2c1.3.9 1 1.9.3 2.8z"/>
 </svg>
index cd7ce21..072276b 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="close">
         <path id="cross" d="M17.4 9.1c.8-.8.8-2 0-2.8L12 11.8 7.4 7.2 6 8.6l4.6 4.6-4 4c-.8.8-.8 2 0 2.8l5.4-5.4 4.6 4.6 1.4-1.4-4.6-4.6z"/>
     </g>
index ee8a82d..19ddef6 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="close">
         <path id="cross" d="M6.6 9.1c-.8-.8-.8-2 0-2.8l5.4 5.5 4.6-4.6L18 8.6l-4.6 4.6 4 4c.8.8.8 2 0 2.8L12 14.6l-4.6 4.6L6 17.8l4.6-4.6z"/>
     </g>
index 8d3d37d..3b129c0 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="code">
         <path id="left-bracket" d="M4 12v-1h1c1 0 1 0 1-1V7.614c0-.514.024-.896.073-1.142.054-.252.14-.463.257-.633.204-.28.473-.48.808-.59.335-.11.872-.25 1.835-.25H10v1h-.752c-.457 0-.77.19-.936.406-.167.216-.312.446-.312 1.07v1.856c0 .73-.04 1.18-.244 1.493-.2.307-.562.53-1.09.667.535.155.9.385 1.096.688.2.31.238.76.238 1.49v1.86c0 .62.145.85.312 1.06.166.22.48.41.936.41H10v1H8.973c-.963 0-1.5-.133-1.835-.248a1.578 1.578 0 0 1-.808-.59 1.68 1.68 0 0 1-.257-.626C6.023 16.283 6 15.9 6 15.386V13c0-1 0-1-1-1H4z"/>
         <use transform="matrix(-1 0 0 1 24 0)" id="right-bracket" width="24" height="24" xlink:href="#left-bracket"/>
index 926f98d..239a248 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="collapse">
         <path id="arrow" d="M6.697 15.714L12 10.412l5.303 5.302 1.414-1.414L12 7.583 5.283 14.3z"/>
     </g>
index 8d0c369..c123fd7 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="comment">
         <path id="speech-bubble" d="M15 6H9a3 3 0 0 0-3 3v4a3 3 0 0 0 3 3v3l3-3h3a3 3 0 0 0 3-3V9a3 3 0 0 0-3-3z"/>
     </g>
index 8bac842..92f19bf 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M16 5H4v12c0 1.6 1.3 3 3 3h12V8c0-1.7-1.4-3-3-3zM7.5 17c-.8 0-1.5-.7-1.5-1.5S6.7 14 7.5 14s1.5.7 1.5 1.5S8.3 17 7.5 17zm0-6C6.7 11 6 10.3 6 9.5S6.7 8 7.5 8 9 8.7 9 9.5 8.3 11 7.5 11zm4 3c-.8 0-1.5-.7-1.5-1.5s.7-1.5 1.5-1.5 1.5.7 1.5 1.5-.7 1.5-1.5 1.5zm4 3c-.8 0-1.5-.7-1.5-1.5s.7-1.5 1.5-1.5 1.5.7 1.5 1.5-.7 1.5-1.5 1.5zm0-6c-.8 0-1.5-.7-1.5-1.5S14.7 8 15.5 8s1.5.7 1.5 1.5-.7 1.5-1.5 1.5z"/>
 </svg>
index 796c176..b7c1c5c 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M7 5h12v12c0 1.6-1.3 3-3 3H4V8c0-1.7 1.4-3 3-3zm8.5 12c.8 0 1.5-.7 1.5-1.5s-.7-1.5-1.5-1.5-1.5.7-1.5 1.5.7 1.5 1.5 1.5zm0-6c.8 0 1.5-.7 1.5-1.5S16.3 8 15.5 8 14 8.7 14 9.5s.7 1.5 1.5 1.5zm-4 3c.8 0 1.5-.7 1.5-1.5s-.7-1.5-1.5-1.5-1.5.7-1.5 1.5.7 1.5 1.5 1.5zm-4 3c.8 0 1.5-.7 1.5-1.5S8.3 14 7.5 14 6 14.7 6 15.5 6.7 17 7.5 17zm0-6c.8 0 1.5-.7 1.5-1.5S8.3 8 7.5 8 6 8.7 6 9.5 6.7 11 7.5 11z"/>
 </svg>
index d18ef8b..e4592ed 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M12 18l8-10H4z"/>
 </svg>
index eef75e1..a1104a8 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M16 11h-3V4c-1.7 0-3 1.3-3 3v4H7l4.5 5 4.5-5zm1 2v5H7c-.6 0-1-.4-1-1v-4H4v4c0 1.9 1.3 3 3 3h12v-7h-2z"/>
 </svg>
index 003faa3..fb82869 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M7 11h3V4c1.7 0 3 1.3 3 3v4h3l-4.5 5L7 11zm-1 2v5h10c.6 0 1-.4 1-1v-4h2v4c0 1.9-1.3 3-3 3H4v-7h2z"/>
 </svg>
index ac4418e..7c0fd75 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M17 2L5 14l-1 5 5-1L21 6c0-2-2-4-4-4zM7.2 15.5c-.3-.3-.7-.6-1-.8C8.5 12.4 17.5 3.3 17.5 3.3c.4.1.7.3 1 .7L7.2 15.5z"/>
 </svg>
index 72e5856..6dbfe37 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #347BFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #347bff }</style>
     <path d="M17 2L5 14l-1 5 5-1L21 6c0-2-2-4-4-4zM7.2 15.5c-.3-.3-.7-.6-1-.8C8.5 12.4 17.5 3.3 17.5 3.3c.4.1.7.3 1 .7L7.2 15.5z"/>
 </svg>
index 801568f..fdfbca5 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M8 2l12 12 1 5-5-1L4 6c0-2 2-4 4-4zm9.8 13.5c.3-.3.7-.6 1-.8C16.5 12.4 7.5 3.3 7.5 3.3c-.4.1-.7.3-1 .7l11.3 11.5z"/>
 </svg>
index b241c50..213841d 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #347BFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #347bff }</style>
     <path d="M8 2l12 12 1 5-5-1L4 6c0-2 2-4 4-4zm9.8 13.5c.3-.3.7-.6 1-.8C16.5 12.4 7.5 3.3 7.5 3.3c-.4.1-.7.3-1 .7l11.3 11.5z"/>
 </svg>
index 59c2a5d..2f9ab93 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M21 4V3s0-3-3-3-3 3-3 3v1h-1v6h8V4zm-1.5 0h-3V3s0-1.5 1.5-1.5c1.48.06 1.5 1.5 1.5 1.5zM13 9.6l-6.8 6.9c-.3-.3-.7-.6-1-.8 1.4-1.4 5-5 7.8-7.9V6l-9 9-1 5 5-1 8-8h-3z"/>
 </svg>
index 4f924f8..9d563fb 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M4 4V3s0-3 3-3 3 3 3 3v1h1v6H3V4zm1.5 0h3V3s0-1.5-1.5-1.5C5.52 1.56 5.5 3 5.5 3zM12 9.6l6.8 6.9c.3-.3.7-.6 1-.8-1.4-1.4-5-5-7.8-7.9V6l9 9 1 5-5-1-8-8h3z"/>
 </svg>
index d5ec132..bdcbb21 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M14.9 2.8c.9 0 1.8.2 2.7.6.9.4 1.6.9 1.9 1.6-2.8.1-5 1.1-6.6 3.1l1.3 2-6.7-.3L8 3l1.7 2c1.8-1.5 3.5-2.2 5.2-2.2z"/>
     <path d="M15.2 11.1l-2.6-.1-5.4 5.5c-.3-.3-.7-.6-1-.8.9-.9 2.8-2.8 4.7-4.8H9.1L5 15l-1 5 5-1 7.8-7.8-1.6-.1zM20.6 6c-1.7 0-3.2.5-4.4 1.4l-.9.9.8 1.3.9 1.4 4-4c0-.3-.1-.7-.2-1h-.2z"/>
 </svg>
index 7722bd8..dfe6877 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M10.1 2.8c-.9 0-1.8.2-2.7.6-.9.4-1.6.9-1.9 1.6 2.8.1 5 1.1 6.6 3.1l-1.3 2 6.7-.3L17 3l-1.7 2c-1.8-1.5-3.5-2.2-5.2-2.2z"/>
     <path d="M9.8 11.1l2.6-.1 5.4 5.5c.3-.3.7-.6 1-.8-.9-.9-2.8-2.8-4.7-4.8h1.8L20 15l1 5-5-1-7.8-7.8 1.6-.1zM4.4 6c1.7 0 3.2.5 4.4 1.4l.9.9-.8 1.3L8 11 4 7c0-.3.1-.7.2-1h.2z"/>
 </svg>
index 19733aa..b512f82 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M8 13c0 .6-.2 1-.6 1.4-.4.4-.9.6-1.4.6-.6 0-1-.2-1.4-.6-.4-.4-.6-.9-.6-1.4s.2-1 .6-1.4c.4-.4.9-.6 1.4-.6s1 .2 1.4.6c.4.4.6.9.6 1.4zM14 13c0 .6-.2 1-.6 1.4-.4.4-.9.6-1.4.6-.6 0-1-.2-1.4-.6-.4-.4-.6-.9-.6-1.4s.2-1 .6-1.4c.4-.4.9-.6 1.4-.6s1 .2 1.4.6c.4.4.6.9.6 1.4zM20 13c0 .6-.2 1-.6 1.4-.4.4-.9.6-1.4.6-.6 0-1-.2-1.4-.6-.4-.4-.6-.9-.6-1.4s.2-1 .6-1.4c.4-.4.9-.6 1.4-.6s1 .2 1.4.6c.4.4.6.9.6 1.4z"/>
 </svg>
index 21f80da..28fb1ef 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="expand">
         <path id="arrow" d="M17.303 8.283L12 13.586 6.697 8.283 5.283 9.697 12 16.414l6.717-6.717z"/>
     </g>
index 0526f75..269d813 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="external">
         <path id="box" d="M4 4h6v2H6v12h12v-4h2v6H4z"/>
         <path id="arrow" d="M12.42 4H20v7.58l-2.84-2.846L12.892 13 11 11.106l4.264-4.266z"/>
index d747aa6..cc06c3a 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="external">
         <path id="box" d="M20 4h-6v2h4v12H6v-4H4v6h16z"/>
         <path id="arrow" d="M11.58 4H4v7.58l2.84-2.846L11.108 13 13 11.106 8.736 6.84z"/>
index 31c10e5..9a0dbaa 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M12 8C7 8 1 14 1 14s6 6 11 6l11-6s-6-6-11-6zm0 10c-2.2 0-4-1.8-4-4s1.8-4 4-4 4 1.8 4 4-1.8 4-4 4z"/>
     <circle cx="12" cy="14" r="2"/>
 </svg>
index d8f7dff..3ce3da3 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M19.4 12.7c.7-.8 1.2-1.7 1.4-2.7h-1.6c-.9 2.5-3.9 4.4-7.7 4.6h-.1c-3.7-.2-6.8-2.1-7.7-4.6H2.2c.2 1 .8 1.9 1.4 2.7l-2 2 .7.7 2-2c.8.6 1.7 1.2 2.7 1.7l-1 2.8.9.3 1-2.8c1 .3 2 .6 3.1.6v3h1v-3c1.1-.1 2.2-.3 3.1-.6l1 2.8.9-.3-1-2.8c1-.4 1.9-1 2.6-1.7l2 2 .7-.7-1.9-2z"/>
 </svg>
index 405c240..55e6441 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="find">
         <path id="magnifying-glass" d="M13.656 11c-1.92 0-3.5 1.548-3.5 3.47 0 1.92 1.58 3.5 3.5 3.5.75 0 1.432-.253 2-.657l.094.156 2.375 2.37c.19.19.534.15.78-.096s.315-.59.126-.78l-2.37-2.377-.185-.094a3.545 3.545 0 0 0 .655-2.03c0-1.92-1.55-3.47-3.47-3.47zm0 1.656a1.8 1.8 0 0 1 1.813 1.813 1.83 1.83 0 0 1-1.82 1.84c-1.01 0-1.844-.83-1.844-1.847s.832-1.814 1.844-1.814z"/>
         <path id="text" d="M6 5v2h10V5H6zm0 3v2h11V8H6zm0 3v2h3.53a4.443 4.443 0 0 1 1.44-2H6zm0 3v2h3.53c-.177-.48-.28-.99-.28-1.53 0-.16.046-.315.063-.47H6z"/>
index b46f53f..e6c6bb5 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="find">
         <path id="magnifying-glass" d="M11.344 11c1.92 0 3.5 1.548 3.5 3.47 0 1.92-1.58 3.5-3.5 3.5-.75 0-1.432-.253-2-.657l-.094.156-2.375 2.37c-.19.19-.534.15-.78-.096s-.315-.59-.126-.78l2.37-2.377.185-.094a3.545 3.545 0 0 1-.655-2.03c0-1.92 1.55-3.47 3.47-3.47zm0 1.656A1.8 1.8 0 0 0 9.53 14.47c0 1.01.806 1.84 1.818 1.84 1.01 0 1.844-.83 1.844-1.845s-.832-1.814-1.844-1.814z"/>
         <path id="text" d="M19 5v2H9V5zm0 3v2H8V8zm0 3v2h-3.53a4.443 4.443 0 0 0-1.44-2zm0 3v2h-3.53c.177-.48.28-.99.28-1.53 0-.16-.046-.315-.063-.47z"/>
index ab66000..832c44f 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M14 6.5V5c-1.4-1.5-5.2-1.2-6 0V4H7v15h1v-7c.8-.8 3.4-.9 5-.5V13c1.2 1.5 4.3 1.2 5 0V6c-.7.7-2.7.9-4 .5z"/>
 </svg>
index 025da1f..3946c4a 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M11 6.5V5c1.4-1.5 5.2-1.2 6 0V4h1v15h-1v-7c-.8-.8-3.4-.9-5-.5V13c-1.2 1.5-4.3 1.2-5 0V6c.7.7 2.7.9 4 .5z"/>
 </svg>
index ea95d5f..2cbc539 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M14 6.5V5c-1.4-1.5-5.2-1.2-6 0V4H7v15h1v-7c.8-.8 3.4-.9 5-.5V13c1.2 1.5 4.3 1.2 5 0V6c-.7.7-2.7.9-4 .5z"/>
     <path d="M17.997 1.99l.99.99-15.98 15.98-.99-.99z"/>
     <path d="M17 1.016l.99.99-15.98 15.98-.99-.99z" fill="#fff"/>
index dc328a8..1faabf6 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M11 6.5V5c1.4-1.5 5.2-1.2 6 0V4h1v15h-1v-7c-.8-.8-3.4-.9-5-.5V13c-1.2 1.5-4.3 1.2-5 0V6c.7.7 2.7.9 4 .5z"/>
     <path d="M7.003 1.99l-.99.99 15.98 15.98.99-.99z"/>
     <path d="M8 1.016l-.99.99 15.98 15.98.99-.99z" fill="#fff"/>
index 7dfc979..c66c281 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M2 5v15h20V5H2zm15 11H8c-.6 0-1-.4-1-1V9h3l2 1h5v6z"/>
 </svg>
index 6151238..f2d9fab 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M22 5v15H2V5h20zM7 16h9c.6 0 1-.4 1-1V9h-3l-2 1H7v6z"/>
 </svg>
index 40d01b4..00d2695 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M11 13L5 6h15l-6 7v7c-1.7 0-3-1.3-3-3v-4z"/>
 </svg>
index d960a65..bd2d7de 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M14 13l6-7H5l6 7v7c1.7 0 3-1.3 3-3v-4z"/>
 </svg>
index 5e5b88e..95b83c2 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M11.4 5.4V2.2c.3 0 .6.1.9.4.3.3.4.5.4.8v2h-1.3zm-5.2-.5c.3 0 .6.1.8.3l1.4 1.4-.8.9-2.2-2.3c.2-.2.5-.3.8-.3zm5.2 11.7h1.2v3.2c-.3 0-.6-.1-.9-.4-.3-.3-.4-.5-.4-.8l.1-2zm-7-6.2h2v1.2H3.2c0-.3.1-.6.4-.9.3-.3.5-.3.8-.3zM6.2 16l1.4-1.4.8.8-2.2 2.2c-.2-.2-.3-.5-.3-.8 0-.3.1-.6.3-.8zM12 7c-2.2 0-4 1.8-4 4s1.8 4 4 4 4-1.8 4-4-1.8-4-4-4zm-3 4c0-1.7 1.3-3 3-3v6c-1.7 0-3-1.3-3-3z"/>
 </svg>
index fc78226..7ace9e4 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M15 7c-2 0-3 2-3 2s-1-2-3-2c-2.5 0-4 2-4 4 0 4 5 5 7 8 2-3 7-4 7-8 0-2-1.5-4-4-4z"/>
 </svg>
index 29df251..a43996c 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="help">
         <path id="circle" d="M12 2.085c-5.477 0-9.915 4.438-9.915 9.916 0 5.48 4.438 9.92 9.916 9.92 5.48 0 9.92-4.44 9.92-9.913 0-5.477-4.44-9.915-9.913-9.915zm.002 18a8.084 8.084 0 1 1 0-16.168 8.084 8.084 0 0 1 0 16.168z"/>
         <g id="question-mark">
index 552fe9f..0c0368e 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="help">
         <path id="circle" d="M12 2.085c5.477 0 9.915 4.438 9.915 9.916 0 5.48-4.438 9.92-9.916 9.92-5.48 0-9.92-4.44-9.92-9.913 0-5.477 4.44-9.915 9.913-9.915zm-.002 18a8.084 8.084 0 1 0 0-16.168 8.084 8.084 0 0 0 0 16.168z"/>
         <g id="question-mark">
index 4b1fd50..6b46920 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="history">
         <path id="clock-hands" d="M17.26 15.076s-2.385-1.935-4.005-3.062c.72-2.397 1.702-6.56 1.702-6.56s-4.35 5.364-4.877 6.7c-.463 1.168 1.46 2.21 2.346 1.678 1.9.55 4.834 1.244 4.834 1.244z"/>
         <path id="arrow" d="M12.086 2.085C6.608 2.085 2.17 6.523 2.17 12a9.86 9.86 0 0 0 1.3 4.9l-2.22 2.04h5.688v-5.22L4.87 15.616A7.982 7.982 0 0 1 4.004 12a8.084 8.084 0 0 1 16.167.004 8.08 8.08 0 0 1-8.08 8.085 7.975 7.975 0 0 1-3.21-.68L8.05 21.04a9.81 9.81 0 0 0 4.045.874C17.563 21.914 22 17.476 22 12c0-5.477-4.438-9.915-9.914-9.915z"/>
index 9b39350..aeb8984 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="image">
         <path id="mountains" d="M18 17l-3-3-2 1-3-3-4 5zm2-11v13H4V6z"/>
     </g>
index f962cbf..8b82c20 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="image">
         <path id="mountains" d="M6 17l3-3 2 1 3-3 4 5zM4 6v13h16V6z"/>
     </g>
index cfcf60d..81b6783 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="imageAdd">
         <path id="mountains" d="M16 17l-3-3-2 1-3-3-4 5zm-1-8v4h3v6H2V6h9v3z"/>
         <path id="add" d="M22 6h-4V2h-2v4h-4v2h4v4h2V8h4z"/>
index 8b20a50..f14a20a 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="imageAdd">
         <path id="mountains" d="M8 17l3-3 2 1 3-3 4 5zm1-8v4H6v6h16V6h-9v3z"/>
         <path id="add" d="M2 6h4V2h2v4h4v2H8v4H6V8H2z"/>
index 6758bc7..785bd49 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M2 4v14h2V6h15V4H2zm3 3v13h16V7H5zm6 6l3 3 2-1 3 3H7l4-5z" id="imageGallery"/>
 </svg>
index ff3123f..fe66e4f 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M21 4v14h-2V6H4V4h17zm-3 3v13H2V7h16zm-6 6l-3 3-2-1-3 3h12l-4-5z" id="imageGallery"/>
 </svg>
index b14b67d..5932525 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="imageAdd">
         <path id="mountains" d="M18 17l-3-3-2 1-3-3-4 5zm2-5v7H4V6h8v6z"/>
         <path id="lock" d="M18.5 5h-3V4s0-1.5 1.5-1.5c1.5.06 1.5 1.5 1.5 1.5zM20 5V4s0-3-3-3-3 3-3 3v1h-1v6h8V5z"/>
index 275dfc2..b92b212 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="imageAdd">
         <path id="mountains" d="M7 17l3-3 2 1 3-3 4 5zm-2-5v7h16V6h-8v6z"/>
         <path id="lock" d="M6.5 5h3V4s0-1.5-1.5-1.5C6.5 2.56 6.5 4 6.5 4zM5 5V4s0-3 3-3 3 3 3 3v1h1v6H4V5z"/>
index 0837550..718c2b0 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M10 8h9v2h-9V8zm0 3h9v2h-9v-2zm0 3h6v2h-6v-2zm11-8H3V4h18v2zm0 14H3v-2h18v2zM3 8v8l5-4-5-4z"/>
 </svg>
index 26e49c7..e6abc95 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M14 8H5v2h9V8zm0 3H5v2h9v-2zm0 3H8v2h6v-2zM3 6h18V4H3v2zm0 14h18v-2H3v2zM21 8v8l-5-4 5-4z"/>
 </svg>
index ce4a75d..e519d7e 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="info">
         <path id="circled-i" d="M11.5 17a5.5 5.5 0 1 1 0-11 5.5 5.5 0 0 1 0 11zm0-12a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13zm.5 5v4h1v1h-3v-1h1v-3h-1v-1zm-1-2h1v1h-1z"/>
     </g>
index 0fc2eb0..ac33a08 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="italic-a">
         <path id="a" d="M14.667 6h-1.372l-7 12H8l2.333-4h4L15 18h1.667l-2-12zm-3.75 7l2.527-4.333.723 4.333h-3.25z"/>
     </g>
index 63dc4e2..5ea6072 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="italic-arab-keheh-jeem">
         <path id="arab-keheh-jeem" d="M18.125 5.844c-1.695.555-3.297 1.162-4.594 1.938-.49.3-.77.712-.87 1.125a1.26 1.26 0 0 0 .065.78c.19.406.54.575.844.814l.093-.12.53.627c.14.165.345.514.47.94.138.462.08.724 0 1.124h-3.44c-.34 0-.592.007-.766-.02-.34-.053-.256-.21-.234-.34.33-.127.56-.173.934-.14.29-.495.593-.886.906-1.314-.98.037-1.877.015-2.687-.094-.346-.046-.698-.185-1.094-.155-.36.026-.77.24-1.03.72-.25.447-.436.838-.66 1.28l.75-.47c.23-.14.486-.226.72-.218.158.004.276.053.407.093-.234.204-.51.4-.72.56-.3.26-.704.69-.908 1-.403.617-.694 1.086-.875 1.78-.18.69.003 1.34.468 1.75.426.38.846.52 1.28.566.65.064 1.206.092 2-.19.658-.23 1.022-.552 1.5-.97-.882.11-1.816.09-2.53.033-.87-.07-1.268-.386-1.47-.596-.27-.283-.306-.64-.155-1.22a1.44 1.44 0 0 1 .25-.53c.17-.228.363-.435.593-.656.45-.436 1.01-.737 1.46-.94-.042.207-.104.444-.052.69.05.23.25.38.44.47.26.12.506.152.69.153 1.42.01 2.86 0 4.28 0 .246 0 .45-.163.593-.375.14-.21.25-.48.343-.845.13-.5.094-1.062-.094-1.625a4.812 4.812 0 0 0-.72-1.406c-.336-.444-.675-.83-1-1.22 1.256-.815 2.715-1.24 3.97-1.688.12-.452.222-.926.31-1.313zm-9.47 8.438c-.26.394-.583.69-.874 1 .38.286.75.556 1.1.813.336-.303.627-.674.876-.97-.39-.267-.77-.587-1.093-.843z"/>
     </g>
index 5191e7f..5b4cd21 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="italic-arab-meem">
         <path id="arab-meem" d="M16 9.73l-.93 2.19h-4.663c-.48 0-.857.12-1.135.366l-.06.11c-.185 2.016-.503 3.558-.956 4.627a8.31 8.31 0 0 1-1.082 1.833c-.177.226-.22.186-.126-.12l.142-.503.17-.67.234-.87.002-.008.202-1.045.258-1.41.353-1.907c.19-.312.42-.638.692-.98a24.1 24.1 0 0 1 .94-1.09c.13-.092.697-.18 1.705-.266 1.05-.086 1.64-.183 1.765-.293l.065-.128c.01-.11-.01-.24-.052-.394a2.403 2.403 0 0 0-.232-.522c-.22-.428-.438-.64-.654-.64-.294 0-.915.268-1.864.805-.36.208-.378.125-.05-.247 1.555-1.71 2.705-2.566 3.45-2.566.38 0 .67.13.86.394.134.195.25.6.343 1.21l.202 1.2c.105.586.24.895.408.925"/>
     </g>
index c7ba181..44753a9 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="italic-armn-sha">
         <path id="armn-sha" d="M11.564 7.678a3.073 3.073 0 0 0-.93-.268c-.35-.047-.75-.07-1.197-.07h-1.11L8.587 6h1.723c.558 0 1.042.032 1.45.095.416.063.794.173 1.136.33l4.483 2.033-.33 1.67-2.625-1.165a1.867 1.867 0 0 0-.433-.134 2.45 2.45 0 0 0-.576-.06 4.88 4.88 0 0 0-1.663.28c-.526.19-1 .46-1.427.812-.42.35-.776.78-1.07 1.283a5.48 5.48 0 0 0-.63 1.71c-.24 1.255-.15 2.21.27 2.87.424.65 1.19.976 2.292.976.55 0 1.044-.08 1.48-.236a3.488 3.488 0 0 0 1.135-.66c.325-.29.59-.634.795-1.034.21-.4.363-.84.458-1.322l.11-.56h1.6l-.12.59a5.925 5.925 0 0 1-.676 1.844 5.19 5.19 0 0 1-1.214 1.423c-.488.395-1.053.7-1.694.923a6.573 6.573 0 0 1-2.106.324c-.767 0-1.434-.114-2-.34-.568-.226-1.025-.554-1.372-.985-.347-.437-.573-.97-.678-1.608-.105-.64-.078-1.366.08-2.186.125-.66.346-1.274.66-1.836A6.332 6.332 0 0 1 8.792 9.54a5.955 5.955 0 0 1 1.496-1.072 5.87 5.87 0 0 1 1.732-.57l-.465-.23"/>
     </g>
index abc0301..467d12d 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="italic-c">
         <path id="c" d="M15.008 13.718l1.48.214c-.467 1.34-1.15 2.354-2.045 3.04a4.835 4.835 0 0 1-3.015 1.03c-1.36 0-2.438-.43-3.237-1.29C7.4 15.85 7 14.618 7 13.012c0-2.09.606-3.817 1.817-5.184C9.897 6.61 11.237 6 12.84 6c1.186 0 2.145.33 2.878.99.738.66 1.165 1.546 1.282 2.66l-1.397.135c-.148-.84-.453-1.464-.916-1.876-.458-.42-1.05-.63-1.78-.63-1.368 0-2.475.63-3.32 1.89-.733 1.087-1.1 2.377-1.1 3.87 0 1.194.283 2.104.848 2.732.565.628 1.3.942 2.206.942.78 0 1.48-.26 2.1-.785.63-.52 1.08-1.26 1.37-2.216"/>
     </g>
index b51d25c..7774790 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="italic-d">
         <path id="d" d="M7 18L9.462 6h3.557c.85 0 1.5.063 1.95.188.642.17 1.192.472 1.65.91.454.43.8.97 1.03 1.62.23.65.344 1.378.344 2.186 0 .966-.146 1.847-.436 2.644-.284.79-.66 1.49-1.127 2.095-.46.6-.946 1.072-1.455 1.416-.504.33-1.1.582-1.794.75-.525.122-1.17.19-1.94.19H7m1.86-1.36h1.866c.842 0 1.59-.08 2.245-.24a3.26 3.26 0 0 0 1.05-.436 4.19 4.19 0 0 0 1.04-.975 6.652 6.652 0 0 0 .975-1.825c.247-.687.37-1.467.37-2.34 0-.97-.166-1.716-.5-2.235-.332-.522-.755-.87-1.27-1.04-.38-.124-.974-.186-1.78-.186H11L9.095 16.64"/>
     </g>
index f6c18e5..da226ae 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="italic-e">
         <path id="e" d="M7 18L9.474 6H18l-.282 1.367H10.77L10.02 11h6.09l-.28 1.367H9.74l-.88 4.273h7.44L16.018 18H7"/>
     </g>
index 3338bef..d848197 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="italic-geor-kan">
         <path id="geor-kan" d="M15.057 14.663C14.617 16.888 13.223 18 10.88 18 8.96 18 8 17.213 8 15.64c0-.298.036-.624.108-.977.083-.43.245-.836.488-1.217l1.24.605-.206.62c-.055.26-.083.497-.083.71 0 .97.52 1.46 1.564 1.46 1.31 0 2.108-.724 2.39-2.17l.058-.33a3.17 3.17 0 0 0 .066-.615c0-.927-.546-1.39-1.64-1.39H10.87l.247-1.26h1.118c1.203-.004 1.91-.55 2.12-1.64.04-.18.057-.355.057-.52 0-1.144-.9-1.715-2.696-1.715L11.94 6C14.646 6 16 6.877 16 8.627c0 .248-.027.516-.082.803-.204 1.092-1.05 1.824-2.54 2.194l-.033.166c1.23.2 1.845.823 1.845 1.872 0 .21-.025.433-.074.67l-.058.332"/>
     </g>
index ecde4b7..3b471d2 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="italic-i">
         <path id="i" d="M12.5 18l.25-.995h-1.5l2.508-10.037h1.5L15.5 6h-5l-.242.968h1.5l-2.51 10.037h-1.5L7.5 18z"/>
     </g>
index 730fb8a..b719095 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="italic-k">
         <path id="k" d="M12.018 10.652L17 6h-2l-5.31 5.234L11 6H9.5l-3 12H8l1.173-4.693 1.54-1.438C11 16 14 18 14 18h2s-4-2-3.982-7.348z"/>
     </g>
index 0ed100f..1cfeb7a 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="italic-s">
         <path id="s" d="M16.474 6.59l-.302 1.525a7.36 7.36 0 0 0-1.557-.628 5.432 5.432 0 0 0-1.487-.217c-.935 0-1.68.204-2.23.612-.554.408-.83.95-.83 1.627 0 .37.1.65.302.86.207.19.733.4 1.58.63l.937.23c1.06.274 1.795.622 2.208 1.046.413.42.62 1.007.62 1.766 0 1.167-.46 2.117-1.38 2.85-.913.734-2.12 1.1-3.617 1.1-.615 0-1.232-.06-1.852-.185-.62-.12-1.242-.3-1.867-.55l.31-1.61a7.613 7.613 0 0 0 1.72.805c.58.18 1.155.27 1.73.27.976 0 1.76-.216 2.347-.65.59-.434.883-1 .883-1.697 0-.465-.12-.816-.354-1.054-.233-.242-.737-.46-1.512-.657l-.937-.24c-1.07-.28-1.8-.6-2.19-.964-.39-.368-.584-.88-.584-1.535 0-1.152.442-2.094 1.325-2.828.89-.74 2.043-1.108 3.463-1.108.555 0 1.1.05 1.644.146.542.1 1.085.245 1.627.442"/>
     </g>
index cbcfff0..45b0391 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M16 9V8h-6v1h6zm-2 2v-1h-4v1h4zM6 5h1v16H6V5zm2 0h10v13c0 1.7-1.3 3-3 3H8V5z"/>
 </svg>
index 6ecb10d..77cf757 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M8 9V8h6v1H8zm2 2v-1h4v1h-4zm8-6h-1v16h1V5zm-2 0H6v13c0 1.7 1.3 3 3 3h7V5z"/>
 </svg>
index 131bbdb..988f38e 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M14.5 4C11.5 4 9 6.5 9 9.5c0 1 .3 1.9.7 2.8L4 18v2h4v-2h2v-2h2l1.2-1.2c.4.1.9.2 1.3.2 3 0 5.5-2.5 5.5-5.5S17.5 4 14.5 4zM16 9c-.8 0-1.5-.7-1.5-1.5S15.2 6 16 6s1.5.7 1.5 1.5S16.8 9 16 9z"/>
 </svg>
index 906ee6e..7ca2f8f 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M9.5 4c3 0 5.5 2.5 5.5 5.5 0 1-.3 1.9-.7 2.8L20 18v2h-4v-2h-2v-2h-2l-1.2-1.2c-.4.1-.9.2-1.3.2-3 0-5.5-2.5-5.5-5.5S6.5 4 9.5 4zM8 9c.8 0 1.5-.7 1.5-1.5S8.8 6 8 6s-1.5.7-1.5 1.5S7.2 9 8 9z"/>
 </svg>
index 238ca48..b9cbad0 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M3 7v9c0 1.7 1.3 3 3 3h15V7H3zm8 2h2v2h-2V9zm0 3h2v2h-2v-2zM8 9h2v2H8V9zm0 3h2v2H8v-2zm-1 5H6c-.6 0-1-.4-1-1v-1h2v2zm0-3H5v-2h2v2zm0-3H5V9h2v2zm9 6H8v-2h8v2zm0-3h-2v-2h2v2zm0-3h-2V9h2v2zm3 6h-2v-2h2v2zm0-3h-2v-2h2v2zm0-3h-2V9h2v2z"/>
 </svg>
index 8248804..d235a35 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M21 7v9c0 1.7-1.3 3-3 3H3V7h18zm-8 2h-2v2h2V9zm0 3h-2v2h2v-2zm3-3h-2v2h2V9zm0 3h-2v2h2v-2zm1 5h1c.6 0 1-.4 1-1v-1h-2v2zm0-3h2v-2h-2v2zm0-3h2V9h-2v2zm-9 6h8v-2H8v2zm0-3h2v-2H8v2zm0-3h2V9H8v2zm-3 6h2v-2H5v2zm0-3h2v-2H5v2zm0-3h2V9H5v2z"/>
 </svg>
index c75d14b..9de74f2 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/language-ltr-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/language-ltr-invert.png differ
index 3365bb9..429ee29 100644 (file)
@@ -1,7 +1,10 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
-    <g id="translation">
-        <path id="english" d="M14.34 9l-3.53 10h2.064l.72-2.406h3.624l.72 2.406H20L16.465 9h-2.12zm1.065 1.53L16.75 15h-2.69z"/>
-        <path id="chinese" d="M8.97 4.22c-.43.29-.88.616-1.25.874l.186.312c.14.194.275.393.407.594H4.47v1.47h1.593c.43 1.41 1.11 2.624 2.03 3.624-1.008.664-2.192 1.248-3.624 1.75L4 13c.317.487.714.976 1.03 1.375l.25-.094c1.593-.59 2.91-1.263 4.032-2.06.818.63 1.71 1.16 2.657 1.595l.56-1.624a13.21 13.21 0 0 1-1.908-1.063c.284-.28.59-.634.906-1.156.46-.716.776-1.57 1-2.5h1.657V6h-4.063c-.283-.552-.596-1.083-.97-1.53l-.186-.25zM7.72 7.47h3.186c-.32 1.075-.83 1.937-1.53 2.624-.713-.705-1.26-1.568-1.657-2.625zm6.31 5.31l-.467 1.658c.292-.514.577-1.075.812-1.532l-.344-.125z"/>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+    <g id="A">
+        <path d="M18.738 15.673l1.137 3.15h1.575L17.775 7.448h-2.188l-3.85 11.375h1.575l1.05-3.15h4.375zM16.55 8.76l1.837 5.427h-3.675l1.838-5.425z"/>
+    </g>
+    <g id="文">
+        <path d="M8.325 6.573h.787l-.875-1.75h-1.75l.438.875a1.56 1.56 0 0 0 1.4.875z"/>
+        <path d="m 9.202,12.874 c 0.7,0.525 1.486,0.963 2.45,1.225 l -0.438,1.31 A 9.17,9.17 0 0 1 8.151,13.835 c -1.49,1.137 -3.063,1.837 -4.813,2.363 L 2.9,14.885 C 4.386,14.36 5.874,13.835 7.1,12.872 5.962,11.648 5.174,10.335 4.65,8.758 l -1.663,0 0,-1.31 10.85,0 -0.438,1.312 -1.75,0 c -0.308,1.33 -1.255,2.957 -2.45,4.114 z m 1.05,-4.114 -4.114,0 c 0.35,1.226 1.138,2.363 2.013,3.238 0.926,-1 1.617,-1.957 2.1,-3.237 z"/>
     </g>
 </svg>
index bdd9abe..e27be3c 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/language-ltr.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/language-ltr.png differ
index 4f1fc10..d64b734 100644 (file)
@@ -1,7 +1,10 @@
 <?xml version="1.0" encoding="utf-8"?>
 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
-    <g id="translation">
-        <path id="english" d="M14.34 9l-3.53 10h2.064l.72-2.406h3.624l.72 2.406H20L16.465 9h-2.12zm1.065 1.53L16.75 15h-2.69z"/>
-        <path id="chinese" d="M8.97 4.22c-.43.29-.88.616-1.25.874l.186.312c.14.194.275.393.407.594H4.47v1.47h1.593c.43 1.41 1.11 2.624 2.03 3.624-1.008.664-2.192 1.248-3.624 1.75L4 13c.317.487.714.976 1.03 1.375l.25-.094c1.593-.59 2.91-1.263 4.032-2.06.818.63 1.71 1.16 2.657 1.595l.56-1.624a13.21 13.21 0 0 1-1.908-1.063c.284-.28.59-.634.906-1.156.46-.716.776-1.57 1-2.5h1.657V6h-4.063c-.283-.552-.596-1.083-.97-1.53l-.186-.25zM7.72 7.47h3.186c-.32 1.075-.83 1.937-1.53 2.624-.713-.705-1.26-1.568-1.657-2.625zm6.31 5.31l-.467 1.658c.292-.514.577-1.075.812-1.532l-.344-.125z"/>
+    <g id="A">
+        <path d="M18.738 15.673l1.137 3.15h1.575L17.775 7.448h-2.188l-3.85 11.375h1.575l1.05-3.15h4.375zM16.55 8.76l1.837 5.427h-3.675l1.838-5.425z"/>
+    </g>
+    <g id="文">
+        <path d="M8.325 6.573h.787l-.875-1.75h-1.75l.438.875a1.56 1.56 0 0 0 1.4.875z"/>
+        <path d="m 9.202,12.874 c 0.7,0.525 1.486,0.963 2.45,1.225 l -0.438,1.31 A 9.17,9.17 0 0 1 8.151,13.835 c -1.49,1.137 -3.063,1.837 -4.813,2.363 L 2.9,14.885 C 4.386,14.36 5.874,13.835 7.1,12.872 5.962,11.648 5.174,10.335 4.65,8.758 l -1.663,0 0,-1.31 10.85,0 -0.438,1.312 -1.75,0 c -0.308,1.33 -1.255,2.957 -2.45,4.114 z m 1.05,-4.114 -4.114,0 c 0.35,1.226 1.138,2.363 2.013,3.238 0.926,-1 1.617,-1.957 2.1,-3.237 z"/>
     </g>
 </svg>
index 680d726..97c0c09 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/language-rtl-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/language-rtl-invert.png differ
index ba3770b..f3c32eb 100644 (file)
@@ -1,7 +1,10 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
-    <g id="translation">
-        <path id="english" d="M7.53 9L4 19h2.063l.72-2.406h3.624l.72 2.406h2.062L9.65 9H7.53zm1.064 1.53L9.938 15H7.25z"/>
-        <path id="chinese" d="M14.594 4.22c-.43.29-.88.616-1.25.874l.187.312c.14.194.28.393.41.594h-3.843v1.47h1.594c.43 1.41 1.11 2.624 2.03 3.624-.662.437-1.413.82-2.25 1.187l.563 1.567a15.882 15.882 0 0 0 2.908-1.625 13.82 13.82 0 0 0 3.97 2.125l.28.094c.293-.514.578-1.075.813-1.532l-.375-.125c-1.38-.49-2.49-1.05-3.375-1.654.284-.28.59-.635.906-1.157.46-.717.775-1.572 1-2.5h1.656V6H15.75c-.283-.552-.596-1.083-.97-1.53l-.186-.25zm-1.25 3.25h3.187c-.315 1.075-.825 1.937-1.53 2.624-.71-.705-1.26-1.568-1.653-2.625zM9.97 12.874L9.624 13c.196.3.406.594.625.875l-.28-1z"/>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+    <g id="A">
+        <path d="M5.612 15.673l-1.137 3.15H2.9L6.575 7.448h2.188l3.85 11.375h-1.575l-1.05-3.15H5.613zM7.8 8.76l-1.837 5.427h3.675L7.8 8.762z" id="path5"/>
+    </g>
+    <g id="文">
+        <path d="M16.384 6.573h.787l-.873-1.75h-1.75l.438.875c.26.535.805.874 1.4.875z" id="path7"/>
+        <path d="M15.15 12.874c-.7.525-1.486.963-2.45 1.225l.438 1.31a9.17 9.17 0 0 0 3.063-1.575c1.49 1.137 3.064 1.837 4.814 2.363l.438-1.313c-1.486-.525-2.974-1.05-4.2-2.013 1.138-1.224 1.926-2.537 2.45-4.114h1.663v-1.31h-10.85l.438 1.312h1.75c.308 1.33 1.255 2.957 2.45 4.114zM14.1 8.76h4.114c-.35 1.226-1.138 2.363-2.013 3.238-.925-1-1.616-1.957-2.1-3.237z" id="path11-7"/>
     </g>
 </svg>
index ed64644..a7fd0cd 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/language-rtl.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/language-rtl.png differ
index 081252e..cf6c1d5 100644 (file)
@@ -1,7 +1,10 @@
 <?xml version="1.0" encoding="utf-8"?>
 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
-    <g id="translation">
-        <path id="english" d="M7.53 9L4 19h2.063l.72-2.406h3.624l.72 2.406h2.062L9.65 9H7.53zm1.064 1.53L9.938 15H7.25z"/>
-        <path id="chinese" d="M14.594 4.22c-.43.29-.88.616-1.25.874l.187.312c.14.194.28.393.41.594h-3.843v1.47h1.594c.43 1.41 1.11 2.624 2.03 3.624-.662.437-1.413.82-2.25 1.187l.563 1.567a15.882 15.882 0 0 0 2.908-1.625 13.82 13.82 0 0 0 3.97 2.125l.28.094c.293-.514.578-1.075.813-1.532l-.375-.125c-1.38-.49-2.49-1.05-3.375-1.654.284-.28.59-.635.906-1.157.46-.717.775-1.572 1-2.5h1.656V6H15.75c-.283-.552-.596-1.083-.97-1.53l-.186-.25zm-1.25 3.25h3.187c-.315 1.075-.825 1.937-1.53 2.624-.71-.705-1.26-1.568-1.653-2.625zM9.97 12.874L9.624 13c.196.3.406.594.625.875l-.28-1z"/>
+    <g id="A">
+        <path d="M5.612 15.673l-1.137 3.15H2.9L6.575 7.448h2.188l3.85 11.375h-1.575l-1.05-3.15H5.613zM7.8 8.76l-1.837 5.427h3.675L7.8 8.762z" id="path5"/>
+    </g>
+    <g id="文">
+        <path d="M16.384 6.573h.787l-.873-1.75h-1.75l.438.875c.26.535.805.874 1.4.875z" id="path7"/>
+        <path d="M15.15 12.874c-.7.525-1.486.963-2.45 1.225l.438 1.31a9.17 9.17 0 0 0 3.063-1.575c1.49 1.137 3.064 1.837 4.814 2.363l.438-1.313c-1.486-.525-2.974-1.05-4.2-2.013 1.138-1.224 1.926-2.537 2.45-4.114h1.663v-1.31h-10.85l.438 1.312h1.75c.308 1.33 1.255 2.957 2.45 4.114zM14.1 8.76h4.114c-.35 1.226-1.138 2.363-2.013 3.238-.925-1-1.616-1.957-2.1-3.237z" id="path11-7"/>
     </g>
 </svg>
index 0ab6bdb..f6e3d8e 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M20.8 20h-8.1v-.8c.4 0 .8-.1 1.3-.2s.8-.2.8-.4v-.2c0-.1 0-.2-.1-.3L13.4 15H8.3c-.1.3-.2.6-.4 1-.1.4-.3.7-.4 1-.1.4-.2.7-.2.8v.4c0 .2.2.4.5.6.3.2.9.3 1.7.3v.9H3.4v-.8c.2 0 .5-.1.8-.1.3-.1.5-.1.7-.2.3-.2.5-.4.7-.6.2-.3.4-.6.5-.9.8-2 1.6-3.9 2.4-5.9.8-2 1.7-4.1 2.7-6.5h2.1c1.4 3.3 2.4 6 3.2 7.9.8 1.9 1.4 3.6 2 4.8l.3.6c.1.2.3.3.6.5.2.1.4.2.7.3.3.1.5.1.7.1v.8zM13 14l-2.1-5.3L8.8 14H13z"/>
 </svg>
index 0ab6bdb..f6e3d8e 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M20.8 20h-8.1v-.8c.4 0 .8-.1 1.3-.2s.8-.2.8-.4v-.2c0-.1 0-.2-.1-.3L13.4 15H8.3c-.1.3-.2.6-.4 1-.1.4-.3.7-.4 1-.1.4-.2.7-.2.8v.4c0 .2.2.4.5.6.3.2.9.3 1.7.3v.9H3.4v-.8c.2 0 .5-.1.8-.1.3-.1.5-.1.7-.2.3-.2.5-.4.7-.6.2-.3.4-.6.5-.9.8-2 1.6-3.9 2.4-5.9.8-2 1.7-4.1 2.7-6.5h2.1c1.4 3.3 2.4 6 3.2 7.9.8 1.9 1.4 3.6 2 4.8l.3.6c.1.2.3.3.6.5.2.1.4.2.7.3.3.1.5.1.7.1v.8zM13 14l-2.1-5.3L8.8 14H13z"/>
 </svg>
index a848318..36a8165 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="layout-ltr">
         <path id="text" d="M5 19V5h6v8h8v6H5z"/>
         <path id="float" d="M13 5v6h6V5h-6zm5 5h-4V6h4v4z"/>
index b8c4586..39ba9c4 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="layout-rtl">
         <path id="text" d="M5 19v-6h8V5h6v14H5z"/>
         <path id="float" d="M5 5v6h6V5H5zm1 1h4v4H6V6z"/>
index 4024b6d..5bee6d5 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M15.387 4.33c-2.1 0-3.6 1.9-5.1 3.3.2 0 .5-.1.8-.1.5 0 1 .1 1.5.3.8-.8 1.6-1.7 2.8-1.7.6 0 1.3.3 1.8.7 1 1 1 2.6 0 3.6l-2.6 2.6c-.4.4-1.2.7-1.8.7-1.4 0-2.1-.9-2.6-2l-1.3 1.3c.8 1.5 2 2.6 3.8 2.6 1.2 0 2.3-.5 3-1.3l2.6-2.6c.9-.9 1.5-2 1.5-3.3-.2-2.2-2.2-4.1-4.4-4.1zm-4.3 12.1l-.9.9c-.4.4-1.2.7-1.8.7-.6 0-1.3-.3-1.8-.7-1-1-1-2.7 0-3.6l2.6-2.6c.4-.4 1.2-.7 1.8-.7 1.4 0 2.1 1 2.6 2l1.3-1.3c-.8-1.5-2-2.6-3.8-2.6-1.2 0-2.3.5-3 1.3l-2.6 2.6c-1.7 1.7-1.7 4.4 0 6 1.6 1.6 4.4 1.7 5.9 0l1.9-1.9c-.3.1-.6.1-.9.1-.5 0-.9 0-1.3-.2z"/>
 </svg>
index 0235bde..8e34361 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M9.025 3.6c2.1 0 3.6 1.9 5.1 3.3-.2 0-.5-.1-.8-.1-.5 0-1 .1-1.5.3-.8-.8-1.6-1.7-2.8-1.7-.6 0-1.3.3-1.8.7-1 1-1 2.6 0 3.6l2.6 2.6c.4.4 1.2.7 1.8.7 1.4 0 2.1-.9 2.6-2l1.3 1.3c-.8 1.5-2 2.6-3.8 2.6-1.2 0-2.3-.5-3-1.3l-2.6-2.6c-.9-.9-1.5-2-1.5-3.3.2-2.2 2.2-4.1 4.4-4.1zm4.3 12.1l.9.9c.4.4 1.2.7 1.8.7.6 0 1.3-.3 1.8-.7 1-1 1-2.7 0-3.6l-2.6-2.6c-.4-.4-1.2-.7-1.8-.7-1.4 0-2.1 1-2.6 2l-1.3-1.3c.8-1.5 2-2.6 3.8-2.6 1.2 0 2.3.5 3 1.3l2.6 2.6c1.7 1.7 1.7 4.4 0 6-1.6 1.6-4.4 1.7-5.9 0l-1.9-1.9c.3.1.6.1.9.1.5 0 .9 0 1.3-.2z"/>
 </svg>
index 686a8e7..2eb5329 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M21 7H9V5h12v2zM7 6c0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2 2 .9 2 2zm14 7H9v-2h12v2zM7 12c0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2 2 .9 2 2zm14 7H9v-2h12v2zM7 18c0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2 2 .9 2 2z"/>
 </svg>
index aebe6f2..dcce2ae 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M3 7h12V5H3v2zm14-1c0 1.1.9 2 2 2s2-.9 2-2-.9-2-2-2-2 .9-2 2zM3 13h12v-2H3v2zm14-1c0 1.1.9 2 2 2s2-.9 2-2-.9-2-2-2-2 .9-2 2zM3 19h12v-2H3v2zm14-1c0 1.1.9 2 2 2s2-.9 2-2-.9-2-2-2-2 .9-2 2z"/>
 </svg>
index 58ffe88..1ab3f23 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M21 7H8V5h13v2zm0 6H8v-2h13v2zm0 6H8v-2h13v2zM4 4h2v4H5V5H4zm-1 6V9h3v3H4v1h2v1H3v-3h2v-1zm3 10H3v-1h2v-1H4v-1h1v-1H3v-1h3z"/>
 </svg>
index 8bec0d5..ab12c83 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M3 7h13V5H3zm0 6h13v-2H3zm0 6h13v-2H3zM18 4h2v4h-1V5h-1zm0 6V9h3v3h-2v1h2v1h-3v-3h2v-1zm3 10h-3v-1h2v-1h-1v-1h1v-1h-2v-1h3z"/>
 </svg>
index 31fe8ff..e0c482b 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #D11D13 }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #d11d13 }</style>
     <path d="M15 8s0-3-2.5-3S10 8 10 8v1h5zm2 0v1h2v10H9c-1.7 0-3-1.3-3-3V9h2V8s0-5 4.5-5S17 8 17 8z"/>
 </svg>
index 03788f1..ee341a9 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M15 8s0-3-2.5-3S10 8 10 8v1h5zm2 0v1h2v10H9c-1.7 0-3-1.3-3-3V9h2V8s0-5 4.5-5S17 8 17 8z"/>
 </svg>
index 79972de..edc9312 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #D11D13 }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #d11d13 }</style>
     <path d="M10 8s0-3 2.5-3S15 8 15 8v1h-5zM8 8v1H6v10h10c1.7 0 3-1.3 3-3V9h-2V8s0-5-4.5-5S8 8 8 8z"/>
 </svg>
index e3fda47..2f8851c 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M10 8s0-3 2.5-3S15 8 15 8v1h-5zM8 8v1H6v10h10c1.7 0 3-1.3 3-3V9h-2V8s0-5-4.5-5S8 8 8 8z"/>
 </svg>
index 8b10f25..f81d7c4 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M15 14v3l5-4.5L15 8v3H8c0 1.7 1.3 3 3 3h4zm-1-9H4v15h10v-2H6V7h8V5z"/>
 </svg>
index 412cd92..d3fae89 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M9 14v3l-5-4.5L9 8v3h7c0 1.7-1.3 3-3 3H9zm1-9h10v15H10v-2h8V7h-8V5z"/>
 </svg>
index a2dcf4e..48eafd4 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M12 6c-3.9 0-7 3.1-7 7s3.1 7 7 7 7-3.1 7-7-3.1-7-7-7zm0 13c-3.3 0-6-2.7-6-6s2.7-6 6-6 6 2.7 6 6-2.7 6-6 6zm-1.7-4.6c-.7 0-1-.4-1-1.2s.3-1.2 1-1.2c.4 0 .6.2.8.6l.9-.5c-.4-.7-1-1-1.9-1-.6 0-1.1.2-1.5.6s-.6.8-.6 1.5.2 1.2.6 1.6c.4.4.9.6 1.5.6.8 0 1.4-.4 1.9-1.1l-.9-.4c-.2.3-.5.5-.8.5zm4 0c-.7 0-1-.4-1-1.2s.3-1.2 1-1.2c.4 0 .6.2.8.6l.9-.5c-.4-.7-1-1-1.9-1-.6 0-1.1.2-1.5.6s-.6.8-.6 1.5.2 1.2.6 1.6c.4.4.9.6 1.5.6.8 0 1.4-.4 1.9-1.1l-.9-.4c-.2.3-.5.5-.8.5z"/>
 </svg>
index ca6d1d2..4794f33 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M15.4 7.8c-2-.9-2.3-2.5-2.4-2.8.1.1 2 1 2 1l-3-5-3 5 2-1s0 .8.6 2.1c.8 1.5 2.2 2.2 2.2 2.2s1.6.7 2.2 1.3l-.7.7-.5-.5-.4 1.8 1.8-.4-.5-.5.7-.7c.9 1 1.5 2.3 1.6 3.8h-1V14l-1.5 1 1.5 1v-.8h1c-.1 1.5-.6 2.8-1.6 3.8l-.7-.7.5-.5-1.8-.4.4 1.8.5-.5.7.7c-1 .9-2.3 1.5-3.8 1.6v-1h.8l-1-1.5-1 1.5h.8v1c-1.5-.1-2.8-.6-3.8-1.6l.7-.7.5.5.4-1.8-1.8.4.5.5-.7.7c-.9-1-1.5-2.3-1.6-3.8h1v.8l1.5-1L7 14v.8H6c.1-1.5.6-2.8 1.6-3.8l.7.7-.5.5 1.8.4-.4-1.8-.5.5-.7-.7-1.5-1.4A7.99 7.99 0 0 0 4 15c0 4.4 3.6 8 8 8s8-3.6 8-8c0-3.2-1.9-5.9-4.6-7.2z"/>
     <circle cx="12" cy="15" r="3"/>
 </svg>
index f6c8e74..1fe3277 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M22.3 6.3c0 .2 0 .3-.1.3-.7.1-1.2.5-1.6 1.1-.1.2-.2.4-.3.7l-4.6 10.1c-.1.2-.2.3-.2.3s-.1.1-.2.1c-.2 0-.4-.1-.5-.4L12.2 13l-2.8 5.5c-.1.3-.3.4-.5.4s-.4-.1-.5-.4L4.1 8.4c-.3-.8-.6-1.2-.8-1.4-.2-.2-.5-.3-1-.4-.1-.1-.1-.2-.1-.3 0-.2 0-.3.1-.3h4.3c.1.1.1.2.1.3 0 .2 0 .3-.1.3-.6.1-1 .2-1.1.4-.1.2 0 .6.3 1.2l3.6 8.2h.1l2.2-4.4L10 8.4c-.3-.7-.6-1.2-.8-1.4s-.5-.3-.9-.4c-.1-.1-.1-.2-.1-.3 0-.2 0-.3.1-.3h3.6c.1.1.1.2.1.3 0 .2 0 .3-.1.3-.4.1-.6.2-.6.4s.1.6.4 1.2l1 1.9 1-1.9c.3-.6.5-.9.5-1.1 0-.2 0-.3-.1-.4-.1-.1-.3-.1-.5-.1l-.1-.3c0-.2 0-.3.1-.3h3c.1.1.1.2.1.3 0 .2 0 .3-.1.3-.5.1-.8.2-1.1.5-.3.3-.6.7-.8 1.3l-1.3 2.8 2.5 5.2h.1l3.7-8.1c.3-.5.3-.9.2-1.2-.1-.3-.5-.4-1.1-.5-.1-.1-.1-.2-.1-.3s0-.3.1-.3h3.7c-.2.1-.2.2-.2.3z"/>
 </svg>
index b988187..2d6d0a0 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M15 6L9 4 3 6v15l6-2 6 2 6-2V4l-6 2zM8.7 18.1L4 19.6V6.7L9 5v12.9l-.3.2zm11.3.2L15 20V7.1l.3-.1L20 5.4v12.9z"/>
 </svg>
index 3e09163..00d3efc 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M9 6l6-2 6 2v15l-6-2-6 2-6-2V4l6 2zm6.3 12.1l4.7 1.5V6.7L15 5v12.9l.3.2zM4 18.3L9 20V7.1L8.7 7 4 5.4v12.9z"/>
 </svg>
index 2315072..dc9791e 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M19 12c0-3.9-3.1-7-7-7s-7 3.1-7 7c0 1.4.4 2.6 1.1 3.7L12 23l5.9-7.3c.7-1.1 1.1-2.3 1.1-3.7zm-7 4c-2.2 0-4-1.8-4-4s1.8-4 4-4 4 1.8 4 4-1.8 4-4 4z"/>
 </svg>
index 59814e7..68fe22a 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M24 4h-4V0h-2v4h-4v2h4v4h2V6h4z"/>
     <path d="M18 11h-1V7.1l-.1-.1H13V5.1c-.3-.1-.7-.1-1-.1-3.9 0-7 3.1-7 7 0 1.4.4 2.6 1.1 3.7L12 23l5.9-7.3c.7-1.1 1.1-2.3 1.1-3.7 0-.3 0-.7-.1-1H18zm-6 5c-2.2 0-4-1.8-4-4s1.8-4 4-4 4 1.8 4 4-1.8 4-4 4z"/>
 </svg>
index 973bfc2..e3ba379 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M0 4h4V0h2v4h4v2H6v4H4V6H0z"/>
     <path d="M6 11h1V7.1l.1-.1H11V5.1c.3-.1.7-.1 1-.1 3.9 0 7 3.1 7 7 0 1.4-.4 2.6-1.1 3.7L12 23l-5.9-7.3C5.4 14.6 5 13.4 5 12c0-.3 0-.7.1-1H6zm6 5c2.2 0 4-1.8 4-4s-1.8-4-4-4-4 1.8-4 4 1.8 4 4 4z"/>
 </svg>
index cd287e4..dbd4a98 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="menu">
         <path id="lines" d="M6 15h12a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1v-1a1 1 0 0 1 1-1zm-1-4v1a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-1a1 1 0 0 0-1-1H6a1 1 0 0 0-1 1zm0-5v1a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1H6a1 1 0 0 0-1 1z"/>
     </g>
index 300e4df..df72450 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M21 9c0-1.7-1.3-3-3-3H3v3l9 4 9-4zM3 11v6c0 1.7 1.3 3 3 3h15v-9l-9 4-9-4z"/>
 </svg>
index 629ddac..1bb1dae 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M3 9c0-1.7 1.3-3 3-3h15v3l-9 4-9-4zm18 2v6c0 1.7-1.3 3-3 3H3v-9l9 4 9-4z"/>
 </svg>
index 0c27ce7..6fce33f 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M19.1 17.5c-3.3 1.4-7.1-.2-8.5-3.5-1.4-3.3.2-7.1 3.5-8.5.2-.1.5-.2.7-.3-1.6-.4-3.2-.3-4.8.4C6 7.3 4 12 5.7 16c1.7 4.1 6.4 6 10.5 4.3 1.7-.7 3-1.9 3.8-3.4-.3.3-.6.4-.9.6z"/>
 </svg>
index 5c7a766..63c7b4c 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M20 11l-4-3v2h-3V7h2l-3-4-3 4h2v3H8V8l-4 3 4 3v-2h3v3H9l3 4 3-4h-2v-3h3v2z"/>
 </svg>
index fbebf0c..6fdddd8 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="move-ltr">
         <path id="arrow" d="M8.935 7.18l5.302 5.303-5.302 5.303L10.35 19.2l6.715-6.717-6.716-6.716z"/>
     </g>
index 1067738..2f1e91e 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="move-rtl">
         <path id="arrow" d="M15.065 17.786l-5.302-5.303 5.302-5.302-1.415-1.41-6.714 6.72 6.714 6.71z"/>
     </g>
index 18e4118..25cf321 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M12 5l2.5 2.5L11 11c-1.2 1.2-1.2 2.8 0 4l5.5-5.5L19 12V5h-7zm5 12H8c-.6 0-1-.4-1-1V7h3L8 5H5v11c0 1.7 1.3 3 3 3h11v-3l-2-2v3z"/>
 </svg>
index e357be6..cb0a035 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M12 5L9.5 7.5 13 11c1.2 1.2 1.2 2.8 0 4L7.5 9.5 5 12V5h7zM7 17h9c.6 0 1-.4 1-1V7h-3l2-2h3v11c0 1.7-1.3 3-3 3H5v-3l2-2v3z"/>
 </svg>
index 8b4ab65..220450a 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M17.8 5.7c-.5 0-.9.2-1.2.5s-.5.7-.5 1.2v4.3H11v-4l-6 5.5 6 5.5v-4h8v-9h-1.2z" id="line_return"/>
 </svg>
index 2642261..214aea9 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M6.2 5.7c.5 0 .9.2 1.2.5.3.3.5.7.5 1.2v4.3H13v-4l6 5.5-6 5.5v-4H5v-9h1.2z" id="line_return"/>
 </svg>
index 555eb59..171f6e6 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M6 7v12c-.6 0-1-.4-1-1V9H4v9c0 1.1.9 2 2 2h15V7H6zm9 11H8v-1h7v1zm0-2H8v-1h7v1zm0-2H8v-1h7v1zm4 4h-3v-5h3v5zm0-7H8V9h11v2z"/>
 </svg>
index 778810c..c161b6e 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M19 7v12c.6 0 1-.4 1-1V9h1v9c0 1.1-.9 2-2 2H4V7h15zm-9 11h7v-1h-7v1zm0-2h7v-1h-7v1zm0-2h7v-1h-7v1zm-4 4h3v-5H6v5zm0-7h11V9H6v2z"/>
 </svg>
index b541ca5..92882b0 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M15 13l2 2V5h-3v2h1zM3 3L2 4l1 1v14h3v-2H5V7l2 2v10h3v-2H9v-6l6 6h-1v2h3l3 3 1-1-3-3zm7 4V5H7l2 2zm8-2v2h1v10l2 2V5z" id="noWikiText-rtl"/>
 </svg>
index 9ebd6a9..b07a6a1 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M9 13l-2 2V5h3v2H9zM21 3l1 1-1 1v14h-3v-2h1V7l-2 2v10h-3v-2h1v-6l-6 6h1v2H7l-3 3-1-1 3-3zm-7 4V5h3l-2 2zM6 5v2H5v10l-2 2V5z" id="noWikiText-rtl"/>
 </svg>
index 871c204..94fd0b8 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M12 5c-4.4 0-8 3.6-8 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8z"/>
 </svg>
index 6ea2ad6..50d4ad6 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="svg3116"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="svg3116"><style>* { fill: #ffffff }</style>
     <path d="M12 18a6 6 0 1 1 0-12 6 6 0 0 1 0 12zm-1-5h2V8h-2zm0 3h2v-2h-2z" id="alert"/>
 </svg>
index f95bb00..e8a56bc 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><style>* { fill: #FFFFFF }</style>
+<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><style>* { fill: #ffffff }</style>
     <path d="M17.8 18.6H2.5l2.7-2.7V6h15.3v9.9c0 1.53-1.17 2.7-2.7 2.7zm-7.542-4.95c0 .405-.135.675-.405.945-.27.27-.607.405-.945.405-.405 0-.675-.135-.945-.405a1.332 1.332 0 0 1-.405-.945c0-.338.135-.675.405-.945.27-.27.608-.405.945-.405.338 0 .675.135.945.405.27.27.405.607.405.945zm4.05 0c0 .405-.135.675-.405.945-.27.27-.607.405-.945.405-.405 0-.675-.135-.945-.405a1.332 1.332 0 0 1-.405-.945c0-.338.135-.675.405-.945.27-.27.608-.405.945-.405.338 0 .675.135.945.405.27.27.405.607.405.945zm4.05 0c0 .405-.135.675-.405.945-.27.27-.607.405-.945.405-.405 0-.675-.135-.945-.405a1.332 1.332 0 0 1-.405-.945c0-.338.135-.675.405-.945.27-.27.608-.405.945-.405.338 0 .675.135.945.405.27.27.405.607.405.945z" id="ongoing-conversation" fill-rule="evenodd"/>
 </svg>
index 7b02ae0..89d2745 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><style>* { fill: #347BFF }</style>
+<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><style>* { fill: #347bff }</style>
     <path d="M17.8 18.6H2.5l2.7-2.7V6h15.3v9.9c0 1.53-1.17 2.7-2.7 2.7zm-7.542-4.95c0 .405-.135.675-.405.945-.27.27-.607.405-.945.405-.405 0-.675-.135-.945-.405a1.332 1.332 0 0 1-.405-.945c0-.338.135-.675.405-.945.27-.27.608-.405.945-.405.338 0 .675.135.945.405.27.27.405.607.405.945zm4.05 0c0 .405-.135.675-.405.945-.27.27-.607.405-.945.405-.405 0-.675-.135-.945-.405a1.332 1.332 0 0 1-.405-.945c0-.338.135-.675.405-.945.27-.27.608-.405.945-.405.338 0 .675.135.945.405.27.27.405.607.405.945zm4.05 0c0 .405-.135.675-.405.945-.27.27-.607.405-.945.405-.405 0-.675-.135-.945-.405a1.332 1.332 0 0 1-.405-.945c0-.338.135-.675.405-.945.27-.27.608-.405.945-.405.338 0 .675.135.945.405.27.27.405.607.405.945z" id="ongoing-conversation" fill-rule="evenodd"/>
 </svg>
index 5b1b067..a66123e 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><style>* { fill: #FFFFFF }</style>
+<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><style>* { fill: #ffffff }</style>
     <path d="M5.2 18.6h15.3l-2.7-2.7V6H2.5v9.9c0 1.53 1.17 2.7 2.7 2.7zm7.542-4.95c0 .405.135.675.405.945.27.27.607.405.945.405.405 0 .675-.135.945-.405.27-.27.405-.607.405-.945 0-.337-.135-.675-.405-.945a1.334 1.334 0 0 0-.945-.405c-.338 0-.675.135-.945.405-.27.27-.405.607-.405.945zm-4.05 0c0 .405.135.675.405.945.27.27.608.405.945.405.405 0 .675-.135.945-.405.27-.27.405-.607.405-.945 0-.337-.135-.675-.405-.945a1.334 1.334 0 0 0-.945-.405c-.338 0-.675.135-.945.405-.27.27-.405.608-.405.945zm-4.05 0c0 .405.135.675.405.945.27.27.608.405.945.405.405 0 .675-.135.945-.405.27-.27.405-.607.405-.945 0-.337-.135-.675-.405-.945a1.332 1.332 0 0 0-.945-.405c-.337 0-.675.135-.945.405-.27.27-.405.608-.405.945z" id="ongoing-conversation" fill-rule="evenodd"/>
 </svg>
index 0d0c46e..d0c3c64 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><style>* { fill: #347BFF }</style>
+<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><style>* { fill: #347bff }</style>
     <path d="M5.2 18.6h15.3l-2.7-2.7V6H2.5v9.9c0 1.53 1.17 2.7 2.7 2.7zm7.542-4.95c0 .405.135.675.405.945.27.27.607.405.945.405.405 0 .675-.135.945-.405.27-.27.405-.607.405-.945 0-.337-.135-.675-.405-.945a1.334 1.334 0 0 0-.945-.405c-.338 0-.675.135-.945.405-.27.27-.405.607-.405.945zm-4.05 0c0 .405.135.675.405.945.27.27.608.405.945.405.405 0 .675-.135.945-.405.27-.27.405-.607.405-.945 0-.337-.135-.675-.405-.945a1.334 1.334 0 0 0-.945-.405c-.338 0-.675.135-.945.405-.27.27-.405.608-.405.945zm-4.05 0c0 .405.135.675.405.945.27.27.608.405.945.405.405 0 .675-.135.945-.405.27-.27.405-.607.405-.945 0-.337-.135-.675-.405-.945a1.332 1.332 0 0 0-.945-.405c-.337 0-.675.135-.945.405-.27.27-.405.608-.405.945z" id="ongoing-conversation" fill-rule="evenodd"/>
 </svg>
index 1695dae..8136cb9 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M10 8h9v2h-9V8zm0 3h9v2h-9v-2zm0 3h6v2h-6v-2zm11-8H3V4h18v2zm0 14H3v-2h18v2zM3 12l5 4V8l-5 4z"/>
 </svg>
index d3e794f..4a08f5f 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M14 8H5v2h9V8zm0 3H5v2h9v-2zm0 3H8v2h6v-2zM3 6h18V4H3v2zm0 14h18v-2H3v2zm18-8l-5 4V8l5 4z"/>
 </svg>
index 62d78e1..d9d1390 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="outline-ltr">
         <path id="text" d="M5 13h14v6H5v-6z"/>
         <path id="float" d="M5 5v6h6V5H5zm5 5H6V6h4v4z"/>
index b992baf..f1dd2df 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="outline-rtl">
         <path id="text" d="M19 19H5v-6h14v6z"/>
         <path id="float" d="M13 5v6h6V5h-6zm1 1h4v4h-4V6z"/>
index 965697b..ffc0cc0 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M12 5c-4.4 0-8 3.6-8 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm-2 12V9l6 4-6 4z"/>
 </svg>
index 67143de..9c3220b 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M12 5c4.4 0 8 3.6 8 8s-3.6 8-8 8-8-3.6-8-8 3.6-8 8-8zm2 12V9l-6 4 6 4z"/>
 </svg>
index 08c2c36..4737769 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M18 8h-1V4H7v4H3v6c0 1.7 1.3 3 3 3h1v3h10v-3h4v-6c0-1.7-1.3-3-3-3zM8 5h8v3H8V5zm8 14H8v-6h8v6z"/>
 </svg>
index d431703..14d5bfe 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M6 8h1V4h10v4h4v6c0 1.7-1.3 3-3 3h-1v3H7v-3H3v-6c0-1.7 1.3-3 3-3zm10-3H8v3h8V5zM8 19h8v-6H8v6z"/>
 </svg>
index 601d880..fb03c63 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M18 9.9c-.7 0-1.4.3-1.8.9V6h-4c.2-.4.4-.8.4-1.2 0-1.2-1-2.2-2.2-2.2-1.3-.1-2.3.9-2.3 2.2 0 .4.2.8.4 1.2H4.1v3.6l.6-.1c1.4 0 2.5 1.1 2.5 2.5s-1.1 2.5-2.5 2.5c-.2 0-.4 0-.6-.1V18H9c-.5.4-.9 1-.9 1.8 0 1.2 1 2.2 2.3 2.2 1.2 0 2.2-1 2.2-2.2 0-.7-.3-1.4-.9-1.8h4.5v-4.5c.4.5 1 .9 1.8.9 1.2 0 2.2-1 2.2-2.2 0-1.3-1-2.3-2.2-2.3z"/>
 </svg>
index 909fb8e..9f7ce1e 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M6.3 9.9c.7 0 1.4.3 1.8.9V6h4c-.2-.4-.4-.8-.4-1.2 0-1.2 1-2.2 2.2-2.2 1.3-.1 2.3.9 2.3 2.2 0 .4-.2.8-.4 1.2h4.4v3.6l-.6-.1c-1.4 0-2.5 1.1-2.5 2.5s1.1 2.5 2.5 2.5c.2 0 .4 0 .6-.1V18h-4.9c.5.4.9 1 .9 1.8 0 1.2-1 2.2-2.3 2.2-1.2 0-2.2-1-2.2-2.2 0-.7.3-1.4.9-1.8H8.1v-4.5c-.4.5-1 .9-1.8.9-1.2 0-2.2-1-2.2-2.2 0-1.3 1-2.3 2.2-2.3z"/>
 </svg>
index 36a8442..17de62b 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="quotes">
         <path id="quote" d="M6.9 8.4c-.446.55-1.974 2.6-1.9 5.7V17h4.7c.9 0 1.6-.7 1.6-1.6V11H8.2s.05-.74.6-1.4c.453-.543 1-.9 1.6-1.2.2-.1.47-.212.6-.5.127-.282.2-.5.2-.9v-.6c-1 .2-1.744.197-2.6.6-.856.403-1.272.873-1.7 1.4z"/>
     </g>
index 5b48b87..0ac72cb 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="quotes">
         <path id="quote" d="M17.1 8.4c.446.55 1.9 2.6 1.9 5.7V17h-4.7c-.9 0-1.6-.7-1.6-1.6V11h3.1s-.05-.74-.6-1.4c-.453-.543-1-.9-1.6-1.2-.2-.1-.47-.212-.6-.5-.127-.282-.2-.5-.2-.9v-.6c1 .2 1.744.197 2.6.6.856.403 1.272.873 1.7 1.4z"/>
     </g>
index d9e2e06..8953f4d 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="quotes-add">
         <path id="quote" d="M5.9 10.4c-.446.55-1.974 2.6-1.9 5.7V19h4.7c.9 0 1.593-.7 1.6-1.6V13H7.2s.05-.74.6-1.4c.453-.543 1-.9 1.6-1.2.2-.1.47-.212.6-.5.127-.282.2-.5.2-.9v-.6c-1 .2-1.744.197-2.6.6-.856.403-1.272.873-1.7 1.4z"/>
         <path id="quote2" d="M15 9.344c-.476.32-.78.677-1.094 1.062A8.76 8.76 0 0 0 12 16.094V19h4.688a1.6 1.6 0 0 0 1.625-1.594V13H15V9.344z"/>
index 63e715a..1ada793 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="quotes-add">
         <path id="quote" d="M18.097 10.4c.446.55 1.974 2.6 1.9 5.7V19h-4.7c-.9 0-1.593-.7-1.6-1.6V13h3.1s-.05-.74-.6-1.4c-.453-.543-1-.9-1.6-1.2-.2-.1-.47-.212-.6-.5-.127-.282-.2-.5-.2-.9v-.6c1 .2 1.744.197 2.6.6.856.403 1.272.873 1.7 1.4z"/>
         <path id="quote2" d="M8.997 9.344c.476.32.782.677 1.094 1.062A8.758 8.758 0 0 1 12 16.094V19H7.31c-.9 0-1.618-.694-1.625-1.594V13h3.312V9.344z"/>
index ae5581c..61b1550 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="regular-expression">
         <path id="left-bracket" d="M3 12.045c0-.99.15-1.915.45-2.777A6.886 6.886 0 0 1 4.764 7H6.23a7.923 7.923 0 0 0-1.25 2.374 8.563 8.563 0 0 0 .007 5.314c.29.85.7 1.622 1.23 2.312h-1.45a6.53 6.53 0 0 1-1.314-2.223 8.126 8.126 0 0 1-.45-2.732"/>
         <path id="dot" d="M10 16a1 1 0 1 1-2 0 1 1 0 0 1 2 0z"/>
index 3a09864..458abd0 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <circle cx="11.5" cy="8.5" r="2.5"/>
     <path d="M16.3 8.7L17 8l-.8-.8.4-.8-1.1-.5.1-.9-1.2-.2-.1-.9-1.2.2-.4-.8-1.1.5L11 3l-.8.8-.9-.4-.5 1.1-.9-.2-.2 1.2-.9.2.2 1.2-.9.4.5 1.1L6 9l.8.8-.4.8 1.1.5-.1.9 1.2.2.1.9 1.2-.2.4.8 1.1-.5.6.8.8-.8.8.4.5-1.1.9.1.2-1.2.9-.1-.2-1.2.8-.4-.4-1zM11.5 12C9.6 12 8 10.4 8 8.5S9.6 5 11.5 5 15 6.6 15 8.5 13.4 12 11.5 12zm.5 3l-.7-.7-1.1.6-.4-.7-.8.3V23l2.5-3 2.5 3v-8.5l-1-.5z"/>
 </svg>
index 992aee9..316ac6d 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="search">
         <path id="magnifying-glass" d="M10.5 4a6.5 6.5 0 1 0 2.844 12.344L16 19c1.4 1.4 2.5 1.5 4 0l-4.438-4.438A6.426 6.426 0 0 0 17 10.5 6.5 6.5 0 0 0 10.5 4zm0 2a4.5 4.5 0 1 1 0 9 4.5 4.5 0 0 1 0-9z"/>
     </g>
index 5da62dc..1d36daf 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="search">
         <path id="magnifying-glass" d="M13.5 4a6.5 6.5 0 1 1-2.844 12.344L8 19c-1.4 1.4-2.5 1.5-4 0l4.438-4.438A6.426 6.426 0 0 1 7 10.5 6.5 6.5 0 0 1 13.5 4zm0 2a4.5 4.5 0 1 0 0 9 4.5 4.5 0 0 0 0-9z"/>
     </g>
index ed31a41..488e2e2 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #ffffff }</style>
     <g id="secure">
         <path id="lock" d="M8 5h.02v-.997c0-.057.003-1.41-.833-2.255-.434-.438-.998-.66-1.68-.66s-1.244.222-1.677.66c-.837.846-.833 2.198-.832 2.25V5H3a1 1 0 0 0-1 1v3a1 1 0 0 0 1 1h5a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1zM3.998 5V3.993c0-.01.005-1 .543-1.543.49-.485 1.45-.487 1.94-.002.543.546.545 1.536.545 1.55V5H3.998z"/>
     </g>
index e67b8f4..543aded 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="settings">
         <path id="gear" d="M3 4h3v2H3zm9 0h9v2h-9zM8 3h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H8a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1zm-5 8h9v2H3zm15 0h3v2h-3zm-4-1h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1zM3 18h6v2H3zm12 0h6v2h-6zm-4-1h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1z"/>
     </g>
index 554525a..101e2af 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M0 20h24v1H0v-1zm6-8l-1-1-2 2-2-2-1 1 2 2-2 2 1 1 2-2 2 2 1-1-2-2zm15.6 3.7c-.9-.5-1.9-.5-2.7 0-1.5.9-3.1.4-3.1.4-.4-.2-.8-.4-1.1-.6 2.2-.6 4.4-1.8 6-3.9 1.1-1.2 2.5-3.9.4-6-.7-.7-1.6-1.1-2.7-1-1.4.1-2.8.9-3.9 2.1-.9 1.1-3.1 4.5-2.3 7.5 0 .1 0 .2.1.3-2.3.3-4.2.2-4.4.1v1.5c.7.1 2.7.2 5.1-.2.5.7 1.3 1.2 2.3 1.6.1 0 2.4.8 4.5-.6.5-.3.9-.1 1.1 0 .4.2.7.6.7 1H23c0-.8-.6-1.7-1.4-2.2zm-8-1.7c-.5-2.2 1.1-5.1 2-6.2.8-.9 1.8-1.5 2.8-1.6h.1c.6 0 1.1.2 1.5.6 1.6 1.6-.4 3.9-.5 4-1.5 2-3.7 3-5.8 3.5l-.1-.3z"/>
 </svg>
index 6b30010..67bd738 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M24 20H0v1h24v-1zm-6-8l1-1 2 2 2-2 1 1-2 2 2 2-1 1-2-2-2 2-1-1 2-2zM2.4 15.7c.9-.5 1.9-.5 2.7 0 1.5.9 3.1.4 3.1.4.4-.2.8-.4 1.1-.6-2.2-.6-4.4-1.8-6-3.9-1.1-1.2-2.5-3.9-.4-6 .7-.7 1.6-1.1 2.7-1 1.4.1 2.8.9 3.9 2.1.9 1.1 3.1 4.5 2.3 7.5 0 .1 0 .2-.1.3 2.3.3 4.2.2 4.4.1v1.5c-.7.1-2.7.2-5.1-.2-.5.7-1.3 1.2-2.3 1.6-.1 0-2.4.8-4.5-.6-.5-.3-.9-.1-1.1 0-.4.2-.7.6-.7 1H1c0-.8.6-1.7 1.4-2.2zm8-1.7c.5-2.2-1.1-5.1-2-6.2-.8-.9-1.8-1.5-2.8-1.6h-.1c-.6 0-1.1.2-1.5.6-1.6 1.6.4 3.9.5 4 1.5 2 3.7 3 5.8 3.5l.1-.3z"/>
 </svg>
index 1126dba..ebbc3c1 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path id="a" d="M12.666 6h-1.372l-4.48 12H8.52l1.493-4h4l1.507 4h1.666l-4.52-12zm-2.28 7l1.617-4.333L13.637 13h-3.25z"/>
     <g id="down">
         <path id="arrow" d="M22 3l-3.5 6L15 3z"/>
index ffac2da..02a8fe6 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path id="a" d="M12.666 6h-1.372l-4.48 12H8.52l1.493-4h4l1.507 4h1.666l-4.52-12zm-2.28 7l1.617-4.333L13.637 13h-3.25z"/>
     <g id="down">
         <path id="arrow" d="M9 3L5.5 9 2 3z"/>
index 9f5a2e3..107f5f6 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M17.6 20h-5.4v-.5c.2 0 .5-.1.9-.1.3-.1.5-.2.5-.3V19s0-.1-.1-.2l-.8-2H9.3c-.1.2-.2.4-.3.7-.1.3-.2.5-.2.7-.1.3-.1.4-.2.6v.2c0 .1.1.3.3.4.2.1.6.2 1.1.2v.4H6v-.5c.2 0 .3 0 .5-.1.2 0 .3-.1.5-.2s.4-.2.5-.4l.3-.6c.5-1.3 1.1-2.6 1.6-3.9.5-1.3 1.1-2.7 1.8-4.3h1.4c.9 2.2 1.6 4 2.1 5.3.5 1.3 1 2.4 1.3 3.2.1.1.1.3.2.4.1.1.2.2.4.3.1.1.3.1.5.2s.3.1.5.1v.5zm-5.2-4L11 12.4 9.6 16h2.8z"/>
 </svg>
index 09b8413..a290c92 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M6 19.5c.1 0 .3 0 .5-.1s.3-.1.5-.2.3-.2.4-.3c.1-.1.2-.2.2-.4.4-.9.8-1.9 1.3-3.2.5-1.3 1.2-3.1 2.1-5.3h1.4c.7 1.6 1.2 3 1.8 4.3.5 1.3 1.1 2.6 1.6 3.9l.3.6c.1.2.3.3.5.4.1.1.3.1.5.2.2 0 .4.1.5.1v.5h-4v-.5c.5 0 .9-.1 1.1-.2.2-.1.3-.2.3-.4v-.2c0-.1-.1-.3-.2-.6-.1-.2-.2-.4-.2-.7-.1-.3-.2-.5-.3-.7h-3.4l-.8 2c0 .1-.1.1-.1.2v.1c0 .1.2.2.5.3.3.1.6.1.9.1v.6H6v-.5zm8-3.5l-1.4-3.6-1.4 3.6H14z"/>
 </svg>
index 30cb63f..43e2606 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="special-character">
         <path id="omega" d="M12 6.708c-.794 0-1.368.103-1.894.31-.525.207-.944.496-1.255.867-.31.366-.53.808-.66 1.327a7.232 7.232 0 0 0-.19 1.7c0 .512.06 1 .18 1.46.12.46.31.87.567 1.23.63.862 1.156 1.138 2.012 1.362L11 18H6v-3h.604l.53 1.353.395.053.6.044.75.035.455.01H10l-.09-.895c-.63-.094-.812-.268-1.337-.522-.525-.26-.98-.59-1.365-.99a4.428 4.428 0 0 1-.89-1.4 4.78 4.78 0 0 1-.32-1.778c0-.82.13-1.537.394-2.15a3.97 3.97 0 0 1 1.163-1.54c.507-.407 1.133-.71 1.878-.912.745-.206 1.6-.31 2.565-.31.96 0 1.81.103 2.556.31.75.2 1.38.504 1.887.912.51.407.9.92 1.16 1.54.27.614.404 1.33.404 2.15a4.79 4.79 0 0 1-.32 1.78 4.35 4.35 0 0 1-.9 1.397c-.38.4-.83.732-1.355.99-.526.255-.708.43-1.337.523l-.092.894h.66l.448-.01.75-.034.606-.044.4-.053.534-1.354H18v3h-5l.246-3.04c1.066-.11 1.337-.698 2.002-1.365.263-.36.452-.77.568-1.23.122-.46.183-.947.183-1.46 0-.62-.07-1.186-.198-1.7a3.175 3.175 0 0 0-.66-1.326c-.31-.37-.73-.66-1.255-.867-.525-.206-1.1-.31-1.894-.31"/>
     </g>
index c4ac930..500bbfb 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M19 20H2l3-3V6h17v11c0 1.7-1.3 3-3 3z"/>
 </svg>
index 84fd324..1a9f6c8 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M5 20h17l-3-3V6H2v11c0 1.7 1.3 3 3 3z"/>
 </svg>
index 44f3048..d359edf 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M19 20H2l3-3V6h17v11c0 1.7-1.3 3-3 3z"/>
     <path fill="#fff" d="M13 9h1v7h-1zm-3 3h7v1h-7z"/>
 </svg>
index 61ce8fb..9c21693 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M5 20h17l-3-3V6H2v11c0 1.7 1.3 3 3 3z"/>
     <path d="M11 9h-1v7h1zm3 3H7v1h7z" fill="#fff"/>
 </svg>
index d224d88..090099b 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M20 9v9l2 2H8V9h12zM3 4h12v4H7v7H1l2-2V4z"/>
 </svg>
index d442be9..1845684 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M3 9v9l-2 2h14V9H3zm17-5H8v4h8v7h6l-2-2V4z"/>
 </svg>
index 9e64bcf..ffe5556 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #00AF89 }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #00af89 }</style>
     <path d="M12 7.4l1.7 3.6 4 .5-2.7 2.8.5 3.9-3.5-1.7-3.6 1.7.6-3.9-2.8-2.8 3.9-.5L12 7.4M12 4L9.2 9.6l-6.2.9 4.5 4.4L6.4 21l5.6-3 5.5 3-1-6.2 4.5-4.4-6.3-.9L12 4z"/>
 </svg>
index af06636..c745706 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M12 7.4l1.7 3.6 4 .5-2.7 2.8.5 3.9-3.5-1.7-3.6 1.7.6-3.9-2.8-2.8 3.9-.5L12 7.4M12 4L9.2 9.6l-6.2.9 4.5 4.4L6.4 21l5.6-3 5.5 3-1-6.2 4.5-4.4-6.3-.9L12 4z"/>
 </svg>
index ef7b7c6..8b49792 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M12 6c3.9 0 7 3.1 7 7s-3.1 7-7 7-7-3.1-7-7 3.1-7 7-7m0-1c-4.4 0-8 3.6-8 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm-3 5h6v6H9z"/>
 </svg>
index 60b36a8..6f3ab7c 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="strikethrough-a">
         <path id="strikethrough" d="M6 11h12v1H6v-1z"/>
         <path id="a" d="M12.666 6h-1.372l-4.48 12H8.52l1.493-4h4l1.507 4h1.666l-4.52-12zm-2.28 7l1.617-4.333L13.637 13h-3.25z"/>
index 27a1740..b3361b1 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="strikethrough-s">
         <path id="strikethrough" d="M6 12h12v1H6v-1z"/>
         <path id="s" d="M12.094 6c-1.133 0-2.076.287-2.75.9-.67.613-1 1.49-1 2.52 0 .89.22 1.602.72 2.13.497.528 1.278.91 2.31 1.14l.813.182v-.03c.656.147 1.128.375 1.375.63.252.256.375.607.375 1.11 0 .573-.172.97-.53 1.26-.36.29-.895.45-1.626.45-.47 0-.962-.074-1.462-.24a7.288 7.288 0 0 1-1.562-.75l-.374-.238v2.158l.156.062c.58.237 1.144.417 1.69.54.548.12 1.07.18 1.56.18 1.287 0 2.298-.293 3-.9.71-.605 1.063-1.486 1.063-2.608 0-.943-.256-1.726-.78-2.312-.522-.592-1.306-1-2.345-1.23l-.812-.18c-.714-.148-1.202-.352-1.404-.54-.206-.202-.313-.484-.313-.934 0-.533.162-.9.5-1.17.342-.27.836-.42 1.53-.42.396 0 .82.052 1.25.18.434.128.91.334 1.407.6l.375.18V6.63s-1.19-.383-1.69-.48c-.5-.097-.983-.15-1.467-.15z"/>
index 50db67b..bdb6528 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="strikethrough-y">
         <path id="strikethrough" d="M6 11h12v1H6v-1z"/>
         <path id="a" d="M7 6h1.724l3.288 4.935L15.276 6H17l-4.194 6.285V18h-1.612v-5.715L7 6"/>
index 97aacad..7eaeea5 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M4 9h12v2H4V9zm0 3h8v2H4v-2zm0-7h16v3H4V5zm16 14H4v-3h16v3z"/>
 </svg>
index 9df2e14..f23d8ab 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M20 9H8v2h12V9zm0 3h-8v2h8v-2zm0-7H4v3h16V5zM4 19h16v-3H4v3z"/>
 </svg>
index dd27787..5600c60 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M20 19H4v-2h16v2zM20 15H4v-2h16v2zM20 11H4V9h16v2z"/>
 </svg>
index 41505fb..8f263c0 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M20 11H4V9h16v2zM4 12h8v2H4v-2z"/>
 </svg>
index 1b7c161..f543b9d 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M4 11h16V9H4v2zm16 1h-8v2h8v-2z"/>
 </svg>
index 8adc078..52487c4 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M17 13H4v-3h13v3zm-5 6H4v-3h8v3zM4 7V4h16v3H4z"/>
 </svg>
index 9e87ded..7c36776 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #347BFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #347bff }</style>
     <path d="M17 13H4v-3h13v3zm-5 6H4v-3h8v3zM4 7V4h16v3H4z"/>
 </svg>
index 9c5adaa..f656017 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M7 13h13v-3H7v3zm5 6h8v-3h-8v3zm8-12V4H4v3h16z"/>
 </svg>
index efc27ab..26a9fc5 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #347BFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #347bff }</style>
     <path d="M7 13h13v-3H7v3zm5 6h8v-3h-8v3zm8-12V4H4v3h16z"/>
 </svg>
index b5ef54e..4638e31 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path id="x" d="M14 9l-2.354 3.406L14 16h-1.2L11 13.25 9.2 16H8l2.403-3.662L8 9h1.188l1.857 2.494L12.797 9H14z"/>
     <path d="M18 13l-1 1v3l1 1h-1l-.527-.46L16 18h-1l1-1v-3l-1-1h1l.485.497L17 13z"/>
 </svg>
index e43eac6..76a8659 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path id="x" d="M12 9l2.354 3.406L12 16h1.2l1.8-2.75L16.8 16H18l-2.403-3.662L18 9h-1.188l-1.857 2.494L13.203 9H12z"/>
     <path d="M8 13l1 1v3l-1 1h1l.527-.46L10 18h1l-1-1v-3l1-1h-1l-.485.497L9 13z"/>
 </svg>
index 7f7ef3a..76601ef 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M18.1 5.1c0 .3-.1.6-.3.9l-1.4 1.4-.9-.8 2.2-2.2c.3.1.4.4.4.7zm-.5 5.3h3.2c0 .3-.1.6-.4.9s-.5.4-.8.4h-2v-1.3zm-6.2-5V2.2c.3 0 .6.1.9.4s.4.5.4.8v2h-1.3zm6.4 11.7c-.3 0-.6-.1-.8-.3l-1.4-1.4.8-.8 2.2 2.2c-.2.2-.5.3-.8.3zM6.2 4.9c.3 0 .6.1.8.3l1.4 1.4-.8.9-2.2-2.3c.2-.2.5-.3.8-.3zm5.2 11.7h1.2v3.2c-.3 0-.6-.1-.9-.4s-.4-.5-.4-.8l.1-2zm-7-6.2h2v1.2H3.2c0-.3.1-.6.4-.9s.5-.3.8-.3zM6.2 16l1.4-1.4.8.8-2.2 2.2c-.2-.2-.3-.5-.3-.8s.1-.6.3-.8z"/>
     <circle cx="12" cy="11" r="4"/>
 </svg>
index 7559366..99bed35 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M5.9 5.1c0 .3.1.6.3.9l1.4 1.4.9-.8-2.2-2.2c-.3.1-.4.4-.4.7zm.5 5.3H3.2c0 .3.1.6.4.9.3.3.5.4.8.4h2v-1.3zm6.2-5V2.2c-.3 0-.6.1-.9.4-.3.3-.4.5-.4.8v2h1.3zM6.2 17.1c.3 0 .6-.1.8-.3l1.4-1.4-.8-.8-2.2 2.2c.2.2.5.3.8.3zM17.8 4.9c-.3 0-.6.1-.8.3l-1.4 1.4.8.9 2.2-2.3c-.2-.2-.5-.3-.8-.3zm-5.2 11.7h-1.2v3.2c.3 0 .6-.1.9-.4.3-.3.4-.5.4-.8l-.1-2zm7-6.2h-2v1.2h3.2c0-.3-.1-.6-.4-.9-.3-.3-.5-.3-.8-.3zM17.8 16l-1.4-1.4-.8.8 2.2 2.2c.2-.2.3-.5.3-.8 0-.3-.1-.6-.3-.8z"/>
     <circle cx="12" cy="11" r="4" transform="matrix(-1 0 0 1 24 0)"/>
 </svg>
index 7f95dcf..bd2e11d 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path id="x" d="M14 9l-2.354 3.406L14 16h-1.2L11 13.25 9.2 16H8l2.403-3.662L8 9h1.188l1.857 2.494L12.797 9H14z"/>
     <path d="M18 7l-1 1v3l1 1h-1l-.527-.46L16 12h-1l1-1V8l-1-1h1l.485.497L17 7z"/>
 </svg>
index 468316d..7e0f907 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path id="x" d="M12 9l2.354 3.406L12 16h1.2l1.8-2.75L16.8 16H18l-2.403-3.662L18 9h-1.188l-1.857 2.494L13.203 9H12z"/>
     <path d="M8 7l1 1v3l-1 1h1l.527-.46L10 12h1l-1-1V8l1-1h-1l-.485.497L9 7z"/>
 </svg>
index 3923614..72af30c 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="table-caption">
         <path id="caption" d="M6 6h12v3H6z"/>
         <path id="table" d="M4 10v7h16v-7H4zm1 1h4v2H5v-2zm5 0h4v2h-4v-2zm5 0h4v2h-4v-2zM5 14h4v2H5v-2zm5 0h4v2h-4v-2zm5 0h4v2h-4v-2z"/>
index 1bb2d7e..37449b3 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="table-insert-column-ltr">
         <path d="M13 9h-2v2H9v2h2v2h2v-2h2v-2h-2z" id="plus"/>
         <path d="M5 5h2v14H5z" id="column"/>
index 8489597..7dbd4f6 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="table-insert-column-rtl">
         <path d="M13 9h-2v2H9v2h2v2h2v-2h2v-2h-2z" id="plus"/>
         <path d="M17 5h2v14h-2z" id="column"/>
index d0813a6..fdb668f 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="table-insert-row-after">
         <path d="M13 9h-2v2H9v2h2v2h2v-2h2v-2h-2z" id="plus"/>
         <path d="M5 17h14v2H5z" id="row"/>
index 516078f..38998d4 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="table-insert-row-before">
         <path d="M13 9h-2v2H9v2h2v2h2v-2h2v-2h-2z" id="plus"/>
         <path d="M5 5h14v2H5z" id="row"/>
index 808d8d8..3866463 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="table-insert">
         <path id="table" d="M4 6v11h15V6zm1 3h6v3H5zm7 0h6v3h-6zm-7 4h6v3H5zm7 0h6v3h-6z"/>
     </g>
index 06cb4da..bd571af 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="table-merge-cells">
         <g id="merge-cell-left">
             <path id="cell-border" d="M4 7v9h7v-3l-1 .834V15H5V8h5v1.167L11 10V7z"/>
index 5df96fd..9feb601 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #00AF89 }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #00af89 }</style>
     <path d="M18.748 11.717a1 1 0 0 1 0 1.414l-4.95 4.95a1 1 0 0 1-1.413 0l-6.01-6.01c-.39-.382-.707-1.15-.707-1.7V6c0-.55.45-1 1-1h4.363c.55 0 1.32.318 1.71.707l6.01 6.01zM8.104 7.457a1.477 1.477 0 0 0 0 2.092 1.49 1.49 0 0 0 2.094 0 1.49 1.49 0 0 0 0-2.1 1.484 1.484 0 0 0-2.094 0z" id="tag"/>
 </svg>
index 631c9bc..1058e83 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #D11D13 }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #d11d13 }</style>
     <path d="M18.748 11.717a1 1 0 0 1 0 1.414l-4.95 4.95a1 1 0 0 1-1.413 0l-6.01-6.01c-.39-.382-.707-1.15-.707-1.7V6c0-.55.45-1 1-1h4.363c.55 0 1.32.318 1.71.707l6.01 6.01zM8.104 7.457a1.477 1.477 0 0 0 0 2.092 1.49 1.49 0 0 0 2.094 0 1.49 1.49 0 0 0 0-2.1 1.484 1.484 0 0 0-2.094 0z" id="tag"/>
 </svg>
index 9fc98f7..066801a 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M18.748 11.717a1 1 0 0 1 0 1.414l-4.95 4.95a1 1 0 0 1-1.413 0l-6.01-6.01c-.39-.382-.707-1.15-.707-1.7V6c0-.55.45-1 1-1h4.363c.55 0 1.32.318 1.71.707l6.01 6.01zM8.104 7.457a1.477 1.477 0 0 0 0 2.092 1.49 1.49 0 0 0 2.094 0 1.49 1.49 0 0 0 0-2.1 1.484 1.484 0 0 0-2.094 0z" id="tag"/>
 </svg>
index 24c64c2..1526306 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #347BFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #347bff }</style>
     <path d="M18.748 11.717a1 1 0 0 1 0 1.414l-4.95 4.95a1 1 0 0 1-1.413 0l-6.01-6.01c-.39-.382-.707-1.15-.707-1.7V6c0-.55.45-1 1-1h4.363c.55 0 1.32.318 1.71.707l6.01 6.01zM8.104 7.457a1.477 1.477 0 0 0 0 2.092 1.49 1.49 0 0 0 2.094 0 1.49 1.49 0 0 0 0-2.1 1.484 1.484 0 0 0-2.094 0z" id="tag"/>
 </svg>
index 3cbd445..df57de5 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FF5D00 }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ff5d00 }</style>
     <path d="M18.748 11.717a1 1 0 0 1 0 1.414l-4.95 4.95a1 1 0 0 1-1.413 0l-6.01-6.01c-.39-.382-.707-1.15-.707-1.7V6c0-.55.45-1 1-1h4.363c.55 0 1.32.318 1.71.707l6.01 6.01zM8.104 7.457a1.477 1.477 0 0 0 0 2.092 1.49 1.49 0 0 0 2.094 0 1.49 1.49 0 0 0 0-2.1 1.484 1.484 0 0 0-2.094 0z" id="tag"/>
 </svg>
index 0ed2901..2f1a02a 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="template-add">
         <path id="add" d="M23 7h-4V3h-2v4h-4v2h4v4h2V9h4z"/>
         <path id="template" d="M18 14v4H6c-1.1 0-2-.9-2-2V8h8V7H3v9c0 1.7 1.3 3 3 3h13v-5z"/>
index 3927113..70ce39f 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="template-add">
         <path id="add" d="M1 7h4V3h2v4h4v2H7v4H5V9H1z"/>
         <path id="template" d="M6 14v4h12c1.1 0 2-.9 2-2V8h-8V7h9v9c0 1.7-1.3 3-3 3H5v-5z"/>
index 8328910..fb1a466 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M7 7H5V6h2l.47.5L8 6h2v1H8v10h2v1H8l-.5-.53L7 18H5v-1h2zm6.976 9v-2H11v-4h2.976V8.044L20 12.022z" id="text-dir-ltr"/>
 </svg>
index 2218b3a..867e464 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M17 17h2v1h-2l-.47-.5-.53.5h-2v-1h2V7h-2V6h2l.5.53L17 6h2v1h-2zm-6.976-9v2H13v4h-2.976v1.956L4 11.978z" id="text-dir-rtl"/>
 </svg>
index 0367cfe..9a713d9 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="text-style">
         <path id="a" d="M15.296 18h2.79l-1.14-12h-2.79L6 18h2.79l2.038-3h4.183l.29 3zm-3.11-5L14.5 9.6l.323 3.4H12.19z"/>
         <path id="underline" d="M6 19h12v1H6v-1z"/>
index 7ef05a6..7489b7a 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #D11D13 }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #d11d13 }</style>
     <path d="M6 8c0-1.1.9-2 2-2h2l1-1h2l1 1h2c1.1 0 2 .9 2 2H6zm1 1h10l-1 11H8z"/>
 </svg>
index 65ba012..90c82f2 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M6 8c0-1.1.9-2 2-2h2l1-1h2l1 1h2c1.1 0 2 .9 2 2H6zm1 1h10l-1 11H8z"/>
 </svg>
index 0c4c20a..70b6f83 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M20.5 20.5L5 5 4 6l3 3 1 11h8l.2-1.8 3.3 3.3zM17 9h-6l5.5 5.5zm1-1c0-1.1-.9-2-2-2h-2l-1-1h-2l-1 1H8l2 2h8z"/>
 </svg>
index e27acd9..d5edf14 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M4 20.5L19.5 5l1 1-3 3-1 11h-8l-.2-1.8L5 21.5zM7.5 9h6L8 14.5zm-1-1c0-1.1.9-2 2-2h2l1-1h2l1 1h2l-2 2h-8z"/>
 </svg>
index 7a6c291..385686c 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #D11D13 }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #d11d13 }</style>
     <path d="M12 9V7s0-5-4.5-5S3 7 3 7h2s0-3 2.5-3S10 7 10 7v2H7v7c0 1.7 1.3 3 3 3h10V9z"/>
 </svg>
index 53c2153..3ac59e0 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M12 9V7s0-5-4.5-5S3 7 3 7h2s0-3 2.5-3S10 7 10 7v2H7v7c0 1.7 1.3 3 3 3h10V9z"/>
 </svg>
index f2e301c..0f79916 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #D11D13 }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #d11d13 }</style>
     <path d="M11 9V7s0-5 4.5-5S20 7 20 7h-2s0-3-2.5-3S13 7 13 7v2h3v7c0 1.7-1.3 3-3 3H3V9z"/>
 </svg>
index 0de2e76..d182c6d 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M11 9V7s0-5 4.5-5S20 7 20 7h-2s0-3-2.5-3S13 7 13 7v2h3v7c0 1.7-1.3 3-3 3H3V9z"/>
 </svg>
index 818f5a7..5e98ccb 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #00AF89 }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #00af89 }</style>
     <path d="M21 11l-6-1-3-6-3 6-6 1 4 4-1 6 6-3 6 3-1-6 4-4z"/>
 </svg>
index b66ce2a..73d8054 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M21 11l-6-1-3-6-3 6-6 1 4 4-1 6 6-3 6 3-1-6 4-4z"/>
 </svg>
index 3581c57..79fcbf3 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="underline-a">
         <path id="a" d="M14.424 16H16.5L13.037 6H10.96L7.5 16h2.077l.627-2h3.604l.616 2zm-3.92-3.623L12 7.997l1.51 4.38h-3z"/>
         <path id="underline" d="M7 17h10v1H7v-1z"/>
index f1c82d2..9e7353e 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="underline-u">
         <path id="u" d="M8 6h2v5.96c-.104 1.706.695 2 2 2.04 1.777.062 2.002-.88 2-2.04V6h2v6.123c0 1.28-.338 2.245-1.016 2.898-.672.658-1.666.98-2.98.98-1.32 0-2.32-.32-2.996-.98C8.336 14.37 8 13.41 8 12.13V6"/>
         <path id="underline" d="M7 17h10v1H7v-1z"/>
index b23189f..52223ab 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M12 8l8 10H4z"/>
 </svg>
index 29eca3d..f29501f 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M10 13c0 1.7 1.3 3 3 3V9h3l-4.5-5L7 9h3v4zm7 0v5H7c-.6 0-1-.4-1-1v-4H4v4c0 1.9 1.3 3 3 3h12v-7h-2z"/>
 </svg>
index 1f81996..68b8b1f 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M13 13c0 1.7-1.3 3-3 3V9H7l4.5-5L16 9h-3v4zm-7 0v5h10c.6 0 1-.4 1-1v-4h2v4c0 1.9-1.3 3-3 3H4v-7h2z"/>
 </svg>
index ed1913a..25923e2 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="viewCompact">
         <circle cx="6" cy="6" r="2"/>
         <circle cx="12" cy="6" r="2"/>
index 7c064cc..4471f59 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="viewDetails">
         <circle cx="5.5" cy="8.5" r="2.5"/>
         <path d="M10 6h12v1H10zm0 2h9v1h-9zm0 2h4v1h-4z"/>
index f4838fe..dad4b91 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="viewDetails">
         <circle cx="18.5" cy="8.5" r="2.5"/>
         <path d="M14 6H2v1h12zm0 2H5v1h9zm0 2h-4v1h4z"/>
index ae0d94e..0e7728f 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M0 10v8h2.3c.3.6 1 1 1.7 1h4c1.5 0 2.7-.8 3-2h2c.3 1.2 1.5 2 3 2h4c.7 0 1.4 0 1.7-1H24v-8zm10 6c0 1-.4 2-2 2H4c-.6 0-1-.4-1-1v-3c0-.6.4-1 1-1h5c.6 0 1 .4 1 1zm11 1c0 .6-.4 1-1 1h-4c-1.6 0-2-1-2-2v-2c0-.6.4-1 1-1h5c.6 0 1 .4 1 1z"/>
 </svg>
index 7545aeb..d52e65c 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M13 14h5v1h-5v-1zm0 3h5v-1h-5v1zm0 1h5v1h-5v-1zm-1-5v3l-5 3 1-6-4-3 6-1 2-5s1.9 5 2 5l6 1-4 3h-4z"/>
 </svg>
index 812ee38..f193915 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M11 14H6v1h5v-1zm0 3H6v-1h5v1zm0 1H6v1h5v-1zm1-5v3l5 3-1-6 4-3-6-1-2-5s-1.9 5-2 5l-6 1 4 3h4z"/>
 </svg>
index ec12d0e..131c83d 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="wikiText">
         <path id="opening-bracket-inner" d="M7 19h3v-2H9V7h1V5H7z"/>
         <path id="closing-bracket-inner" d="M17 19h-3v-2h1V7h-1V5h3z"/>
index dc90fba..df03c66 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M15 9l.7-1.8c.9.4 1.8.7 2.4.9l-.6 1.7v.2L15 9zm-4.3-1.9l.8-1.8c1.2.5 2.6 1.1 3 1.4l-.8 1.8-3-1.4zm-5.9-1c-.8 0-1.4.2-2 .6L1.7 5c.9-.6 1.9-.9 3.1-.9v2zm-4.3.7l1.8.8c-.3.7-.3 1.3-.1 1.8l-1.9.7C0 8.9 0 7.8.5 6.8zm4.2 5.4l-1.3 1.5c-1-1-1.7-1.6-2-2l1.5-1.3c.7.8 1.3 1.4 1.8 1.8zm7.3 4.3c0 1.9-1.6 3.5-3.5 3.5S5 18.4 5 16.5 6.6 13 8.5 13s3.5 1.6 3.5 3.5zM24 8l-1-1-1.5 1.5L20 7l-1 1 1.5 1.5L19 11l1 1 1.5-1.5L23 12l1-1-1.5-1.5z"/>
     <circle cx="8" cy="5" r="2"/>
 </svg>
index 09ac9db..9153a1a 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <path d="M9.095 9l-.7-1.8c-.9.4-1.8.7-2.4.9l.6 1.7v.2l2.5-1zm4.3-1.9l-.8-1.8c-1.2.5-2.6 1.1-3 1.4l.8 1.8 3-1.4zm5.9-1c.8 0 1.4.2 2 .6l1.1-1.7c-.9-.6-1.9-.9-3.1-.9v2zm4.3.7l-1.8.8c.3.7.3 1.3.1 1.8l1.9.7c.3-1.2.3-2.3-.2-3.3zm-4.2 5.4l1.3 1.5c1-1 1.7-1.6 2-2l-1.5-1.3c-.7.8-1.3 1.4-1.8 1.8zm-7.3 4.3c0 1.9 1.6 3.5 3.5 3.5s3.5-1.6 3.5-3.5-1.6-3.5-3.5-3.5-3.5 1.6-3.5 3.5zM.095 8l1-1 1.5 1.5 1.5-1.5 1 1-1.5 1.5 1.5 1.5-1 1-1.5-1.5-1.5 1.5-1-1 1.5-1.5z"/>
     <circle cx="8" cy="5" r="2" transform="matrix(-1 0 0 1 24.095 0)"/>
 </svg>
index a1d59d5..699d6b9 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
     <g id="window">
         <path id="title" d="M7 10h10v1H7z"/>
         <path id="frame" d="M16 19H8c-2.206 0-4-1.794-4-4V9c0-2.206 1.794-4 4-4h8c2.206 0 4 1.794 4 4v6c0 2.206-1.794 4-4 4zM8 7c-1.103 0-2 .897-2 2v6c0 1.103.897 2 2 2h8c1.103 0 2-.897 2-2V9c0-1.103-.897-2-2-2H8z"/>
index d59e992..0b692e3 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #ffffff }</style>
     <path d="M6 12A6 6 0 1 1 6 0a6 6 0 0 1 0 12zM5 7h2V2H5zm0 3h2V8H5z" id="alert"/>
 </svg>
index 7567b5d..3f7f447 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #ffffff }</style>
     <g id="down">
         <path id="arrow" d="M1 4h10L6 9 1 4"/>
     </g>
index 63384fa..01ec154 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #ffffff }</style>
     <g id="ltr">
         <path id="arrow" d="M4 1v10l5-5-5-5"/>
     </g>
index a9e9ce3..a12a0e7 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #ffffff }</style>
     <g id="rtl">
         <path id="arrow" d="M8 11V1L3 6l5 5"/>
     </g>
index 2d11d4f..a561ff7 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #ffffff }</style>
     <g id="up">
         <path id="arrow" d="M1 8h10L6 3 1 8"/>
     </g>
index 5b03054..36b5dd7 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #ffffff }</style>
     <g id="clear">
         <path id="circle-with-cross" d="M6 0C2.7 0 0 2.7 0 6s2.7 6 6 6 6-2.7 6-6-2.7-6-6-6zM3.5 2.5L6 5l2.5-2.5 1 1L7 6l2.5 2.5-1 1L6 7 3.5 9.5l-1-1L5 6 2.5 3.5z"/>
     </g>
index dd6ef78..b3a3745 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #ffffff }</style>
     <path d="M5 1h2v10H5zm4.83 1.634l1 1.732-8.66 5-1-1.732zM1.17 4.366l1-1.732 8.66 5-1 1.732z" id="required"/>
 </svg>
index 2ac9647..b16e101 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #ffffff }</style>
     <g id="search">
         <path id="magnifying-glass" d="M10.37 9.474L7.994 7.1l-.17-.1a3.45 3.45 0 0 0 .644-2.01A3.478 3.478 0 1 0 4.99 8.47c.75 0 1.442-.24 2.01-.648l.098.17 2.375 2.373c.19.188.543.142.79-.105s.293-.6.104-.79zm-5.38-2.27a2.21 2.21 0 1 1 2.21-2.21A2.21 2.21 0 0 1 4.99 7.21z"/>
     </g>
index 0d0ded4..ee8ad61 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #FFFFFF }</style>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><style>* { fill: #ffffff }</style>
     <g id="search">
         <path id="magnifying-glass" d="M1.63 9.474L4.006 7.1l.17-.1a3.45 3.45 0 0 1-.644-2.01A3.478 3.478 0 1 1 7.01 8.47 3.43 3.43 0 0 1 5 7.822l-.098.17-2.375 2.373c-.19.188-.543.142-.79-.105s-.293-.6-.104-.79zm5.378-2.27A2.21 2.21 0 1 0 4.8 4.994 2.21 2.21 0 0 0 7.01 7.21z"/>
     </g>
index 5a83258..876a055 100644 (file)
@@ -4,7 +4,7 @@
        "intro": "@import '../../../../src/styles/common';",
        "variants": {
                "invert": {
-                       "color": "#FFFFFF",
+                       "color": "#ffffff",
                        "global": true
                }
        },
index 85c58d7..f4ef540 100644 (file)
@@ -1,20 +1,19 @@
 @import "mediawiki.mixins";
 
-// Table Sorting
+/* Table Sorting */
 
-.client-js table.jquery-tablesorter th.headerSort {
+table.jquery-tablesorter th.headerSort {
        .background-image-svg( 'images/sort_both.svg', 'images/sort_both.png' );
        cursor: pointer;
-       // Keep synchronized with mediawiki.skinning.content styles
        background-repeat: no-repeat;
        background-position: center right;
        padding-right: 21px;
 }
 
-.client-js table.jquery-tablesorter th.headerSortUp {
+table.jquery-tablesorter th.headerSortUp {
        .background-image-svg( 'images/sort_up.svg', 'images/sort_up.png' );
 }
 
-.client-js table.jquery-tablesorter th.headerSortDown {
+table.jquery-tablesorter th.headerSortDown {
        .background-image-svg( 'images/sort_down.svg', 'images/sort_down.png' );
 }
index 1d4d0e9..3b19b35 100644 (file)
@@ -75,6 +75,7 @@
 
                api = new mw.Api();
                postData = {
+                       formatversion: 2,
                        action: 'parse',
                        title: mw.config.get( 'wgPageName' ),
                        summary: $summary.textSelection( 'getContents' ),
@@ -89,8 +90,8 @@
                        }
 
                        diffRequest = api.post( {
+                               formatversion: 2,
                                action: 'query',
-                               indexpageids: true,
                                prop: 'revisions',
                                titles: mw.config.get( 'wgPageName' ),
                                rvdifftotext: $textbox.textSelection( 'getContents' ),
 
                        // Wait for the summary before showing the diff so the page doesn't jump twice
                        $.when( diffRequest, parseRequest ).done( function ( response ) {
-                               var diffHtml,
-                                       query = response[ 0 ].query;
+                               var diffHtml;
                                try {
-                                       diffHtml = query.pages[ query.pageids[ 0 ] ]
-                                               .revisions[ 0 ].diff[ '*' ];
+                                       diffHtml = response[ 0 ].query.pages[ 0 ]
+                                               .revisions[ 0 ].diff.body;
                                        $wikiDiff.find( 'table.diff tbody' ).html( diffHtml );
                                        mw.hook( 'wikipage.diff' ).fire( $wikiDiff.find( 'table.diff' ) );
                                } catch ( e ) {
                                }
 
                                newList = [];
-                               $.each( response.parse.indicators, function ( i, indicator ) {
+                               $.each( response.parse.indicators, function ( name, indicator ) {
                                        newList.push(
                                                $( '<div>' )
                                                        .addClass( 'mw-indicator' )
-                                                       .attr( 'id', mw.util.escapeId( 'mw-indicator-' + indicator.name ) )
-                                                       .html( indicator[ '*' ] )
+                                                       .attr( 'id', mw.util.escapeId( 'mw-indicator-' + name ) )
+                                                       .html( indicator )
                                                        .get( 0 ),
                                                // Add a whitespace between the <div>s because
                                                // they get displayed with display: inline-block
                                        );
                                }
                                if ( response.parse.categorieshtml ) {
-                                       $content = $( $.parseHTML( response.parse.categorieshtml[ '*' ] ) );
+                                       $content = $( $.parseHTML( response.parse.categorieshtml ) );
                                        mw.hook( 'wikipage.categories' ).fire( $content );
                                        $( '.catlinks[data-mw="interface"]' ).replaceWith( $content );
                                }
                                                li = $( '<li>' )
                                                        .append( $( '<a>' )
                                                                .attr( {
-                                                                       href: mw.util.getUrl( template[ '*' ] ),
-                                                                       'class': ( template.exists !== undefined ? '' : 'new' )
+                                                                       href: mw.util.getUrl( template.title ),
+                                                                       'class': ( template.exists ? '' : 'new' )
                                                                } )
-                                                               .text( template[ '*' ] )
+                                                               .text( template.title )
                                                        );
                                                newList.push( li );
                                        } );
                                        $editform.find( '.templatesUsed .mw-editfooter-list' ).detach().empty().append( newList ).appendTo( '.templatesUsed' );
                                }
                                if ( response.parse.limitreporthtml ) {
-                                       $( '.limitreport' ).html( response.parse.limitreporthtml[ '*' ] );
+                                       $( '.limitreport' ).html( response.parse.limitreporthtml );
                                }
                                if ( response.parse.langlinks && mw.config.get( 'skin' ) === 'vector' ) {
                                        newList = [];
                                                        .append( $( '<a>' )
                                                                .attr( {
                                                                        href: langlink.url,
-                                                                       title: langlink[ '*' ] + ' - ' + langlink.langname,
+                                                                       title: langlink.title + ' - ' + langlink.langname,
                                                                        lang: langlink.lang,
                                                                        hreflang: langlink.lang
                                                                } )
                                        $list.detach().empty().append( newList ).prependTo( $parent );
                                }
 
-                               if ( response.parse.text[ '*' ] ) {
+                               if ( response.parse.text ) {
                                        $content = $wikiPreview.children( '.mw-content-ltr,.mw-content-rtl' );
                                        $content
                                                .detach()
-                                               .html( response.parse.text[ '*' ] );
+                                               .html( response.parse.text );
 
                                        mw.hook( 'wikipage.content' ).fire( $content );
 
                                isSubject = ( section === 'new' ),
                                summaryMsg = isSubject ? 'subject-preview' : 'summary-preview',
                                $summaryPreview = $editform.find( '.mw-summary-preview' ).empty();
-                       if ( parse && parse.parsedsummary && parse.parsedsummary[ '*' ] !== '' ) {
+                       if ( parse && parse.parsedsummary ) {
                                $summaryPreview.append(
                                        mw.message( summaryMsg ).parse(),
                                        ' ',
                                        $( '<span>' ).addClass( 'comment' ).html(
                                                // There is no equivalent to rawParams
                                                mw.message( 'parentheses' ).escaped()
-                                                       .replace( '$1', parse.parsedsummary[ '*' ] )
+                                                       .replace( '$1', parse.parsedsummary )
                                        )
                                );
                        }
index 4803a0a..77ecfcb 100644 (file)
@@ -127,11 +127,23 @@ pre, .mw-code {
 }
 
 /* Space between the columns for tocnumber and toctext */
-.tocnumber:after {
-       content: "";
+.tocnumber {
+       padding-left: 0;
        padding-right: 0.5em;
 }
 
+/* @noflip */
+.mw-content-ltr .tocnumber {
+       padding-left: 0;
+       padding-right: 0.5em;
+}
+
+/* @noflip */
+.mw-content-rtl .tocnumber {
+       padding-left: 0.5em;
+       padding-right: 0;
+}
+
 #footer {
        background: white;
        color: black;
index f6c407a..a550cbe 100644 (file)
@@ -55,7 +55,6 @@
 .transition(@value) {
        -webkit-transition: @value; // Safari 3.1-6.0, iOS 3.2-6.1, Android 2.1-4.3
        -moz-transition: @value; // Firefox 4-15
-       -o-transition: @value; // Opera 10.5-12.0
        transition: @value; // Chrome 26+, Firefox 16+, IE 10+, Safari 6.1+, Opera 12.1+, iOS 7+, Android 4.4+
 }
 
index a873cdf..e6f92a5 100644 (file)
@@ -96,11 +96,21 @@ table.toc td {
 }
 
 /* Space between the columns for tocnumber and toctext */
-/* Ignored by IE7 and lower */
-.tocnumber:after {
-       content: "";
-       display: inline-block;
-       width: 0.5em;
+.tocnumber {
+       padding-left: 0;
+       padding-right: 0.5em;
+}
+
+/* @noflip */
+.mw-content-ltr .tocnumber {
+       padding-left: 0;
+       padding-right: 0.5em;
+}
+
+/* @noflip */
+.mw-content-rtl .tocnumber {
+       padding-left: 0.5em;
+       padding-right: 0;
 }
 
 /* Warning */
@@ -241,16 +251,3 @@ div.tright {
 div.tleft {
        margin: .5em 1.4em 1.3em 0;
 }
-
-/* Make space for the jquery.tablesorter icon and display a placeholder if JavaScript is loaded, while
-   tablesorter is still loading and setting up the tables for sorting. This avoids a flash of
-   unstyled content during page load (FOUC). The styles can also be used by WYSIWYG editors. */
-.client-js table.sortable th:not(.unsortable) {
-       background-image: url(images/sort_both_readonly.png);
-       /* @embed */
-       background-image: linear-gradient(transparent, transparent), url(images/sort_both_readonly.svg);
-       /* Keep synchronised with jquery.tablesorter styles */
-       background-repeat: no-repeat;
-       background-position: center right;
-       padding-right: 21px;
-}
diff --git a/resources/src/mediawiki.skinning/images/sort_both_readonly.png b/resources/src/mediawiki.skinning/images/sort_both_readonly.png
deleted file mode 100644 (file)
index bdb09e3..0000000
Binary files a/resources/src/mediawiki.skinning/images/sort_both_readonly.png and /dev/null differ
diff --git a/resources/src/mediawiki.skinning/images/sort_both_readonly.svg b/resources/src/mediawiki.skinning/images/sort_both_readonly.svg
deleted file mode 100644 (file)
index 3b97000..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 21 9" height="9" width="21">
-    <path d="M14.5 5l-4 4-4-4zM14.5 4l-4-4-4 4z" style="fill-opacity: 0.5;"/>
-</svg>
diff --git a/resources/src/mediawiki.special/mediawiki.special.apisandbox.css b/resources/src/mediawiki.special/mediawiki.special.apisandbox.css
new file mode 100644 (file)
index 0000000..e52955f
--- /dev/null
@@ -0,0 +1,74 @@
+.mw-apisandbox-fullscreen {
+       overflow: hidden;
+}
+
+.mw-apisandbox-toolbar {
+       text-align: right;
+       padding: 0.5em;
+}
+
+.mw-apisandbox-popup .oo-ui-popupWidget-body > .oo-ui-widget {
+       vertical-align: middle;
+}
+
+/* So DateTimeInputWidget's calendar popup works... */
+.mw-apisandbox-popup .oo-ui-popupWidget-popup,
+.mw-apisandbox-popup .oo-ui-popupWidget-body {
+       overflow: visible;
+}
+
+.mw-apisandbox-fullscreen #mw-apisandbox-ui {
+       position: fixed;
+       top: 0;
+       left: 0;
+       bottom: 0;
+       right: 0;
+       background: #fff;
+       z-index: 100;
+}
+
+.mw-apisandbox-spacer {
+       display: inline-block;
+       height: 1px;
+       width: 5em;
+}
+
+.mw-apisandbox-optionalWidget {
+       width: 100%;
+}
+
+.mw-apisandbox-optionalWidget.oo-ui-widget-disabled {
+       position: relative;
+       z-index: 0; /* New stacking context to prevent the overlay from leaking out */
+}
+
+.mw-apisandbox-optionalWidget-overlay {
+       position: absolute;
+       left: 0;
+       right: 0;
+       top: 0;
+       bottom: 0;
+       z-index: 2;
+       cursor: pointer;
+}
+
+.mw-apisandbox-optionalWidget-fields {
+       display: table;
+       width: 100%;
+}
+
+.mw-apisandbox-optionalWidget-widget,
+.mw-apisandbox-optionalWidget-checkbox {
+       display: table-cell;
+       vertical-align: middle;
+}
+
+.mw-apisandbox-optionalWidget-checkbox {
+       width: 1%; /* Will be expanded by content */
+       white-space: nowrap;
+       padding-left: 0.5em;
+}
+
+.oo-ui-textInputWidget.oo-ui-widget-enabled > .oo-ui-indicatorElement-indicator.mw-apisandbox-clickable-indicator {
+       cursor: pointer;
+}
diff --git a/resources/src/mediawiki.special/mediawiki.special.apisandbox.js b/resources/src/mediawiki.special/mediawiki.special.apisandbox.js
new file mode 100644 (file)
index 0000000..10664fa
--- /dev/null
@@ -0,0 +1,1659 @@
+/*global OO */
+( function ( $, mw, OO ) {
+       'use strict';
+       var ApiSandbox, Util, WidgetMethods, Validators,
+               $content, panel, booklet, oldhash, windowManager, fullscreenButton,
+               api = new mw.Api(),
+               bookletPages = [],
+               availableFormats = {},
+               resultPage = null,
+               suppressErrors = true,
+               updatingBooklet = false,
+               pages = {},
+               moduleInfoCache = {};
+
+       WidgetMethods = {
+               textInputWidget: {
+                       getApiValue: function () {
+                               return this.getValue();
+                       },
+                       setApiValue: function ( v ) {
+                               if ( v === undefined ) {
+                                       v = this.paramInfo[ 'default' ];
+                               }
+                               this.setValue( v );
+                       },
+                       apiCheckValid: function () {
+                               var that = this;
+                               return this.isValid().done( function ( ok ) {
+                                       ok = ok || suppressErrors;
+                                       that.setIcon( ok ? null : 'alert' );
+                                       that.setIconTitle( ok ? '' : mw.message( 'apisandbox-alert-field' ).plain() );
+                               } );
+                       }
+               },
+
+               dateTimeInputWidget: {
+                       isValid: function () {
+                               var ok = !Util.apiBool( this.paramInfo.required ) || this.getApiValue() !== '';
+                               return $.Deferred().resolve( ok ).promise();
+                       }
+               },
+
+               tokenWidget: {
+                       alertTokenError: function ( code, error ) {
+                               windowManager.openWindow( 'errorAlert', {
+                                       title: mw.message(
+                                               'apisandbox-results-fixtoken-fail', this.paramInfo.tokentype
+                                       ).parse(),
+                                       message: error,
+                                       actions: [
+                                               {
+                                                       action: 'accept',
+                                                       label: OO.ui.msg( 'ooui-dialog-process-dismiss' ),
+                                                       flags: 'primary'
+                                               }
+                                       ]
+                               } );
+                       },
+                       fetchToken: function () {
+                               this.pushPending();
+                               return api.getToken( this.paramInfo.tokentype )
+                                       .done( this.setApiValue.bind( this ) )
+                                       .fail( this.alertTokenError.bind( this ) )
+                                       .always( this.popPending.bind( this ) );
+                       },
+                       setApiValue: function ( v ) {
+                               WidgetMethods.textInputWidget.setApiValue.call( this, v );
+                               if ( v === '123ABC' ) {
+                                       this.fetchToken();
+                               }
+                       }
+               },
+
+               passwordWidget: {
+                       getApiValueForDisplay: function () {
+                               return '';
+                       }
+               },
+
+               toggleSwitchWidget: {
+                       getApiValue: function () {
+                               return this.getValue() ? 1 : undefined;
+                       },
+                       setApiValue: function ( v ) {
+                               this.setValue( Util.apiBool( v ) );
+                       },
+                       apiCheckValid: function () {
+                               return $.Deferred().resolve( true ).promise();
+                       }
+               },
+
+               dropdownWidget: {
+                       getApiValue: function () {
+                               var item = this.getMenu().getSelectedItem();
+                               return item === null ? undefined : item.getData();
+                       },
+                       setApiValue: function ( v ) {
+                               var menu = this.getMenu();
+
+                               if ( v === undefined ) {
+                                       v = this.paramInfo[ 'default' ];
+                               }
+                               if ( v === undefined ) {
+                                       menu.selectItem();
+                               } else {
+                                       menu.selectItemByData( String( v ) );
+                               }
+                       },
+                       apiCheckValid: function () {
+                               var ok = this.getApiValue() !== undefined || suppressErrors;
+                               this.setIcon( ok ? null : 'alert' );
+                               this.setIconTitle( ok ? '' : mw.message( 'apisandbox-alert-field' ).plain() );
+                               return $.Deferred().resolve( ok ).promise();
+                       }
+               },
+
+               capsuleWidget: {
+                       getApiValue: function () {
+                               return this.getItemsData().join( '|' );
+                       },
+                       setApiValue: function ( v ) {
+                               this.setItemsFromData( v === undefined || v === '' ? [] : String( v ).split( '|' ) );
+                       },
+                       apiCheckValid: function () {
+                               var ok = this.getApiValue() !== undefined || suppressErrors;
+                               this.setIcon( ok ? null : 'alert' );
+                               this.setIconTitle( ok ? '' : mw.message( 'apisandbox-alert-field' ).plain() );
+                               return $.Deferred().resolve( ok ).promise();
+                       }
+               },
+
+               optionalWidget: {
+                       getApiValue: function () {
+                               return this.isDisabled() ? undefined : this.widget.getApiValue();
+                       },
+                       setApiValue: function ( v ) {
+                               this.setDisabled( v === undefined );
+                               this.widget.setApiValue( v );
+                       },
+                       apiCheckValid: function () {
+                               if ( this.isDisabled() ) {
+                                       return $.Deferred().resolve( true ).promise();
+                               } else {
+                                       return this.widget.apiCheckValid();
+                               }
+                       }
+               },
+
+               submoduleWidget: {
+                       single: function () {
+                               var v = this.isDisabled() ? this.paramInfo[ 'default' ] : this.getApiValue();
+                               return v === undefined ? [] : [ { value: v, path: this.paramInfo.submodules[ v ] } ];
+                       },
+                       multi: function () {
+                               var map = this.paramInfo.submodules,
+                                       v = this.isDisabled() ? this.paramInfo[ 'default' ] : this.getApiValue();
+                               return v === undefined || v === '' ? [] : $.map( String( v ).split( '|' ), function ( v ) {
+                                       return { value: v, path: map[ v ] };
+                               } );
+                       }
+               },
+
+               uploadWidget: {
+                       getApiValueForDisplay: function () {
+                               return '...';
+                       },
+                       getApiValue: function () {
+                               return this.getValue();
+                       },
+                       setApiValue: function () {
+                               // Can't, sorry.
+                       },
+                       apiCheckValid: function () {
+                               var ok = this.getValue() !== null || suppressErrors;
+                               this.setIcon( ok ? null : 'alert' );
+                               this.setIconTitle( ok ? '' : mw.message( 'apisandbox-alert-field' ).plain() );
+                               return $.Deferred().resolve( ok ).promise();
+                       }
+               }
+       };
+
+       Validators = {
+               generic: function () {
+                       return !Util.apiBool( this.paramInfo.required ) || this.getApiValue() !== '';
+               }
+       };
+
+       /**
+        * @class mw.special.ApiSandbox.Utils
+        * @private
+        */
+       Util = {
+               /**
+                * Fetch API module info
+                *
+                * @param {string} module Module to fetch data for
+                * @return {jQuery.Promise}
+                */
+               fetchModuleInfo: function ( module ) {
+                       var apiPromise,
+                               deferred = $.Deferred();
+
+                       if ( moduleInfoCache.hasOwnProperty( module ) ) {
+                               return deferred
+                                       .resolve( moduleInfoCache[ module ] )
+                                       .promise( { abort: function () {} } );
+                       } else {
+                               apiPromise = api.post( {
+                                       action: 'paraminfo',
+                                       modules: module,
+                                       helpformat: 'html',
+                                       uselang: mw.config.get( 'wgUserLanguage' )
+                               } ).done( function ( data ) {
+                                       var info;
+
+                                       if ( data.warnings && data.warnings.paraminfo ) {
+                                               deferred.reject( '???', data.warnings.paraminfo[ '*' ] );
+                                               return;
+                                       }
+
+                                       info = data.paraminfo.modules;
+                                       if ( !info || info.length !== 1 || info[ 0 ].path !== module ) {
+                                               deferred.reject( '???', 'No module data returned' );
+                                               return;
+                                       }
+
+                                       moduleInfoCache[ module ] = info[ 0 ];
+                                       deferred.resolve( info[ 0 ] );
+                               } ).fail( function ( code, details ) {
+                                       if ( code === 'http' ) {
+                                               details = 'HTTP error: ' + details.exception;
+                                       } else if ( details.error ) {
+                                               details = details.error.info;
+                                       }
+                                       deferred.reject( code, details );
+                               } );
+                               return deferred
+                                       .promise( { abort: apiPromise.abort } );
+                       }
+               },
+
+               /**
+                * Mark all currently-in-use tokens as bad
+                */
+               markTokensBad: function () {
+                       var page, subpages, i,
+                               checkPages = [ pages.main ];
+
+                       while ( checkPages.length ) {
+                               page = checkPages.shift();
+
+                               if ( page.tokenWidget ) {
+                                       api.badToken( page.tokenWidget.paramInfo.tokentype );
+                               }
+
+                               subpages = page.getSubpages();
+                               for ( i = 0; i < subpages.length; i++ ) {
+                                       if ( pages.hasOwnProperty( subpages[ i ].key ) ) {
+                                               checkPages.push( pages[ subpages[ i ].key ] );
+                                       }
+                               }
+                       }
+               },
+
+               /**
+                * Test an API boolean
+                *
+                * @param {Mixed} value
+                * @return {boolean}
+                */
+               apiBool: function ( value ) {
+                       return value !== undefined && value !== false;
+               },
+
+               /**
+                * Create a widget for a parameter.
+                *
+                * @param {Object} pi Parameter info from API
+                * @param {Object} opts Additional options
+                * @return {OO.ui.Widget}
+                */
+               createWidgetForParameter: function ( pi, opts ) {
+                       var widget, innerWidget, finalWidget, items, $button, $content, func,
+                               multiMode = 'none';
+
+                       opts = opts || {};
+
+                       switch ( pi.type ) {
+                               case 'boolean':
+                                       widget = new OO.ui.ToggleSwitchWidget();
+                                       widget.paramInfo = pi;
+                                       $.extend( widget, WidgetMethods.toggleSwitchWidget );
+                                       pi.required = true; // Avoid wrapping in the non-required widget
+                                       break;
+
+                               case 'string':
+                               case 'user':
+                                       if ( pi.tokentype ) {
+                                               widget = new TextInputWithIndicatorWidget( {
+                                                       input: {
+                                                               indicator: 'previous',
+                                                               indicatorTitle: mw.message( 'apisandbox-fetch-token' ).text(),
+                                                               required: Util.apiBool( pi.required )
+                                                       }
+                                               } );
+                                       } else if ( Util.apiBool( pi.multi ) ) {
+                                               widget = new OO.ui.CapsuleMultiSelectWidget( {
+                                                       allowArbitrary: true
+                                               } );
+                                               widget.paramInfo = pi;
+                                               $.extend( widget, WidgetMethods.capsuleWidget );
+                                       } else {
+                                               widget = new OO.ui.TextInputWidget( {
+                                                       required: Util.apiBool( pi.required )
+                                               } );
+                                       }
+                                       if ( !Util.apiBool( pi.multi ) ) {
+                                               widget.paramInfo = pi;
+                                               $.extend( widget, WidgetMethods.textInputWidget );
+                                               widget.setValidation( Validators.generic );
+                                       }
+                                       if ( pi.tokentype ) {
+                                               $.extend( widget, WidgetMethods.tokenWidget );
+                                               widget.input.paramInfo = pi;
+                                               $.extend( widget.input, WidgetMethods.textInputWidget );
+                                               $.extend( widget.input, WidgetMethods.tokenWidget );
+                                               widget.on( 'indicator', widget.fetchToken, [], widget );
+                                       }
+                                       break;
+
+                               case 'text':
+                                       widget = new OO.ui.TextInputWidget( {
+                                               multiline: true,
+                                               required: Util.apiBool( pi.required )
+                                       } );
+                                       widget.paramInfo = pi;
+                                       $.extend( widget, WidgetMethods.textInputWidget );
+                                       widget.setValidation( Validators.generic );
+                                       break;
+
+                               case 'password':
+                                       widget = new OO.ui.TextInputWidget( {
+                                               type: 'password',
+                                               required: Util.apiBool( pi.required )
+                                       } );
+                                       widget.paramInfo = pi;
+                                       $.extend( widget, WidgetMethods.textInputWidget );
+                                       $.extend( widget, WidgetMethods.passwordWidget );
+                                       widget.setValidation( Validators.generic );
+                                       multiMode = 'enter';
+                                       break;
+
+                               case 'integer':
+                                       widget = new OO.ui.NumberInputWidget( {
+                                               required: Util.apiBool( pi.required ),
+                                               isInteger: true
+                                       } );
+                                       widget.setIcon = widget.input.setIcon.bind( widget.input );
+                                       widget.setIconTitle = widget.input.setIconTitle.bind( widget.input );
+                                       widget.isValid = widget.input.isValid.bind( widget.input );
+                                       widget.paramInfo = pi;
+                                       $.extend( widget, WidgetMethods.textInputWidget );
+                                       if ( Util.apiBool( pi.enforcerange ) ) {
+                                               widget.setRange( pi.min || -Infinity, pi.max || Infinity );
+                                       }
+                                       multiMode = 'enter';
+                                       break;
+
+                               case 'limit':
+                                       widget = new OO.ui.NumberInputWidget( {
+                                               required: Util.apiBool( pi.required ),
+                                               isInteger: true
+                                       } );
+                                       widget.setIcon = widget.input.setIcon.bind( widget.input );
+                                       widget.setIconTitle = widget.input.setIconTitle.bind( widget.input );
+                                       widget.isValid = widget.input.isValid.bind( widget.input );
+                                       widget.input.setValidation( function ( value ) {
+                                               return value === 'max' || widget.validateNumber( value );
+                                       } );
+                                       widget.paramInfo = pi;
+                                       $.extend( widget, WidgetMethods.textInputWidget );
+                                       widget.setRange( pi.min || 0, mw.config.get( 'apihighlimits' ) ? pi.highmax : pi.max );
+                                       multiMode = 'enter';
+                                       break;
+
+                               case 'timestamp':
+                                       widget = new mw.widgets.datetime.DateTimeInputWidget( {
+                                               formatter: {
+                                                       format: '${year|0}-${month|0}-${day|0}T${hour|0}:${minute|0}:${second|0}${zone|short}'
+                                               },
+                                               required: Util.apiBool( pi.required ),
+                                               clearable: false
+                                       } );
+                                       widget.paramInfo = pi;
+                                       $.extend( widget, WidgetMethods.textInputWidget );
+                                       $.extend( widget, WidgetMethods.dateTimeInputWidget );
+                                       multiMode = 'indicator';
+                                       break;
+
+                               case 'upload':
+                                       widget = new OO.ui.SelectFileWidget();
+                                       widget.paramInfo = pi;
+                                       $.extend( widget, WidgetMethods.uploadWidget );
+                                       break;
+
+                               case 'namespace':
+                                       items = $.map( mw.config.get( 'wgFormattedNamespaces' ), function ( name, ns ) {
+                                               if ( ns === '0' ) {
+                                                       name = mw.message( 'blanknamespace' ).text();
+                                               }
+                                               return new OO.ui.MenuOptionWidget( { data: ns, label: name } );
+                                       } ).sort( function ( a, b ) {
+                                               return a.data - b.data;
+                                       } );
+                                       if ( Util.apiBool( pi.multi ) ) {
+                                               widget = new OO.ui.CapsuleMultiSelectWidget( {
+                                                       menu: { items: items }
+                                               } );
+                                               widget.paramInfo = pi;
+                                               $.extend( widget, WidgetMethods.capsuleWidget );
+                                       } else {
+                                               widget = new OO.ui.DropdownWidget( {
+                                                       menu: { items: items }
+                                               } );
+                                               widget.paramInfo = pi;
+                                               $.extend( widget, WidgetMethods.dropdownWidget );
+                                       }
+                                       break;
+
+                               default:
+                                       if ( !$.isArray( pi.type ) ) {
+                                               throw new Error( 'Unknown parameter type ' + pi.type );
+                                       }
+
+                                       items = $.map( pi.type, function ( v ) {
+                                               return new OO.ui.MenuOptionWidget( { data: String( v ), label: String( v ) } );
+                                       } );
+                                       if ( Util.apiBool( pi.multi ) ) {
+                                               widget = new OO.ui.CapsuleMultiSelectWidget( {
+                                                       menu: { items: items }
+                                               } );
+                                               widget.paramInfo = pi;
+                                               $.extend( widget, WidgetMethods.capsuleWidget );
+                                               if ( Util.apiBool( pi.submodules ) ) {
+                                                       widget.getSubmodules = WidgetMethods.submoduleWidget.multi;
+                                                       widget.on( 'change', ApiSandbox.updateUI );
+                                               }
+                                       } else {
+                                               widget = new OO.ui.DropdownWidget( {
+                                                       menu: { items: items }
+                                               } );
+                                               widget.paramInfo = pi;
+                                               $.extend( widget, WidgetMethods.dropdownWidget );
+                                               if ( Util.apiBool( pi.submodules ) ) {
+                                                       widget.getSubmodules = WidgetMethods.submoduleWidget.single;
+                                                       widget.getMenu().on( 'choose', ApiSandbox.updateUI );
+                                               }
+                                       }
+
+                                       break;
+                       }
+
+                       if ( Util.apiBool( pi.multi ) && multiMode !== 'none' ) {
+                               innerWidget = widget;
+                               switch ( multiMode ) {
+                                       case 'enter':
+                                               $content = innerWidget.$element;
+                                               break;
+
+                                       case 'indicator':
+                                               $button = innerWidget.$indicator;
+                                               $button.css( 'cursor', 'pointer' );
+                                               $button.attr( 'tabindex', 0 );
+                                               $button.parent().append( $button );
+                                               innerWidget.setIndicator( 'next' );
+                                               $content = innerWidget.$element;
+                                               break;
+
+                                       default:
+                                               throw new Error( 'Unknown multiMode "' + multiMode + '"' );
+                               }
+
+                               widget = new OO.ui.CapsuleMultiSelectWidget( {
+                                       allowArbitrary: true,
+                                       popup: {
+                                               classes: [ 'mw-apisandbox-popup' ],
+                                               $content: $content
+                                       }
+                               } );
+                               widget.paramInfo = pi;
+                               $.extend( widget, WidgetMethods.capsuleWidget );
+
+                               func = function () {
+                                       if ( !innerWidget.isDisabled() ) {
+                                               innerWidget.apiCheckValid().done( function ( ok ) {
+                                                       if ( ok ) {
+                                                               widget.addItemsFromData( [ innerWidget.getApiValue() ] );
+                                                               innerWidget.setApiValue( undefined );
+                                                       }
+                                               } );
+                                               return false;
+                                       }
+                               };
+                               switch ( multiMode ) {
+                                       case 'enter':
+                                               innerWidget.connect( null, { enter: func } );
+                                               break;
+
+                                       case 'indicator':
+                                               $button.on( {
+                                                       click: func,
+                                                       keypress: function ( e ) {
+                                                               if ( e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER ) {
+                                                                       func();
+                                                               }
+                                                       }
+                                               } );
+                                               break;
+                               }
+                       }
+
+                       if ( Util.apiBool( pi.required ) || opts.nooptional ) {
+                               finalWidget = widget;
+                       } else {
+                               finalWidget = new OptionalWidget( widget );
+                               finalWidget.paramInfo = pi;
+                               $.extend( finalWidget, WidgetMethods.optionalWidget );
+                               if ( widget.getSubmodules ) {
+                                       finalWidget.getSubmodules = widget.getSubmodules.bind( widget );
+                                       finalWidget.on( 'disable', function () { setTimeout( ApiSandbox.updateUI ); } );
+                               }
+                               finalWidget.setDisabled( true );
+                       }
+
+                       widget.setApiValue( pi[ 'default' ] );
+
+                       return finalWidget;
+               },
+
+               /**
+                * Parse an HTML string, adding target="_blank" to any links
+                *
+                * @param {string} html HTML to parse
+                * @return {jQuery}
+                */
+               parseHTML: function ( html ) {
+                       var $ret = $( $.parseHTML( html ) );
+                       $ret.filter( 'a' ).add( $ret.find( 'a' ) )
+                               .filter( '[href]:not([target])' )
+                               .attr( 'target', '_blank' );
+                       return $ret;
+               }
+       };
+
+       /**
+       * Interface to ApiSandbox UI
+       *
+       * @class mw.special.ApiSandbox
+       */
+       mw.special.ApiSandbox = ApiSandbox = {
+               /**
+                * Initialize the UI
+                *
+                * Automatically called on $.ready()
+                */
+               init: function () {
+                       var $toolbar;
+
+                       $content = $( '#mw-apisandbox' );
+
+                       windowManager = new OO.ui.WindowManager();
+                       $( 'body' ).append( windowManager.$element );
+                       windowManager.addWindows( {
+                               errorAlert: new OO.ui.MessageDialog()
+                       } );
+
+                       fullscreenButton = new OO.ui.ButtonWidget( {
+                               label: mw.message( 'apisandbox-fullscreen' ).text(),
+                               title: mw.message( 'apisandbox-fullscreen-tooltip' ).text()
+                       } ).on( 'click', ApiSandbox.toggleFullscreen );
+
+                       $toolbar = $( '<div>' )
+                               .addClass( 'mw-apisandbox-toolbar' )
+                               .append(
+                                       fullscreenButton.$element,
+                                       new OO.ui.ButtonWidget( {
+                                               label: mw.message( 'apisandbox-submit' ).text(),
+                                               flags: [ 'primary', 'constructive' ]
+                                       } ).on( 'click', ApiSandbox.sendRequest ).$element,
+                                       new OO.ui.ButtonWidget( {
+                                               label: mw.message( 'apisandbox-reset' ).text(),
+                                               flags: 'destructive'
+                                       } ).on( 'click', ApiSandbox.resetUI ).$element
+                               );
+
+                       booklet = new OO.ui.BookletLayout( {
+                               outlined: true,
+                               autoFocus: false
+                       } );
+
+                       panel = new OO.ui.PanelLayout( {
+                               classes: [ 'mw-apisandbox-container' ],
+                               content: [ booklet ],
+                               expanded: false,
+                               framed: true
+                       } );
+
+                       pages.main = new ApiSandbox.PageLayout( { key: 'main', path: 'main' } );
+
+                       // Parse the current hash string
+                       if ( !ApiSandbox.loadFromHash() ) {
+                               ApiSandbox.updateUI();
+                       }
+
+                       // If the hashchange event exists, use it. Otherwise, fake it.
+                       // And, of course, IE has to be dumb.
+                       if ( 'onhashchange' in window &&
+                               ( document.documentMode === undefined || document.documentMode >= 8 )
+                       ) {
+                               $( window ).on( 'hashchange', ApiSandbox.loadFromHash );
+                       } else {
+                               setInterval( function () {
+                                       if ( oldhash !== location.hash ) {
+                                               ApiSandbox.loadFromHash();
+                                       }
+                               }, 1000 );
+                       }
+
+                       $content
+                               .empty()
+                               .append( $( '<p>' ).append( mw.message( 'apisandbox-intro' ).parse() ) )
+                               .append(
+                                       $( '<div>', { id: 'mw-apisandbox-ui' } )
+                                               .append( $toolbar )
+                                               .append( panel.$element )
+                               );
+
+                       $( window ).on( 'resize', ApiSandbox.resizePanel );
+
+                       ApiSandbox.resizePanel();
+               },
+
+               /**
+                * Toggle "fullscreen" mode
+                */
+               toggleFullscreen: function () {
+                       var $body = $( document.body );
+
+                       $body.toggleClass( 'mw-apisandbox-fullscreen' );
+                       if ( $body.hasClass( 'mw-apisandbox-fullscreen' ) ) {
+                               fullscreenButton.setLabel( mw.message( 'apisandbox-unfullscreen' ).text() );
+                               fullscreenButton.setTitle( mw.message( 'apisandbox-unfullscreen-tooltip' ).text() );
+                               $body.append( $( '#mw-apisandbox-ui' ) );
+                       } else {
+                               fullscreenButton.setLabel( mw.message( 'apisandbox-fullscreen' ).text() );
+                               fullscreenButton.setTitle( mw.message( 'apisandbox-fullscreen-tooltip' ).text() );
+                               $content.append( $( '#mw-apisandbox-ui' ) );
+                       }
+                       ApiSandbox.resizePanel();
+               },
+
+               /**
+                * Set the height of the panel based on the current viewport.
+                */
+               resizePanel: function () {
+                       var height = $( window ).height(),
+                               contentTop = $content.offset().top;
+
+                       if ( $( document.body ).hasClass( 'mw-apisandbox-fullscreen' ) ) {
+                               height -= panel.$element.offset().top - $( '#mw-apisandbox-ui' ).offset().top;
+                               panel.$element.height( height - 1 );
+                       } else {
+                               // Subtract the height of the intro text
+                               height -= panel.$element.offset().top - contentTop;
+
+                               panel.$element.height( height - 10 );
+                               $( window ).scrollTop( contentTop - 5 );
+                       }
+               },
+
+               /**
+                * Update the current query when the page hash changes
+                */
+               loadFromHash: function () {
+                       var params, m, re,
+                               hash = location.hash;
+
+                       if ( oldhash === hash ) {
+                               return false;
+                       }
+                       oldhash = hash;
+                       if ( hash === '' ) {
+                               return false;
+                       }
+
+                       // I'm surprised this doesn't seem to exist in jQuery or mw.util.
+                       params = {};
+                       hash = hash.replace( '+', '%20' );
+                       re = /([^&=#]+)=?([^&#]*)/g;
+                       while ( ( m = re.exec( hash ) ) ) {
+                               params[ decodeURIComponent( m[ 1 ] ) ] = decodeURIComponent( m[ 2 ] );
+                       }
+
+                       ApiSandbox.updateUI( params );
+                       return true;
+               },
+
+               /**
+                * Update the pages in the booklet
+                *
+                * @param {Object} [params] Optional query parameters to load
+                */
+               updateUI: function ( params ) {
+                       var i, page, subpages, j, removePages,
+                               addPages = [];
+
+                       if ( !$.isPlainObject( params ) ) {
+                               params = undefined;
+                       }
+
+                       if ( updatingBooklet ) {
+                               return;
+                       }
+                       updatingBooklet = true;
+                       try {
+                               if ( params !== undefined ) {
+                                       pages.main.loadQueryParams( params );
+                               }
+                               addPages.push( pages.main );
+                               if ( resultPage !== null ) {
+                                       addPages.push( resultPage );
+                               }
+                               pages.main.apiCheckValid();
+
+                               i = 0;
+                               while ( addPages.length ) {
+                                       page = addPages.shift();
+                                       if ( bookletPages[ i ] !== page ) {
+                                               for ( j = i; j < bookletPages.length; j++ ) {
+                                                       if ( bookletPages[ j ].getName() === page.getName() ) {
+                                                               bookletPages.splice( j, 1 );
+                                                       }
+                                               }
+                                               bookletPages.splice( i, 0, page );
+                                               booklet.addPages( [ page ], i );
+                                       }
+                                       i++;
+
+                                       if ( page.getSubpages ) {
+                                               subpages = page.getSubpages();
+                                               for ( j = 0; j < subpages.length; j++ ) {
+                                                       if ( !pages.hasOwnProperty( subpages[ j ].key ) ) {
+                                                               subpages[ j ].indentLevel = page.indentLevel + 1;
+                                                               pages[ subpages[ j ].key ] = new ApiSandbox.PageLayout( subpages[ j ] );
+                                                       }
+                                                       if ( params !== undefined ) {
+                                                               pages[ subpages[ j ].key ].loadQueryParams( params );
+                                                       }
+                                                       addPages.splice( j, 0, pages[ subpages[ j ].key ] );
+                                                       pages[ subpages[ j ].key ].apiCheckValid();
+                                               }
+                                       }
+                               }
+
+                               if ( bookletPages.length > i ) {
+                                       removePages = bookletPages.splice( i, bookletPages.length - i );
+                                       booklet.removePages( removePages );
+                               }
+
+                               if ( !booklet.getCurrentPageName() ) {
+                                       booklet.selectFirstSelectablePage();
+                               }
+                       } finally {
+                               updatingBooklet = false;
+                       }
+               },
+
+               /**
+                * Reset button handler
+                */
+               resetUI: function () {
+                       suppressErrors = true;
+                       pages = {
+                               main: new ApiSandbox.PageLayout( { key: 'main', path: 'main' } )
+                       };
+                       resultPage = null;
+                       ApiSandbox.updateUI();
+               },
+
+               /**
+                * Submit button handler
+                */
+               sendRequest: function () {
+                       var page, subpages, i, query, $result,
+                               progress, $progressText, progressLoading,
+                               deferreds = [],
+                               params = {},
+                               displayParams = {},
+                               checkPages = [ pages.main ];
+
+                       suppressErrors = false;
+
+                       while ( checkPages.length ) {
+                               page = checkPages.shift();
+                               deferreds.push( page.apiCheckValid() );
+                               page.getQueryParams( params, displayParams );
+                               subpages = page.getSubpages();
+                               for ( i = 0; i < subpages.length; i++ ) {
+                                       if ( pages.hasOwnProperty( subpages[ i ].key ) ) {
+                                               checkPages.push( pages[ subpages[ i ].key ] );
+                                       }
+                               }
+                       }
+
+                       $.when.apply( $, deferreds ).done( function () {
+                               if ( $.inArray( false, arguments ) !== -1 ) {
+                                       windowManager.openWindow( 'errorAlert', {
+                                               title: mw.message( 'apisandbox-submit-invalid-fields-title' ).parse(),
+                                               message: mw.message( 'apisandbox-submit-invalid-fields-message' ).parse(),
+                                               actions: [
+                                                       {
+                                                               action: 'accept',
+                                                               label: OO.ui.msg( 'ooui-dialog-process-dismiss' ),
+                                                               flags: 'primary'
+                                                       }
+                                               ]
+                                       } );
+                                       return;
+                               }
+
+                               query = $.param( displayParams );
+
+                               // Force a 'fm' format with wrappedhtml=1, if available
+                               if ( params.format !== undefined ) {
+                                       if ( availableFormats.hasOwnProperty( params.format + 'fm' ) ) {
+                                               params.format = params.format + 'fm';
+                                       }
+                                       if ( params.format.substr( -2 ) === 'fm' ) {
+                                               params.wrappedhtml = 1;
+                                       }
+                               }
+
+                               progressLoading = false;
+                               $progressText = $( '<span>' ).text( mw.message( 'apisandbox-sending-request' ).text() );
+                               progress = new OO.ui.ProgressBarWidget( {
+                                       progress: false,
+                                       $content: $progressText
+                               } );
+
+                               $result = $( '<div>' )
+                                       .append( progress.$element );
+
+                               resultPage = page = new OO.ui.PageLayout( '|results|' );
+                               page.setupOutlineItem = function () {
+                                       this.outlineItem.setLabel( mw.message( 'apisandbox-results' ).text() );
+                               };
+                               page.$element.empty()
+                                       .append(
+                                               new OO.ui.FieldLayout(
+                                                       new OO.ui.TextInputWidget( {
+                                                               readOnly: true,
+                                                               value: mw.util.wikiScript( 'api' ) + '?' + query
+                                                       } ), {
+                                                               label: mw.message( 'apisandbox-request-url-label' ).parse()
+                                                       }
+                                               ).$element,
+                                               $result
+                                       );
+                               ApiSandbox.updateUI();
+                               booklet.setPage( '|results|' );
+
+                               location.href = oldhash = '#' + query;
+
+                               api.post( params, {
+                                       contentType: 'multipart/form-data',
+                                       dataType: 'text',
+                                       xhr: function () {
+                                               var xhr = new window.XMLHttpRequest();
+                                               xhr.upload.addEventListener( 'progress', function ( e ) {
+                                                       if ( !progressLoading ) {
+                                                               if ( e.lengthComputable ) {
+                                                                       progress.setProgress( e.loaded * 100 / e.total );
+                                                               } else {
+                                                                       progress.setProgress( false );
+                                                               }
+                                                       }
+                                               } );
+                                               xhr.addEventListener( 'progress', function ( e ) {
+                                                       if ( !progressLoading ) {
+                                                               progressLoading = true;
+                                                               $progressText.text( mw.message( 'apisandbox-loading-results' ).text() );
+                                                       }
+                                                       if ( e.lengthComputable ) {
+                                                               progress.setProgress( e.loaded * 100 / e.total );
+                                                       } else {
+                                                               progress.setProgress( false );
+                                                       }
+                                               } );
+                                               return xhr;
+                                       }
+                               } )
+                                       .fail( function ( code, data ) {
+                                               var details = 'HTTP error: ' + data.exception;
+                                               $result.empty()
+                                                       .append(
+                                                               new OO.ui.LabelWidget( {
+                                                                       label: mw.message( 'apisandbox-results-error', details ).text(),
+                                                                       classes: [ 'error' ]
+                                                               } ).$element
+                                                       );
+                                       } )
+                                       .done( function ( data, jqXHR ) {
+                                               var m, loadTime, button,
+                                                       ct = jqXHR.getResponseHeader( 'Content-Type' );
+
+                                               $result.empty();
+                                               if ( /^text\/mediawiki-api-prettyprint-wrapped(?:;|$)/.test( ct ) ) {
+                                                       data = $.parseJSON( data );
+                                                       if ( data.modules.length ) {
+                                                               mw.loader.load( data.modules );
+                                                       }
+                                                       $result.append( Util.parseHTML( data.html ) );
+                                                       loadTime = data.time;
+                                               } else if ( ( m = data.match( /<pre[ >][\s\S]*<\/pre>/ ) ) ) {
+                                                       $result.append( Util.parseHTML( m[ 0 ] ) );
+                                                       if ( ( m = data.match( /"wgBackendResponseTime":\s*(\d+)/ ) ) ) {
+                                                               loadTime = parseInt( m[ 1 ], 10 );
+                                                       }
+                                               } else {
+                                                       $( '<pre>' )
+                                                               .addClass( 'api-pretty-content' )
+                                                               .text( data )
+                                                               .appendTo( $result );
+                                               }
+                                               if ( typeof loadTime === 'number' ) {
+                                                       $result.append(
+                                                               $( '<div>' ).append(
+                                                                       new OO.ui.LabelWidget( {
+                                                                               label: mw.message( 'apisandbox-request-time', loadTime ).text()
+                                                                       } ).$element
+                                                               )
+                                                       );
+                                               }
+
+                                               if ( jqXHR.getResponseHeader( 'MediaWiki-API-Error' ) === 'badtoken' ) {
+                                                       // Flush all saved tokens in case one of them is the bad one.
+                                                       Util.markTokensBad();
+                                                       button = new OO.ui.ButtonWidget( {
+                                                               label: mw.message( 'apisandbox-results-fixtoken' ).text()
+                                                       } );
+                                                       button.on( 'click', ApiSandbox.fixTokenAndResend )
+                                                               .on( 'click', button.setDisabled, [ true ], button )
+                                                               .$element.appendTo( $result );
+                                               }
+                                       } );
+                       } );
+               },
+
+               /**
+                * Handler for the "Correct token and resubmit" button
+                *
+                * Used on a 'badtoken' error, it re-fetches token parameters for all
+                * pages and then re-submits the query.
+                */
+               fixTokenAndResend: function () {
+                       var page, subpages, i, k,
+                               ok = true,
+                               tokenWait = { dummy: true },
+                               checkPages = [ pages.main ],
+                               success = function ( k ) {
+                                       delete tokenWait[ k ];
+                                       if ( ok && $.isEmptyObject( tokenWait ) ) {
+                                               ApiSandbox.sendRequest();
+                                       }
+                               },
+                               failure = function ( k ) {
+                                       delete tokenWait[ k ];
+                                       ok = false;
+                               };
+
+                       while ( checkPages.length ) {
+                               page = checkPages.shift();
+
+                               if ( page.tokenWidget ) {
+                                       k = page.apiModule + page.tokenWidget.paramInfo.name;
+                                       tokenWait[ k ] = page.tokenWidget.fetchToken()
+                                               .done( success.bind( page.tokenWidget, k ) )
+                                               .fail( failure.bind( page.tokenWidget, k ) );
+                               }
+
+                               subpages = page.getSubpages();
+                               for ( i = 0; i < subpages.length; i++ ) {
+                                       if ( pages.hasOwnProperty( subpages[ i ].key ) ) {
+                                               checkPages.push( pages[ subpages[ i ].key ] );
+                                       }
+                               }
+                       }
+
+                       success( 'dummy', '' );
+               },
+
+               /**
+                * Reset validity indicators for all widgets
+                */
+               updateValidityIndicators: function () {
+                       var page, subpages, i,
+                               checkPages = [ pages.main ];
+
+                       while ( checkPages.length ) {
+                               page = checkPages.shift();
+                               page.apiCheckValid();
+                               subpages = page.getSubpages();
+                               for ( i = 0; i < subpages.length; i++ ) {
+                                       if ( pages.hasOwnProperty( subpages[ i ].key ) ) {
+                                               checkPages.push( pages[ subpages[ i ].key ] );
+                                       }
+                               }
+                       }
+               }
+       };
+
+       /**
+        * PageLayout for API modules
+        *
+        * @class
+        * @private
+        * @extends OO.ui.PageLayout
+        * @constructor
+        * @param {Object} [config] Configuration options
+        */
+       ApiSandbox.PageLayout = function ( config ) {
+               config = $.extend( { prefix: '' }, config );
+               this.displayText = config.key;
+               this.apiModule = config.path;
+               this.prefix = config.prefix;
+               this.paramInfo = null;
+               this.apiIsValid = true;
+               this.loadFromQueryParams = null;
+               this.widgets = {};
+               this.tokenWidget = null;
+               this.indentLevel = config.indentLevel ? config.indentLevel : 0;
+               ApiSandbox.PageLayout[ 'super' ].call( this, config.key, config );
+               this.loadParamInfo();
+       };
+       OO.inheritClass( ApiSandbox.PageLayout, OO.ui.PageLayout );
+       ApiSandbox.PageLayout.prototype.setupOutlineItem = function () {
+               this.outlineItem.setLevel( this.indentLevel );
+               this.outlineItem.setLabel( this.displayText );
+               this.outlineItem.setIcon( this.apiIsValid || suppressErrors ? null : 'alert' );
+               this.outlineItem.setIconTitle(
+                       this.apiIsValid || suppressErrors ? '' : mw.message( 'apisandbox-alert-page' ).plain()
+               );
+       };
+
+       /**
+        * Fetch module information for this page's module, then create UI
+        */
+       ApiSandbox.PageLayout.prototype.loadParamInfo = function () {
+               var dynamicFieldset, dynamicParamNameWidget,
+                       that = this,
+                       removeDynamicParamWidget = function ( name, layout ) {
+                               dynamicFieldset.removeItems( [ layout ] );
+                               delete that.widgets[ name ];
+                       },
+                       addDynamicParamWidget = function () {
+                               var name, layout, widget, button;
+
+                               // Check name is filled in
+                               name = dynamicParamNameWidget.getValue().trim();
+                               if ( name === '' ) {
+                                       dynamicParamNameWidget.focus();
+                                       return;
+                               }
+
+                               if ( that.widgets[ name ] !== undefined ) {
+                                       windowManager.openWindow( 'errorAlert', {
+                                               title: mw.message(
+                                                       'apisandbox-dynamic-error-exists', name
+                                               ).parse(),
+                                               actions: [
+                                                       {
+                                                               action: 'accept',
+                                                               label: OO.ui.msg( 'ooui-dialog-process-dismiss' ),
+                                                               flags: 'primary'
+                                                       }
+                                               ]
+                                       } );
+                                       return;
+                               }
+
+                               widget = Util.createWidgetForParameter( {
+                                       name: name,
+                                       type: 'string',
+                                       'default': ''
+                               }, {
+                                       nooptional: true
+                               } );
+                               button = new OO.ui.ButtonWidget( {
+                                       icon: 'remove',
+                                       flags: 'destructive'
+                               } );
+                               layout = new OO.ui.ActionFieldLayout(
+                                       widget,
+                                       button,
+                                       {
+                                               label: name,
+                                               align: 'left'
+                                       }
+                               );
+                               button.on( 'click', removeDynamicParamWidget, [ name, layout ] );
+                               that.widgets[ name ] = widget;
+                               dynamicFieldset.addItems( [ layout ], dynamicFieldset.getItems().length - 1 );
+                               widget.focus();
+
+                               dynamicParamNameWidget.setValue( '' );
+                       };
+
+               this.$element.empty()
+                       .append( new OO.ui.ProgressBarWidget( {
+                               progress: false,
+                               text: mw.message( 'apisandbox-loading', this.displayText ).text()
+                       } ).$element );
+
+               Util.fetchModuleInfo( this.apiModule )
+                       .done( function ( pi ) {
+                               var prefix, i, j, dl, widget, $widgetLabel, widgetField, helpField, tmp, flag, count,
+                                       items = [],
+                                       deprecatedItems = [],
+                                       buttons = [],
+                                       filterFmModules = function ( v ) {
+                                               return v.substr( -2 ) !== 'fm' ||
+                                                       !availableFormats.hasOwnProperty( v.substr( 0, v.length - 2 ) );
+                                       },
+                                       widgetLabelOnClick = function () {
+                                               var f = this.getField();
+                                               if ( $.isFunction( f.setDisabled ) ) {
+                                                       f.setDisabled( false );
+                                               }
+                                               if ( $.isFunction( f.focus ) ) {
+                                                       f.focus();
+                                               }
+                                       },
+                                       doNothing = function () {};
+
+                               // This is something of a hack. We always want the 'format' and
+                               // 'action' parameters from the main module to be specified,
+                               // and for 'format' we also want to simplify the dropdown since
+                               // we always send the 'fm' variant.
+                               if ( that.apiModule === 'main' ) {
+                                       for ( i = 0; i < pi.parameters.length; i++ ) {
+                                               if ( pi.parameters[ i ].name === 'action' ) {
+                                                       pi.parameters[ i ].required = true;
+                                                       delete pi.parameters[ i ][ 'default' ];
+                                               }
+                                               if ( pi.parameters[ i ].name === 'format' ) {
+                                                       tmp = pi.parameters[ i ].type;
+                                                       for ( j = 0; j < tmp.length; j++ ) {
+                                                               availableFormats[ tmp[ j ] ] = true;
+                                                       }
+                                                       pi.parameters[ i ].type = $.grep( tmp, filterFmModules );
+                                                       pi.parameters[ i ][ 'default' ] = 'json';
+                                                       pi.parameters[ i ].required = true;
+                                               }
+                                       }
+                               }
+
+                               // Hide the 'wrappedhtml' parameter on format modules
+                               if ( pi.group === 'format' ) {
+                                       pi.parameters = $.grep( pi.parameters, function ( p ) {
+                                               return p.name !== 'wrappedhtml';
+                                       } );
+                               }
+
+                               that.paramInfo = pi;
+
+                               items.push( new OO.ui.FieldLayout(
+                                       new OO.ui.Widget( {} ).toggle( false ), {
+                                               align: 'top',
+                                               label: Util.parseHTML( pi.description )
+                                       }
+                               ) );
+
+                               if ( pi.helpurls.length ) {
+                                       buttons.push( new OO.ui.PopupButtonWidget( {
+                                               label: mw.message( 'apisandbox-helpurls' ).text(),
+                                               icon: 'help',
+                                               popup: {
+                                                       $content: $( '<ul>' ).append( $.map( pi.helpurls, function ( link ) {
+                                                               return $( '<li>' ).append( $( '<a>', {
+                                                                       href: link,
+                                                                       target: '_blank',
+                                                                       text: link
+                                                               } ) );
+                                                       } ) )
+                                               }
+                                       } ) );
+                               }
+
+                               if ( pi.examples.length ) {
+                                       buttons.push( new OO.ui.PopupButtonWidget( {
+                                               label: mw.message( 'apisandbox-examples' ).text(),
+                                               icon: 'code',
+                                               popup: {
+                                                       $content: $( '<ul>' ).append( $.map( pi.examples, function ( example ) {
+                                                               var a = $( '<a>', {
+                                                                       href: '#' + example.query,
+                                                                       html: example.description
+                                                               } );
+                                                               a.find( 'a' ).contents().unwrap(); // Can't nest links
+                                                               return $( '<li>' ).append( a );
+                                                       } ) )
+                                               }
+                                       } ) );
+                               }
+
+                               if ( buttons.length ) {
+                                       items.push( new OO.ui.FieldLayout(
+                                               new OO.ui.ButtonGroupWidget( {
+                                                       items: buttons
+                                               } ), { align: 'top' }
+                                       ) );
+                               }
+
+                               if ( pi.parameters.length ) {
+                                       prefix = that.prefix + pi.prefix;
+                                       for ( i = 0; i < pi.parameters.length; i++ ) {
+                                               widget = Util.createWidgetForParameter( pi.parameters[ i ] );
+                                               that.widgets[ prefix + pi.parameters[ i ].name ] = widget;
+                                               if ( pi.parameters[ i ].tokentype ) {
+                                                       that.tokenWidget = widget;
+                                               }
+
+                                               dl = $( '<dl>' );
+                                               dl.append( $( '<dd>', {
+                                                       addClass: 'description',
+                                                       append: Util.parseHTML( pi.parameters[ i ].description )
+                                               } ) );
+                                               if ( pi.parameters[ i ].info && pi.parameters[ i ].info.length ) {
+                                                       for ( j = 0; j < pi.parameters[ i ].info.length; j++ ) {
+                                                               dl.append( $( '<dd>', {
+                                                                       addClass: 'info',
+                                                                       append: Util.parseHTML( pi.parameters[ i ].info[ j ] )
+                                                               } ) );
+                                                       }
+                                               }
+                                               flag = true;
+                                               count = 1e100;
+                                               switch ( pi.parameters[ i ].type ) {
+                                                       case 'namespace':
+                                                               flag = false;
+                                                               count = mw.config.get( 'wgFormattedNamespaces' ).length;
+                                                               break;
+
+                                                       case 'limit':
+                                                               if ( pi.parameters[ i ].highmax !== undefined ) {
+                                                                       dl.append( $( '<dd>', {
+                                                                               addClass: 'info',
+                                                                               append: Util.parseHTML( mw.message(
+                                                                                       'api-help-param-limit2', pi.parameters[ i ].max, pi.parameters[ i ].highmax
+                                                                               ).parse() )
+                                                                       } ) );
+                                                               } else {
+                                                                       dl.append( $( '<dd>', {
+                                                                               addClass: 'info',
+                                                                               append: Util.parseHTML( mw.message(
+                                                                                       'api-help-param-limit', pi.parameters[ i ].max
+                                                                               ).parse() )
+                                                                       } ) );
+                                                               }
+                                                               break;
+
+                                                       case 'integer':
+                                                               tmp = '';
+                                                               if ( pi.parameters[ i ].min !== undefined ) {
+                                                                       tmp += 'min';
+                                                               }
+                                                               if ( pi.parameters[ i ].max !== undefined ) {
+                                                                       tmp += 'max';
+                                                               }
+                                                               if ( tmp !== '' ) {
+                                                                       dl.append( $( '<dd>', {
+                                                                               addClass: 'info',
+                                                                               append: Util.parseHTML( mw.message(
+                                                                                       'api-help-param-integer-' + tmp,
+                                                                                       Util.apiBool( pi.parameters[ i ].multi ) ? 2 : 1,
+                                                                                       pi.parameters[ i ].min, pi.parameters[ i ].max
+                                                                               ).parse() )
+                                                                       } ) );
+                                                               }
+                                                               break;
+
+                                                       default:
+                                                               if ( $.isArray( pi.parameters[ i ].type ) ) {
+                                                                       flag = false;
+                                                                       count = pi.parameters[ i ].type.length;
+                                                               }
+                                                               break;
+                                               }
+                                               if ( Util.apiBool( pi.parameters[ i ].multi ) ) {
+                                                       tmp = [];
+                                                       if ( flag && !( widget instanceof OO.ui.CapsuleMultiSelectWidget ) &&
+                                                               !(
+                                                                       widget instanceof OptionalWidget &&
+                                                                       widget.widget instanceof OO.ui.CapsuleMultiSelectWidget
+                                                               )
+                                                       ) {
+                                                               tmp.push( mw.message( 'api-help-param-multi-separate' ).parse() );
+                                                       }
+                                                       if ( count > pi.parameters[ i ].lowlimit ) {
+                                                               tmp.push(
+                                                                       mw.message( 'api-help-param-multi-max',
+                                                                               pi.parameters[ i ].lowlimit, pi.parameters[ i ].highlimit
+                                                                       ).parse()
+                                                               );
+                                                       }
+                                                       if ( tmp.length ) {
+                                                               dl.append( $( '<dd>', {
+                                                                       addClass: 'info',
+                                                                       append: Util.parseHTML( tmp.join( ' ' ) )
+                                                               } ) );
+                                                       }
+                                               }
+                                               helpField = new OO.ui.FieldLayout(
+                                                       new OO.ui.Widget( {
+                                                               $content: '\xa0',
+                                                               classes: [ 'mw-apisandbox-spacer' ]
+                                                       } ), {
+                                                               align: 'inline',
+                                                               label: dl
+                                                       }
+                                               );
+
+                                               $widgetLabel = $( '<span>' );
+                                               widgetField = new OO.ui.FieldLayout(
+                                                       widget,
+                                                       {
+                                                               align: 'left',
+                                                               label: prefix + pi.parameters[ i ].name,
+                                                               $label: $widgetLabel
+                                                       }
+                                               );
+
+                                               // FieldLayout only does click for InputElement
+                                               // widgets. So supply our own click handler.
+                                               $widgetLabel.on( 'click', widgetLabelOnClick.bind( widgetField ) );
+
+                                               // Don't grey out the label when the field is disabled,
+                                               // it makes it too hard to read and our "disabled"
+                                               // isn't really disabled.
+                                               widgetField.onFieldDisable = doNothing;
+
+                                               if ( Util.apiBool( pi.parameters[ i ].deprecated ) ) {
+                                                       deprecatedItems.push( widgetField, helpField );
+                                               } else {
+                                                       items.push( widgetField, helpField );
+                                               }
+                                       }
+                               }
+
+                               if ( !pi.parameters.length && !Util.apiBool( pi.dynamicparameters ) ) {
+                                       items.push( new OO.ui.FieldLayout(
+                                               new OO.ui.Widget( {} ).toggle( false ), {
+                                                       align: 'top',
+                                                       label: Util.parseHTML( mw.message( 'apisandbox-no-parameters' ).parse() )
+                                               }
+                                       ) );
+                               }
+
+                               that.$element.empty();
+
+                               new OO.ui.FieldsetLayout( {
+                                       label: that.displayText
+                               } ).addItems( items )
+                                       .$element.appendTo( that.$element );
+
+                               if ( Util.apiBool( pi.dynamicparameters ) ) {
+                                       dynamicFieldset = new OO.ui.FieldsetLayout();
+                                       dynamicParamNameWidget = new OO.ui.TextInputWidget( {
+                                               placeholder: mw.message( 'apisandbox-dynamic-parameters-add-placeholder' ).text()
+                                       } ).on( 'enter', addDynamicParamWidget );
+                                       dynamicFieldset.addItems( [
+                                               new OO.ui.FieldLayout(
+                                                       new OO.ui.Widget( {} ).toggle( false ), {
+                                                               align: 'top',
+                                                               label: Util.parseHTML( pi.dynamicparameters )
+                                                       }
+                                               ),
+                                               new OO.ui.ActionFieldLayout(
+                                                       dynamicParamNameWidget,
+                                                       new OO.ui.ButtonWidget( {
+                                                               icon: 'add',
+                                                               flags: 'constructive'
+                                                       } ).on( 'click', addDynamicParamWidget ),
+                                                       {
+                                                               label: mw.message( 'apisandbox-dynamic-parameters-add-label' ).text(),
+                                                               align: 'left'
+                                                       }
+                                               )
+                                       ] );
+                                       $( '<fieldset>' )
+                                               .append(
+                                                       $( '<legend>' ).text( mw.message( 'apisandbox-dynamic-parameters' ).text() ),
+                                                       dynamicFieldset.$element
+                                               )
+                                               .appendTo( that.$element );
+                               }
+
+                               if ( deprecatedItems.length ) {
+                                       tmp = new OO.ui.FieldsetLayout().addItems( deprecatedItems ).toggle( false );
+                                       $( '<fieldset>' )
+                                               .append(
+                                                       $( '<legend>' ).append(
+                                                               new OO.ui.ToggleButtonWidget( {
+                                                                       label: mw.message( 'apisandbox-deprecated-parameters' ).text()
+                                                               } ).on( 'change', tmp.toggle, [], tmp ).$element
+                                                       ),
+                                                       tmp.$element
+                                               )
+                                               .appendTo( that.$element );
+                               }
+
+                               // Load stored params, if any, then update the booklet if we
+                               // have subpages (or else just update our valid-indicator).
+                               tmp = that.loadFromQueryParams;
+                               that.loadFromQueryParams = null;
+                               if ( $.isPlainObject( tmp ) ) {
+                                       that.loadQueryParams( tmp );
+                               }
+                               if ( that.getSubpages().length > 0 ) {
+                                       ApiSandbox.updateUI( tmp );
+                               } else {
+                                       that.apiCheckValid();
+                               }
+                       } ).fail( function ( code, detail ) {
+                               that.$element.empty()
+                                       .append(
+                                               new OO.ui.LabelWidget( {
+                                                       label: mw.message( 'apisandbox-load-error', that.apiModule, detail ).text(),
+                                                       classes: [ 'error' ]
+                                               } ).$element,
+                                               new OO.ui.ButtonWidget( {
+                                                       label: mw.message( 'apisandbox-retry' ).text()
+                                               } ).on( 'click', that.loadParamInfo, [], that ).$element
+                                       );
+                       } );
+       };
+
+       /**
+        * Check that all widgets on the page are in a valid state.
+        *
+        * @return {boolean}
+        */
+       ApiSandbox.PageLayout.prototype.apiCheckValid = function () {
+               var that = this;
+
+               if ( this.paramInfo === null ) {
+                       return $.Deferred().resolve( false ).promise();
+               } else {
+                       return $.when.apply( $, $.map( this.widgets, function ( widget ) {
+                               return widget.apiCheckValid();
+                       } ) ).then( function () {
+                               that.apiIsValid = $.inArray( false, arguments ) === -1;
+                               if ( that.getOutlineItem() ) {
+                                       that.getOutlineItem().setIcon( that.apiIsValid || suppressErrors ? null : 'alert' );
+                                       that.getOutlineItem().setIconTitle(
+                                               that.apiIsValid || suppressErrors ? '' : mw.message( 'apisandbox-alert-page' ).plain()
+                                       );
+                               }
+                               return $.Deferred().resolve( that.apiIsValid ).promise();
+                       } );
+               }
+       };
+
+       /**
+        * Load form fields from query parameters
+        *
+        * @param {Object} params
+        */
+       ApiSandbox.PageLayout.prototype.loadQueryParams = function ( params ) {
+               if ( this.paramInfo === null ) {
+                       this.loadFromQueryParams = params;
+               } else {
+                       $.each( this.widgets, function ( name, widget ) {
+                               var v = params.hasOwnProperty( name ) ? params[ name ] : undefined;
+                               widget.setApiValue( v );
+                       } );
+               }
+       };
+
+       /**
+        * Load query params from form fields
+        *
+        * @param {Object} params Write query parameters into this object
+        * @param {Object} displayParams Write query parameters for display into this object
+        */
+       ApiSandbox.PageLayout.prototype.getQueryParams = function ( params, displayParams ) {
+               $.each( this.widgets, function ( name, widget ) {
+                       var value = widget.getApiValue();
+                       if ( value !== undefined ) {
+                               params[ name ] = value;
+                               if ( $.isFunction( widget.getApiValueForDisplay ) ) {
+                                       value = widget.getApiValueForDisplay();
+                               }
+                               displayParams[ name ] = value;
+                       }
+               } );
+       };
+
+       /**
+        * Fetch a list of subpage names loaded by this page
+        *
+        * @return {Array}
+        */
+       ApiSandbox.PageLayout.prototype.getSubpages = function () {
+               var ret = [];
+               $.each( this.widgets, function ( name, widget ) {
+                       var submodules, i;
+                       if ( $.isFunction( widget.getSubmodules ) ) {
+                               submodules = widget.getSubmodules();
+                               for ( i = 0; i < submodules.length; i++ ) {
+                                       ret.push( {
+                                               key: name + '=' + submodules[ i ].value,
+                                               path: submodules[ i ].path,
+                                               prefix: widget.paramInfo.submoduleparamprefix || ''
+                                       } );
+                               }
+                       }
+               } );
+               return ret;
+       };
+
+       /**
+        * A text input with a clickable indicator
+        *
+        * @class
+        * @private
+        * @constructor
+        * @param {Object} [config] Configuration options
+        */
+       function TextInputWithIndicatorWidget( config ) {
+               var k;
+
+               config = config || {};
+               TextInputWithIndicatorWidget[ 'super' ].call( this, config );
+
+               this.$indicator = $( '<span>' ).addClass( 'mw-apisandbox-clickable-indicator' );
+               OO.ui.mixin.TabIndexedElement.call(
+                       this, $.extend( {}, config, { $tabIndexed: this.$indicator } )
+               );
+
+               this.input = new OO.ui.TextInputWidget( $.extend( {
+                       $indicator: this.$indicator,
+                       disabled: this.isDisabled()
+               }, config.input ) );
+
+               // Forward most methods for convenience
+               for ( k in this.input ) {
+                       if ( $.isFunction( this.input[ k ] ) && !this[ k ] ) {
+                               this[ k ] = this.input[ k ].bind( this.input );
+                       }
+               }
+
+               this.$indicator.on( {
+                       click: this.onIndicatorClick.bind( this ),
+                       keypress: this.onIndicatorKeyPress.bind( this )
+               } );
+
+               this.$element.append( this.input.$element );
+       }
+       OO.inheritClass( TextInputWithIndicatorWidget, OO.ui.Widget );
+       OO.mixinClass( TextInputWithIndicatorWidget, OO.ui.mixin.TabIndexedElement );
+       TextInputWithIndicatorWidget.prototype.onIndicatorClick = function ( e ) {
+               if ( !this.isDisabled() && e.which === 1 ) {
+                       this.emit( 'indicator' );
+               }
+               return false;
+       };
+       TextInputWithIndicatorWidget.prototype.onIndicatorKeyPress = function ( e ) {
+               if ( !this.isDisabled() && ( e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER ) ) {
+                       this.emit( 'indicator' );
+                       return false;
+               }
+       };
+       TextInputWithIndicatorWidget.prototype.setDisabled = function ( disabled ) {
+               TextInputWithIndicatorWidget[ 'super' ].prototype.setDisabled.call( this, disabled );
+               if ( this.input ) {
+                       this.input.setDisabled( this.isDisabled() );
+               }
+               return this;
+       };
+
+       /**
+        * A wrapper for a widget that provides an enable/disable button
+        *
+        * @class
+        * @private
+        * @constructor
+        * @param {OO.ui.Widget} widget
+        * @param {Object} [config] Configuration options
+        */
+       function OptionalWidget( widget, config ) {
+               var k;
+
+               config = config || {};
+
+               this.widget = widget;
+               this.$overlay = config.$overlay ||
+                       $( '<div>' ).addClass( 'mw-apisandbox-optionalWidget-overlay' );
+               this.checkbox = new OO.ui.CheckboxInputWidget( config.checkbox )
+                       .on( 'change', this.onCheckboxChange, [], this );
+
+               OptionalWidget[ 'super' ].call( this, config );
+
+               // Forward most methods for convenience
+               for ( k in this.widget ) {
+                       if ( $.isFunction( this.widget[ k ] ) && !this[ k ] ) {
+                               this[ k ] = this.widget[ k ].bind( this.widget );
+                       }
+               }
+
+               this.$overlay.on( 'click', this.onOverlayClick.bind( this ) );
+
+               this.$element
+                       .addClass( 'mw-apisandbox-optionalWidget' )
+                       .append(
+                               this.$overlay,
+                               $( '<div>' ).addClass( 'mw-apisandbox-optionalWidget-fields' ).append(
+                                       $( '<div>' ).addClass( 'mw-apisandbox-optionalWidget-widget' ).append(
+                                               widget.$element
+                                       ),
+                                       $( '<div>' ).addClass( 'mw-apisandbox-optionalWidget-checkbox' ).append(
+                                               this.checkbox.$element
+                                       )
+                               )
+                       );
+
+               this.setDisabled( widget.isDisabled() );
+       }
+       OO.inheritClass( OptionalWidget, OO.ui.Widget );
+       OptionalWidget.prototype.onCheckboxChange = function ( checked ) {
+               this.setDisabled( !checked );
+       };
+       OptionalWidget.prototype.onOverlayClick = function () {
+               this.setDisabled( false );
+               if ( $.isFunction( this.widget.focus ) ) {
+                       this.widget.focus();
+               }
+       };
+       OptionalWidget.prototype.setDisabled = function ( disabled ) {
+               OptionalWidget[ 'super' ].prototype.setDisabled.call( this, disabled );
+               this.widget.setDisabled( this.isDisabled() );
+               this.checkbox.setSelected( !this.isDisabled() );
+               this.$overlay.toggle( this.isDisabled() );
+               return this;
+       };
+
+       $( ApiSandbox.init );
+
+}( jQuery, mediaWiki, OO ) );
diff --git a/resources/src/mediawiki.special/mediawiki.special.apisandbox.top.css b/resources/src/mediawiki.special/mediawiki.special.apisandbox.top.css
new file mode 100644 (file)
index 0000000..4dc4c27
--- /dev/null
@@ -0,0 +1,3 @@
+.client-js .mw-apisandbox-nojs {
+       display: none;
+}
index 6db7c12..29eaaad 100644 (file)
                        return;
                }
                this.currentRequest = this.api.get( {
+                       formatversion: 2,
                        action: 'query',
                        prop: [ 'info' ],
                        titles: titles
                } ).done( function ( response ) {
-                       var index, curr, title;
-                       for ( index in response.query.pages ) {
-                               curr = response.query.pages[ index ];
-                               title = new ForeignTitle( curr.title ).getPrefixedText();
-                               this.existenceCache[ title ] = curr.missing === undefined;
+                       $.each( response.query.pages, function ( index, page ) {
+                               var title = new ForeignTitle( page.title ).getPrefixedText();
+                               this.existenceCache[ title ] = !page.missing;
                                queue[ title ].resolve( this.existenceCache[ title ] );
-                       }
+                       } );
                }.bind( this ) );
        };
 
index c242b6c..5d7d115 100644 (file)
                switch ( searchType ) {
                        case CategorySelector.SearchType.OpenSearch:
                                this.api.get( {
+                                       formatversion: 2,
                                        action: 'opensearch',
                                        namespace: NS_CATEGORY,
                                        limit: this.limit,
 
                        case CategorySelector.SearchType.InternalSearch:
                                this.api.get( {
+                                       formatversion: 2,
                                        action: 'query',
                                        list: 'allpages',
                                        apnamespace: NS_CATEGORY,
                                }
 
                                this.api.get( {
+                                       formatversion: 2,
                                        action: 'query',
                                        prop: 'info',
                                        titles: 'Category:' + input
                                } ).done( function ( res ) {
-                                       var page,
-                                               categories = [];
+                                       var categories = [];
 
-                                       for ( page in res.query.pages ) {
-                                               if ( parseInt( page, 10 ) > -1 ) {
-                                                       categories.push( res.query.pages[ page ].title );
+                                       $.each( res.query.pages, function ( index, page ) {
+                                               if ( !page.missing ) {
+                                                       categories.push( page.title );
                                                }
-                                       }
+                                       } );
 
                                        deferred.resolve( categories );
                                } ).fail( deferred.reject.bind( deferred ) );
                                }
 
                                this.api.get( {
+                                       formatversion: 2,
                                        action: 'query',
                                        list: 'categorymembers',
                                        cmtype: 'subcat',
                                }
 
                                this.api.get( {
+                                       formatversion: 2,
                                        action: 'query',
                                        prop: 'categories',
                                        cllimit: this.limit,
                                        titles: 'Category:' + input
                                } ).done( function ( res )  {
-                                       var page,
-                                               categories = [];
+                                       var categories = [];
 
-                                       for ( page in res.query.pages ) {
-                                               if ( parseInt( page, 10 ) > -1 ) {
-                                                       if ( $.isArray( res.query.pages[ page ].categories ) ) {
-                                                               categories.push.apply( categories, res.query.pages[ page ].categories.map( function ( category ) {
+                                       $.each( res.query.pages, function ( index, page ) {
+                                               if ( !page.missing ) {
+                                                       if ( $.isArray( page.categories ) ) {
+                                                               categories.push.apply( categories, page.categories.map( function ( category ) {
                                                                        return category.title;
                                                                } ) );
                                                        }
                                                }
-                                       }
+                                       } );
 
                                        deferred.resolve( categories );
                                } ).fail( deferred.reject.bind( deferred ) );
index bc3d44f..49219d7 100644 (file)
@@ -14,6 +14,7 @@
                 */
                parse: function ( wikitext ) {
                        var apiPromise = this.get( {
+                               formatversion: 2,
                                action: 'parse',
                                contentmodel: 'wikitext',
                                text: wikitext
@@ -21,7 +22,7 @@
 
                        return apiPromise
                                .then( function ( data ) {
-                                       return data.parse.text[ '*' ];
+                                       return data.parse.text;
                                } )
                                .promise( { abort: apiPromise.abort } );
                }
index 3036237..47d80d6 100644 (file)
                 * - It is incompatible with uploads to a foreign wiki using mw.ForeignApi
                 * - You must pass a HTMLInputElement and not a File for it to be possible
                 *
-                * @param {HTMLInputElement|File} file HTML input type=file element with a file already inside
+                * @param {HTMLInputElement|File|Blob} file HTML input type=file element with a file already inside
                 *  of it, or a File object.
                 * @param {Object} data Other upload options, see action=upload API docs for more
                 * @return {jQuery.Promise}
                                throw new Error( 'No file' );
                        }
 
-                       canUseFormData = formDataAvailable() && file instanceof window.File;
+                       // Blobs are allowed in formdata uploads, it turns out
+                       canUseFormData = formDataAvailable() && ( file instanceof window.File || file instanceof window.Blob );
 
                        if ( !isFileInput && !canUseFormData ) {
                                throw new Error( 'Unsupported argument type passed to mw.Api.upload' );
diff --git a/resources/src/mediawiki/bookletlayout/option2/ccbysa.png b/resources/src/mediawiki/bookletlayout/option2/ccbysa.png
deleted file mode 100644 (file)
index b39ecb6..0000000
Binary files a/resources/src/mediawiki/bookletlayout/option2/ccbysa.png and /dev/null differ
diff --git a/resources/src/mediawiki/bookletlayout/option2/ccbysa.svg b/resources/src/mediawiki/bookletlayout/option2/ccbysa.svg
deleted file mode 100644 (file)
index 605af75..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
-<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
-        viewBox="0 0 150 150" style="enable-background:new 0 0 150 150;" xml:space="preserve">
-<style type="text/css">
-       .st0{fill:#FFFFFF;stroke:#000000;stroke-width:2;stroke-miterlimit:10;}
-       .st1{fill-rule:evenodd;clip-rule:evenodd;}
-</style>
-<g>
-       <circle class="st0" cx="74.9" cy="75.1" r="58.1"/>
-       <g>
-               <path class="st1" d="M74.1,63.4c-4-7.3-10.8-10.2-18.7-10.2c-11.5,0-20.6,8.1-20.6,21.9c0,14,8.6,21.9,21,21.9
-                       c8,0,14.8-4.4,18.5-11l-8.8-4.5c-2,4.7-4.9,6.1-8.7,6.1c-6.5,0-9.5-5.4-9.5-12.5c0-7.1,2.5-12.5,9.5-12.5c1.9,0,5.6,1,7.8,5.7
-                       L74.1,63.4z"/>
-               <path class="st1" d="M114.8,63.4c-4-7.3-10.8-10.2-18.7-10.2c-11.5,0-20.6,8.1-20.6,21.9c0,14,8.6,21.9,21,21.9
-                       c8,0,14.8-4.4,18.5-11l-8.8-4.5c-2,4.7-4.9,6.1-8.7,6.1c-6.5,0-9.5-5.4-9.5-12.5c0-7.1,2.5-12.5,9.5-12.5c1.9,0,5.6,1,7.8,5.7
-                       L114.8,63.4z"/>
-       </g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-</svg>
diff --git a/resources/src/mediawiki/bookletlayout/option2/noderiv.png b/resources/src/mediawiki/bookletlayout/option2/noderiv.png
deleted file mode 100644 (file)
index afac81f..0000000
Binary files a/resources/src/mediawiki/bookletlayout/option2/noderiv.png and /dev/null differ
diff --git a/resources/src/mediawiki/bookletlayout/option2/noderiv.svg b/resources/src/mediawiki/bookletlayout/option2/noderiv.svg
deleted file mode 100644 (file)
index 96d8084..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
-<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
-        viewBox="0 0 150 150" style="enable-background:new 0 0 150 150;" xml:space="preserve">
-<style type="text/css">
-       .st0{fill:#FFFFFF;stroke:#000000;stroke-width:2;stroke-miterlimit:10;}
-       .st1{fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:10;}
-       .st2{stroke:#000000;stroke-width:2;stroke-miterlimit:10;}
-</style>
-<g>
-       
-               <rect x="7.8" y="34.6" transform="matrix(0.2182 0.9759 -0.9759 0.2182 113.5065 3.8061)" class="st0" width="93.2" height="76.2"/>
-       <circle class="st1" cx="54.4" cy="72.7" r="26.1"/>
-</g>
-<g>
-       
-               <rect x="51.6" y="41.7" transform="matrix(-0.1524 0.9883 -0.9883 -0.1524 192.0548 -5.1264)" class="st0" width="93.2" height="76.2"/>
-       <circle class="st2" cx="98.2" cy="79.8" r="26.1"/>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-</svg>
diff --git a/resources/src/mediawiki/bookletlayout/option2/ownwork.png b/resources/src/mediawiki/bookletlayout/option2/ownwork.png
deleted file mode 100644 (file)
index 69df378..0000000
Binary files a/resources/src/mediawiki/bookletlayout/option2/ownwork.png and /dev/null differ
diff --git a/resources/src/mediawiki/bookletlayout/option2/ownwork.svg b/resources/src/mediawiki/bookletlayout/option2/ownwork.svg
deleted file mode 100644 (file)
index dc660c8..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
-<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
-        viewBox="0 0 150 150" style="enable-background:new 0 0 150 150;" xml:space="preserve">
-<style type="text/css">
-       .st0{fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:10;}
-       .st1{stroke:#000000;stroke-width:2;stroke-miterlimit:10;}
-</style>
-<polygon class="st0" points="6.3,43.3 38.4,43.3 46,34.4 78.4,34.4 85.5,42.6 142.6,42.6 142.6,115 6.3,115 "/>
-<g>
-       <circle class="st0" cx="63.3" cy="78.8" r="25.8"/>
-       <circle class="st1" cx="63.3" cy="78.8" r="16.2"/>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-</svg>
diff --git a/resources/src/mediawiki/bookletlayout/option2/useful.png b/resources/src/mediawiki/bookletlayout/option2/useful.png
deleted file mode 100644 (file)
index d5b9429..0000000
Binary files a/resources/src/mediawiki/bookletlayout/option2/useful.png and /dev/null differ
diff --git a/resources/src/mediawiki/bookletlayout/option2/useful.svg b/resources/src/mediawiki/bookletlayout/option2/useful.svg
deleted file mode 100644 (file)
index 2af3f93..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
-<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
-        viewBox="0 0 150 150" style="enable-background:new 0 0 150 150;" xml:space="preserve">
-<style type="text/css">
-       .st0{fill:#FFFFFF;stroke:#000000;stroke-width:2;stroke-miterlimit:10;}
-</style>
-<circle class="st0" cx="72.7" cy="62.5" r="47.1"/>
-<path d="M76,128.8c34.8,0,63.2-28.3,63.2-63.2c0-18.3-7.9-34.9-20.4-46.4l11.8-11.8L129.1,6l-11.8,11.8c0,0,0,0,0,0l-1.4,1.4
-       c13,11.2,21.3,27.8,21.3,46.4c0,33.7-27.4,61.2-61.2,61.2c-18.5,0-35.1-8.3-46.4-21.3l-0.4,0.4l0,0l-13,13l1.4,1.4l11.9-11.9
-       c10.9,11.9,26.4,19.6,43.6,20.4v15.7H49.7v2h48.9v-2H75.1v-15.7C75.4,128.8,75.7,128.8,76,128.8z"/>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-</svg>
diff --git a/resources/src/mediawiki/bookletlayout/option4/camera.png b/resources/src/mediawiki/bookletlayout/option4/camera.png
deleted file mode 100644 (file)
index a48f9fb..0000000
Binary files a/resources/src/mediawiki/bookletlayout/option4/camera.png and /dev/null differ
diff --git a/resources/src/mediawiki/bookletlayout/option4/camera.svg b/resources/src/mediawiki/bookletlayout/option4/camera.svg
deleted file mode 100644 (file)
index b110396..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
-<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
-        viewBox="0 0 264 162" height="162" width="264" style="enable-background:new 0 0 264 162;" xml:space="preserve">
-<style type="text/css">
-       .st0{fill:#00AF89;}
-       .st1{fill:#FFFFFF;}
-       .st2{fill:#FFFFFF;stroke:#E5E5E5;stroke-miterlimit:10;}
-       .st3{fill:#E5E5E5;}
-       .st4{fill:none;stroke:#E5E5E5;stroke-miterlimit:10;}
-       .st5{fill:#9B9A9A;}
-       .st6{fill:none;stroke:#E5E5E5;stroke-linejoin:round;stroke-miterlimit:10;}
-</style>
-<rect x="62.3" y="28.1" class="st0" width="137.6" height="105"/>
-<g>
-       <path class="st1" d="M73.1,57v64.4h45.3V78.5h25.3v42.9h45.3V57H73.1z M94.3,107.6H80.9V94.3h13.3V107.6z M94.3,91.9H80.9V78.5
-               h13.3V91.9z M109.9,107.6H96.6V94.3h13.3V107.6z M109.9,91.9H96.6V78.5h13.3V91.9z M164.3,107.6h-13.3V94.3h13.3V107.6z
-                M164.3,91.9h-13.3V78.5h13.3V91.9z M179.9,107.6h-13.3V94.3h13.3V107.6z M179.9,91.9h-13.3V78.5h13.3V91.9z"/>
-       <rect x="73.1" y="48.5" class="st1" width="116" height="5.7"/>
-       <rect x="73.1" y="39.9" class="st1" width="116" height="5.7"/>
-</g>
-<rect x="62.3" y="124" class="st1" width="137.6" height="1.7"/>
-<g>
-       <g>
-               <rect x="6.7" y="7.4" class="st2" width="35.3" height="12.7"/>
-               <rect x="42" y="11.1" class="st3" width="2.3" height="5.7"/>
-       </g>
-       <rect x="8.2" y="9.1" class="st3" width="5.2" height="9.2"/>
-       <rect x="14.5" y="9.1" class="st3" width="5.2" height="9.2"/>
-       <rect x="21" y="9.1" class="st3" width="5.2" height="9.2"/>
-</g>
-<circle class="st3" cx="252.7" cy="12.1" r="6.2"/>
-<rect x="239.3" y="9.1" class="st3" width="5.3" height="7.7"/>
-<rect x="233" y="9.1" class="st3" width="5.3" height="7.7"/>
-<rect x="214" y="143.3" class="st3" width="5.3" height="7.7"/>
-<rect x="207.6" y="143.3" class="st3" width="5.3" height="7.7"/>
-<rect x="50.4" y="143.3" class="st3" width="5.3" height="7.7"/>
-<rect x="39" y="143.3" class="st3" width="5.3" height="7.7"/>
-<line class="st4" x1="73.1" y1="148" x2="189.1" y2="148"/>
-<line class="st4" x1="132" y1="143.1" x2="132" y2="152.9"/>
-<line class="st4" x1="189.1" y1="144.3" x2="189.1" y2="151.7"/>
-<line class="st4" x1="73.1" y1="144.3" x2="73.1" y2="151.7"/>
-<line class="st4" x1="86" y1="145.5" x2="86" y2="150.5"/>
-<line class="st4" x1="100.7" y1="145.5" x2="100.7" y2="150.5"/>
-<line class="st4" x1="115.3" y1="145.5" x2="115.3" y2="150.5"/>
-<line class="st4" x1="145.8" y1="145.5" x2="145.8" y2="150.5"/>
-<line class="st4" x1="160.5" y1="145.5" x2="160.5" y2="150.5"/>
-<line class="st4" x1="175.2" y1="145.5" x2="175.2" y2="150.5"/>
-<g>
-       <rect x="113.8" y="154.1" class="st5" width="3.2" height="4.7"/>
-       <polygon class="st5" points="113.8,154.1 115.3,152.3 116.9,154.1        "/>
-</g>
-<g>
-       <g>
-               <polyline class="st6" points="34.3,147.1 31.5,147.1 34.3,144.3 31.5,147.1 34.3,147.1 31.5,149.9                 "/>
-       </g>
-       <polygon class="st3" points="33.9,149.5 32.1,151.7 30.3,149.5   "/>
-</g>
-<line class="st6" x1="48.9" y1="143.2" x2="45.9" y2="151.1"/>
-</svg>
diff --git a/resources/src/mediawiki/bookletlayout/option4/graphics.png b/resources/src/mediawiki/bookletlayout/option4/graphics.png
deleted file mode 100644 (file)
index 5613f88..0000000
Binary files a/resources/src/mediawiki/bookletlayout/option4/graphics.png and /dev/null differ
diff --git a/resources/src/mediawiki/bookletlayout/option4/graphics.svg b/resources/src/mediawiki/bookletlayout/option4/graphics.svg
deleted file mode 100644 (file)
index c32f79f..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
-<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
-        viewBox="0 0 264 162" style="enable-background:new 0 0 264 162;" xml:space="preserve">
-<style type="text/css">
-       .st0{fill:none;stroke:#E5E5E5;stroke-miterlimit:10;}
-       .st1{fill:none;stroke:#9B9A9A;stroke-miterlimit:10;}
-       .st2{fill:#E5E5E5;}
-       .st3{fill:#9B9A9A;}
-       .st4{fill:#00AF89;}
-       .st5{fill:#FFFFFF;}
-       .st6{fill:none;stroke:#FFFFFF;stroke-width:5;stroke-miterlimit:10;}
-</style>
-<line class="st0" x1="2" y1="10.5" x2="262" y2="10.5"/>
-<line class="st0" x1="2" y1="24.8" x2="262" y2="24.8"/>
-<g>
-       <rect x="6.9" y="32.8" class="st1" width="9.3" height="9.3"/>
-       <rect x="16.2" y="32.8" class="st1" width="9.3" height="9.3"/>
-       <rect x="6.9" y="42.1" class="st1" width="9.3" height="9.3"/>
-       <rect x="16.2" y="42.1" class="st1" width="9.3" height="9.3"/>
-       <rect x="6.9" y="51.4" class="st1" width="9.3" height="9.3"/>
-       <rect x="16.2" y="51.4" class="st1" width="9.3" height="9.3"/>
-       <rect x="6.9" y="60.8" class="st1" width="9.3" height="9.3"/>
-       <rect x="16.2" y="60.8" class="st1" width="9.3" height="9.3"/>
-       <rect x="6.9" y="70.1" class="st1" width="9.3" height="9.3"/>
-       <rect x="16.2" y="70.1" class="st1" width="9.3" height="9.3"/>
-       <rect x="6.9" y="79.4" class="st1" width="9.3" height="9.3"/>
-       <rect x="16.2" y="79.4" class="st1" width="9.3" height="9.3"/>
-       <rect x="6.9" y="88.8" class="st1" width="9.3" height="9.3"/>
-       <rect x="16.2" y="88.8" class="st1" width="9.3" height="9.3"/>
-       <rect x="6.9" y="98.1" class="st1" width="9.3" height="9.3"/>
-       <rect x="16.2" y="98.1" class="st1" width="9.3" height="9.3"/>
-       <rect x="6.9" y="107.4" class="st1" width="9.3" height="9.3"/>
-       <rect x="16.2" y="107.4" class="st1" width="9.3" height="9.3"/>
-       <rect x="6.9" y="116.8" class="st1" width="9.3" height="9.3"/>
-       <rect x="16.2" y="116.8" class="st1" width="9.3" height="9.3"/>
-</g>
-<rect x="3.2" y="12.7" class="st2" width="9.3" height="9.3"/>
-<rect x="3.2" y="3.4" class="st2" width="4.7" height="4.7"/>
-<rect x="10.2" y="3.4" class="st2" width="4.7" height="4.7"/>
-<rect x="16.9" y="3.4" class="st2" width="4.7" height="4.7"/>
-<rect x="249.2" y="3.4" class="st2" width="4.7" height="4.7"/>
-<rect x="255.9" y="3.4" class="st2" width="4.7" height="4.7"/>
-<rect x="14.6" y="12.7" class="st2" width="9.3" height="9.3"/>
-<rect x="239.9" y="12.7" class="st2" width="9.3" height="9.3"/>
-<rect x="228.9" y="12.7" class="st2" width="9.3" height="9.3"/>
-<rect x="251.2" y="12.7" class="st2" width="9.3" height="9.3"/>
-<g>
-       <path class="st2" d="M73.5,13.7V21H26.6v-7.3H73.5 M74.5,12.7H25.6V22h48.9V12.7L74.5,12.7z"/>
-</g>
-<g>
-       <path class="st2" d="M117.8,13.7V21H76.9v-7.3H117.8 M118.8,12.7H75.9V22h42.9V12.7L118.8,12.7z"/>
-</g>
-<g>
-       <path class="st2" d="M162.5,13.7V21h-40.9v-7.3H162.5 M163.5,12.7h-42.9V22h42.9V12.7L163.5,12.7z"/>
-</g>
-<rect x="209.7" y="30.3" class="st2" width="50.9" height="32.3"/>
-<rect x="209.7" y="30.3" class="st3" width="50.9" height="5.7"/>
-<g>
-       <rect x="212.2" y="38.5" class="st3" width="6.2" height="6.2"/>
-       <rect x="239.9" y="38.5" class="st3" width="6.2" height="2.5"/>
-       <rect x="239.9" y="42.2" class="st3" width="6.2" height="2.5"/>
-       <g>
-               <path class="st3" d="M237.2,39.5v4.2H221v-4.2H237.2 M238.2,38.5H220v6.2h18.2V38.5L238.2,38.5z"/>
-       </g>
-</g>
-<g>
-       <rect x="212.2" y="46.5" class="st3" width="6.2" height="6.2"/>
-       <rect x="239.9" y="46.5" class="st3" width="6.2" height="2.5"/>
-       <rect x="239.9" y="50.2" class="st3" width="6.2" height="2.5"/>
-       <g>
-               <path class="st3" d="M237.2,47.5v4.2H221v-4.2H237.2 M238.2,46.5H220v6.2h18.2V46.5L238.2,46.5z"/>
-       </g>
-</g>
-<rect x="209.7" y="66.6" class="st2" width="50.9" height="32.3"/>
-<rect x="209.7" y="66.6" class="st3" width="50.9" height="5.7"/>
-<g>
-       <path class="st3" d="M257.2,75.8V80h-44.1v-4.2H257.2 M258.2,74.8h-46.1V81h46.1V74.8L258.2,74.8z"/>
-</g>
-<g>
-       <path class="st3" d="M257.2,81v4.2h-44.1V81H257.2 M258.2,80h-46.1v6.2h46.1V80L258.2,80z"/>
-</g>
-<g>
-       <path class="st3" d="M257.2,86.2v4.2h-44.1v-4.2H257.2 M258.2,85.2h-46.1v6.2h46.1V85.2L258.2,85.2z"/>
-</g>
-<g>
-       <path class="st3" d="M257.2,91.3v4.2h-44.1v-4.2H257.2 M258.2,90.3h-46.1v6.2h46.1V90.3L258.2,90.3z"/>
-</g>
-<ellipse class="st1" cx="218.3" cy="85.7" rx="0" ry="10.2"/>
-<rect x="69.5" y="47.5" class="st4" width="108" height="82.4"/>
-<circle class="st5" cx="123.5" cy="88.7" r="6.6"/>
-<circle class="st5" cx="116.9" cy="106.2" r="6.6"/>
-<circle class="st5" cx="154.5" cy="80.3" r="6.6"/>
-<circle class="st5" cx="93.1" cy="81.5" r="6.6"/>
-<circle class="st5" cx="141.7" cy="114.1" r="6.6"/>
-<circle class="st6" cx="123.5" cy="88.7" r="18.2"/>
-<circle class="st6" cx="123.5" cy="88.7" r="31.7"/>
-</svg>
diff --git a/resources/src/mediawiki/bookletlayout/option4/guide.html b/resources/src/mediawiki/bookletlayout/option4/guide.html
deleted file mode 100644 (file)
index c747851..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<div class="mw-foreignStructuredUpload-bookletLayout-guide">
-       <div class="mw-foreignStructuredUpload-bookletLayout-guide-text-wrapper-good">
-               <span></span>
-       </div>
-       <div class="mw-foreignStructuredUpload-bookletLayout-guide-text-wrapper-bad">
-               <span></span>
-       </div>
-       <div class="mw-foreignStructuredUpload-bookletLayout-guide-image mw-foreignStructuredUpload-bookletLayout-guide-image-camera"></div>
-       <div class="mw-foreignStructuredUpload-bookletLayout-guide-image mw-foreignStructuredUpload-bookletLayout-guide-image-graphics"></div>
-       <div class="mw-foreignStructuredUpload-bookletLayout-guide-image mw-foreignStructuredUpload-bookletLayout-guide-image-website"></div>
-       <div class="mw-foreignStructuredUpload-bookletLayout-guide-image mw-foreignStructuredUpload-bookletLayout-guide-image-search"></div>
-</div>
diff --git a/resources/src/mediawiki/bookletlayout/option4/search-ltr.png b/resources/src/mediawiki/bookletlayout/option4/search-ltr.png
deleted file mode 100644 (file)
index 26d6beb..0000000
Binary files a/resources/src/mediawiki/bookletlayout/option4/search-ltr.png and /dev/null differ
diff --git a/resources/src/mediawiki/bookletlayout/option4/search-ltr.svg b/resources/src/mediawiki/bookletlayout/option4/search-ltr.svg
deleted file mode 100644 (file)
index 67c4ef0..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
-<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
-        viewBox="0 0 264 162" style="enable-background:new 0 0 264 162;" xml:space="preserve">
-<style type="text/css">
-       .st0{display:none;}
-       .st1{display:inline;}
-       .st2{fill:#FFFFFF;}
-       .st3{fill:#E5E5E5;}
-       .st4{opacity:0.5;fill:#E5E5E5;enable-background:new    ;}
-       .st5{fill:#E5E5E5;stroke:#E5E5E5;}
-       .st6{fill:#9B9B9B;}
-       .st7{fill:#4470B6;}
-       .st8{fill:#D0D1D1;}
-       .st9{fill:none;stroke:#E5E5E5;stroke-miterlimit:10;}
-       .st10{fill:none;stroke:#D02227;stroke-miterlimit:10;}
-       .st11{fill:#D02227;}
-</style>
-<g id="Layer_1" class="st0">
-       <g class="st1">
-               <path class="st2" d="M24.8,151.4l-11.5,10l-12.9-9.6V0.5h263v151.7l-9,6.7V42.5h-39v112.6l-4.4-3.7l-7.6,5.3V42.5h-155v110.8
-                       l-9.8,8L24.8,151.4z"/>
-               <path class="st3" d="M263,1v151l-8,6V42h-40v112l-3.3-2.8l-0.6-0.5l-0.6,0.4l-6.5,4.6V42H48v111.1l-9.3,7.7l-13.3-9.6l-0.6-0.5
-                       l-0.6,0.5l-10.9,9.5L1,151.5V1L263,1L263,1z M264,0H0v152l13.4,10l11.5-10l13.9,10l10.2-8.4V43h154v114.7l8.1-5.7l4.9,4.2V43h38
-                       v116.9l10-7.4V0L264,0z M203,43H49v110.6l2.1-1.6l12.6,10l13.8-10l14.2,10l13-10l13,10l13.8-10l12.1,10l13.4-10l14.2,10l13-10
-                       l13,10l5.8-4.3L203,43L203,43z"/>
-               <path class="st4" d="M254,43h-38v113.2l7.6,5.8l13.8-10l13.9,10l2.7-2.1V43z"/>
-               <path class="st5" d="M2,14.5h260"/>
-               <path class="st3" d="M232.5,5h26v6h-26V5z M208.5,5h22v6h-22V5z M142,6v4H50V6H142z M143,5H49v6h94V5z M184.5,5h22v6h-22V5z
-                        M161.5,5h13v6h-13V5z M176.5,5h6v6h-6V5z M153.5,5h6v6h-6V5z M9,5h32v6H9V5z M52,7h2v2h-2V7z M11,36c0-7.7,6.3-14,14-14
-                       s14,6.3,14,14s-6.3,14-14,14S11,43.7,11,36z M38,107V72H13v35H38z M38,59v-5H13v5H38z"/>
-       </g>
-       <g class="st1">
-               <path class="st6" d="M123.8,68H85.6v7.8h38.1L123.8,68L123.8,68z M180.5,103v-8.8h-50.8v8.8H180.5z M90.8,80.6H69.9v8.6h20.8V80.6
-                       z M189.3,88.4v-7.8h-37.7v7.8H189.3z M100.7,94.1H75v7.8h25.7V94.1z M175.2,68H129v7.8h46.2V68z"/>
-               <path class="st7" d="M94.7,80.6v9.2h51.6v-9.2L94.7,80.6z"/>
-       </g>
-       <g class="st1">
-               <path class="st2" d="M94,94h75v44H94V94z"/>
-               <path class="st7" d="M169,94h-31v44h31V94z M150.8,118.1l5.9,9.7l3.2-3l4.8,6.1l-21.1,0l6.2-12.8H150.8z"/>
-               <g transform="scale(-1 1)">
-                       <path class="st8" d="M-102,100h-18c-0.6,0-1,0.2-1,0.5l0,0c0,0.3,0.4,0.5,1,0.5h18c0.6,0,1-0.2,1-0.5l0,0
-                               C-101,100.2-101.4,100-102,100z"/>
-                       <path class="st8" d="M-102,104h-18c-0.6,0-1,0.2-1,0.5l0,0c0,0.3,0.4,0.5,1,0.5h18c0.6,0,1-0.2,1-0.5l0,0
-                               C-101,104.2-101.4,104-102,104z"/>
-                       <path class="st8" d="M-102,108h-18c-0.6,0-1,0.2-1,0.5l0,0c0,0.3,0.4,0.5,1,0.5h18c0.6,0,1-0.2,1-0.5l0,0
-                               C-101,108.2-101.4,108-102,108z"/>
-                       <path class="st8" d="M-102,112h-26c-0.6,0-1,0.2-1,0.5l0,0c0,0.3,0.4,0.5,1,0.5h26c0.6,0,1-0.2,1-0.5l0,0
-                               C-101,112.2-101.4,112-102,112z"/>
-                       <path class="st8" d="M-102,116h-26c-0.6,0-1,0.2-1,0.5l0,0c0,0.3,0.4,0.5,1,0.5h26c0.6,0,1-0.2,1-0.5l0,0
-                               C-101,116.2-101.4,116-102,116z"/>
-                       <path class="st8" d="M-102,120h-26c-0.6,0-1,0.2-1,0.5l0,0c0,0.3,0.4,0.5,1,0.5h26c0.6,0,1-0.2,1-0.5l0,0
-                               C-101,120.2-101.4,120-102,120z"/>
-                       <path class="st8" d="M-102,124h-26c-0.6,0-1,0.2-1,0.5l0,0c0,0.3,0.4,0.5,1,0.5h26c0.6,0,1-0.2,1-0.5l0,0
-                               C-101,124.2-101.4,124-102,124z"/>
-                       <path class="st8" d="M-102,128h-26c-0.6,0-1,0.2-1,0.5l0,0c0,0.3,0.4,0.5,1,0.5h26c0.6,0,1-0.2,1-0.5l0,0
-                               C-101,128.2-101.4,128-102,128z"/>
-               </g>
-       </g>
-</g>
-<g id="Layer_2">
-       <g>
-               <rect x="4.8" y="5.5" class="st2" width="93" height="5"/>
-               <path class="st3" d="M97.2,6v4h-92V6H97.2 M98.2,5h-94v6h94V5L98.2,5z"/>
-       </g>
-       <polygon class="st3" points="177.7,8 176.4,8 109.6,8 108.2,8 106,14 111.8,14 174.2,14 180,14    "/>
-       <polygon class="st3" points="256.5,8 255.2,8 188.3,8 187,8 184.7,14 190.6,14 252.9,14 258.8,14  "/>
-       <line class="st9" x1="2" y1="14.5" x2="262" y2="14.5"/>
-       <line class="st9" x1="2" y1="32.8" x2="262" y2="32.8"/>
-       <g>
-               <circle class="st10" cx="8.9" cy="22.6" r="3.6"/>
-               <line class="st10" x1="11.6" y1="25" x2="15" y2="28.5"/>
-       </g>
-       <rect x="20.9" y="19" class="st3" width="57.6" height="9.5"/>
-       <rect x="4.3" y="37" class="st3" width="69.2" height="32.7"/>
-       <rect x="4.3" y="72.5" class="st3" width="90.1" height="56.8"/>
-       <rect x="97" y="72.5" class="st11" width="106.5" height="56.8"/>
-       <rect x="206.3" y="72.5" class="st3" width="52.4" height="56.8"/>
-       <rect x="4.3" y="132.2" class="st3" width="36.5" height="25.3"/>
-       <rect x="43.7" y="132.2" class="st3" width="62.3" height="25.3"/>
-       <rect x="109.3" y="132.2" class="st3" width="18.1" height="25.3"/>
-       <rect x="169.1" y="132.2" class="st3" width="18.1" height="25.3"/>
-       <rect x="189.8" y="132.2" class="st3" width="18.1" height="25.3"/>
-       <rect x="210.6" y="132.2" class="st3" width="48.1" height="25.3"/>
-       <rect x="130" y="132.2" class="st3" width="36" height="25.3"/>
-       <rect x="115.2" y="37" class="st3" width="36.5" height="32.7"/>
-       <rect x="76.1" y="37" class="st3" width="36.5" height="32.7"/>
-       <rect x="154.6" y="37" class="st3" width="56" height="32.7"/>
-       <rect x="213.3" y="37" class="st3" width="45.4" height="32.7"/>
-       <path class="st2" d="M102.4,115.4l9.5-9.5l12.8,12.8l26.1-26.1l31.8,31.8h-80.3L102.4,115.4z"/>
-</g>
-</svg>
diff --git a/resources/src/mediawiki/bookletlayout/option4/search-rtl.png b/resources/src/mediawiki/bookletlayout/option4/search-rtl.png
deleted file mode 100644 (file)
index 3305928..0000000
Binary files a/resources/src/mediawiki/bookletlayout/option4/search-rtl.png and /dev/null differ
diff --git a/resources/src/mediawiki/bookletlayout/option4/search-rtl.svg b/resources/src/mediawiki/bookletlayout/option4/search-rtl.svg
deleted file mode 100644 (file)
index 17c54d2..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
-<svg version="1.1" id="Layer_2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
-        viewBox="0 0 264 162" style="enable-background:new 0 0 264 162;" xml:space="preserve">
-<style type="text/css">
-       .st0{fill:#FFFFFF;}
-       .st1{fill:#E5E5E5;}
-       .st2{fill:none;stroke:#E5E5E5;stroke-miterlimit:10;}
-       .st3{fill:none;stroke:#D02227;stroke-miterlimit:10;}
-       .st4{fill:#D02227;}
-</style>
-<g>
-       <rect x="166.2" y="5.5" class="st0" width="93" height="5"/>
-       <path class="st1" d="M258.8,6v4h-92V6H258.8 M259.8,5h-94v6h94V5L259.8,5z"/>
-</g>
-<polygon class="st1" points="86.3,8 87.6,8 154.4,8 155.8,8 158,14 152.2,14 89.8,14 84,14 "/>
-<polygon class="st1" points="7.5,8 8.8,8 75.7,8 77,8 79.3,14 73.4,14 11.1,14 5.2,14 "/>
-<line class="st2" x1="262" y1="14.5" x2="2" y2="14.5"/>
-<line class="st2" x1="262" y1="32.8" x2="2" y2="32.8"/>
-<g>
-       <circle class="st3" cx="255.1" cy="22.6" r="3.6"/>
-       <line class="st3" x1="252.4" y1="25" x2="249" y2="28.5"/>
-</g>
-<rect x="185.5" y="19" class="st1" width="57.6" height="9.5"/>
-<rect x="190.5" y="37" class="st1" width="69.2" height="32.7"/>
-<rect x="169.7" y="72.5" class="st1" width="90.1" height="56.8"/>
-<rect x="60.4" y="72.5" class="st4" width="106.5" height="56.8"/>
-<rect x="5.2" y="72.5" class="st1" width="52.4" height="56.8"/>
-<rect x="223.3" y="132.2" class="st1" width="36.5" height="25.3"/>
-<rect x="158" y="132.2" class="st1" width="62.3" height="25.3"/>
-<rect x="136.6" y="132.2" class="st1" width="18.1" height="25.3"/>
-<rect x="76.8" y="132.2" class="st1" width="18.1" height="25.3"/>
-<rect x="56.1" y="132.2" class="st1" width="18.1" height="25.3"/>
-<rect x="5.2" y="132.2" class="st1" width="48.1" height="25.3"/>
-<rect x="97.9" y="132.2" class="st1" width="36" height="25.3"/>
-<rect x="112.3" y="37" class="st1" width="36.5" height="32.7"/>
-<rect x="151.4" y="37" class="st1" width="36.5" height="32.7"/>
-<rect x="53.4" y="37" class="st1" width="56" height="32.7"/>
-<rect x="5.2" y="37" class="st1" width="45.4" height="32.7"/>
-<path class="st0" d="M161.7,124.3H81.4l31.8-31.8l26.1,26.1l12.8-12.8l9.5,9.5L161.7,124.3z"/>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-</svg>
diff --git a/resources/src/mediawiki/bookletlayout/option4/website-ltr.png b/resources/src/mediawiki/bookletlayout/option4/website-ltr.png
deleted file mode 100644 (file)
index 7e00751..0000000
Binary files a/resources/src/mediawiki/bookletlayout/option4/website-ltr.png and /dev/null differ
diff --git a/resources/src/mediawiki/bookletlayout/option4/website-ltr.svg b/resources/src/mediawiki/bookletlayout/option4/website-ltr.svg
deleted file mode 100644 (file)
index ed07c61..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
-<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
-        viewBox="0 0 264 162" style="enable-background:new 0 0 264 162;" xml:space="preserve">
-<style type="text/css">
-       .st0{fill:#FFFFFF;}
-       .st1{fill:#E5E5E5;}
-       .st2{fill:none;stroke:#E5E5E5;stroke-miterlimit:10;}
-       .st3{fill:#D02227;}
-       .st4{fill:#9B9A9A;}
-</style>
-<g>
-       <rect x="4.8" y="5.5" class="st0" width="93" height="5"/>
-       <path class="st1" d="M97.2,6v4h-92V6H97.2 M98.2,5h-94v6h94V5L98.2,5z"/>
-</g>
-<polygon class="st1" points="177.7,8 176.4,8 109.6,8 108.2,8 106,14 111.8,14 174.2,14 180,14 "/>
-<polygon class="st1" points="256.5,8 255.2,8 188.3,8 187,8 184.7,14 190.6,14 252.9,14 258.8,14 "/>
-<line class="st2" x1="2" y1="14.5" x2="262" y2="14.5"/>
-<rect x="44" y="48.4" class="st3" width="106.5" height="56.8"/>
-<path class="st0" d="M49.3,91.2l9.5-9.5l12.8,12.8l26.1-26.1l31.8,31.8H49.3L49.3,91.2z"/>
-<circle class="st1" cx="55" cy="32.3" r="11.9"/>
-<g>
-       <rect x="70.7" y="27.4" class="st4" width="76.2" height="7"/>
-       <rect x="70.7" y="35.5" class="st4" width="22.3" height="1.7"/>
-       <rect x="95" y="35.5" class="st4" width="28.2" height="1.7"/>
-</g>
-<rect x="44" y="107.8" class="st1" width="23.3" height="23.3"/>
-<rect x="70.7" y="107.8" class="st1" width="79.8" height="1.8"/>
-<rect x="70.7" y="114" class="st1" width="79.8" height="1.8"/>
-<rect x="70.7" y="120.2" class="st1" width="79.8" height="1.8"/>
-<rect x="70.7" y="126.4" class="st1" width="79.8" height="1.8"/>
-<rect x="44" y="134.4" class="st1" width="106.5" height="1.8"/>
-<rect x="44" y="140.6" class="st1" width="106.5" height="1.8"/>
-<rect x="44" y="146.8" class="st1" width="106.5" height="1.8"/>
-<rect x="44" y="153" class="st1" width="106.5" height="1.8"/>
-<g>
-       <rect x="155.7" y="37.2" class="st1" width="57.3" height="1.8"/>
-       <rect x="155.7" y="43.4" class="st1" width="57.3" height="1.8"/>
-       <rect x="155.7" y="49.6" class="st1" width="57.3" height="1.8"/>
-       <rect x="155.7" y="55.8" class="st1" width="57.3" height="1.8"/>
-       <rect x="155.7" y="27.4" class="st1" width="57.3" height="7"/>
-</g>
-<g>
-       <rect x="155.7" y="78.2" class="st1" width="57.3" height="1.8"/>
-       <rect x="155.7" y="84.4" class="st1" width="57.3" height="1.8"/>
-       <rect x="155.7" y="90.6" class="st1" width="57.3" height="1.8"/>
-       <rect x="155.7" y="96.8" class="st1" width="57.3" height="1.8"/>
-       <rect x="155.7" y="68.4" class="st1" width="57.3" height="7"/>
-</g>
-<g>
-       <rect x="155.7" y="116.7" class="st1" width="57.3" height="1.8"/>
-       <rect x="155.7" y="122.9" class="st1" width="57.3" height="1.8"/>
-       <rect x="155.7" y="129.1" class="st1" width="57.3" height="1.8"/>
-       <rect x="155.7" y="135.3" class="st1" width="57.3" height="1.8"/>
-       <rect x="155.7" y="106.9" class="st1" width="57.3" height="7"/>
-</g>
-</svg>
diff --git a/resources/src/mediawiki/bookletlayout/option4/website-rtl.png b/resources/src/mediawiki/bookletlayout/option4/website-rtl.png
deleted file mode 100644 (file)
index 83ba6d3..0000000
Binary files a/resources/src/mediawiki/bookletlayout/option4/website-rtl.png and /dev/null differ
diff --git a/resources/src/mediawiki/bookletlayout/option4/website-rtl.svg b/resources/src/mediawiki/bookletlayout/option4/website-rtl.svg
deleted file mode 100644 (file)
index dd8b0f0..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
-<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
-        viewBox="0 0 264 162" style="enable-background:new 0 0 264 162;" xml:space="preserve">
-<style type="text/css">
-       .st0{fill:#FFFFFF;}
-       .st1{fill:#E5E5E5;}
-       .st2{fill:none;stroke:#E5E5E5;stroke-miterlimit:10;}
-       .st3{fill:#D02227;}
-       .st4{fill:#9B9A9A;}
-</style>
-<g>
-       <rect x="166.2" y="5.5" class="st0" width="93" height="5"/>
-       <path class="st1" d="M258.8,6v4h-92V6H258.8 M259.8,5h-94v6h94V5L259.8,5z"/>
-</g>
-<polygon class="st1" points="86.3,8 87.6,8 154.4,8 155.8,8 158,14 152.2,14 89.8,14 84,14 "/>
-<polygon class="st1" points="7.5,8 8.8,8 75.7,8 77,8 79.3,14 73.4,14 11.1,14 5.2,14 "/>
-<line class="st2" x1="262" y1="14.5" x2="2" y2="14.5"/>
-<rect x="113.5" y="48.4" class="st3" width="106.5" height="56.8"/>
-<path class="st0" d="M214.7,100.2h-80.3l31.8-31.8l26.1,26.1l12.8-12.8l9.5,9.5L214.7,100.2z"/>
-<circle class="st1" cx="209" cy="32.3" r="11.9"/>
-<g>
-       <rect x="117" y="27.4" class="st4" width="76.2" height="7"/>
-       <rect x="171" y="35.5" class="st4" width="22.3" height="1.7"/>
-       <rect x="140.8" y="35.5" class="st4" width="28.2" height="1.7"/>
-</g>
-<rect x="196.7" y="107.8" class="st1" width="23.3" height="23.3"/>
-<rect x="113.5" y="107.8" class="st1" width="79.8" height="1.8"/>
-<rect x="113.5" y="114" class="st1" width="79.8" height="1.8"/>
-<rect x="113.5" y="120.2" class="st1" width="79.8" height="1.8"/>
-<rect x="113.5" y="126.4" class="st1" width="79.8" height="1.8"/>
-<rect x="113.5" y="134.4" class="st1" width="106.5" height="1.8"/>
-<rect x="113.5" y="140.6" class="st1" width="106.5" height="1.8"/>
-<rect x="113.5" y="146.8" class="st1" width="106.5" height="1.8"/>
-<rect x="113.5" y="153" class="st1" width="106.5" height="1.8"/>
-<g>
-       <rect x="51" y="37.2" class="st1" width="57.3" height="1.8"/>
-       <rect x="51" y="43.4" class="st1" width="57.3" height="1.8"/>
-       <rect x="51" y="49.6" class="st1" width="57.3" height="1.8"/>
-       <rect x="51" y="55.8" class="st1" width="57.3" height="1.8"/>
-       <rect x="51" y="27.4" class="st1" width="57.3" height="7"/>
-</g>
-<g>
-       <rect x="51" y="78.2" class="st1" width="57.3" height="1.8"/>
-       <rect x="51" y="84.4" class="st1" width="57.3" height="1.8"/>
-       <rect x="51" y="90.6" class="st1" width="57.3" height="1.8"/>
-       <rect x="51" y="96.8" class="st1" width="57.3" height="1.8"/>
-       <rect x="51" y="68.4" class="st1" width="57.3" height="7"/>
-</g>
-<g>
-       <rect x="51" y="116.7" class="st1" width="57.3" height="1.8"/>
-       <rect x="51" y="122.9" class="st1" width="57.3" height="1.8"/>
-       <rect x="51" y="129.1" class="st1" width="57.3" height="1.8"/>
-       <rect x="51" y="135.3" class="st1" width="57.3" height="1.8"/>
-       <rect x="51" y="106.9" class="st1" width="57.3" height="7"/>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-</svg>
index 97d81fb..23e80b9 100644 (file)
         * @inheritdoc
         */
        mw.ForeignStructuredUpload.BookletLayout.prototype.renderUploadForm = function () {
-               var
-                       query = /[?&]uploadbucket=(\d)/.exec( location.search ),
-                       isTestEnabled = !!mw.config.get( 'wgForeignUploadTestEnabled' ),
-                       defaultBucket = mw.config.get( 'wgForeignUploadTestDefault' ) || 1,
-                       userId = mw.config.get( 'wgUserId' );
-
-               if ( query && query[ 1 ] ) {
-                       // Testing and debugging
-                       this.shouldRecordBucket = false;
-                       this.bucket = Number( query[ 1 ] );
-               } else if ( !userId || !isTestEnabled ) {
-                       // a) Anonymous user. This can actually happen, because our software sucks.
-                       // b) Test is not enabled on this wiki.
-                       // In either case, display the old interface and don't record bucket on uploads.
-                       this.shouldRecordBucket = false;
-                       this.bucket = defaultBucket;
-               } else {
-                       // Regular logged in user on a wiki where the test is running
-                       this.shouldRecordBucket = true;
-                       this.bucket = ( userId % 4 ) + 1; // 1, 2, 3, 4
-               }
-
-               return this[ 'renderUploadForm' + this.bucket ]();
-       };
-
-       /**
-        * Test option 1, the original one. See T120867.
-        */
-       mw.ForeignStructuredUpload.BookletLayout.prototype.renderUploadForm1 = function () {
                var fieldset, $ownWorkMessage, $notOwnWorkMessage,
-                       onUploadFormChange,
                        ownWorkMessage, notOwnWorkMessage, notOwnWorkLocal,
                        validTargets = mw.config.get( 'wgForeignUploadTargets' ),
                        target = this.target || validTargets[ 0 ] || 'local',
                        layout = this;
 
-               // Temporary override to make my life easier during A/B test
-               target = 'shared';
-
                // foreign-structured-upload-form-label-own-work-message-local
                // foreign-structured-upload-form-label-own-work-message-shared
                ownWorkMessage = mw.message( 'foreign-structured-upload-form-label-own-work-message-' + target );
                        $( '<p>' ).append( notOwnWorkMessage.parseDom() ),
                        $( '<p>' ).append( notOwnWorkLocal.parseDom() )
                );
-               $ownWorkMessage.add( $notOwnWorkMessage ).find( 'a' )
-                       .attr( 'target', '_blank' )
-                       .on( 'click', function ( e ) {
-                               // Some stupid code is trying to prevent default on all clicks, which causes the links to
-                               // not be openable, don't let it
-                               e.stopPropagation();
-                       } );
+               $ownWorkMessage.add( $notOwnWorkMessage ).find( 'a' ).attr( 'target', '_blank' );
 
-               this.selectFileWidget = new OO.ui.SelectFileWidget();
+               this.selectFileWidget = new OO.ui.SelectFileWidget( {
+                       showDropTarget: true
+               } );
                this.messageLabel = new OO.ui.LabelWidget( {
                        label: $notOwnWorkMessage
                } );
                fieldset = new OO.ui.FieldsetLayout();
                fieldset.addItems( [
                        new OO.ui.FieldLayout( this.selectFileWidget, {
-                               align: 'top',
-                               label: mw.msg( 'upload-form-label-select-file' )
+                               align: 'top'
                        } ),
                        new OO.ui.FieldLayout( this.ownWorkCheckbox, {
                                align: 'inline',
                ] );
                this.uploadForm = new OO.ui.FormLayout( { items: [ fieldset ] } );
 
-               onUploadFormChange = function () {
-                       var file = this.selectFileWidget.getValue(),
-                               ownWork = this.ownWorkCheckbox.isSelected(),
-                               valid = !!file && ownWork;
-                       this.emit( 'uploadValid', valid );
-               };
-
                // Validation
-               this.selectFileWidget.on( 'change', onUploadFormChange.bind( this ) );
-               this.ownWorkCheckbox.on( 'change', onUploadFormChange.bind( this ) );
+               this.selectFileWidget.on( 'change', this.onUploadFormChange.bind( this ) );
+               this.ownWorkCheckbox.on( 'change', this.onUploadFormChange.bind( this ) );
 
                this.selectFileWidget.on( 'change', function () {
                        var file = layout.getFile();
                        layout.getDateFromExif( file ).done( function ( date ) {
                                layout.dateWidget.setValue( date );
                        } );
-               } );
-
-               return this.uploadForm;
-       };
-
-       /**
-        * Test option 2, idea A from T121021. See T120867.
-        */
-       mw.ForeignStructuredUpload.BookletLayout.prototype.renderUploadForm2 = function () {
-               var fieldset, checkboxes, fields, onUploadFormChange;
-
-               this.selectFileWidget = new OO.ui.SelectFileWidget();
-               this.licenseCheckboxes = checkboxes = [
-                       new OO.ui.CheckboxInputWidget(),
-                       new OO.ui.CheckboxInputWidget(),
-                       new OO.ui.CheckboxInputWidget(),
-                       new OO.ui.CheckboxInputWidget()
-               ];
-
-               fields = [
-                       new OO.ui.FieldLayout( this.selectFileWidget, {
-                               align: 'top',
-                               label: mw.msg( 'upload-form-label-select-file' )
-                       } ),
-                       new OO.ui.FieldLayout( new OO.ui.LabelWidget( {
-                               label: mw.message( 'foreign-structured-upload-form-2-label-intro' ).parseDom()
-                       } ), {
-                               align: 'top'
-                       } ),
-                       new OO.ui.FieldLayout( checkboxes[ 0 ], {
-                               align: 'inline',
-                               classes: [
-                                       'mw-foreignStructuredUpload-bookletLayout-withicon',
-                                       'mw-foreignStructuredUpload-bookletLayout-ownwork'
-                               ],
-                               label: mw.message( 'foreign-structured-upload-form-2-label-ownwork' ).parseDom()
-                       } ),
-                       new OO.ui.FieldLayout( checkboxes[ 1 ], {
-                               align: 'inline',
-                               classes: [
-                                       'mw-foreignStructuredUpload-bookletLayout-withicon',
-                                       'mw-foreignStructuredUpload-bookletLayout-noderiv'
-                               ],
-                               label: mw.message( 'foreign-structured-upload-form-2-label-noderiv' ).parseDom()
-                       } ),
-                       new OO.ui.FieldLayout( checkboxes[ 2 ], {
-                               align: 'inline',
-                               classes: [
-                                       'mw-foreignStructuredUpload-bookletLayout-withicon',
-                                       'mw-foreignStructuredUpload-bookletLayout-useful'
-                               ],
-                               label: mw.message( 'foreign-structured-upload-form-2-label-useful' ).parseDom()
-                       } ),
-                       new OO.ui.FieldLayout( checkboxes[ 3 ], {
-                               align: 'inline',
-                               classes: [
-                                       'mw-foreignStructuredUpload-bookletLayout-withicon',
-                                       'mw-foreignStructuredUpload-bookletLayout-ccbysa'
-                               ],
-                               label: mw.message( 'foreign-structured-upload-form-2-label-ccbysa' ).parseDom()
-                       } ),
-                       new OO.ui.FieldLayout( new OO.ui.LabelWidget( {
-                               label: $()
-                                       .add( $( '<p>' ).msg( 'foreign-structured-upload-form-2-label-alternative' ) )
-                                       .add( $( '<p>' ).msg( 'foreign-structured-upload-form-2-label-termsofuse' )
-                                               .addClass( 'mw-foreignStructuredUpload-bookletLayout-license' ) )
-                       } ), {
-                               align: 'top'
-                       } )
-               ];
-
-               fieldset = new OO.ui.FieldsetLayout( { items: fields } );
-               this.uploadForm = new OO.ui.FormLayout( { items: [ fieldset ] } );
-
-               this.uploadForm.$element.find( 'a' )
-                       .attr( 'target', '_blank' )
-                       .on( 'click', function ( e ) {
-                               // Some stupid code is trying to prevent default on all clicks, which causes the links to
-                               // not be openable, don't let it
-                               e.stopPropagation();
-                       } );
-
-               onUploadFormChange = function () {
-                       var file = this.selectFileWidget.getValue(),
-                               checks = checkboxes.every( function ( checkbox ) {
-                                       return checkbox.isSelected();
-                               } ),
-                               valid = !!file && checks;
-                       this.emit( 'uploadValid', valid );
-               };
-
-               // Validation
-               this.selectFileWidget.on( 'change', onUploadFormChange.bind( this ) );
-               checkboxes[ 0 ].on( 'change', onUploadFormChange.bind( this ) );
-               checkboxes[ 1 ].on( 'change', onUploadFormChange.bind( this ) );
-               checkboxes[ 2 ].on( 'change', onUploadFormChange.bind( this ) );
-               checkboxes[ 3 ].on( 'change', onUploadFormChange.bind( this ) );
-
-               return this.uploadForm;
-       };
-
-       /**
-        * Test option 3, idea D from T121021. See T120867.
-        */
-       mw.ForeignStructuredUpload.BookletLayout.prototype.renderUploadForm3 = function () {
-               var ownWorkCheckbox, fieldset, yesMsg, noMsg, selects, selectFields,
-                       alternativeField, fields, onUploadFormChange;
-
-               this.selectFileWidget = new OO.ui.SelectFileWidget();
-               this.ownWorkCheckbox = ownWorkCheckbox = new OO.ui.CheckboxInputWidget();
-
-               yesMsg = mw.message( 'foreign-structured-upload-form-3-label-yes' ).text();
-               noMsg = mw.message( 'foreign-structured-upload-form-3-label-no' ).text();
-               selects = [
-                       new OO.ui.RadioSelectWidget( {
-                               items: [
-                                       new OO.ui.RadioOptionWidget( { data: false, label: yesMsg } ),
-                                       new OO.ui.RadioOptionWidget( { data: true, label: noMsg } )
-                               ]
-                       } ),
-                       new OO.ui.RadioSelectWidget( {
-                               items: [
-                                       new OO.ui.RadioOptionWidget( { data: true, label: yesMsg } ),
-                                       new OO.ui.RadioOptionWidget( { data: false, label: noMsg } )
-                               ]
-                       } ),
-                       new OO.ui.RadioSelectWidget( {
-                               items: [
-                                       new OO.ui.RadioOptionWidget( { data: false, label: yesMsg } ),
-                                       new OO.ui.RadioOptionWidget( { data: true, label: noMsg } )
-                               ]
-                       } )
-               ];
 
-               this.licenseSelectFields = selectFields = [
-                       new OO.ui.FieldLayout( selects[ 0 ], {
-                               align: 'top',
-                               classes: [ 'mw-foreignStructuredUpload-bookletLayout-question' ],
-                               label: mw.message( 'foreign-structured-upload-form-3-label-question-website' ).parseDom()
-                       } ),
-                       new OO.ui.FieldLayout( selects[ 1 ], {
-                               align: 'top',
-                               classes: [ 'mw-foreignStructuredUpload-bookletLayout-question' ],
-                               label: mw.message( 'foreign-structured-upload-form-3-label-question-ownwork' ).parseDom()
-                       } ).toggle( false ),
-                       new OO.ui.FieldLayout( selects[ 2 ], {
-                               align: 'top',
-                               classes: [ 'mw-foreignStructuredUpload-bookletLayout-question' ],
-                               label: mw.message( 'foreign-structured-upload-form-3-label-question-noderiv' ).parseDom()
-                       } ).toggle( false )
-               ];
-
-               alternativeField = new OO.ui.FieldLayout( new OO.ui.LabelWidget( {
-                       label: mw.message( 'foreign-structured-upload-form-3-label-alternative' ).parseDom()
-               } ), {
-                       align: 'top'
-               } ).toggle( false );
-
-               // Choosing the right answer to each question shows the next question.
-               // Switching to wrong answer hides all subsequent questions.
-               selects.forEach( function ( select, i ) {
-                       select.on( 'choose', function ( selectedOption ) {
-                               var isRightAnswer = !!selectedOption.getData();
-                               alternativeField.toggle( !isRightAnswer );
-                               if ( i + 1 === selectFields.length ) {
-                                       // Last question
-                                       return;
-                               }
-                               if ( isRightAnswer ) {
-                                       selectFields[ i + 1 ].toggle( true );
-                               } else {
-                                       selectFields.slice( i + 1 ).forEach( function ( field ) {
-                                               field.fieldWidget.selectItem( null );
-                                               field.toggle( false );
-                                       } );
-                               }
-                       } );
+                       layout.updateFilePreview();
                } );
 
-               fields = [
-                       new OO.ui.FieldLayout( this.selectFileWidget, {
-                               align: 'top',
-                               label: mw.msg( 'upload-form-label-select-file' )
-                       } ),
-                       selectFields[ 0 ],
-                       selectFields[ 1 ],
-                       selectFields[ 2 ],
-                       alternativeField,
-                       new OO.ui.FieldLayout( ownWorkCheckbox, {
-                               classes: [ 'mw-foreignStructuredUpload-bookletLayout-checkbox' ],
-                               align: 'inline',
-                               label: mw.message( 'foreign-structured-upload-form-label-own-work-message-shared' ).parseDom()
-                       } )
-               ];
-
-               // Must be done late, after it's been associated with the FieldLayout
-               ownWorkCheckbox.setDisabled( true );
-
-               fieldset = new OO.ui.FieldsetLayout( { items: fields } );
-               this.uploadForm = new OO.ui.FormLayout( { items: [ fieldset ] } );
-
-               this.uploadForm.$element.find( 'a' )
-                       .attr( 'target', '_blank' )
-                       .on( 'click', function ( e ) {
-                               // Some stupid code is trying to prevent default on all clicks, which causes the links to
-                               // not be openable, don't let it
-                               e.stopPropagation();
-                       } );
-
-               onUploadFormChange = function () {
-                       var file = this.selectFileWidget.getValue(),
-                               checkbox = ownWorkCheckbox.isSelected(),
-                               rightAnswers = selects.every( function ( select ) {
-                                       return select.getSelectedItem() && !!select.getSelectedItem().getData();
-                               } ),
-                               valid = !!file && checkbox && rightAnswers;
-                       ownWorkCheckbox.setDisabled( !rightAnswers );
-                       if ( !rightAnswers ) {
-                               ownWorkCheckbox.setSelected( false );
-                       }
-                       this.emit( 'uploadValid', valid );
-               };
-
-               // Validation
-               this.selectFileWidget.on( 'change', onUploadFormChange.bind( this ) );
-               this.ownWorkCheckbox.on( 'change', onUploadFormChange.bind( this ) );
-               selects[ 0 ].on( 'choose', onUploadFormChange.bind( this ) );
-               selects[ 1 ].on( 'choose', onUploadFormChange.bind( this ) );
-               selects[ 2 ].on( 'choose', onUploadFormChange.bind( this ) );
-
-               return this.uploadForm;
-       };
-
-       /**
-        * Test option 4, idea E from T121021. See T120867.
-        */
-       mw.ForeignStructuredUpload.BookletLayout.prototype.renderUploadForm4 = function () {
-               var fieldset, $guide;
-               this.renderUploadForm1();
-               fieldset = this.uploadForm.getItems()[ 0 ];
-
-               $guide = mw.template.get( 'mediawiki.ForeignStructuredUpload.BookletLayout', 'guide.html' ).render();
-               $guide.find( '.mw-foreignStructuredUpload-bookletLayout-guide-text-wrapper-good span' )
-                       .msg( 'foreign-structured-upload-form-4-label-good' );
-               $guide.find( '.mw-foreignStructuredUpload-bookletLayout-guide-text-wrapper-bad span' )
-                       .msg( 'foreign-structured-upload-form-4-label-bad' );
-
-               // Note the index, we insert after the SelectFileWidget field
-               fieldset.addItems( [
-                       new OO.ui.FieldLayout( new OO.ui.Widget( {
-                               $content: $guide
-                       } ), {
-                               align: 'top'
-                       } )
-               ], 1 );
-
-               // Hook for custom styles
-               fieldset.getItems()[ 2 ].$element.addClass( 'mw-foreignStructuredUpload-bookletLayout-guide-checkbox' );
-
-               // Streamline: remove mention of local Special:Upload
-               fieldset.getItems()[ 3 ].$element.find( 'p' ).last().remove();
-
                return this.uploadForm;
        };
 
        /**
         * @inheritdoc
         */
-       mw.ForeignStructuredUpload.BookletLayout.prototype.onUploadFormChange = function () {};
+       mw.ForeignStructuredUpload.BookletLayout.prototype.onUploadFormChange = function () {
+               var file = this.selectFileWidget.getValue(),
+                       ownWork = this.ownWorkCheckbox.isSelected(),
+                       valid = !!file && ownWork;
+               this.emit( 'uploadValid', valid );
+       };
 
        /**
         * @inheritdoc
        mw.ForeignStructuredUpload.BookletLayout.prototype.renderInfoForm = function () {
                var fieldset;
 
+               this.filePreview = new OO.ui.Widget( {
+                       classes: [ 'mw-upload-bookletLayout-filePreview' ]
+               } );
                this.filenameWidget = new OO.ui.TextInputWidget( {
                        required: true,
                        validate: /.+/
                        new OO.ui.FieldLayout( this.filenameWidget, {
                                label: mw.msg( 'upload-form-label-infoform-name' ),
                                align: 'top',
-                               help: mw.msg( 'upload-form-label-infoform-name-tooltip' )
+                               classes: [ 'mw-foreignStructuredUploa-bookletLayout-small-notice' ],
+                               notices: [ mw.msg( 'upload-form-label-infoform-name-tooltip' ) ]
                        } ),
                        new OO.ui.FieldLayout( this.descriptionWidget, {
                                label: mw.msg( 'upload-form-label-infoform-description' ),
                                align: 'top',
-                               help: mw.msg( 'upload-form-label-infoform-description-tooltip' )
+                               classes: [ 'mw-foreignStructuredUploa-bookletLayout-small-notice' ],
+                               notices: [ mw.msg( 'upload-form-label-infoform-description-tooltip' ) ]
                        } ),
                        new OO.ui.FieldLayout( this.categoriesWidget, {
                                label: mw.msg( 'foreign-structured-upload-form-label-infoform-categories' ),
                                align: 'top'
                        } )
                ] );
-               this.infoForm = new OO.ui.FormLayout( { items: [ fieldset ] } );
+               this.infoForm = new OO.ui.FormLayout( {
+                       classes: [ 'mw-upload-bookletLayout-infoForm' ],
+                       items: [ this.filePreview, fieldset ]
+               } );
 
                // Validation
                this.filenameWidget.on( 'change', this.onInfoFormChange.bind( this ) );
        mw.ForeignStructuredUpload.BookletLayout.prototype.clear = function () {
                mw.ForeignStructuredUpload.BookletLayout.parent.prototype.clear.call( this );
 
-               if ( this.ownWorkCheckbox ) {
-                       this.ownWorkCheckbox.setSelected( false );
-               }
-               if ( this.licenseCheckboxes ) {
-                       this.licenseCheckboxes.forEach( function ( checkbox ) {
-                               checkbox.setSelected( false );
-                       } );
-               }
-               if ( this.licenseSelectFields ) {
-                       this.licenseSelectFields.forEach( function ( field, i ) {
-                               field.fieldWidget.selectItem( null );
-                               if ( i !== 0 ) {
-                                       field.toggle( false );
-                               }
-                       } );
-               }
-
+               this.ownWorkCheckbox.setSelected( false );
                this.categoriesWidget.setItemsFromData( [] );
                this.dateWidget.setValue( '' ).setValidityFlag( true );
        };
index fa15a2e..0021caf 100644 (file)
-@import "mediawiki.mixins";
-
-/* All */
-
 .mw-foreignStructuredUpload-bookletLayout-license {
        font-size: 90%;
        line-height: 1.4em;
        color: #555;
 }
 
-/* Option 2 */
-
-.mw-foreignStructuredUpload-bookletLayout-withicon.oo-ui-fieldLayout.oo-ui-fieldLayout-align-inline {
-       background-repeat: no-repeat;
-       background-size: 3.5em;
-       min-height: 4em;
-       margin-bottom: 0.25em;
-       display: table;
-       vertical-align: middle;
-       -webkit-box-sizing: border-box;
-       -moz-box-sizing: border-box;
-       box-sizing: border-box;
-       width: 100%;
-}
-
-.mw-foreignStructuredUpload-bookletLayout-withicon.oo-ui-fieldLayout.oo-ui-fieldLayout-align-inline > .oo-ui-fieldLayout-body {
-       /* Together with 'display: table' above, and FieldLayout styles, this aligns the text */
-       /* vertically to the middle. Don't try this at home, kids. */
-       display: table-row;
-}
+.mw-foreignStructuredUploa-bookletLayout-small-notice {
+       .oo-ui-fieldLayout-messages-notice {
+               .oo-ui-iconWidget {
+                       display: none;
+               }
 
-.mw-foreignStructuredUpload-bookletLayout-ownwork {
-       .background-image-svg('bookletlayout/option2/ownwork.svg', 'bookletlayout/option2/ownwork.png');
-       background-position: right center;
-       padding-right: 4.5em;
-}
-
-.mw-foreignStructuredUpload-bookletLayout-noderiv {
-       .background-image-svg('bookletlayout/option2/noderiv.svg', 'bookletlayout/option2/noderiv.png');
-       background-position: right center;
-       padding-right: 4.5em;
-}
-
-.mw-foreignStructuredUpload-bookletLayout-useful {
-       .background-image-svg('bookletlayout/option2/useful.svg', 'bookletlayout/option2/useful.png');
-       background-position: right center;
-       padding-right: 4.5em;
-}
-
-.mw-foreignStructuredUpload-bookletLayout-ccbysa {
-       .background-image-svg('bookletlayout/option2/ccbysa.svg', 'bookletlayout/option2/ccbysa.png');
-       background-position: right center;
-       padding-right: 4.5em;
-}
-
-/* Option 3 */
-
-.mw-foreignStructuredUpload-bookletLayout-question .oo-ui-radioOptionWidget {
-       display: inline-block;
-       margin-right: 2em;
-}
-
-.mw-foreignStructuredUpload-bookletLayout-checkbox.oo-ui-fieldLayout-disabled > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label {
-       /* No unreadable greys, please. This is the lightest WCAG AA compliant grey. */
-       color: #707070;
-}
-
-/* Option 4 */
-
-.mw-foreignStructuredUpload-bookletLayout-guide {
-       position: relative;
-       height: 315px;
-}
-
-.mw-foreignStructuredUpload-bookletLayout-guide-text-wrapper-good,
-.mw-foreignStructuredUpload-bookletLayout-guide-text-wrapper-bad {
-       display: table;
-       width: 150px;
-       height: 140px;
-       position: absolute;
-}
-
-.mw-foreignStructuredUpload-bookletLayout-guide-text-wrapper-good span,
-.mw-foreignStructuredUpload-bookletLayout-guide-text-wrapper-bad span {
-       display: table-cell;
-       vertical-align: middle;
-}
-
-.mw-foreignStructuredUpload-bookletLayout-guide-text-wrapper-good {
-       top: 0;
-       left: 0;
-}
-
-.mw-foreignStructuredUpload-bookletLayout-guide-text-wrapper-bad {
-       bottom: 0;
-       right: 0;
-}
-
-.mw-foreignStructuredUpload-bookletLayout-guide-image {
-       position: absolute;
-       width: 200px;
-       height: 122px;
-       background-color: #fff;
-       background-size: 200px;
-       background-repeat: no-repeat;
-       border: 1px solid #e5e5e5;
-}
-
-.mw-foreignStructuredUpload-bookletLayout-guide-image-camera {
-       .background-image-svg('bookletlayout/option4/camera.svg','bookletlayout/option4/camera.png');
-       top: 0;
-       right: 80px;
-}
-
-.mw-foreignStructuredUpload-bookletLayout-guide-image-graphics {
-       .background-image-svg('bookletlayout/option4/graphics.svg','bookletlayout/option4/graphics.png');
-       top: 50px;
-       right: 0;
-}
-
-.mw-foreignStructuredUpload-bookletLayout-guide-image-website {
-       .background-image-svg('bookletlayout/option4/website-ltr.svg','bookletlayout/option4/website-ltr.png');
-       left: 0;
-       bottom: 50px;
-}
-
-.mw-foreignStructuredUpload-bookletLayout-guide-image-search {
-       .background-image-svg('bookletlayout/option4/search-ltr.svg','bookletlayout/option4/search-ltr.png');
-       left: 80px;
-       bottom: 0;
-}
-
-.oo-ui-fieldLayout.oo-ui-fieldLayout-align-inline.mw-foreignStructuredUpload-bookletLayout-guide-checkbox {
-       /* We're really tight on vertical space. */
-       margin-bottom: 0;
-}
+               .oo-ui-labelWidget {
+                       line-height: 1.2em;
+                       font-size: 0.9em;
+                       color: #555;
+               }
+       }
+}
\ No newline at end of file
index ec2a4b1..ffb3041 100644 (file)
@@ -6,17 +6,15 @@
 ( function ( mw, $ ) {
        /*jshint latedef:false */
 
-       // jscs:disable jsDoc
        /**
-        * @class mw.Title
-        *
         * Parse titles into an object structure. Note that when using the constructor
         * directly, passing invalid titles will result in an exception. Use #newFromText to use the
         * logic directly and get null for invalid titles which is easier to work with.
         *
-        * @constructor
-        *
-        * Note that in the constructor amd #newFromText method, `namespace` is the **default** namespace
+        * @class mw.Title
+        */
+       /**
+        * Note that in the constructor and #newFromText method, `namespace` is the **default** namespace
         * only, and can be overridden by a namespace prefix in `title`. If you do not want this behavior,
         * use #makeTitle. Compare:
         *
@@ -32,6 +30,7 @@
         *     mw.Title.newFromText( 'Template:Foo', NS_TEMPLATE ).getPrefixedText(); // => 'Template:Foo'
         *     mw.Title.makeTitle( NS_TEMPLATE, 'Template:Foo' ).getPrefixedText();   // => 'Template:Template:Foo'
         *
+        * @method constructor
         * @param {string} title Title of the page. If no second argument given,
         *  this will be searched for a namespace
         * @param {number} [namespace=NS_MAIN] If given, will used as default namespace for the given title
@@ -50,7 +49,6 @@
 
                return this;
        }
-       // jscs:enable jsDoc
 
        /* Private members */
 
diff --git a/resources/src/mediawiki/mediawiki.Upload.BookletLayout.css b/resources/src/mediawiki/mediawiki.Upload.BookletLayout.css
new file mode 100644 (file)
index 0000000..11bad8c
--- /dev/null
@@ -0,0 +1,19 @@
+.mw-upload-bookletLayout-filePreview {
+       width: 100%;
+       height: 1em;
+       background-color: #eee;
+       background-size: cover;
+       background-position: center center;
+       padding: 1.5em;
+       margin: -1.5em;
+       margin-bottom: 1.5em;
+}
+
+.mw-upload-bookletLayout-infoForm.mw-upload-bookletLayout-hasThumbnail .mw-upload-bookletLayout-filePreview {
+       height: 10em;
+}
+
+.mw-upload-bookletLayout-filePreview p {
+       line-height: 1em;
+       margin: 0;
+}
\ No newline at end of file
index c8d64b9..eaab8c7 100644 (file)
 
                this.setPage( 'info' );
 
-               if ( this.shouldRecordBucket ) {
-                       this.upload.bucket = this.bucket;
-               }
-
                this.upload.setFile( file );
                // The original file name might contain invalid characters, so use our sanitized one
                this.upload.setFilename( this.getFilename() );
         * @return {OO.ui.FormLayout}
         */
        mw.Upload.BookletLayout.prototype.renderUploadForm = function () {
-               var fieldset;
+               var fieldset,
+                       layout = this;
 
-               this.selectFileWidget = new OO.ui.SelectFileWidget();
-               fieldset = new OO.ui.FieldsetLayout( { label: mw.msg( 'upload-form-label-select-file' ) } );
+               this.selectFileWidget = new OO.ui.SelectFileWidget( {
+                       showDropTarget: true
+               } );
+               fieldset = new OO.ui.FieldsetLayout();
                fieldset.addItems( [ this.selectFileWidget ] );
                this.uploadForm = new OO.ui.FormLayout( { items: [ fieldset ] } );
 
                // Validation
                this.selectFileWidget.on( 'change', this.onUploadFormChange.bind( this ) );
 
+               this.selectFileWidget.on( 'change', function () {
+                       layout.updateFilePreview();
+               } );
+
                return this.uploadForm;
        };
 
+       /**
+        * Updates the file preview on the info form when a file is added.
+        *
+        * @protected
+        */
+       mw.Upload.BookletLayout.prototype.updateFilePreview = function () {
+               this.selectFileWidget.loadAndGetImageUrl().done( function ( url ) {
+                       this.filePreview.$element.find( 'p' ).remove();
+                       this.filePreview.$element.css( 'background-image', 'url(' + url + ')' );
+                       this.infoForm.$element.addClass( 'mw-upload-bookletLayout-hasThumbnail' );
+               }.bind( this ) ).fail( function () {
+                       this.filePreview.$element.find( 'p' ).remove();
+                       if ( this.selectFileWidget.getValue() ) {
+                               this.filePreview.$element.append(
+                                       $( '<p>' ).text( this.selectFileWidget.getValue().name )
+                               );
+                       }
+                       this.filePreview.$element.css( 'background-image', '' );
+                       this.infoForm.$element.removeClass( 'mw-upload-bookletLayout-hasThumbnail' );
+               }.bind( this ) );
+       };
+
        /**
         * Handle change events to the upload form
         *
        mw.Upload.BookletLayout.prototype.renderInfoForm = function () {
                var fieldset;
 
+               this.filePreview = new OO.ui.Widget( {
+                       classes: [ 'mw-upload-bookletLayout-filePreview' ]
+               } );
                this.filenameWidget = new OO.ui.TextInputWidget( {
                        indicator: 'required',
                        required: true,
                                help: mw.msg( 'upload-form-label-infoform-description-tooltip' )
                        } )
                ] );
-               this.infoForm = new OO.ui.FormLayout( { items: [ fieldset ] } );
+               this.infoForm = new OO.ui.FormLayout( {
+                       classes: [ 'mw-upload-bookletLayout-infoForm' ],
+                       items: [ this.filePreview, fieldset ]
+               } );
 
                this.filenameWidget.on( 'change', this.onInfoFormChange.bind( this ) );
                this.descriptionWidget.on( 'change', this.onInfoFormChange.bind( this ) );
index 8a74ffc..4a463b0 100644 (file)
        /**
         * Set the file to be uploaded.
         *
-        * @param {HTMLInputElement|File} file
+        * @param {HTMLInputElement|File|Blob} file
         */
        UP.setFile = function ( file ) {
                this.file = file;
        /**
         * Get the file being uploaded.
         *
-        * @return {HTMLInputElement|File}
+        * @return {HTMLInputElement|File|Blob}
         */
        UP.getFile = function () {
                return this.file;
                        upload.setState( Upload.State.UPLOADING );
 
                        return finishStash( {
-                               bucket: upload.bucket, // Automatically ignored if undefined
                                watchlist: ( upload.getWatchlist() ) ? 1 : undefined,
                                comment: upload.getComment(),
                                filename: upload.getFilename(),
index fe5e634..99e4569 100644 (file)
@@ -1,4 +1,4 @@
-h1.firstHeading {
+.mw-special-ApiHelp h1.firstHeading {
        display: none;
 }
 
index 4be4a8d..a2386b3 100644 (file)
                $checkboxes.prop( 'checked', check );
        }
 
-       $( '#checkbox-all' ).click( function ( e ) {
+       $( '.mw-checkbox-all' ).click( function ( e ) {
                selectAll( true );
                e.preventDefault();
        } );
-       $( '#checkbox-none' ).click( function ( e ) {
+       $( '.mw-checkbox-none' ).click( function ( e ) {
                selectAll( false );
                e.preventDefault();
        } );
-       $( '#checkbox-invert' ).click( function ( e ) {
+       $( '.mw-checkbox-invert' ).click( function ( e ) {
                $checkboxes.each( function () {
                        $( this ).prop( 'checked', !$( this ).is( ':checked' ) );
                } );
index 97a94b8..8a3784c 100644 (file)
@@ -6,7 +6,7 @@
  * @author Moriel Schottlender, 2015
  * @since 1.19
  */
-/*jshint es3:false */
+/*jshint esversion:5 */
 /*global OO*/
 ( function ( mw, $ ) {
        /**
index b8349d0..f282db6 100644 (file)
                         *             'group': 'somegroup', (or) null
                         *             'source': 'local', (or) 'anotherwiki'
                         *             'skip': 'return !!window.Example', (or) null
+                        *             'module': export Object
                         *
                         *             // Set from execute() or mw.loader.state()
                         *             'state': 'registered', 'loaded', 'loading', 'ready', 'error', or 'missing'
                                // List of modules which will be loaded as when ready
                                batch = [],
 
+                               // Pending queueModuleScript() requests
+                               handlingPendingRequests = false,
+                               pendingRequests = [],
+
                                // List of modules to be loaded
                                queue = [],
 
                                } );
                        }
 
+                       /**
+                        * Queue the loading and execution of a script for a particular module.
+                        *
+                        * @private
+                        * @param {string} src URL of the script
+                        * @param {string} [moduleName] Name of currently executing module
+                        * @return {jQuery.Promise}
+                        */
+                       function queueModuleScript( src, moduleName ) {
+                               var r = $.Deferred();
+
+                               pendingRequests.push( function () {
+                                       if ( moduleName && !hasOwn.call( registry, moduleName ) ) {
+                                               window.require = mw.loader.require;
+                                               window.module = registry[ moduleName ].module;
+                                       }
+                                       addScript( src ).always( function () {
+                                               // Clear environment
+                                               delete window.require;
+                                               delete window.module;
+                                               r.resolve();
+
+                                               // Start the next one (if any)
+                                               if ( pendingRequests[ 0 ] ) {
+                                                       pendingRequests.shift()();
+                                               } else {
+                                                       handlingPendingRequests = false;
+                                               }
+                                       } );
+                               } );
+                               if ( !handlingPendingRequests && pendingRequests[ 0 ] ) {
+                                       handlingPendingRequests = true;
+                                       pendingRequests.shift()();
+                               }
+                               return r.promise();
+                       }
+
                        /**
                         * Utility function for execute()
                         *
                                                        handlePending( module );
                                                };
                                                nestedAddScript = function ( arr, callback, i ) {
-                                                       // Recursively call addScript() in its own callback
+                                                       // Recursively call queueModuleScript() in its own callback
                                                        // for each element of arr.
                                                        if ( i >= arr.length ) {
                                                                // We're at the end of the array
                                                                return;
                                                        }
 
-                                                       addScript( arr[ i ] ).always( function () {
+                                                       queueModuleScript( arr[ i ], module ).always( function () {
                                                                nestedAddScript( arr, callback, i + 1 );
                                                        } );
                                                };
                                                        } else if ( $.isFunction( script ) ) {
                                                                // Pass jQuery twice so that the signature of the closure which wraps
                                                                // the script can bind both '$' and 'jQuery'.
-                                                               script( $, $ );
+                                                               script( $, $, mw.loader.require, registry[ module ].module );
                                                                markModuleReady();
+
                                                        } else if ( typeof script === 'string' ) {
                                                                // Site and user modules are legacy scripts that run in the global scope.
                                                                // This is transported as a string instead of a function to avoid needing
                                        }
                                        // List the module as registered
                                        registry[ module ] = {
+                                               // Exposed to execute() for mw.loader.implement() closures.
+                                               // Import happens via require().
+                                               module: {
+                                                       exports: {}
+                                               },
                                                version: version !== undefined ? String( version ) : '',
                                                dependencies: [],
                                                group: typeof group === 'string' ? group : null,
                                        } );
                                },
 
+                               /**
+                                * Get the exported value of a module.
+                                *
+                                * Module provide this value via their local `module.exports`.
+                                *
+                                * @since 1.27
+                                * @return {Array}
+                                */
+                               require: function ( moduleName ) {
+                                       var state = mw.loader.getState( moduleName );
+
+                                       // Only ready mudules can be required
+                                       if ( state !== 'ready' ) {
+                                               // Module may've forgotten to declare a dependency
+                                               throw new Error( 'Module "' + moduleName + '" is not loaded.' );
+                                       }
+
+                                       return registry[ moduleName ].module.exports;
+                               },
+
                                /**
                                 * @inheritdoc mw.inspect#runReports
                                 * @method
                                        // Whether the store is in use on this page.
                                        enabled: null,
 
-                                       // Modules whose string representation exceeds 100 kB (30 kB on FF) are
-                                       // ineligible for storage due to bug T66721. The quota is stricter on
-                                       // Firefox due to <https://bugzilla.mozilla.org/show_bug.cgi?id=1064466>.
-                                       MODULE_SIZE_MAX: ( /Firefox/.test( navigator.userAgent ) ? 30 : 100 ) * 1000,
+                                       MODULE_SIZE_MAX: 100 * 1000,
 
                                        // The contents of the store, mapping '[module name]@[version]' keys
                                        // to module implementations.
                                                        return;
                                                }
 
-                                               if ( !mw.config.get( 'wgResourceLoaderStorageEnabled' ) ) {
+                                               if (
+                                                       // Disabled because localStorage quotas are tight and (in Firefox's case)
+                                                       // shared by multiple origins.
+                                                       // See T66721, and <https://bugzilla.mozilla.org/show_bug.cgi?id=1064466>.
+                                                       /Firefox|Opera/.test( navigator.userAgent ) ||
+
                                                        // Disabled by configuration.
+                                                       !mw.config.get( 'wgResourceLoaderStorageEnabled' )
+                                               ) {
                                                        // Clear any previous store to free up space. (T66721)
                                                        mw.loader.store.clear();
                                                        mw.loader.store.enabled = false;
                                         */
                                        clear: function () {
                                                mw.loader.store.items = {};
-                                               localStorage.removeItem( mw.loader.store.getStoreKey() );
+                                               try {
+                                                       localStorage.removeItem( mw.loader.store.getStoreKey() );
+                                               } catch ( ignored ) {}
                                        },
 
                                        /**
index 02a90fc..99e9dbe 100644 (file)
@@ -11,6 +11,7 @@
                        api = api || new mw.Api();
 
                        $.data( node, 'request', api.get( {
+                               formatversion: 2,
                                action: 'query',
                                list: 'allusers',
                                // Prefix of list=allusers is case sensitive. Normalise first
diff --git a/resources/src/oojs-ui-styles-skip.js b/resources/src/oojs-ui-styles-skip.js
new file mode 100644 (file)
index 0000000..57c905a
--- /dev/null
@@ -0,0 +1,9 @@
+/*!
+ * Skip function for OOjs UI PHP style modules.
+ *
+ * The `<meta name="X-OOUI-PHP" />` is added to pages by OutputPage::enableOOUI().
+ *
+ * Looking for elements in the DOM might be expensive, but it's probably better than double-loading
+ * 200 KB of CSS with embedded images because of bug T87871.
+ */
+return !!jQuery( 'meta[name="X-OOUI-PHP"]' ).length;
index 0c2d6d6..e53e5f3 100644 (file)
@@ -4,7 +4,7 @@
  * even the most ancient of browsers, so be very careful when editing.
  */
 /*jshint unused: false, evil: true */
-/*globals mw, RLQ: true, $VARS, $CODE, performance */
+/*globals mw, RLQ: true, NORLQ: true, $VARS, $CODE, performance */
 
 var mediaWikiLoadStart = ( new Date() ).getTime(),
 
@@ -67,11 +67,29 @@ function isCompatible( ua ) {
 
 // Conditional script injection
 ( function () {
+       var NORLQ, script;
        if ( !isCompatible() ) {
                // Undo class swapping in case of an unsupported browser.
                // See OutputPage::getHeadScripts().
                document.documentElement.className = document.documentElement.className
                        .replace( /(^|\s)client-js(\s|$)/, '$1client-nojs$2' );
+
+               NORLQ = window.NORLQ || [];
+               while ( NORLQ.length ) {
+                       NORLQ.shift()();
+               }
+               window.NORLQ = {
+                       push: function ( fn ) {
+                               fn();
+                       }
+               };
+
+               // Clear and disable the other queue
+               window.RLQ = {
+                       // No-op
+                       push: function () {}
+               };
+
                return;
        }
 
@@ -96,9 +114,15 @@ function isCompatible( ua ) {
                                fn();
                        }
                };
+
+               // Clear and disable the other queue
+               window.NORLQ = {
+                       // No-op
+                       push: function () {}
+               };
        }
 
-       var script = document.createElement( 'script' );
+       script = document.createElement( 'script' );
        script.src = $VARS.baseModulesUri;
        script.onload = script.onreadystatechange = function () {
                if ( !script.readyState || /loaded|complete/.test( script.readyState ) ) {
index 078fa6c..d4e7119 100644 (file)
@@ -2520,6 +2520,7 @@ Barack Obama <President> of the United States
 </p>
 !! end
 
+## PHP parser discards the "<pre " string
 !! test
 Handle broken pre-like tags (bug 64025)
 !! options
@@ -2530,13 +2531,8 @@ parsoid=wt2html
 <table><pre </table>
 !! html/php
 <pre>x</pre>
-<table>&lt;pre </table>
+<table><pre></pre></table>
 
-!! html/php+tidy
-<pre>
-x
-</pre>
-<p>&lt;pre</p>
 !! html/parsoid
 <pre about="#mwt1" typeof="mw:Transclusion" data-parsoid='{"a":{"&lt;pre":null},"sa":{"&lt;pre":""},"stx":"html","pi":[[{"k":"1","spc":["","","",""]}]]}' data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"&lt;pre &lt;pre>x&lt;/pre>"}},"i":0}}]}'>x</pre>
 
@@ -6334,6 +6330,24 @@ parsoid=wt2html,html2html
 <td data-parsoid='{"startTagSrc":"| ","attrSepSrc":"|","autoInsertedEnd":true}'><a rel="mw:ExtLink" href="ftp://|x||"></a>" onmouseover="alert(document.cookie)">test</td></tr></tbody></table>
 !! end
 
+!! test
+! and || in element attributes should not be parsed as <th>/<td>
+!! wikitext
+{|
+| <div style="color: red !important;" data-contrived="put this here ||">hi</div>
+|}
+!! html/php
+<table>
+<tr>
+<td> <div style="color: red !important;" data-contrived="put this here &#124;&#124;">hi</div>
+</td></tr></table>
+
+!! html/parsoid
+<table>
+<tbody><tr><td> <div style="color: red !important;" data-contrived="put this here ||" data-parsoid='{"stx":"html"}'>hi</div></td></tr>
+</tbody></table>
+!! end
+
 # FIXME: The output seems broken. Filed as T110268.
 !! test
 ! and || in td attributes should not be parsed as <th>/<td>
@@ -10951,8 +10965,6 @@ Un-closed <includeonly>
 !! wikitext
 <includeonly>
 !! html
-<p>&lt;includeonly&gt;
-</p>
 !! end
 
 ## We used to, but no longer wt2wt this test since the default serializer
index f69e342..2e1e270 100644 (file)
@@ -1,4 +1,8 @@
 <?php
+use MediaWiki\Logger\LegacySpi;
+use MediaWiki\Logger\LoggerFactory;
+use MediaWiki\Logger\MonologSpi;
+use Psr\Log\LoggerInterface;
 
 /**
  * @since 1.18
@@ -64,6 +68,12 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
         */
        private $mwGlobals = array();
 
+       /**
+        * Holds original loggers which have been replaced by setLogger()
+        * @var LoggerInterface[]
+        */
+       private $loggers = array();
+
        /**
         * Table name prefixes. Oracle likes it shorter.
         */
@@ -252,6 +262,7 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
                        $GLOBALS[$key] = $value;
                }
                $this->mwGlobals = array();
+               $this->restoreLoggers();
                RequestContext::resetMain();
                MediaHandler::resetCache();
                if ( session_id() !== '' ) {
@@ -404,6 +415,61 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
                $this->setMwGlobals( $name, $merged );
        }
 
+       /**
+        * Sets the logger for a specified channel, for the duration of the test.
+        * @since 1.27
+        * @param string $channel
+        * @param LoggerInterface $logger
+        */
+       protected function setLogger( $channel, LoggerInterface $logger ) {
+               $provider = LoggerFactory::getProvider();
+               $wrappedProvider = TestingAccessWrapper::newFromObject( $provider );
+               $singletons = $wrappedProvider->singletons;
+               if ( $provider instanceof MonologSpi ) {
+                       if ( !isset( $this->loggers[$channel] ) ) {
+                               $this->loggers[$channel] = isset( $singletons['loggers'][$channel] )
+                                       ? $singletons['loggers'][$channel] : null;
+                       }
+                       $singletons['loggers'][$channel] = $logger;
+               } elseif ( $provider instanceof LegacySpi ) {
+                       if ( !isset( $this->loggers[$channel] ) ) {
+                               $this->loggers[$channel] = isset( $singletons[$channel] ) ? $singletons[$channel] : null;
+                       }
+                       $singletons[$channel] = $logger;
+               } else {
+                       throw new LogicException( __METHOD__ . ': setting a logger for ' . get_class( $provider )
+                               . ' is not implemented' );
+               }
+               $wrappedProvider->singletons = $singletons;
+       }
+
+       /**
+        * Restores loggers replaced by setLogger().
+        * @since 1.27
+        */
+       private function restoreLoggers() {
+               $provider = LoggerFactory::getProvider();
+               $wrappedProvider = TestingAccessWrapper::newFromObject( $provider );
+               $singletons = $wrappedProvider->singletons;
+               foreach ( $this->loggers as $channel => $logger ) {
+                       if ( $provider instanceof MonologSpi ) {
+                               if ( $logger === null ) {
+                                       unset( $singletons['loggers'][$channel] );
+                               } else {
+                                       $singletons['loggers'][$channel] = $logger;
+                               }
+                       } elseif ( $provider instanceof LegacySpi ) {
+                               if ( $logger === null ) {
+                                       unset( $singletons[$channel] );
+                               } else {
+                                       $singletons[$channel] = $logger;
+                               }
+                       }
+               }
+               $wrappedProvider->singletons = $singletons;
+               $this->loggers = array();
+       }
+
        /**
         * @return string
         * @since 1.18
index 90ee1bb..ce9e1d6 100644 (file)
@@ -364,6 +364,25 @@ class EditPageTest extends MediaWikiLangTestCase {
        }
 
        public function testUpdatePage() {
+               $checkIds = array();
+
+               $this->setMwGlobals( 'wgHooks', array(
+                       'PageContentInsertComplete' => array( function (
+                               WikiPage &$page, User &$user, Content $content,
+                               $summary, $minor, $u1, $u2, &$flags, Revision $revision
+                       ) {
+                               // types/refs checked
+                       } ),
+                       'PageContentSaveComplete' => array( function (
+                               WikiPage &$page, User &$user, Content $content,
+                               $summary, $minor, $u1, $u2, &$flags, Revision $revision,
+                               Status &$status, $baseRevId
+                       ) use ( &$checkIds ) {
+                               $checkIds[] = $status->value['revision']->getId();
+                               // types/refs checked
+                       } ),
+               ) );
+
                $text = "one";
                $edit = array(
                        'wpTextbox1' => $text,
@@ -373,6 +392,7 @@ class EditPageTest extends MediaWikiLangTestCase {
                $page = $this->assertEdit( 'EditPageTest_testUpdatePage', "zero", null, $edit,
                        EditPage::AS_SUCCESS_UPDATE, $text,
                        "expected successfull update with given text" );
+               $this->assertGreaterThan( 0, $checkIds[0], "First event rev ID set" );
 
                $this->forceRevisionDate( $page, '20120101000000' );
 
@@ -385,6 +405,62 @@ class EditPageTest extends MediaWikiLangTestCase {
                $this->assertEdit( 'EditPageTest_testUpdatePage', null, null, $edit,
                        EditPage::AS_SUCCESS_UPDATE, $text,
                        "expected successfull update with given text" );
+               $this->assertGreaterThan( 0, $checkIds[1], "Second edit hook rev ID set" );
+               $this->assertGreaterThan( $checkIds[0], $checkIds[1], "Second event rev ID is higher" );
+       }
+
+       public function testUpdatePageTrx() {
+               $text = "one";
+               $edit = array(
+                       'wpTextbox1' => $text,
+                       'wpSummary' => 'first update',
+               );
+
+               $page = $this->assertEdit( 'EditPageTest_testTrxUpdatePage', "zero", null, $edit,
+                       EditPage::AS_SUCCESS_UPDATE, $text,
+                       "expected successfull update with given text" );
+
+               $this->forceRevisionDate( $page, '20120101000000' );
+
+               $checkIds = array();
+               $this->setMwGlobals( 'wgHooks', array(
+                       'PageContentSaveComplete' => array( function (
+                               WikiPage &$page, User &$user, Content $content,
+                               $summary, $minor, $u1, $u2, &$flags, Revision $revision,
+                               Status &$status, $baseRevId
+                       ) use ( &$checkIds ) {
+                               $checkIds[] = $status->value['revision']->getId();
+                               // types/refs checked
+                       } ),
+               ) );
+
+               wfGetDB( DB_MASTER )->begin( __METHOD__ );
+
+               $text = "two";
+               $edit = array(
+                       'wpTextbox1' => $text,
+                       'wpSummary' => 'second update',
+               );
+
+               $this->assertEdit( 'EditPageTest_testTrxUpdatePage', null, null, $edit,
+                       EditPage::AS_SUCCESS_UPDATE, $text,
+                       "expected successfull update with given text" );
+
+               $text = "three";
+               $edit = array(
+                       'wpTextbox1' => $text,
+                       'wpSummary' => 'third update',
+               );
+
+               $this->assertEdit( 'EditPageTest_testTrxUpdatePage', null, null, $edit,
+                       EditPage::AS_SUCCESS_UPDATE, $text,
+                       "expected successfull update with given text" );
+
+               wfGetDB( DB_MASTER )->commit( __METHOD__ );
+
+               $this->assertGreaterThan( 0, $checkIds[0], "First event rev ID set" );
+               $this->assertGreaterThan( 0, $checkIds[1], "Second edit hook rev ID set" );
+               $this->assertGreaterThan( $checkIds[0], $checkIds[1], "Second event rev ID is higher" );
        }
 
        public static function provideSectionEdit() {
index 830a7be..a4ef950 100644 (file)
@@ -99,7 +99,7 @@ class HtmlTest extends MediaWikiTestCase {
        }
 
        /**
-        * @covers HTML::expandAttributes
+        * @covers Html::expandAttributes
         */
        public function testExpandAttributesSkipsNullAndFalse() {
 
@@ -120,7 +120,7 @@ class HtmlTest extends MediaWikiTestCase {
        }
 
        /**
-        * @covers HTML::expandAttributes
+        * @covers Html::expandAttributes
         */
        public function testExpandAttributesForBooleans() {
                $this->assertEquals(
@@ -155,7 +155,7 @@ class HtmlTest extends MediaWikiTestCase {
        }
 
        /**
-        * @covers HTML::expandAttributes
+        * @covers Html::expandAttributes
         */
        public function testExpandAttributesForNumbers() {
                $this->assertEquals(
@@ -171,7 +171,7 @@ class HtmlTest extends MediaWikiTestCase {
        }
 
        /**
-        * @covers HTML::expandAttributes
+        * @covers Html::expandAttributes
         */
        public function testExpandAttributesForObjects() {
                $this->assertEquals(
@@ -481,7 +481,7 @@ class HtmlTest extends MediaWikiTestCase {
                $this->assertEquals(
                        '<input type=' . $HTML5InputType . '>',
                        Html::element( 'input', array( 'type' => $HTML5InputType ) ),
-                       'In HTML5, HTML::element() should accept type="' . $HTML5InputType . '"'
+                       'In HTML5, Html::element() should accept type="' . $HTML5InputType . '"'
                );
        }
 
diff --git a/tests/phpunit/includes/MergeHistoryTest.php b/tests/phpunit/includes/MergeHistoryTest.php
new file mode 100644 (file)
index 0000000..0c1a7a8
--- /dev/null
@@ -0,0 +1,124 @@
+<?php
+
+/**
+ * @group Database
+ */
+class MergeHistoryTest extends MediaWikiTestCase {
+
+       /**
+        * Make some pages to work with
+        */
+       public function addDBData() {
+               // Pages that won't actually be merged
+               $this->insertPage( 'Test' );
+               $this->insertPage( 'Test2' );
+
+               // Pages that will be merged
+               $this->insertPage( 'Merge1' );
+               $this->insertPage( 'Merge2' );
+       }
+
+       /**
+        * @dataProvider provideIsValidMerge
+        * @covers MergeHistory::isValidMerge
+        * @param $source string Source page
+        * @param $dest string Destination page
+        * @param $timestamp string|bool Timestamp up to which revisions are merged (or false for all)
+        * @param $error string|bool Expected error for test (or true for no error)
+        */
+       public function testIsValidMerge( $source, $dest, $timestamp, $error ) {
+               $this->setMwGlobals( 'wgContentHandlerUseDB', false );
+               $mh = new MergeHistory(
+                       Title::newFromText( $source ),
+                       Title::newFromText( $dest ),
+                       $timestamp
+               );
+               $status = $mh->isValidMerge();
+               if ( $error === true ) {
+                       $this->assertTrue( $status->isGood() );
+               } else {
+                       $this->assertTrue( $status->hasMessage( $error ) );
+               }
+       }
+
+       public static function provideIsValidMerge() {
+               return array(
+                       // for MergeHistory::isValidMerge
+                       array( 'Test', 'Test2', false, true ),
+                       // Although this timestamp is after the latest timestamp of both pages,
+                       // MergeHistory should select the latest source timestamp up to this which should
+                       // still work for the merge.
+                       array( 'Test', 'Test2', strtotime( 'tomorrow' ), true ),
+                       array( 'Test', 'Test', false, 'mergehistory-fail-self-merge' ),
+                       array( 'Nonexistant', 'Test2', false, 'mergehistory-fail-invalid-source' ),
+                       array( 'Test', 'Nonexistant', false, 'mergehistory-fail-invalid-dest' ),
+                       array(
+                               'Test',
+                               'Test2',
+                               'This is obviously an invalid timestamp',
+                               'mergehistory-fail-bad-timestamp'
+                       ),
+               );
+       }
+
+       /**
+        * Test merge revision limit checking
+        * @covers MergeHistory::isValidMerge
+        */
+       public function testIsValidMergeRevisionLimit() {
+               $limit = MergeHistory::REVISION_LIMIT;
+
+               $mh = $this->getMockBuilder( 'MergeHistory' )
+                       ->setMethods( array( 'getRevisionCount' ) )
+                       ->setConstructorArgs( array(
+                               Title::newFromText( 'Test' ),
+                               Title::newFromText( 'Test2' ),
+                       ) )
+                       ->getMock();
+               $mh->expects( $this->once() )
+                       ->method( 'getRevisionCount' )
+                       ->will( $this->returnValue( $limit + 1 ) );
+
+               $status = $mh->isValidMerge();
+               $this->assertTrue( $status->hasMessage( 'mergehistory-fail-toobig' ) );
+               $errors = $status->getErrorsByType( 'error' );
+               $params = $errors[0]['params'];
+               $this->assertEquals( $params[0], Message::numParam( $limit ) );
+       }
+
+       /**
+        * Test user permission checking
+        * @covers MergeHistory::checkPermissions
+        */
+       public function testCheckPermissions() {
+               $mh = new MergeHistory(
+                       Title::newFromText( 'Test' ),
+                       Title::newFromText( 'Test2' )
+               );
+
+               // Sysop with mergehistory permission
+               $sysop = User::newFromName( 'UTSysop' );
+               $status = $mh->checkPermissions( $sysop, '' );
+               $this->assertTrue( $status->isOK() );
+
+               // Normal user
+               $notSysop = User::newFromName( 'UTNotSysop' );
+               $notSysop->addToDatabase();
+               $status = $mh->checkPermissions( $notSysop, '' );
+               $this->assertTrue( $status->hasMessage( 'mergehistory-fail-permission' ) );
+       }
+
+       /**
+        * Test merged revision count
+        * @covers MergeHistory::getMergedRevisionCount
+        */
+       public function testGetMergedRevisionCount() {
+               $mh = new MergeHistory(
+                       Title::newFromText( 'Merge1' ),
+                       Title::newFromText( 'Merge2' )
+               );
+
+               $mh->merge( User::newFromName( 'UTSysop' ) );
+               $this->assertEquals( $mh->getMergedRevisionCount(), 1 );
+       }
+}
index 9a1f597..e3d69de 100644 (file)
@@ -92,7 +92,7 @@ class TestPageProps extends MediaWikiLangTestCase {
        public function testGetSingleProperty() {
                $pageProps = PageProps::getInstance();
                $page1ID = $this->title1->getArticleID();
-               $result = $pageProps->getProperty( $this->title1, "property1" );
+               $result = $pageProps->getProperties( $this->title1, "property1" );
                $this->assertArrayHasKey( $page1ID, $result, "Found property" );
                $this->assertEquals( $result[$page1ID], "value1", "Get property" );
        }
@@ -109,13 +109,42 @@ class TestPageProps extends MediaWikiLangTestCase {
                        $this->title1,
                        $this->title2
                );
-               $result = $pageProps->getProperty( $titles, "property1" );
+               $result = $pageProps->getProperties( $titles, "property1" );
                $this->assertArrayHasKey( $page1ID, $result, "Found page 1 property" );
                $this->assertArrayHasKey( $page2ID, $result, "Found page 2 property" );
                $this->assertEquals( $result[$page1ID], "value1", "Get property page 1" );
                $this->assertEquals( $result[$page2ID], "value1", "Get property page 2" );
        }
 
+       /**
+        * Test getting multiple properties from multiple pages. The properties
+        * were set in setUp().
+        */
+       public function testGetMultiplePropertiesMultiplePages() {
+               $pageProps = PageProps::getInstance();
+               $page1ID = $this->title1->getArticleID();
+               $page2ID = $this->title2->getArticleID();
+               $titles = array(
+                       $this->title1,
+                       $this->title2
+               );
+               $properties = array(
+                       "property1",
+                       "property2"
+               );
+               $result = $pageProps->getProperties( $titles, $properties );
+               $this->assertArrayHasKey( $page1ID, $result, "Found page 1 property" );
+               $this->assertArrayHasKey( "property1", $result[$page1ID], "Found page 1 property 1" );
+               $this->assertArrayHasKey( "property2", $result[$page1ID], "Found page 1 property 2" );
+               $this->assertArrayHasKey( $page2ID, $result, "Found page 2 property" );
+               $this->assertArrayHasKey( "property1", $result[$page2ID], "Found page 2 property 1" );
+               $this->assertArrayHasKey( "property2", $result[$page2ID], "Found page 2 property 2" );
+               $this->assertEquals( $result[$page1ID]["property1"], "value1", "Get page 1 property 1" );
+               $this->assertEquals( $result[$page1ID]["property2"], "value2", "Get page 1 property 2" );
+               $this->assertEquals( $result[$page2ID]["property1"], "value1", "Get page 2 property 1" );
+               $this->assertEquals( $result[$page2ID]["property2"], "value2", "Get page 2 property 2" );
+       }
+
        /**
         * Test getting all properties from a single page. The properties were
         * set in setUp(). The properties retrieved from the page may include
@@ -130,7 +159,7 @@ class TestPageProps extends MediaWikiLangTestCase {
        public function testGetAllProperties() {
                $pageProps = PageProps::getInstance();
                $page1ID = $this->title1->getArticleID();
-               $result = $pageProps->getProperties( $this->title1 );
+               $result = $pageProps->getAllProperties( $this->title1 );
                $this->assertArrayHasKey( $page1ID, $result, "Found properties" );
                $properties = $result[$page1ID];
                $patched = array_replace_recursive( $properties, $this->the_properties );
@@ -149,7 +178,7 @@ class TestPageProps extends MediaWikiLangTestCase {
                        $this->title1,
                        $this->title2
                );
-               $result = $pageProps->getProperties( $titles );
+               $result = $pageProps->getAllProperties( $titles );
                $this->assertArrayHasKey( $page1ID, $result, "Found page 1 properties" );
                $this->assertArrayHasKey( $page2ID, $result, "Found page 2 properties" );
                $properties1 = $result[$page1ID];
@@ -169,9 +198,9 @@ class TestPageProps extends MediaWikiLangTestCase {
        public function testSingleCache() {
                $pageProps = PageProps::getInstance();
                $page1ID = $this->title1->getArticleID();
-               $value1 = $pageProps->getProperty( $this->title1, "property1" );
+               $value1 = $pageProps->getProperties( $this->title1, "property1" );
                $this->setProperty( $page1ID, "property1", "another value" );
-               $value2 = $pageProps->getProperty( $this->title1, "property1" );
+               $value2 = $pageProps->getProperties( $this->title1, "property1" );
                $this->assertEquals( $value1, $value2, "Single cache" );
        }
 
@@ -184,9 +213,9 @@ class TestPageProps extends MediaWikiLangTestCase {
        public function testMultiCache() {
                $pageProps = PageProps::getInstance();
                $page1ID = $this->title1->getArticleID();
-               $properties1 = $pageProps->getProperties( $this->title1 );
+               $properties1 = $pageProps->getAllProperties( $this->title1 );
                $this->setProperty( $page1ID, "property1", "another value" );
-               $properties2 = $pageProps->getProperties( $this->title1 );
+               $properties2 = $pageProps->getAllProperties( $this->title1 );
                $this->assertEquals( $properties1, $properties2, "Multi Cache" );
        }
 
@@ -201,11 +230,11 @@ class TestPageProps extends MediaWikiLangTestCase {
        public function testClearCache() {
                $pageProps = PageProps::getInstance();
                $page1ID = $this->title1->getArticleID();
-               $pageProps->getProperty( $this->title1, "property1" );
+               $pageProps->getProperties( $this->title1, "property1" );
                $new_value = "another value";
                $this->setProperty( $page1ID, "property1", $new_value );
-               $pageProps->getProperties( $this->title1 );
-               $result = $pageProps->getProperty( $this->title1, "property1" );
+               $pageProps->getAllProperties( $this->title1 );
+               $result = $pageProps->getProperties( $this->title1, "property1" );
                $this->assertArrayHasKey( $page1ID, $result, "Found property" );
                $this->assertEquals( $result[$page1ID], "another value", "Clear cache" );
        }
index 08a984e..56bbd06 100644 (file)
@@ -25,6 +25,7 @@ class ApiMessageTest extends MediaWikiTestCase {
 
        /**
         * @covers ApiMessage
+        * @covers ApiMessageTrait
         */
        public function testApiMessage() {
                $msg = new Message( array( 'foo', 'bar' ), array( 'baz' ) );
@@ -63,6 +64,7 @@ class ApiMessageTest extends MediaWikiTestCase {
 
        /**
         * @covers ApiRawMessage
+        * @covers ApiMessageTrait
         */
        public function testApiRawMessage() {
                $msg = new RawMessage( 'foo', array( 'baz' ) );
index 8178c12..a72247b 100644 (file)
@@ -26,6 +26,9 @@ class ContentHandlerTest extends MediaWikiTestCase {
                                CONTENT_MODEL_CSS => 'CssContentHandler',
                                CONTENT_MODEL_TEXT => 'TextContentHandler',
                                'testing' => 'DummyContentHandlerForTesting',
+                               'testing-callbacks' => function( $modelId ) {
+                                       return new DummyContentHandlerForTesting( $modelId );
+                               }
                        ),
                ) );
 
@@ -378,4 +381,26 @@ class ContentHandlerTest extends MediaWikiTestCase {
 
                return true;
        }
+
+       public function provideGetModelForID() {
+               return array(
+                       array( CONTENT_MODEL_WIKITEXT, 'WikitextContentHandler' ),
+                       array( CONTENT_MODEL_JAVASCRIPT, 'JavaScriptContentHandler' ),
+                       array( CONTENT_MODEL_JSON, 'JsonContentHandler' ),
+                       array( CONTENT_MODEL_CSS, 'CssContentHandler' ),
+                       array( CONTENT_MODEL_TEXT, 'TextContentHandler' ),
+                       array( 'testing', 'DummyContentHandlerForTesting' ),
+                       array( 'testing-callbacks', 'DummyContentHandlerForTesting' ),
+               );
+       }
+
+       /**
+        * @dataProvider provideGetModelForID
+        */
+       public function testGetModelForID( $modelId, $handlerClass ) {
+               $handler = ContentHandler::getForModelID( $modelId );
+
+               $this->assertInstanceOf( $handlerClass, $handler );
+       }
+
 }
index b0df616..b28de57 100644 (file)
@@ -55,13 +55,19 @@ class AvroFormatterTest extends MediaWikiTestCase {
        }
 
        public function testDoesSomethingWhenSchemaAvailable() {
-               $formatter = new AvroFormatter( array( 'string' => array( 'type' => 'string' ) ) );
+               $formatter = new AvroFormatter( array(
+                       'string' => array(
+                               'schema' => array( 'type' => 'string' ),
+                               'revision' => 1010101,
+                       )
+               ) );
                $res = $formatter->format( array(
                        'channel' => 'string',
                        'context' => 'better to be',
                ) );
                $this->assertNotNull( $res );
-               // basically just tell us if avro changes its string encoding
-               $this->assertEquals( base64_decode( 'GGJldHRlciB0byBiZQ==' ), $res );
+               // basically just tell us if avro changes its string encoding, or if
+               // we completely fail to generate a log message.
+               $this->assertEquals( 'AAAAAAAAD2m1GGJldHRlciB0byBiZQ==', base64_encode( $res ) );
        }
 }
index 32b150c..3efc4c3 100644 (file)
@@ -23,11 +23,10 @@ class ArrayUtilsTest extends PHPUnit_Framework_TestCase {
        }
 
        function provideFindLowerBound() {
-               $that = $this;
-               $indexValueCallback = function ( $size ) use ( $that ) {
-                       return function ( $val ) use ( $that, $size ) {
-                               $that->assertTrue( $val >= 0 );
-                               $that->assertTrue( $val < $size );
+               $indexValueCallback = function ( $size ) {
+                       return function ( $val ) use ( $size ) {
+                               $this->assertTrue( $val >= 0 );
+                               $this->assertTrue( $val < $size );
                                return $val;
                        };
                };
diff --git a/tests/phpunit/includes/libs/objectcache/CachedBagOStuffTest.php b/tests/phpunit/includes/libs/objectcache/CachedBagOStuffTest.php
new file mode 100644 (file)
index 0000000..3b19c9a
--- /dev/null
@@ -0,0 +1,52 @@
+<?php
+
+/**
+ * @group BagOStuff
+ */
+class CachedBagOStuffTest extends PHPUnit_Framework_TestCase {
+
+       public function testGetFromBackend() {
+               $backend = new HashBagOStuff;
+               $cache = new CachedBagOStuff( $backend );
+
+               $backend->set( 'foo', 'bar' );
+               $this->assertEquals( 'bar', $cache->get( 'foo' ) );
+
+               $backend->set( 'foo', 'baz' );
+               $this->assertEquals( 'bar', $cache->get( 'foo' ), 'cached' );
+       }
+
+       public function testSetAndDelete() {
+               $backend = new HashBagOStuff;
+               $cache = new CachedBagOStuff( $backend );
+
+               for ( $i = 0; $i < 10; $i++ ) {
+                       $cache->set( "key$i", 1 );
+                       $this->assertEquals( 1, $cache->get( "key$i" ) );
+                       $this->assertEquals( 1, $backend->get( "key$i" ) );
+                       $cache->delete( "key$i" );
+                       $this->assertEquals( false, $cache->get( "key$i" ) );
+                       $this->assertEquals( false, $backend->get( "key$i" ) );
+               }
+       }
+
+       public function testWriteCacheOnly() {
+               $backend = new HashBagOStuff;
+               $cache = new CachedBagOStuff( $backend );
+
+               $cache->set( 'foo', 'bar', 0, CachedBagOStuff::WRITE_CACHE_ONLY );
+               $this->assertEquals( 'bar', $cache->get( 'foo' ) );
+               $this->assertFalse( $backend->get( 'foo' ) );
+
+               $cache->set( 'foo', 'old' );
+               $this->assertEquals( 'old', $cache->get( 'foo' ) );
+               $this->assertEquals( 'old', $backend->get( 'foo' ) );
+
+               $cache->set( 'foo', 'new', 0, CachedBagOStuff::WRITE_CACHE_ONLY );
+               $this->assertEquals( 'new', $cache->get( 'foo' ) );
+               $this->assertEquals( 'old', $backend->get( 'foo' ) );
+
+               $cache->delete( 'foo', CachedBagOStuff::WRITE_CACHE_ONLY );
+               $this->assertEquals( 'old', $cache->get( 'foo' ) ); // Reloaded from backend
+       }
+}
index 6556186..4630b48 100644 (file)
@@ -44,7 +44,11 @@ class IPTCTest extends MediaWikiTestCase {
         * @covers IPTC::Parse
         */
        public function testIPTCParseForcedUTFButInvalid() {
-               if ( version_compare( PHP_VERSION, '5.5.26', '<' ) ) {
+               if ( version_compare( PHP_VERSION, '5.5.26', '<' )
+                       || ( version_compare( PHP_VERSION, '5.6.0', '>' )
+                               && version_compare( PHP_VERSION, '5.6.10', '<' )
+                       )
+               ) {
                        $this->markTestSkipped( 'Test fails on pre-PHP 5.5.25. See T124574/T39665 for details.' );
                }
                $iptcData = "Photoshop 3.0\08BIM\4\4\0\0\0\0\0\x11\x1c\x02\x19\x00\x04\xC3\xC3\xC3\xB8"
index b940230..1ebba1a 100644 (file)
@@ -48,7 +48,7 @@ class PreprocessorTest extends MediaWikiTestCase {
                        array( "<noinclude> Foo bar </noinclude>", "<root><ignore>&lt;noinclude&gt;</ignore> Foo bar <ignore>&lt;/noinclude&gt;</ignore></root>" ),
                        array( "<noinclude>\n{{Foo}}\n</noinclude>", "<root><ignore>&lt;noinclude&gt;</ignore>\n<template lineStart=\"1\"><title>Foo</title></template>\n<ignore>&lt;/noinclude&gt;</ignore></root>" ),
                        array( "<noinclude>\n{{Foo}}\n</noinclude>\n", "<root><ignore>&lt;noinclude&gt;</ignore>\n<template lineStart=\"1\"><title>Foo</title></template>\n<ignore>&lt;/noinclude&gt;</ignore>\n</root>" ),
-                       array( "<gallery>foo bar", "<root>&lt;gallery&gt;foo bar</root>" ),
+                       array( "<gallery>foo bar", "<root><ext><name>gallery</name><attr></attr><inner>foo bar</inner></ext></root>" ),
                        array( "<{{foo}}>", "<root>&lt;<template><title>foo</title></template>&gt;</root>" ),
                        array( "<{{{foo}}}>", "<root>&lt;<tplarg><title>foo</title></tplarg>&gt;</root>" ),
                        array( "<gallery></gallery</gallery>", "<root><ext><name>gallery</name><attr></attr><inner>&lt;/gallery</inner><close>&lt;/gallery&gt;</close></ext></root>" ),
index b683885..9ef2588 100644 (file)
@@ -206,7 +206,6 @@ mw.example();
                                'styles' => array(),
                                'messages' => new XmlJsCode( '{}' ),
                                'templates' => array(),
-                               'title' => 'scripts, styles and messags',
 
                                'expected' => 'mw.loader.implement( "test.example", function ( $, jQuery ) {
 mw.example();
diff --git a/tests/phpunit/includes/search/SearchEnginePrefixTest.php b/tests/phpunit/includes/search/SearchEnginePrefixTest.php
new file mode 100644 (file)
index 0000000..2664fa6
--- /dev/null
@@ -0,0 +1,334 @@
+<?php
+/**
+ * @group Search
+ * @group Database
+ */
+class SearchEnginePrefixTest extends MediaWikiLangTestCase {
+
+       /**
+        * @var SearchEngine
+        */
+       private $search;
+
+       public function addDBData() {
+               if ( !$this->isWikitextNS( NS_MAIN ) ) {
+                       // tests are skipped if NS_MAIN is not wikitext
+                       return;
+               }
+
+               $this->insertPage( 'Sandbox' );
+               $this->insertPage( 'Bar' );
+               $this->insertPage( 'Example' );
+               $this->insertPage( 'Example Bar' );
+               $this->insertPage( 'Example Foo' );
+               $this->insertPage( 'Example Foo/Bar' );
+               $this->insertPage( 'Example/Baz' );
+               $this->insertPage( 'Redirect test', '#REDIRECT [[Redirect Test]]' );
+               $this->insertPage( 'Redirect Test' );
+               $this->insertPage( 'Redirect Test Worse Result' );
+               $this->insertPage( 'Redirect test2', '#REDIRECT [[Redirect Test2]]' );
+               $this->insertPage( 'Redirect TEST2', '#REDIRECT [[Redirect Test2]]' );
+               $this->insertPage( 'Redirect Test2' );
+               $this->insertPage( 'Redirect Test2 Worse Result' );
+
+               $this->insertPage( 'Talk:Sandbox' );
+               $this->insertPage( 'Talk:Example' );
+
+               $this->insertPage( 'User:Example' );
+       }
+
+       protected function setUp() {
+               parent::setUp();
+
+               if ( !$this->isWikitextNS( NS_MAIN ) ) {
+                       $this->markTestSkipped( 'Main namespace does not support wikitext.' );
+               }
+
+               // Avoid special pages from extensions interferring with the tests
+               $this->setMwGlobals( 'wgSpecialPages', array() );
+               $this->search = SearchEngine::create();
+               $this->search->setNamespaces( array() );
+       }
+
+       protected function searchProvision( Array $results = null ) {
+               if ( $results === null ) {
+                       $this->setMwGlobals( 'wgHooks', array() );
+               } else {
+                       $this->setMwGlobals( 'wgHooks', array(
+                               'PrefixSearchBackend' => array(
+                                       function ( $namespaces, $search, $limit, &$srchres ) use ( $results ) {
+                                               $srchres = $results;
+                                               return false;
+                                       }
+                               ),
+                       ) );
+               }
+       }
+
+       public static function provideSearch() {
+               return array(
+                       array( array(
+                               'Empty string',
+                               'query' => '',
+                               'results' => array(),
+                       ) ),
+                       array( array(
+                               'Main namespace with title prefix',
+                               'query' => 'Ex',
+                               'results' => array(
+                                       'Example',
+                                       'Example/Baz',
+                                       'Example Bar',
+                               ),
+                               // Third result when testing offset
+                               'offsetresult' => array(
+                                       'Example Foo',
+                               ),
+                       ) ),
+                       array( array(
+                               'Talk namespace prefix',
+                               'query' => 'Talk:',
+                               'results' => array(
+                                       'Talk:Example',
+                                       'Talk:Sandbox',
+                               ),
+                       ) ),
+                       array( array(
+                               'User namespace prefix',
+                               'query' => 'User:',
+                               'results' => array(
+                                       'User:Example',
+                               ),
+                       ) ),
+                       array( array(
+                               'Special namespace prefix',
+                               'query' => 'Special:',
+                               'results' => array(
+                                       'Special:ActiveUsers',
+                                       'Special:AllMessages',
+                                       'Special:AllMyFiles',
+                               ),
+                               // Third result when testing offset
+                               'offsetresult' => array(
+                                       'Special:AllMyUploads',
+                               ),
+                       ) ),
+                       array( array(
+                               'Special namespace with prefix',
+                               'query' => 'Special:Un',
+                               'results' => array(
+                                       'Special:Unblock',
+                                       'Special:UncategorizedCategories',
+                                       'Special:UncategorizedFiles',
+                               ),
+                               // Third result when testing offset
+                               'offsetresult' => array(
+                                       'Special:UncategorizedImages',
+                               ),
+                       ) ),
+                       array( array(
+                               'Special page name',
+                               'query' => 'Special:EditWatchlist',
+                               'results' => array(
+                                       'Special:EditWatchlist',
+                               ),
+                       ) ),
+                       array( array(
+                               'Special page subpages',
+                               'query' => 'Special:EditWatchlist/',
+                               'results' => array(
+                                       'Special:EditWatchlist/clear',
+                                       'Special:EditWatchlist/raw',
+                               ),
+                       ) ),
+                       array( array(
+                               'Special page subpages with prefix',
+                               'query' => 'Special:EditWatchlist/cl',
+                               'results' => array(
+                                       'Special:EditWatchlist/clear',
+                               ),
+                       ) ),
+               );
+       }
+
+       /**
+        * @dataProvider provideSearch
+        * @covers SearchEngine::defaultPrefixSearch
+        */
+       public function testSearch( Array $case ) {
+               $this->search->setLimitOffset( 3 );
+               $results = $this->search->defaultPrefixSearch( $case['query'] );
+               $results = array_map( function( Title $t ) {
+                       return $t->getPrefixedText();
+               }, $results );
+               $this->assertEquals(
+                       $case['results'],
+                       $results,
+                       $case[0]
+               );
+       }
+
+       /**
+        * @dataProvider provideSearch
+        * @covers SearchEngine::defaultPrefixSearch
+        */
+       public function testSearchWithOffset( Array $case ) {
+               $this->search->setLimitOffset( 3, 1 );
+               $results = $this->search->defaultPrefixSearch( $case['query'] );
+               $results = array_map( function( Title $t ) {
+                       return $t->getPrefixedText();
+               }, $results );
+
+               // We don't expect the first result when offsetting
+               array_shift( $case['results'] );
+               // And sometimes we expect a different last result
+               $expected = isset( $case['offsetresult'] ) ?
+                       array_merge( $case['results'], $case['offsetresult'] ) :
+                       $case['results'];
+
+               $this->assertEquals(
+                       $expected,
+                       $results,
+                       $case[0]
+               );
+       }
+
+       public static function provideSearchBackend() {
+               return array(
+                       array( array(
+                               'Simple case',
+                               'provision' => array(
+                                       'Bar',
+                                       'Barcelona',
+                                       'Barbara',
+                               ),
+                               'query' => 'Bar',
+                               'results' => array(
+                                       'Bar',
+                                       'Barcelona',
+                                       'Barbara',
+                               ),
+                       ) ),
+                       array( array(
+                               'Exact match not on top (bug 70958)',
+                               'provision' => array(
+                                       'Barcelona',
+                                       'Bar',
+                                       'Barbara',
+                               ),
+                               'query' => 'Bar',
+                               'results' => array(
+                                       'Bar',
+                                       'Barcelona',
+                                       'Barbara',
+                               ),
+                       ) ),
+                       array( array(
+                               'Exact match missing (bug 70958)',
+                               'provision' => array(
+                                       'Barcelona',
+                                       'Barbara',
+                                       'Bart',
+                               ),
+                               'query' => 'Bar',
+                               'results' => array(
+                                       'Bar',
+                                       'Barcelona',
+                                       'Barbara',
+                               ),
+                       ) ),
+                       array( array(
+                               'Exact match missing and not existing',
+                               'provision' => array(
+                                       'Exile',
+                                       'Exist',
+                                       'External',
+                               ),
+                               'query' => 'Ex',
+                               'results' => array(
+                                       'Exile',
+                                       'Exist',
+                                       'External',
+                               ),
+                       ) ),
+                       array( array(
+                               "Exact match shouldn't override already found match if " .
+                                       "exact is redirect and found isn't",
+                               'provision' => array(
+                                       // Target of the exact match is low in the list
+                                       'Redirect Test Worse Result',
+                                       'Redirect Test',
+                               ),
+                               'query' => 'redirect test',
+                               'results' => array(
+                                       // Redirect target is pulled up and exact match isn't added
+                                       'Redirect Test',
+                                       'Redirect Test Worse Result',
+                               ),
+                       ) ),
+                       array( array(
+                               "Exact match shouldn't override already found match if " .
+                                       "both exact match and found match are redirect",
+                               'provision' => array(
+                                       // Another redirect to the same target as the exact match
+                                       // is low in the list
+                                       'Redirect Test2 Worse Result',
+                                       'Redirect test2',
+                               ),
+                               'query' => 'redirect TEST2',
+                               'results' => array(
+                                       // Found redirect is pulled to the top and exact match isn't
+                                       // added
+                                       'Redirect test2',
+                                       'Redirect Test2 Worse Result',
+                               ),
+                       ) ),
+                       array( array(
+                               "Exact match should override any already found matches that " .
+                                       "are redirects to it",
+                               'provision' => array(
+                                       // Another redirect to the same target as the exact match
+                                       // is low in the list
+                                       'Redirect Test Worse Result',
+                                       'Redirect test',
+                               ),
+                               'query' => 'Redirect Test',
+                               'results' => array(
+                                       // Found redirect is pulled to the top and exact match isn't
+                                       // added
+                                       'Redirect Test',
+                                       'Redirect Test Worse Result',
+                                       'Redirect test',
+                               ),
+                       ) ),
+               );
+       }
+
+       /**
+        * @dataProvider provideSearchBackend
+        * @covers PrefixSearch::searchBackend
+        */
+       public function testSearchBackend( Array $case ) {
+               $search = $stub = $this->getMockBuilder( 'SearchEngine' )
+                       ->setMethods( array( 'completionSearchBackend' ) )->getMock();
+
+               $return = SearchSuggestionSet::fromStrings( $case['provision'] );
+
+               $search->expects( $this->any() )
+                       ->method( 'completionSearchBackend' )
+                       ->will( $this->returnValue( $return ) );
+
+               $search->setLimitOffset( 3 );
+               $results = $search->completionSearch( $case['query'] );
+
+               $results = $results->map( function( SearchSuggestion $s ) {
+                       return $s->getText();
+               } );
+
+               $this->assertEquals(
+                       $case['results'],
+                       $results,
+                       $case[0]
+               );
+       }
+}
diff --git a/tests/phpunit/includes/search/SearchSuggestionSetTest.php b/tests/phpunit/includes/search/SearchSuggestionSetTest.php
new file mode 100644 (file)
index 0000000..60559fc
--- /dev/null
@@ -0,0 +1,104 @@
+<?php
+
+/**
+ * Test for filter utilities.
+ *
+ * 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
+ */
+
+class SearchSuggestionSetTest extends \PHPUnit_Framework_TestCase {
+       /**
+        * Test that adding a new suggestion at the end
+        * will keep proper score ordering
+        */
+       public function testAppend() {
+               $set = SearchSuggestionSet::emptySuggestionSet();
+               $this->assertEquals( 0, $set->getSize() );
+               $set->append( new SearchSuggestion( 3 ) );
+               $this->assertEquals( 3, $set->getWorstScore() );
+               $this->assertEquals( 3, $set->getBestScore() );
+
+               $suggestion = new SearchSuggestion( 4 );
+               $set->append( $suggestion );
+               $this->assertEquals( 2, $set->getWorstScore() );
+               $this->assertEquals( 3, $set->getBestScore() );
+               $this->assertEquals( 2, $suggestion->getScore() );
+
+               $suggestion = new SearchSuggestion( 2 );
+               $set->append( $suggestion );
+               $this->assertEquals( 1, $set->getWorstScore() );
+               $this->assertEquals( 3, $set->getBestScore() );
+               $this->assertEquals( 1, $suggestion->getScore() );
+
+               $scores = $set->map( function( $s ) {
+                       return $s->getScore();
+               } );
+               $sorted = $scores;
+               asort( $sorted );
+               $this->assertEquals( $sorted, $scores );
+       }
+
+       /**
+        * Test that adding a new best suggestion will keep proper score
+        * ordering
+        */
+       public function testInsertBest() {
+               $set = SearchSuggestionSet::emptySuggestionSet();
+               $this->assertEquals( 0, $set->getSize() );
+               $set->prepend( new SearchSuggestion( 3 ) );
+               $this->assertEquals( 3, $set->getWorstScore() );
+               $this->assertEquals( 3, $set->getBestScore() );
+
+               $suggestion = new SearchSuggestion( 4 );
+               $set->prepend( $suggestion );
+               $this->assertEquals( 3, $set->getWorstScore() );
+               $this->assertEquals( 4, $set->getBestScore() );
+               $this->assertEquals( 4, $suggestion->getScore() );
+
+               $suggestion = new SearchSuggestion( 0 );
+               $set->prepend( $suggestion );
+               $this->assertEquals( 3, $set->getWorstScore() );
+               $this->assertEquals( 5, $set->getBestScore() );
+               $this->assertEquals( 5, $suggestion->getScore() );
+
+               $suggestion = new SearchSuggestion( 2 );
+               $set->prepend( $suggestion );
+               $this->assertEquals( 3, $set->getWorstScore() );
+               $this->assertEquals( 6, $set->getBestScore() );
+               $this->assertEquals( 6, $suggestion->getScore() );
+
+               $scores = $set->map( function( $s ) {
+                       return $s->getScore();
+               } );
+               $sorted = $scores;
+               asort( $sorted );
+               $this->assertEquals( $sorted, $scores );
+       }
+
+       public function testShrink() {
+               $set = SearchSuggestionSet::emptySuggestionSet();
+               for ( $i = 0; $i < 100; $i++ ) {
+                       $set->append( new SearchSuggestion( 0 ) );
+               }
+               $set->shrink( 10 );
+               $this->assertEquals( 10, $set->getSize() );
+
+               $set->shrink( 0 );
+               $this->assertEquals( 0, $set->getSize() );
+       }
+
+       // TODO: test for fromTitles
+}
index 52872a4..e1ba0ba 100644 (file)
@@ -207,11 +207,7 @@ class BotPasswordSessionProviderTest extends MediaWikiTestCase {
        }
 
        public function testCheckSessionInfo() {
-               $logger = new \TestLogger( true, function ( $m ) {
-                       return preg_replace(
-                               '/^Session \[\d+\][a-zA-Z0-9_\\\\]+<(?:null|anon|[+-]:\d+:\w+)>\w+: /', 'Session X: ', $m
-                       );
-               } );
+               $logger = new \TestLogger( true );
                $provider = $this->getProvider();
                $provider->setLogger( $logger );
 
@@ -242,7 +238,7 @@ class BotPasswordSessionProviderTest extends MediaWikiTestCase {
 
                        $this->assertFalse( $provider->refreshSessionInfo( $info, $request, $metadata ) );
                        $this->assertSame( array(
-                               array( LogLevel::INFO, "Session X: Missing metadata: $key" )
+                               array( LogLevel::INFO, 'Session "{session}": Missing metadata: {missing}' )
                        ), $logger->getBuffer() );
                        $logger->clearBuffer();
                }
@@ -253,7 +249,7 @@ class BotPasswordSessionProviderTest extends MediaWikiTestCase {
                $metadata = $info->getProviderMetadata();
                $this->assertFalse( $provider->refreshSessionInfo( $info, $request, $metadata ) );
                $this->assertSame( array(
-                       array( LogLevel::INFO, "Session X: No BotPassword for {$bp->getUserCentralId()} Foobar" ),
+                       array( LogLevel::INFO, 'Session "{session}": No BotPassword for {centralId} {appId}' ),
                ), $logger->getBuffer() );
                $logger->clearBuffer();
 
@@ -263,7 +259,7 @@ class BotPasswordSessionProviderTest extends MediaWikiTestCase {
                $metadata = $info->getProviderMetadata();
                $this->assertFalse( $provider->refreshSessionInfo( $info, $request, $metadata ) );
                $this->assertSame( array(
-                       array( LogLevel::INFO, 'Session X: BotPassword token check failed' ),
+                       array( LogLevel::INFO, 'Session "{session}": BotPassword token check failed' ),
                ), $logger->getBuffer() );
                $logger->clearBuffer();
 
@@ -275,7 +271,7 @@ class BotPasswordSessionProviderTest extends MediaWikiTestCase {
                $metadata = $info->getProviderMetadata();
                $this->assertFalse( $provider->refreshSessionInfo( $info, $request2, $metadata ) );
                $this->assertSame( array(
-                       array( LogLevel::INFO, 'Session X: Restrictions check failed' ),
+                       array( LogLevel::INFO, 'Session "{session}": Restrictions check failed' ),
                ), $logger->getBuffer() );
                $logger->clearBuffer();
 
index e5df458..659826f 100644 (file)
@@ -4,6 +4,7 @@ namespace MediaWiki\Session;
 
 use MediaWikiTestCase;
 use User;
+use Psr\Log\LogLevel;
 
 /**
  * @group Session
@@ -159,7 +160,8 @@ class CookieSessionProviderTest extends MediaWikiTestCase {
                        'cookieOptions' => array( 'prefix' => 'x' ),
                );
                $provider = new CookieSessionProvider( $params );
-               $provider->setLogger( new \TestLogger() );
+               $logger = new \TestLogger( true );
+               $provider->setLogger( $logger );
                $provider->setConfig( $this->getConfig() );
                $provider->setManager( new SessionManager() );
 
@@ -174,6 +176,8 @@ class CookieSessionProviderTest extends MediaWikiTestCase {
                $request = new \FauxRequest();
                $info = $provider->provideSessionInfo( $request );
                $this->assertNull( $info );
+               $this->assertSame( array(), $logger->getBuffer() );
+               $logger->clearBuffer();
 
                // Session key only
                $request = new \FauxRequest();
@@ -188,6 +192,13 @@ class CookieSessionProviderTest extends MediaWikiTestCase {
                $this->assertSame( 0, $info->getUserInfo()->getId() );
                $this->assertNull( $info->getUserInfo()->getName() );
                $this->assertFalse( $info->forceHTTPS() );
+               $this->assertSame( array(
+                       array(
+                               LogLevel::DEBUG,
+                               'Session "{session}" requested without UserID cookie',
+                       ),
+               ), $logger->getBuffer() );
+               $logger->clearBuffer();
 
                // User, no session key
                $request = new \FauxRequest();
@@ -203,6 +214,8 @@ class CookieSessionProviderTest extends MediaWikiTestCase {
                $this->assertSame( $id, $info->getUserInfo()->getId() );
                $this->assertSame( $name, $info->getUserInfo()->getName() );
                $this->assertFalse( $info->forceHTTPS() );
+               $this->assertSame( array(), $logger->getBuffer() );
+               $logger->clearBuffer();
 
                // User and session key
                $request = new \FauxRequest();
@@ -219,6 +232,8 @@ class CookieSessionProviderTest extends MediaWikiTestCase {
                $this->assertSame( $id, $info->getUserInfo()->getId() );
                $this->assertSame( $name, $info->getUserInfo()->getName() );
                $this->assertFalse( $info->forceHTTPS() );
+               $this->assertSame( array(), $logger->getBuffer() );
+               $logger->clearBuffer();
 
                // User with bad token
                $request = new \FauxRequest();
@@ -229,6 +244,13 @@ class CookieSessionProviderTest extends MediaWikiTestCase {
                ), '' );
                $info = $provider->provideSessionInfo( $request );
                $this->assertNull( $info );
+               $this->assertSame( array(
+                       array(
+                               LogLevel::WARNING,
+                               'Session "{session}" requested with invalid Token cookie.'
+                       ),
+               ), $logger->getBuffer() );
+               $logger->clearBuffer();
 
                // User id with no token
                $request = new \FauxRequest();
@@ -245,6 +267,8 @@ class CookieSessionProviderTest extends MediaWikiTestCase {
                $this->assertSame( $id, $info->getUserInfo()->getId() );
                $this->assertSame( $name, $info->getUserInfo()->getName() );
                $this->assertFalse( $info->forceHTTPS() );
+               $this->assertSame( array(), $logger->getBuffer() );
+               $logger->clearBuffer();
 
                $request = new \FauxRequest();
                $request->setCookies( array(
@@ -252,6 +276,8 @@ class CookieSessionProviderTest extends MediaWikiTestCase {
                ), '' );
                $info = $provider->provideSessionInfo( $request );
                $this->assertNull( $info );
+               $this->assertSame( array(), $logger->getBuffer() );
+               $logger->clearBuffer();
 
                // User and session key, with forceHTTPS flag
                $request = new \FauxRequest();
@@ -269,6 +295,8 @@ class CookieSessionProviderTest extends MediaWikiTestCase {
                $this->assertSame( $id, $info->getUserInfo()->getId() );
                $this->assertSame( $name, $info->getUserInfo()->getName() );
                $this->assertTrue( $info->forceHTTPS() );
+               $this->assertSame( array(), $logger->getBuffer() );
+               $logger->clearBuffer();
 
                // Invalid user id
                $request = new \FauxRequest();
@@ -278,6 +306,8 @@ class CookieSessionProviderTest extends MediaWikiTestCase {
                ), '' );
                $info = $provider->provideSessionInfo( $request );
                $this->assertNull( $info );
+               $this->assertSame( array(), $logger->getBuffer() );
+               $logger->clearBuffer();
 
                // User id with matching name
                $request = new \FauxRequest();
@@ -295,6 +325,8 @@ class CookieSessionProviderTest extends MediaWikiTestCase {
                $this->assertSame( $id, $info->getUserInfo()->getId() );
                $this->assertSame( $name, $info->getUserInfo()->getName() );
                $this->assertFalse( $info->forceHTTPS() );
+               $this->assertSame( array(), $logger->getBuffer() );
+               $logger->clearBuffer();
 
                // User id with wrong name
                $request = new \FauxRequest();
@@ -305,6 +337,13 @@ class CookieSessionProviderTest extends MediaWikiTestCase {
                ), '' );
                $info = $provider->provideSessionInfo( $request );
                $this->assertNull( $info );
+               $this->assertSame( array(
+                       array(
+                               LogLevel::WARNING,
+                               'Session "{session}" requested with mismatched UserID and UserName cookies.',
+                       ),
+               ), $logger->getBuffer() );
+               $logger->clearBuffer();
        }
 
        public function testGetVaryCookies() {
@@ -352,7 +391,7 @@ class CookieSessionProviderTest extends MediaWikiTestCase {
                $provider->setManager( SessionManager::singleton() );
 
                $sessionId = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
-               $store = new \HashBagOStuff();
+               $store = new TestBagOStuff();
                $user = User::newFromName( 'UTSysop' );
                $anon = new User;
 
@@ -365,7 +404,6 @@ class CookieSessionProviderTest extends MediaWikiTestCase {
                                'idIsSafe' => true,
                        ) ),
                        $store,
-                       $store,
                        new \Psr\Log\NullLogger(),
                        10
                );
@@ -451,8 +489,7 @@ class CookieSessionProviderTest extends MediaWikiTestCase {
                                'persisted' => true,
                                'idIsSafe' => true,
                        ) ),
-                       new \EmptyBagOStuff(),
-                       new \EmptyBagOStuff(),
+                       new TestBagOStuff(),
                        new \Psr\Log\NullLogger(),
                        10
                );
@@ -531,8 +568,6 @@ class CookieSessionProviderTest extends MediaWikiTestCase {
        }
 
        public function testPersistSessionWithHook() {
-               $that = $this;
-
                $provider = new CookieSessionProvider( array(
                        'priority' => 1,
                        'sessionName' => 'MySessionName',
@@ -544,7 +579,7 @@ class CookieSessionProviderTest extends MediaWikiTestCase {
                $provider->setManager( SessionManager::singleton() );
 
                $sessionId = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
-               $store = new \HashBagOStuff();
+               $store = new TestBagOStuff();
                $user = User::newFromName( 'UTSysop' );
                $anon = new User;
 
@@ -557,7 +592,6 @@ class CookieSessionProviderTest extends MediaWikiTestCase {
                                'idIsSafe' => true,
                        ) ),
                        $store,
-                       $store,
                        new \Psr\Log\NullLogger(),
                        10
                );
@@ -584,14 +618,14 @@ class CookieSessionProviderTest extends MediaWikiTestCase {
                // Logged-in user, no remember
                $mock = $this->getMock( __CLASS__, array( 'onUserSetCookies' ) );
                $mock->expects( $this->once() )->method( 'onUserSetCookies' )
-                       ->will( $this->returnCallback( function ( $u, &$sessionData, &$cookies ) use ( $that, $user ) {
-                               $that->assertSame( $user, $u );
-                               $that->assertEquals( array(
+                       ->will( $this->returnCallback( function ( $u, &$sessionData, &$cookies ) use ( $user ) {
+                               $this->assertSame( $user, $u );
+                               $this->assertEquals( array(
                                        'wsUserID' => $user->getId(),
                                        'wsUserName' => $user->getName(),
                                        'wsToken' => $user->getToken(),
                                ), $sessionData );
-                               $that->assertEquals( array(
+                               $this->assertEquals( array(
                                        'UserID' => $user->getId(),
                                        'UserName' => $user->getName(),
                                        'Token' => false,
@@ -627,14 +661,14 @@ class CookieSessionProviderTest extends MediaWikiTestCase {
                // Logged-in user, remember
                $mock = $this->getMock( __CLASS__, array( 'onUserSetCookies' ) );
                $mock->expects( $this->once() )->method( 'onUserSetCookies' )
-                       ->will( $this->returnCallback( function ( $u, &$sessionData, &$cookies ) use ( $that, $user ) {
-                               $that->assertSame( $user, $u );
-                               $that->assertEquals( array(
+                       ->will( $this->returnCallback( function ( $u, &$sessionData, &$cookies ) use ( $user ) {
+                               $this->assertSame( $user, $u );
+                               $this->assertEquals( array(
                                        'wsUserID' => $user->getId(),
                                        'wsUserName' => $user->getName(),
                                        'wsToken' => $user->getToken(),
                                ), $sessionData );
-                               $that->assertEquals( array(
+                               $this->assertEquals( array(
                                        'UserID' => $user->getId(),
                                        'UserName' => $user->getName(),
                                        'Token' => $user->getToken(),
index 46f23f3..95f8e01 100644 (file)
@@ -204,8 +204,7 @@ class ImmutableSessionProviderWithCookieTest extends MediaWikiTestCase {
                                'userInfo' => UserInfo::newFromUser( $user, true ),
                                'idIsSafe' => true,
                        ) ),
-                       new \EmptyBagOStuff(),
-                       new \EmptyBagOStuff(),
+                       new TestBagOStuff(),
                        new \Psr\Log\NullLogger(),
                        10
                );
index 1c54a20..e071132 100644 (file)
@@ -78,7 +78,7 @@ class PHPSessionHandlerTest extends MediaWikiTestCase {
                ini_set( 'session.use_cookies', 1 );
                ini_set( 'session.use_trans_sid', 1 );
 
-               $store = new \HashBagOStuff();
+               $store = new TestBagOStuff();
                $logger = new \TestLogger();
                $manager = new SessionManager( array(
                        'store' => $store,
@@ -112,9 +112,10 @@ class PHPSessionHandlerTest extends MediaWikiTestCase {
                        'wgObjectCacheSessionExpiry' => 2,
                ) );
 
-               $store = new \HashBagOStuff();
+               $store = new TestBagOStuff();
                $logger = new \TestLogger( true, function ( $m ) {
-                       return preg_match( '/^SessionBackend a{32} /', $m ) ? null : $m;
+                       // Discard all log events starting with expected prefix
+                       return preg_match( '/^SessionBackend "\{session\}" /', $m ) ? null : $m;
                } );
                $manager = new SessionManager( array(
                        'store' => $store,
@@ -172,6 +173,14 @@ class PHPSessionHandlerTest extends MediaWikiTestCase {
                        $this->assertSame( $expect, $_SESSION );
                }
 
+               // Test expiry
+               session_write_close();
+               ini_set( 'session.gc_divisor', 1 );
+               ini_set( 'session.gc_probability', 1 );
+               sleep( 3 );
+               session_start();
+               $this->assertSame( array(), $_SESSION );
+
                // Re-fill the session, then test that session_destroy() works.
                $_SESSION['AuthenticationSessionTest'] = $rand;
                session_write_close();
index 85fa9bd..481b693 100644 (file)
@@ -59,7 +59,7 @@ class SessionBackendTest extends MediaWikiTestCase {
                ) );
                $id = new SessionId( $info->getId() );
 
-               $backend = new SessionBackend( $id, $info, $this->store, $this->store, $logger, 10 );
+               $backend = new SessionBackend( $id, $info, $this->store, $logger, 10 );
                $priv = \TestingAccessWrapper::newFromObject( $backend );
                $priv->persist = false;
                $priv->requests = array( 100 => new \FauxRequest() );
@@ -87,7 +87,7 @@ class SessionBackendTest extends MediaWikiTestCase {
                $id = new SessionId( $info->getId() );
                $logger = new \Psr\Log\NullLogger();
                try {
-                       new SessionBackend( $id, $info, $this->store, $this->store, $logger, 10 );
+                       new SessionBackend( $id, $info, $this->store, $logger, 10 );
                        $this->fail( 'Expected exception not thrown' );
                } catch ( \InvalidArgumentException $ex ) {
                        $this->assertSame(
@@ -103,7 +103,7 @@ class SessionBackendTest extends MediaWikiTestCase {
                ) );
                $id = new SessionId( $info->getId() );
                try {
-                       new SessionBackend( $id, $info, $this->store, $this->store, $logger, 10 );
+                       new SessionBackend( $id, $info, $this->store, $logger, 10 );
                        $this->fail( 'Expected exception not thrown' );
                } catch ( \InvalidArgumentException $ex ) {
                        $this->assertSame( 'Cannot create session without a provider', $ex->getMessage() );
@@ -118,7 +118,7 @@ class SessionBackendTest extends MediaWikiTestCase {
                ) );
                $id = new SessionId( '!' . $info->getId() );
                try {
-                       new SessionBackend( $id, $info, $this->store, $this->store, $logger, 10 );
+                       new SessionBackend( $id, $info, $this->store, $logger, 10 );
                        $this->fail( 'Expected exception not thrown' );
                } catch ( \InvalidArgumentException $ex ) {
                        $this->assertSame(
@@ -135,7 +135,7 @@ class SessionBackendTest extends MediaWikiTestCase {
                        'idIsSafe' => true,
                ) );
                $id = new SessionId( $info->getId() );
-               $backend = new SessionBackend( $id, $info, $this->store, $this->store, $logger, 10 );
+               $backend = new SessionBackend( $id, $info, $this->store, $logger, 10 );
                $this->assertSame( self::SESSIONID, $backend->getId() );
                $this->assertSame( $id, $backend->getSessionId() );
                $this->assertSame( $this->provider, $backend->getProvider() );
@@ -157,7 +157,7 @@ class SessionBackendTest extends MediaWikiTestCase {
                        'idIsSafe' => true,
                ) );
                $id = new SessionId( $info->getId() );
-               $backend = new SessionBackend( $id, $info, $this->store, $this->store, $logger, 10 );
+               $backend = new SessionBackend( $id, $info, $this->store, $logger, 10 );
                $this->assertSame( self::SESSIONID, $backend->getId() );
                $this->assertSame( $id, $backend->getSessionId() );
                $this->assertSame( $this->provider, $backend->getProvider() );
@@ -468,6 +468,8 @@ class SessionBackendTest extends MediaWikiTestCase {
                $this->assertInternalType( 'array', $metadata );
                $this->assertArrayHasKey( '???', $metadata );
                $this->assertSame( '!!!', $metadata['???'] );
+               $this->assertFalse( $this->store->getSessionFromBackend( self::SESSIONID ),
+                       'making sure it didn\'t save to backend' );
 
                // Persistent, not dirty
                $this->provider = $neverProvider;
@@ -516,6 +518,8 @@ class SessionBackendTest extends MediaWikiTestCase {
                $this->assertInternalType( 'array', $metadata );
                $this->assertArrayHasKey( '???', $metadata );
                $this->assertSame( '!!!', $metadata['???'] );
+               $this->assertNotSame( false, $this->store->getSessionFromBackend( self::SESSIONID ),
+                       'making sure it did save to backend' );
 
                $this->provider = $this->getMock( 'DummySessionProvider', array( 'persistSession' ) );
                $this->provider->expects( $this->atLeastOnce() )->method( 'persistSession' );
@@ -538,6 +542,8 @@ class SessionBackendTest extends MediaWikiTestCase {
                $this->assertInternalType( 'array', $metadata );
                $this->assertArrayHasKey( '???', $metadata );
                $this->assertSame( '!!!', $metadata['???'] );
+               $this->assertNotSame( false, $this->store->getSessionFromBackend( self::SESSIONID ),
+                       'making sure it did save to backend' );
 
                $this->provider = $this->getMock( 'DummySessionProvider', array( 'persistSession' ) );
                $this->provider->expects( $this->atLeastOnce() )->method( 'persistSession' );
@@ -559,8 +565,11 @@ class SessionBackendTest extends MediaWikiTestCase {
                $this->assertInternalType( 'array', $metadata );
                $this->assertArrayHasKey( '???', $metadata );
                $this->assertSame( '!!!', $metadata['???'] );
+               $this->assertNotSame( false, $this->store->getSessionFromBackend( self::SESSIONID ),
+                       'making sure it did save to backend' );
 
                // Not marked dirty, but dirty data
+               // (e.g. indirect modification from ArrayAccess::offsetGet)
                $this->provider = $neverProvider;
                $this->onSessionMetadataCalled = false;
                $this->mergeMwGlobalArrayValue( 'wgHooks', array( 'SessionMetadata' => array( $this ) ) );
@@ -581,6 +590,8 @@ class SessionBackendTest extends MediaWikiTestCase {
                $this->assertInternalType( 'array', $metadata );
                $this->assertArrayHasKey( '???', $metadata );
                $this->assertSame( '!!!', $metadata['???'] );
+               $this->assertNotSame( false, $this->store->getSessionFromBackend( self::SESSIONID ),
+                       'making sure it did save to backend' );
 
                // Bad hook
                $this->provider = null;
index b411f3c..f9c9b8e 100644 (file)
@@ -2,7 +2,6 @@
 
 namespace MediaWiki\Session;
 
-use Psr\Log\LogLevel;
 use MediaWikiTestCase;
 
 /**
index 4fde341..e618644 100644 (file)
@@ -2,8 +2,10 @@
 
 namespace MediaWiki\Session;
 
-use Psr\Log\LogLevel;
+use AuthPlugin;
+use MediaWiki\Logger\LoggerFactory;
 use MediaWikiTestCase;
+use Psr\Log\LogLevel;
 use User;
 
 /**
@@ -103,7 +105,7 @@ class SessionManagerTest extends MediaWikiTestCase {
                $manager = \TestingAccessWrapper::newFromObject( $this->getManager() );
                $this->assertSame( $this->config, $manager->config );
                $this->assertSame( $this->logger, $manager->logger );
-               $this->assertSame( $this->store, $manager->permStore );
+               $this->assertSame( $this->store, $manager->store );
 
                $manager = \TestingAccessWrapper::newFromObject( new SessionManager() );
                $this->assertSame( \RequestContext::getMain()->getConfig(), $manager->config );
@@ -111,7 +113,7 @@ class SessionManagerTest extends MediaWikiTestCase {
                $manager = \TestingAccessWrapper::newFromObject( new SessionManager( array(
                        'config' => $this->config,
                ) ) );
-               $this->assertSame( \ObjectCache::$instances['testSessionStore'], $manager->permStore );
+               $this->assertSame( \ObjectCache::$instances['testSessionStore'], $manager->store );
 
                foreach ( array(
                        'config' => '$options[\'config\'] must be an instance of Config',
@@ -300,10 +302,6 @@ class SessionManagerTest extends MediaWikiTestCase {
 
        public function testGetSessionById() {
                $manager = $this->getManager();
-
-               // Disable the in-process cache so our $this->store->setSession() takes effect.
-               \TestingAccessWrapper::newFromObject( $manager )->tempStore = new \EmptyBagOStuff;
-
                try {
                        $manager->getSessionById( 'bad' );
                        $this->fail( 'Expected exception not thrown' );
@@ -600,7 +598,6 @@ class SessionManagerTest extends MediaWikiTestCase {
                        'Bar' => array( 'X', 'Bar1', 3 => 'Bar2' ),
                        'Quux' => array( 'Quux' ),
                        'Baz' => array(),
-                       'Quux' => array( 'Quux' ),
                );
 
                $this->assertEquals( $expect, $manager->getVaryHeaders() );
@@ -764,10 +761,11 @@ class SessionManagerTest extends MediaWikiTestCase {
        public function testAutoCreateUser() {
                global $wgGroupPermissions;
 
-               $that = $this;
-
-               \ObjectCache::$instances[__METHOD__] = new \HashBagOStuff();
+               \ObjectCache::$instances[__METHOD__] = new TestBagOStuff();
                $this->setMwGlobals( array( 'wgMainCacheType' => __METHOD__ ) );
+               $this->setMWGlobals( array(
+                       'wgAuth' => new AuthPlugin,
+               ) );
 
                $this->stashMwGlobals( array( 'wgGroupPermissions' ) );
                $wgGroupPermissions['*']['createaccount'] = true;
@@ -783,7 +781,6 @@ class SessionManagerTest extends MediaWikiTestCase {
                                return null;
                        }
                        $m = str_replace( 'MediaWiki\Session\SessionManager::autoCreateUser: ', '', $m );
-                       $m = preg_replace( '/ - from: .*$/', ' - from: XXX', $m );
                        return $m;
                } );
                $manager->setLogger( $logger );
@@ -809,7 +806,7 @@ class SessionManagerTest extends MediaWikiTestCase {
                        $user->getId(), User::idFromName( 'UTSessionAutoCreate1', User::READ_LATEST )
                );
                $this->assertSame( array(
-                       array( LogLevel::INFO, 'creating new user (UTSessionAutoCreate1) - from: XXX' ),
+                       array( LogLevel::INFO, 'creating new user ({username}) - from: {url}' ),
                ), $logger->getBuffer() );
                $logger->clearBuffer();
 
@@ -823,7 +820,10 @@ class SessionManagerTest extends MediaWikiTestCase {
                $this->assertEquals( 0, User::idFromName( 'UTDoesNotExist', User::READ_LATEST ) );
                $session->clear();
                $this->assertSame( array(
-                       array( LogLevel::DEBUG, 'user is blocked from this wiki, blacklisting' ),
+                       array(
+                               LogLevel::DEBUG,
+                               'user is blocked from this wiki, blacklisting',
+                       ),
                ), $logger->getBuffer() );
                $logger->clearBuffer();
 
@@ -839,7 +839,7 @@ class SessionManagerTest extends MediaWikiTestCase {
                        $user->getId(), User::idFromName( 'UTSessionAutoCreate2', User::READ_LATEST )
                );
                $this->assertSame( array(
-                       array( LogLevel::INFO, 'creating new user (UTSessionAutoCreate2) - from: XXX' ),
+                       array( LogLevel::INFO, 'creating new user ({username}) - from: {url}' ),
                ), $logger->getBuffer() );
                $logger->clearBuffer();
 
@@ -877,7 +877,7 @@ class SessionManagerTest extends MediaWikiTestCase {
                        $user->getId(), User::idFromName( 'UTSessionAutoCreate3', User::READ_LATEST )
                );
                $this->assertSame( array(
-                       array( LogLevel::INFO, 'creating new user (UTSessionAutoCreate3) - from: XXX' ),
+                       array( LogLevel::INFO, 'creating new user ({username}) - from: {url}' ),
                ), $logger->getBuffer() );
                $logger->clearBuffer();
 
@@ -1011,10 +1011,10 @@ class SessionManagerTest extends MediaWikiTestCase {
                $logger->clearBuffer();
 
                // Sanity check that creation still works, and test completion hook
-               $cb = $this->callback( function ( User $user ) use ( $that ) {
-                       $that->assertNotEquals( 0, $user->getId() );
-                       $that->assertSame( 'UTSessionAutoCreate4', $user->getName() );
-                       $that->assertEquals(
+               $cb = $this->callback( function ( User $user ) {
+                       $this->assertNotEquals( 0, $user->getId() );
+                       $this->assertSame( 'UTSessionAutoCreate4', $user->getName() );
+                       $this->assertEquals(
                                $user->getId(), User::idFromName( 'UTSessionAutoCreate4', User::READ_LATEST )
                        );
                        return true;
@@ -1043,7 +1043,7 @@ class SessionManagerTest extends MediaWikiTestCase {
                        'LocalUserCreated' => array(),
                ) );
                $this->assertSame( array(
-                       array( LogLevel::INFO, 'creating new user (UTSessionAutoCreate4) - from: XXX' ),
+                       array( LogLevel::INFO, 'creating new user ({username}) - from: {url}' ),
                ), $logger->getBuffer() );
                $logger->clearBuffer();
        }
@@ -1067,28 +1067,17 @@ class SessionManagerTest extends MediaWikiTestCase {
                        $this->objectCacheDef( $provider1 ),
                ) );
 
-               $user = User::newFromName( 'UTSysop' );
-               $token = $user->getToken( true );
-
                $this->assertFalse( $manager->isUserSessionPrevented( 'UTSysop' ) );
                $manager->preventSessionsForUser( 'UTSysop' );
-               $this->assertNotEquals( $token, User::newFromName( 'UTSysop' )->getToken() );
                $this->assertTrue( $manager->isUserSessionPrevented( 'UTSysop' ) );
        }
 
        public function testLoadSessionInfoFromStore() {
                $manager = $this->getManager();
-               $logger = new \TestLogger( true, function ( $m ) {
-                       return preg_replace(
-                               '/^Session \[\d+\]\w+<(?:null|anon|[+-]:\d+:\w+)>\w+: /', 'Session X: ', $m
-                       );
-               } );
+               $logger = new \TestLogger( true );
                $manager->setLogger( $logger );
                $request = new \FauxRequest();
 
-               // Disable the in-process cache so our $this->store->setSession() takes effect.
-               \TestingAccessWrapper::newFromObject( $manager )->tempStore = new \EmptyBagOStuff;
-
                // TestingAccessWrapper can't handle methods with reference arguments, sigh.
                $rClass = new \ReflectionClass( $manager );
                $rMethod = $rClass->getMethod( 'loadSessionInfoFromStore' );
@@ -1124,7 +1113,7 @@ class SessionManagerTest extends MediaWikiTestCase {
                $provider->expects( $this->any() )->method( 'mergeMetadata' )
                        ->will( $this->returnCallback( function ( $a, $b ) {
                                if ( $b === array( 'Throw' ) ) {
-                                       throw new \UnexpectedValueException( 'no merge!' );
+                                       throw new MetadataMergeException( 'no merge!' );
                                }
                                return array( 'Merged' );
                        } ) );
@@ -1199,7 +1188,10 @@ class SessionManagerTest extends MediaWikiTestCase {
                $this->assertSame( $unverifiedUserInfo, $info->getUserInfo() );
                $this->assertFalse( $loadSessionInfoFromStore( $info ) );
                $this->assertSame( array(
-                       array( LogLevel::WARNING, 'Session X: Unverified user provided and no metadata to auth it' )
+                       array(
+                               LogLevel::WARNING,
+                               'Session "{session}": Unverified user provided and no metadata to auth it',
+                       )
                ), $logger->getBuffer() );
                $logger->clearBuffer();
 
@@ -1210,7 +1202,7 @@ class SessionManagerTest extends MediaWikiTestCase {
                ) );
                $this->assertFalse( $loadSessionInfoFromStore( $info ) );
                $this->assertSame( array(
-                       array( LogLevel::WARNING, 'Session X: Null provider and no metadata' ),
+                       array( LogLevel::WARNING, 'Session "{session}": Null provider and no metadata' ),
                ), $logger->getBuffer() );
                $logger->clearBuffer();
 
@@ -1233,7 +1225,7 @@ class SessionManagerTest extends MediaWikiTestCase {
                $this->assertFalse( $info->isIdSafe(), 'sanity check' );
                $this->assertFalse( $loadSessionInfoFromStore( $info ) );
                $this->assertSame( array(
-                       array( LogLevel::INFO, 'Session X: No user provided and provider cannot set user' )
+                       array( LogLevel::INFO, 'Session "{session}": No user provided and provider cannot set user' )
                ), $logger->getBuffer() );
                $logger->clearBuffer();
 
@@ -1241,14 +1233,14 @@ class SessionManagerTest extends MediaWikiTestCase {
                $this->store->setRawSession( $id, true );
                $this->assertFalse( $loadSessionInfoFromStore( $info ) );
                $this->assertSame( array(
-                       array( LogLevel::WARNING, 'Session X: Bad data' ),
+                       array( LogLevel::WARNING, 'Session "{session}": Bad data' ),
                ), $logger->getBuffer() );
                $logger->clearBuffer();
 
                $this->store->setRawSession( $id, array( 'data' => array() ) );
                $this->assertFalse( $loadSessionInfoFromStore( $info ) );
                $this->assertSame( array(
-                       array( LogLevel::WARNING, 'Session X: Bad data structure' ),
+                       array( LogLevel::WARNING, 'Session "{session}": Bad data structure' ),
                ), $logger->getBuffer() );
                $logger->clearBuffer();
 
@@ -1256,21 +1248,21 @@ class SessionManagerTest extends MediaWikiTestCase {
                $this->store->setRawSession( $id, array( 'metadata' => $metadata ) );
                $this->assertFalse( $loadSessionInfoFromStore( $info ) );
                $this->assertSame( array(
-                       array( LogLevel::WARNING, 'Session X: Bad data structure' ),
+                       array( LogLevel::WARNING, 'Session "{session}": Bad data structure' ),
                ), $logger->getBuffer() );
                $logger->clearBuffer();
 
                $this->store->setRawSession( $id, array( 'metadata' => $metadata, 'data' => true ) );
                $this->assertFalse( $loadSessionInfoFromStore( $info ) );
                $this->assertSame( array(
-                       array( LogLevel::WARNING, 'Session X: Bad data structure' ),
+                       array( LogLevel::WARNING, 'Session "{session}": Bad data structure' ),
                ), $logger->getBuffer() );
                $logger->clearBuffer();
 
                $this->store->setRawSession( $id, array( 'metadata' => true, 'data' => array() ) );
                $this->assertFalse( $loadSessionInfoFromStore( $info ) );
                $this->assertSame( array(
-                       array( LogLevel::WARNING, 'Session X: Bad data structure' ),
+                       array( LogLevel::WARNING, 'Session "{session}": Bad data structure' ),
                ), $logger->getBuffer() );
                $logger->clearBuffer();
 
@@ -1280,7 +1272,7 @@ class SessionManagerTest extends MediaWikiTestCase {
                        $this->store->setRawSession( $id, array( 'metadata' => $tmp, 'data' => array() ) );
                        $this->assertFalse( $loadSessionInfoFromStore( $info ) );
                        $this->assertSame( array(
-                               array( LogLevel::WARNING, 'Session X: Bad metadata' ),
+                               array( LogLevel::WARNING, 'Session "{session}": Bad metadata' ),
                        ), $logger->getBuffer() );
                        $logger->clearBuffer();
                }
@@ -1306,7 +1298,7 @@ class SessionManagerTest extends MediaWikiTestCase {
                ) );
                $this->assertFalse( $loadSessionInfoFromStore( $info ) );
                $this->assertSame( array(
-                       array( LogLevel::WARNING, 'Session X: Wrong provider, Bad !== Mock' ),
+                       array( LogLevel::WARNING, 'Session "{session}": Wrong provider Bad !== Mock' ),
                ), $logger->getBuffer() );
                $logger->clearBuffer();
 
@@ -1318,7 +1310,7 @@ class SessionManagerTest extends MediaWikiTestCase {
                ) );
                $this->assertFalse( $loadSessionInfoFromStore( $info ) );
                $this->assertSame( array(
-                       array( LogLevel::WARNING, 'Session X: Unknown provider, Bad' ),
+                       array( LogLevel::WARNING, 'Session "{session}": Unknown provider Bad' ),
                ), $logger->getBuffer() );
                $logger->clearBuffer();
 
@@ -1341,7 +1333,7 @@ class SessionManagerTest extends MediaWikiTestCase {
                ) );
                $this->assertFalse( $loadSessionInfoFromStore( $info ) );
                $this->assertSame( array(
-                       array( LogLevel::ERROR, 'Session X: Invalid ID' ),
+                       array( LogLevel::ERROR, 'Session "{session}": {exception}' ),
                ), $logger->getBuffer() );
                $logger->clearBuffer();
 
@@ -1354,7 +1346,7 @@ class SessionManagerTest extends MediaWikiTestCase {
                ) );
                $this->assertFalse( $loadSessionInfoFromStore( $info ) );
                $this->assertSame( array(
-                       array( LogLevel::ERROR, 'Session X: Invalid user name' ),
+                       array( LogLevel::ERROR, 'Session "{session}": {exception}', ),
                ), $logger->getBuffer() );
                $logger->clearBuffer();
 
@@ -1369,7 +1361,7 @@ class SessionManagerTest extends MediaWikiTestCase {
                ) );
                $this->assertFalse( $loadSessionInfoFromStore( $info ) );
                $this->assertSame( array(
-                       array( LogLevel::WARNING, 'Session X: User ID mismatch, 2 !== 1' ),
+                       array( LogLevel::WARNING, 'Session "{session}": User ID mismatch, {uid_a} !== {uid_b}' ),
                ), $logger->getBuffer() );
                $logger->clearBuffer();
 
@@ -1384,7 +1376,7 @@ class SessionManagerTest extends MediaWikiTestCase {
                ) );
                $this->assertFalse( $loadSessionInfoFromStore( $info ) );
                $this->assertSame( array(
-                       array( LogLevel::WARNING, 'Session X: User name mismatch, X !== UTSysop' ),
+                       array( LogLevel::WARNING, 'Session "{session}": User name mismatch, {uname_a} !== {uname_b}' ),
                ), $logger->getBuffer() );
                $logger->clearBuffer();
 
@@ -1400,7 +1392,8 @@ class SessionManagerTest extends MediaWikiTestCase {
                $this->assertFalse( $loadSessionInfoFromStore( $info ) );
                $this->assertSame( array(
                        array(
-                               LogLevel::WARNING, 'Session X: User ID matched but name didn\'t (rename?), X !== UTSysop'
+                               LogLevel::WARNING,
+                               'Session "{session}": User ID matched but name didn\'t (rename?), {uname_a} !== {uname_b}'
                        ),
                ), $logger->getBuffer() );
                $logger->clearBuffer();
@@ -1417,7 +1410,9 @@ class SessionManagerTest extends MediaWikiTestCase {
                $this->assertFalse( $loadSessionInfoFromStore( $info ) );
                $this->assertSame( array(
                        array(
-                               LogLevel::WARNING, 'Session X: Metadata has an anonymous user, but a non-anon user was provided'
+                               LogLevel::WARNING,
+                               'Session "{session}": Metadata has an anonymous user, ' .
+                               'but a non-anon user was provided',
                        ),
                ), $logger->getBuffer() );
                $logger->clearBuffer();
@@ -1501,7 +1496,7 @@ class SessionManagerTest extends MediaWikiTestCase {
                ) );
                $this->assertFalse( $loadSessionInfoFromStore( $info ) );
                $this->assertSame( array(
-                       array( LogLevel::WARNING, 'Session X: User token mismatch' ),
+                       array( LogLevel::WARNING, 'Session "{session}": User token mismatch' ),
                ), $logger->getBuffer() );
                $logger->clearBuffer();
 
@@ -1545,7 +1540,10 @@ class SessionManagerTest extends MediaWikiTestCase {
                ) );
                $this->assertFalse( $loadSessionInfoFromStore( $info ) );
                $this->assertSame( array(
-                       array( LogLevel::WARNING, 'Session X: Metadata merge failed: no merge!' ),
+                       array(
+                               LogLevel::WARNING,
+                               'Session "{session}": Metadata merge failed: {exception}',
+                       ),
                ), $logger->getBuffer() );
                $logger->clearBuffer();
 
@@ -1650,7 +1648,6 @@ class SessionManagerTest extends MediaWikiTestCase {
                $this->assertSame( array(), $logger->getBuffer() );
 
                // Hook
-               $that = $this;
                $called = false;
                $data = array( 'foo' => 1 );
                $this->store->setSession( $id, array( 'metadata' => $metadata, 'data' => $data ) );
@@ -1661,14 +1658,14 @@ class SessionManagerTest extends MediaWikiTestCase {
                ) );
                $this->mergeMwGlobalArrayValue( 'wgHooks', array(
                        'SessionCheckInfo' => array( function ( &$reason, $i, $r, $m, $d ) use (
-                               $that, $info, $metadata, $data, $request, &$called
+                               $info, $metadata, $data, $request, &$called
                        ) {
-                               $that->assertSame( $info->getId(), $i->getId() );
-                               $that->assertSame( $info->getProvider(), $i->getProvider() );
-                               $that->assertSame( $info->getUserInfo(), $i->getUserInfo() );
-                               $that->assertSame( $request, $r );
-                               $that->assertEquals( $metadata, $m );
-                               $that->assertEquals( $data, $d );
+                               $this->assertSame( $info->getId(), $i->getId() );
+                               $this->assertSame( $info->getProvider(), $i->getProvider() );
+                               $this->assertSame( $info->getUserInfo(), $i->getUserInfo() );
+                               $this->assertSame( $request, $r );
+                               $this->assertEquals( $metadata, $m );
+                               $this->assertEquals( $data, $d );
                                $called = true;
                                return false;
                        } )
@@ -1676,9 +1673,78 @@ class SessionManagerTest extends MediaWikiTestCase {
                $this->assertFalse( $loadSessionInfoFromStore( $info ) );
                $this->assertTrue( $called );
                $this->assertSame( array(
-                       array( LogLevel::WARNING, 'Session X: Hook aborted' ),
+                       array( LogLevel::WARNING, 'Session "{session}": Hook aborted' ),
                ), $logger->getBuffer() );
                $logger->clearBuffer();
        }
 
+       /**
+        * @dataProvider provideCheckIpLimits
+        */
+       public function testCheckIpLimits( $ip, $sessionData, $userData, $logLevel1, $logLevel2 ) {
+               $this->setMwGlobals( array(
+                       'wgSuspiciousIpPerSessionLimit' => 5,
+                       'wgSuspiciousIpPerUserLimit' => 10,
+                       'wgSuspiciousIpExpiry' => 600,
+                       'wgSquidServers' => array( '11.22.33.44' ),
+               ) );
+               $manager = new SessionManager();
+               $logger = $this->getMock( '\Psr\Log\LoggerInterface' );
+               $this->setLogger( 'session-ip', $logger );
+               $request = new \FauxRequest();
+               $request->setIP( $ip );
+
+               $session = $manager->getSessionForRequest( $request );
+               /** @var SessionBackend $backend */
+               $backend = \TestingAccessWrapper::newFromObject( $session )->backend;
+               $data = &$backend->getData();
+               $data = array( 'SessionManager-ip' => $sessionData );
+               $backend->setUser( User::newFromName( 'UTSysop' ) );
+               $manager = \TestingAccessWrapper::newFromObject( $manager );
+               $manager->store->set( 'SessionManager-ip:' . md5( 'UTSysop' ), $userData );
+
+               $logger->expects( $this->exactly( isset( $logLevel1 ) + isset( $logLevel2 ) ) )->method( 'log' );
+               if ( $logLevel1 ) {
+                       $logger->expects( $this->at( 0 ) )->method( 'log' )->with( $logLevel1,
+                               'Same session used from {count} IPs', $this->isType( 'array' ) );
+               }
+               if ( $logLevel2 ) {
+                       $logger->expects( $this->at( isset( $logLevel1 ) ) )->method( 'log' )->with( $logLevel2,
+                               'Same user had sessions from {count} IPs', $this->isType( 'array' ) );
+               }
+
+               $manager->checkIpLimits( $session );
+       }
+
+       public function provideCheckIpLimits() {
+               $future = time() + 1000;
+               $past = time() - 1000;
+               return array(
+                       // DEBUG log for first new IP
+                       array( '1.2.3.4', array(), array(), LogLevel::DEBUG, LogLevel::DEBUG ),
+                       // no log for same IP
+                       array( '1.2.3.4', array( '1.2.3.4'  => $future ), array( '1.2.3.4' => $future ),
+                                  null, null ),
+                       array( '1.2.3.4', array(), array( '1.2.3.4' => $future ),
+                                  LogLevel::DEBUG, null ),
+                       // INFO log for second new IP
+                       array( '1.2.3.4', array( '10.20.30.40'  => $future ), array( '10.20.30.40' => $future ),
+                          LogLevel::INFO, LogLevel::INFO ),
+                       // WARNING above $wgSuspiciousIpPerSessionLimit
+                       array( '1.2.3.4', array_fill_keys( range( 1, 5 ), $future ),
+                          array_fill_keys( range( 1, 5 ), $future ), LogLevel::WARNING, LogLevel::INFO ),
+                       // WARNING above $wgSuspiciousIpPerUserLimit
+
+                       array( '1.2.3.4', array_fill_keys( range( 1, 2 ), $future ),
+                                  array_fill_keys( range( 1, 12 ), $future ), LogLevel::INFO, LogLevel::WARNING ),
+                       // expired keys ignored
+                       array( '1.2.3.4', array( '1.2.3.4'  => $past ), array( '1.2.3.4' => $past ),
+                          LogLevel::DEBUG, LogLevel::DEBUG ),
+                       array( '1.2.3.4', array_fill_keys( range( 1, 5 ), $past ),
+                                  array_fill_keys( range( 1, 5 ), $past ), LogLevel::DEBUG, LogLevel::DEBUG ),
+                       // special IPs are ignored
+                       array( '127.0.0.1', array(), array(), null, null ),
+                       array( '11.22.33.44', array(), array(), null, null ),
+               );
+       }
 }
index d7aebcd..7d9004a 100644 (file)
@@ -89,8 +89,10 @@ class SessionProviderTest extends MediaWikiTestCase {
                                array( 'bar' => 2, 'baz' => '3' )
                        );
                        $this->fail( 'Expected exception not thrown' );
-               } catch ( \UnexpectedValueException $ex ) {
+               } catch ( MetadataMergeException $ex ) {
                        $this->assertSame( 'Key "baz" changed', $ex->getMessage() );
+                       $this->assertSame(
+                               [ 'old_value' => 3, 'new_value' => '3' ], $ex->getContext() );
                }
 
                $res = $provider->mergeMetadata(
index 858996d..3c5090a 100644 (file)
@@ -2,6 +2,7 @@
 
 namespace MediaWiki\Session;
 
+use Psr\Log\LogLevel;
 use MediaWikiTestCase;
 use User;
 
@@ -16,7 +17,7 @@ class SessionTest extends MediaWikiTestCase {
                \TestingAccessWrapper::newFromObject( $backend )->requests = array( -1 => 'dummy' );
                \TestingAccessWrapper::newFromObject( $backend )->id = new SessionId( 'abc' );
 
-               $session = new Session( $backend, 42 );
+               $session = new Session( $backend, 42, new \TestLogger );
                $priv = \TestingAccessWrapper::newFromObject( $session );
                $this->assertSame( $backend, $priv->backend );
                $this->assertSame( 42, $priv->index );
@@ -152,6 +153,71 @@ class SessionTest extends MediaWikiTestCase {
                $this->assertFalse( $backend->dirty );
        }
 
+       public function testArrayAccess() {
+               $logger = new \TestLogger;
+               $session = TestUtils::getDummySession( null, -1, $logger );
+               $backend = \TestingAccessWrapper::newFromObject( $session )->backend;
+
+               $this->assertEquals( 1, $session['foo'] );
+               $this->assertEquals( 'zero', $session[0] );
+               $this->assertFalse( $backend->dirty );
+
+               $logger->setCollect( true );
+               $this->assertEquals( null, $session['null'] );
+               $logger->setCollect( false );
+               $this->assertFalse( $backend->dirty );
+               $this->assertSame( array(
+                       array( LogLevel::DEBUG, 'Undefined index (auto-adds to session with a null value): null' )
+               ), $logger->getBuffer() );
+               $logger->clearBuffer();
+
+               $session['foo'] = 55;
+               $this->assertEquals( 55, $backend->data['foo'] );
+               $this->assertTrue( $backend->dirty );
+               $backend->dirty = false;
+
+               $session[1] = 'one';
+               $this->assertEquals( 'one', $backend->data[1] );
+               $this->assertTrue( $backend->dirty );
+               $backend->dirty = false;
+
+               $session[1] = 'one';
+               $this->assertFalse( $backend->dirty );
+
+               $session['bar'] = array( 'baz' => array() );
+               $session['bar']['baz']['quux'] = 2;
+               $this->assertEquals( array( 'baz' => array( 'quux' => 2 ) ), $backend->data['bar'] );
+
+               $logger->setCollect( true );
+               $session['bar2']['baz']['quux'] = 3;
+               $logger->setCollect( false );
+               $this->assertEquals( array( 'baz' => array( 'quux' => 3 ) ), $backend->data['bar2'] );
+               $this->assertSame( array(
+                       array( LogLevel::DEBUG, 'Undefined index (auto-adds to session with a null value): bar2' )
+               ), $logger->getBuffer() );
+               $logger->clearBuffer();
+
+               $backend->dirty = false;
+               $this->assertTrue( isset( $session['foo'] ) );
+               $this->assertTrue( isset( $session[1] ) );
+               $this->assertFalse( isset( $session['null'] ) );
+               $this->assertFalse( isset( $session['missing'] ) );
+               $this->assertFalse( isset( $session[100] ) );
+               $this->assertFalse( $backend->dirty );
+
+               unset( $session['foo'] );
+               $this->assertArrayNotHasKey( 'foo', $backend->data );
+               $this->assertTrue( $backend->dirty );
+               $backend->dirty = false;
+               unset( $session[1] );
+               $this->assertArrayNotHasKey( 1, $backend->data );
+               $this->assertTrue( $backend->dirty );
+               $backend->dirty = false;
+
+               unset( $session[101] );
+               $this->assertFalse( $backend->dirty );
+       }
+
        public function testClear() {
                $session = TestUtils::getDummySession();
                $priv = \TestingAccessWrapper::newFromObject( $session );
index e674e7b..8d1b544 100644 (file)
@@ -5,7 +5,11 @@ namespace MediaWiki\Session;
 /**
  * BagOStuff with utility functions for MediaWiki\\Session\\* testing
  */
-class TestBagOStuff extends \HashBagOStuff {
+class TestBagOStuff extends \CachedBagOStuff {
+
+       public function __construct() {
+               parent::__construct( new \HashBagOStuff );
+       }
 
        /**
         * @param string $id Session ID
@@ -68,6 +72,14 @@ class TestBagOStuff extends \HashBagOStuff {
                return $this->get( wfMemcKey( 'MWSession', $id ) );
        }
 
+       /**
+        * @param string $id Session ID
+        * @return mixed
+        */
+       public function getSessionFromBackend( $id ) {
+               return $this->backend->get( wfMemcKey( 'MWSession', $id ) );
+       }
+
        /**
         * @param string $id Session ID
         */
index 1619983..cc20ab5 100644 (file)
@@ -2,6 +2,8 @@
 
 namespace MediaWiki\Session;
 
+use Psr\Log\LoggerInterface;
+
 /**
  * Utility functions for Session unit tests
  */
@@ -67,7 +69,9 @@ class TestUtils {
                        );
                }
 
-               return $rc->newInstanceWithoutConstructor();
+               $ret = $rc->newInstanceWithoutConstructor();
+               \TestingAccessWrapper::newFromObject( $ret )->logger = new \TestLogger;
+               return $ret;
        }
 
        /**
@@ -75,9 +79,10 @@ class TestUtils {
         * construct one, use this.
         * @param object $backend Object to serve as the SessionBackend
         * @param int $index Index
+        * @param LoggerInterface $logger
         * @return Session
         */
-       public static function getDummySession( $backend = null, $index = -1 ) {
+       public static function getDummySession( $backend = null, $index = -1, $logger = null ) {
                $rc = new \ReflectionClass( 'MediaWiki\\Session\\Session' );
                if ( !method_exists( $rc, 'newInstanceWithoutConstructor' ) ) {
                        \PHPUnit_Framework_Assert::markTestSkipped(
@@ -93,6 +98,7 @@ class TestUtils {
                $priv = \TestingAccessWrapper::newFromObject( $session );
                $priv->backend = $backend;
                $priv->index = $index;
+               $priv->logger = $logger ?: new \TestLogger;
                return $session;
        }
 
index 4305ceb..dd62074 100644 (file)
@@ -95,18 +95,15 @@ class CachingSiteStoreTest extends MediaWikiTestCase {
                        ->disableOriginalConstructor()
                        ->getMock();
 
-               // php 5.3 compatibility!
-               $that = $this;
-
                $dbSiteStore->expects( $this->any() )
                        ->method( 'getSite' )
-                       ->will( $this->returnValue( $that->getTestSite() ) );
+                       ->will( $this->returnValue( $this->getTestSite() ) );
 
                $dbSiteStore->expects( $this->any() )
                        ->method( 'getSites' )
-                       ->will( $this->returnCallback( function() use ( $that ) {
+                       ->will( $this->returnCallback( function() {
                                $siteList = new SiteList();
-                               $siteList->setSite( $that->getTestSite() );
+                               $siteList->setSite( $this->getTestSite() );
 
                                return $siteList;
                        } ) );
index b11b1a9..a49f06c 100644 (file)
@@ -34,11 +34,10 @@ class SiteImporterTest extends PHPUnit_Framework_TestCase {
        private function newSiteImporter( array $expectedSites, $errorCount ) {
                $store = $this->getMock( 'SiteStore' );
 
-               $that = $this;
                $store->expects( $this->once() )
                        ->method( 'saveSites' )
-                       ->will( $this->returnCallback( function ( $sites ) use ( $expectedSites, $that ) {
-                               $that->assertSitesEqual( $expectedSites, $sites );
+                       ->will( $this->returnCallback( function ( $sites ) use ( $expectedSites ) {
+                               $this->assertSitesEqual( $expectedSites, $sites );
                        } ) );
 
                $store->expects( $this->any() )
diff --git a/tests/phpunit/languages/classes/LanguageGanTest.php b/tests/phpunit/languages/classes/LanguageGanTest.php
new file mode 100644 (file)
index 0000000..d548380
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+
+class LanguageGanTest extends LanguageClassesTestCase {
+       /**
+        * @dataProvider provideAutoConvertToAllVariants
+        * @covers Language::autoConvertToAllVariants
+        */
+       public function testAutoConvertToAllVariants( $result, $value ) {
+               $this->assertEquals( $result, $this->getLang()->autoConvertToAllVariants( $value ) );
+       }
+
+       public static function provideAutoConvertToAllVariants() {
+               return array(
+                       // zh2Hans
+                       array(
+                               array(
+                                       'gan' => '㑯',
+                                       'gan-hans' => '㑔',
+                                       'gan-hant' => '㑯',
+                               ),
+                               '㑯'
+                       ),
+                       // zh2Hant
+                       array(
+                               array(
+                                       'gan' => '㐷',
+                                       'gan-hans' => '㐷',
+                                       'gan-hant' => '傌',
+                               ),
+                               '㐷'
+                       ),
+               );
+       }
+}
diff --git a/tests/phpunit/languages/classes/LanguageIuTest.php b/tests/phpunit/languages/classes/LanguageIuTest.php
new file mode 100644 (file)
index 0000000..4599e7a
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+
+class LanguageIuTest extends LanguageClassesTestCase {
+       /**
+        * @dataProvider provideAutoConvertToAllVariants
+        * @covers Language::autoConvertToAllVariants
+        */
+       public function testAutoConvertToAllVariants( $result, $value ) {
+               $this->assertEquals( $result, $this->getLang()->autoConvertToAllVariants( $value ) );
+       }
+
+       public static function provideAutoConvertToAllVariants() {
+               return array(
+                       // ike-cans
+                       array(
+                               array(
+                                       'ike-cans' => 'ᐴ',
+                                       'ike-latn' => 'PUU',
+                                       'iu' => 'PUU',
+                               ),
+                               'PUU'
+                       ),
+                       // ike-latn
+                       array(
+                               array(
+                                       'ike-cans' => 'ᐴ',
+                                       'ike-latn' => 'puu',
+                                       'iu' => 'ᐴ',
+                               ),
+                               'ᐴ'
+                       ),
+               );
+       }
+}
diff --git a/tests/phpunit/languages/classes/LanguageKkTest.php b/tests/phpunit/languages/classes/LanguageKkTest.php
new file mode 100644 (file)
index 0000000..33968d9
--- /dev/null
@@ -0,0 +1,28 @@
+<?php
+
+class LanguageKkTest extends LanguageClassesTestCase {
+       /**
+        * @dataProvider provideAutoConvertToAllVariants
+        * @covers Language::autoConvertToAllVariants
+        */
+       public function testAutoConvertToAllVariants( $result, $value ) {
+               $this->assertEquals( $result, $this->getLang()->autoConvertToAllVariants( $value ) );
+       }
+
+       public static function provideAutoConvertToAllVariants() {
+               return array(
+                       array(
+                               array(
+                                       'kk'      => 'Адамдарға ақыл-парасат, ар-ождан берілген',
+                                       'kk-cyrl' => 'Адамдарға ақыл-парасат, ар-ождан берілген',
+                                       'kk-latn' => 'Adamdarğa aqıl-parasat, ar-ojdan berilgen',
+                                       'kk-arab' => 'ادامدارعا اقىل-پاراسات، ار-وجدان بەرىلگەن',
+                                       'kk-kz'   => 'Адамдарға ақыл-парасат, ар-ождан берілген',
+                                       'kk-tr'   => 'Adamdarğa aqıl-parasat, ar-ojdan berilgen',
+                                       'kk-cn'   => 'ادامدارعا اقىل-پاراسات، ار-وجدان بەرىلگەن'
+                               ),
+                               'Адамдарға ақыл-парасат, ар-ождан берілген'
+                       ),
+               );
+       }
+}
diff --git a/tests/phpunit/languages/classes/LanguageKuTest.php b/tests/phpunit/languages/classes/LanguageKuTest.php
new file mode 100644 (file)
index 0000000..a87fae7
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+
+class LanguageKuTest extends LanguageClassesTestCase {
+       /**
+        * @dataProvider provideAutoConvertToAllVariants
+        * @covers Language::autoConvertToAllVariants
+        */
+       public function testAutoConvertToAllVariants( $result, $value ) {
+               $this->assertEquals( $result, $this->getLang()->autoConvertToAllVariants( $value ) );
+       }
+
+       public static function provideAutoConvertToAllVariants() {
+               return array(
+                       array(
+                               array(
+                                       'ku'      => '١',
+                                       'ku-arab' => '١',
+                                       'ku-latn' => '1',
+                               ),
+                               '١'
+                       ),
+                       array(
+                               array(
+                                       'ku'      => 'Wîkîpediya ensîklopediyeke azad bi rengê wîkî ye.',
+                                       'ku-arab' => 'ویکیپەدیائە نسیکلۆپەدیەکەئا زاد ب رەنگێ ویکی یە.',
+                                       'ku-latn' => 'Wîkîpediya ensîklopediyeke azad bi rengê wîkî ye.',
+                               ),
+                               'Wîkîpediya ensîklopediyeke azad bi rengê wîkî ye.'
+                       ),
+                       array(
+                               array(
+                                       'ku'      => 'ویکیپەدیا ەنسیکلۆپەدیەکەئا زاد ب رەنگێ ویکی یە.',
+                                       'ku-arab' => 'ویکیپەدیا ەنسیکلۆپەدیەکەئا زاد ب رەنگێ ویکی یە.',
+                                       'ku-latn' => 'wîkîpedîa ensîklopedîekea zad b rengê wîkî îe.',
+                               ),
+                               'ویکیپەدیا ەنسیکلۆپەدیەکەئا زاد ب رەنگێ ویکی یە.'
+                       ),
+               );
+       }
+}
diff --git a/tests/phpunit/languages/classes/LanguageShiTest.php b/tests/phpunit/languages/classes/LanguageShiTest.php
new file mode 100644 (file)
index 0000000..90843c1
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+
+class LanguageShiTest extends LanguageClassesTestCase {
+       /**
+        * @dataProvider provideAutoConvertToAllVariants
+        * @covers Language::autoConvertToAllVariants
+        */
+       public function testAutoConvertToAllVariants( $result, $value ) {
+               $this->assertEquals( $result, $this->getLang()->autoConvertToAllVariants( $value ) );
+       }
+
+       public static function provideAutoConvertToAllVariants() {
+               return array(
+                       array(
+                               array(
+                                       'shi'      => 'AƔ',
+                                       'shi-tfng' => 'ⴰⵖ',
+                                       'shi-latn' => 'AƔ',
+                               ),
+                               'AƔ'
+                       ),
+                       array(
+                               array(
+                                       'shi'      => 'ⴰⵖ',
+                                       'shi-tfng' => 'ⴰⵖ',
+                                       'shi-latn' => 'aɣ',
+                               ),
+                               'ⴰⵖ'
+                       ),
+               );
+       }
+}
diff --git a/tests/phpunit/languages/classes/LanguageTgTest.php b/tests/phpunit/languages/classes/LanguageTgTest.php
new file mode 100644 (file)
index 0000000..ff3a7a4
--- /dev/null
@@ -0,0 +1,30 @@
+<?php
+
+class LanguageTgTest extends LanguageClassesTestCase {
+       /**
+        * @dataProvider provideAutoConvertToAllVariants
+        * @covers Language::autoConvertToAllVariants
+        */
+       public function testAutoConvertToAllVariants( $result, $value ) {
+               $this->assertEquals( $result, $this->getLang()->autoConvertToAllVariants( $value ) );
+       }
+
+       public static function provideAutoConvertToAllVariants() {
+               return array(
+                       array(
+                               array(
+                                       'tg'      => 'г',
+                                       'tg-latn' => 'g',
+                               ),
+                               'г'
+                       ),
+                       array(
+                               array(
+                                       'tg'      => 'g',
+                                       'tg-latn' => 'g',
+                               ),
+                               'g'
+                       ),
+               );
+       }
+}
diff --git a/tests/phpunit/languages/classes/LanguageZhTest.php b/tests/phpunit/languages/classes/LanguageZhTest.php
new file mode 100644 (file)
index 0000000..f19d967
--- /dev/null
@@ -0,0 +1,121 @@
+<?php
+
+class LanguageZhTest extends LanguageClassesTestCase {
+       /**
+        * @dataProvider provideAutoConvertToAllVariants
+        * @covers Language::autoConvertToAllVariants
+        */
+       public function testAutoConvertToAllVariants( $result, $value ) {
+               $this->assertEquals( $result, $this->getLang()->autoConvertToAllVariants( $value ) );
+       }
+
+       public static function provideAutoConvertToAllVariants() {
+               return array(
+                       // Plain hant -> hans
+                       array(
+                               array(
+                                       'zh'      => '㑯',
+                                       'zh-hans' => '㑔',
+                                       'zh-hant' => '㑯',
+                                       'zh-cn'   => '㑔',
+                                       'zh-hk'   => '㑯',
+                                       'zh-mo'   => '㑯',
+                                       'zh-my'   => '㑔',
+                                       'zh-sg'   => '㑔',
+                                       'zh-tw'   => '㑯',
+                               ),
+                               '㑯'
+                       ),
+                       // Plain hans -> hant
+                       array(
+                               array(
+                                       'zh'      => '㐷',
+                                       'zh-hans' => '㐷',
+                                       'zh-hant' => '傌',
+                                       'zh-cn'   => '㐷',
+                                       'zh-hk'   => '傌',
+                                       'zh-mo'   => '傌',
+                                       'zh-my'   => '㐷',
+                                       'zh-sg'   => '㐷',
+                                       'zh-tw'   => '傌',
+                               ),
+                               '㐷'
+                       ),
+                       // zh-cn specific
+                       array(
+                               array(
+                                       'zh'      => '仲介',
+                                       'zh-hans' => '仲介',
+                                       'zh-hant' => '仲介',
+                                       'zh-cn'   => '中介',
+                                       'zh-hk'   => '仲介',
+                                       'zh-mo'   => '仲介',
+                                       'zh-my'   => '中介',
+                                       'zh-sg'   => '中介',
+                                       'zh-tw'   => '仲介',
+                               ),
+                               '仲介'
+                       ),
+                       // zh-hk specific
+                       array(
+                               array(
+                                       'zh'      => '中文里',
+                                       'zh-hans' => '中文里',
+                                       'zh-hant' => '中文裡',
+                                       'zh-cn'   => '中文里',
+                                       'zh-hk'   => '中文裏',
+                                       'zh-mo'   => '中文裏',
+                                       'zh-my'   => '中文里',
+                                       'zh-sg'   => '中文里',
+                                       'zh-tw'   => '中文裡',
+                               ),
+                               '中文里'
+                       ),
+                       // zh-tw specific
+                       array(
+                               array(
+                                       'zh'      => '甲肝',
+                                       'zh-hans' => '甲肝',
+                                       'zh-hant' => '甲肝',
+                                       'zh-cn'   => '甲肝',
+                                       'zh-hk'   => '甲肝',
+                                       'zh-mo'   => '甲肝',
+                                       'zh-my'   => '甲肝',
+                                       'zh-sg'   => '甲肝',
+                                       'zh-tw'   => 'A肝',
+                               ),
+                               '甲肝'
+                       ),
+                       // zh-tw overrides zh-hant
+                       array(
+                               array(
+                                       'zh'      => '账',
+                                       'zh-hans' => '账',
+                                       'zh-hant' => '賬',
+                                       'zh-cn'   => '账',
+                                       'zh-hk'   => '賬',
+                                       'zh-mo'   => '賬',
+                                       'zh-my'   => '账',
+                                       'zh-sg'   => '账',
+                                       'zh-tw'   => '帳',
+                               ),
+                               '账'
+                       ),
+                       // zh-hk overrides zh-hant
+                       array(
+                               array(
+                                       'zh'      => '一地里',
+                                       'zh-hans' => '一地里',
+                                       'zh-hant' => '一地裡',
+                                       'zh-cn'   => '一地里',
+                                       'zh-hk'   => '一地裏',
+                                       'zh-mo'   => '一地裏',
+                                       'zh-my'   => '一地里',
+                                       'zh-sg'   => '一地里',
+                                       'zh-tw'   => '一地裡',
+                               ),
+                               '一地里'
+                       ),
+               );
+       }
+}
index 0ae0b21..81dc412 100755 (executable)
@@ -254,14 +254,6 @@ class PHPUnitMaintClass extends Maintenance {
 $maintClass = 'PHPUnitMaintClass';
 require RUN_MAINTENANCE_IF_MAIN;
 
-// Prevent segfault when we have lots of unit tests (bug 62623)
-if ( version_compare( PHP_VERSION, '5.4.0', '<' ) ) {
-       register_shutdown_function( function () {
-               gc_collect_cycles();
-               gc_disable();
-       } );
-}
-
 $ok = false;
 
 if ( class_exists( 'PHPUnit_TextUI_Command' ) ) {
@@ -302,4 +294,8 @@ if ( $puVersion !== '@package_version@' && version_compare( $puVersion, '3.7.0',
        exit( 1 );
 }
 
+echo defined( 'HHVM_VERSION' ) ?
+       'Using HHVM ' . HHVM_VERSION . ' (' . PHP_VERSION . ")\n" :
+       'Using PHP ' . PHP_VERSION . "\n";
+
 PHPUnit_TextUI_Command::main();
index d2f96dc..5cf75bd 100644 (file)
@@ -33,6 +33,9 @@ class ApiDocumentationTest extends MediaWikiTestCase {
                if ( !self::$main ) {
                        self::$main = new ApiMain( RequestContext::getMain() );
                        self::$main->getContext()->setLanguage( 'en' );
+                       self::$main->getContext()->setTitle(
+                               Title::makeTitle( NS_SPECIAL, 'Badtitle/dummy title for ApiDocumentationTest' )
+                       );
                }
                return self::$main;
        }
index 8674329..e5b7a93 100644 (file)
@@ -71,7 +71,7 @@ class AutoLoaderTest extends MediaWikiTestCase {
                        $matches = array();
                        preg_match_all( '/
                                ^ [\t ]* (?:
-                                       (?:final\s+)? (?:abstract\s+)? (?:class|interface) \s+
+                                       (?:final\s+)? (?:abstract\s+)? (?:class|interface|trait) \s+
                                        (?P<class> [a-zA-Z0-9_]+)
                                |
                                        class_alias \s* \( \s*
index 8887499..3361166 100644 (file)
@@ -1,4 +1,5 @@
 <?php
+use MediaWiki\Logger\LoggerFactory;
 
 /**
  * @covers MediaWikiTestCase
@@ -97,4 +98,37 @@ class MediaWikiTestCaseTest extends MediaWikiTestCase {
                $this->stashMwGlobals( self::GLOBAL_KEY_NONEXISTING );
        }
 
+       /**
+        * @covers MediaWikiTestCase::setLogger
+        * @covers MediaWikiTestCase::restoreLogger
+        */
+       public function testLoggersAreRestoredOnTearDown() {
+               // replacing an existing logger
+               $logger1 = LoggerFactory::getInstance( 'foo' );
+               $this->setLogger( 'foo', $this->getMock( '\Psr\Log\LoggerInterface' ) );
+               $logger2 = LoggerFactory::getInstance( 'foo' );
+               $this->tearDown();
+               $logger3 = LoggerFactory::getInstance( 'foo' );
+
+               $this->assertSame( $logger1, $logger3 );
+               $this->assertNotSame( $logger1, $logger2 );
+
+               // replacing a non-existing logger
+               $this->setLogger( 'bar', $this->getMock( '\Psr\Log\LoggerInterface' ) );
+               $logger1 = LoggerFactory::getInstance( 'bar' );
+               $this->tearDown();
+               $logger2 = LoggerFactory::getInstance( 'bar' );
+
+               $this->assertNotSame( $logger1, $logger2 );
+               $this->assertInstanceOf( '\Psr\Log\LoggerInterface', $logger2 );
+
+               // replacing same logger twice
+               $logger1 = LoggerFactory::getInstance( 'baz' );
+               $this->setLogger( 'baz', $this->getMock( '\Psr\Log\LoggerInterface' ) );
+               $this->setLogger( 'baz', $this->getMock( '\Psr\Log\LoggerInterface' ) );
+               $this->tearDown();
+               $logger2 = LoggerFactory::getInstance( 'baz' );
+
+               $this->assertSame( $logger1, $logger2 );
+       }
 }
index e7b45bd..07e6f26 100644 (file)
         * Configuration
         */
 
-       // When a test() indicates asynchronicity with stop(),
-       // allow 30 seconds to pass before killing the test(),
-       // and assuming failure.
-       QUnit.config.testTimeout = 30 * 1000;
+       // For each test() that is asynchronous, allow this time to pass before
+       // killing the test and assuming timeout failure.
+       QUnit.config.testTimeout = 60 * 1000;
 
        QUnit.config.requireExpects = true;
 
index cd0db7c..f2865eb 100644 (file)
@@ -16,7 +16,7 @@
 
                this.server.respondWith( /action=parse.*&text='''Hello\+world'''/, function ( request ) {
                        request.respond( 200, { 'Content-Type': 'application/json' },
-                               '{ "parse": { "text": { "*": "<p><b>Hello world</b></p>" } } }'
+                               '{ "parse": { "text": "<p><b>Hello world</b></p>" } }'
                        );
                } );
 
index 43b324e..07eddbf 100644 (file)
        /**
         * @param {Function[]} tasks List of functions that perform tasks
         *  that may be asynchronous. Invoke the callback parameter when done.
-        * @param {Function} complete Called when all tasks are done, or when the sequence is aborted.
         */
-       function process( tasks, complete ) {
+       function process( tasks ) {
                /*jshint latedef:false */
                function abort() {
                        tasks.splice( 0, tasks.length );
                        } else {
                                // Remove tasks list to indicate the process is final.
                                tasks = null;
-                               complete();
                        }
                }
                next();
                mw.messages.set( mw.libs.phpParserData.messages );
                var tasks = $.map( mw.libs.phpParserData.tests, function ( test ) {
                        return function ( next, abort ) {
+                               var done = assert.async();
                                getMwLanguage( test.lang )
                                        .then( function ( langClass ) {
                                                mw.config.set( 'wgUserLanguage', test.lang );
                                        }, function () {
                                                assert.ok( false, 'Language "' + test.lang + '" failed to load.' );
                                        } )
+                                       .then( done, done )
                                        .then( next, abort );
                        };
                } );
 
-               QUnit.stop();
-               process( tasks, QUnit.start );
+               process( tasks );
        } );
 
        QUnit.test( 'Links', 14, function ( assert ) {
                mw.messages.set( 'formatnum-msg-int', '{{formatnum:$1|R}}' );
                var queue = $.map( formatnumTests, function ( test ) {
                        return function ( next, abort ) {
+                               var done = assert.async();
                                getMwLanguage( test.lang )
                                        .then( function ( langClass ) {
                                                mw.config.set( 'wgUserLanguage', test.lang );
                                        }, function () {
                                                assert.ok( false, 'Language "' + test.lang + '" failed to load' );
                                        } )
+                                       .then( done, done )
                                        .then( next, abort );
                        };
                } );
-               QUnit.stop();
-               process( queue, QUnit.start );
+               process( queue );
        } );
 
        // HTML in wikitext
index fe5530b..ce4ea8b 100644 (file)
                );
        } );
 
+       QUnit.test( 'mw.loader.require', 6, function ( assert ) {
+               var module1, module2, module3, module4;
+
+               mw.loader.register( [
+                       [ 'test.module.require1', '0' ],
+                       [ 'test.module.require2', '0' ],
+                       [ 'test.module.require3', '0' ],
+                       [ 'test.module.require4', '0', [ 'test.module.require3' ] ]
+               ] );
+               mw.loader.implement( 'test.module.require1', function () {} );
+               mw.loader.implement( 'test.module.require2', function ( $, jQuery, require, module ) {
+                       module.exports = 1;
+               } );
+               mw.loader.implement( 'test.module.require3', function ( $, jQuery, require, module ) {
+                       module.exports = function () {
+                               return 'hello world';
+                       };
+               } );
+               mw.loader.implement( 'test.module.require4', function ( $, jQuery, require, module ) {
+                       var other = require( 'test.module.require3' );
+                       module.exports = {
+                               pizza: function () {
+                                       return other();
+                               }
+                       };
+               } );
+               module1 = mw.loader.require( 'test.module.require1' );
+               module2 = mw.loader.require( 'test.module.require2' );
+               module3 = mw.loader.require( 'test.module.require3' );
+               module4 = mw.loader.require( 'test.module.require4' );
+
+               assert.strictEqual( typeof module1, 'object', 'export of module with no export' );
+               assert.strictEqual( module2, 1, 'export a number' );
+               assert.strictEqual( module3(), 'hello world', 'export a function' );
+               assert.strictEqual( typeof module4.pizza, 'function', 'export an object' );
+               assert.strictEqual( module4.pizza(), 'hello world', 'module can require other modules' );
+
+               assert.throws( function () {
+                       mw.loader.require( '_badmodule' );
+               }, /is not loaded/, 'Requesting non-existent modules throws error.' );
+       } );
+
 }( mediaWiki, jQuery ) );