From: Ed Sanders Date: Tue, 15 Nov 2016 21:02:04 +0000 (+0000) Subject: build: Replace jscs+jshint with eslint X-Git-Tag: 1.31.0-rc.0~4852^2~1 X-Git-Url: https://git.cyclocoop.org/%7B%24www_url%7Dadmin/compta/banques/ajouter.php?a=commitdiff_plain;h=c0fb8a883633f110a8083a164672e8334714d450;p=lhc%2Fweb%2Fwiklou.git build: Replace jscs+jshint with eslint Change-Id: Id0a23c03aabadfaf2ec705528ae4b3bd0908fa3e --- diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000000..135d3b95f3 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,19 @@ +{ + "extends": "wikimedia", + "env": { + "browser": true, + "jquery": true, + "qunit": true + }, + "globals": { + "require": false, + "module": false, + "mediaWiki": false, + "mwPerformance": false, + "OO": false + }, + "rules": { + "dot-notation": 0, + "valid-jsdoc": 0 + } +} diff --git a/.jscsrc b/.jscsrc deleted file mode 100644 index 3f7e90d209..0000000000 --- a/.jscsrc +++ /dev/null @@ -1,36 +0,0 @@ -{ - "preset": "wikimedia", - "es3": true, - - "requireVarDeclFirst": null, - - "requireDotNotation": { "allExcept": [ "keywords" ] }, - "jsDoc": { - "checkAnnotations": { - "preset": "jsduck5", - "extra": { - "context": "some", - "see": "some" - } - }, - "checkParamNames": true, - "checkRedundantAccess": true, - "checkRedundantReturns": true, - "checkTypes": "strictNativeCase", - "requireNewlineAfterDescription": true, - "requireParamTypes": true, - "requireReturnTypes": true - }, - - "excludeFiles": [ - "docs/**", - "extensions/**", - "node_modules/**", - "resources/lib/**", - "resources/src/jquery.tipsy/**", - "resources/src/jquery/jquery.farbtastic.js", - "resources/src/mediawiki.libs/**", - "skins/**", - "vendor/**" - ] -} diff --git a/.jshintignore b/.jshintignore deleted file mode 100644 index fdde7d054d..0000000000 --- a/.jshintignore +++ /dev/null @@ -1,12 +0,0 @@ -# Generated documentation -docs/** - -# third-party libs -extensions/** -node_modules/** -resources/lib/** -resources/src/jquery.tipsy/** -resources/src/jquery/jquery.farbtastic.js -resources/src/mediawiki.libs/** -skins/** -vendor/** diff --git a/.jshintrc b/.jshintrc deleted file mode 100644 index 518d5cbec1..0000000000 --- a/.jshintrc +++ /dev/null @@ -1,34 +0,0 @@ -{ - // Enforcing - "bitwise": true, - "eqeqeq": true, - "esversion": 3, - "freeze": true, - "futurehostile": true, - "latedef": "nofunc", - "noarg": true, - "nonew": true, - "strict": false, - "undef": true, - "unused": true, - - // Relaxing - "laxbreak": true, - "multistr": true, - "-W024": false, - - // Environment - "browser": true, - - "globals": { - "require": false, - "module": false, - "mediaWiki": true, - "JSON": true, - "OO": true, - "mwPerformance": true, - "jQuery": false, - "QUnit": false, - "sinon": false - } -} diff --git a/Gruntfile.js b/Gruntfile.js index a08db5c780..b38bc9adfc 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,33 +1,44 @@ -/*jshint node:true */ +/* eslint-env node */ + module.exports = function ( grunt ) { - grunt.loadNpmTasks( 'grunt-contrib-copy' ); - grunt.loadNpmTasks( 'grunt-contrib-jshint' ); - grunt.loadNpmTasks( 'grunt-stylelint' ); - grunt.loadNpmTasks( 'grunt-contrib-watch' ); - grunt.loadNpmTasks( 'grunt-banana-checker' ); - grunt.loadNpmTasks( 'grunt-jscs' ); - grunt.loadNpmTasks( 'grunt-jsonlint' ); - grunt.loadNpmTasks( 'grunt-karma' ); var wgServer = process.env.MW_SERVER, wgScriptPath = process.env.MW_SCRIPT_PATH, karmaProxy = {}; + grunt.loadNpmTasks( 'grunt-banana-checker' ); + grunt.loadNpmTasks( 'grunt-contrib-copy' ); + grunt.loadNpmTasks( 'grunt-contrib-watch' ); + grunt.loadNpmTasks( 'grunt-eslint' ); + grunt.loadNpmTasks( 'grunt-jsonlint' ); + grunt.loadNpmTasks( 'grunt-karma' ); + grunt.loadNpmTasks( 'grunt-stylelint' ); + karmaProxy[ wgScriptPath ] = wgServer + wgScriptPath; grunt.initConfig( { - jshint: { - options: { - jshintrc: true - }, - all: '.' - }, - jscs: { - all: '.' + eslint: { + all: [ + '**/*.js', + '!docs/**', + '!tests/**', + '!extensions/**', + '!node_modules/**', + '!resources/lib/**', + '!resources/src/jquery.tipsy/**', + '!resources/src/jquery/jquery.farbtastic.js', + '!resources/src/mediawiki.libs/**', + '!skins/**', + '!vendor/**', + // Skip functions aren't even parseable + '!resources/src/dom-level2-skip.js', + '!resources/src/es5-skip.js', + '!resources/src/json-skip.js', + '!resources/src/mediawiki.hidpi-skip.js' + ] }, jsonlint: { all: [ - '.jscsrc', '**/*.json', '!{docs/js,extensions,node_modules,skins,vendor}/**' ] @@ -48,7 +59,7 @@ module.exports = function ( grunt ) { }, watch: { files: [ - '.{stylelintrc,jscsrc,jshintignore,jshintrc}', + '.{stylelintrc,eslintrc.json}', '**/*', '!{docs,extensions,node_modules,skins,vendor}/**' ], @@ -103,7 +114,7 @@ module.exports = function ( grunt ) { return !!( process.env.MW_SERVER && process.env.MW_SCRIPT_PATH ); } ); - grunt.registerTask( 'lint', [ 'jshint', 'jscs', 'jsonlint', 'banana', 'stylelint' ] ); + grunt.registerTask( 'lint', [ 'eslint', 'banana', 'stylelint' ] ); grunt.registerTask( 'qunit', [ 'assert-mw-env', 'karma:main' ] ); grunt.registerTask( 'test', [ 'lint' ] ); diff --git a/package.json b/package.json index 3b75c3acb1..efc6c9fcd4 100644 --- a/package.json +++ b/package.json @@ -6,12 +6,12 @@ "postdoc": "grunt copy:jsduck" }, "devDependencies": { + "eslint-config-wikimedia": "0.2.0", "grunt": "1.0.1", "grunt-banana-checker": "0.5.0", "grunt-contrib-copy": "1.0.0", - "grunt-contrib-jshint": "1.0.0", "grunt-contrib-watch": "1.0.0", - "grunt-jscs": "2.8.0", + "grunt-eslint": "19.0.0", "grunt-jsonlint": "1.0.7", "grunt-karma": "2.0.0", "grunt-stylelint": "0.6.0", diff --git a/resources/src/jquery/jquery.accessKeyLabel.js b/resources/src/jquery/jquery.accessKeyLabel.js index a6106e4562..63b599b07a 100644 --- a/resources/src/jquery/jquery.accessKeyLabel.js +++ b/resources/src/jquery/jquery.accessKeyLabel.js @@ -5,217 +5,223 @@ */ ( function ( $, mw ) { -// Cached access key modifiers for used browser -var cachedAccessKeyModifiers, - - // Whether to use 'test-' instead of correct prefix (used for testing) - useTestPrefix = false, - - // tag names which can have a label tag - // https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Content_categories#Form-associated_content - labelable = 'button, input, textarea, keygen, meter, output, progress, select'; - -/** - * Find the modifier keys that need to be pressed together with the accesskey to trigger the input. - * - * The result is dependant on the ua paramater or the current platform. - * For browsers that support accessKeyLabel, #getAccessKeyLabel never calls here. - * Valid key values that are returned can be: ctrl, alt, option, shift, esc - * - * @private - * @param {Object} [ua] An object with a 'userAgent' and 'platform' property. - * @return {Array} Array with 0 or more of the string values: ctrl, option, alt, shift, esc - */ -function getAccessKeyModifiers( ua ) { - // use cached prefix if possible - if ( !ua && cachedAccessKeyModifiers ) { - return cachedAccessKeyModifiers; - } + // Cached access key modifiers for used browser + var cachedAccessKeyModifiers, + + // Whether to use 'test-' instead of correct prefix (used for testing) + useTestPrefix = false, + + // tag names which can have a label tag + // https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Content_categories#Form-associated_content + labelable = 'button, input, textarea, keygen, meter, output, progress, select'; + + /** + * Find the modifier keys that need to be pressed together with the accesskey to trigger the input. + * + * The result is dependant on the ua paramater or the current platform. + * For browsers that support accessKeyLabel, #getAccessKeyLabel never calls here. + * Valid key values that are returned can be: ctrl, alt, option, shift, esc + * + * @private + * @param {Object} [ua] An object with a 'userAgent' and 'platform' property. + * @return {Array} Array with 0 or more of the string values: ctrl, option, alt, shift, esc + */ + function getAccessKeyModifiers( ua ) { + var profile, accessKeyModifiers; + + // use cached prefix if possible + if ( !ua && cachedAccessKeyModifiers ) { + return cachedAccessKeyModifiers; + } - var profile = $.client.profile( ua ), + profile = $.client.profile( ua ); accessKeyModifiers = [ 'alt' ]; - // Classic Opera on any platform - if ( profile.name === 'opera' && profile.versionNumber < 15 ) { - accessKeyModifiers = [ 'shift', 'esc' ]; - - // Chrome and modern Opera on any platform - } else if ( profile.name === 'chrome' || profile.name === 'opera' ) { - accessKeyModifiers = ( - profile.platform === 'mac' - // Chrome on Mac - ? [ 'ctrl', 'option' ] - // Chrome on Windows or Linux - // (both alt- and alt-shift work, but alt with E, D, F etc does not - // work since they are browser shortcuts) - : [ 'alt', 'shift' ] - ); - - // Non-Windows Safari with webkit_version > 526 - } else if ( profile.platform !== 'win' - && profile.name === 'safari' - && profile.layoutVersion > 526 - ) { - accessKeyModifiers = [ 'ctrl', 'alt' ]; - - // Safari/Konqueror on any platform, or any browser on Mac - // (but not Safari on Windows) - } else if ( !( profile.platform === 'win' && profile.name === 'safari' ) - && ( profile.name === 'safari' - || profile.platform === 'mac' - || profile.name === 'konqueror' ) - ) { - accessKeyModifiers = [ 'ctrl' ]; - - // Firefox/Iceweasel 2.x and later - } else if ( ( profile.name === 'firefox' || profile.name === 'iceweasel' ) - && profile.versionBase > '1' - ) { - accessKeyModifiers = [ 'alt', 'shift' ]; - } + // Classic Opera on any platform + if ( profile.name === 'opera' && profile.versionNumber < 15 ) { + accessKeyModifiers = [ 'shift', 'esc' ]; + + // Chrome and modern Opera on any platform + } else if ( profile.name === 'chrome' || profile.name === 'opera' ) { + accessKeyModifiers = ( + profile.platform === 'mac' ? + // Chrome on Mac + [ 'ctrl', 'option' ] : + // Chrome on Windows or Linux + // (both alt- and alt-shift work, but alt with E, D, F etc does not + // work since they are browser shortcuts) + [ 'alt', 'shift' ] + ); + + // Non-Windows Safari with webkit_version > 526 + } else if ( profile.platform !== 'win' && + profile.name === 'safari' && + profile.layoutVersion > 526 + ) { + accessKeyModifiers = [ 'ctrl', 'alt' ]; + + // Safari/Konqueror on any platform, or any browser on Mac + // (but not Safari on Windows) + } else if ( + !( profile.platform === 'win' && profile.name === 'safari' ) && + ( + profile.name === 'safari' || + profile.platform === 'mac' || + profile.name === 'konqueror' + ) + ) { + accessKeyModifiers = [ 'ctrl' ]; + + // Firefox/Iceweasel 2.x and later + } else if ( + ( profile.name === 'firefox' || profile.name === 'iceweasel' ) && + profile.versionBase > '1' + ) { + accessKeyModifiers = [ 'alt', 'shift' ]; + } - // cache modifiers - if ( !ua ) { - cachedAccessKeyModifiers = accessKeyModifiers; + // cache modifiers + if ( !ua ) { + cachedAccessKeyModifiers = accessKeyModifiers; + } + return accessKeyModifiers; } - return accessKeyModifiers; -} -/** - * Get the access key label for an element. - * - * Will use native accessKeyLabel if available (currently only in Firefox 8+), - * falls back to #getAccessKeyModifiers. - * - * @private - * @param {HTMLElement} element Element to get the label for - * @return {string} Access key label - */ -function getAccessKeyLabel( element ) { - // abort early if no access key - if ( !element.accessKey ) { - return ''; - } - // use accessKeyLabel if possible - // https://html.spec.whatwg.org/multipage/interaction.html#dom-accesskeylabel - if ( !useTestPrefix && element.accessKeyLabel ) { - return element.accessKeyLabel; + /** + * Get the access key label for an element. + * + * Will use native accessKeyLabel if available (currently only in Firefox 8+), + * falls back to #getAccessKeyModifiers. + * + * @private + * @param {HTMLElement} element Element to get the label for + * @return {string} Access key label + */ + function getAccessKeyLabel( element ) { + // abort early if no access key + if ( !element.accessKey ) { + return ''; + } + // use accessKeyLabel if possible + // https://html.spec.whatwg.org/multipage/interaction.html#dom-accesskeylabel + if ( !useTestPrefix && element.accessKeyLabel ) { + return element.accessKeyLabel; + } + return ( useTestPrefix ? 'test' : getAccessKeyModifiers().join( '-' ) ) + '-' + element.accessKey; } - return ( useTestPrefix ? 'test' : getAccessKeyModifiers().join( '-' ) ) + '-' + element.accessKey; -} - -/** - * Update the title for an element (on the element with the access key or it's label) to show - * the correct access key label. - * - * @private - * @param {HTMLElement} element Element with the accesskey - * @param {HTMLElement} titleElement Element with the title to update (may be the same as `element`) - */ -function updateTooltipOnElement( element, titleElement ) { - var oldTitle, parts, regexp, newTitle, accessKeyLabel; - oldTitle = titleElement.title; - if ( !oldTitle ) { - // don't add a title if the element didn't have one before - return; - } + /** + * Update the title for an element (on the element with the access key or it's label) to show + * the correct access key label. + * + * @private + * @param {HTMLElement} element Element with the accesskey + * @param {HTMLElement} titleElement Element with the title to update (may be the same as `element`) + */ + function updateTooltipOnElement( element, titleElement ) { + var oldTitle, parts, regexp, newTitle, accessKeyLabel; + + oldTitle = titleElement.title; + if ( !oldTitle ) { + // don't add a title if the element didn't have one before + return; + } - parts = ( mw.msg( 'word-separator' ) + mw.msg( 'brackets' ) ).split( '$1' ); - regexp = new RegExp( $.map( parts, mw.RegExp.escape ).join( '.*?' ) + '$' ); - newTitle = oldTitle.replace( regexp, '' ); - accessKeyLabel = getAccessKeyLabel( element ); + parts = ( mw.msg( 'word-separator' ) + mw.msg( 'brackets' ) ).split( '$1' ); + regexp = new RegExp( $.map( parts, mw.RegExp.escape ).join( '.*?' ) + '$' ); + newTitle = oldTitle.replace( regexp, '' ); + accessKeyLabel = getAccessKeyLabel( element ); - if ( accessKeyLabel ) { - // Should be build the same as in Linker::titleAttrib - newTitle += mw.msg( 'word-separator' ) + mw.msg( 'brackets', accessKeyLabel ); - } - if ( oldTitle !== newTitle ) { - titleElement.title = newTitle; + if ( accessKeyLabel ) { + // Should be build the same as in Linker::titleAttrib + newTitle += mw.msg( 'word-separator' ) + mw.msg( 'brackets', accessKeyLabel ); + } + if ( oldTitle !== newTitle ) { + titleElement.title = newTitle; + } } -} -/** - * Update the title for an element to show the correct access key label. - * - * @private - * @param {HTMLElement} element Element with the accesskey - */ -function updateTooltip( element ) { - var id, $element, $label, $labelParent; - updateTooltipOnElement( element, element ); - - // update associated label if there is one - $element = $( element ); - if ( $element.is( labelable ) ) { - // Search it using 'for' attribute - id = element.id.replace( /"/g, '\\"' ); - if ( id ) { - $label = $( 'label[for="' + id + '"]' ); - if ( $label.length === 1 ) { - updateTooltipOnElement( element, $label[ 0 ] ); + /** + * Update the title for an element to show the correct access key label. + * + * @private + * @param {HTMLElement} element Element with the accesskey + */ + function updateTooltip( element ) { + var id, $element, $label, $labelParent; + updateTooltipOnElement( element, element ); + + // update associated label if there is one + $element = $( element ); + if ( $element.is( labelable ) ) { + // Search it using 'for' attribute + id = element.id.replace( /"/g, '\\"' ); + if ( id ) { + $label = $( 'label[for="' + id + '"]' ); + if ( $label.length === 1 ) { + updateTooltipOnElement( element, $label[ 0 ] ); + } } - } - // Search it as parent, because the form control can also be inside the label element itself - $labelParent = $element.parents( 'label' ); - if ( $labelParent.length === 1 ) { - updateTooltipOnElement( element, $labelParent[ 0 ] ); + // Search it as parent, because the form control can also be inside the label element itself + $labelParent = $element.parents( 'label' ); + if ( $labelParent.length === 1 ) { + updateTooltipOnElement( element, $labelParent[ 0 ] ); + } } } -} - -/** - * Update the titles for all elements in a jQuery selection. - * - * @return {jQuery} - * @chainable - */ -$.fn.updateTooltipAccessKeys = function () { - return this.each( function () { - updateTooltip( this ); - } ); -}; -/** - * getAccessKeyModifiers - * - * @method updateTooltipAccessKeys_getAccessKeyModifiers - * @inheritdoc #getAccessKeyModifiers - */ -$.fn.updateTooltipAccessKeys.getAccessKeyModifiers = getAccessKeyModifiers; - -/** - * getAccessKeyLabel - * - * @method updateTooltipAccessKeys_getAccessKeyLabel - * @inheritdoc #getAccessKeyLabel - */ -$.fn.updateTooltipAccessKeys.getAccessKeyLabel = getAccessKeyLabel; - -/** - * getAccessKeyPrefix - * - * @method updateTooltipAccessKeys_getAccessKeyPrefix - * @deprecated since 1.27 Use #getAccessKeyModifiers - */ -$.fn.updateTooltipAccessKeys.getAccessKeyPrefix = function ( ua ) { - return getAccessKeyModifiers( ua ).join( '-' ) + '-'; -}; - -/** - * Switch test mode on and off. - * - * @method updateTooltipAccessKeys_setTestMode - * @param {boolean} mode New mode - */ -$.fn.updateTooltipAccessKeys.setTestMode = function ( mode ) { - useTestPrefix = mode; -}; - -/** - * @class jQuery - * @mixins jQuery.plugin.accessKeyLabel - */ + /** + * Update the titles for all elements in a jQuery selection. + * + * @return {jQuery} + * @chainable + */ + $.fn.updateTooltipAccessKeys = function () { + return this.each( function () { + updateTooltip( this ); + } ); + }; + + /** + * getAccessKeyModifiers + * + * @method updateTooltipAccessKeys_getAccessKeyModifiers + * @inheritdoc #getAccessKeyModifiers + */ + $.fn.updateTooltipAccessKeys.getAccessKeyModifiers = getAccessKeyModifiers; + + /** + * getAccessKeyLabel + * + * @method updateTooltipAccessKeys_getAccessKeyLabel + * @inheritdoc #getAccessKeyLabel + */ + $.fn.updateTooltipAccessKeys.getAccessKeyLabel = getAccessKeyLabel; + + /** + * getAccessKeyPrefix + * + * @method updateTooltipAccessKeys_getAccessKeyPrefix + * @deprecated since 1.27 Use #getAccessKeyModifiers + */ + $.fn.updateTooltipAccessKeys.getAccessKeyPrefix = function ( ua ) { + return getAccessKeyModifiers( ua ).join( '-' ) + '-'; + }; + + /** + * Switch test mode on and off. + * + * @method updateTooltipAccessKeys_setTestMode + * @param {boolean} mode New mode + */ + $.fn.updateTooltipAccessKeys.setTestMode = function ( mode ) { + useTestPrefix = mode; + }; + + /** + * @class jQuery + * @mixins jQuery.plugin.accessKeyLabel + */ }( jQuery, mediaWiki ) ); diff --git a/resources/src/jquery/jquery.autoEllipsis.js b/resources/src/jquery/jquery.autoEllipsis.js index fd7e8d1e39..8716b69405 100644 --- a/resources/src/jquery/jquery.autoEllipsis.js +++ b/resources/src/jquery/jquery.autoEllipsis.js @@ -3,169 +3,169 @@ */ ( function ( $ ) { -var - // Cache ellipsed substrings for every string-width-position combination - cache = {}, + var + // Cache ellipsed substrings for every string-width-position combination + cache = {}, - // Use a separate cache when match highlighting is enabled - matchTextCache = {}; + // Use a separate cache when match highlighting is enabled + matchTextCache = {}; -// Due to -// jscs:disable jsDoc -/** - * Automatically truncate the plain text contents of an element and add an ellipsis - * - * @param {Object} options - * @param {'left'|'center'|'right'} [options.position='center'] Where to remove text. - * @param {boolean} [options.tooltip=false] Whether to show a tooltip with the remainder - * of the text. - * @param {boolean} [options.restoreText=false] Whether to save the text for restoring - * later. - * @param {boolean} [options.hasSpan=false] Whether the element is already a container, - * or if the library should create a new container for it. - * @param {string|null} [options.matchText=null] Text to highlight, e.g. search terms. - * @return {jQuery} - * @chainable - */ -$.fn.autoEllipsis = function ( options ) { - options = $.extend( { - position: 'center', - tooltip: false, - restoreText: false, - hasSpan: false, - matchText: null - }, options ); + // Due to + // jscs:disable jsDoc + /** + * Automatically truncate the plain text contents of an element and add an ellipsis + * + * @param {Object} options + * @param {'left'|'center'|'right'} [options.position='center'] Where to remove text. + * @param {boolean} [options.tooltip=false] Whether to show a tooltip with the remainder + * of the text. + * @param {boolean} [options.restoreText=false] Whether to save the text for restoring + * later. + * @param {boolean} [options.hasSpan=false] Whether the element is already a container, + * or if the library should create a new container for it. + * @param {string|null} [options.matchText=null] Text to highlight, e.g. search terms. + * @return {jQuery} + * @chainable + */ + $.fn.autoEllipsis = function ( options ) { + options = $.extend( { + position: 'center', + tooltip: false, + restoreText: false, + hasSpan: false, + matchText: null + }, options ); - return this.each( function () { - var $trimmableText, - text, trimmableText, w, pw, - l, r, i, side, m, - // container element - used for measuring against - $container = $( this ); + return this.each( function () { + var $trimmableText, + text, trimmableText, w, pw, + l, r, i, side, m, + // container element - used for measuring against + $container = $( this ); - if ( options.restoreText ) { - if ( !$container.data( 'autoEllipsis.originalText' ) ) { - $container.data( 'autoEllipsis.originalText', $container.text() ); - } else { - $container.text( $container.data( 'autoEllipsis.originalText' ) ); + if ( options.restoreText ) { + if ( !$container.data( 'autoEllipsis.originalText' ) ) { + $container.data( 'autoEllipsis.originalText', $container.text() ); + } else { + $container.text( $container.data( 'autoEllipsis.originalText' ) ); + } } - } - // trimmable text element - only the text within this element will be trimmed - if ( options.hasSpan ) { - $trimmableText = $container.children( options.selector ); - } else { - $trimmableText = $( '' ) - .css( 'whiteSpace', 'nowrap' ) - .text( $container.text() ); - $container - .empty() - .append( $trimmableText ); - } + // trimmable text element - only the text within this element will be trimmed + if ( options.hasSpan ) { + $trimmableText = $container.children( options.selector ); + } else { + $trimmableText = $( '' ) + .css( 'whiteSpace', 'nowrap' ) + .text( $container.text() ); + $container + .empty() + .append( $trimmableText ); + } - text = $container.text(); - trimmableText = $trimmableText.text(); - w = $container.width(); - pw = 0; + text = $container.text(); + trimmableText = $trimmableText.text(); + w = $container.width(); + pw = 0; - // Try cache - if ( options.matchText ) { - if ( !( text in matchTextCache ) ) { - matchTextCache[ text ] = {}; - } - if ( !( options.matchText in matchTextCache[ text ] ) ) { - matchTextCache[ text ][ options.matchText ] = {}; - } - if ( !( w in matchTextCache[ text ][ options.matchText ] ) ) { - matchTextCache[ text ][ options.matchText ][ w ] = {}; - } - if ( options.position in matchTextCache[ text ][ options.matchText ][ w ] ) { - $container.html( matchTextCache[ text ][ options.matchText ][ w ][ options.position ] ); - if ( options.tooltip ) { - $container.attr( 'title', text ); + // Try cache + if ( options.matchText ) { + if ( !( text in matchTextCache ) ) { + matchTextCache[ text ] = {}; } - return; - } - } else { - if ( !( text in cache ) ) { - cache[ text ] = {}; - } - if ( !( w in cache[ text ] ) ) { - cache[ text ][ w ] = {}; - } - if ( options.position in cache[ text ][ w ] ) { - $container.html( cache[ text ][ w ][ options.position ] ); - if ( options.tooltip ) { - $container.attr( 'title', text ); + if ( !( options.matchText in matchTextCache[ text ] ) ) { + matchTextCache[ text ][ options.matchText ] = {}; + } + if ( !( w in matchTextCache[ text ][ options.matchText ] ) ) { + matchTextCache[ text ][ options.matchText ][ w ] = {}; + } + if ( options.position in matchTextCache[ text ][ options.matchText ][ w ] ) { + $container.html( matchTextCache[ text ][ options.matchText ][ w ][ options.position ] ); + if ( options.tooltip ) { + $container.attr( 'title', text ); + } + return; + } + } else { + if ( !( text in cache ) ) { + cache[ text ] = {}; + } + if ( !( w in cache[ text ] ) ) { + cache[ text ][ w ] = {}; + } + if ( options.position in cache[ text ][ w ] ) { + $container.html( cache[ text ][ w ][ options.position ] ); + if ( options.tooltip ) { + $container.attr( 'title', text ); + } + return; } - return; } - } - if ( $trimmableText.width() + pw > w ) { - switch ( options.position ) { - case 'right': - // Use binary search-like technique for efficiency - l = 0; - r = trimmableText.length; - do { - m = Math.ceil( ( l + r ) / 2 ); - $trimmableText.text( trimmableText.slice( 0, m ) + '...' ); - if ( $trimmableText.width() + pw > w ) { - // Text is too long - r = m - 1; - } else { - l = m; + if ( $trimmableText.width() + pw > w ) { + switch ( options.position ) { + case 'right': + // Use binary search-like technique for efficiency + l = 0; + r = trimmableText.length; + do { + m = Math.ceil( ( l + r ) / 2 ); + $trimmableText.text( trimmableText.slice( 0, m ) + '...' ); + if ( $trimmableText.width() + pw > w ) { + // Text is too long + r = m - 1; + } else { + l = m; + } + } while ( l < r ); + $trimmableText.text( trimmableText.slice( 0, l ) + '...' ); + break; + case 'center': + // TODO: Use binary search like for 'right' + i = [ Math.round( trimmableText.length / 2 ), Math.round( trimmableText.length / 2 ) ]; + // Begin with making the end shorter + side = 1; + while ( $trimmableText.outerWidth() + pw > w && i[ 0 ] > 0 ) { + $trimmableText.text( trimmableText.slice( 0, i[ 0 ] ) + '...' + trimmableText.slice( i[ 1 ] ) ); + // Alternate between trimming the end and begining + if ( side === 0 ) { + // Make the begining shorter + i[ 0 ]--; + side = 1; + } else { + // Make the end shorter + i[ 1 ]++; + side = 0; + } } - } while ( l < r ); - $trimmableText.text( trimmableText.slice( 0, l ) + '...' ); - break; - case 'center': - // TODO: Use binary search like for 'right' - i = [ Math.round( trimmableText.length / 2 ), Math.round( trimmableText.length / 2 ) ]; - // Begin with making the end shorter - side = 1; - while ( $trimmableText.outerWidth() + pw > w && i[ 0 ] > 0 ) { - $trimmableText.text( trimmableText.slice( 0, i[ 0 ] ) + '...' + trimmableText.slice( i[ 1 ] ) ); - // Alternate between trimming the end and begining - if ( side === 0 ) { - // Make the begining shorter - i[ 0 ]--; - side = 1; - } else { - // Make the end shorter - i[ 1 ]++; - side = 0; + break; + case 'left': + // TODO: Use binary search like for 'right' + r = 0; + while ( $trimmableText.outerWidth() + pw > w && r < trimmableText.length ) { + $trimmableText.text( '...' + trimmableText.slice( r ) ); + r++; } - } - break; - case 'left': - // TODO: Use binary search like for 'right' - r = 0; - while ( $trimmableText.outerWidth() + pw > w && r < trimmableText.length ) { - $trimmableText.text( '...' + trimmableText.slice( r ) ); - r++; - } - break; + break; + } + } + if ( options.tooltip ) { + $container.attr( 'title', text ); + } + if ( options.matchText ) { + $container.highlightText( options.matchText ); + matchTextCache[ text ][ options.matchText ][ w ][ options.position ] = $container.html(); + } else { + cache[ text ][ w ][ options.position ] = $container.html(); } - } - if ( options.tooltip ) { - $container.attr( 'title', text ); - } - if ( options.matchText ) { - $container.highlightText( options.matchText ); - matchTextCache[ text ][ options.matchText ][ w ][ options.position ] = $container.html(); - } else { - cache[ text ][ w ][ options.position ] = $container.html(); - } - } ); -}; -// jscs:enable jsDoc + } ); + }; + // jscs:enable jsDoc -/** - * @class jQuery - * @mixins jQuery.plugin.autoEllipsis - */ + /** + * @class jQuery + * @mixins jQuery.plugin.autoEllipsis + */ }( jQuery ) ); diff --git a/resources/src/jquery/jquery.byteLimit.js b/resources/src/jquery/jquery.byteLimit.js index b1692bbc44..567bec8ad6 100644 --- a/resources/src/jquery/jquery.byteLimit.js +++ b/resources/src/jquery/jquery.byteLimit.js @@ -3,6 +3,17 @@ */ ( function ( $ ) { + var eventKeys = [ + 'keyup.byteLimit', + 'keydown.byteLimit', + 'change.byteLimit', + 'mouseup.byteLimit', + 'cut.byteLimit', + 'paste.byteLimit', + 'focus.byteLimit', + 'blur.byteLimit' + ].join( ' ' ); + /** * Utility function to trim down a string, based on byteLimit * and given a safe start position. It supports insertion anywhere @@ -94,17 +105,6 @@ }; }; - var eventKeys = [ - 'keyup.byteLimit', - 'keydown.byteLimit', - 'change.byteLimit', - 'mouseup.byteLimit', - 'cut.byteLimit', - 'paste.byteLimit', - 'focus.byteLimit', - 'blur.byteLimit' - ].join( ' ' ); - /** * Enforces a byte limit on an input field, so that UTF-8 entries are counted as well, * when, for example, a database field has a byte limit rather than a character limit. diff --git a/resources/src/jquery/jquery.color.js b/resources/src/jquery/jquery.color.js index a3cc8fc3af..70dc1057a6 100644 --- a/resources/src/jquery/jquery.color.js +++ b/resources/src/jquery/jquery.color.js @@ -10,7 +10,6 @@ ( function ( $ ) { function getColor( elem, attr ) { - /*jshint boss:true */ var color; do { @@ -22,6 +21,7 @@ } attr = 'backgroundColor'; + // eslint-disable-next-line no-cond-assign } while ( elem = elem.parentNode ); return $.colorUtil.getRGB( color ); diff --git a/resources/src/jquery/jquery.colorUtil.js b/resources/src/jquery/jquery.colorUtil.js index c14f2c861c..c53ec3b147 100644 --- a/resources/src/jquery/jquery.colorUtil.js +++ b/resources/src/jquery/jquery.colorUtil.js @@ -23,7 +23,6 @@ * @return {Array} */ getRGB: function ( color ) { - /*jshint boss:true */ var result; // Check if we're already dealing with an array of colors @@ -32,6 +31,7 @@ } // Look for rgb(num,num,num) + // eslint-disable-next-line no-cond-assign if ( result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec( color ) ) { return [ parseInt( result[ 1 ], 10 ), @@ -41,6 +41,7 @@ } // Look for rgb(num%,num%,num%) + // eslint-disable-next-line no-cond-assign if ( result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec( color ) ) { return [ parseFloat( result[ 1 ] ) * 2.55, @@ -50,6 +51,7 @@ } // Look for #a0b1c2 + // eslint-disable-next-line no-cond-assign if ( result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec( color ) ) { return [ parseInt( result[ 1 ], 16 ), @@ -59,6 +61,7 @@ } // Look for #fff + // eslint-disable-next-line no-cond-assign if ( result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec( color ) ) { return [ parseInt( result[ 1 ] + result[ 1 ], 16 ), @@ -68,6 +71,7 @@ } // Look for rgba(0, 0, 0, 0) == transparent in Safari 3 + // eslint-disable-next-line no-cond-assign if ( result = /rgba\(0, 0, 0, 0\)/.exec( color ) ) { return $.colorUtil.colors.transparent; } @@ -148,16 +152,15 @@ * @return {number[]} The HSL representation */ rgbToHsl: function ( r, g, b ) { + var d, h, s, l, min, max; + r = r / 255; g = g / 255; b = b / 255; - var d, - max = Math.max( r, g, b ), - min = Math.min( r, g, b ), - h, - s, - l = ( max + min ) / 2; + max = Math.max( r, g, b ); + min = Math.min( r, g, b ); + l = ( max + min ) / 2; if ( max === min ) { // achromatic diff --git a/resources/src/jquery/jquery.hidpi.js b/resources/src/jquery/jquery.hidpi.js index 7d308f8e84..ed3124049f 100644 --- a/resources/src/jquery/jquery.hidpi.js +++ b/resources/src/jquery/jquery.hidpi.js @@ -11,166 +11,166 @@ */ ( function ( $ ) { -/** - * Get reported or approximate device pixel ratio. - * - * - 1.0 means 1 CSS pixel is 1 hardware pixel - * - 2.0 means 1 CSS pixel is 2 hardware pixels - * - etc. - * - * Uses `window.devicePixelRatio` if available, or CSS media queries on IE. - * - * @static - * @inheritable - * @return {number} Device pixel ratio - */ -$.devicePixelRatio = function () { - if ( window.devicePixelRatio !== undefined ) { - // Most web browsers: - // * WebKit/Blink (Safari, Chrome, Android browser, etc) - // * Opera - // * Firefox 18+ - // * Microsoft Edge (Windows 10) - return window.devicePixelRatio; - } else if ( window.msMatchMedia !== undefined ) { - // Windows 8 desktops / tablets, probably Windows Phone 8 - // - // IE 10/11 doesn't report pixel ratio directly, but we can get the - // screen DPI and divide by 96. We'll bracket to [1, 1.5, 2.0] for - // simplicity, but you may get different values depending on zoom - // factor, size of screen and orientation in Metro IE. - if ( window.msMatchMedia( '(min-resolution: 192dpi)' ).matches ) { + /** + * Get reported or approximate device pixel ratio. + * + * - 1.0 means 1 CSS pixel is 1 hardware pixel + * - 2.0 means 1 CSS pixel is 2 hardware pixels + * - etc. + * + * Uses `window.devicePixelRatio` if available, or CSS media queries on IE. + * + * @static + * @inheritable + * @return {number} Device pixel ratio + */ + $.devicePixelRatio = function () { + if ( window.devicePixelRatio !== undefined ) { + // Most web browsers: + // * WebKit/Blink (Safari, Chrome, Android browser, etc) + // * Opera + // * Firefox 18+ + // * Microsoft Edge (Windows 10) + return window.devicePixelRatio; + } else if ( window.msMatchMedia !== undefined ) { + // Windows 8 desktops / tablets, probably Windows Phone 8 + // + // IE 10/11 doesn't report pixel ratio directly, but we can get the + // screen DPI and divide by 96. We'll bracket to [1, 1.5, 2.0] for + // simplicity, but you may get different values depending on zoom + // factor, size of screen and orientation in Metro IE. + if ( window.msMatchMedia( '(min-resolution: 192dpi)' ).matches ) { + return 2; + } else if ( window.msMatchMedia( '(min-resolution: 144dpi)' ).matches ) { + return 1.5; + } else { + return 1; + } + } else { + // Legacy browsers... + // Assume 1 if unknown. + return 1; + } + }; + + /** + * Bracket a given device pixel ratio to one of [1, 1.5, 2]. + * + * This is useful for grabbing images on the fly with sizes based on the display + * density, without causing slowdown and extra thumbnail renderings on devices + * that are slightly different from the most common sizes. + * + * The bracketed ratios match the default 'srcset' output on MediaWiki thumbnails, + * so will be consistent with default renderings. + * + * @static + * @inheritable + * @return {number} Device pixel ratio + */ + $.bracketDevicePixelRatio = function ( baseRatio ) { + if ( baseRatio > 1.5 ) { return 2; - } else if ( window.msMatchMedia( '(min-resolution: 144dpi)' ).matches ) { + } else if ( baseRatio > 1 ) { return 1.5; } else { return 1; } - } else { - // Legacy browsers... - // Assume 1 if unknown. - return 1; - } -}; - -/** - * Bracket a given device pixel ratio to one of [1, 1.5, 2]. - * - * This is useful for grabbing images on the fly with sizes based on the display - * density, without causing slowdown and extra thumbnail renderings on devices - * that are slightly different from the most common sizes. - * - * The bracketed ratios match the default 'srcset' output on MediaWiki thumbnails, - * so will be consistent with default renderings. - * - * @static - * @inheritable - * @return {number} Device pixel ratio - */ -$.bracketDevicePixelRatio = function ( baseRatio ) { - if ( baseRatio > 1.5 ) { - return 2; - } else if ( baseRatio > 1 ) { - return 1.5; - } else { - return 1; - } -}; + }; -/** - * Get reported or approximate device pixel ratio, bracketed to [1, 1.5, 2]. - * - * This is useful for grabbing images on the fly with sizes based on the display - * density, without causing slowdown and extra thumbnail renderings on devices - * that are slightly different from the most common sizes. - * - * The bracketed ratios match the default 'srcset' output on MediaWiki thumbnails, - * so will be consistent with default renderings. - * - * - 1.0 means 1 CSS pixel is 1 hardware pixel - * - 1.5 means 1 CSS pixel is 1.5 hardware pixels - * - 2.0 means 1 CSS pixel is 2 hardware pixels - * - * @static - * @inheritable - * @return {number} Device pixel ratio - */ -$.bracketedDevicePixelRatio = function () { - return $.bracketDevicePixelRatio( $.devicePixelRatio() ); -}; + /** + * Get reported or approximate device pixel ratio, bracketed to [1, 1.5, 2]. + * + * This is useful for grabbing images on the fly with sizes based on the display + * density, without causing slowdown and extra thumbnail renderings on devices + * that are slightly different from the most common sizes. + * + * The bracketed ratios match the default 'srcset' output on MediaWiki thumbnails, + * so will be consistent with default renderings. + * + * - 1.0 means 1 CSS pixel is 1 hardware pixel + * - 1.5 means 1 CSS pixel is 1.5 hardware pixels + * - 2.0 means 1 CSS pixel is 2 hardware pixels + * + * @static + * @inheritable + * @return {number} Device pixel ratio + */ + $.bracketedDevicePixelRatio = function () { + return $.bracketDevicePixelRatio( $.devicePixelRatio() ); + }; -/** - * Implement responsive images based on srcset attributes, if browser has no - * native srcset support. - * - * @return {jQuery} This selection - * @chainable - */ -$.fn.hidpi = function () { - var $target = this, - // TODO add support for dpi media query checks on Firefox, IE - devicePixelRatio = $.devicePixelRatio(), - testImage = new Image(); + /** + * Implement responsive images based on srcset attributes, if browser has no + * native srcset support. + * + * @return {jQuery} This selection + * @chainable + */ + $.fn.hidpi = function () { + var $target = this, + // TODO add support for dpi media query checks on Firefox, IE + devicePixelRatio = $.devicePixelRatio(), + testImage = new Image(); - if ( devicePixelRatio > 1 && testImage.srcset === undefined ) { - // No native srcset support. - $target.find( 'img' ).each( function () { - var $img = $( this ), - srcset = $img.attr( 'srcset' ), - match; - if ( typeof srcset === 'string' && srcset !== '' ) { - match = $.matchSrcSet( devicePixelRatio, srcset ); - if ( match !== null ) { - $img.attr( 'src', match ); + if ( devicePixelRatio > 1 && testImage.srcset === undefined ) { + // No native srcset support. + $target.find( 'img' ).each( function () { + var $img = $( this ), + srcset = $img.attr( 'srcset' ), + match; + if ( typeof srcset === 'string' && srcset !== '' ) { + match = $.matchSrcSet( devicePixelRatio, srcset ); + if ( match !== null ) { + $img.attr( 'src', match ); + } } - } - } ); - } + } ); + } - return $target; -}; + return $target; + }; -/** - * Match a srcset entry for the given device pixel ratio - * - * Exposed for testing. - * - * @private - * @static - * @param {number} devicePixelRatio - * @param {string} srcset - * @return {Mixed} null or the matching src string - */ -$.matchSrcSet = function ( devicePixelRatio, srcset ) { - var candidates, - candidate, - bits, - src, - i, - ratioStr, - ratio, - selectedRatio = 1, - selectedSrc = null; - candidates = srcset.split( / *, */ ); - for ( i = 0; i < candidates.length; i++ ) { - candidate = candidates[ i ]; - bits = candidate.split( / +/ ); - src = bits[ 0 ]; - if ( bits.length > 1 && bits[ 1 ].charAt( bits[ 1 ].length - 1 ) === 'x' ) { - ratioStr = bits[ 1 ].slice( 0, -1 ); - ratio = parseFloat( ratioStr ); - if ( ratio <= devicePixelRatio && ratio > selectedRatio ) { - selectedRatio = ratio; - selectedSrc = src; + /** + * Match a srcset entry for the given device pixel ratio + * + * Exposed for testing. + * + * @private + * @static + * @param {number} devicePixelRatio + * @param {string} srcset + * @return {Mixed} null or the matching src string + */ + $.matchSrcSet = function ( devicePixelRatio, srcset ) { + var candidates, + candidate, + bits, + src, + i, + ratioStr, + ratio, + selectedRatio = 1, + selectedSrc = null; + candidates = srcset.split( / *, */ ); + for ( i = 0; i < candidates.length; i++ ) { + candidate = candidates[ i ]; + bits = candidate.split( / +/ ); + src = bits[ 0 ]; + if ( bits.length > 1 && bits[ 1 ].charAt( bits[ 1 ].length - 1 ) === 'x' ) { + ratioStr = bits[ 1 ].slice( 0, -1 ); + ratio = parseFloat( ratioStr ); + if ( ratio <= devicePixelRatio && ratio > selectedRatio ) { + selectedRatio = ratio; + selectedSrc = src; + } } } - } - return selectedSrc; -}; + return selectedSrc; + }; -/** - * @class jQuery - * @mixins jQuery.plugin.hidpi - */ + /** + * @class jQuery + * @mixins jQuery.plugin.hidpi + */ }( jQuery ) ); diff --git a/resources/src/jquery/jquery.highlightText.js b/resources/src/jquery/jquery.highlightText.js index e37f19b04a..3feca81cc1 100644 --- a/resources/src/jquery/jquery.highlightText.js +++ b/resources/src/jquery/jquery.highlightText.js @@ -45,12 +45,14 @@ // replace the matched node, with our span-wrapped clone of the matched node middlebit.parentNode.replaceChild( spannode, middlebit ); } - } else if ( node.nodeType === Node.ELEMENT_NODE + } else if ( + node.nodeType === Node.ELEMENT_NODE && // element with childnodes, and not a script, style or an element we created - && node.childNodes - && !/(script|style)/i.test( node.tagName ) - && !( node.tagName.toLowerCase() === 'span' - && node.className.match( /\bhighlight/ ) + node.childNodes && + !/(script|style)/i.test( node.tagName ) && + !( + node.tagName.toLowerCase() === 'span' && + node.className.match( /\bhighlight/ ) ) ) { for ( i = 0; i < node.childNodes.length; ++i ) { diff --git a/resources/src/jquery/jquery.localize.js b/resources/src/jquery/jquery.localize.js index f5932b246f..05b3891dea 100644 --- a/resources/src/jquery/jquery.localize.js +++ b/resources/src/jquery/jquery.localize.js @@ -3,168 +3,168 @@ */ ( function ( $, mw ) { -/** - * Gets a localized message, using parameters from options if present. - * - * @ignore - * @param {Object} options - * @param {string} key - * @return {string} Localized message - */ -function msg( options, key ) { - var args = options.params[ key ] || []; - // Format: mw.msg( key [, p1, p2, ...] ) - args.unshift( options.prefix + ( options.keys[ key ] || key ) ); - return mw.msg.apply( mw, args ); -} + /** + * Gets a localized message, using parameters from options if present. + * + * @ignore + * @param {Object} options + * @param {string} key + * @return {string} Localized message + */ + function msg( options, key ) { + var args = options.params[ key ] || []; + // Format: mw.msg( key [, p1, p2, ...] ) + args.unshift( options.prefix + ( options.keys[ key ] || key ) ); + return mw.msg.apply( mw, args ); + } -/** - * Localizes a DOM selection by replacing elements with localized text and adding - * localized title and alt attributes to elements with title-msg and alt-msg attributes - * respectively. - * - * Call on a selection of HTML which contains `` elements or elements - * with title-msg="message-key", alt-msg="message-key" or placeholder-msg="message-key" attributes. - * `` elements will be replaced with localized text, *-msg attributes will be replaced - * with attributes that do not have the "-msg" suffix and contain a localized message. - * - * Example: - * // Messages: { 'title': 'Awesome', 'desc': 'Cat doing backflip' 'search' contains 'Search' } - * var html = '\ - *

