Merge "build: Update qunitjs to 2.9.1"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Tue, 8 Jan 2019 19:00:37 +0000 (19:00 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Tue, 8 Jan 2019 19:00:37 +0000 (19:00 +0000)
81 files changed:
.eslintrc.json
.mailmap
CREDITS
RELEASE-NOTES-1.33
includes/Block.php
includes/DefaultSettings.php
includes/Revision/SlotRecord.php
includes/Revision/SlotRoleRegistry.php
includes/Title.php
includes/block/BlockRestriction.php
includes/block/Restriction/AbstractRestriction.php
includes/block/Restriction/PageRestriction.php
includes/block/Restriction/Restriction.php
includes/changes/ChangesList.php
includes/htmlform/HTMLSelectLanguageField.php
includes/libs/mime/IEContentAnalyzer.php
includes/upload/UploadBase.php
includes/user/User.php
includes/utils/ZipDirectoryReader.php
includes/widget/CheckMatrixWidget.php
languages/i18n/en.json
languages/i18n/qqq.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/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 aaa05b1..c0dd84f 100644 (file)
@@ -197,6 +197,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 9a42419..9df4ab6 100644 (file)
@@ -9020,8 +9020,8 @@ $wgActorTableSchemaMigrationStage = SCHEMA_COMPAT_OLD;
  * Flag to enable Partial Blocks. This allows an admin to prevent a user from editing specific pages
  * or namespaces.
  *
- * @since 1.32
- * @deprecated 1.32
+ * @since 1.33
+ * @deprecated 1.33
  * @var bool
  */
 $wgEnablePartialBlocks = false;
index 89980f4..064f7a4 100644 (file)
@@ -219,8 +219,6 @@ class SlotRecord {
        }
 
        /**
-        * SlotRecord constructor.
-        *
         * The following fields are supported by the $row parameter:
         *
         *   $row->blob_data
index b108b98..41ea4a6 100644 (file)
@@ -63,8 +63,6 @@ class SlotRoleRegistry {
        private $handlers;
 
        /**
-        * SlotRoleRegistry constructor.
-        *
         * @param NameTableStore $roleNamesStore
         */
        public function __construct( NameTableStore $roleNamesStore ) {
index d1f2fd9..3496668 100644 (file)
@@ -2691,14 +2691,34 @@ class Title implements LinkTarget, IDBAccessObject {
                }
 
                $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;
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 a39568b..6a8bd6d 100644 (file)
@@ -58,8 +58,6 @@ class ChangesList extends ContextSource {
        protected $filterGroups;
 
        /**
-        * Changeslist constructor
-        *
         * @param Skin|IContextSource $obj
         * @param array $filterGroups Array of ChangesListFilterGroup objects (currently optional)
         */
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 802ed2d..4ae3cf9 100644 (file)
@@ -311,7 +311,6 @@ class IEContentAnalyzer {
         */
        protected $typeTable = [];
 
-       /** constructor */
        function __construct() {
                // Construct versioned type arrays from the base type array plus additions
                $types = $this->baseTypeTable;
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 a585a53..65fc4b4 100644 (file)
@@ -4545,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 46f3bbc..46f1aaa 100644 (file)
@@ -127,7 +127,6 @@ class ZipDirectoryReader {
        const GENERAL_CD_ENCRYPTED = 13;
 
        /**
-        * Private constructor
         * @param string $fileName
         * @param callable $callback
         * @param array $options
index 797a81f..06d8095 100644 (file)
@@ -19,8 +19,6 @@ class CheckMatrixWidget extends \OOUI\Widget {
        protected $forcedOff = [];
 
        /**
-        * CheckMatrixWidget constructor
-        *
         * Operates similarly to MultiSelectWidget, but instead of using an array of
         * options, uses an array of rows and an array of columns to dynamically
         * construct a matrix of options. The tags used to identify a particular cell
index 543b3e9..9a65ead 100644 (file)
        "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 1cad3c4..9e38c01 100644 (file)
        "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 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 c597921..03be1e5 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 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();
                } );
        } );