Merge "Fix tag for partial blocks config"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Tue, 8 Jan 2019 18:48:56 +0000 (18:48 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Tue, 8 Jan 2019 18:48:56 +0000 (18:48 +0000)
102 files changed:
.eslintrc.json
.mailmap
CREDITS
RELEASE-NOTES-1.33
includes/Block.php
includes/DefaultSettings.php
includes/Title.php
includes/api/i18n/zh-hant.json
includes/auth/AbstractPasswordPrimaryAuthenticationProvider.php
includes/block/BlockRestriction.php
includes/block/Restriction/AbstractRestriction.php
includes/block/Restriction/PageRestriction.php
includes/block/Restriction/Restriction.php
includes/filerepo/file/OldLocalFile.php
includes/htmlform/HTMLSelectLanguageField.php
includes/import/ImportableUploadRevisionImporter.php
includes/installer/i18n/uk.json
includes/libs/objectcache/WANObjectCache.php
includes/page/WikiPage.php
includes/password/UserPasswordPolicy.php
includes/specials/SpecialUserrights.php
includes/upload/UploadBase.php
includes/user/User.php
languages/i18n/be-tarask.json
languages/i18n/be.json
languages/i18n/bqi.json
languages/i18n/cs.json
languages/i18n/de.json
languages/i18n/en.json
languages/i18n/exif/be-tarask.json
languages/i18n/exif/ia.json
languages/i18n/exif/uk.json
languages/i18n/fr.json
languages/i18n/gor.json
languages/i18n/he.json
languages/i18n/min.json
languages/i18n/qqq.json
languages/i18n/uz.json
languages/i18n/yi.json
languages/i18n/zh-hans.json
mw-config/config.js
package.json
resources/src/jquery.tablesorter/jquery.tablesorter.js
resources/src/jquery/jquery.checkboxShiftClick.js
resources/src/jquery/jquery.makeCollapsible.js
resources/src/jquery/jquery.suggestions.js
resources/src/jquery/jquery.textSelection.js
resources/src/mediawiki.action/mediawiki.action.edit.editWarning.js
resources/src/mediawiki.action/mediawiki.action.edit.js
resources/src/mediawiki.action/mediawiki.action.edit.preview.js
resources/src/mediawiki.action/mediawiki.action.history.js
resources/src/mediawiki.action/mediawiki.action.view.dblClickEdit.js
resources/src/mediawiki.action/mediawiki.action.view.postEdit.js
resources/src/mediawiki.action/mediawiki.action.view.rightClickEdit.js
resources/src/mediawiki.api/upload.js
resources/src/mediawiki.checkboxtoggle.js
resources/src/mediawiki.debug/debug.js
resources/src/mediawiki.filewarning/filewarning.js
resources/src/mediawiki.htmlform.checker.js
resources/src/mediawiki.htmlform/cloner.js
resources/src/mediawiki.htmlform/multiselect.js
resources/src/mediawiki.htmlform/selectandother.js
resources/src/mediawiki.jqueryMsg/mediawiki.jqueryMsg.js
resources/src/mediawiki.legacy/protect.js
resources/src/mediawiki.notification/notification.js
resources/src/mediawiki.page.gallery.js
resources/src/mediawiki.page.ready.js
resources/src/mediawiki.page.watch.ajax.js
resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FiltersViewModel.js
resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.SavedQueriesModel.js
resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js
resources/src/mediawiki.rcfilters/mw.rcfilters.init.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ChangesListWrapperWidget.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterTagMultiselectWidget.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.SavedLinksListWidget.js
resources/src/mediawiki.special.apisandbox/apisandbox.js
resources/src/mediawiki.special.import.js
resources/src/mediawiki.special.recentchanges.js
resources/src/mediawiki.special.search.commonsInterwikiWidget.js
resources/src/mediawiki.special.search/search.js
resources/src/mediawiki.special.undelete.js
resources/src/mediawiki.special.unwatchedPages/unwatchedPages.js
resources/src/mediawiki.special.upload/upload.js
resources/src/mediawiki.special.userrights.js
resources/src/mediawiki.special.watchlist/watchlist.js
resources/src/mediawiki.toc/toc.js
resources/src/mediawiki.widgets.datetime/DateTimeInputWidget.js
resources/src/mediawiki.widgets/MediaSearch/mw.widgets.APIResultsQueue.js
resources/src/mediawiki.widgets/MediaSearch/mw.widgets.MediaResourceProvider.js
resources/src/mediawiki.widgets/mw.widgets.DateInputWidget.js
resources/src/mediawiki.widgets/mw.widgets.SearchInputWidget.js
tests/phpunit/includes/TitlePermissionTest.php
tests/phpunit/includes/auth/AbstractPasswordPrimaryAuthenticationProviderTest.php
tests/phpunit/includes/auth/LocalPasswordPrimaryAuthenticationProviderTest.php
tests/phpunit/includes/password/UserPasswordPolicyTest.php
tests/phpunit/includes/upload/UploadBaseTest.php
tests/qunit/.eslintrc.json
tests/qunit/data/testrunner.js
tests/qunit/suites/resources/jquery/jquery.color.test.js
tests/qunit/suites/resources/jquery/jquery.tablesorter.test.js
tests/qunit/suites/resources/mediawiki.special/mediawiki.special.recentchanges.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.toc.test.js

index 97f7c31..0c0a7b5 100644 (file)
@@ -10,6 +10,8 @@
                "OO": false
        },
        "rules": {
-               "max-len": 0
+               "quote-props": [ "error", "as-needed" ],
+               "max-len": "off",
+               "jquery/no-global-selector": "off"
        }
 }
index 8f2aa28..1265bd2 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -493,3 +493,5 @@ Zppix <support@zppixballee.com>
 Étienne Beaulé <beauleetienne0@gmail.com>
 Željko Filipin <zeljko.filipin@gmail.com>
 Željko Filipin <zeljko.filipin@gmail.com> <zfilipin@wikimedia.org>
+星耀晨曦 <razesoldier@outlook.com>
+星耀晨曦 <razesoldier@outlook.com> <liguangjie4399@hotmail.com>
diff --git a/CREDITS b/CREDITS
index 5e19ca0..319b566 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -589,7 +589,6 @@ The following list can be found parsed under Special:Version/Credits -->
 * rahul21
 * Raimond Spekking
 * Ramunas Geciauskas
-* RazeSoldier
 * Remember the dot
 * René Kijewski
 * Reza
index 8533741..ae67249 100644 (file)
@@ -196,6 +196,10 @@ because of Phabricator reports.
 * The PasswordPolicy 'PasswordCannotBePopular' has been deprecated. To
   follow best practices, it is reccommended to use 'PasswordNotInLargeBlacklist'
   instead which blacklists 100,000 commonly used passwords.
+* (T208862) Action::requiresUnblock() is now called from
+  Title::getUserPermissionsErrors() and Title::userCan(). Previously, the method
+  was only called in Action::checkCanExecute(). Actions should ensure that their
+  requiresUnblock() returns the proper result (the default is `true`).
 * …
 
 === Other changes in 1.33 ===