\ - * \ - * \ - * \ - *

'; - * $( 'body' ).append( $( html ).localize() ); - * - * Appends something like this to the body... - *

- * Awesome - * Cat doing backflip - * - *

- * - * Arguments can be passed into uses of a message using the params property of the options object - * given to .localize(). Multiple messages can be given parameters, because the params property is - * an object keyed by the message key to apply the parameters to, each containing an array of - * parameters to use. The limitation is that you can not use different parameters to individual uses - * of a message in the same selection being localized - they will all recieve the same parameters. - * - * Example: - * // Messages: { 'easy-as': 'Easy as $1 $2 $3.' } - * var html = '

'; - * $( 'body' ).append( $( html ).localize( { 'params': { 'easy-as': ['a', 'b', 'c'] } } ) ); - * - * Appends something like this to the body... - *

Easy as a, b, c

- * - * Raw HTML content can be used, instead of it being escaped as text. To do this, just use the raw - * attribute on a msg element. - * - * Example: - * // Messages: { 'hello': 'Hello $1!' } - * var html = '\ - *

\ - * \ - * \ - *

'; - * $( 'body' ).append( $( html ).localize( { 'params': { 'hello': ['world'] } } ) ); - * - * Appends something like this to the body... - *

- * <b><i>Hello</i> world!</b> - * Hello world! - *

- * - * Message keys can also be remapped, allowing the same generic template to be used with a variety - * of messages. This is important for improving re-usability of templates. - * - * Example: - * // Messages: { 'good-afternoon': 'Good afternoon' } - * var html = '

'; - * $( 'body' ).append( $( html ).localize( { 'keys': { 'greeting': 'good-afternoon' } } ) ); - * - * Appends something like this to the body... - *

Good afternoon

- * - * Message keys can also be prefixed globally, which is handy when writing extensions, where by - * convention all messages are prefixed with the extension's name. - * - * Example: - * // Messages: { 'teleportation-warning': 'You may not get there all in one piece.' } - * var html = '

'; - * $( 'body' ).append( $( html ).localize( { 'prefix': 'teleportation-' } ) ); - * - * Appends something like this to the body... - *

You may not get there all in one piece.

- * - * @param {Object} options Map of options to be used while localizing - * @param {string} options.prefix String to prepend to all message keys - * @param {Object} options.keys Message key aliases, used for remapping keys to a template - * @param {Object} options.params Lists of parameters to use with certain message keys - * @return {jQuery} - * @chainable - */ -$.fn.localize = function ( options ) { - var $target = this, - attributes = [ 'title', 'alt', 'placeholder' ]; + /** + * Localizes a DOM selection by replacing elements with localized text and adding + * localized title and alt attributes to elements with title-msg and alt-msg attributes + * respectively. + * + * Call on a selection of HTML which contains `` elements or elements + * with title-msg="message-key", alt-msg="message-key" or placeholder-msg="message-key" attributes. + * `` elements will be replaced with localized text, *-msg attributes will be replaced + * with attributes that do not have the "-msg" suffix and contain a localized message. + * + * Example: + * // Messages: { 'title': 'Awesome', 'desc': 'Cat doing backflip' 'search' contains 'Search' } + * var html = '\ + *

\ + * \ + * \ + * \ + *

'; + * $( 'body' ).append( $( html ).localize() ); + * + * Appends something like this to the body... + *

+ * Awesome + * Cat doing backflip + * + *

+ * + * Arguments can be passed into uses of a message using the params property of the options object + * given to .localize(). Multiple messages can be given parameters, because the params property is + * an object keyed by the message key to apply the parameters to, each containing an array of + * parameters to use. The limitation is that you can not use different parameters to individual uses + * of a message in the same selection being localized - they will all recieve the same parameters. + * + * Example: + * // Messages: { 'easy-as': 'Easy as $1 $2 $3.' } + * var html = '

'; + * $( 'body' ).append( $( html ).localize( { 'params': { 'easy-as': ['a', 'b', 'c'] } } ) ); + * + * Appends something like this to the body... + *

Easy as a, b, c

+ * + * Raw HTML content can be used, instead of it being escaped as text. To do this, just use the raw + * attribute on a msg element. + * + * Example: + * // Messages: { 'hello': 'Hello $1!' } + * var html = '\ + *

\ + * \ + * \ + *

'; + * $( 'body' ).append( $( html ).localize( { 'params': { 'hello': ['world'] } } ) ); + * + * Appends something like this to the body... + *

+ * <b><i>Hello</i> world!</b> + * Hello world! + *

+ * + * Message keys can also be remapped, allowing the same generic template to be used with a variety + * of messages. This is important for improving re-usability of templates. + * + * Example: + * // Messages: { 'good-afternoon': 'Good afternoon' } + * var html = '

'; + * $( 'body' ).append( $( html ).localize( { 'keys': { 'greeting': 'good-afternoon' } } ) ); + * + * Appends something like this to the body... + *

Good afternoon

+ * + * Message keys can also be prefixed globally, which is handy when writing extensions, where by + * convention all messages are prefixed with the extension's name. + * + * Example: + * // Messages: { 'teleportation-warning': 'You may not get there all in one piece.' } + * var html = '

'; + * $( 'body' ).append( $( html ).localize( { 'prefix': 'teleportation-' } ) ); + * + * Appends something like this to the body... + *

You may not get there all in one piece.

+ * + * @param {Object} options Map of options to be used while localizing + * @param {string} options.prefix String to prepend to all message keys + * @param {Object} options.keys Message key aliases, used for remapping keys to a template + * @param {Object} options.params Lists of parameters to use with certain message keys + * @return {jQuery} + * @chainable + */ + $.fn.localize = function ( options ) { + var $target = this, + attributes = [ 'title', 'alt', 'placeholder' ]; - // Extend options - options = $.extend( { - prefix: '', - keys: {}, - params: {} - }, options ); + // Extend options + options = $.extend( { + prefix: '', + keys: {}, + params: {} + }, options ); - // Elements - // Ok, so here's the story on this selector. In IE 6/7, searching for 'msg' turns up the - // 'html:msg', but searching for 'html:msg' doesn't. In later IE and other browsers, searching - // for 'html:msg' turns up the 'html:msg', but searching for 'msg' doesn't. So searching for - // both 'msg' and 'html:msg' seems to get the job done. This feels pretty icky, though. - $target.find( 'msg,html\\:msg' ).each( function () { - var $el = $( this ); - // Escape by default - if ( $el.attr( 'raw' ) ) { - $el.html( msg( options, $el.attr( 'key' ) ) ); - } else { - $el.text( msg( options, $el.attr( 'key' ) ) ); - } - // Remove wrapper - $el.replaceWith( $el.html() ); - } ); - - // Attributes - // Note: there's no way to prevent escaping of values being injected into attributes, this is - // on purpose, not a design flaw. - $.each( attributes, function ( i, attr ) { - var msgAttr = attr + '-msg'; - $target.find( '[' + msgAttr + ']' ).each( function () { + // Elements + // Ok, so here's the story on this selector. In IE 6/7, searching for 'msg' turns up the + // 'html:msg', but searching for 'html:msg' doesn't. In later IE and other browsers, searching + // for 'html:msg' turns up the 'html:msg', but searching for 'msg' doesn't. So searching for + // both 'msg' and 'html:msg' seems to get the job done. This feels pretty icky, though. + $target.find( 'msg,html\\:msg' ).each( function () { var $el = $( this ); - $el.attr( attr, msg( options, $el.attr( msgAttr ) ) ).removeAttr( msgAttr ); + // Escape by default + if ( $el.attr( 'raw' ) ) { + $el.html( msg( options, $el.attr( 'key' ) ) ); + } else { + $el.text( msg( options, $el.attr( 'key' ) ) ); + } + // Remove wrapper + $el.replaceWith( $el.html() ); } ); - } ); - // HTML, Text for elements which cannot have children e.g. OPTION - $target.find( '[data-msg-text]' ).each( function () { - var $el = $( this ); - $el.text( msg( options, $el.attr( 'data-msg-text' ) ) ); - } ); + // Attributes + // Note: there's no way to prevent escaping of values being injected into attributes, this is + // on purpose, not a design flaw. + $.each( attributes, function ( i, attr ) { + var msgAttr = attr + '-msg'; + $target.find( '[' + msgAttr + ']' ).each( function () { + var $el = $( this ); + $el.attr( attr, msg( options, $el.attr( msgAttr ) ) ).removeAttr( msgAttr ); + } ); + } ); - $target.find( '[data-msg-html]' ).each( function () { - var $el = $( this ); - $el.html( msg( options, $el.attr( 'data-msg-html' ) ) ); - } ); + // HTML, Text for elements which cannot have children e.g. OPTION + $target.find( '[data-msg-text]' ).each( function () { + var $el = $( this ); + $el.text( msg( options, $el.attr( 'data-msg-text' ) ) ); + } ); + + $target.find( '[data-msg-html]' ).each( function () { + var $el = $( this ); + $el.html( msg( options, $el.attr( 'data-msg-html' ) ) ); + } ); - return $target; -}; + return $target; + }; -// Let IE know about the msg tag before it's used... -document.createElement( 'msg' ); + // Let IE know about the msg tag before it's used... + document.createElement( 'msg' ); -/** - * @class jQuery - * @mixins jQuery.plugin.localize - */ + /** + * @class jQuery + * @mixins jQuery.plugin.localize + */ }( jQuery, mediaWiki ) ); diff --git a/resources/src/jquery/jquery.mwExtension.js b/resources/src/jquery/jquery.mwExtension.js index 27ceb2bc45..f9675fa69c 100644 --- a/resources/src/jquery/jquery.mwExtension.js +++ b/resources/src/jquery/jquery.mwExtension.js @@ -40,10 +40,11 @@ return false; }, compareArray: function ( arrThis, arrAgainst ) { + var i; if ( arrThis.length !== arrAgainst.length ) { return false; } - for ( var i = 0; i < arrThis.length; i++ ) { + for ( i = 0; i < arrThis.length; i++ ) { if ( $.isArray( arrThis[ i ] ) ) { if ( !$.compareArray( arrThis[ i ], arrAgainst[ i ] ) ) { return false; @@ -124,4 +125,4 @@ return str.replace( /([\\{}()|.?*+\-\^$\[\]])/g, '\\$1' ); }, 'Use mediawiki.RegExp instead.' ); -} )( jQuery, mediaWiki ); +}( jQuery, mediaWiki ) ); diff --git a/resources/src/jquery/jquery.placeholder.js b/resources/src/jquery/jquery.placeholder.js index c472ac7b58..ae9c943869 100644 --- a/resources/src/jquery/jquery.placeholder.js +++ b/resources/src/jquery/jquery.placeholder.js @@ -35,7 +35,7 @@ function args( elem ) { // Return an object of element attributes var newAttrs = {}, - rinlinejQuery = /^jQuery\d+$/; + rinlinejQuery = /^jQuery\d+$/; $.each( elem.attributes, function ( i, attr ) { if ( attr.specified && !rinlinejQuery.test( attr.name ) ) { newAttrs[ attr.name ] = attr.value; @@ -46,7 +46,7 @@ function clearPlaceholder( event, value ) { var input = this, - $input = $( input ); + $input = $( input ); if ( input.value === $input.attr( 'placeholder' ) && $input.hasClass( 'placeholder' ) ) { if ( $input.data( 'placeholder-password' ) ) { $input = $input.hide().next().show().attr( 'id', $input.removeAttr( 'id' ).data( 'placeholder-id' ) ); @@ -68,9 +68,9 @@ function setPlaceholder() { var $replacement, - input = this, - $input = $( input ), - id = this.id; + input = this, + $input = $( input ), + id = this.id; if ( !input.value ) { if ( input.type === 'password' ) { if ( !$input.data( 'placeholder-textinput' ) ) { @@ -105,7 +105,7 @@ function changePlaceholder( text ) { var hasArgs = arguments.length, - $input = this; + $input = this; if ( hasArgs ) { if ( $input.attr( 'placeholder' ) !== text ) { $input.prop( 'placeholder', text ); diff --git a/resources/src/jquery/jquery.qunit.completenessTest.js b/resources/src/jquery/jquery.qunit.completenessTest.js index 8d263fbf2e..68bf83001e 100644 --- a/resources/src/jquery/jquery.qunit.completenessTest.js +++ b/resources/src/jquery/jquery.qunit.completenessTest.js @@ -17,9 +17,9 @@ var util, hasOwn = Object.prototype.hasOwnProperty, - log = ( window.console && window.console.log ) - ? function () { return window.console.log.apply( window.console, arguments ); } - : function () {}; + log = ( window.console && window.console.log ) ? + function () { return window.console.log.apply( window.console, arguments ); } : + function () {}; // Simplified version of a few jQuery methods, except that they don't // call other jQuery methods. Required to be able to run the CompletenessTest @@ -94,11 +94,11 @@ } ); QUnit.done( function () { + var toolbar, testResults, cntTotal, cntCalled, cntMissing; + that.populateMissingTests(); log( 'CompletenessTest/populateMissingTests', that ); - var toolbar, testResults, cntTotal, cntCalled, cntMissing; - cntTotal = util.keys( that.injectionTracker ).length; cntCalled = util.keys( that.methodCallTracker ).length; cntMissing = util.keys( that.missingTests ).length; @@ -292,11 +292,7 @@ // Make the spy inherit from the original so that its static methods are also // visible in the spy (e.g. when we inject a check into mw.log, mw.log.warn // must remain accessible). - // XXX: https://github.com/jshint/jshint/issues/2656 - /*jshint ignore:start */ - /*jshint proto:true */ spy.__proto__ = val; - /*jshint ignore:end */ // Objects are by reference, members (unless objects) are not. obj[ key ] = spy; diff --git a/resources/src/jquery/jquery.spinner.js b/resources/src/jquery/jquery.spinner.js index af5a97d6a4..9079cc09b7 100644 --- a/resources/src/jquery/jquery.spinner.js +++ b/resources/src/jquery/jquery.spinner.js @@ -59,6 +59,8 @@ * @return {jQuery} */ createSpinner: function ( opts ) { + var $spinner; + if ( opts !== undefined && $.type( opts ) !== 'object' ) { opts = { id: opts @@ -67,7 +69,7 @@ opts = $.extend( {}, defaults, opts ); - var $spinner = $( '
' ).addClass( 'mw-spinner' ).attr( 'title', '...' ); + $spinner = $( '
' ).addClass( 'mw-spinner' ).attr( 'title', '...' ); if ( opts.id !== undefined ) { $spinner.attr( 'id', 'mw-spinner-' + opts.id ); } diff --git a/resources/src/jquery/jquery.suggestions.js b/resources/src/jquery/jquery.suggestions.js index 884ecb6e96..f3e4e09313 100644 --- a/resources/src/jquery/jquery.suggestions.js +++ b/resources/src/jquery/jquery.suggestions.js @@ -353,7 +353,6 @@ $results.empty(); expWidth = -1; for ( i = 0; i < context.config.suggestions.length; i++ ) { - /*jshint loopfunc:true */ text = context.config.suggestions[ i ]; $result = $( '
' ) .addClass( 'suggestions-result' ) @@ -758,9 +757,9 @@ 46, // delete 8 // backspace ]; - if ( context.data.keypressedCount === 0 - && e.which === context.data.keypressed - && $.inArray( e.which, allowed ) !== -1 + if ( context.data.keypressedCount === 0 && + e.which === context.data.keypressed && + $.inArray( e.which, allowed ) !== -1 ) { $.suggestions.keypress( e, context, context.data.keypressed ); } diff --git a/resources/src/jquery/jquery.tablesorter.js b/resources/src/jquery/jquery.tablesorter.js index 62be0d8c4a..3b85f9a4c2 100644 --- a/resources/src/jquery/jquery.tablesorter.js +++ b/resources/src/jquery/jquery.tablesorter.js @@ -442,10 +442,11 @@ } function setHeadersCss( table, $headers, list, css, msg, columnToHeader ) { + var i, len; // Remove all header information and reset titles to default message $headers.removeClass( css[ 0 ] ).removeClass( css[ 1 ] ).attr( 'title', msg[ 1 ] ); - for ( var i = 0; i < list.length; i++ ) { + for ( i = 0, len = list.length; i < len; i++ ) { $headers .eq( columnToHeader[ list[ i ][ 0 ] ] ) .addClass( css[ list[ i ][ 1 ] ] ) @@ -686,12 +687,10 @@ } function buildCollationTable() { + var key, keys = []; ts.collationTable = mw.config.get( 'tableSorterCollation' ); ts.collationRegex = null; if ( ts.collationTable ) { - var key, - keys = []; - // Build array of key names for ( key in ts.collationTable ) { // Check hasOwn to be safe @@ -755,298 +754,299 @@ /* Public scope */ $.tablesorter = { - defaultOptions: { - cssHeader: 'headerSort', - cssAsc: 'headerSortUp', - cssDesc: 'headerSortDown', - cssChildRow: 'expand-child', - sortMultiSortKey: 'shiftKey', - unsortableClass: 'unsortable', - parsers: [], - cancelSelection: true, - sortList: [], - headerList: [], - headerToColumns: [], - columnToHeader: [], - columns: 0 - }, - - dateRegex: [], - monthNames: {}, - - /** - * @param {jQuery} $tables - * @param {Object} [settings] - */ - construct: function ( $tables, settings ) { - return $tables.each( function ( i, table ) { - // Declare and cache. - var $headers, cache, config, sortCSS, sortMsg, - $table = $( table ), - firstTime = true; - - // Quit if no tbody - if ( !table.tBodies ) { + defaultOptions: { + cssHeader: 'headerSort', + cssAsc: 'headerSortUp', + cssDesc: 'headerSortDown', + cssChildRow: 'expand-child', + sortMultiSortKey: 'shiftKey', + unsortableClass: 'unsortable', + parsers: [], + cancelSelection: true, + sortList: [], + headerList: [], + headerToColumns: [], + columnToHeader: [], + columns: 0 + }, + + dateRegex: [], + monthNames: {}, + + /** + * @param {jQuery} $tables + * @param {Object} [settings] + */ + construct: function ( $tables, settings ) { + return $tables.each( function ( i, table ) { + // Declare and cache. + var $headers, cache, config, sortCSS, sortMsg, + $table = $( table ), + firstTime = true; + + // Quit if no tbody + if ( !table.tBodies ) { + return; + } + if ( !table.tHead ) { + // No thead found. Look for rows with s and + // move them into a tag or a tag + emulateTHeadAndFoot( $table ); + + // Still no thead? Then quit + if ( !table.tHead ) { return; } - if ( !table.tHead ) { - // No thead found. Look for rows with s and - // move them into a tag or a tag - emulateTHeadAndFoot( $table ); + } + $table.addClass( 'jquery-tablesorter' ); - // Still no thead? Then quit - if ( !table.tHead ) { - return; - } - } - $table.addClass( 'jquery-tablesorter' ); - - // Merge and extend - config = $.extend( {}, $.tablesorter.defaultOptions, settings ); - - // Save the settings where they read - $.data( table, 'tablesorter', { config: config } ); - - // Get the CSS class names, could be done elsewhere - sortCSS = [ config.cssAsc, config.cssDesc ]; - // Messages tell the the user what the *next* state will be - // so are in reverse order to the CSS classes. - sortMsg = [ mw.msg( 'sort-descending' ), mw.msg( 'sort-ascending' ) ]; - - // Build headers - $headers = buildHeaders( table, sortMsg ); - - // Grab and process locale settings. - buildTransformTable(); - buildDateTable(); - - // Precaching regexps can bring 10 fold - // performance improvements in some browsers. - cacheRegexs(); - - function setupForFirstSort() { - firstTime = false; - - // Defer buildCollationTable to first sort. As user and site scripts - // may customize tableSorterCollation but load after $.ready(), other - // scripts may call .tablesorter() before they have done the - // tableSorterCollation customizations. - buildCollationTable(); - - // Legacy fix of .sortbottoms - // Wrap them inside a tfoot (because that's what they actually want to be) - // and put the at the end of the - var $tfoot, - $sortbottoms = $table.find( '> tbody > tr.sortbottom' ); - if ( $sortbottoms.length ) { - $tfoot = $table.children( 'tfoot' ); - if ( $tfoot.length ) { - $tfoot.eq( 0 ).prepend( $sortbottoms ); - } else { - $table.append( $( '' ).append( $sortbottoms ) ); - } - } + // Merge and extend + config = $.extend( {}, $.tablesorter.defaultOptions, settings ); - explodeRowspans( $table ); - manageColspans( $table ); + // Save the settings where they read + $.data( table, 'tablesorter', { config: config } ); - // Try to auto detect column type, and store in tables config - config.parsers = buildParserCache( table, $headers ); - } + // Get the CSS class names, could be done elsewhere + sortCSS = [ config.cssAsc, config.cssDesc ]; + // Messages tell the the user what the *next* state will be + // so are in reverse order to the CSS classes. + sortMsg = [ mw.msg( 'sort-descending' ), mw.msg( 'sort-ascending' ) ]; - // Apply event handling to headers - // this is too big, perhaps break it out? - $headers.on( 'keypress click', function ( e ) { - var cell, $cell, columns, newSortList, i, - totalRows, - j, s, o; - - if ( e.type === 'click' && e.target.nodeName.toLowerCase() === 'a' ) { - // The user clicked on a link inside a table header. - // Do nothing and let the default link click action continue. - return true; - } + // Build headers + $headers = buildHeaders( table, sortMsg ); - if ( e.type === 'keypress' && e.which !== 13 ) { - // Only handle keypresses on the "Enter" key. - return true; - } + // Grab and process locale settings. + buildTransformTable(); + buildDateTable(); - if ( firstTime ) { - setupForFirstSort(); + // Precaching regexps can bring 10 fold + // performance improvements in some browsers. + cacheRegexs(); + + function setupForFirstSort() { + var $tfoot, $sortbottoms; + + firstTime = false; + + // Defer buildCollationTable to first sort. As user and site scripts + // may customize tableSorterCollation but load after $.ready(), other + // scripts may call .tablesorter() before they have done the + // tableSorterCollation customizations. + buildCollationTable(); + + // Legacy fix of .sortbottoms + // Wrap them inside a tfoot (because that's what they actually want to be) + // and put the at the end of the
+ $sortbottoms = $table.find( '> tbody > tr.sortbottom' ); + if ( $sortbottoms.length ) { + $tfoot = $table.children( 'tfoot' ); + if ( $tfoot.length ) { + $tfoot.eq( 0 ).prepend( $sortbottoms ); + } else { + $table.append( $( '' ).append( $sortbottoms ) ); } + } - // Build the cache for the tbody cells - // to share between calculations for this sort action. - // Re-calculated each time a sort action is performed due to possiblity - // that sort values change. Shouldn't be too expensive, but if it becomes - // too slow an event based system should be implemented somehow where - // cells get event .change() and bubbles up to the
here - cache = buildCache( table ); - - totalRows = ( $table[ 0 ].tBodies[ 0 ] && $table[ 0 ].tBodies[ 0 ].rows.length ) || 0; - if ( totalRows > 0 ) { - cell = this; - $cell = $( cell ); - - // Get current column sort order - $cell.data( { - order: $cell.data( 'count' ) % 2, - count: $cell.data( 'count' ) + 1 - } ); + explodeRowspans( $table ); + manageColspans( $table ); - cell = this; - // Get current column index - columns = config.headerToColumns[ $cell.data( 'headerIndex' ) ]; - newSortList = $.map( columns, function ( c ) { - // jQuery "helpfully" flattens the arrays... - return [ [ c, $cell.data( 'order' ) ] ]; - } ); - // Index of first column belonging to this header - i = columns[ 0 ]; + // Try to auto detect column type, and store in tables config + config.parsers = buildParserCache( table, $headers ); + } - if ( !e[ config.sortMultiSortKey ] ) { - // User only wants to sort on one column set - // Flush the sort list and add new columns - config.sortList = newSortList; - } else { - // Multi column sorting - // It is not possible for one column to belong to multiple headers, - // so this is okay - we don't need to check for every value in the columns array - if ( isValueInArray( i, config.sortList ) ) { - // The user has clicked on an already sorted column. - // Reverse the sorting direction for all tables. - for ( j = 0; j < config.sortList.length; j++ ) { - s = config.sortList[ j ]; - o = config.headerList[ config.columnToHeader[ s[ 0 ] ] ]; - if ( isValueInArray( s[ 0 ], newSortList ) ) { - $( o ).data( 'count', s[ 1 ] + 1 ); - s[ 1 ] = $( o ).data( 'count' ) % 2; - } + // Apply event handling to headers + // this is too big, perhaps break it out? + $headers.on( 'keypress click', function ( e ) { + var cell, $cell, columns, newSortList, i, + totalRows, + j, s, o; + + if ( e.type === 'click' && e.target.nodeName.toLowerCase() === 'a' ) { + // The user clicked on a link inside a table header. + // Do nothing and let the default link click action continue. + return true; + } + + if ( e.type === 'keypress' && e.which !== 13 ) { + // Only handle keypresses on the "Enter" key. + return true; + } + + if ( firstTime ) { + setupForFirstSort(); + } + + // Build the cache for the tbody cells + // to share between calculations for this sort action. + // Re-calculated each time a sort action is performed due to possiblity + // that sort values change. Shouldn't be too expensive, but if it becomes + // too slow an event based system should be implemented somehow where + // cells get event .change() and bubbles up to the
here + cache = buildCache( table ); + + totalRows = ( $table[ 0 ].tBodies[ 0 ] && $table[ 0 ].tBodies[ 0 ].rows.length ) || 0; + if ( totalRows > 0 ) { + cell = this; + $cell = $( cell ); + + // Get current column sort order + $cell.data( { + order: $cell.data( 'count' ) % 2, + count: $cell.data( 'count' ) + 1 + } ); + + cell = this; + // Get current column index + columns = config.headerToColumns[ $cell.data( 'headerIndex' ) ]; + newSortList = $.map( columns, function ( c ) { + // jQuery "helpfully" flattens the arrays... + return [ [ c, $cell.data( 'order' ) ] ]; + } ); + // Index of first column belonging to this header + i = columns[ 0 ]; + + if ( !e[ config.sortMultiSortKey ] ) { + // User only wants to sort on one column set + // Flush the sort list and add new columns + config.sortList = newSortList; + } else { + // Multi column sorting + // It is not possible for one column to belong to multiple headers, + // so this is okay - we don't need to check for every value in the columns array + if ( isValueInArray( i, config.sortList ) ) { + // The user has clicked on an already sorted column. + // Reverse the sorting direction for all tables. + for ( j = 0; j < config.sortList.length; j++ ) { + s = config.sortList[ j ]; + o = config.headerList[ config.columnToHeader[ s[ 0 ] ] ]; + if ( isValueInArray( s[ 0 ], newSortList ) ) { + $( o ).data( 'count', s[ 1 ] + 1 ); + s[ 1 ] = $( o ).data( 'count' ) % 2; } - } else { - // Add columns to sort list array - config.sortList = config.sortList.concat( newSortList ); } + } else { + // Add columns to sort list array + config.sortList = config.sortList.concat( newSortList ); } + } - // Reset order/counts of cells not affected by sorting - setHeadersOrder( $headers, config.sortList, config.headerToColumns ); + // Reset order/counts of cells not affected by sorting + setHeadersOrder( $headers, config.sortList, config.headerToColumns ); - // Set CSS for headers - setHeadersCss( $table[ 0 ], $headers, config.sortList, sortCSS, sortMsg, config.columnToHeader ); - appendToTable( - $table[ 0 ], multisort( $table[ 0 ], config.sortList, cache ) - ); + // Set CSS for headers + setHeadersCss( $table[ 0 ], $headers, config.sortList, sortCSS, sortMsg, config.columnToHeader ); + appendToTable( + $table[ 0 ], multisort( $table[ 0 ], config.sortList, cache ) + ); - // Stop normal event by returning false - return false; - } + // Stop normal event by returning false + return false; + } - // Cancel selection - } ).mousedown( function () { - if ( config.cancelSelection ) { - this.onselectstart = function () { - return false; - }; + // Cancel selection + } ).mousedown( function () { + if ( config.cancelSelection ) { + this.onselectstart = function () { return false; - } - } ); + }; + return false; + } + } ); - /** - * Sorts the table. If no sorting is specified by passing a list of sort - * objects, the table is sorted according to the initial sorting order. - * Passing an empty array will reset sorting (basically just reset the headers - * making the table appear unsorted). - * - * @param {Array} [sortList] List of sort objects. - */ - $table.data( 'tablesorter' ).sort = function ( sortList ) { - - if ( firstTime ) { - setupForFirstSort(); - } + /** + * Sorts the table. If no sorting is specified by passing a list of sort + * objects, the table is sorted according to the initial sorting order. + * Passing an empty array will reset sorting (basically just reset the headers + * making the table appear unsorted). + * + * @param {Array} [sortList] List of sort objects. + */ + $table.data( 'tablesorter' ).sort = function ( sortList ) { + + if ( firstTime ) { + setupForFirstSort(); + } - if ( sortList === undefined ) { - sortList = config.sortList; - } else if ( sortList.length > 0 ) { - sortList = convertSortList( sortList ); - } + if ( sortList === undefined ) { + sortList = config.sortList; + } else if ( sortList.length > 0 ) { + sortList = convertSortList( sortList ); + } - // Set each column's sort count to be able to determine the correct sort - // order when clicking on a header cell the next time - setHeadersOrder( $headers, sortList, config.headerToColumns ); + // Set each column's sort count to be able to determine the correct sort + // order when clicking on a header cell the next time + setHeadersOrder( $headers, sortList, config.headerToColumns ); - // re-build the cache for the tbody cells - cache = buildCache( table ); + // re-build the cache for the tbody cells + cache = buildCache( table ); - // set css for headers - setHeadersCss( table, $headers, sortList, sortCSS, sortMsg, config.columnToHeader ); + // set css for headers + setHeadersCss( table, $headers, sortList, sortCSS, sortMsg, config.columnToHeader ); - // sort the table and append it to the dom - appendToTable( table, multisort( table, sortList, cache ) ); - }; + // sort the table and append it to the dom + appendToTable( table, multisort( table, sortList, cache ) ); + }; - // sort initially - if ( config.sortList.length > 0 ) { - config.sortList = convertSortList( config.sortList ); - $table.data( 'tablesorter' ).sort(); - } + // sort initially + if ( config.sortList.length > 0 ) { + config.sortList = convertSortList( config.sortList ); + $table.data( 'tablesorter' ).sort(); + } - } ); - }, + } ); + }, - addParser: function ( parser ) { - if ( !getParserById( parser.id ) ) { - parsers.push( parser ); - } - }, - - formatDigit: function ( s ) { - var out, c, p, i; - if ( ts.transformTable !== false ) { - out = ''; - for ( p = 0; p < s.length; p++ ) { - c = s.charAt( p ); - if ( c in ts.transformTable ) { - out += ts.transformTable[ c ]; - } else { - out += c; - } + addParser: function ( parser ) { + if ( !getParserById( parser.id ) ) { + parsers.push( parser ); + } + }, + + formatDigit: function ( s ) { + var out, c, p, i; + if ( ts.transformTable !== false ) { + out = ''; + for ( p = 0; p < s.length; p++ ) { + c = s.charAt( p ); + if ( c in ts.transformTable ) { + out += ts.transformTable[ c ]; + } else { + out += c; } - s = out; } - i = parseFloat( s.replace( /[, ]/g, '' ).replace( '\u2212', '-' ) ); - return isNaN( i ) ? 0 : i; - }, + s = out; + } + i = parseFloat( s.replace( /[, ]/g, '' ).replace( '\u2212', '-' ) ); + return isNaN( i ) ? 0 : i; + }, - formatFloat: function ( s ) { - var i = parseFloat( s ); - return isNaN( i ) ? 0 : i; - }, + formatFloat: function ( s ) { + var i = parseFloat( s ); + return isNaN( i ) ? 0 : i; + }, - formatInt: function ( s ) { - var i = parseInt( s, 10 ); - return isNaN( i ) ? 0 : i; - }, + formatInt: function ( s ) { + var i = parseInt( s, 10 ); + return isNaN( i ) ? 0 : i; + }, - clearTableBody: function ( table ) { - $( table.tBodies[ 0 ] ).empty(); - }, + clearTableBody: function ( table ) { + $( table.tBodies[ 0 ] ).empty(); + }, - getParser: function ( id ) { - buildTransformTable(); - buildDateTable(); - cacheRegexs(); - buildCollationTable(); + getParser: function ( id ) { + buildTransformTable(); + buildDateTable(); + cacheRegexs(); + buildCollationTable(); - return getParserById( id ); - }, + return getParserById( id ); + }, - getParsers: function () { // for table diagnosis - return parsers; - } - }; + getParsers: function () { // for table diagnosis + return parsers; + } + }; // Shortcut ts = $.tablesorter; @@ -1063,9 +1063,10 @@ return true; }, format: function ( s ) { + var tsc; s = $.trim( s.toLowerCase() ); if ( ts.collationRegex ) { - var tsc = ts.collationTable; + tsc = ts.collationTable; s = s.replace( ts.collationRegex, function ( match ) { var r = tsc[ match ] ? tsc[ match ] : tsc[ match.toUpperCase() ]; return r.toLowerCase(); @@ -1135,7 +1136,7 @@ if ( !matches ) { return $.tablesorter.formatFloat( 0 ); } - isodate = new Date( matches[ 2 ] + '/' + matches[ 3 ] + '/' + matches[ 1 ] ); + isodate = new Date( matches[ 2 ] + '/' + matches[ 3 ] + '/' + matches[ 1 ] ); } else { matches = s.match( ts.rgx.isoDate[ 0 ] ); if ( !matches ) { diff --git a/resources/src/jquery/jquery.textSelection.js b/resources/src/jquery/jquery.textSelection.js index 5e93ba62c1..b6fbe35985 100644 --- a/resources/src/jquery/jquery.textSelection.js +++ b/resources/src/jquery/jquery.textSelection.js @@ -34,10 +34,11 @@ * Helper function to get an IE TextRange object for an element */ function rangeForElementIE( e ) { + var sel; if ( e.nodeName.toLowerCase() === 'input' ) { return e.createTextRange(); } else { - var sel = document.body.createTextRange(); + sel = document.body.createTextRange(); sel.moveToElementText( e ); return sel; } @@ -275,7 +276,7 @@ var caretPos = 0, endPos = 0, preText, rawPreText, periText, - rawPeriText, postText, rawPostText, + rawPeriText, postText, // IE Support preFinished, periFinished, @@ -310,7 +311,7 @@ // Load the text values we need to compare preText = rawPreText = preRange.text; periText = rawPeriText = periRange.text; - postText = rawPostText = postRange.text; + postText = postRange.text; /* * Check each range for trimmed newlines by shrinking the range by 1 @@ -347,9 +348,7 @@ postFinished = true; } else { postRange.moveEnd( 'character', -1 ); - if ( postRange.text === postText ) { - rawPostText += '\r\n'; - } else { + if ( postRange.text !== postText ) { postFinished = true; } } diff --git a/resources/src/mediawiki.action/mediawiki.action.edit.stash.js b/resources/src/mediawiki.action/mediawiki.action.edit.stash.js index 6c639575da..c8d3fad631 100644 --- a/resources/src/mediawiki.action/mediawiki.action.edit.stash.js +++ b/resources/src/mediawiki.action/mediawiki.action.edit.stash.js @@ -1,6 +1,7 @@ /*! * Scripts for pre-emptive edit preparing on action=edit */ +/* eslint-disable no-use-before-define */ ( function ( mw, $ ) { if ( !mw.config.get( 'wgAjaxEditStash' ) ) { return; @@ -138,10 +139,10 @@ if ( // Reverts may involve use (undo) links; stash as they review the diff. // Since the form has a pre-filled summary, stash the edit immediately. - mw.util.getParamValue( 'undo' ) !== null + mw.util.getParamValue( 'undo' ) !== null || // Pressing "show changes" and "preview" also signify that the user will // probably save the page soon - || $.inArray( $form.find( '#mw-edit-mode' ).val(), [ 'preview', 'diff' ] ) > -1 + $.inArray( $form.find( '#mw-edit-mode' ).val(), [ 'preview', 'diff' ] ) > -1 ) { checkStash(); } diff --git a/resources/src/mediawiki.action/mediawiki.action.view.dblClickEdit.js b/resources/src/mediawiki.action/mediawiki.action.view.dblClickEdit.js index 2be29f0963..7439754668 100644 --- a/resources/src/mediawiki.action/mediawiki.action.view.dblClickEdit.js +++ b/resources/src/mediawiki.action/mediawiki.action.view.dblClickEdit.js @@ -4,11 +4,12 @@ ( function ( mw, $ ) { $( function () { mw.util.$content.dblclick( function ( e ) { + var $a; // Recheck preference so extensions can do a hack to disable this code. if ( parseInt( mw.user.options.get( 'editondblclick' ), 10 ) ) { e.preventDefault(); // Trigger native HTMLElement click instead of opening URL (bug 43052) - var $a = $( '#ca-edit a' ); + $a = $( '#ca-edit a' ); // Not every page has an edit link (bug 57713) if ( $a.length ) { $a.get( 0 ).click(); diff --git a/resources/src/mediawiki.action/mediawiki.action.view.redirect.js b/resources/src/mediawiki.action/mediawiki.action.view.redirect.js index 29a5a79f9b..39a122d981 100644 --- a/resources/src/mediawiki.action/mediawiki.action.view.redirect.js +++ b/resources/src/mediawiki.action/mediawiki.action.view.redirect.js @@ -28,7 +28,7 @@ } // Note that this will update the hash in a modern browser, retaining back behaviour - history.replaceState( /*data=*/ history.state, /*title=*/ document.title, /*url=*/ canonical ); + history.replaceState( /* data= */ history.state, /* title= */ document.title, /* url= */ canonical ); if ( shouldChangeFragment ) { // Specification for history.replaceState() doesn't require browser to scroll, // so scroll to be sure (see also T110501). Support for IE9 and IE10. diff --git a/resources/src/mediawiki.language/languages/ga.js b/resources/src/mediawiki.language/languages/ga.js index a4c911a339..e1362114c1 100644 --- a/resources/src/mediawiki.language/languages/ga.js +++ b/resources/src/mediawiki.language/languages/ga.js @@ -3,7 +3,6 @@ */ mediaWiki.language.convertGrammar = function ( word, form ) { - /*jshint onecase:true */ var grammarForms = mediaWiki.language.getData( 'ga', 'grammarForms' ); if ( grammarForms && grammarForms[ form ] ) { return grammarForms[ form ][ word ]; diff --git a/resources/src/mediawiki.language/languages/he.js b/resources/src/mediawiki.language/languages/he.js index 945f02fed2..5bf8c4df91 100644 --- a/resources/src/mediawiki.language/languages/he.js +++ b/resources/src/mediawiki.language/languages/he.js @@ -21,7 +21,7 @@ mediaWiki.language.convertGrammar = function ( word, form ) { } // Add a hyphen (maqaf) before numbers and non-Hebrew letters - if ( word.slice( 0, 1 ) < 'א' || word.slice( 0, 1 ) > 'ת' ) { + if ( word.slice( 0, 1 ) < 'א' || word.slice( 0, 1 ) > 'ת' ) { word = '־' + word; } } diff --git a/resources/src/mediawiki.language/languages/hy.js b/resources/src/mediawiki.language/languages/hy.js index 935d466d14..bb6f61dffe 100644 --- a/resources/src/mediawiki.language/languages/hy.js +++ b/resources/src/mediawiki.language/languages/hy.js @@ -3,7 +3,6 @@ */ mediaWiki.language.convertGrammar = function ( word, form ) { - /*jshint onecase:true */ var grammarForms = mediaWiki.language.getData( 'hy', 'grammarForms' ); if ( grammarForms && grammarForms[ form ] ) { return grammarForms[ form ][ word ]; diff --git a/resources/src/mediawiki.language/mediawiki.language.js b/resources/src/mediawiki.language/mediawiki.language.js index cf3ef79746..fc2af3d0d8 100644 --- a/resources/src/mediawiki.language/mediawiki.language.js +++ b/resources/src/mediawiki.language/mediawiki.language.js @@ -3,154 +3,155 @@ */ ( function ( mw, $ ) { -/** - * @class mw.language - */ -$.extend( mw.language, { - /** - * Process the PLURAL template substitution - * - * @private - * @param {Object} template Template object - * @param {string} template.title - * @param {Array} template.parameters - * @return {string} + * @class mw.language */ - procPLURAL: function ( template ) { - if ( template.title && template.parameters && mw.language.convertPlural ) { - // Check if we have forms to replace - if ( template.parameters.length === 0 ) { - return ''; + $.extend( mw.language, { + + /** + * Process the PLURAL template substitution + * + * @private + * @param {Object} template Template object + * @param {string} template.title + * @param {Array} template.parameters + * @return {string} + */ + procPLURAL: function ( template ) { + var count; + if ( template.title && template.parameters && mw.language.convertPlural ) { + // Check if we have forms to replace + if ( template.parameters.length === 0 ) { + return ''; + } + // Restore the count into a Number ( if it got converted earlier ) + count = mw.language.convertNumber( template.title, true ); + // Do convertPlural call + return mw.language.convertPlural( parseInt( count, 10 ), template.parameters ); } - // Restore the count into a Number ( if it got converted earlier ) - var count = mw.language.convertNumber( template.title, true ); - // Do convertPlural call - return mw.language.convertPlural( parseInt( count, 10 ), template.parameters ); - } - // Could not process plural return first form or nothing - if ( template.parameters[ 0 ] ) { - return template.parameters[ 0 ]; - } - return ''; - }, + // Could not process plural return first form or nothing + if ( template.parameters[ 0 ] ) { + return template.parameters[ 0 ]; + } + return ''; + }, - /** - * Plural form transformations, needed for some languages. - * - * @param {number} count Non-localized quantifier - * @param {Array} forms List of plural forms - * @param {Object} [explicitPluralForms] List of explicit plural forms - * @return {string} Correct form for quantifier in this language - */ - convertPlural: function ( count, forms, explicitPluralForms ) { - var pluralRules, - pluralFormIndex = 0; + /** + * Plural form transformations, needed for some languages. + * + * @param {number} count Non-localized quantifier + * @param {Array} forms List of plural forms + * @param {Object} [explicitPluralForms] List of explicit plural forms + * @return {string} Correct form for quantifier in this language + */ + convertPlural: function ( count, forms, explicitPluralForms ) { + var pluralRules, + pluralFormIndex = 0; - if ( explicitPluralForms && ( explicitPluralForms[ count ] !== undefined ) ) { - return explicitPluralForms[ count ]; - } + if ( explicitPluralForms && ( explicitPluralForms[ count ] !== undefined ) ) { + return explicitPluralForms[ count ]; + } - if ( !forms || forms.length === 0 ) { - return ''; - } + if ( !forms || forms.length === 0 ) { + return ''; + } - pluralRules = mw.language.getData( mw.config.get( 'wgUserLanguage' ), 'pluralRules' ); - if ( !pluralRules ) { - // default fallback. - return ( count === 1 ) ? forms[ 0 ] : forms[ 1 ]; - } - pluralFormIndex = mw.cldr.getPluralForm( count, pluralRules ); - pluralFormIndex = Math.min( pluralFormIndex, forms.length - 1 ); - return forms[ pluralFormIndex ]; - }, + pluralRules = mw.language.getData( mw.config.get( 'wgUserLanguage' ), 'pluralRules' ); + if ( !pluralRules ) { + // default fallback. + return ( count === 1 ) ? forms[ 0 ] : forms[ 1 ]; + } + pluralFormIndex = mw.cldr.getPluralForm( count, pluralRules ); + pluralFormIndex = Math.min( pluralFormIndex, forms.length - 1 ); + return forms[ pluralFormIndex ]; + }, - /** - * Pads an array to a specific length by copying the last one element. - * - * @private - * @param {Array} forms Number of forms given to convertPlural - * @param {number} count Number of forms required - * @return {Array} Padded array of forms - */ - preConvertPlural: function ( forms, count ) { - while ( forms.length < count ) { - forms.push( forms[ forms.length - 1 ] ); - } - return forms; - }, + /** + * Pads an array to a specific length by copying the last one element. + * + * @private + * @param {Array} forms Number of forms given to convertPlural + * @param {number} count Number of forms required + * @return {Array} Padded array of forms + */ + preConvertPlural: function ( forms, count ) { + while ( forms.length < count ) { + forms.push( forms[ forms.length - 1 ] ); + } + return forms; + }, - /** - * Provides an alternative text depending on specified gender. - * - * Usage in message text: `{{gender:[gender|user object]|masculine|feminine|neutral}}`. - * If second or third parameter are not specified, masculine is used. - * - * These details may be overridden per language. - * - * @param {string} gender 'male', 'female', or anything else for neutral. - * @param {Array} forms List of gender forms - * @return {string} - */ - gender: function ( gender, forms ) { - if ( !forms || forms.length === 0 ) { - return ''; - } - forms = mw.language.preConvertPlural( forms, 2 ); - if ( gender === 'male' ) { - return forms[ 0 ]; - } - if ( gender === 'female' ) { - return forms[ 1 ]; - } - return ( forms.length === 3 ) ? forms[ 2 ] : forms[ 0 ]; - }, + /** + * Provides an alternative text depending on specified gender. + * + * Usage in message text: `{{gender:[gender|user object]|masculine|feminine|neutral}}`. + * If second or third parameter are not specified, masculine is used. + * + * These details may be overridden per language. + * + * @param {string} gender 'male', 'female', or anything else for neutral. + * @param {Array} forms List of gender forms + * @return {string} + */ + gender: function ( gender, forms ) { + if ( !forms || forms.length === 0 ) { + return ''; + } + forms = mw.language.preConvertPlural( forms, 2 ); + if ( gender === 'male' ) { + return forms[ 0 ]; + } + if ( gender === 'female' ) { + return forms[ 1 ]; + } + return ( forms.length === 3 ) ? forms[ 2 ] : forms[ 0 ]; + }, - /** - * Grammatical transformations, needed for inflected languages. - * Invoked by putting `{{grammar:form|word}}` in a message. - * - * The rules can be defined in $wgGrammarForms global or computed - * dynamically by overriding this method per language. - * - * @param {string} word - * @param {string} form - * @return {string} - */ - convertGrammar: function ( word, form ) { - var grammarForms = mw.language.getData( mw.config.get( 'wgUserLanguage' ), 'grammarForms' ); - if ( grammarForms && grammarForms[ form ] ) { - return grammarForms[ form ][ word ] || word; - } - return word; - }, + /** + * Grammatical transformations, needed for inflected languages. + * Invoked by putting `{{grammar:form|word}}` in a message. + * + * The rules can be defined in $wgGrammarForms global or computed + * dynamically by overriding this method per language. + * + * @param {string} word + * @param {string} form + * @return {string} + */ + convertGrammar: function ( word, form ) { + var grammarForms = mw.language.getData( mw.config.get( 'wgUserLanguage' ), 'grammarForms' ); + if ( grammarForms && grammarForms[ form ] ) { + return grammarForms[ form ][ word ] || word; + } + return word; + }, - /** - * Turn a list of string into a simple list using commas and 'and'. - * - * See Language::listToText in languages/Language.php - * - * @param {string[]} list - * @return {string} - */ - listToText: function ( list ) { - var text = '', - i = 0; + /** + * Turn a list of string into a simple list using commas and 'and'. + * + * See Language::listToText in languages/Language.php + * + * @param {string[]} list + * @return {string} + */ + listToText: function ( list ) { + var text = '', + i = 0; - for ( ; i < list.length; i++ ) { - text += list[ i ]; - if ( list.length - 2 === i ) { - text += mw.msg( 'and' ) + mw.msg( 'word-separator' ); - } else if ( list.length - 1 !== i ) { - text += mw.msg( 'comma-separator' ); + for ( ; i < list.length; i++ ) { + text += list[ i ]; + if ( list.length - 2 === i ) { + text += mw.msg( 'and' ) + mw.msg( 'word-separator' ); + } else if ( list.length - 1 !== i ) { + text += mw.msg( 'comma-separator' ); + } } - } - return text; - }, + return text; + }, - setSpecialCharacters: function ( data ) { - this.specialCharacters = data; - } -} ); + setSpecialCharacters: function ( data ) { + this.specialCharacters = data; + } + } ); }( mediaWiki, jQuery ) ); diff --git a/resources/src/mediawiki.language/mediawiki.language.numbers.js b/resources/src/mediawiki.language/mediawiki.language.numbers.js index 268985f8e4..11926508b5 100644 --- a/resources/src/mediawiki.language/mediawiki.language.numbers.js +++ b/resources/src/mediawiki.language/mediawiki.language.numbers.js @@ -15,11 +15,12 @@ * @return {string} */ function replicate( str, num ) { + var buf = []; + if ( num <= 0 || !str ) { return ''; } - var buf = []; while ( num-- ) { buf.push( str ); } @@ -43,12 +44,14 @@ * @return {string} */ function pad( text, size, ch, end ) { + var out, padStr; + if ( !ch ) { ch = '0'; } - var out = String( text ), - padStr = replicate( ch, Math.ceil( ( size - out.length ) / ch.length ) ); + out = String( text ); + padStr = replicate( ch, Math.ceil( ( size - out.length ) / ch.length ) ); return end ? out + padStr : padStr + out; } @@ -69,15 +72,6 @@ * @return {string} */ function commafyNumber( value, pattern, options ) { - options = options || { - group: ',', - decimal: '.' - }; - - if ( isNaN( value ) ) { - return value; - } - var padLength, patternDigits, index, @@ -92,6 +86,15 @@ groupSize2 = 0, pieces = []; + options = options || { + group: ',', + decimal: '.' + }; + + if ( isNaN( value ) ) { + return value; + } + if ( patternParts[ 1 ] ) { // Pad fractional with trailing zeros padLength = ( patternParts[ 1 ] && patternParts[ 1 ].lastIndexOf( '0' ) + 1 ); diff --git a/resources/src/mediawiki.legacy/protect.js b/resources/src/mediawiki.legacy/protect.js index 6226c90b8d..fd3fb00caa 100644 --- a/resources/src/mediawiki.legacy/protect.js +++ b/resources/src/mediawiki.legacy/protect.js @@ -1,242 +1,242 @@ ( function ( mw, $ ) { -var ProtectionForm = window.ProtectionForm = { - /** - * Set up the protection chaining interface (i.e. "unlock move permissions" checkbox) - * on the protection form - */ - init: function () { - var $cell = $( '' ).append( $cell ); - - if ( !$( '#mwProtectSet' ).length ) { - return false; - } - - if ( mw.config.get( 'wgCascadeableLevels' ) !== undefined ) { - $( 'form#mw-Protect-Form' ).submit( this.toggleUnchainedInputs.bind( ProtectionForm, true ) ); - } - this.getExpirySelectors().each( function () { - $( this ).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 ) ); - } ); - - $( '#mwProtectSet > tbody > tr:first' ).after( $row ); - - // If there is only one protection type, there is nothing to chain - if ( $( '[id ^= mw-protect-table-]' ).length > 1 ) { - $cell.append( - $( '' ) - .attr( { id: 'mwProtectUnchained', type: 'checkbox' } ) - .click( this.onChainClick.bind( this ) ) - .prop( 'checked', !this.areAllTypesMatching() ), - document.createTextNode( ' ' ), - $( '' ).append( $cell ); + + if ( !$( '#mwProtectSet' ).length ) { return false; - } else { - $( '#mwProtect-cascade' ).prop( 'disabled', false ); } - } ); - }, - - /** - * Checks if a certain protection level is cascadeable. - * - * @param {string} level - * @return {boolean} - */ - isCascadeableLevel: function ( level ) { - return $.inArray( level, mw.config.get( 'wgCascadeableLevels' ) ) !== -1; - }, - - /** - * When protection levels are locked together, update the rest - * when one action's level changes - * - * @param {Element} source Level selector that changed - */ - updateLevels: function ( source ) { - if ( !this.isUnchained() ) { - this.setAllSelectors( source.selectedIndex ); - } - this.updateCascadeCheckbox(); - }, - - /** - * When protection levels are locked together, update the - * expiries when one changes - * - * @param {Element} source expiry input that changed - */ - - updateExpiry: function ( source ) { - if ( !this.isUnchained() ) { + + if ( mw.config.get( 'wgCascadeableLevels' ) !== undefined ) { + $( 'form#mw-Protect-Form' ).submit( this.toggleUnchainedInputs.bind( ProtectionForm, true ) ); + } + this.getExpirySelectors().each( function () { + $( this ).change( ProtectionForm.updateExpiryList.bind( ProtectionForm, this ) ); + } ); this.getExpiryInputs().each( function () { - this.value = source.value; + $( this ).on( 'keyup change', ProtectionForm.updateExpiry.bind( ProtectionForm, this ) ); } ); - } - if ( this.isUnchained() ) { - $( '#' + source.id.replace( /^mwProtect-(\w+)-expires$/, 'mwProtectExpirySelection-$1' ) ).val( 'othertime' ); - } else { - this.getExpirySelectors().each( function () { - this.value = 'othertime'; + this.getLevelSelectors().each( function () { + $( this ).change( ProtectionForm.updateLevels.bind( ProtectionForm, this ) ); } ); - } - }, - - /** - * When protection levels are locked together, update the - * expiry lists when one changes and clear the custom inputs - * - * @param {Element} source Expiry selector that changed - */ - updateExpiryList: function ( source ) { - if ( !this.isUnchained() ) { - this.getExpirySelectors().each( function () { - this.value = source.value; + + $( '#mwProtectSet > tbody > tr:first' ).after( $row ); + + // If there is only one protection type, there is nothing to chain + if ( $( '[id ^= mw-protect-table-]' ).length > 1 ) { + $cell.append( + $( '' ) + .attr( { id: 'mwProtectUnchained', type: 'checkbox' } ) + .click( this.onChainClick.bind( this ) ) + .prop( 'checked', !this.areAllTypesMatching() ), + document.createTextNode( ' ' ), + $( '
' ), - $row = $( '
' ), + $row = $( '
' ); $( '' ) - .append( $( '#' ).css( 'width', '4em' ) ) + .append( $( '#' ).css( 'width', '4em' ) ) .append( $( 'SQL' ) ) - .append( $( 'Time' ).css( 'width', '8em' ) ) + .append( $( 'Time' ).css( 'width', '8em' ) ) .append( $( 'Call' ).css( 'width', '18em' ) ) .appendTo( $table ); diff --git a/resources/src/mediawiki/mediawiki.experiments.js b/resources/src/mediawiki/mediawiki.experiments.js index 49ff4115b5..0c9ea97c14 100644 --- a/resources/src/mediawiki/mediawiki.experiments.js +++ b/resources/src/mediawiki/mediawiki.experiments.js @@ -1,4 +1,3 @@ -/* jshint bitwise:false */ ( function ( mw, $ ) { var CONTROL_BUCKET = 'control', @@ -17,6 +16,7 @@ * @see https://jsbin.com/kejewi/4/watch?js,console */ function hashString( string ) { + /* eslint-disable no-bitwise */ var hash = 0, i = string.length; @@ -30,6 +30,7 @@ hash += ( hash << 15 ); return hash >>> 0; + /* eslint-enable no-bitwise */ } /** diff --git a/resources/src/mediawiki/mediawiki.feedback.js b/resources/src/mediawiki/mediawiki.feedback.js index e0785b6100..6abdf83877 100644 --- a/resources/src/mediawiki/mediawiki.feedback.js +++ b/resources/src/mediawiki/mediawiki.feedback.js @@ -6,7 +6,6 @@ * @author Moriel Schottlender, 2015 * @since 1.19 */ -/*global OO*/ ( function ( mw, $ ) { /** * This is a way of getting simple feedback from users. It's useful @@ -295,7 +294,7 @@ this.feedbackSubjectInput.getValue() ); - this.actions.setAbilities( { submit: isValid } ); + this.actions.setAbilities( { submit: isValid } ); }; /** @@ -457,10 +456,10 @@ if ( secondaryCode === 'http' ) { fb.status = 'error3'; // ajax request failed - mw.log.warn( 'Feedback report failed with HTTP error: ' + details.textStatus ); + mw.log.warn( 'Feedback report failed with HTTP error: ' + details.textStatus ); } else { fb.status = 'error2'; - mw.log.warn( 'Feedback report failed with API error: ' + secondaryCode ); + mw.log.warn( 'Feedback report failed with API error: ' + secondaryCode ); } } else { fb.status = 'error1'; diff --git a/resources/src/mediawiki/mediawiki.filewarning.js b/resources/src/mediawiki/mediawiki.filewarning.js index 882affe17f..72bf3d7696 100644 --- a/resources/src/mediawiki/mediawiki.filewarning.js +++ b/resources/src/mediawiki/mediawiki.filewarning.js @@ -4,7 +4,6 @@ * @author Mark Holmquist, 2015 * @since 1.25 */ -/*global OO*/ ( function ( mw, $, oo ) { var warningConfig = mw.config.get( 'wgFileWarning' ), warningMessages = warningConfig.messages, diff --git a/resources/src/mediawiki/mediawiki.inspect.js b/resources/src/mediawiki/mediawiki.inspect.js index a74aef3b3c..249430585a 100644 --- a/resources/src/mediawiki/mediawiki.inspect.js +++ b/resources/src/mediawiki/mediawiki.inspect.js @@ -4,7 +4,9 @@ * @author Ori Livneh * @since 1.22 */ -/*jshint devel:true */ + +/* eslint-disable no-console */ + ( function ( mw, $ ) { var inspect, @@ -18,11 +20,12 @@ } function humanSize( bytes ) { - if ( !$.isNumeric( bytes ) || bytes === 0 ) { return bytes; } - var i = 0, + var i, units = [ '', ' KiB', ' MiB', ' GiB', ' TiB', ' PiB' ]; - for ( ; bytes >= 1024; bytes /= 1024 ) { i++; } + if ( !$.isNumeric( bytes ) || bytes === 0 ) { return bytes; } + + for ( i = 0; bytes >= 1024; bytes /= 1024 ) { i++; } // Maintain one decimal for kB and above, but don't // add ".0" for bytes. return bytes.toFixed( i > 0 ? 1 : 0 ) + units[ i ]; @@ -229,7 +232,7 @@ allSelectors: stats.total, matchedSelectors: stats.matched, percentMatched: stats.total !== 0 ? - ( stats.matched / stats.total * 100 ).toFixed( 2 ) + '%' : null + ( stats.matched / stats.total * 100 ).toFixed( 2 ) + '%' : null } ); } ); sortByProperty( modules, 'allSelectors', true ); @@ -247,7 +250,7 @@ $.extend( stats, mw.loader.store.stats ); try { raw = localStorage.getItem( mw.loader.store.getStoreKey() ); - stats.totalSizeInBytes = $.byteLength( raw ); + stats.totalSizeInBytes = $.byteLength( raw ); stats.totalSize = humanSize( $.byteLength( raw ) ); } catch ( e ) {} } @@ -278,8 +281,8 @@ // Grep module's CSS if ( - $.isPlainObject( module.style ) && $.isArray( module.style.css ) - && pattern.test( module.style.css.join( '' ) ) + $.isPlainObject( module.style ) && $.isArray( module.style.css ) && + pattern.test( module.style.css.join( '' ) ) ) { // Module's CSS source matches return true; diff --git a/resources/src/mediawiki/mediawiki.jqueryMsg.js b/resources/src/mediawiki/mediawiki.jqueryMsg.js index cad59b8f87..8504964685 100644 --- a/resources/src/mediawiki/mediawiki.jqueryMsg.js +++ b/resources/src/mediawiki/mediawiki.jqueryMsg.js @@ -136,6 +136,7 @@ function getFailableParserFn( options ) { return function ( args ) { var fallback, + // eslint-disable-next-line new-cap parser = new mw.jqueryMsg.parser( options ), key = args[ 0 ], argsArray = $.isArray( args[ 1 ] ) ? args[ 1 ] : slice.call( args, 1 ); @@ -212,10 +213,11 @@ } return function () { + var failableResult; if ( !failableParserFn ) { failableParserFn = getFailableParserFn( options ); } - var failableResult = failableParserFn( arguments ); + failableResult = failableParserFn( arguments ); if ( format === 'text' || format === 'escaped' ) { return failableResult.text(); } else { @@ -250,10 +252,11 @@ var failableParserFn; return function () { + var $target; if ( !failableParserFn ) { failableParserFn = getFailableParserFn( options ); } - var $target = this.empty(); + $target = this.empty(); appendWithoutParsing( $target, failableParserFn( arguments ) ); return $target; }; @@ -272,6 +275,7 @@ this.settings.onlyCurlyBraceTransform = ( this.settings.format === 'text' || this.settings.format === 'escaped' ); this.astCache = {}; + // eslint-disable-next-line new-cap this.emitter = new mw.jqueryMsg.htmlEmitter( this.settings.language, this.settings.magic ); }; @@ -935,8 +939,8 @@ * htmlEmitter - object which primarily exists to emit HTML from parser ASTs */ mw.jqueryMsg.htmlEmitter = function ( language, magic ) { - this.language = language; var jmsg = this; + this.language = language; $.each( magic, function ( key, val ) { jmsg[ key.toLowerCase() ] = function () { return val; @@ -1275,7 +1279,7 @@ * @return {number|string} Formatted number */ formatnum: function ( nodes ) { - var isInteger = ( nodes[ 1 ] && nodes[ 1 ] === 'R' ) ? true : false, + var isInteger = !!nodes[ 1 ] && nodes[ 1 ] === 'R', number = nodes[ 0 ]; return this.language.convertNumber( number, isInteger ); @@ -1371,6 +1375,6 @@ return function () { return reusableParent.msg( this.key, this.parameters ).contents().detach(); }; - } )(); + }() ); }( mediaWiki, jQuery ) ); diff --git a/resources/src/mediawiki/mediawiki.js b/resources/src/mediawiki/mediawiki.js index d5258131ae..4dce192cf3 100644 --- a/resources/src/mediawiki/mediawiki.js +++ b/resources/src/mediawiki/mediawiki.js @@ -7,7 +7,9 @@ * @alternateClassName mediaWiki * @singleton */ -/*jshint latedef:false */ + +/* eslint-disable no-use-before-define */ + ( function ( $ ) { 'use strict'; @@ -31,7 +33,7 @@ * @return {string} hash as an seven-character base 36 string */ function fnv132( str ) { - /*jshint bitwise:false */ + /* eslint-disable no-bitwise */ var hash = 0x811C9DC5, i; @@ -46,6 +48,7 @@ } return hash; + /* eslint-enable no-bitwise */ } StringSet = window.Set || ( function () { @@ -463,9 +466,9 @@ log.deprecate = !Object.defineProperty ? function ( obj, key, val ) { obj[ key ] = val; } : function ( obj, key, val, msg, logName ) { + var logged = new StringSet(); logName = logName || key; msg = 'Use of "' + logName + '" is deprecated.' + ( msg ? ( ' ' + msg ) : '' ); - var logged = new StringSet(); function uniqueTrace() { var trace = new Error().stack; if ( logged.has( trace ) ) { @@ -1112,7 +1115,7 @@ } if ( registry[ module ].skip !== null ) { - /*jshint evil:true */ + // eslint-disable-next-line no-new-func skip = new Function( registry[ module ].skip ); registry[ module ].skip = null; if ( skip() ) { @@ -1152,7 +1155,7 @@ ) ); } - unresolved.add( module ); + unresolved.add( module ); sortDependencies( deps[ i ], resolved, unresolved ); } } @@ -1297,9 +1300,9 @@ } ); }; - implicitDependencies = ( $.inArray( module, legacyModules ) !== -1 ) - ? [] - : legacyModules; + implicitDependencies = ( $.inArray( module, legacyModules ) !== -1 ) ? + [] : + legacyModules; if ( module === 'user' ) { // Implicit dependency on the site module. Not real dependency because @@ -1307,9 +1310,9 @@ implicitDependencies.push( 'site' ); } - legacyWait = implicitDependencies.length - ? mw.loader.using( implicitDependencies ) - : $.Deferred().resolve(); + legacyWait = implicitDependencies.length ? + mw.loader.using( implicitDependencies ) : + $.Deferred().resolve(); legacyWait.always( function () { try { @@ -1638,9 +1641,9 @@ prefix = modules[ i ].substr( 0, lastDotIndex ); suffix = modules[ i ].slice( lastDotIndex + 1 ); - bytesAdded = hasOwn.call( moduleMap, prefix ) - ? suffix.length + 3 // '%2C'.length == 3 - : modules[ i ].length + 3; // '%7C'.length == 3 + bytesAdded = hasOwn.call( moduleMap, prefix ) ? + suffix.length + 3 : // '%2C'.length == 3 + modules[ i ].length + 3; // '%7C'.length == 3 // If the url would become too long, create a new one, // but don't create empty requests @@ -1780,6 +1783,7 @@ return true; } ); asyncEval( implementations, function ( err ) { + var failed; // Not good, the cached mw.loader.implement calls failed! This should // never happen, barring ResourceLoader bugs, browser bugs and PEBKACs. // Depending on how corrupt the string is, it is likely that some @@ -1794,7 +1798,7 @@ mw.track( 'resourceloader.exception', { exception: err, source: 'store-eval' } ); // Re-add the failed ones that are still pending back to the batch - var failed = $.grep( sourceModules, function ( module ) { + failed = $.grep( sourceModules, function ( module ) { return registry[ module ].state === 'loading'; } ); batchRequest( failed ); @@ -2329,7 +2333,7 @@ // Partial descriptor // (e.g. skipped module, or style module with state=ready) $.inArray( undefined, [ descriptor.script, descriptor.style, - descriptor.messages, descriptor.templates ] ) !== -1 + descriptor.messages, descriptor.templates ] ) !== -1 ) { // Decline to store return false; diff --git a/resources/src/mediawiki/mediawiki.log.js b/resources/src/mediawiki/mediawiki.log.js index c886817799..4d23604ca1 100644 --- a/resources/src/mediawiki/mediawiki.log.js +++ b/resources/src/mediawiki/mediawiki.log.js @@ -52,11 +52,11 @@ if ( !$log.length ) { $log = $( '
' ).css( { - overflow: 'auto', - height: '150px', - backgroundColor: 'white', - borderTop: 'solid 2px #ADADAD' - } ); + overflow: 'auto', + height: '150px', + backgroundColor: 'white', + borderTop: 'solid 2px #ADADAD' + } ); hovzer = $.getFootHovzer(); hovzer.$.append( $log ); hovzer.update(); diff --git a/resources/src/mediawiki/mediawiki.notification.js b/resources/src/mediawiki/mediawiki.notification.js index 36b45f177f..6c6f5607f2 100644 --- a/resources/src/mediawiki/mediawiki.notification.js +++ b/resources/src/mediawiki/mediawiki.notification.js @@ -25,7 +25,7 @@ * @private */ function Notification( message, options ) { - var $notification, $notificationTitle, $notificationContent; + var $notification, $notificationContent; $notification = $( '
' ) .data( 'mw.notification', this ) @@ -48,7 +48,7 @@ } if ( options.title ) { - $notificationTitle = $( '
' ) + $( '
' ) .text( options.title ) .appendTo( $notification ); } @@ -227,7 +227,7 @@ $area.hide(); notif.$notification.remove(); } else { - notif.$notification.slideUp( 'fast', function () { + notif.$notification.slideUp( 'fast', function () { $( this ).remove(); } ); } diff --git a/resources/src/mediawiki/mediawiki.template.mustache.js b/resources/src/mediawiki/mediawiki.template.mustache.js index 7f62256adf..c5e96eb4e7 100644 --- a/resources/src/mediawiki/mediawiki.template.mustache.js +++ b/resources/src/mediawiki/mediawiki.template.mustache.js @@ -1,4 +1,4 @@ -/*global Mustache */ +/* global Mustache */ ( function ( mw, $ ) { // Register mustache compiler mw.template.registerCompiler( 'mustache', { diff --git a/resources/src/mediawiki/mediawiki.user.js b/resources/src/mediawiki/mediawiki.user.js index 63e7de87ef..240d1bd1b1 100644 --- a/resources/src/mediawiki/mediawiki.user.js +++ b/resources/src/mediawiki/mediawiki.user.js @@ -2,6 +2,7 @@ * @class mw.user * @singleton */ +/* global Uint8Array */ ( function ( mw, $ ) { var i, userInfoPromise, @@ -50,7 +51,7 @@ * @return {string} 64 bit integer in hex format, padded */ generateRandomSessionId: function () { - /*jshint bitwise:false */ + /* eslint-disable no-bitwise */ var rnds, i, r, hexRnds = new Array( 8 ), // Support: IE 11 @@ -79,6 +80,7 @@ // Concatenation of two random integers with entropy n and m // returns a string with entropy n+m if those strings are independent return hexRnds.join( '' ); + /* eslint-enable no-bitwise */ }, /** @@ -108,10 +110,11 @@ * unavailable, or Date for when the user registered. */ getRegistration: function () { + var registration; if ( mw.user.isAnon() ) { return false; } - var registration = mw.config.get( 'wgUserRegistration' ); + registration = mw.config.get( 'wgUserRegistration' ); // Registration may be unavailable if the user signed up before MediaWiki // began tracking this. return !registration ? null : new Date( registration ); diff --git a/resources/src/mediawiki/mediawiki.util.js b/resources/src/mediawiki/mediawiki.util.js index 654f232c3d..bee22c7d3d 100644 --- a/resources/src/mediawiki/mediawiki.util.js +++ b/resources/src/mediawiki/mediawiki.util.js @@ -124,9 +124,9 @@ query = $.param( params ); } if ( query ) { - url = title - ? util.wikiScript() + '?title=' + util.wikiUrlencode( title ) + '&' + query - : util.wikiScript() + '?' + query; + url = title ? + util.wikiScript() + '?title=' + util.wikiUrlencode( title ) + '&' + query : + util.wikiScript() + '?' + query; } else { url = mw.config.get( 'wgArticlePath' ) .replace( '$1', util.wikiUrlencode( title ).replace( /\$/g, '$$$$' ) ); @@ -188,12 +188,10 @@ * @return {Mixed} Parameter value or null. */ getParamValue: function ( param, url ) { - if ( url === undefined ) { - url = location.href; - } // Get last match, stop at hash var re = new RegExp( '^[^#]*[&?]' + mw.RegExp.escape( param ) + '=([^&#]*)' ), - m = re.exec( url ); + m = re.exec( url !== undefined ? url : location.href ); + if ( m ) { // Beware that decodeURIComponent is not required to understand '+' // by spec, as encodeURIComponent does not produce it. @@ -414,20 +412,15 @@ html5EmailRegexp = new RegExp( // start of string - '^' - + + '^' + // User part which is liberal :p - '[' + rfc5322Atext + '\\.]+' - + + '[' + rfc5322Atext + '\\.]+' + // 'at' - '@' - + + '@' + // Domain first part - '[' + rfc1034LdhStr + ']+' - + + '[' + rfc1034LdhStr + ']+' + // Optional second part and following are separated by a dot - '(?:\\.[' + rfc1034LdhStr + ']+)*' - + + '(?:\\.[' + rfc1034LdhStr + ']+)*' + // End of string '$', // RegExp is case insensitive @@ -444,13 +437,15 @@ * @return {boolean} */ isIPv4Address: function ( address, allowBlock ) { + var block, RE_IP_BYTE, RE_IP_ADD; + if ( typeof address !== 'string' ) { return false; } - var block = allowBlock ? '(?:\\/(?:3[0-2]|[12]?\\d))?' : '', - RE_IP_BYTE = '(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|0?[0-9]?[0-9])', - RE_IP_ADD = '(?:' + RE_IP_BYTE + '\\.){3}' + RE_IP_BYTE; + block = allowBlock ? '(?:\\/(?:3[0-2]|[12]?\\d))?' : ''; + RE_IP_BYTE = '(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|0?[0-9]?[0-9])'; + RE_IP_ADD = '(?:' + RE_IP_BYTE + '\\.){3}' + RE_IP_BYTE; return ( new RegExp( '^' + RE_IP_ADD + block + '$' ).test( address ) ); }, @@ -463,19 +458,21 @@ * @return {boolean} */ isIPv6Address: function ( address, allowBlock ) { + var block, RE_IPV6_ADD; + if ( typeof address !== 'string' ) { return false; } - var block = allowBlock ? '(?:\\/(?:12[0-8]|1[01][0-9]|[1-9]?\\d))?' : '', - RE_IPV6_ADD = - '(?:' + // starts with "::" (including "::") - ':(?::|(?::' + '[0-9A-Fa-f]{1,4}' + '){1,7})' + - '|' + // ends with "::" (except "::") - '[0-9A-Fa-f]{1,4}' + '(?::' + '[0-9A-Fa-f]{1,4}' + '){0,6}::' + - '|' + // contains no "::" - '[0-9A-Fa-f]{1,4}' + '(?::' + '[0-9A-Fa-f]{1,4}' + '){7}' + - ')'; + block = allowBlock ? '(?:\\/(?:12[0-8]|1[01][0-9]|[1-9]?\\d))?' : ''; + RE_IPV6_ADD = + '(?:' + // starts with "::" (including "::") + ':(?::|(?::' + '[0-9A-Fa-f]{1,4}' + '){1,7})' + + '|' + // ends with "::" (except "::") + '[0-9A-Fa-f]{1,4}' + '(?::' + '[0-9A-Fa-f]{1,4}' + '){0,6}::' + + '|' + // contains no "::" + '[0-9A-Fa-f]{1,4}' + '(?::' + '[0-9A-Fa-f]{1,4}' + '){7}' + + ')'; if ( new RegExp( '^' + RE_IPV6_ADD + block + '$' ).test( address ) ) { return true; @@ -485,9 +482,9 @@ RE_IPV6_ADD = '[0-9A-Fa-f]{1,4}' + '(?:::?' + '[0-9A-Fa-f]{1,4}' + '){1,6}'; return ( - new RegExp( '^' + RE_IPV6_ADD + block + '$' ).test( address ) - && /::/.test( address ) - && !/::.*::/.test( address ) + new RegExp( '^' + RE_IPV6_ADD + block + '$' ).test( address ) && + /::/.test( address ) && + !/::.*::/.test( address ) ); }, diff --git a/resources/src/mediawiki/mediawiki.viewport.js b/resources/src/mediawiki/mediawiki.viewport.js index 6396331f90..cb1e73fa30 100644 --- a/resources/src/mediawiki/mediawiki.viewport.js +++ b/resources/src/mediawiki/mediawiki.viewport.js @@ -85,7 +85,7 @@ */ isElementCloseToViewport: function ( el, threshold, rectangle ) { var viewport = rectangle ? $.extend( {}, rectangle ) : this.makeViewportFromWindow(); - threshold = threshold || 50 ; + threshold = threshold || 50; viewport.top -= threshold; viewport.left -= threshold; diff --git a/resources/src/mediawiki/page/gallery-slideshow.js b/resources/src/mediawiki/page/gallery-slideshow.js index 26f568b2cf..be75a2e80f 100644 --- a/resources/src/mediawiki/page/gallery-slideshow.js +++ b/resources/src/mediawiki/page/gallery-slideshow.js @@ -335,7 +335,7 @@ if ( this.imageInfoCache[ imageSrc ] === undefined ) { api = new mw.Api(); // TODO: This supports only gallery of images - title = new mw.Title.newFromImg( $img ); + title = mw.Title.newFromImg( $img ); params = { action: 'query', formatversion: 2, @@ -452,9 +452,8 @@ // Bootstrap all slideshow galleries mw.hook( 'wikipage.content' ).add( function ( $content ) { $content.find( '.mw-gallery-slideshow' ).each( function () { - /*jshint -W031 */ + // eslint-disable-next-line no-new new mw.GallerySlideshow( this ); - /*jshint +W031 */ } ); } ); }( mediaWiki, jQuery, OO ) ); diff --git a/resources/src/mediawiki/page/image-pagination.js b/resources/src/mediawiki/page/image-pagination.js index 02bc1de949..6038a574c8 100644 --- a/resources/src/mediawiki/page/image-pagination.js +++ b/resources/src/mediawiki/page/image-pagination.js @@ -1,8 +1,10 @@ /*! * Implement AJAX navigation for multi-page images so the user may browse without a full page reload. */ + +/* eslint-disable no-use-before-define */ + ( function ( mw, $ ) { - /*jshint latedef:false */ var jqXhr, $multipageimage, $spinner, cache = {}, cacheOrder = []; diff --git a/resources/src/mediawiki/page/patrol.ajax.js b/resources/src/mediawiki/page/patrol.ajax.js index 89bbbe79d8..6d6d46dd94 100644 --- a/resources/src/mediawiki/page/patrol.ajax.js +++ b/resources/src/mediawiki/page/patrol.ajax.js @@ -35,11 +35,12 @@ rcid: rcid } ) .done( function ( data ) { + var title; // Remove all patrollinks from the page (including any spinners inside). $patrolLinks.closest( '.patrollink' ).remove(); if ( data.patrol !== undefined ) { // Success - var title = new mw.Title( data.patrol.title ); + title = new mw.Title( data.patrol.title ); mw.notify( mw.msg( 'markedaspatrollednotify', title.toText() ) ); } else { // This should never happen as errors should trigger fail diff --git a/resources/src/mediawiki/page/rollback.js b/resources/src/mediawiki/page/rollback.js index 83d14b3ed3..cb46b110dc 100644 --- a/resources/src/mediawiki/page/rollback.js +++ b/resources/src/mediawiki/page/rollback.js @@ -43,9 +43,9 @@ } $( e.delegateTarget ).remove(); }, function ( errorCode, data ) { - var message = data && data.error && data.error.messageHtml - ? $.parseHTML( data.error.messageHtml ) - : mw.msg( 'rollbackfailed' ), + var message = data && data.error && data.error.messageHtml ? + $.parseHTML( data.error.messageHtml ) : + mw.msg( 'rollbackfailed' ), type = errorCode === 'alreadyrolled' ? 'warn' : 'error'; mw.notify( message, { diff --git a/resources/src/mediawiki/page/startup.js b/resources/src/mediawiki/page/startup.js index 282799ad36..076357a40c 100644 --- a/resources/src/mediawiki/page/startup.js +++ b/resources/src/mediawiki/page/startup.js @@ -3,6 +3,7 @@ mw.page = {}; $( function () { + var $diff; mw.util.init(); /** @@ -23,7 +24,7 @@ */ mw.hook( 'wikipage.content' ).fire( $( '#mw-content-text' ) ); - var $diff = $( 'table.diff[data-mw="interface"]' ); + $diff = $( 'table.diff[data-mw="interface"]' ); if ( $diff.length ) { /** * Fired when the diff is added to a page containing a diff diff --git a/resources/src/moment-dmy.js b/resources/src/moment-dmy.js index c67b93e976..0abb9574bf 100644 --- a/resources/src/moment-dmy.js +++ b/resources/src/moment-dmy.js @@ -1,7 +1,7 @@ // Use DMY date format for Moment.js, in accordance with MediaWiki's date formatting routines. // This affects English only (and languages without localisations, that fall back to English). // http://momentjs.com/docs/#/customization/long-date-formats/ -/*global moment */ +/* global moment */ moment.locale( 'en', { longDateFormat: { // Unchanged, but have to be repeated here: diff --git a/resources/src/moment-locale-overrides.js b/resources/src/moment-locale-overrides.js index dd33b007f6..13f26f07dc 100644 --- a/resources/src/moment-locale-overrides.js +++ b/resources/src/moment-locale-overrides.js @@ -1,4 +1,4 @@ -/*global moment, mw */ +/* global moment, mw */ // HACK: Overwrite moment's i18n with MediaWiki's for the current language so that // wgTranslateNumerals is respected. diff --git a/resources/src/polyfill-nodeTypes.js b/resources/src/polyfill-nodeTypes.js index 5f52d1e309..c8acc86df4 100644 --- a/resources/src/polyfill-nodeTypes.js +++ b/resources/src/polyfill-nodeTypes.js @@ -4,16 +4,16 @@ */ window.Node = window.Node || { - ELEMENT_NODE: 1, - ATTRIBUTE_NODE: 2, - TEXT_NODE: 3, - CDATA_SECTION_NODE: 4, - ENTITY_REFERENCE_NODE: 5, - ENTITY_NODE: 6, + ELEMENT_NODE: 1, + ATTRIBUTE_NODE: 2, + TEXT_NODE: 3, + CDATA_SECTION_NODE: 4, + ENTITY_REFERENCE_NODE: 5, + ENTITY_NODE: 6, PROCESSING_INSTRUCTION_NODE: 7, - COMMENT_NODE: 8, - DOCUMENT_NODE: 9, - DOCUMENT_TYPE_NODE: 10, - DOCUMENT_FRAGMENT_NODE: 11, - NOTATION_NODE: 12 + COMMENT_NODE: 8, + DOCUMENT_NODE: 9, + DOCUMENT_TYPE_NODE: 10, + DOCUMENT_FRAGMENT_NODE: 11, + NOTATION_NODE: 12 }; diff --git a/resources/src/startup.js b/resources/src/startup.js index 5e05590a44..e3094d2e61 100644 --- a/resources/src/startup.js +++ b/resources/src/startup.js @@ -3,11 +3,11 @@ * * This file is where we decide whether to initialise the modern run-time. */ -/*jshint unused: false */ -/*globals mw, RLQ: true, NORLQ: true, $VARS, $CODE, performance */ -var mediaWikiLoadStart = ( new Date() ).getTime(), +/* global mw, $VARS, $CODE */ +// eslint-disable-next-line no-unused-vars +var mediaWikiLoadStart = ( new Date() ).getTime(), mwPerformance = ( window.performance && performance.mark ) ? performance : { mark: function () {} }; @@ -51,19 +51,19 @@ function isCompatible( str ) { var ua = str || navigator.userAgent; return !!( // http://caniuse.com/#feat=queryselector - 'querySelector' in document + 'querySelector' in document && // http://caniuse.com/#feat=namevalue-storage // https://developer.blackberry.com/html5/apis/v1_0/localstorage.html // https://blog.whatwg.org/this-week-in-html-5-episode-30 - && 'localStorage' in window + 'localStorage' in window && // http://caniuse.com/#feat=addeventlistener - && 'addEventListener' in window + 'addEventListener' in window && // Hardcoded exceptions for browsers that pass the requirement but we don't want to // support in the modern run-time. - && !( + !( ua.match( /webOS\/1\.[0-4]|SymbianOS|Series60|NetFront|Opera Mini|S40OviBrowser|MeeGo|Android.+Glass/ ) || ua.match( /PlayStation/i ) || // UC Mini (speed mode on) @@ -112,6 +112,7 @@ function isCompatible( str ) { // Must be after mw.config.set because these callbacks may use mw.loader which // needs to have values 'skin', 'debug' etc. from mw.config. + // eslint-disable-next-line vars-on-top var RLQ = window.RLQ || []; while ( RLQ.length ) { RLQ.shift()();