index ec8cae8..fb3caf6 100644 (file)
@@ -1132,6 +1132,7 @@ class Block {
         * prohibited from editing any page on the site (other than their own talk
         * page).
         *
+        * @since 1.33
         * @param null|bool $x
         * @return bool
         */
@@ -1728,6 +1729,7 @@ class Block {
        /**
         * Get block information used in different block error messages
         *
+        * @since 1.33
         * @param IContextSource $context
         * @return array
         */
@@ -1769,6 +1771,7 @@ class Block {
         * Getting the restrictions will perform a database query if the restrictions
         * are not already loaded.
         *
+        * @since 1.33
         * @return Restriction[]
         */
        public function getRestrictions() {
@@ -1787,8 +1790,8 @@ class Block {
        /**
         * Set Restrictions.
         *
+        * @since 1.33
         * @param Restriction[] $restrictions
-        *
         * @return self
         */
        public function setRestrictions( array $restrictions ) {
index 77d9148..9df4ab6 100644 (file)
@@ -4454,13 +4454,20 @@ $wgCentralIdLookupProvider = 'local';
  * Password policy for the wiki.
  * Structured as
  * [
- *     'policies' => [ <group> => [ <policy> => <value>, ... ], ... ],
+ *     'policies' => [ <group> => [ <policy> => <settings>, ... ], ... ],
  *     'checks' => [ <policy> => <callback>, ... ],
  * ]
  * where <group> is a user group, <policy> is a password policy name
  * (arbitrary string) defined in the 'checks' part, <callback> is the
- * PHP callable implementing the policy check, <value> is a number,
- * boolean or null that gets passed to the callback.
+ * PHP callable implementing the policy check, <settings> is an array
+ * of options with the following keys:
+ * - value: (number, boolean or null) the value to pass to the callback
+ * - forceChange: (bool, default false) if the password is invalid, do
+ *   not let the user log in without changing the password
+ * As a shorthand for [ 'value' => <value> ], simply <value> can be written.
+ * When multiple password policies are defined for a user, the settings
+ * arrays are merged, and for fields which are set in both arrays, the
+ * larger value (as understood by PHP's 'max' method) is taken.
  *
  * A user's effective policy is the superset of all policy statements
  * from the policies for the groups where the user is a member. If more
index 909f528..3496668 100644 (file)
@@ -36,7 +36,7 @@ use MediaWiki\MediaWikiServices;
  * @note Consider using a TitleValue object instead. TitleValue is more lightweight
  *       and does not rely on global state or the database.
  */
-class Title implements LinkTarget {
+class Title implements LinkTarget, IDBAccessObject {
        /** @var MapCacheLRU */
        static private $titleCache = null;
 
@@ -2691,14 +2691,34 @@ class Title implements LinkTarget {
                }
 
                $useReplica = ( $rigor !== 'secure' );
-               if ( ( $action == 'edit' || $action == 'create' )
-                       && !$user->isBlockedFrom( $this, $useReplica )
-               ) {
-                       // Don't block the user from editing their own talk page unless they've been
-                       // explicitly blocked from that too.
-               } elseif ( $user->isBlocked() && $user->getBlock()->prevents( $action ) !== false ) {
+               $block = $user->getBlock( $useReplica );
+
+               // The block may explicitly allow an action (like "read" or "upload").
+               if ( $block && $block->prevents( $action ) === false ) {
+                       return $errors;
+               }
+
+               // Determine if the user is blocked from this action on this page.
+               // What gets passed into this method is a user right, not an action nmae.
+               // There is no way to instantiate an action by restriction. However, this
+               // will get the action where the restriction is the same. This may result
+               // in actions being blocked that shouldn't be.
+               if ( Action::exists( $action ) ) {
                        // @todo FIXME: Pass the relevant context into this function.
-                       $errors[] = $user->getBlock()->getPermissionsError( RequestContext::getMain() );
+                       $action = Action::factory( $action, WikiPage::factory( $this ), RequestContext::getMain() );
+               } else {
+                       $action = null;
+               }
+
+               // If no action object is returned, assume that the action requires unblock
+               // which is the default.
+               if ( !$action || $action->requiresUnblock() ) {
+                       if ( $user->isBlockedFrom( $this, $useReplica ) ) {
+                               // @todo FIXME: Pass the relevant context into this function.
+                               $errors[] = $block
+                                       ? $block->getPermissionsError( RequestContext::getMain() )
+                                       : [ 'actionblockedtext' ];
+                       }
                }
 
                return $errors;
@@ -3278,13 +3298,12 @@ class Title implements LinkTarget {
         * indicating who can move or edit the page from the page table, (pre 1.10) rows.
         * Edit and move sections are separated by a colon
         * Example: "edit=autoconfirmed,sysop:move=sysop"
-        * @param bool $readLatest When true, skip replicas and read from the master DB.
         */
-       public function loadRestrictionsFromRows(
-               $rows, $oldFashionedRestrictions = null, $readLatest = false
-       ) {
-               $whichDb = $readLatest ? DB_MASTER : DB_REPLICA;
-               $dbr = wfGetDB( $whichDb );
+       public function loadRestrictionsFromRows( $rows, $oldFashionedRestrictions = null ) {
+               // This function will only read rows from a table that we migrated away
+               // from before adding READ_LATEST support to loadRestrictions, so we
+               // don't need to support reading from DB_MASTER here.
+               $dbr = wfGetDB( DB_REPLICA );
 
                $restrictionTypes = $this->getRestrictionTypes();
 
@@ -3354,39 +3373,52 @@ class Title implements LinkTarget {
         * indicating who can move or edit the page from the page table, (pre 1.10) rows.
         * Edit and move sections are separated by a colon
         * Example: "edit=autoconfirmed,sysop:move=sysop"
-        * @param bool $readLatest When true, skip replicas and read from the master DB.
+        * @param int $flags A bit field. If self::READ_LATEST is set, skip replicas and read
+        *  from the master DB.
         */
-       public function loadRestrictions( $oldFashionedRestrictions = null, $readLatest = false ) {
+       public function loadRestrictions( $oldFashionedRestrictions = null, $flags = 0 ) {
+               $readLatest = DBAccessObjectUtils::hasFlags( $flags, self::READ_LATEST );
                if ( $this->mRestrictionsLoaded && !$readLatest ) {
                        return;
                }
 
+               // TODO: should probably pass $flags into getArticleID, but it seems hacky
+               // to mix READ_LATEST and GAID_FOR_UPDATE, even if they have the same value.
+               // Maybe deprecate GAID_FOR_UPDATE now that we implement IDBAccessObject?
                $id = $this->getArticleID();
                if ( $id ) {
-                       $cache = ObjectCache::getMainWANInstance();
                        $fname = __METHOD__;
-                       $rows = $cache->getWithSetCallback(
-                               // Page protections always leave a new null revision
-                               $cache->makeKey( 'page-restrictions', $id, $this->getLatestRevID(), $readLatest ),
-                               $cache::TTL_DAY,
-                               function ( $curValue, &$ttl, array &$setOpts ) use ( $fname, $readLatest ) {
-                                       $whichDb = $readLatest ? DB_MASTER : DB_REPLICA;
-                                       $dbr = wfGetDB( $whichDb );
-
-                                       $setOpts += Database::getCacheSetOptions( $dbr );
-
-                                       return iterator_to_array(
-                                               $dbr->select(
-                                                       'page_restrictions',
-                                                       [ 'pr_type', 'pr_expiry', 'pr_level', 'pr_cascade' ],
-                                                       [ 'pr_page' => $this->getArticleID() ],
-                                                       $fname
-                                               )
-                                       );
-                               }
-                       );
+                       $loadRestrictionsFromDb = function ( Database $dbr ) use ( $fname, $id ) {
+                               return iterator_to_array(
+                                       $dbr->select(
+                                               'page_restrictions',
+                                               [ 'pr_type', 'pr_expiry', 'pr_level', 'pr_cascade' ],
+                                               [ 'pr_page' => $id ],
+                                               $fname
+                                       )
+                               );
+                       };
+
+                       if ( $readLatest ) {
+                               $dbr = wfGetDB( DB_MASTER );
+                               $rows = $loadRestrictionsFromDb( $dbr );
+                       } else {
+                               $cache = ObjectCache::getMainWANInstance();
+                               $rows = $cache->getWithSetCallback(
+                                       // Page protections always leave a new null revision
+                                       $cache->makeKey( 'page-restrictions', $id, $this->getLatestRevID() ),
+                                       $cache::TTL_DAY,
+                                       function ( $curValue, &$ttl, array &$setOpts ) use ( $loadRestrictionsFromDb ) {
+                                               $dbr = wfGetDB( DB_REPLICA );
+
+                                               $setOpts += Database::getCacheSetOptions( $dbr );
+
+                                               return $loadRestrictionsFromDb( $dbr );
+                                       }
+                               );
+                       }
 
-                       $this->loadRestrictionsFromRows( $rows, $oldFashionedRestrictions, $readLatest );
+                       $this->loadRestrictionsFromRows( $rows, $oldFashionedRestrictions );
                } else {
                        $title_protection = $this->getTitleProtectionInternal();
 
index e1e8658..e1cc8e2 100644 (file)
        "apihelp-expandtemplates-param-title": "頁面標題。",
        "apihelp-expandtemplates-param-text": "要轉換的 Wikitext。",
        "apihelp-expandtemplates-param-revid": "修訂 ID,用於 <code><nowiki>{{REVISIONID}}</nowiki></code> 和相似變數。",
+       "apihelp-expandtemplates-param-prop": "所要取得的資訊部份。\n\n請注意若沒有選定值,結果會包含 wiki 文字,輸出內容會採用棄用格式。",
        "apihelp-expandtemplates-paramvalue-prop-wikitext": "展開的 wiki 文字。",
        "apihelp-expandtemplates-paramvalue-prop-categories": "任何呈現在輸入中,且未在 wiki 文字輸出裡表現出的分類。",
        "apihelp-expandtemplates-paramvalue-prop-properties": "透過在 wiki 文字裡擴充魔術字所定義的頁面屬性。",
        "apihelp-feedrecentchanges-param-hidecategorization": "隱藏分類成員更改。",
        "apihelp-feedrecentchanges-param-tagfilter": "按標籤篩選。",
        "apihelp-feedrecentchanges-param-target": "僅顯示從該頁面所連結頁面上的變更。",
+       "apihelp-feedrecentchanges-param-showlinkedto": "改成顯示出連結到所選頁面的那些頁面之變更。",
        "apihelp-feedrecentchanges-example-simple": "顯示近期變更。",
        "apihelp-feedrecentchanges-example-30days": "顯示近期30天內的變動",
        "apihelp-feedwatchlist-summary": "返回監視清單摘要。",
index 4096f19..b6fedcd 100644 (file)
@@ -121,9 +121,10 @@ abstract class AbstractPasswordPrimaryAuthenticationProvider
                $reset = $this->getPasswordResetData( $username, $data );
 
                if ( !$reset && $this->config->get( 'InvalidPasswordReset' ) && !$status->isGood() ) {
+                       $hard = $status->getValue()['forceChange'] ?? false;
                        $reset = (object)[
-                               'msg' => $status->getMessage( 'resetpass-validity-soft' ),
-                               'hard' => false,
+                               'msg' => $status->getMessage( $hard ? 'resetpass-validity' : 'resetpass-validity-soft' ),
+                               'hard' => $hard,
                        ];
                }
 
index 5bf286d..5fe9650 100644 (file)
@@ -32,6 +32,7 @@ class BlockRestriction {
        /**
         * Retrieves the restrictions from the database by block id.
         *
+        * @since 1.33
         * @param int|array $blockId
         * @param IDatabase|null $db
         * @return Restriction[]
@@ -58,6 +59,7 @@ class BlockRestriction {
        /**
         * Inserts the restrictions into the database.
         *
+        * @since 1.33
         * @param Restriction[] $restrictions
         * @return bool
         */
@@ -92,6 +94,7 @@ class BlockRestriction {
         * Updates the list of restrictions. This method does not allow removing all
         * of the restrictions. To do that, use ::deleteByBlockId().
         *
+        * @since 1.33
         * @param Restriction[] $restrictions
         * @return bool
         */
@@ -156,6 +159,7 @@ class BlockRestriction {
        /**
         * Updates the list of restrictions by parent id.
         *
+        * @since 1.33
         * @param int $parentBlockId
         * @param Restriction[] $restrictions
         * @return bool
@@ -195,6 +199,7 @@ class BlockRestriction {
        /**
         * Delete the restrictions.
         *
+        * @since 1.33
         * @param Restriction[]|null $restrictions
         * @throws MWException
         * @return bool
@@ -224,6 +229,7 @@ class BlockRestriction {
        /**
         * Delete the restrictions by Block ID.
         *
+        * @since 1.33
         * @param int|array $blockId
         * @throws MWException
         * @return bool
@@ -240,6 +246,7 @@ class BlockRestriction {
        /**
         * Delete the restrictions by Parent Block ID.
         *
+        * @since 1.33
         * @param int|array $parentBlockId
         * @throws MWException
         * @return bool
@@ -261,6 +268,7 @@ class BlockRestriction {
         * equality check as the restrictions do not have to contain the same block
         * ids.
         *
+        * @since 1.33
         * @param Restriction[] $a
         * @param Restriction[] $b
         * @return bool
@@ -305,6 +313,7 @@ class BlockRestriction {
        /**
         * Set the blockId on a set of restrictions and return a new set.
         *
+        * @since 1.33
         * @param int $blockId
         * @param Restriction[] $restrictions
         * @return Restriction[]
index 88a6a0f..8c3e27f 100644 (file)
@@ -37,6 +37,7 @@ abstract class AbstractRestriction implements Restriction {
        /**
         * Create Restriction.
         *
+        * @since 1.33
         * @param int $blockId
         * @param int $value
         */
index 209b148..b72baea 100644 (file)
@@ -56,6 +56,7 @@ class PageRestriction extends AbstractRestriction {
        /**
         * Set the title.
         *
+        * @since 1.33
         * @param \Title $title
         * @return self
         */
@@ -68,6 +69,7 @@ class PageRestriction extends AbstractRestriction {
        /**
         * Get Title.
         *
+        * @since 1.33
         * @return \Title|null
         */
        public function getTitle() {
index 5fefecc..a89ca38 100644 (file)
@@ -27,6 +27,7 @@ interface Restriction {
        /**
         * Gets the id of the block.
         *
+        * @since 1.33
         * @return int
         */
        public function getBlockId();
@@ -34,6 +35,7 @@ interface Restriction {
        /**
         * Sets the id of the block.
         *
+        * @since 1.33
         * @param int $blockId
         * @return self
         */
@@ -42,6 +44,7 @@ interface Restriction {
        /**
         * Gets the value of the restriction.
         *
+        * @since 1.33
         * @return int
         */
        public function getValue();
@@ -49,6 +52,7 @@ interface Restriction {
        /**
         * Gets the type of restriction
         *
+        * @since 1.33
         * @return string
         */
        public function getType();
@@ -56,6 +60,7 @@ interface Restriction {
        /**
         * Gets the id of the type of restriction. This id is used in the database.
         *
+        * @since 1.33
         * @return string
         */
        public function getTypeId();
@@ -63,6 +68,7 @@ interface Restriction {
        /**
         * Creates a new Restriction from a database row.
         *
+        * @since 1.33
         * @param \stdClass $row
         * @return self
         */
@@ -71,6 +77,7 @@ interface Restriction {
        /**
         * Convert a restriction object into a row array for insertion.
         *
+        * @since 1.33
         * @return array
         */
        public function toRow();
@@ -78,6 +85,7 @@ interface Restriction {
        /**
         * Determine if a restriction matches a given title.
         *
+        * @since 1.33
         * @param \Title $title
         * @return bool
         */
@@ -86,6 +94,7 @@ interface Restriction {
        /**
         * Determine if a restriction equals another restriction.
         *
+        * @since 1.33
         * @param Restriction $other
         * @return bool
         */
@@ -94,6 +103,7 @@ interface Restriction {
        /**
         * Create a unique hash of the block restriction based on the type and value.
         *
+        * @since 1.33
         * @return string
         */
        public function getHash();
index ad95bb4..23ff304 100644 (file)
@@ -406,16 +406,15 @@ class OldLocalFile extends LocalFile {
         * Upload a file directly into archive. Generally for Special:Import.
         *
         * @param string $srcPath File system path of the source file
-        * @param string $archiveName Full archive name of the file, in the form
-        *   $timestamp!$filename, where $filename must match $this->getName()
         * @param string $timestamp
         * @param string $comment
         * @param User $user
         * @return Status
         */
-       function uploadOld( $srcPath, $archiveName, $timestamp, $comment, $user ) {
+       public function uploadOld( $srcPath, $timestamp, $comment, $user ) {
                $this->lock();
 
+               $archiveName = $this->getArchiveName();
                $dstRel = $this->getArchiveRel( $archiveName );
                $status = $this->publishTo( $srcPath, $dstRel );
 
index 5d2019c..98cf3b1 100644 (file)
@@ -23,6 +23,8 @@ class HTMLSelectLanguageField extends HTMLSelectField {
                        $languages[$languageCode] = $languageCode;
                }
 
+               ksort( $languages );
+
                foreach ( $languages as $code => $name ) {
                        $this->mParams['options'][$code . ' - ' . $name] = $code;
                }
index 4fbddb5..4b378c1 100644 (file)
@@ -104,9 +104,13 @@ class ImportableUploadRevisionImporter implements UploadRevisionImporter {
                        ?: User::newFromName( $importableRevision->getUser(), false );
 
                # Do the actual upload
-               if ( $archiveName ) {
-                       $status = $file->uploadOld( $source, $archiveName,
-                               $importableRevision->getTimestamp(), $importableRevision->getComment(), $user );
+               if ( $file instanceof OldLocalFile ) {
+                       $status = $file->uploadOld(
+                               $source,
+                               $importableRevision->getTimestamp(),
+                               $importableRevision->getComment(),
+                               $user
+                       );
                } else {
                        $flags = 0;
                        $status = $file->upload(
index 51a3ba4..76e37aa 100644 (file)
        "config-install-mainpage-failed": "Не вдається вставити головну сторінку: $1",
        "config-install-done": "<strong>Вітаємо!</strong>\nВи успішно встановили MediaWiki.\n\nІнсталятор згенерував файл <code>LocalSettings.php</code>, який містить усі Ваші налаштування.\n\nВам необхідно завантажити його і помістити у кореневу папку Вашої вікі (туди ж, де index.php). Завантаження мало початись автоматично.\n\nЯкщо завантаження не почалось або Ви його скасували, можете заново його почати, натиснувши на посилання внизу:\n\n$3\n\n<strong>Примітка</strong>: Якщо Ви не зробите цього зараз, цей файл не буде доступним пізніше, коли Ви вийдете з встановлення, не скачавши його.\n\nПісля виконання дій, описаних вище, Ви зможете <strong>[$2 увійти у свою вікі]</strong>.",
        "config-install-done-path": "<strong>Вітаємо!</strong>\nВи встановили Медіавікі.\n\nІнсталятор створив файл <code>LocalSettings.php</code>.\nУ ньому містяться всі Ваші налаштування.\n\nВам потрібно завантажити його й помістити в <code>$4</code>. Завантаження повинно було автоматично розпочатись.\n\nЯкщо завантаження не було запропоновано, або Ви його скасували, Ви можете перезапустити завантаження натиснувши на посилання нижче:\n\n$3\n\n<strong>Зверніть увагу:</strong> Якщо Ви не зробите це зараз, цей згенерований файл налаштувань не буде доступним для Вас пізніше якщо Ви вийдете зі встановлення не завантаживши його.\n\nКоли це було зроблено Ви можете <strong>[$2 зайти до своєї вікі]</strong>.",
-       "config-install-success": "Mediawiki Ñ\83Ñ\81пÑ\96Ñ\88но Ð²Ñ\81Ñ\82ановлено. Ð\97аÑ\80аз Ð²Ð¸ Ð¼Ð¾Ð¶ÐµÑ\82е Ð¿ÐµÑ\80ейÑ\82и Ð´Ð¾ <$1$2>, Ñ\89об Ð¿ÐµÑ\80еглÑ\8fнÑ\83Ñ\82и Ñ\81воÑ\8e Ð²Ñ\96кÑ\96. Ð¯ÐºÑ\89о Ñ\83 Ð²Ð°Ñ\81 Ñ\94 Ð¿Ð¸Ñ\82аннÑ\8f, Ð¾Ð·Ð½Ð°Ð¹Ð¾Ð¼Ñ\82еÑ\81Ñ\8f Ð· Ð½Ð°Ñ\88им FAQ: <https://www.mediawiki.org/wiki/Manual:FAQ> або використовуйте один з форумів підтримки, які вказано на цій сторінці.",
+       "config-install-success": "Mediawiki Ñ\83Ñ\81пÑ\96Ñ\88но Ð²Ñ\81Ñ\82ановлено. Ð\97аÑ\80аз Ð\92и Ð¼Ð¾Ð¶ÐµÑ\82е Ð¿ÐµÑ\80ейÑ\82и Ð´Ð¾ <$1$2>, Ñ\89об Ð¿ÐµÑ\80еглÑ\8fнÑ\83Ñ\82и Ñ\81воÑ\8e Ð²Ñ\96кÑ\96. Ð¯ÐºÑ\89о Ñ\83 Ð\92аÑ\81 Ñ\94 Ð¿Ð¸Ñ\82аннÑ\8f, Ð¾Ð·Ð½Ð°Ð¹Ð¾Ð¼Ñ\82еÑ\81Ñ\8f Ð· Ð½Ð°Ñ\88им FAQ: <https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ> або використовуйте один з форумів підтримки, які вказано на цій сторінці.",
        "config-download-localsettings": "Завантажити <code>LocalSettings.php</code>",
        "config-help": "допомога",
        "config-help-tooltip": "натисніть, щоб розгорнути",
        "config-nofile": "Файл \"$1\" не знайдено. Його видалено?",
-       "config-extension-link": "Чи знаєте ви, що ваше вікі підтримує [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions розширення]?\n\nВи можете переглядати [https://www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category розширення по категорії] або в [https://www.mediawiki.org/wiki/Extension_Matrix матрицю розширень] щоб побачити повний список розширень.",
+       "config-extension-link": "Чи знали Ви, що Ваша вікі підтримує [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions розширення]?\n\nМожете переглянути [https://www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category розширення за категорією]",
        "config-skins-screenshots": "$1 (скріншоти: $2)",
        "config-extensions-requires": "$1 (вимагає $2)",
        "config-screenshot": "скріншот",
+       "config-extension-not-found": "Не вдалося знайти файл реєстрації для розширення «$1»",
+       "config-extension-dependency": "При встановленні розширення «$1» виникла помилка залежності: $2",
        "mainpagetext": "<strong>Програмне забезпечення «MediaWiki» встановлено.</strong>",
        "mainpagedocfooter": "Інформацію про роботу з цією вікі можна знайти на сторінці [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Довідка:Вміст].\n\n== Деякі корисні ресурси ==\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Список налаштувань];\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Часті питання з приводу MediaWiki];\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Розсилка повідомлень про появу нових версій MediaWiki];\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Локалізувати MediaWiki своєю мовою]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Дізнатися, як боротися зі спамом у своїй вікі]"
 }
index 4bbebd6..6e8d266 100644 (file)
@@ -941,6 +941,36 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         *     );
         * @endcode
         *
+        * Example usage (key that is expensive with too many DB dependencies for "check keys"):
+        * @code
+        *     $catToys = $cache->getWithSetCallback(
+        *         // Key to store the cached value under
+        *         $cache->makeKey( 'cat-toys', $catId ),
+        *         // Time-to-live (seconds)
+        *         $cache::TTL_HOUR,
+        *         // Function that derives the new key value
+        *         function ( $oldValue, &$ttl, array &$setOpts ) {
+        *             // Determine new value from the DB
+        *             $dbr = wfGetDB( DB_REPLICA );
+        *             // Account for any snapshot/replica DB lag
+        *             $setOpts += Database::getCacheSetOptions( $dbr );
+        *
+        *             return CatToys::newFromResults( $dbr->select( ... ) );
+        *         },
+        *         [
+        *              // Get the highest timestamp of any of the cat's toys
+        *             'touchedCallback' => function ( $value ) use ( $catId ) {
+        *                 $dbr = wfGetDB( DB_REPLICA );
+        *                 $ts = $dbr->selectField( 'cat_toys', 'MAX(ct_touched)', ... );
+        *
+        *                 return wfTimestampOrNull( TS_UNIX, $ts );
+        *             },
+        *             // Avoid DB queries for repeated access
+        *             'pcTTL' => $cache::TTL_PROC_SHORT
+        *         ]
+        *     );
+        * @endcode
+        *
         * Example usage (hot key holding most recent 100 events):
         * @code
         *     $lastCatActions = $cache->getWithSetCallback(
@@ -1082,8 +1112,8 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         *      expired for this specified time. This is useful if adaptiveTTL() is used on the old
         *      value's as-of time when it is verified as still being correct.
         *      Default: WANObjectCache::STALE_TTL_NONE
-        *   - touchedCallback: A callback that takes the current value and returns a timestamp that
-        *      indicates the last time a dynamic dependency changed. Null can be returned if there
+        *   - touchedCallback: A callback that takes the current value and returns a UNIX timestamp
+        *      indicating the last time a dynamic dependency changed. Null can be returned if there
         *      are no relevant dependency changes to check. This can be used to check against things
         *      like last-modified times of files or DB timestamp fields. This should generally not be
         *      used for small and easily queried values in a DB if the callback itself ends up doing
index c7c7069..b1bf7bd 100644 (file)
@@ -2147,6 +2147,7 @@ class WikiPage implements Page, IDBAccessObject {
                }
 
                $this->loadPageData( 'fromdbmaster' );
+               $this->mTitle->loadRestrictions( null, Title::READ_LATEST );
                $restrictionTypes = $this->mTitle->getRestrictionTypes();
                $id = $this->getId();
 
index 0c52354..9eb921d 100644 (file)
@@ -43,7 +43,7 @@ class UserPasswordPolicy {
        /**
         * @param array $policies
         * @param array $checks mapping statement to its checking function. Checking functions are
-        * called with the policy value for this user, the user object, and the password to check.
+        *   called with the policy value for this user, the user object, and the password to check.
         */
        public function __construct( array $policies, array $checks ) {
                if ( !isset( $policies['default'] ) ) {
@@ -68,7 +68,9 @@ class UserPasswordPolicy {
         * @param User $user who's policy we are checking
         * @param string $password the password to check
         * @return Status error to indicate the password didn't meet the policy, or fatal to
-        *      indicate the user shouldn't be allowed to login.
+        *   indicate the user shouldn't be allowed to login. The status value will be an array,
+        *   potentially with the following keys:
+        *   - forceChange: do not allow the user to login without changing the password if invalid.
         */
        public function checkUserPassword( User $user, $password ) {
                $effectivePolicy = $this->getPoliciesForUser( $user );
@@ -88,7 +90,9 @@ class UserPasswordPolicy {
         * @param string $password the password to check
         * @param array $groups list of groups to which we assume the user belongs
         * @return Status error to indicate the password didn't meet the policy, or fatal to
-        *      indicate the user shouldn't be allowed to login.
+        *   indicate the user shouldn't be allowed to login. The status value will be an array,
+        *   potentially with the following keys:
+        *   - forceChange: do not allow the user to login without changing the password if invalid.
         */
        public function checkUserPasswordForGroups( User $user, $password, array $groups ) {
                $effectivePolicy = self::getPoliciesForGroups(
@@ -112,19 +116,34 @@ class UserPasswordPolicy {
         * @return Status
         */
        private function checkPolicies( User $user, $password, $policies, $policyCheckFunctions ) {
-               $status = Status::newGood();
-               foreach ( $policies as $policy => $value ) {
+               $status = Status::newGood( [] );
+               $forceChange = false;
+               foreach ( $policies as $policy => $settings ) {
                        if ( !isset( $policyCheckFunctions[$policy] ) ) {
                                throw new DomainException( "Invalid password policy config. No check defined for '$policy'." );
                        }
-                       $status->merge(
-                               call_user_func(
-                                       $policyCheckFunctions[$policy],
-                                       $value,
-                                       $user,
-                                       $password
-                               )
+                       if ( !is_array( $settings ) ) {
+                               // legacy format
+                               $settings = [ 'value' => $settings ];
+                       }
+                       if ( !array_key_exists( 'value', $settings ) ) {
+                               throw new DomainException( "Invalid password policy config. No value defined for '$policy'." );
+                       }
+                       $value = $settings['value'];
+                       /** @var StatusValue $policyStatus */
+                       $policyStatus = call_user_func(
+                               $policyCheckFunctions[$policy],
+                               $value,
+                               $user,
+                               $password
                        );
+                       if ( !$policyStatus->isGood() && !empty( $settings['forceChange'] ) ) {
+                               $forceChange = true;
+                       }
+                       $status->merge( $policyStatus );
+               }
+               if ( $status->isOK() && $forceChange ) {
+                       $status->value['forceChange'] = true;
                }
                return $status;
        }
@@ -174,6 +193,7 @@ class UserPasswordPolicy {
        /**
         * Utility function to get a policy that is the most restrictive of $p1 and $p2. For
         * simplicity, we setup the policy values so the maximum value is always more restrictive.
+        * It is also used recursively to merge settings within the same policy.
         * @param array $p1
         * @param array $p2
         * @return array containing the more restrictive values of $p1 and $p2
@@ -186,8 +206,15 @@ class UserPasswordPolicy {
                                $ret[$key] = $p2[$key];
                        } elseif ( !isset( $p2[$key] ) ) {
                                $ret[$key] = $p1[$key];
-                       } else {
+                       } elseif ( !is_array( $p1[$key] ) && !is_array( $p2[$key] ) ) {
                                $ret[$key] = max( $p1[$key], $p2[$key] );
+                       } else {
+                               if ( !is_array( $p1[$key] ) ) {
+                                       $p1[$key] = [ 'value' => $p1[$key] ];
+                               } elseif ( !is_array( $p2[$key] ) ) {
+                                       $p2[$key] = [ 'value' => $p2[$key] ];
+                               }
+                               $ret[$key] = self::maxOfPolicies( $p1[$key], $p2[$key] );
                        }
                }
                return $ret;
index 94b8184..3c2907b 100644 (file)
@@ -175,7 +175,7 @@ class UserrightsPage extends SpecialPage {
                        $userGroups = $targetUser->getGroups();
 
                        if ( $userGroups !== $conflictCheck ) {
-                               $out->addWikiMsg( 'userrights-conflict' );
+                               $out->wrapWikiMsg( '<span class="error">$1</span>', 'userrights-conflict' );
                        } else {
                                $status = $this->saveUserGroups(
                                        $this->mTarget,
index c7dbf83..a579b69 100644 (file)
@@ -1740,9 +1740,10 @@ abstract class UploadBase {
                        }
 
                        # image filters can pull in url, which could be svg that executes scripts
+                       # Only allow url( "#foo" ). Do not allow url( http://example.com )
                        if ( $strippedElement == 'image'
                                && $stripped == 'filter'
-                               && preg_match( '!url\s*\(!sim', $value )
+                               && preg_match( '!url\s*\(\s*["\']?[^#]!sim', $value )
                        ) {
                                wfDebug( __METHOD__ . ": Found image filter with url: "
                                        . "\"<$strippedElement $stripped='$value'...\" in uploaded file.\n" );
index 22fe44c..65fc4b4 100644 (file)
@@ -1179,15 +1179,17 @@ class User implements IDBAccessObject, UserIdentity {
        /**
         * Check if this is a valid password for this user
         *
-        * Create a Status object based on the password's validity.
-        * The Status should be set to fatal if the user should not
-        * be allowed to log in, and should have any errors that
-        * would block changing the password.
-        *
-        * If the return value of this is not OK, the password
-        * should not be checked. If the return value is not Good,
-        * the password can be checked, but the user should not be
-        * able to set their password to this.
+        * Returns a Status object with a set of messages describing
+        * problems with the password. If the return status is fatal,
+        * the action should be refused and the password should not be
+        * checked at all (this is mainly meant for DoS mitigation).
+        * If the return value is OK but not good, the password can be checked,
+        * but the user should not be able to set their password to this.
+        * The value of the returned Status object will be an array which
+        * can have the following fields:
+        * - forceChange (bool): if set to true, the user should not be
+        *   allowed to log with this password unless they change it during
+        *   the login process (see ResetPasswordSecondaryAuthenticationProvider).
         *
         * @param string $password Desired password
         * @return Status
@@ -1201,7 +1203,7 @@ class User implements IDBAccessObject, UserIdentity {
                        $wgPasswordPolicy['checks']
                );
 
-               $status = Status::newGood();
+               $status = Status::newGood( [] );
                $result = false; // init $result to false for the internal checks
 
                if ( !Hooks::run( 'isValidPassword', [ $password, &$result, $this ] ) ) {
@@ -1210,7 +1212,7 @@ class User implements IDBAccessObject, UserIdentity {
                }
 
                if ( $result === false ) {
-                       $status->merge( $upp->checkUserPassword( $this, $password ) );
+                       $status->merge( $upp->checkUserPassword( $this, $password ), true );
                        return $status;
                } elseif ( $result === true ) {
                        return $status;
@@ -4543,6 +4545,7 @@ class User implements IDBAccessObject, UserIdentity {
        /**
         * Get whether the user is blocked from using Special:Upload
         *
+        * @since 1.33
         * @return bool
         */
        public function isBlockedFromUpload() {
index ffdc4c4..fac054e 100644 (file)
        "statistics": "Статыстыка",
        "statistics-header-pages": "Статыстыка старонак",
        "statistics-header-edits": "Статыстыка рэдагаваньняў",
-       "statistics-header-users": "СÑ\82аÑ\82Ñ\8bÑ\81Ñ\82Ñ\8bка Ñ\9eдзелÑ\83",
+       "statistics-header-users": "СÑ\82аÑ\82Ñ\8bÑ\81Ñ\82Ñ\8bка Ñ\9eдзелÑ\8cнÑ\96каÑ\9e",
        "statistics-header-hooks": "Іншая статыстыка",
        "statistics-articles": "Колькасьць старонак са зьместам",
        "statistics-pages": "Колькасьць старонак",
index ab446f2..17624ac 100644 (file)
        "logentry-delete-delete": "$1 {{GENDER:$2|выдаліў|выдаліла}} старонку $3",
        "logentry-delete-delete_redir": "$1 {{GENDER:$2|выдаліў|выдаліла}} перанакіраванне $3 шляхам перазапісу",
        "logentry-delete-restore": "$1 {{GENDER:$2|аднавіў|аднавіла}} старонку $3 ($4)",
+       "logentry-delete-restore-nocount": "$1 {{GENDER:$2|аднавіў|аднавіла}} старонку $3",
        "restore-count-revisions": "{{PLURAL:$1|1 версія|$1 версіі|$1 версій}}",
        "logentry-delete-event": "$1 {{GENDER:$2|змяніў|змяніла}} бачнасць {{PLURAL:$5|запісу журнала|$5 запісаў журнала}} $3: $4",
        "logentry-delete-revision": "$1 {{GENDER:$2|змяніў|змяніла}} бачнасць {{PLURAL:$5|версіі|$5 версій|$5 версій}} старонкі $3: $4",
index e435682..36239fe 100644 (file)
@@ -15,6 +15,9 @@
        },
        "tog-underline": "هومپیڤٱندا زیر خٱتدار",
        "tog-hideminor": "دٱم تی نٱبیڌن آلشتا کۊچیر",
+       "tog-hidepatrolled": "بؽدیار نڤیڌن آلشڌٱل کوچیر",
+       "tog-newpageshidepatrolled": "بٱلٛگیٱل لرهٱرڌاْ زاْ فاٛئرست بٱلٛگیٱل نۊ بؽدیار ڤۊهاْ",
+       "tog-hidecategorization": "بؽدیارنیڌن رٱئڌاٛڤٱنی بٱلٛگیٱل",
        "tog-extendwatchlist": "گپ کردن نوم گه آ مو سی دیئن همه آلشتا نه فقط هونو که بیشتر ز همه انجوم ابون.",
        "tog-usenewrc": "جٱرغاٛ کاری آلشتا ڤا آلشتکاری بٱلگاٛیلسۊن و سئیل بٱرگسۊن",
        "tog-numberheadings": "شوماراٛ ڤٱندن خودٱنجوم سی سربٱلگاٛیل",
@@ -24,6 +27,8 @@
        "tog-watchdefault": "اٛزاف کردن بٱلگاٛیٱل و جانیایٱلی کاٛ مو مئن سئیل برگوم ڤیرایشدسۊن کردوماٛ",
        "tog-watchmoves": "اْزاف کردن بٱلگاٛیٱلی کاٛ خوم جا ب جاسۊن کردوماٛ سی سئل بٱرگوم",
        "tog-watchdeletion": "اٛزاف کردن بٱلگاٛیٱل و جانیایٱلی کاٛ خوم ز مئن سئیل بٱرگوم پاکسا کردوماٛ",
+       "tog-watchuploads": "پٱرڤٱنداٛیٱل نۊئی کاْ باراْنم ڤاْ فاٛئرسڌ دیناگریٱل مو بالاڤٱن ڤۊ",
+       "tog-watchrollback": "بالاڤٱن کرڌن بٱلٛگیٱلؽ کاْ اْؤورگٱرنیم ڤاْ فاٛئرسڌ دیناگریٱل مو",
        "tog-minordefault": "دیاری کردن جۊر ڤیرایشتا ناقس",
        "tog-previewontop": "دیاری کردن پیش سئیل پیش ز ڤیرایشد جٱڤٱ",
        "tog-previewonfirst": "دیاری کردن پیش سئیل مئن ٱڤلین ڤیرایشد",
        "tog-watchlisthidebots": "قایم کردن اصلاحات بوت زه لیست پیگیریها",
        "tog-watchlisthideminor": "قایم کردن اصلاحات ریز زه لیست پیگیریها",
        "tog-watchlisthideliu": "قایم کردن اصلاحات انجام وابیده  بوسیله کاربران داخل سیستم وابیده زه لیست پیگیریها",
+       "tog-watchlistreloadautomatically": "راتؽ کاْ یٱ پاْلایاٛ آلشڌآڤیڌ فاٛئرسڌ دیناگری بؽنگوڌ(خوڌکار)ڤ رۊز ڤۊهاْ(هوجاْ ڤاْ جاڤا اسکریپت)",
+       "tog-watchlistunwatchlinks": "فرٱنیڌن دیاری کونٱنڌیٱل نڤیڌ دیناگری/دیناگری ({{int:Watchlist-unwatch}}/{{int:Watchlist-unwatch-undo}})ڤاْ بٱلٛگیٱل دیناگری آلشڌدار (سی عملیات کلؽز ۉ بلؽز کرڌن، جاڤاسکریپت   هوجاْ هؽڌآ)",
        "tog-watchlisthideanons": "قایم کردن اصلاحات انجام شده بوسیله کاربران داخل سیستم نشده زه لیست پیگیری",
+       "tog-watchlisthidepatrolled": "تٱپنیڌن پٱخڌارٱل ساوا زاْ لیسڌ دیناگریٱل\nتٱپنیڌن پٱخڌارٱل بۊت زاْ لیسڌ دیناگریٱل\nتٱپنیڌن پٱخڌارٱل مو زاْ لیسڌ دیناگریٱل",
+       "tog-watchlisthidecategorization": "تٱپنیڌن رٱئڌڤٱنی بٱلٛگیٱل",
        "tog-ccmeonemails": "ارسال کپی امیلهایی که مو به  کاربران دیه ارسال کردم به مو",
        "tog-diffonly": "بٱلگاٛیی نٱ کاٛ مئنۊناٛ فٱرخداراٛ نشۊن مٱڌاٛ",
        "tog-showhiddencats": "دیاری کردن جٱرغاٛ بٱندیٱل نادیار",
index 405b970..9263654 100644 (file)
        "tooltip-preferences-save": "Uložit nastavení",
        "tooltip-summary": "Zadejte stručné shrnutí",
        "interlanguage-link-title": "$1 – $2",
-       "common.css": "/* Zde uvedené CSS bude ovlivňovat všechny styly */",
+       "common.css": "/* Zde uvedené CSS bude ovlivňovat všechny vzhledy */",
        "print.css": "/* Zde uvedené CSS bude ovlivňovat tiskový výstup */",
        "noscript.css": "/* Zde uvedené CSS bude ovlivňovat uživatele s vypnutým JavaScriptem */",
        "group-autoconfirmed.css": "/* Zde uvedené CSS bude ovlivňovat pouze automaticky schválené uživatele */",
        "group-sysop.css": "/* Zde uvedené CSS bude ovlivňovat pouze správce */",
        "group-bureaucrat.css": "/* Zde uvedené CSS bude ovlivňovat pouze byrokraty */",
        "common.json": "/* Zde uvedený JSON se načte pro všechny uživatele při načtení každé stránky. */",
-       "common.js": "/* Zde uvedený JavaScript bude použit pro všechny uživatele při načtení každé stránky. */",
+       "common.js": "/* Zde uvedený JavaScript bude použit pro všechny uživatele při načtení každé stránky */",
        "group-autoconfirmed.js": "/* Zde uvedený JavaScript bude použit pouze pro automaticky schválené uživatele */",
        "group-user.js": "/* Zde uvedený JavaScript bude použit pouze pro registrované uživatele */",
        "group-bot.js": "/* Zde uvedený JavaScript bude použit pouze pro roboty */",
index 2943e3c..d0cb901 100644 (file)
        "resetpass-abort-generic": "Die Passwortänderung wurde durch eine Erweiterung abgebrochen.",
        "resetpass-expired": "Dein Passwort ist abgelaufen. Bitte lege ein neues Passwort zur Anmeldung fest.",
        "resetpass-expired-soft": "Dein Passwort ist abgelaufen und muss geändert werden. Bitte wähle jetzt ein neues Passwort aus oder klicke auf „{{int:authprovider-resetpass-skip-label}}“, um es später zu ändern.",
+       "resetpass-validity": "Dein Passwort ist nicht gültig: $1\n\nBitte lege zur Anmeldung ein neues Passwort fest.",
        "resetpass-validity-soft": "Dein Passwort ist ungültig: $1\n\nBitte wähle jetzt ein neues Passwort aus oder klicke auf „{{int:authprovider-resetpass-skip-label}}“, um es später zu ändern.",
        "passwordreset": "Passwort zurücksetzen",
        "passwordreset-text-one": "Fülle dieses Formular aus, um ein temporäres Passwort per E-Mail zu erhalten.",
index 3425056..9a65ead 100644 (file)
        "resetpass-abort-generic": "Password change has been aborted by an extension.",
        "resetpass-expired": "Your password has expired. Please set a new password to log in.",
        "resetpass-expired-soft": "Your password has expired and needs to be changed. Please choose a new password now, or click \"{{int:authprovider-resetpass-skip-label}}\" to change it later.",
+       "resetpass-validity": "Your password is not valid: $1\n\nPlease set a new password to log in.",
        "resetpass-validity-soft": "Your password is not valid: $1\n\nPlease choose a new password now, or click \"{{int:authprovider-resetpass-skip-label}}\" to change it later.",
        "passwordreset": "Reset password",
        "passwordreset-text-one": "Complete this form to receive a temporary password via email.",
        "blockedtext": "<strong>Your username or IP address has been blocked.</strong>\n\nThe block was made by $1.\nThe reason given is <em>$2</em>.\n\n* Start of block: $8\n* Expiration of block: $6\n* Intended blockee: $7\n\nYou can contact $1 or another [[{{MediaWiki:Grouppage-sysop}}|administrator]] to discuss the block.\nYou cannot use the \"{{int:emailuser}}\" feature unless a valid email address is specified in your [[Special:Preferences|account preferences]] and you have not been blocked from using it.\nYour current IP address is $3, and the block ID is #$5.\nPlease include all above details in any queries you make.",
        "autoblockedtext": "Your IP address has been automatically blocked because it was used by another user, who was blocked by $1.\nThe reason given is:\n\n:<em>$2</em>\n\n* Start of block: $8\n* Expiration of block: $6\n* Intended blockee: $7\n\nYou may contact $1 or one of the other [[{{MediaWiki:Grouppage-sysop}}|administrators]] to discuss the block.\n\nNote that you may not use the \"{{int:emailuser}}\" feature unless you have a valid email address registered in your [[Special:Preferences|user preferences]] and you have not been blocked from using it.\n\nYour current IP address is $3, and the block ID is #$5.\nPlease include all above details in any queries you make.",
        "systemblockedtext": "Your username or IP address has been automatically blocked by MediaWiki.\nThe reason given is:\n\n:<em>$2</em>\n\n* Start of block: $8\n* Expiration of block: $6\n* Intended blockee: $7\n\nYour current IP address is $3.\nPlease include all above details in any queries you make.",
+       "actionblockedtext": "You have been blocked from performing this action.",
        "blockednoreason": "no reason given",
        "whitelistedittext": "Please $1 to edit pages.",
        "confirmedittext": "You must confirm your email address before editing pages.\nPlease set and validate your email address through your [[Special:Preferences|user preferences]].",
index e3cbac4..70ff246 100644 (file)
        "exif-photometricinterpretation-3": "Палітра",
        "exif-photometricinterpretation-4": "Маска празрыстасьці",
        "exif-photometricinterpretation-5": "Падзелены (імаверна, CMYK)",
+       "exif-photometricinterpretation-8": "CIE L*a*b*",
        "exif-unknowndate": "Невядомая дата",
        "exif-orientation-1": "Звычайная",
        "exif-orientation-2": "Адлюстраваная па гарызанталі",
index 91e6110..37d33f2 100644 (file)
        "exif-compression-4": "CCITT Group 4 codification fax",
        "exif-copyrighted-true": "Sub copyright",
        "exif-copyrighted-false": "Stato de copyright non definite",
+       "exif-photometricinterpretation-0": "Nigro e blanco (blanco es 0)",
        "exif-photometricinterpretation-1": "Nigre e blanc (0 pro nigre)",
+       "exif-photometricinterpretation-3": "Paletta",
+       "exif-photometricinterpretation-4": "Masca de transparentia",
+       "exif-photometricinterpretation-5": "Separate (probabilemente CMYK)",
+       "exif-photometricinterpretation-8": "CIE L*a*b*",
+       "exif-photometricinterpretation-9": "CIE L*a*b* (codification ICC)",
+       "exif-photometricinterpretation-10": "CIE L*a*b* (codification ITU)",
        "exif-unknowndate": "Data incognite",
        "exif-orientation-1": "Normal",
        "exif-orientation-2": "Invertite horizontalmente",
index 5dbe4ac..73eb361 100644 (file)
@@ -9,7 +9,8 @@
                        "Ua2004",
                        "Ата",
                        "Максим Підліснюк",
-                       "Тест"
+                       "Тест",
+                       "Piramidion"
                ]
        },
        "exif-imagewidth": "Ширина",
        "exif-compression-34712": "JPEG2000",
        "exif-copyrighted-true": "Охороняється законом про авторське право",
        "exif-copyrighted-false": "Авторські права не встановлено",
+       "exif-photometricinterpretation-0": "Чорний і білий (Білий — це 0)",
        "exif-photometricinterpretation-1": "Чорний і білий (білий — 0)",
        "exif-photometricinterpretation-2": "RGB",
+       "exif-photometricinterpretation-3": "Палітра",
+       "exif-photometricinterpretation-4": "Маска прозорості",
+       "exif-photometricinterpretation-5": "Відокремлено (ймовірно CMYK)",
        "exif-photometricinterpretation-6": "YCbCr",
+       "exif-photometricinterpretation-8": "CIE L*a*b*",
+       "exif-photometricinterpretation-9": "CIE L*a*b* (ICC-кодування)",
+       "exif-photometricinterpretation-10": "CIE L*a*b* (ITU-кодування)",
        "exif-unknowndate": "Невідома дата",
        "exif-orientation-1": "Нормальна",
        "exif-orientation-2": "Відображено по горизонталі",
index e6d9304..42ec243 100644 (file)
        "accmailtext": "Un mot de passe généré aléatoirement pour [[User talk:$1|$1]] a été envoyé à $2.\nIl peut être modifié sur la page ''[[Special:ChangePassword|Changement de mot de passe]]'' après connexion.",
        "newarticle": "(Nouveau)",
        "newarticletext": "Vous avez suivi un lien vers une page qui n’existe pas encore. \nAfin de créer cette page, entrez votre texte dans la boîte ci-après (vous pouvez consulter [$1 la page d’aide] pour plus d’informations). \nSi vous êtes arrivé{{GENDER:||e}} ici par erreur, cliquez sur le bouton <strong>Retour</strong> de votre navigateur.",
-       "anontalkpagetext": "----\n<em>Vous êtes sur la page de discussion d’un utilisateur anonyme qui n’a pas encore créé de compte ou qui n’en utilise pas</em>.\nPour cette raison, nous devons utiliser son adresse IP pour les identifier.\nUne adresse IP peut être partagée par plusieurs utilisateurs.\nSi vous êtes un{{GENDER:||e|}} utilisat{{GENDER:|eur|rice|eur}} anonyme et si vous constatez que des commentaires qui ne vous concernent pas vous ont été adressés, vous pouvez [[Special:CreateAccount|créer un compte]] ou [[Special:UserLogin|vous connecter]] afin d’éviter toute confusion future avec d’autres contributeurs anonymes.",
+       "anontalkpagetext": "----\n<em>Vous êtes sur la page de discussion d’un utilisateur anonyme qui n’a pas encore créé de compte ou qui n’en utilise pas</em>.\nPour cette raison, nous devons utiliser son adresse IP pour l'identifier.\nUne telle adresse IP peut être partagée par plusieurs utilisateurs.\nSi vous êtes un{{GENDER:||e|}} utilisat{{GENDER:|eur|rice|eur}} anonyme et si vous constatez que des commentaires qui ne vous concernent pas vous ont été adressés, vous pouvez [[Special:CreateAccount|créer un compte]] ou [[Special:UserLogin|vous connecter]] afin d’éviter toute confusion future avec d’autres contributeurs anonymes.",
        "noarticletext": "Il n’y a pour l’instant aucun texte sur cette page.\nVous pouvez [[Special:Search/{{PAGENAME}}|lancer une recherche sur ce titre]] dans les autres pages,\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} rechercher dans les opérations liées]\nou [{{fullurl:{{FULLPAGENAME}}|action=edit}} créer cette page]</span>.",
        "noarticletext-nopermission": "Il n'y a pour l'instant aucun texte sur cette page.\nVous pouvez [[Special:Search/{{PAGENAME}}|faire une recherche sur ce titre]] dans les autres pages,\nou <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} rechercher dans les journaux associés]</span>, mais vous n'avez pas la permission de créer cette page.",
        "missing-revision": "La révision nº $1 de la page intitulée « {{FULLPAGENAME}} » n’existe pas.\n\nCela survient en général en suivant un lien historique désuet vers une page qui a été supprimée.\nVous pouvez trouver plus de détails dans le [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} journal des suppressions].",
        "special-characters-title-emdash": "tiret cadratin",
        "special-characters-title-minus": "signe moins",
        "mw-widgets-abandonedit": "Êtes-vous sûr de vouloir quitter le mode d’édition sans enregistrer d’abord ?",
-       "mw-widgets-abandonedit-discard": "Ignorer les modifications",
+       "mw-widgets-abandonedit-discard": "Annuler les modifications",
        "mw-widgets-abandonedit-keep": "Continuer à modifier",
        "mw-widgets-abandonedit-title": "Êtes-vous sûr ?",
        "mw-widgets-dateinput-no-date": "Aucune date sélectionnée",
index 3185b8d..f76e312 100644 (file)
@@ -74,8 +74,8 @@
        "thu": "Ham",
        "fri": "Jum",
        "sat": "Sah",
-       "january": "Januwari",
-       "february": "Peburuari",
+       "january": "Januari",
+       "february": "Pebruari",
        "march": "Maret",
        "april": "April",
        "may_long": "Mei",
@@ -86,7 +86,7 @@
        "october": "Oktober",
        "november": "Nopember",
        "december": "Desember",
-       "january-gen": "Januwari",
+       "january-gen": "Januari",
        "february-gen": "Pebruari",
        "march-gen": "Maret",
        "april-gen": "April",
        "newmessagesdifflinkplural": "{{PLURAL:$1|biloli'o|999=u biloli'o}} pulitiyo",
        "youhavenewmessagesmulti": "Yio lo'otapu tahuli bohu to $1",
        "editsection": "boli'a",
-       "editold": "boli'",
+       "editold": "boli'a",
        "viewsourceold": "Bilohi bungoliyo",
        "editlink": "boli'a",
        "viewsourcelink": "Bilohi bungoliyo",
index 09aad7a..0dcec29 100644 (file)
        "resetpass-abort-generic": "שינוי הסיסמה בוטל על־ידי הרחבה.",
        "resetpass-expired": "סיסמתך פקעה. נא להגדיר סיסמה חדשה כדי להיכנס.",
        "resetpass-expired-soft": "הסיסמה שלך פקעה, וצריך לשנות אותה. יש לבחור סיסמה חדשה כעת, או ללחוץ על \"{{int:authprovider-resetpass-skip-label}}\" כדי לשנות אותה מאוחר יותר.",
+       "resetpass-validity": "סיסמתך אינה חוקית: $1\n\nנא להגדיר סיסמה חדשה כדי להיכנס.",
        "resetpass-validity-soft": "הסיסמה שלך אינה תקינה: $1\n\nיש לבחור סיסמה חדשה כעת או ללחוץ על \"{{int:authprovider-resetpass-skip-label}}\" כדי לשנות את הסיסמה מאוחר יותר.",
        "passwordreset": "איפוס סיסמה",
        "passwordreset-text-one": "יש למלא טופס זה כדי לקבל סיסמה זמנית בדוא\"ל.",
index d5632f8..86d1008 100644 (file)
@@ -12,7 +12,8 @@
                        "Macofe",
                        "Fitoschido",
                        "Baloch Khan",
-                       "Muhraz"
+                       "Muhraz",
+                       "Ardzun"
                ]
        },
        "tog-underline": "Garih bawahi tautan:",
        "permalink": "Pautan parmanen",
        "print": "Cetak",
        "view": "Baco",
+       "view-foreign": "Caliak di $1",
        "edit": "Suntiang",
        "create": "Buek",
+       "create-local": "Tambah sumber deskripsi lokal",
        "delete": "Hapuih",
        "undelete_short": "Batal hapuih $1 {{PLURAL:$1|suntiangan}}",
        "viewdeleted_short": "Lihek {{PLURAL:$1|$1 suntiangan}} nan dihapuih",
        "otherlanguages": "Dalam bahaso lain",
        "redirectedfrom": "(Dialiahkan dari $1)",
        "redirectpagesub": "Laman pangaliahan",
+       "redirectto": "Dialiahkan ka",
        "lastmodifiedat": "Laman ko tarakhia diubah pado $2, tanggal $1.",
        "viewcount": "Laman ko lah dicaliak {{PLURAL:$1|$1 kali}}.",
        "protectedpage": "Laman nan dilinduangi",
        "actionthrottled": "Tindakan tabateh",
        "actionthrottledtext": "Sanak tabateh untuak malakuan tindakan ko banyak-banyak dalam wakatu singkek. Cubo lah laik satalah bara minit.",
        "protectedpagetext": "Laman ko alah dikunci untuak manghindari panyuntiangan.",
-       "viewsourcetext": "Sanak dapek malihek atau manyalin sumber laman iko:",
+       "viewsourcetext": "Sanak dapek mancaliak atau manyalin sumber laman iko:",
        "viewyourtext": "Sanak dapek mancaliak jo mangkopi sumber untuak \"suntiangan sanak\" ka laman ko",
        "protectedinterface": "Laman ko baisi teks antarmuko untuak digunoan dek parangkaik lunak di wiki ko sajo, dan alah dikunci untuak maindaan kasalahan. \nUntuak manambah atau maubah tajamahan di kasado wiki, harap gunoan [https://translatewiki.net/ translatewiki.net], yaitu proyek palokalan MediaWiki.",
        "editinginterface": "'''Paringatan:''' Sanak manyuntiang laman nan digunoan untuak manyadiokan teks antarmuko untuak parangkaik lunak.\nParubahan teks ko akan mampangaruhi tampilan pado antarmuko pangguno untuak pangguno lain.\nUntuak tajamahan, harap gunoan [https://translatewiki.net/wiki/Main_Page?setlang=min translatewiki.net], proyek palokalan MediaWiki.",
        "userlogin-noaccount": "Alun ado akun?",
        "userlogin-joinproject": "Join {{SITENAME}}",
        "createaccount": "Buek akun",
-       "userlogin-resetpassword-link": "Buek ulang kato sandi",
+       "userlogin-resetpassword-link": "Lupo kato sandi Sanak?",
+       "userlogin-helplink2": "Bantuan masuak log",
        "createacct-emailrequired": "Alamaik surel",
        "createacct-emailoptional": "Alamaik surel (opsional)",
        "createacct-email-ph": "Masuakan alamaik surel Sanak",
        "loginlanguagelabel": "Baso: $1",
        "suspicious-userlogout": "Pamintaan Sanak untuak kalua log ditulak karano tampaknyo dikirim oleh paramban nan rusak atau proksi panyinggah.",
        "pt-login": "Masuak log",
+       "pt-login-button": "Masuak log",
        "pt-createaccount": "Buek akun",
        "pt-userlogout": "Kalua log",
        "php-mail-error-unknown": "Kasalahan nan indak jaleh dalam fungsi mail() PHP",
        "preview": "Caliak",
        "showpreview": "Pratonton",
        "showdiff": "Parubahan",
-       "anoneditwarning": "'''Ingek:''' Sanak alun masuak log.\nAlamat IP sanak tacatat pado riwayaik suntiangan laman ko.",
+       "anoneditwarning": "'''Ingek:''' Sanak alun masuak log.\nAlamat IP sanak tacatat pado riwayaik suntiangan laman ko. Kok Sanak <strong>[$1 log in]</strong> atau <strong>[$2 mambuek akun]</strong>, suantiang Sanak ka didistribusian kapado namo pangguno Sanak, sarato baragam kauntuangan lainnyo.",
        "anonpreviewwarning": "''Sanak alun masuak log. Manyimpan laman akan manyababkan alamaik IP Sanak tacatat pado riwayat suntiangan laman iko.''",
        "missingsummary": "'''Paringatan:''' Sanak indak mamasuakan ringkasan panyuntiangan. Jikok Sanak baliak manakan tombol Simpan, suntiangan Sanak akan disimpan tanpa ringkasan panyuntiangan.",
        "missingcommenttext": "Masuakan komentar Sanak di bawah ko.",
        "permissionserrorstext": "Sanak indak ado hak untuak malakuannyo dek {{PLURAL:$1|alasan}} barikuik:",
        "permissionserrorstext-withaction": "Sanak indak punyo hak akses untuak $2, dek {{PLURAL:$1|alasan}} barikuik:",
        "recreate-moveddeleted-warn": "'''Ingek: Sanak mambuek ulang suatu laman nan alah dihapuih.'''\n\nHarap ditimbang apo rancak malanjuikan suntiangan Sanak.\nBarikuik ko log pangapuihan jo pamindahan dari laman ko:",
-       "moveddeleted-notice": "Laman ko alah dihapuih.\nSabagai reperensi, barikuik adolah log pangapuihan dan pamindahannyo.",
+       "moveddeleted-notice": "Laman ko alah dihapuih.\nSabagai referensi, barikuik adolah log pangapuihan jo pamindahannyo.",
        "log-fulllog": "Liek saluruah log",
        "edit-hook-aborted": "Suntiangan dibatalan samo kait parser\ntanpa ado katarangan.",
        "edit-gone-missing": "Indak dapek mampabarui laman.\nMungkin alah dihapuih.",
        "histlegend": "Bandiangan piliahan: Tandoi revisi untuak mambandiangan dan takan enter atau tombol di bawah.<br />\nContoh: '''({{int:cur}})''' = bedo jo versi tarakhia, '''({{int:last}})''' = bedo jo versi sabalunnyo, '''{{int:minoreditletter}}''' = suntiangan ketek.",
        "history-fieldset-title": "Talusuri riwayaik",
        "history-show-deleted": "Hanyo nan dihapuih",
-       "histfirst": "Nan lamo",
-       "histlast": "Nan baru",
+       "histfirst": "Nan paliang lamo",
+       "histlast": "Nan paliang baru",
        "historysize": "({{PLURAL:$1|$1  bita}})",
        "historyempty": "(kosong)",
        "history-feed-title": "Riwayat revisi",
        "shown-title": "Tampilkan $1 {{PLURAL:$1|hasil}} per laman",
        "viewprevnext": "Caliak ($1 {{int:pipe-separator}} $2) ($3)",
        "searchmenu-exists": "'''Ado laman nan banamo \"[[:$1]]\" pado wiki ko.'''",
-       "searchmenu-new": "'''Buek laman \"[[:$1]]\" di wiki ko!'''",
+       "searchmenu-new": "<strong>Buek laman \"[[:$1]]\" di wiki ko!</strong> {{PLURAL:$2|0=|Caliak pulo laman nan ditamukan dari pancarian Sanal.|Caliak pulo hasia pancarian nan ditamukan.}}",
        "searchprofile-articles": "Laman isi",
        "searchprofile-images": "Multimedia",
        "searchprofile-everything": "Sadonyo",
        "searchrelated": "bakaitan",
        "searchall": "sado",
        "showingresults": "Di bawah ko dikaluaan sampai {{PLURAL:$1|'''$1''' hasil}}, dimulai dari #'''$2'''.",
+       "search-showingresults": "{{PLURAL:$4|Hasia <strong>$1</strong> dari <strong>$3</strong>|Hasia <strong>$1 - $2</strong> dari <strong>$3</strong>}}",
        "search-nonefound": "Indak ado hasil nan cocok sasuai jo parmintaan",
        "powersearch-legend": "Pencarian lanjut",
        "powersearch-ns": "Mancari di ruangnamo:",
        "action-writeapi": "manggunoan panulisan API",
        "action-import": "impor laman ko dari wiki lain",
        "nchanges": "$1 {{PLURAL:$1|parubahan}}",
+       "enhancedrc-history": "riwayaik",
        "recentchanges": "Parubahan baru",
        "recentchanges-legend": "Piliahan parubahan baru",
        "recentchanges-summary": "Caliak parubahan tabaru pado wiki di laman ko.<br />\n;Patunjuak:(<span style=\"color:blue;\">bedo</span>) parubahan, (<span style=\"color:blue;\">sijarah</span>) riwayaik parubahan, '''B''' laman baru, '''b''' suntiangan bot, '''k''' suntiangan ketek, <span class=\"unpatrolled\">!</span> parubahan alun dipatroli,<br /><span style=\"color:green;\">'''(+ ''bita'')'''</span> isi laman batambah, <span style=\"color:red;\">(- ''bita'')</span> isi laman bakurang, (← Ikhtisar otomatih), (→ <span style=\"color:grey;\">Suntiangan bagian</span>)",
+       "recentchanges-noresult": "Indak ado parubahan dalam rantang wakatu ko nan sasuai jo kriteria.",
        "recentchanges-feed-description": "Tamuan parubahan baru dalam umpan wiki ko",
        "recentchanges-label-newpage": "Suntiangan ko mambuek laman baru",
        "recentchanges-label-minor": "Iko suntiangan ketek",
        "rcnotefrom": "Di bawah ko ado parubahan mulai dari '''$2''' (sampai '''$1''' parubahan).",
        "rclistfrom": "Tunjuakan parubahan baru mulai dari tanggal $3 $2",
        "rcshowhideminor": "$1 suntiangan ketek",
+       "rcshowhideminor-show": "Tunjuakan",
+       "rcshowhideminor-hide": "Suruakan",
        "rcshowhidebots": "$1 bot",
+       "rcshowhidebots-show": "Tunjuakan",
+       "rcshowhidebots-hide": "Suruakan",
        "rcshowhideliu": "$1 pangguno tadaftar",
+       "rcshowhideliu-show": "Tunjuakan",
+       "rcshowhideliu-hide": "Suruakan",
        "rcshowhideanons": "$1 pangguno anon",
+       "rcshowhideanons-show": "Tunjuakan",
+       "rcshowhideanons-hide": "Suruakan",
        "rcshowhidepatr": "$1 suntiangan tapatroli",
        "rcshowhidemine": "$1 suntiangan denai",
+       "rcshowhidemine-show": "Tunjuakan",
+       "rcshowhidemine-hide": "Suruakan",
        "rclinks": "Tunjuakkan $1 parubahan tabaru dalam $2 hari nan tarakhia",
        "diff": "bedo",
        "hist": "sijarah",
        "imagelinks": "Panggunoan berkas",
        "linkstoimage": "{{PLURAL:$1|Halaman|$1 halaman}} nan iko manggunoan berkas nan iko:",
        "linkstoimage-more": "Labiah dari $1 {{PLURAL:$1|laman}} ado pautan ka berkas ko.\nDaftar barikuik manunjuakan {{PLURAL:$1|$1 laman jo pautan langsuang}} ka berkas ko.\nAdo juo tasadio [[Special:WhatLinksHere/$2|daftar langkoknyo]].",
-       "nolinkstoimage": "Indak ado laman nan bapauik ka berkas ko.",
+       "nolinkstoimage": "Indak ado laman nan manggunokan berkas ko.",
        "morelinkstoimage": "Caliak [[Special:WhatLinksHere/$1|pautan baliak]] ka berkas ko.",
        "linkstoimage-redirect": "$1 (pangaliahan berkas) $2",
        "duplicatesoffile": "Sabanyak {{PLURAL:$1|$1 berkas barikuik}} marupoan duplikat dari berkas ko ([[Special:FileDuplicateSearch/$2|rincian labiah lanjuik]]):",
        "apisandbox": "Bak kasiak API",
        "booksources": "Sumber buku",
        "booksources-search-legend": "Cari di sumber buku",
+       "booksources-search": "Cari",
        "specialloguserlabel": "Pangguno:",
        "speciallogtitlelabel": "Target (judul atau pangguno):",
        "log": "Log",
        "contributions": "Jariah {{GENDER:$1|pangguno}}",
        "contributions-title": "Jariah pangguno untuak $1",
        "mycontris": "Jariah",
-       "contribsub2": "Untuak $1 ($2)",
+       "anoncontribs": "Jariah",
+       "contribsub2": "Untuak {{GENDER:$3|$1}} ($2)",
        "uctop": "kini",
        "month": "Dari bulan (dan sabalunnyo):",
        "year": "Dari taun (dan sabalunnyo):",
        "sp-contributions-search": "Cari jariah",
        "sp-contributions-username": "Alamaik IP atau namo pangguno:",
        "sp-contributions-toponly": "Hanyo manampilan suntiangan nan tarakhia",
+       "sp-contributions-newonly": "Hanyo manampilan suntiangan nan tarakhia",
        "sp-contributions-submit": "Cari",
        "whatlinkshere": "Pautan baliak",
        "whatlinkshere-title": "Laman nan takaik ka \"$1\"",
        "importstart": "Mangimpor laman...",
        "importnosources": "Indak ado sumber impor transwiki nan lah dibuek dan pamuatan riwayaik sacaro langsuang alah dinon-aktipan.",
        "importlogpagetext": "Administrasi laman impor jo riwayaik panyuntiangannyo dari wiki lain.",
-       "tooltip-pt-userpage": "Laman pangguno Sanak",
+       "tooltip-pt-userpage": "Laman {{GENDER:|pangguno Sanak}}",
        "tooltip-pt-anonuserpage": "Laman pangguno IP Sanak",
-       "tooltip-pt-mytalk": "Laman rundiang Sanak",
+       "tooltip-pt-mytalk": "Laman rundiang {{GENDER:|Sanak}}",
        "tooltip-pt-anontalk": "Parundiangan tantang suntiangan dari IP ko",
-       "tooltip-pt-preferences": "Pangaturan denai",
+       "tooltip-pt-preferences": "Piliahan {{GENDER:|Sanak}}",
        "tooltip-pt-watchlist": "Daftar laman nan dipantau.",
-       "tooltip-pt-mycontris": "Daftar jariah Sanak",
+       "tooltip-pt-mycontris": "Daftar jariah {{GENDER:|Sanak}}",
        "tooltip-pt-login": "Sanak disaranan untuak masuak log; walaupun indak wajib",
        "tooltip-pt-logout": "Kalua log",
        "tooltip-pt-createaccount": "Sanak dianjuaan mambuek akun dan masuak log; walaupun hal iko indak aruih",
        "tooltip-t-recentchangeslinked": "Parubahan baru laman nan bakaik jo laman ko",
        "tooltip-feed-rss": "Umpan RSS untuak laman ko",
        "tooltip-feed-atom": "Umpan Atom untuak laman ko",
-       "tooltip-t-contributions": "Caliak daftar jariah pangguno ko",
+       "tooltip-t-contributions": "Daftar kontribusi {{GENDER:$1|pangguno iko}}",
        "tooltip-t-emailuser": "Kirimkan surel pado pangguno ko",
        "tooltip-t-upload": "Muek berkas",
        "tooltip-t-specialpages": "Daftar kasado laman istimewa",
        "logentry-newusers-autocreate": "Akun pangguno $1 alah {{GENDER:$2|dibuek}} sacaro otomatih",
        "logentry-rights-rights": "$1 {{GENDER:$2|maubah}} kaanggotaan kalompok $3 dari $4 manjadi $5",
        "logentry-rights-rights-legacy": "$1 {{GENDER:$2|maubah}} kaanggotaan kalompok $3",
+       "logentry-upload-upload": "$1 {{GENDER:$2|mangunggah}} $3",
        "rightsnone": "(indak ado)",
        "searchsuggest-search": "Cari {{SITENAME}}",
        "searchsuggest-containing": "Barisi...",
index 2349b20..9e38c01 100644 (file)
        "resetpass-abort-generic": "Generic error message shown on [[Special:ChangePassword]] when an extension aborts a password change from a hook.",
        "resetpass-expired": "Generic error message shown on [[Special:ChangePassword]] when a user's password is expired",
        "resetpass-expired-soft": "Generic warning message shown on [[Special:ChangePassword]] when a user needs to reset their password, but they are not prevented from logging in at this time",
+       "resetpass-validity": "Warning message shown on [[Special:ChangePassword]] when a user needs to reset their password, because their password is not valid.\n\nParameters:\n* $1 - error message",
        "resetpass-validity-soft": "Warning message shown on [[Special:ChangePassword]] when a user needs to reset their password, because their password is not valid.\n\nRefers to {{msg-mw|authprovider-resetpass-skip-label}}.\n\nParameters:\n* $1 - error message",
        "passwordreset": "Title of [[Special:PasswordReset]].\n{{Identical|Reset password}}",
        "passwordreset-text-one": "Text on [[Special:PasswordReset]] that appears when there is only one way of resetting the password.\n\n{{msg-mw|Passwordreset-text-many}} will be used, when there are multiple ways of resetting the password.",
        "blockedtext": "Text displayed to blocked users.\n\n\"email this user\" should be consistent with {{msg-mw|Emailuser}}.\n\nParameters:\n* $1 - the blocking sysop (with a link to his/her userpage)\n* $2 - the reason for the block\n* $3 - the current IP address of the blocked user\n* $4 - (Unused) the blocking sysop's username (plain text, without the link)\n* $5 - the unique numeric identifier of the applied autoblock\n* $6 - the expiry of the block\n* $7 - the intended target of the block (what the blocking user specified in the blocking form)\n* $8 - the timestamp when the block started\nSee also:\n* {{msg-mw|Grouppage-sysop}}\n* {{msg-mw|Autoblockedtext|notext=1}}\n* {{msg-mw|Systemblockedtext|notext=1}}",
        "autoblockedtext": "Text displayed to automatically blocked users.\n\n\"email this user\" should be consistent with {{msg-mw|Emailuser}}.\n\nParameters:\n* $1 - the blocking sysop (with a link to his/her userpage)\n* $2 - the reason for the block (in case of autoblocks: {{msg-mw|autoblocker}})\n* $3 - the current IP address of the blocked user\n* $4 - (Unused) the blocking sysop's username (plain text, without the link). Use it for GENDER.\n* $5 - the unique numeric identifier of the applied autoblock\n* $6 - the expiry of the block\n* $7 - the intended target of the block (what the blocking user specified in the blocking form)\n* $8 - the timestamp when the block started\nSee also:\n* {{msg-mw|Grouppage-sysop}}\n* {{msg-mw|Blockedtext|notext=1}}\n* {{msg-mw|Systemblockedtext|notext=1}}",
        "systemblockedtext": "Text displayed to requests blocked by MediaWiki configuration.\n\n\"email this user\" should be consistent with {{msg-mw|Emailuser}}.\n\nParameters:\n* $1 - (Unused) A dummy user attributed as the blocker, possibly as a link to a user page.\n* $2 - the reason for the block\n* $3 - the current IP address of the blocked user\n* $4 - (Unused) the dummy blocking user's username (plain text, without the link).\n* $5 - A short string indicating the type of system block.\n* $6 - the expiry of the block\n* $7 - the intended target of the block\n* $8 - the timestamp when the block started\nSee also:\n* {{msg-mw|Grouppage-sysop}}\n* {{msg-mw|Blockedtext|notext=1}}\n* {{msg-mw|Autoblockedtext|notext=1}}",
+       "actionblockedtext": "Text displayed when a user is blocked from perofmring an action, but no matching block for the user exists. This can happen if an extension forces a user to be blocked.",
        "blockednoreason": "Substituted with <code>$2</code> in the following message if the reason is not given:\n* {{msg-mw|cantcreateaccount-text}}.\n{{Identical|No reason given}}",
        "whitelistedittext": "Used as error message. Parameters:\n* $1 - a link to [[Special:UserLogin]] with {{msg-mw|loginreqlink}} as link description\n* $2 - an URL to the same\n\nSee also:\n* {{msg-mw|Nocreatetext}}\n* {{msg-mw|Uploadnologintext}}\n* {{msg-mw|Loginreqpagetext}}",
        "confirmedittext": "Used as error message.",
index 1ca81b1..45f424a 100644 (file)
        "tog-watchdefault": "Men tahrirlagan sahifa va fayllar kuzatuv roʻyxatimga qoʻshilsin",
        "tog-watchmoves": "Men nomini koʻchirgan sahifa va fayllar kuzatuv roʻyxatimga qoʻshilsin",
        "tog-watchdeletion": "Men oʻchirgan sahifa va fayllarni kuzatuv roʻyxatimga qoʻsh",
-       "tog-minordefault": "Sukut boʻyicha barcha tahrirlarimni «kichik tahrir» etib belgilash",
+       "tog-watchuploads": "Yuklagan fayllarim kuzatuv roʻyxatimga qoʻshilsin",
+       "tog-watchrollback": "Tezda qaytarish harakatini amalga oshirganimdan keyin oʻsha sahifa kuzatuv roʻyxatimga qoʻshilsin",
+       "tog-minordefault": "Sukut boʻyicha barcha tahrirlarimni «kichik tahrir» deb belgilansin",
        "tog-previewontop": "Koʻrib chiqish imkoniyati tahrir oynasi tepasida boʻlsin",
-       "tog-previewonfirst": "Tahrirlashga oʻtgandayoq koʻrib chiqishni boshlash",
+       "tog-previewonfirst": "Tahrirlashga oʻtgandayoq koʻrib chiqish boshlansin",
        "tog-enotifwatchlistpages": "Kuzatuv roʻyxatimdagi sahifa yoki fayllar oʻzgartirilsa, menga bu haqda xat yuborilsin",
        "tog-enotifusertalkpages": "Munozara sahifam oʻzgartirilsa, menga bu haqda xat yuborilsin",
-       "tog-enotifminoredits": "Kichik tahrir qilinsa ham e-pochtamga bu haqda xat yuborilsin",
-       "tog-enotifrevealaddr": "Xabar beruvchi xatlarda e-pochta manzilim koʻrsatilsin",
-       "tog-shownumberswatching": "Sahifani kuzatuv roʻyxatiga olgan foydalanuvchilar sonini koʻrsatish",
-       "tog-oldsig": "Joriy imzo:",
+       "tog-enotifminoredits": "Sahifa va fayllarga kichik oʻzgarish kiritilsa ham elektron pochtamga bu haqda xat yuborilsin",
+       "tog-enotifrevealaddr": "Xabar beruvchi xatlarda elektron pochta manzilim koʻrsatilsin",
+       "tog-shownumberswatching": "Sahifani kuzatuv roʻyxatiga qoʻshgan foydalanuvchilar soni koʻrsatilsin",
+       "tog-oldsig": "Joriy imzoingiz:",
        "tog-fancysig": "Imzoni viki-belgi qilib koʻrsatish (avtomatik ishoratsiz)",
-       "tog-uselivepreview": "Tez koʻrib chiqish",
-       "tog-forceeditsummary": "Qisqa tavsif oynasi toʻldirilmagani haqida ogohlantirish koʻrsatish",
+       "tog-uselivepreview": "Oʻzgarishlarni sahifani yangilamasdan koʻrib chiqish",
+       "tog-forceeditsummary": "Qisqa tavsif oynasi toʻldirilmagani haqida ogohlantirish berilsin",
        "tog-watchlisthideown": "Oʻz tahrirlarim kuzatuv roʻyxatimda koʻrsatilmasin",
        "tog-watchlisthidebots": "Botlar qilgan tahrirlar kuzatuv roʻyxatimda koʻrsatilmasin",
        "tog-watchlisthideminor": "Kichik tahrirlar kuzatuv roʻyxatimda koʻrsatilmasin",
        "tog-watchlisthideliu": "Tizimga kirgan foydalanuvchilar tahrirlari kuzatuv roʻyxatimda koʻrsatilmasin",
-       "tog-watchlisthideanons": "Anonim foydalanuvchilar tahrirlari kuzatuv roʻyxatimda koʻrsatilmasin",
+       "tog-watchlisthideanons": "Anonimlarning tahrirlari kuzatuv roʻyxatimda koʻrsatilmasin",
        "tog-watchlisthidepatrolled": "Tekshirilgan tahrirlar kuzatuv roʻyxatimda koʻrsatilmasin",
+       "tog-watchlisthidecategorization": "Sahifalarning turkumlari yashirilsin",
        "tog-ccmeonemails": "Boshqa ishtirokchilarga yozgan xatimning nusxasi oʻzimga yuborilsin",
        "tog-diffonly": "Versiyalar taqqoslanayotganda, pastda sahifa matni koʻrsatilmasin",
-       "tog-showhiddencats": "Yashirin turkumlarni koʻrsatish",
-       "tog-norollbackdiff": "Tahrir qaytarilganda, versiyalar taqqosi koʻrsatilmasin",
-       "tog-useeditwarning": "Oʻzgarishlarni saqlamay sahifadan chiqib ketayotganim haqida ogohlantir",
-       "tog-prefershttps": "Doim himoyalangan holda kirish",
+       "tog-showhiddencats": "Yashirin turkumlar koʻrsatilsin",
+       "tog-norollbackdiff": "Biron tahrir tezda qaytarilsa, versiyalar taqqosini koʻrsatishning hojati yoʻq",
+       "tog-useeditwarning": "Oʻzgarishlarni saqlamay sahifadan chiqib ketayotganim haqida ogohlantirish berilsin",
+       "tog-prefershttps": "Tizimga kirganimdan keyin doim himoyalangan aloqadan foydalanilsin",
        "underline-always": "Har doim",
        "underline-never": "Hech qachon",
-       "underline-default": "Bezak mavzusi yoki brauzer andozasi boʻyicha",
+       "underline-default": "Brauzer sozlamalaridan foydalanilsin",
        "editfont-style": "Tahrirlash maydonidagi shrift turi:",
        "editfont-monospace": "Teng enli shrift (Monospaced)",
        "editfont-sansserif": "Kertiksiz shrift (Sans-serif)",
        "category_header": "„$1“ turkumidagi sahifalar",
        "subcategories": "Ostturkumlar",
        "category-media-header": "„$1“ turkumidagi fayllar",
-       "category-empty": "''Ushbu turkumda hozircha sahifa yoki fayllar yoʻq.''",
+       "category-empty": "<em>Ushbu turkumda hozircha sahifa yoki fayllar yoʻq.</em>",
        "hidden-categories": "{{PLURAL:$1|Yashirin turkum}}",
        "hidden-category-category": "Yashirin turkumlar",
        "category-subcat-count": "{{PLURAL:$2|Ushbu turkumda faqat bitta ostturkum mavjud.|Quyida ushbu turkumga kiruvchi $2 ta ostturkumdan $1 tasi koʻrsatilgan.}}",
        "mytalk": "Munozara",
        "anontalk": "Ushbu IP-manzil munozarasi",
        "navigation": "Saytda harakatlanish",
-       "and": "&nbsp;va",
+       "and": "&#32;va",
        "faq": "TSS",
        "actions": "Amallar",
        "namespaces": "Nomfazolar",
        "viewsourcelink": "manbasini koʻrish",
        "editsectionhint": "Boʻlimni tahrirlash: $1",
        "toc": "Mundarija",
-       "showtoc": "koʻrsatish",
+       "showtoc": "koʻrsat",
        "hidetoc": "yashirish",
        "collapsible-collapse": "Yigʻish",
        "collapsible-expand": "Yoyish",
        "viewsource-title": "$1 sahifasining manbasini koʻrish",
        "actionthrottled": "Tezlik cheklovi",
        "protectedpagetext": "Bu sahifa tahrirlash va boshqa oʻzgarishlar kiritishdan himoyalangan.",
-       "viewsourcetext": "Siz bu sahifaning manbasini koʻrishingiz va uni nusxasini olishingiz mumkin:",
+       "viewsourcetext": "Siz bu sahifaning ichki kodini koʻrishingiz va undan nusxa olishingiz mumkin:",
        "protectedinterface": "Ushbu sahifada dasturiy taʼminot interfeysi xabari mavjud. Bezoriliklardan saqlash uchun uni oʻzgartirish taʼqiqlangan.\nUshbu xabar tarjimasini qoʻshish yoki oʻzgartirish uchun, iltimos, MediaWikining [https://translatewiki.net/ translatewiki.net] mahalliylashtirish saytidan foydalaning.",
        "editinginterface": "<strong>Eʼtibor bering:</strong> Siz interfeys matnini aks ettiruvchi sahifani tahrirlamoqdasiz.\nUning oʻzgartirilishi boshqa foydalanuvchilar uchun ham interfeys oʻzgarishiga olib keladi.",
        "translateinterface": "Ushbu xabar tarjimasini qoʻshish yoki oʻzgartirish uchun, iltimos, MediaWikining [https://translatewiki.net/ translatewiki.net] mahalliylashtirish saytidan foydalaning.",
        "subject": "Mavzu/sarlavha",
        "minoredit": "Bu kichik tahrir",
        "watchthis": "Sahifani kuzatish",
-       "savearticle": "Saqla",
-       "publishpage": "Sahifani chop et",
-       "publishchanges": "Oʻzgarishlarni chop et",
+       "savearticle": "Chop etish",
+       "savechanges": "Oʻzgarishlarni saqlash",
+       "publishpage": "Sahifani chop etish",
+       "publishchanges": "Oʻzgarishlarni chop etish",
        "preview": "Ko‘rib chiqish",
        "showpreview": "Koʻrib chiqish",
        "showdiff": "Kiritilgan o‘zgarishlar",
        "rev-deleted-user": "(muallif nomi oʻchirilgan)",
        "rev-deleted-event": "(jurnal tafsilotlari o‘chirildi)",
        "rev-delundel": "koʻrsatish/yashirish",
-       "rev-showdeleted": "koʻrsatish",
+       "rev-showdeleted": "koʻrsat",
        "revdelete-show-file-submit": "Ha",
        "revdelete-confirm": "Iltimos, haqiqatdan ham shu harakatni amalga oshirmoqchiligingizni, uning oqibatlarini tushunib turganingizni va harakatingiz [[{{MediaWiki:Policy-url}}|qoidalarga]] asosanlanganini tasdiqlang.",
        "revdelete-hide-text": "Sahifaning ushbu versiyasi matnini yashirish",
        "prefs-signature": "Imzo",
        "prefs-dateformat": "Sana formati",
        "prefs-timeoffset": "Vaqt farqi",
-       "prefs-advancedediting": "Qoʻshimcha moslamalar",
+       "prefs-advancedediting": "Asosiy bandlar",
+       "prefs-editor": "Tahrirlagich",
        "prefs-advancedrc": "Qoʻshimcha moslamalar",
        "prefs-advancedrendering": "Qoʻshimcha moslamalar",
        "prefs-advancedsearchoptions": "Qoʻshimcha moslamalar",
        "userrights": "Huquqlarini oʻzgartirish",
        "userrights-lookup-user": "Foydalanuvchini tanlash",
        "userrights-user-editname": "Foydalanuvchi nomi:",
-       "editusergroup": "Shu foydalanuvchi huquqlarini oʻzgartirish",
+       "editusergroup": "Qaysi guruhlarga aʼzo ekanligini koʻrish",
        "editinguser": "{{GENDER:$1|Foydalanuvchi}} <strong>[[User:$1|$1]]</strong> $2 huquqlarini oʻzgartirish",
        "userrights-editusergroup": "Guruhlardagi aʼzoligini oʻzgartirish",
        "saveusergroups": "Oʻzgarishlarni saqlash",
        "newuserlogpage": "Foydalanuvchilarni roʻyxatga olish qaydlari",
        "newuserlogpagetext": "Yaqinda roʻyxatdan oʻtgan foydalanuvchilar roʻyxati",
        "rightslog": "Foydalanuvchi huquqlari koʻrsatilgan qaydlar",
-       "rightslogtext": "Foydalanuvchi huquqlarini oʻzgartirish qaydlari.",
+       "rightslogtext": "Bu sahifada foydalanuvchilarning huquqlarini oʻzgartirish qaydlari koʻrsatilgan.",
        "action-edit": "ushbu sahifani tahrirlash",
        "action-move": "bu sahifani koʻchirish",
        "action-move-subpages": "Bu sahifani va uning ostsahifalarini koʻchirish",
        "recentchanges-label-plusminus": "Sahifa vazni qanchaga oʻzgargani (bayt)",
        "recentchanges-legend-heading": "<strong>Izoh:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} ([[Special:NewPages|alohida roʻyxat]])",
+       "recentchanges-submit": "Koʻrsat",
+       "rcfilters-activefilters-show": "Koʻrsat",
        "rcnotefrom": "Quyida <strong>$3, $4</strong> dan keyin sodir boʻlgan oʻzgarishlar koʻrsatilgan (oxirgi <strong>$1</strong> tasi).",
        "rclistfrom": "$3, $2 dan keyin sodir boʻlgan oʻzgarishlarni koʻrsat",
        "rcshowhideminor": "Kichik tahrirlarni $1",
-       "rcshowhideminor-show": "koʻrsat",
+       "rcshowhideminor-show": "Koʻrsat",
        "rcshowhideminor-hide": "yashir",
        "rcshowhidebots": "Botlarni $1",
-       "rcshowhidebots-show": "koʻrsat",
+       "rcshowhidebots-show": "Koʻrsat",
        "rcshowhidebots-hide": "yashir",
        "rcshowhideliu": "Roʻyxatdan oʻtganlarni $1",
        "rcshowhideliu-show": "koʻrsat",
        "rcshowhideliu-hide": "yashir",
        "rcshowhideanons": "Anonimlarni $1",
-       "rcshowhideanons-show": "koʻrsat",
+       "rcshowhideanons-show": "Koʻrsat",
        "rcshowhideanons-hide": "yashir",
        "rcshowhidepatr": "Tekshirilgan tahrirlarni $1",
+       "rcshowhidepatr-show": "Koʻrsat",
        "rcshowhidepatr-hide": "yashir",
        "rcshowhidemine": "Oʻz tahrirlarimni $1",
-       "rcshowhidemine-show": "koʻrsat",
+       "rcshowhidemine-show": "Koʻrsat",
        "rcshowhidemine-hide": "yashir",
        "rclinks": "Oxirgi $2 kun ichida sodir boʻlgan $1 ta oʻzgarish koʻrsatildi",
        "diff": "farq",
        "hist": "tarix",
-       "hide": "yashir",
-       "show": "koʻrsat",
+       "hide": "Yashir",
+       "show": "Koʻrsat",
        "minoreditletter": "k",
        "newpageletter": "Y",
        "boteditletter": "b",
        "statistics-header-users": "Foydalanuvchilar statistikasi",
        "statistics-articles": "Maqolalar",
        "statistics-pages": "Sahifalar",
-       "statistics-pages-desc": "Ushbu vikidagi barcha sahifalar, jumladan munozara, yoʻnaltirish va hk.",
+       "statistics-pages-desc": "Ushbu vikidagi barcha sahifalar, shu jumladan munozara sahifalari, qayta yoʻnaltiruvchi va boshqa sahifalar",
        "statistics-files": "Yuklangan fayllar",
        "statistics-edits": "{{SITENAME}}dagi tahrirlarning umumiy soni",
        "statistics-edits-average": "Oʻrtacha tahrirlar soni (sahifa boshiga)",
        "pageswithprop-prop": "Xossa nomi:",
        "pageswithprop-submit": "Oʻtish",
        "brokenredirects-edit": "tahrirlash",
+       "withoutinterwiki-submit": "Koʻrsat",
        "nbytes": "$1 {{PLURAL:$1|bayt}}",
        "ncategories": "$1 {{PLURAL:$1|turkum|turkumlar}}",
        "nmembers": "$1 {{PLURAL:$1|ta sahifa}}",
        "wantedcategories": "Talab qilinayotgan turkumlar",
        "mostcategories": "Eng koʻp turkumli sahifalar",
        "prefixindex": "Prefiksli barcha sahifalar",
+       "prefixindex-submit": "Koʻrsat",
        "prefixindex-strip": "Natijalar roʻyxatida prefiks koʻrsatilmasin",
        "protectedpages": "Himoyalangan sahifalar",
        "listusers": "Foydalanuvchilar roʻyxati",
        "usercreated": "$1, $2 da {{GENDER:$3|roʻyxatdan oʻtgan}}",
        "newpages": "Yangi sahifalar",
+       "newpages-submit": "Koʻrsat",
        "move": "Ko‘chirish",
        "movethispage": "Bu sahifani koʻchirish",
        "pager-newer-n": "{{PLURAL:$1|yangiroq 1|yangiroq $1}}",
        "specialloguserlabel": "Ijrochi:",
        "speciallogtitlelabel": "Moʻljal:",
        "log": "Qaydlar",
+       "logeventslist-submit": "Koʻrsat",
        "all-logs-page": "Barcha ochiq qaydlar",
        "alllogstext": "{{SITENAME}}dagi barcha jurnallar roʻyxati.\nNatijalarni jurnal nomi, foydalanuvchi nomi (harflar katta-kichikligi inobatga olinadi) yoki sahifa nomi boʻyicha saralashingiz mumkin.\n* Biror foydalanuvchi amalga oshirgan qaydni topish uchun uning foydalanuvchi nomini „Ijrochi“ oynasiga kiriting.\n* Biror foydalanuvchi yoki sahifaga nisbatan amalga oshirilgan qaydni topish uchun ulardan birining nomini „Moʻljal“ oynasiga kiriting.",
        "logempty": "Talabga mos yozuvlar mavjud emas.",
        "allpages-hide-redirects": "Yoʻnaltirishlarni yashirish",
        "cachedspecial-refresh-now": "Oxirgi versiyasini koʻrish",
        "categories": "Turkumlar",
+       "categories-submit": "Koʻrsat",
        "categoriespagetext": "Quyidagi {{PLURAL:$1|turkumda|turkumlarda}} sahifa yoki media-fayllar mavjud.\n[[Special:UnusedCategories|Ishlatilmayotgan turkumlar]] bu yerda koʻrsatilmaydi.\nShuningdek qarang: [[Special:WantedCategories|talab qilinayotgan turkumlar]].",
        "categoriesfrom": "Quyidagidan boshlanuvchi turkumlarni koʻrsatish:",
        "deletedcontributions": "Foydalanuvchining o‘chirilgan hissasi",
        "linksearch-ok": "Qidirish",
        "linksearch-line": "$2 ichidan $1 ga havola",
        "listusersfrom": "Quyidagidan boshlanuvchi foydalanuvchilarni koʻrsatish:",
-       "listusers-submit": "Koʻrsatish",
+       "listusers-submit": "Koʻrsat",
        "listusers-noresult": "Foydalanuvchilar topilmadi.",
        "listusers-blocked": "(chetlashtirilgan)",
        "activeusers": "Faol foydalanuvchilar roʻyxati",
        "wlheader-showupdated": "Siz oxirgi marta kirganingizdan keyin oʻzgartirilgan sahifalar '''qalin''' yozuv bilan ajratib koʻrsatilgan.",
        "wlnote": "Quyida oxirgi $2 soat ichida sodir boʻlgan {{PLURAL:$1|oxirgi oʻzgarish|<strong>$1</strong> ta oʻzgarishlar}} koʻrsatilgan. $3, $4.",
        "wlshowlast": "Oxirgi $1 soatdagi $2 kundagi tahrirlarni koʻrsatish",
+       "watchlist-submit": "Koʻrsat",
        "watchlist-options": "Kuzatuv roʻyxati moslamalari",
        "watching": "Kuzatish...",
        "unwatching": "Kuzatuv roʻyxatidan oʻchirilmoqda...",
        "delete-confirm": "$1 — oʻchirish",
        "delete-legend": "Sahifani o‘chirish",
        "historywarning": "<strong>Diqqat:</strong> Siz oʻchirmoqchi boʻlayotgan sahifaning tarixida $1 ta {{PLURAL:$1|versiyasi}} bor:",
+       "historyaction-submit": "Koʻrsat",
        "confirmdeletetext": "Siz ushbu sahifani va uning tarixini butunlay oʻchirib tashlamoqchi boʻlyapsiz. Iltimos, [[Special:Whatlinkshere/{{FULLPAGENAMEE}}|bogʻlangan sahifalar]] bilan tanishib chiqishni unutmang.",
        "actioncomplete": "Bajarildi",
        "actionfailed": "Jarayon amalga oshmadi",
        "contributions-title": "{{GENDER:$1|Foydalanuvchi}} $1 hissasi",
        "mycontris": "Hissam",
        "anoncontribs": "Qoʻshilgan hissa",
-       "contribsub2": "$1 uchun ($2)",
+       "contribsub2": "$1ning tahrirlari ($2)",
        "nocontribs": "Belgilangan shartlarga muvofiq oʻzgarishlar topilmadi.",
        "uctop": "joriy",
        "month": "Oydan (va avvalroq)",
        "year": "Yildan (va avvalroq)",
+       "date": "Shu sanadan avvalroq:",
        "sp-contributions-newbies": "Faqatgina yangi foydalanuvchilarning hissalarini koʻrsat",
        "sp-contributions-newbies-sub": "Yangi hisob yozuvlaridan",
        "sp-contributions-newbies-title": "Yangi hisob yozuvlarining hissalari",
        "tooltip-ca-nstab-category": "Turkum sahifasini koʻrish",
        "tooltip-minoredit": "Kichik o‘zgartirish sifatida belgilash",
        "tooltip-save": "Oʻzgarishlarni saqlash",
+       "tooltip-publish": "Siz kiritgan oʻzgarishlarni chop etish",
        "tooltip-preview": "Oʻzgarishlarni koʻrib chiqish; Iltimos, saqlashdan oldin undan foydalaning!",
        "tooltip-diff": "Matnga qanday oʻzgarishlar kiritganligingizni koʻrish.",
        "tooltip-compareselectedversions": "Bu sahifaning ikki tanlangan versiyalari orasidagi farqni koʻrish.",
        "watchlisttools-raw": "Kuzatuv roʻyxatimni tahrirlash",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|munozara]])",
        "duplicate-defaultsort": "'''Diqqat:''' \"$2\" boshlang'ich saralash kaliti oldingi \"$1\" boshlang'ich saralash kalitini qayta aniqlayapti.",
+       "version": "Versiyasi",
        "version-specialpages": "Maxsus sahifalar",
+       "version-ext-colheader-version": "Versiyasi",
+       "version-software-version": "Versiyasi",
+       "version-libraries-version": "Versiyasi",
        "specialpages": "Maxsus sahifalar",
        "tag-filter": "[[Special:Tags|Nishonlar]] filtri:",
        "tag-list-wrapper": "[[Special:Tags|{{PLURAL:$1|Teg|Teglar}}]]: $2",
        "special-characters-group-khmer": "Kxmer",
        "special-characters-title-emdash": "uzun tire",
        "special-characters-title-minus": "minus belgisi",
-       "mw-widgets-abandonedit": "Siz haqiqatdan ham oʻzgarishlarni saqlamasdan koʻrish tartibiga oʻtishni xohlaysizmi?"
+       "mw-widgets-abandonedit": "Siz haqiqatdan ham oʻzgarishlarni saqlamasdan koʻrish tartibiga oʻtishni xohlaysizmi?",
+       "log-action-filter-block": "Chetlatish turi:",
+       "log-action-filter-rights": "Huquqlarni oʻzgartirish turi:",
+       "log-action-filter-all": "Barchasi",
+       "log-action-filter-block-block": "Chetlatish",
+       "log-action-filter-block-reblock": "Chetlatish turini oʻzgartirish",
+       "log-action-filter-block-unblock": "Chetlatishni bekor qilish",
+       "log-action-filter-rights-rights": "Qoʻlda kiritilgan oʻzgarish",
+       "authprovider-resetpass-skip-label": "Qoldirib ketish"
 }
index 296caa7..0a86677 100644 (file)
        "booksources-search": "זוכן",
        "booksources-text": "אונטן איז א ליסטע פון סייטס וואס פֿארקויפֿן נייע און גענוצטע ביכער און האבן אויך נאך אינפֿארמאציע וועגן די ביכער וואס איר זוכט:",
        "booksources-invalid-isbn": "דאָס געגעבענע ISBN זעט נישט אויס צו זיין גילטיק; קאנטראלירט פֿאַר גרײַזן בײַם קאפּירן פון דעם ערשטיקן מקור.",
+       "magiclink-tracking-rfc": "בלעטער וואס ניצן מאגישע RFC-לינקען",
+       "magiclink-tracking-pmid": "בלעטער וואס ניצן מאגישע PMID-לינקען",
+       "magiclink-tracking-isbn": "בלעטער וואס ניצן מאגישע ISBN-לינקען",
        "specialloguserlabel": "אויספֿירער:",
        "speciallogtitlelabel": "ציל (טיטל אדער {{ns:user}}:באניצער־נאמען פאר א באניצער):",
        "log": "לאגביכער",
index 30ef496..fb02c34 100644 (file)
        "edit-gone-missing": "不能更新页面。\n它可能刚刚被删除。",
        "edit-conflict": "编辑冲突。",
        "edit-no-change": "因为没有文字更改,您的编辑已被忽略。",
+       "edit-slots-cannot-add": "下列{{PLURAL:$1|栏位|栏位}}在此不受支持:$2。",
+       "edit-slots-cannot-remove": "下列{{PLURAL:$1|栏位|栏位}}是必需的且无法被移除:$2。",
        "postedit-confirmation-created": "页面已创建。",
        "postedit-confirmation-restored": "页面已恢复。",
        "postedit-confirmation-saved": "您的编辑已保存。",
index 74bdcf8..b0c5c4c 100644 (file)
@@ -18,7 +18,9 @@
                $( '.config-help-field-data' ).hide()
                        .closest( '.config-help-field-container' ).find( '.config-help-field-hint' )
                        .show()
-                       .click( function () {
+                       .on( 'click', function () {
+                               // FIXME: Use CSS transition
+                               // eslint-disable-next-line jquery/no-slide
                                $( this ).closest( '.config-help-field-container' ).find( '.config-help-field-data' )
                                        .slideToggle( 'fast' );
                        } );
                        $( document.getElementById( $( this ).attr( 'rel' ) ) ).hide();
                } );
                $( document.getElementById( $( '.dbRadio:checked' ).attr( 'rel' ) ) ).show();
-               $( '.dbRadio' ).click( function () {
+               $( '.dbRadio' ).on( 'click', function () {
                        var $checked = $( '.dbRadio:checked' ),
                                $wrapper = $( document.getElementById( $checked.attr( 'rel' ) ) );
                        if ( $wrapper.is( ':hidden' ) ) {
+                               // FIXME: Use CSS transition
+                               // eslint-disable-next-line jquery/no-animate-toggle
                                $( '.dbWrapper' ).hide( 'slow' );
+                               // eslint-disable-next-line jquery/no-animate-toggle
                                $wrapper.show( 'slow' );
                        }
                } );
                } );
 
                // Show/hide Creative Commons thingy
-               $( '.licenseRadio' ).click( function () {
+               $( '.licenseRadio' ).on( 'click', function () {
                        var $wrapper = $( '#config-cc-wrapper' );
                        if ( $( '#config__LicenseCode_cc-choose' ).is( ':checked' ) ) {
+                               // FIXME: Use CSS transition
+                               // eslint-disable-next-line jquery/no-animate-toggle
                                $wrapper.show( 'slow' );
                        } else {
+                               // eslint-disable-next-line jquery/no-animate-toggle
                                $wrapper.hide( 'slow' );
                        }
                } );
 
                // Show/hide random stuff (email, upload)
-               $( '.showHideRadio' ).click( function () {
+               $( '.showHideRadio' ).on( 'click', function () {
                        var $wrapper = $( '#' + $( this ).attr( 'rel' ) );
                        if ( $( this ).is( ':checked' ) ) {
+                               // FIXME: Use CSS transition
+                               // eslint-disable-next-line jquery/no-animate-toggle
                                $wrapper.show( 'slow' );
                        } else {
+                               // eslint-disable-next-line jquery/no-animate-toggle
                                $wrapper.hide( 'slow' );
                        }
                } );
-               $( '.hideShowRadio' ).click( function () {
+               $( '.hideShowRadio' ).on( 'click', function () {
                        var $wrapper = $( '#' + $( this ).attr( 'rel' ) );
                        if ( $( this ).is( ':checked' ) ) {
+                               // FIXME: Use CSS transition
+                               // eslint-disable-next-line jquery/no-animate-toggle
                                $wrapper.hide( 'slow' );
                        } else {
+                               // eslint-disable-next-line jquery/no-animate-toggle
                                $wrapper.show( 'slow' );
                        }
                } );
                }
 
                // Enable/disable "other" textboxes
-               $( '.enableForOther' ).click( function () {
+               $( '.enableForOther' ).on( 'click', function () {
                        var $textbox = $( document.getElementById( $( this ).attr( 'rel' ) ) );
                        // FIXME: Ugh, this is ugly
                        if ( $( this ).val() === 'other' ) {
+                               // FIXME: Use CSS transition
+                               // eslint-disable-next-line jquery/no-slide
                                $textbox.prop( 'readonly', false ).closest( '.config-block' ).slideDown( 'fast' );
                        } else {
+                               // eslint-disable-next-line jquery/no-slide
                                $textbox.prop( 'readonly', true ).closest( '.config-block' ).slideUp( 'fast' );
                        }
                } );
                $( '#config_wgSitename' ).on( 'keyup change', syncText ).each( syncText );
 
                // Show/Hide memcached servers when needed
-               $( 'input[name$="config__MainCacheType"]' ).change( function () {
+               $( 'input[name$="config__MainCacheType"]' ).on( 'change', function () {
                        var $memc = $( '#config-memcachewrapper' );
                        if ( $( 'input[name$="config__MainCacheType"]:checked' ).val() === 'memcached' ) {
+                               // FIXME: Use CSS transition
+                               // eslint-disable-next-line jquery/no-animate-toggle
                                $memc.show( 'slow' );
                        } else {
+                               // eslint-disable-next-line jquery/no-animate-toggle
                                $memc.hide( 'slow' );
                        }
                } );
index e3379ca..75ebfef 100644 (file)
@@ -11,7 +11,7 @@
   },
   "devDependencies": {
     "deepmerge": "1.3.2",
-    "eslint-config-wikimedia": "0.9.0",
+    "eslint-config-wikimedia": "0.10.0",
     "grunt": "1.0.3",
     "grunt-banana-checker": "0.6.0",
     "grunt-contrib-copy": "1.0.0",
index 2b67568..4882e9e 100644 (file)
                                        }
 
                                // Cancel selection
-                               } ).mousedown( function () {
+                               } ).on( 'mousedown', function () {
                                        if ( config.cancelSelection ) {
                                                this.onselectstart = function () {
                                                        return false;
index 63cf28c..435e23f 100644 (file)
@@ -14,7 +14,7 @@
                var prevCheckbox = null,
                        $box = this;
                // When our boxes are clicked..
-               $box.click( function ( e ) {
+               $box.on( 'click', function ( e ) {
                        // And one has been clicked before...
                        if ( prevCheckbox !== null && e.shiftKey ) {
                                // Check or uncheck this one and all in-between checkboxes,
index 09306f6..f5f0475 100644 (file)
@@ -82,6 +82,7 @@
                        }
                }
 
+               // eslint-disable-next-line jquery/no-animate-toggle
                $containers.toggle( action === 'expand' );
                hookCallback();
        }
index 1016e72..f2251f5 100644 (file)
                                // Only fetch if the value in the textbox changed and is not empty, or if the results were hidden
                                // if the textbox is empty then clear the result div, but leave other settings intouched
                                if ( val.length === 0 ) {
+                                       // eslint-disable-next-line jquery/no-animate-toggle
                                        $.suggestions.hide( context );
                                        context.data.prevText = '';
                                } else if (
                                        if ( context.data !== undefined ) {
                                                if ( context.data.$textbox.val().length === 0 ) {
                                                        // Hide the div when no suggestion exist
+                                                       // eslint-disable-next-line jquery/no-animate-toggle
                                                        $.suggestions.hide( context );
                                                } else {
                                                        // Rebuild the suggestions list
                                                                        .addClass( 'suggestions-result' )
                                                                        .attr( 'rel', i )
                                                                        .data( 'text', context.config.suggestions[ i ] )
-                                                                       .mousemove( function () {
+                                                                       .on( 'mousemove', function () {
                                                                                context.data.selectedWithMouse = true;
                                                                                $.suggestions.highlight(
                                                                                        context,
                                        break;
                                // Escape
                                case 27:
+                                       // eslint-disable-next-line jquery/no-animate-toggle
                                        $.suggestions.hide( context );
                                        $.suggestions.restore( context );
                                        $.suggestions.cancel( context );
                                case 13:
                                        preventDefault = wasVisible;
                                        selected = context.data.$container.find( '.suggestions-result-current' );
+                                       // eslint-disable-next-line jquery/no-animate-toggle
                                        $.suggestions.hide( context );
                                        if ( selected.length === 0 || context.data.selectedWithMouse ) {
                                                // If nothing is selected or if something was selected with the mouse
                                                        // Can't use click() because the container div is hidden when the
                                                        // textbox loses focus. Instead, listen for a mousedown followed
                                                        // by a mouseup on the same div.
-                                                       .mousedown( function ( e ) {
+                                                       .on( 'mousedown', function ( e ) {
                                                                context.data.mouseDownOn = $( e.target ).closest( '.suggestions-results .suggestions-result' );
                                                        } )
-                                                       .mouseup( function ( e ) {
+                                                       .on( 'mouseup', function ( e ) {
                                                                var $result = $( e.target ).closest( '.suggestions-results .suggestions-result' ),
                                                                        $other = context.data.mouseDownOn;
 
                                                                        // This will hide the link we're just clicking on, which causes problems
                                                                        // when done synchronously in at least Firefox 3.6 (T64858).
                                                                        setTimeout( function () {
+                                                                               // eslint-disable-next-line jquery/no-animate-toggle
                                                                                $.suggestions.hide( context );
-                                                                       }, 0 );
+                                                                       } );
                                                                }
                                                                // Always bring focus to the textbox, as that's probably where the user expects it
                                                                // if they were just typing.
                                                        // Can't use click() because the container div is hidden when the
                                                        // textbox loses focus. Instead, listen for a mousedown followed
                                                        // by a mouseup on the same div.
-                                                       .mousedown( function ( e ) {
+                                                       .on( 'mousedown', function ( e ) {
                                                                context.data.mouseDownOn = $( e.target ).closest( '.suggestions-special' );
                                                        } )
-                                                       .mouseup( function ( e ) {
+                                                       .on( 'mouseup', function ( e ) {
                                                                var $special = $( e.target ).closest( '.suggestions-special' ),
                                                                        $other = context.data.mouseDownOn;
 
                                                                        // This will hide the link we're just clicking on, which causes problems
                                                                        // when done synchronously in at least Firefox 3.6 (T64858).
                                                                        setTimeout( function () {
+                                                                               // eslint-disable-next-line jquery/no-animate-toggle
                                                                                $.suggestions.hide( context );
-                                                                       }, 0 );
+                                                                       } );
                                                                }
                                                                // Always bring focus to the textbox, as that's probably where the user expects it
                                                                // if they were just typing.
                                                                context.data.$textbox.focus();
                                                        } )
-                                                       .mousemove( function ( e ) {
+                                                       .on( 'mousemove', function ( e ) {
                                                                context.data.selectedWithMouse = true;
                                                                $.suggestions.highlight(
                                                                        context, $( e.target ).closest( '.suggestions-special' ), false
                                $( this )
                                        // Stop browser autocomplete from interfering
                                        .attr( 'autocomplete', 'off' )
-                                       .keydown( function ( e ) {
+                                       .on( 'keydown', function ( e ) {
                                                // Store key pressed to handle later
                                                context.data.keypressed = e.which;
                                                context.data.keypressedCount = 0;
                                        } )
-                                       .keypress( function ( e ) {
+                                       .on( 'keypress', function ( e ) {
                                                context.data.keypressedCount++;
+                                               // eslint-disable-next-line jquery/no-event-shorthand
                                                $.suggestions.keypress( e, context, context.data.keypressed );
                                        } )
-                                       .keyup( function ( e ) {
+                                       .on( 'keyup', function ( e ) {
                                                // The keypress event is fired when a key is pressed down and that key normally
                                                // produces a character value. We also want to handle some keys that don't
                                                // produce a character value so we also attach to the keydown/keyup events.
                                                        e.which === context.data.keypressed &&
                                                        allowed.indexOf( e.which ) !== -1
                                                ) {
+                                                       // eslint-disable-next-line jquery/no-event-shorthand
                                                        $.suggestions.keypress( e, context, context.data.keypressed );
                                                }
                                        } )
-                                       .blur( function () {
+                                       .on( 'blur', function () {
                                                // When losing focus because of a mousedown
                                                // on a suggestion, don't hide the suggestions
                                                if ( context.data.mouseDownOn.length > 0 ) {
                                                        return;
                                                }
+                                               // eslint-disable-next-line jquery/no-animate-toggle
                                                $.suggestions.hide( context );
                                                $.suggestions.cancel( context );
                                        } );
index 6b4ab97..82aa24f 100644 (file)
                                        }
 
                                        isSample = false;
-                                       $( this ).focus();
+                                       $( this ).trigger( 'focus' );
                                        if ( options.selectionStart !== undefined ) {
                                                $( this ).textSelection( 'setSelection', { start: options.selectionStart, end: options.selectionEnd } );
                                        }
index 4c4f5eb..cbfaf62 100644 (file)
@@ -34,7 +34,7 @@
                } );
 
                // Add form submission handler
-               $( '#editform' ).submit( function () {
+               $( '#editform' ).on( 'submit', function () {
                        allowCloseWindow.release();
                } );
        } );
index 966b5bc..a26da1d 100644 (file)
@@ -43,7 +43,7 @@
                        if ( scrollTop.value ) {
                                editBox.scrollTop = scrollTop.value;
                        }
-                       $editForm.submit( function () {
+                       $editForm.on( 'submit', function () {
                                scrollTop.value = editBox.scrollTop;
                        } );
                }
index 363b494..e907a98 100644 (file)
@@ -73,6 +73,8 @@
 
                // Can't use fadeTo because it calls show(), and we might want to keep some elements hidden
                // (e.g. empty #catlinks)
+               // FIXME: Use CSS transition
+               // eslint-disable-next-line jquery/no-animate
                $copyElements.animate( { opacity: 0.4 }, 'fast' );
 
                api = new mw.Api();
                                                        .append( $( '<a>' )
                                                                .attr( {
                                                                        href: mw.util.getUrl( template.title ),
-                                                                       'class': ( template.exists ? '' : 'new' )
+                                                                       class: ( template.exists ? '' : 'new' )
                                                                } )
                                                                .text( template.title )
                                                        );
                        mw.hook( 'wikipage.editform' ).fire( $editform );
                } ).always( function () {
                        $spinner.hide();
+                       // FIXME: Use CSS transition
+                       // eslint-disable-next-line jquery/no-animate
                        $copyElements.animate( {
                                opacity: 1
                        }, 'fast' );
                if ( !document.getElementById( 'p-lang' ) && document.getElementById( 'p-tb' ) && mw.config.get( 'skin' ) === 'vector' ) {
                        $( '.portal:last' ).after(
                                $( '<div>' ).attr( {
-                                       'class': 'portal',
+                                       class: 'portal',
                                        id: 'p-lang',
                                        role: 'navigation',
                                        'aria-labelledby': 'p-lang-label'
index a0bba6f..e638108 100644 (file)
@@ -51,7 +51,7 @@ $( function () {
                return true;
        }
 
-       $lis.find( 'input[name="diff"], input[name="oldid"]' ).click( updateDiffRadios );
+       $lis.find( 'input[name="diff"], input[name="oldid"]' ).on( 'click', updateDiffRadios );
 
        // Set initial state
        updateDiffRadios();
@@ -61,7 +61,7 @@ $( function () {
 
        // Ideally we'd use e.target instead of $historySubmitter, but e.target points
        // to the form element for submit actions, so.
-       $historyCompareForm.find( '.historysubmit' ).click( function () {
+       $historyCompareForm.find( '.historysubmit' ).on( 'click', function () {
                $historySubmitter = $( this );
        } );
 
@@ -71,7 +71,7 @@ $( function () {
        // Without the cloning we'd be changing the real form, which is slower, could make
        // the page look broken for a second in slow browsers and might show the form broken
        // again when coming back from a "next" page.
-       $historyCompareForm.submit( function ( e ) {
+       $historyCompareForm.on( 'submit', function ( e ) {
                var $copyForm, $copyRadios, $copyAction;
 
                if ( $historySubmitter ) {
@@ -92,14 +92,14 @@ $( function () {
                                $copyForm.find( ':submit' ).remove();
                        }
 
-                       // IE7 doesn't do submission from an off-DOM clone, so insert hidden into document first
+                       // Firefox requires the form to be attached, so insert hidden into document first
                        // Also remove potentially conflicting id attributes that we don't need anyway
                        $copyForm
                                .css( 'display', 'none' )
                                .find( '[id]' ).removeAttr( 'id' )
                                .end()
                                .insertAfter( $historyCompareForm )
-                               .submit();
+                               .trigger( 'submit' );
 
                        e.preventDefault();
                        return false; // Because the submit is special, return false as well.
index 4b0e49b..e359416 100644 (file)
@@ -12,6 +12,7 @@
                                $a = $( '#ca-edit a' );
                                // Not every page has an edit link (T59713)
                                if ( $a.length ) {
+                                       // eslint-disable-next-line jquery/no-event-shorthand
                                        $a.get( 0 ).click();
                                }
                        }
index 68b5214..c5dcd36 100644 (file)
@@ -54,7 +54,7 @@
                }
 
                $popup = $( '<div>' ).addClass( 'postedit mw-notification' ).append( $content )
-                       .click( function () {
+                       .on( 'click', function () {
                                clearTimeout( timeoutId );
                                fadeOutConfirmation();
                        } );
index 59d1d4f..e3f96b1 100644 (file)
@@ -21,6 +21,7 @@
                if ( e.target.nodeName.toLowerCase() !== 'a' ) {
                        // Trigger native HTMLElement click instead of opening URL (T45052)
                        e.preventDefault();
+                       // eslint-disable-next-line jquery/no-event-shorthand
                        $edit.get( 0 ).click();
                }
        } );
index 8d4b2bc..e5d0574 100644 (file)
                                        }
                                } );
                                tokenPromise.done( function () {
-                                       $form.submit();
+                                       $form.trigger( 'submit' );
                                } );
                        } );
 
index a89293d..f0fbdb6 100644 (file)
                        $checkboxes.prop( 'checked', check );
                }
 
-               $( '.mw-checkbox-all' ).click( function () {
+               $( '.mw-checkbox-all' ).on( 'click', function () {
                        selectAll( true );
                } );
-               $( '.mw-checkbox-none' ).click( function () {
+               $( '.mw-checkbox-none' ).on( 'click', function () {
                        selectAll( false );
                } );
-               $( '.mw-checkbox-invert' ).click( function () {
+               $( '.mw-checkbox-invert' ).on( 'click', function () {
                        $checkboxes.prop( 'checked', function ( i, val ) {
                                return !val;
                        } );
index 32dbdc1..189363f 100644 (file)
@@ -55,7 +55,7 @@
                        hovzer.$.append( this.$container );
                        hovzer.update();
 
-                       $( '.mw-debug-panelink' ).click( this.switchPane );
+                       $( '.mw-debug-panelink' ).on( 'click', this.switchPane );
                },
 
                /**
@@ -90,6 +90,8 @@
 
                        // Hide the current pane
                        if ( requestedPaneId === currentPaneId ) {
+                               // FIXME: Use CSS transition
+                               // eslint-disable-next-line jquery/no-slide
                                $currentPane.slideUp( updateHov );
                                debug.$container.data( 'currentPane', null );
                                return;
                        debug.$container.data( 'currentPane', requestedPaneId );
 
                        if ( currentPaneId === undefined || currentPaneId === null ) {
+                               // FIXME: Use CSS transition
+                               // eslint-disable-next-line jquery/no-slide
                                $requestedPane.slideDown( updateHov );
                        } else {
                                $currentPane.hide();
index 572d830..81bf1dd 100644 (file)
@@ -62,6 +62,6 @@
 
                // Override toggle handler because we don't need it for this popup
                // object at all. Sort of nasty, but it gets the job done.
-               dialog.getPopup().toggle = $.noop;
+               dialog.getPopup().toggle = function () {};
        }
 }() );
index beecfea..661a1c4 100644 (file)
                        $errorBox = this.$errorBox;
 
                if ( errors.length === 0 ) {
+                       // FIXME: Use CSS transition
+                       // eslint-disable-next-line jquery/no-slide
                        $errorBox.slideUp( function () {
                                $errorBox
                                        .removeAttr( 'class' )
                                                .removeAttr( 'class' )
                                                .detach();
                                }
+                               // FIXME: Use CSS transition
+                               // eslint-disable-next-line jquery/no-slide
                                $errorBox
                                        .attr( 'class', 'error' )
                                        .empty()
                                        .slideDown();
                        };
                        if ( $oldErrorBox !== $errorBox && $oldErrorBox.hasClass( 'error' ) ) {
+                               // eslint-disable-next-line jquery/no-slide
                                $oldErrorBox.slideUp( showFunc );
                        } else {
                                showFunc();
index 6e33856..8ead7a4 100644 (file)
@@ -43,8 +43,8 @@
                                        deleteButton.$element.closest( 'li.mw-htmlform-cloner-li' ).remove();
                                } );
                        } else {
-                               $element.filter( ':input' ).click( function ( ev ) {
-                                       ev.preventDefault();
+                               $element.filter( ':input' ).on( 'click', function ( e ) {
+                                       e.preventDefault();
                                        $( this ).closest( 'li.mw-htmlform-cloner-li' ).remove();
                                } );
                        }
@@ -56,8 +56,8 @@
                                appendToCloner( createButton.$element );
                        } );
                } else {
-                       $createElement.filter( ':input' ).click( function ( ev ) {
-                               ev.preventDefault();
+                       $createElement.filter( ':input' ).on( 'click', function ( e ) {
+                               e.preventDefault();
 
                                appendToCloner( $( this ) );
                        } );
index ab11f85..a778902 100644 (file)
@@ -14,7 +14,7 @@
                        name: name,
                        multiple: 'multiple',
                        'data-placeholder': dataPlaceholder.plain(),
-                       'class': 'htmlform-chzn-select mw-input ' + oldClass
+                       class: 'htmlform-chzn-select mw-input ' + oldClass
                } );
                $oldContainer.find( 'input' ).each( function () {
                        var $oldInput = $( this ),
index 7a643a5..da90c14 100644 (file)
@@ -41,7 +41,7 @@
                                        // cache the current selection to avoid expensive lookup
                                        currentValReasonList = $reasonList.val();
 
-                                       $reasonList.change( function () {
+                                       $reasonList.on( 'change', function () {
                                                currentValReasonList = $reasonList.val();
                                        } );
 
index 540de23..fdc988b 100644 (file)
                 * @param {Array} nodes List of nodes
                 * @return {string} Other message
                 */
-               'int': function ( nodes ) {
+               int: function ( nodes ) {
                        var msg = textify( nodes[ 0 ] );
                        return mw.jqueryMsg.getMessageFunction()( msg.charAt( 0 ).toLowerCase() + msg.slice( 1 ) );
                },
index 2da5122..73978d9 100644 (file)
                        }
 
                        if ( mw.config.get( 'wgCascadeableLevels' ) !== undefined ) {
-                               $( 'form#mw-Protect-Form' ).submit( this.toggleUnchainedInputs.bind( ProtectionForm, true ) );
+                               $( 'form#mw-Protect-Form' ).on( 'submit', this.toggleUnchainedInputs.bind( ProtectionForm, true ) );
                        }
                        this.getExpirySelectors().each( function () {
-                               $( this ).change( ProtectionForm.updateExpiryList.bind( ProtectionForm, this ) );
+                               $( this ).on( 'change', ProtectionForm.updateExpiryList.bind( ProtectionForm, this ) );
                        } );
                        this.getExpiryInputs().each( function () {
                                $( this ).on( 'keyup change', ProtectionForm.updateExpiry.bind( ProtectionForm, this ) );
                        } );
                        this.getLevelSelectors().each( function () {
-                               $( this ).change( ProtectionForm.updateLevels.bind( ProtectionForm, this ) );
+                               $( this ).on( 'change', ProtectionForm.updateLevels.bind( ProtectionForm, this ) );
                        } );
 
                        $( '#mwProtectSet > tbody > tr:first' ).after( $row );
@@ -38,7 +38,7 @@
                                $cell.append(
                                        $( '<input>' )
                                                .attr( { id: 'mwProtectUnchained', type: 'checkbox' } )
-                                               .click( this.onChainClick.bind( this ) )
+                                               .on( 'click', this.onChainClick.bind( this ) )
                                                .prop( 'checked', !this.areAllTypesMatching() ),
                                        document.createTextNode( ' ' ),
                                        $( '<label>' )
index f72fc92..2765d07 100644 (file)
                 * @property {Object}
                 */
                autoHideSeconds: {
-                       'short': 5,
-                       'long': 30
+                       short: 5,
+                       long: 30
                },
 
                /**
index 3f35c4b..0ffc867 100644 (file)
                        if ( !bound ) {
                                bound = true;
                                $( window )
-                                       .resize( $.debounce( 300, true, handleResizeStart ) )
-                                       .resize( $.debounce( 300, handleResizeEnd ) );
+                                       .on( 'resize', $.debounce( 300, true, handleResizeStart ) )
+                                       .on( 'resize', $.debounce( 300, handleResizeEnd ) );
                        }
                } );
        } );
index 71e7f80..12009d1 100644 (file)
@@ -49,7 +49,7 @@
                        mw.hook( 'wikipage.categories' ).fire( $nodes );
                }
 
-               $( '#t-print a' ).click( function ( e ) {
+               $( '#t-print a' ).on( 'click', function ( e ) {
                        window.print();
                        e.preventDefault();
                } );
index f7fbeef..c56aada 100644 (file)
                        $links = $links.filter( ':not( #bodyContent *, #content * )' );
                }
 
-               $links.click( function ( e ) {
+               $links.on( 'click', function ( e ) {
                        var mwTitle, action, api, $link;
 
                        mwTitle = mw.Title.newFromText( title );
index 36575f7..5d51d10 100644 (file)
 
                // Collect views
                allViews = $.extend( true, {
-                       'default': {
+                       default: {
                                title: mw.msg( 'rcfilters-filterlist-title' ),
                                groups: filterGroups
                        }
index b01aa7a..adf3fbb 100644 (file)
                                                id,
                                                obj.label,
                                                normalizedData,
-                                               { 'default': isDefault }
+                                               { default: isDefault }
                                        )
                                ] );
 
                                randomID,
                                label,
                                normalizedData,
-                               { 'default': isDefault }
+                               { default: isDefault }
                        )
                ] );
 
index 7565a0d..1651432 100644 (file)
@@ -90,7 +90,7 @@
                                        name: 'namespace', // parameter name is singular
                                        type: 'string_options',
                                        title: mw.msg( 'namespaces' ),
-                                       labelPrefixKey: { 'default': 'rcfilters-tag-prefix-namespace', inverted: 'rcfilters-tag-prefix-namespace-inverted' },
+                                       labelPrefixKey: { default: 'rcfilters-tag-prefix-namespace', inverted: 'rcfilters-tag-prefix-namespace-inverted' },
                                        separator: ';',
                                        fullCoverage: true,
                                        filters: items
                                                hidden: true,
                                                filters: [ {
                                                        name: 'invert',
-                                                       'default': '0'
+                                                       default: '0'
                                                } ]
                                        } ]
                        };
                                                max: 1000
                                        },
                                        sortFunc: function ( a, b ) { return Number( a.name ) - Number( b.name ); },
-                                       'default': mw.user.options.get( this.limitPreferenceName, displayConfig.limitDefault ),
+                                       default: mw.user.options.get( this.limitPreferenceName, displayConfig.limitDefault ),
                                        sticky: true,
                                        filters: displayConfig.limitArray.map( function ( num ) {
                                                return controller._createFilterDataFromNumber( num, num );
                                                        ( Number( i ) * 24 ).toFixed( 2 ) :
                                                        Number( i );
                                        },
-                                       'default': mw.user.options.get( this.daysPreferenceName, displayConfig.daysDefault ),
+                                       default: mw.user.options.get( this.daysPreferenceName, displayConfig.daysDefault ),
                                        sticky: true,
                                        filters: [
                                                // Hours (1, 2, 6, 12)
                                        filters: [
                                                {
                                                        name: 'enhanced',
-                                                       'default': String( mw.user.options.get( 'usenewrc', 0 ) )
+                                                       default: String( mw.user.options.get( 'usenewrc', 0 ) )
                                                }
                                        ]
                                }
index 2adfa7d..f866aa4 100644 (file)
@@ -52,7 +52,7 @@
                                                        filters: [
                                                                {
                                                                        name: 'target',
-                                                                       'default': ''
+                                                                       default: ''
                                                                }
                                                        ]
                                                },
@@ -65,7 +65,7 @@
                                                        filters: [
                                                                {
                                                                        name: 'showlinkedto',
-                                                                       'default': false
+                                                                       default: false
                                                                }
                                                        ]
                                                }
index 3c3f261..b76078e 100644 (file)
                        $firstNew.after( $indicator );
                }
 
+               // FIXME: Use CSS transition
+               // eslint-disable-next-line jquery/no-fade
                $newChanges
                        .hide()
                        .fadeIn( 1000 );
index 5e5cd6e..d0cc117 100644 (file)
                        this.queriesModel.connect( this, {
                                itemUpdate: 'onSavedQueriesItemUpdate',
                                initialize: 'onSavedQueriesInitialize',
-                               'default': 'reevaluateResetRestoreState'
+                               default: 'reevaluateResetRestoreState'
                        } );
                }
 
                                )
                        )
                ) {
+                       // eslint-disable-next-line jquery/no-animate
                        $( container ).animate( {
                                scrollTop: newScrollTop
                        } );
index 034a1c9..b4ec781 100644 (file)
@@ -40,8 +40,8 @@
                this.menu = new mw.rcfilters.ui.GroupWidget( {
                        events: {
                                click: 'menuItemClick',
-                               'delete': 'menuItemDelete',
-                               'default': 'menuItemDefault',
+                               delete: 'menuItemDelete',
+                               default: 'menuItemDefault',
                                edit: 'menuItemEdit'
                        },
                        classes: [ 'mw-rcfilters-ui-savedLinksListWidget-menu' ],
index 3762ae7..83a39d4 100644 (file)
                        // OO.ui.ButtonWidget doesn't take focus itself (T128054)
                        $focus = $( '#mw-apisandbox-ui' ).find( document.activeElement );
                        if ( $focus.length ) {
+                               // eslint-disable-next-line jquery/no-event-shorthand
                                $focus[ 0 ].blur();
                        }
 
                // it makes it too hard to read and our "disabled"
                // isn't really disabled.
                widgetField.onFieldDisable( false );
-               widgetField.onFieldDisable = $.noop;
+               widgetField.onFieldDisable = function () {};
 
                widgetField.apiParamIndex = ppi.index;
 
                                widget = Util.createWidgetForParameter( {
                                        name: name,
                                        type: 'string',
-                                       'default': ''
+                                       default: ''
                                }, {
                                        nooptional: true
                                } );
                                }
 
                                that.deprecatedItemsFieldset = new OO.ui.FieldsetLayout().addItems( deprecatedItems ).toggle( false );
+                               // eslint-disable-next-line jquery/no-animate-toggle
                                tmp = $( '<fieldset>' )
                                        .toggle( !that.deprecatedItemsFieldset.isEmpty() )
                                        .append(
index ba34eb3..bc8ca37 100644 (file)
@@ -30,7 +30,7 @@
        $( function () {
                var $projectField = $( '#mw-import-table-interwiki #interwiki' );
                if ( $projectField.length ) {
-                       $projectField.change( updateImportSubprojectList );
+                       $projectField.on( 'change', updateImportSubprojectList );
                        updateImportSubprojectList();
                }
        } );
index d8f73b2..8885883 100644 (file)
@@ -27,7 +27,7 @@
 
                        // Bind to change event, and trigger once to set the initial state of the checkboxes.
                        rc.updateCheckboxes();
-                       $select.change( rc.updateCheckboxes );
+                       $select.on( 'change', rc.updateCheckboxes );
                }
        };
 
index 8e6d160..ae4cd55 100644 (file)
 
                for ( i = 0; i < results.length; i++ ) {
                        result = results[ i ];
-                       imageCaption = mw.html.element( 'span', { 'class': 'iw-result__mini-gallery__caption' }, result.title );
+                       imageCaption = mw.html.element( 'span', { class: 'iw-result__mini-gallery__caption' }, result.title );
                        imageThumbnailSrc = ( result.thumbnail ) ? result.thumbnail.source : '';
                        resultOutput += '<div class="iw-result__mini-gallery">' +
                                                /* escaping response content */
                                                mw.html.element( 'a', {
                                                        href: '/wiki/' + result.title,
-                                                       'class': 'iw-result__mini-gallery__image',
+                                                       class: 'iw-result__mini-gallery__image',
                                                        style: 'background-image: url(' + imageThumbnailSrc + ');'
                                                }, new mw.html.Raw( imageCaption ) ) +
                                        '</div>';
index d5760b2..02ac862 100644 (file)
@@ -7,15 +7,15 @@
 
                // Emulate HTML5 autofocus behavior in non HTML5 compliant browsers
                if ( !( 'autofocus' in document.createElement( 'input' ) ) ) {
-                       $( 'input[autofocus]' ).eq( 0 ).focus();
+                       $( 'input[autofocus]' ).eq( 0 ).trigger( 'focus' );
                }
 
                // Attach handler for check all/none buttons
                $checkboxes = $( '#powersearch input[id^=mw-search-ns]' );
-               $( '#mw-search-toggleall' ).click( function () {
+               $( '#mw-search-toggleall' ).on( 'click', function () {
                        $checkboxes.prop( 'checked', true );
                } );
-               $( '#mw-search-togglenone' ).click( function () {
+               $( '#mw-search-togglenone' ).on( 'click', function () {
                        $checkboxes.prop( 'checked', false );
                } );
 
@@ -39,7 +39,7 @@
                updateHeaderLinks( searchWidget.getValue() );
 
                // When saving settings, use the proper request method (POST instead of GET).
-               $( '#mw-search-powersearch-remember' ).change( function () {
+               $( '#mw-search-powersearch-remember' ).on( 'change', function () {
                        this.form.method = this.checked ? 'post' : 'get';
                } ).trigger( 'change' );
 
index e8eb870..92a5987 100644 (file)
@@ -7,7 +7,7 @@
                        summaryByteLimit = mw.config.get( 'wgCommentByteLimit' ),
                        wpComment = OO.ui.infuse( $( '#wpComment' ).closest( '.oo-ui-widget' ) );
 
-               $( '#mw-undelete-invert' ).click( function () {
+               $( '#mw-undelete-invert' ).on( 'click', function () {
                        $( '.mw-undelete-revlist input[type="checkbox"]' ).prop( 'checked', function ( i, val ) {
                                return !val;
                        } );
index 7783ff7..53617ca 100644 (file)
@@ -3,7 +3,7 @@
  */
 ( function () {
        $( function () {
-               $( 'a.mw-watch-link' ).click( function ( e ) {
+               $( 'a.mw-watch-link' ).on( 'click', function ( e ) {
                        var promise,
                                api = new mw.Api(),
                                $link = $( this ),
index bfb4c5e..8abb8f2 100644 (file)
                if ( ajaxUploadDestCheck ) {
                        // Insert an event handler that fetches upload warnings when wpDestFile
                        // has been changed
-                       $( '#wpDestFile' ).change( function () {
+                       $( '#wpDestFile' ).on( 'change', function () {
                                uploadWarning.checkNow( $( this ).val() );
                        } );
                        // Insert a row where the warnings will be displayed just below the
 
                if ( mw.config.get( 'wgAjaxLicensePreview' ) && $license.length ) {
                        // License selector check
-                       $license.change( function () {
+                       $license.on( 'change', function () {
                                // We might show a preview
                                uploadTemplatePreview.getPreview( $license, $( '#mw-license-preview' ) );
                        } );
 
                // fillDestFile setup
                mw.config.get( 'wgUploadSourceIds' ).forEach( function ( sourceId ) {
-                       $( '#' + sourceId ).change( function () {
+                       $( '#' + sourceId ).on( 'change', function () {
                                var path, slash, backslash, fname;
                                if ( !mw.config.get( 'wgUploadAutoFill' ) ) {
                                        return;
                /* Initialization */
                if ( hasFileAPI() ) {
                        // Update thumbnail when the file selection control is updated.
-                       $( '#wpUploadFile' ).change( function () {
+                       $( '#wpUploadFile' ).on( 'change', function () {
                                var file;
                                clearPreview();
                                if ( this.files && this.files.length ) {
                        namespace: 'uploadwarning'
                } );
 
-               $uploadForm.submit( function () {
+               $uploadForm.on( 'submit', function () {
                        allowCloseWindow.release();
                } );
        } );
 
                        // Change tabindex only when main div has focus
                        if ( $( this ).is( ':focus' ) ) {
-                               $( this ).find( 'a' ).first().focus();
+                               $( this ).find( 'a' ).first().trigger( 'focus' );
                                setEditTabindex( '0' );
                        }
                } );
index a4f5d1a..63d9623 100644 (file)
@@ -12,6 +12,7 @@
 
        // Dynamically show/hide the "other time" input under each dropdown
        $( '.mw-userrights-nested select' ).on( 'change', function ( e ) {
+               // eslint-disable-next-line jquery/no-animate-toggle
                $( e.target.parentNode ).find( 'input' ).toggle( $( e.target ).val() === 'other' );
        } );
 
index b9be51f..2d60b1d 100644 (file)
@@ -8,7 +8,7 @@
                // If the user wants to reset their watchlist, use an API call to do so (no reload required)
                // Adapted from a user script by User:NQ of English Wikipedia
                // (User:NQ/WatchlistResetConfirm.js)
-               $resetForm.submit( function ( event ) {
+               $resetForm.on( 'submit', function ( event ) {
                        var $button = $resetForm.find( 'input[name=mw-watchlist-reset-submit]' );
 
                        event.preventDefault();
@@ -41,7 +41,7 @@
                        } ).fail( function () {
                                // On error, fall back to server-side reset
                                // First remove this submit listener and then re-submit the form
-                               $resetForm.off( 'submit' ).submit();
+                               $resetForm.off( 'submit' ).trigger( 'submit' );
                        } );
                } );
 
@@ -50,7 +50,7 @@
                        // add a listener on all form elements in the header form
                        $( '#mw-watchlist-form input, #mw-watchlist-form select' ).on( 'change', function () {
                                // submit the form when one of the input fields is modified
-                               $( '#mw-watchlist-form' ).submit();
+                               $( '#mw-watchlist-form' ).trigger( 'submit' );
                        } );
                }
 
 
                                event.preventDefault();
                                event.stopPropagation();
-                               $unwatchLink.blur();
+                               $unwatchLink.trigger( 'blur' );
                        } );
                }
        } );
index de81d6a..85dbf97 100644 (file)
                        // Hide/show the table of contents element
                        function toggleToc() {
                                if ( $tocList.is( ':hidden' ) ) {
+                                       // FIXME: Use CSS transitions
+                                       // eslint-disable-next-line jquery/no-slide
                                        $tocList.slideDown( 'fast' );
                                        $tocToggleLink.text( mw.msg( 'hidetoc' ) );
                                        $this.removeClass( 'tochidden' );
                                        mw.cookie.set( 'hidetoc', null );
                                } else {
+                                       // eslint-disable-next-line jquery/no-slide
                                        $tocList.slideUp( 'fast' );
                                        $tocToggleLink.text( mw.msg( 'showtoc' ) );
                                        $this.addClass( 'tochidden' );
index 545bf40..f4ad578 100644 (file)
                                                );
                                        }
                                        if ( $field.is( ':input' ) ) {
-                                               $field.select();
+                                               $field.trigger( 'select' );
                                        }
                                        return false;
                        }
                                this.setValue( this.formatter.getDefaultDate() );
                        }
                        if ( $field.is( ':input' ) ) {
-                               $field.select();
+                               $field.trigger( 'select' );
                        }
 
                        if ( this.calendar ) {
index 86103d1..58646a9 100644 (file)
@@ -48,7 +48,7 @@
         *  are set up. Note: The promise must have an .abort() functionality.
         */
        mw.widgets.APIResultsQueue.prototype.setup = function () {
-               return $.Deferred().resolve().promise( { abort: $.noop } );
+               return $.Deferred().resolve().promise( { abort: function () {} } );
        };
 
        /**
index 23ee2c0..3002d45 100644 (file)
                        provider = this;
 
                if ( !this.isValid() ) {
-                       return $.Deferred().reject().promise( { abort: $.noop } );
+                       return $.Deferred().reject().promise( { abort: function () {} } );
                }
 
                api = this.isLocal ? new mw.Api() : new mw.ForeignApi( this.getAPIurl(), { anonymous: true } );
index c7502da..4d58f18 100644 (file)
                                if ( e.which === OO.ui.Keys.TAB ) {
                                        if ( e.shiftKey ) {
                                                // Tabbing backward from text input: normal browser behavior
-                                               $.noop();
                                        } else {
                                                // Tabbing forward from text input: just focus the calendar
                                                this.calendar.$element.focus();
index 76c6718..f1c0c6f 100644 (file)
                        self = this;
 
                // reuse the searchSuggest function from mw.searchSuggest
-               promise = mw.searchSuggest.request( api, this.getQueryValue(), $.noop, this.limit, this.getNamespace() );
+               promise = mw.searchSuggest.request( api, this.getQueryValue(), function () {}, this.limit, this.getNamespace() );
 
                // tracking purposes
                promise.done( function ( data, jqXHR ) {
index 11b9c01..cb5e1f8 100644 (file)
@@ -1,5 +1,6 @@
 <?php
 
+use MediaWiki\Block\Restriction\PageRestriction;
 use MediaWiki\MediaWikiServices;
 
 /**
@@ -892,7 +893,7 @@ class TitlePermissionTest extends MediaWikiLangTestCase {
                        'wgEmailAuthentication' => true,
                ] );
 
-               $this->setUserPerm( [ "createpage", "move" ] );
+               $this->setUserPerm( [ 'createpage', 'edit', 'move', 'rollback', 'patrol', 'upload', 'purge' ] );
                $this->setTitle( NS_HELP, "test page" );
 
                # $wgEmailConfirmToEdit only applies to 'edit' action
@@ -964,11 +965,24 @@ class TitlePermissionTest extends MediaWikiLangTestCase {
                        'expiry' => 10,
                        'systemBlock' => 'test',
                ] );
-               $this->assertEquals( [ [ 'systemblockedtext',
+
+               $errors = [ [ 'systemblockedtext',
                                '[[User:Useruser|Useruser]]', 'no reason given', '127.0.0.1',
                                'Useruser', 'test', '23:00, 31 December 1969', '127.0.8.1',
-                               $wgLang->timeanddate( wfTimestamp( TS_MW, $now ), true ) ] ],
+                               $wgLang->timeanddate( wfTimestamp( TS_MW, $now ), true ) ] ];
+
+               $this->assertEquals( $errors,
+                       $this->title->getUserPermissionsErrors( 'edit', $this->user ) );
+               $this->assertEquals( $errors,
                        $this->title->getUserPermissionsErrors( 'move-target', $this->user ) );
+               $this->assertEquals( $errors,
+                       $this->title->getUserPermissionsErrors( 'rollback', $this->user ) );
+               $this->assertEquals( $errors,
+                       $this->title->getUserPermissionsErrors( 'patrol', $this->user ) );
+               $this->assertEquals( $errors,
+                       $this->title->getUserPermissionsErrors( 'upload', $this->user ) );
+               $this->assertEquals( [],
+                       $this->title->getUserPermissionsErrors( 'purge', $this->user ) );
 
                // partial block message test
                $this->user->mBlockedby = $this->user->getName();
@@ -981,10 +995,39 @@ class TitlePermissionTest extends MediaWikiLangTestCase {
                        'expiry' => 10,
                ] );
 
-               $this->assertEquals( [ [ 'blockedtext-partial',
+               $this->assertEquals( [],
+                       $this->title->getUserPermissionsErrors( 'edit', $this->user ) );
+               $this->assertEquals( [],
+                       $this->title->getUserPermissionsErrors( 'move-target', $this->user ) );
+               $this->assertEquals( [],
+                       $this->title->getUserPermissionsErrors( 'rollback', $this->user ) );
+               $this->assertEquals( [],
+                       $this->title->getUserPermissionsErrors( 'patrol', $this->user ) );
+               $this->assertEquals( [],
+                       $this->title->getUserPermissionsErrors( 'upload', $this->user ) );
+               $this->assertEquals( [],
+                       $this->title->getUserPermissionsErrors( 'purge', $this->user ) );
+
+               $this->user->mBlock->setRestrictions( [
+                               ( new PageRestriction( 0, $this->title->getArticleID() ) )->setTitle( $this->title ),
+               ] );
+
+               $errors = [ [ 'blockedtext-partial',
                                '[[User:Useruser|Useruser]]', 'no reason given', '127.0.0.1',
                                'Useruser', null, '23:00, 31 December 1969', '127.0.8.1',
-                               $wgLang->timeanddate( wfTimestamp( TS_MW, $now ), true ) ] ],
+                               $wgLang->timeanddate( wfTimestamp( TS_MW, $now ), true ) ] ];
+
+               $this->assertEquals( $errors,
+                       $this->title->getUserPermissionsErrors( 'edit', $this->user ) );
+               $this->assertEquals( $errors,
                        $this->title->getUserPermissionsErrors( 'move-target', $this->user ) );
+               $this->assertEquals( $errors,
+                       $this->title->getUserPermissionsErrors( 'rollback', $this->user ) );
+               $this->assertEquals( $errors,
+                       $this->title->getUserPermissionsErrors( 'patrol', $this->user ) );
+               $this->assertEquals( [],
+                       $this->title->getUserPermissionsErrors( 'upload', $this->user ) );
+               $this->assertEquals( [],
+                       $this->title->getUserPermissionsErrors( 'purge', $this->user ) );
        }
 }
index 5f1b9fe..e67d405 100644 (file)
@@ -88,7 +88,7 @@ class AbstractPasswordPrimaryAuthenticationProviderTest extends \MediaWikiTestCa
 
        public function testCheckPasswordValidity() {
                $uppCalled = 0;
-               $uppStatus = \Status::newGood();
+               $uppStatus = \Status::newGood( [] );
                $this->setMwGlobals( [
                        'wgPasswordPolicy' => [
                                'policies' => [
index b7e86c8..e1b25a1 100644 (file)
@@ -174,6 +174,16 @@ class LocalPasswordPrimaryAuthenticationProviderTest extends \MediaWikiTestCase
                $this->assertNotNull( $ret );
                $this->assertSame( 'resetpass-validity-soft', $ret->msg->getKey() );
                $this->assertFalse( $ret->hard );
+
+               $this->manager->removeAuthenticationSessionData( null );
+               $row->user_password_expires = null;
+               $status = \Status::newGood( [ 'forceChange' => true ] );
+               $status->error( 'testing' );
+               $providerPriv->setPasswordResetFlag( $userName, $status, $row );
+               $ret = $this->manager->getAuthenticationSessionData( 'reset-pass' );
+               $this->assertNotNull( $ret );
+               $this->assertSame( 'resetpass-validity', $ret->msg->getKey() );
+               $this->assertTrue( $ret->hard );
        }
 
        public function testAuthentication() {
index 78175fa..80881ad 100644 (file)
@@ -30,7 +30,7 @@ class UserPasswordPolicyTest extends MediaWikiTestCase {
 
        protected $policies = [
                'checkuser' => [
-                       'MinimalPasswordLength' => 10,
+                       'MinimalPasswordLength' => [ 'value' => 10, 'forceChange' => true ],
                        'MinimumPasswordLengthToLogin' => 6,
                        'PasswordCannotMatchUsername' => true,
                ],
@@ -44,6 +44,8 @@ class UserPasswordPolicyTest extends MediaWikiTestCase {
                        'MinimumPasswordLengthToLogin' => 1,
                        'PasswordCannotMatchBlacklist' => true,
                        'MaximalPasswordLength' => 4096,
+                       // test null handling
+                       'PasswordCannotMatchUsername' => null,
                ],
        ];
 
@@ -62,15 +64,24 @@ class UserPasswordPolicyTest extends MediaWikiTestCase {
        public function testGetPoliciesForUser() {
                $upp = $this->getUserPasswordPolicy();
 
-               $user = User::newFromName( 'TestUserPolicy' );
-               $user->addToDatabase();
-               $user->addGroup( 'sysop' );
-
+               $user = $this->getTestUser( [ 'sysop' ] )->getUser();
                $this->assertArrayEquals(
                        [
                                'MinimalPasswordLength' => 8,
                                'MinimumPasswordLengthToLogin' => 1,
-                               'PasswordCannotMatchUsername' => 1,
+                               'PasswordCannotMatchUsername' => true,
+                               'PasswordCannotMatchBlacklist' => true,
+                               'MaximalPasswordLength' => 4096,
+                       ],
+                       $upp->getPoliciesForUser( $user )
+               );
+
+               $user = $this->getTestUser( [ 'sysop', 'checkuser' ] )->getUser();
+               $this->assertArrayEquals(
+                       [
+                               'MinimalPasswordLength' => [ 'value' => 10, 'forceChange' => true ],
+                               'MinimumPasswordLengthToLogin' => 6,
+                               'PasswordCannotMatchUsername' => true,
                                'PasswordCannotMatchBlacklist' => true,
                                'MaximalPasswordLength' => 4096,
                        ],
@@ -87,7 +98,7 @@ class UserPasswordPolicyTest extends MediaWikiTestCase {
 
                $this->assertArrayEquals(
                        [
-                               'MinimalPasswordLength' => 10,
+                               'MinimalPasswordLength' => [ 'value' => 10, 'forceChange' => true ],
                                'MinimumPasswordLengthToLogin' => 6,
                                'PasswordCannotMatchUsername' => true,
                                'PasswordCannotMatchBlacklist' => true,
@@ -100,108 +111,96 @@ class UserPasswordPolicyTest extends MediaWikiTestCase {
        /**
         * @dataProvider provideCheckUserPassword
         */
-       public function testCheckUserPassword( $username, $groups, $password, $valid, $ok, $msg ) {
+       public function testCheckUserPassword( $groups, $password, StatusValue $expectedStatus ) {
                $upp = $this->getUserPasswordPolicy();
-
-               $user = User::newFromName( $username );
-               $user->addToDatabase();
-               foreach ( $groups as $group ) {
-                       $user->addGroup( $group );
-               }
+               $user = $this->getTestUser( $groups )->getUser();
 
                $status = $upp->checkUserPassword( $user, $password );
-               $this->assertSame( $valid, $status->isGood(), $msg . ' - password valid' );
-               $this->assertSame( $ok, $status->isOK(), $msg . ' - can login' );
+               $this->assertSame( $expectedStatus->isGood(), $status->isGood(), 'password valid' );
+               $this->assertSame( $expectedStatus->isOK(), $status->isOK(), 'can login' );
+               $this->assertSame( $expectedStatus->getValue(), $status->getValue(), 'flags' );
        }
 
        public function provideCheckUserPassword() {
+               $success = Status::newGood( [] );
+               $warning = Status::newGood( [] );
+               $forceChange = Status::newGood( [ 'forceChange' => true ] );
+               $fatal = Status::newGood( [] );
+               // the message does not matter, we only test for state and value
+               $warning->warning( 'invalid-password' );
+               $forceChange->warning( 'invalid-password' );
+               $warning->warning( 'invalid-password' );
+               $fatal->fatal( 'invalid-password' );
                return [
-                       [
-                               'PassPolicyUser',
+                       'No groups, default policy, password too short to login' => [
                                [],
                                '',
-                               false,
-                               false,
-                               'No groups, default policy, password too short to login'
+                               $fatal,
                        ],
-                       [
-                               'PassPolicyUser',
+                       'Default policy, short password' => [
                                [ 'user' ],
                                'aaa',
-                               false,
-                               true,
-                               'Default policy, short password'
+                               $warning,
                        ],
-                       [
-                               'PassPolicyUser',
+                       'Sysop with good password' => [
                                [ 'sysop' ],
                                'abcdabcdabcd',
-                               true,
-                               true,
-                               'Sysop with good password'
+                               $success,
                        ],
-                       [
-                               'PassPolicyUser',
+                       'Sysop with short password' => [
                                [ 'sysop' ],
                                'abcd',
-                               false,
-                               true,
-                               'Sysop with short password'
+                               $warning,
                        ],
-                       [
-                               'PassPolicyUser',
+                       'Checkuser with short password' => [
                                [ 'sysop', 'checkuser' ],
                                'abcdabcd',
-                               false,
-                               true,
-                               'Checkuser with short password'
+                               $forceChange,
                        ],
-                       [
-                               'PassPolicyUser',
+                       'Checkuser with too short password to login' => [
                                [ 'sysop', 'checkuser' ],
                                'abcd',
-                               false,
-                               false,
-                               'Checkuser with too short password to login'
-                       ],
-                       [
-                               'Useruser',
-                               [ 'user' ],
-                               'Passpass',
-                               false,
-                               true,
-                               'Username & password on blacklist'
+                               $fatal,
                        ],
                ];
        }
 
+       public function testCheckUserPassword_blacklist() {
+               $upp = $this->getUserPasswordPolicy();
+               $user = User::newFromName( 'Useruser' );
+               $user->addToDatabase();
+
+               $status = $upp->checkUserPassword( $user, 'Passpass' );
+               $this->assertFalse( $status->isGood(), 'password invalid' );
+               $this->assertTrue( $status->isOK(), 'can login' );
+       }
+
        /**
         * @dataProvider provideMaxOfPolicies
         */
-       public function testMaxOfPolicies( $p1, $p2, $max, $msg ) {
+       public function testMaxOfPolicies( $p1, $p2, $max ) {
                $this->assertArrayEquals(
                        $max,
-                       UserPasswordPolicy::maxOfPolicies( $p1, $p2 ),
-                       $msg
+                       UserPasswordPolicy::maxOfPolicies( $p1, $p2 )
                );
        }
 
        public function provideMaxOfPolicies() {
                return [
-                       [
+                       'Basic max in p1' => [
                                [ 'MinimalPasswordLength' => 8 ], // p1
                                [ 'MinimalPasswordLength' => 2 ], // p2
                                [ 'MinimalPasswordLength' => 8 ], // max
-                               'Basic max in p1'
                        ],
-                       [
+                       'Basic max in p2' => [
                                [ 'MinimalPasswordLength' => 2 ], // p1
                                [ 'MinimalPasswordLength' => 8 ], // p2
                                [ 'MinimalPasswordLength' => 8 ], // max
-                               'Basic max in p2'
                        ],
-                       [
-                               [ 'MinimalPasswordLength' => 8 ], // p1
+                       'Missing items in p1' => [
+                               [
+                                       'MinimalPasswordLength' => 8,
+                               ], // p1
                                [
                                        'MinimalPasswordLength' => 2,
                                        'PasswordCannotMatchUsername' => 1,
@@ -210,9 +209,8 @@ class UserPasswordPolicyTest extends MediaWikiTestCase {
                                        'MinimalPasswordLength' => 8,
                                        'PasswordCannotMatchUsername' => 1,
                                ], // max
-                               'Missing items in p1'
                        ],
-                       [
+                       'Missing items in p2' => [
                                [
                                        'MinimalPasswordLength' => 8,
                                        'PasswordCannotMatchUsername' => 1,
@@ -224,7 +222,64 @@ class UserPasswordPolicyTest extends MediaWikiTestCase {
                                        'MinimalPasswordLength' => 8,
                                        'PasswordCannotMatchUsername' => 1,
                                ], // max
-                               'Missing items in p2'
+                       ],
+                       'complex value in p1' => [
+                               [
+                                       'MinimalPasswordLength' => [
+                                               'value' => 8,
+                                               'foo' => 1,
+                                       ],
+                               ], // p1
+                               [
+                                       'MinimalPasswordLength' => 2,
+                               ], // p2
+                               [
+                                       'MinimalPasswordLength' => [
+                                               'value' => 8,
+                                               'foo' => 1,
+                                       ],
+                               ], // max
+                       ],
+                       'complex value in p2' => [
+                               [
+                                       'MinimalPasswordLength' => 8,
+                               ], // p1
+                               [
+                                       'MinimalPasswordLength' => [
+                                               'value' => 2,
+                                               'foo' => 1,
+                                       ],
+                               ], // p2
+                               [
+                                       'MinimalPasswordLength' => [
+                                               'value' => 8,
+                                               'foo' => 1,
+                                       ],
+                               ], // max
+                       ],
+                       'complex value in both p1 and p2' => [
+                               [
+                                       'MinimalPasswordLength' => [
+                                               'value' => 8,
+                                               'foo' => 1,
+                                               'baz' => false,
+                                       ],
+                               ], // p1
+                               [
+                                       'MinimalPasswordLength' => [
+                                               'value' => 2,
+                                               'bar' => 2,
+                                               'baz' => true,
+                                       ],
+                               ], // p2
+                               [
+                                       'MinimalPasswordLength' => [
+                                               'value' => 8,
+                                               'foo' => 1,
+                                               'bar' => 2,
+                                               'baz' => true,
+                                       ],
+                               ], // max
                        ],
                ];
        }
index a80262e..58c69e3 100644 (file)
@@ -143,8 +143,8 @@ class UploadBaseTest extends MediaWikiTestCase {
                        // html5sec SVG vectors
                        [
                                '<svg xmlns="http://www.w3.org/2000/svg"><script>alert(1)</script></svg>',
-                               true,
-                               true,
+                               true, /* SVG is well formed */
+                               true, /* Evil SVG detected */
                                'Script tag in svg (http://html5sec.org/#47)'
                        ],
                        [
@@ -509,7 +509,20 @@ class UploadBaseTest extends MediaWikiTestCase {
                                true,
                                false,
                                'DTD with aliased entities apos (Should be allowed)'
-                       ]
+                       ],
+                       [
+                               '<svg xmlns="http://www.w3.org/2000/svg"><g filter="url( \'#foo\' )"></g></svg>',
+                               true,
+                               false,
+                               'SVG with local filter (T69044)'
+                       ],
+                       [
+                               '<svg xmlns="http://www.w3.org/2000/svg"><g filter="url( http://example.com/#foo )"></g></svg>',
+                               true,
+                               true,
+                               'SVG with non-local filter (T69044)'
+                       ],
+
                ];
                // phpcs:enable
        }
index 47f04b2..03b02ba 100644 (file)
@@ -7,10 +7,11 @@
                "sinon": false
        },
        "rules": {
-               "operator-linebreak": 0,
+               "operator-linebreak": "off",
                "quote-props": [ "error", "as-needed" ],
-               "valid-jsdoc": 0,
-               "qunit/require-expect": 0,
-               "qunit/resolve-async": 0
+               "valid-jsdoc": "off",
+               "qunit/require-expect": "off",
+               "qunit/resolve-async": "off",
+               "jquery/no-parse-html-literal": "off"
        }
 }
index 743660a..df3d61b 100644 (file)
                        if ( warn === undefined ) {
                                warn = mw.log.warn;
                                error = mw.log.error;
-                               mw.log.warn = mw.log.error = $.noop;
+                               mw.log.warn = mw.log.error = function () {};
                        }
                }
 
index d207260..2e35420 100644 (file)
@@ -5,6 +5,7 @@
                var done = assert.async(),
                        $canvas = $( '<div>' ).css( 'background-color', '#fff' ).appendTo( '#qunit-fixture' );
 
+               // eslint-disable-next-line jquery/no-animate
                $canvas.animate( { 'background-color': '#000' }, 3 ).promise()
                        .done( function () {
                                var endColors = $.colorUtil.getRGB( $canvas.css( 'background-color' ) );
index 360ef01..bd6ee16 100644 (file)
                                callback( $table );
                        } else {
                                $table.tablesorter();
-                               $table.find( '#sortme' ).click();
+                               $table.find( '#sortme' ).trigger( 'click' );
                        }
 
                        // Table sorting is done synchronously; if it ever needs to change back
                planetsAscName,
                function ( $table ) {
                        $table.tablesorter();
-                       $table.find( '.headerSort:eq(0)' ).click();
+                       $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
                }
        );
        tableTest(
                planetsAscName,
                function ( $table ) {
                        $table.tablesorter();
-                       $table.find( '.headerSort:eq(0)' ).click();
+                       $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
                }
        );
        tableTest(
                planetsAscName,
                function ( $table ) {
                        $table.tablesorter();
-                       $table.find( '.headerSort:eq(0)' ).click();
-                       $table.find( '.headerSort:eq(1)' ).click();
-                       $table.find( '.headerSort:eq(0)' ).click();
+                       $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
+                       $table.find( '.headerSort:eq(1)' ).trigger( 'click' );
+                       $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
                }
        );
        tableTest(
                reversed( planetsAscName ),
                function ( $table ) {
                        $table.tablesorter();
-                       $table.find( '.headerSort:eq(0)' ).click().click();
+                       $table.find( '.headerSort:eq(0)' ).trigger( 'click' ).trigger( 'click' );
                }
        );
        tableTest(
                planetsAscRadius,
                function ( $table ) {
                        $table.tablesorter();
-                       $table.find( '.headerSort:eq(1)' ).click();
+                       $table.find( '.headerSort:eq(1)' ).trigger( 'click' );
                }
        );
        tableTest(
                reversed( planetsAscRadius ),
                function ( $table ) {
                        $table.tablesorter();
-                       $table.find( '.headerSort:eq(1)' ).click().click();
+                       $table.find( '.headerSort:eq(1)' ).trigger( 'click' ).trigger( 'click' );
                }
        );
        tableTest(
                        $table.tablesorter(
                                { sortList: [ { 0: 'asc' }, { 1: 'asc' } ] }
                        );
-                       $table.find( '.headerSort:eq(0)' ).click();
+                       $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
                }
        );
        tableTest(
                        $table.tablesorter(
                                { sortList: [ { 0: 'desc' }, { 1: 'desc' } ] }
                        );
-                       $table.find( '.headerSort:eq(0)' ).click();
+                       $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
 
                        // Pretend to click while pressing the multi-sort key
                        event = $.Event( 'click' );
                        $table.find( 'tr:eq(0) th:eq(0)' ).attr( 'colspan', '3' );
 
                        $table.tablesorter();
-                       $table.find( '.headerSort:eq(0)' ).click();
+                       $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
                }
        );
        tableTest( 'Sorting with colspanned headers: sort spanned column twice',
                        $table.find( 'tr:eq(0) th:eq(0)' ).attr( 'colspan', '3' );
 
                        $table.tablesorter();
-                       $table.find( '.headerSort:eq(0)' ).click();
-                       $table.find( '.headerSort:eq(0)' ).click();
+                       $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
+                       $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
                }
        );
        tableTest( 'Sorting with colspanned headers: subsequent column',
                        $table.find( 'tr:eq(0) th:eq(0)' ).attr( 'colspan', '3' );
 
                        $table.tablesorter();
-                       $table.find( '.headerSort:eq(1)' ).click();
+                       $table.find( '.headerSort:eq(1)' ).trigger( 'click' );
                }
        );
        tableTest( 'Sorting with colspanned headers: sort subsequent column twice',
                        $table.find( 'tr:eq(0) th:eq(0)' ).attr( 'colspan', '3' );
 
                        $table.tablesorter();
-                       $table.find( '.headerSort:eq(1)' ).click();
-                       $table.find( '.headerSort:eq(1)' ).click();
+                       $table.find( '.headerSort:eq(1)' ).trigger( 'click' );
+                       $table.find( '.headerSort:eq(1)' ).trigger( 'click' );
                }
        );
 
                $table.find( 'tr:eq(0) > th:eq(0)' ).addClass( 'unsortable' );
 
                $table.tablesorter();
-               $table.find( 'tr:eq(0) > th:eq(0)' ).click();
+               $table.find( 'tr:eq(0) > th:eq(0)' ).trigger( 'click' );
 
                assert.deepEqual(
                        tableExtract( $table ),
                );
 
                $cell = $table.find( 'tr:eq(0) > th:eq(0)' );
-               $table.find( 'tr:eq(0) > th:eq(1)' ).click();
+               $table.find( 'tr:eq(0) > th:eq(1)' ).trigger( 'click' );
 
                assert.strictEqual(
                        $cell.hasClass( 'headerSortUp' ) || $cell.hasClass( 'headerSortDown' ),
                        mw.config.set( 'wgPageContentLanguage', 'de' );
 
                        $table.tablesorter();
-                       $table.find( '.headerSort:eq(0)' ).click();
+                       $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
                }
        );
 
                        mw.config.set( 'wgDefaultDateFormat', 'mdy' );
 
                        $table.tablesorter();
-                       $table.find( '.headerSort:eq(0)' ).click();
+                       $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
                }
        );
 
                ipv4Sorted,
                function ( $table ) {
                        $table.tablesorter();
-                       $table.find( '.headerSort:eq(0)' ).click();
+                       $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
                }
        );
        tableTest(
                reversed( ipv4Sorted ),
                function ( $table ) {
                        $table.tablesorter();
-                       $table.find( '.headerSort:eq(0)' ).click().click();
+                       $table.find( '.headerSort:eq(0)' ).trigger( 'click' ).trigger( 'click' );
                }
        );
 
                        } );
 
                        $table.tablesorter();
-                       $table.find( '.headerSort:eq(0)' ).click();
+                       $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
                }
        );
 
                        } );
 
                        $table.tablesorter();
-                       $table.find( '.headerSort:eq(0)' ).click();
+                       $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
                }
        );
 
                        $table.find( 'tr:eq(2) td:eq(1)' ).attr( 'rowspan', '3' );
 
                        $table.tablesorter();
-                       $table.find( '.headerSort:eq(0)' ).click();
+                       $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
                }
        );
        tableTest(
                        $table.find( 'tr:eq(2) td:eq(0)' ).attr( 'rowspan', '3' );
 
                        $table.tablesorter();
-                       $table.find( '.headerSort:eq(0)' ).click();
+                       $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
                }
        );
 
                        mw.config.set( 'wgDefaultDateFormat', 'mdy' );
 
                        $table.tablesorter();
-                       $table.find( '.headerSort:eq(0)' ).click();
+                       $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
                }
        );
 
                currencySorted,
                function ( $table ) {
                        $table.tablesorter();
-                       $table.find( '.headerSort:eq(0)' ).click();
+                       $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
                }
        );
 
                function ( $table ) {
                        $table.find( 'tr:last' ).addClass( 'sortbottom' );
                        $table.tablesorter();
-                       $table.find( '.headerSort:eq(0)' ).click();
+                       $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
                }
        );
 
                                '</table>'
                );
                $table.tablesorter();
-               $table.find( '.headerSort:eq(0)' ).click();
+               $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
 
                assert.strictEqual(
                        $table.data( 'tablesorter' ).config.parsers[ 0 ].id,
                                '<tr><td data-sort-value="Cherry">Dolphin</td></tr>' +
                                '</tbody></table>'
                );
-               $table.tablesorter().find( '.headerSort:eq(0)' ).click();
+               $table.tablesorter().find( '.headerSort:eq(0)' ).trigger( 'click' );
 
                data = [];
                $table.find( 'tbody > tr' ).each( function ( i, tr ) {
                                '<tr><td><span data-sort-value="D">H</span></td></tr>' +
                                '</tbody></table>'
                );
-               $table.tablesorter().find( '.headerSort:eq(0)' ).click();
+               $table.tablesorter().find( '.headerSort:eq(0)' ).trigger( 'click' );
 
                data = [];
                $table.find( 'tbody > tr' ).each( function ( i, tr ) {
                // initialize table sorter and sort once
                $table
                        .tablesorter()
-                       .find( '.headerSort:eq(0)' ).click();
+                       .find( '.headerSort:eq(0)' ).trigger( 'click' );
 
                // Change the sortValue data properties (T40152)
                // - change data
                $table.find( 'td:contains(G)' ).removeData( 'sortValue' );
 
                // Now sort again (twice, so it is back at Ascending)
-               $table.find( '.headerSort:eq(0)' ).click();
-               $table.find( '.headerSort:eq(0)' ).click();
+               $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
+               $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
 
                data = [];
                $table.find( 'tbody > tr' ).each( function ( i, tr ) {
                [ 'Numbers' ], numbers, numbersAsc,
                function ( $table ) {
                        $table.tablesorter();
-                       $table.find( '.headerSort:eq(0)' ).click();
+                       $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
                }
        );
 
                [ 'Numbers' ], numbers, reversed( numbersAsc ),
                function ( $table ) {
                        $table.tablesorter();
-                       $table.find( '.headerSort:eq(0)' ).click().click();
+                       $table.find( '.headerSort:eq(0)' ).trigger( 'click' ).trigger( 'click' );
                }
        );
        // TODO add numbers sorting tests for T10115 with a different language
                        mw.config.set( 'wgDefaultDateFormat', 'mdy' );
 
                        $table.tablesorter();
-                       $table.find( '.headerSort:eq(0)' ).click();
+                       $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
                }
        );
 
                        mw.config.set( 'wgDefaultDateFormat', 'dmy' );
 
                        $table.tablesorter();
-                       $table.find( '.headerSort:eq(0)' ).click();
+                       $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
                }
        );
 
                        mw.config.set( 'wgDefaultDateFormat', 'dmy' );
 
                        $table.tablesorter();
-                       $table.find( '.headerSort:eq(0)' ).click();
+                       $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
                }
        );
 
                                '<tr><td>1</td></tr>' +
                                '</table>'
                );
-               $table.tablesorter().find( '.headerSort:eq(0)' ).click();
+               $table.tablesorter().find( '.headerSort:eq(0)' ).trigger( 'click' );
 
                assert.strictEqual(
                        $table.find( 'td' ).first().text(),
                                '<tr><td><img alt="A" />C</tr>' +
                                '</table>'
                );
-               $table.tablesorter().find( '.headerSort:eq(0)' ).click();
+               $table.tablesorter().find( '.headerSort:eq(0)' ).trigger( 'click' );
 
                assert.strictEqual(
                        $table.find( 'td' ).text(),
                                '<tr><td>4</td></tr>' +
                                '</table>'
                );
-               $table.tablesorter().find( '.headerSort:eq(0)' ).click();
+               $table.tablesorter().find( '.headerSort:eq(0)' ).trigger( 'click' );
 
                assert.strictEqual(
                        $table.find( 'td' ).text(),
                                '</table>'
                );
                $table.tablesorter();
-               $table.find( '.headerSort:eq(0)' ).click();
+               $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
                // now the first row have 2 columns
-               $table.find( '.headerSort:eq(1)' ).click();
+               $table.find( '.headerSort:eq(1)' ).trigger( 'click' );
 
                parsers = $table.data( 'tablesorter' ).config.parsers;
 
                                '</table>'
                );
                $table.tablesorter();
-               $table.find( '.headerSort:eq(0)' ).click();
+               $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
 
                assert.deepEqual(
                        tableExtract( $table ),
                                '</table>'
                );
                $table.tablesorter();
-               $table.find( '.headerSort:eq(0)' ).click();
+               $table.find( '.headerSort:eq(0)' ).trigger( 'click' );
 
                assert.deepEqual(
                        tableExtract( $table ),
index 54fdffc..458df92 100644 (file)
@@ -43,7 +43,7 @@
                $options = $( '#namespace' ).find( 'option' );
                $options.eq( 0 ).removeProp( 'selected' );
                $options.eq( 1 ).prop( 'selected', true );
-               $( '#namespace' ).change();
+               $( '#namespace' ).trigger( 'change' );
 
                // ... and checkboxes should be enabled again
                assert.strictEqual( $( '#nsinvert' ).prop( 'disabled' ), false );
@@ -52,7 +52,7 @@
                // select first option ( 'all' namespace)...
                $options.eq( 1 ).removeProp( 'selected' );
                $options.eq( 0 ).prop( 'selected', true );
-               $( '#namespace' ).change();
+               $( '#namespace' ).trigger( 'change' );
 
                // ... and checkboxes should now be disabled
                assert.strictEqual( $( '#nsinvert' ).prop( 'disabled' ), true );
index b9a64b5..74fd743 100644 (file)
 
                assert.strictEqual( $tocList.is( ':hidden' ), false, 'The table of contents is now visible' );
 
-               $toggleLink.click();
+               $toggleLink.trigger( 'click' );
                return $tocList.promise().then( function () {
                        assert.strictEqual( $tocList.is( ':hidden' ), true, 'The table of contents is now hidden' );
 
-                       $toggleLink.click();
+                       $toggleLink.trigger( 'click' );
                        return $tocList.promise();
                } );
        } );