From: Timo Tijhof Date: Wed, 26 Sep 2012 07:14:52 +0000 (+0200) Subject: Lint: Go-go-gadget jshint! Passing entire JS code base (again). X-Git-Tag: 1.31.0-rc.0~21681^2 X-Git-Url: https://git.heureux-cyclage.org/?a=commitdiff_plain;ds=sidebyside;h=8d306686cf733f3cc424a41959d284827593d540;p=lhc%2Fweb%2Fwiklou.git Lint: Go-go-gadget jshint! Passing entire JS code base (again). There were still some files not passing jshint, and for files that did, we managed to screw 'em up again. Added more explicit settings in .jshintrc to avoid relying on a kind of default somewhere. There are too many default-factors: closest(.jshintrc), ~/.jshintrc, IDE/editor, node-jshint.. Added node_modules/ and extensions/ to .jshintignore. Previously "$ jshint ." would recurse over all kinds of unrelated code. Extensions should have their own jshint dotfiles. When linting from Jenkins this won't be a problem as those will be ran per repo (so when linting core it will skip extensions and when in an extension dir, the core dotfiles don't apply as they'll be out of scope). Some of our modules are really messy and should be refactored to be less spaghetti-ish and have more descriptive variable names and more manageable function-level complexity. But for this commit, I'm keeping it as much as-is as possible, because its hard/large enough to review as it is. A few errors are cited below to give an impression of the kind of warnings I addressed (for cases where the diff isn't so obvious): * jquery.hidpi.js: line 110, col 15, Empty block. * mediawiki.jqueryMsg.js: line 34, col 17, Too many var statements. * mediawiki.jqueryMsg.js: line 145, col 33, Strings must use singlequote. * mediawiki.action.edit.js: line 74, col 73, 'selectText' is defined but never used. * startup.js: line 19, col 22, 'isCompatible' is defined but never used. * jquery.byteLength.test.js: line 26, col 9, Identifier 'U_00A2' is not in camel case. * jquery.localize.test.js: line 63, col 29, 'attrs' is defined but never used. * mediawiki.cldr.test.js: line 72, col 27, 'mw' is not defined. * mediawiki.jscompat.test.js: line 6, col 17, Strings must use singlequote. * mediawiki.api.parse.test.js: line 9, col 17, Strings must use singlequote. * mediawiki.api.parse.test.js: line 7, col 15, 'mw' is not defined. * mediawiki.api.parse.test.js: line 14, col 24, '$' is not defined. * mediawiki.api.test.js: line 43, col 28, 'data' is defined but never used. Other fixes: * Add closures fix implied global errors ($, mw and more), and prevents local variables from becoming globals. * Avoid jQ magic map arg etc. (per code conventions). * Fix incorrect usage of jQuery methods (prop instead of attr, prop false instead of removeProp, ..). * Unquote keys in object literals for consistency, and enforce single quotes (no magic quotes in javascript, as much as we might think "\n" and '/n' are really exactly the same). Chose single quotes over double quotes since that's what most code already had and what conventions seemed to prefer (both the old generic ones and the new per-lang ones since 2011/2012). * Enforce camelCase variable names with jshint per code conventions. * $foo.on('x', fn).trigger('x') -> $foo.on('x', fn); fn() (No event simulation overhead, unless intended of course) * Incorrect indentation (ignore whitespace in the diff!). * Avoid proprietary selectors like ':first' when .eq(0) afterwards is just as possible (significantly faster in jQuery due to mostly avoiding the Sizzle engine and going native in modern browsers). * When at it, convert deprecated jQuery methods to new ones. Mostly just .delegate(sel, type, fn) -> .on(type, sel, fn). * Addressed whitespace here and there. Interesting: * mediawiki.js: local function "compare" wasn't used anymore (hasn't been in a while!) removed per jshint warning. * mediawiki.special.recentchanges.js: Was a mess, only a few lines of code, rewritten. Pfew, let's hope it's the last one before we lint from Jenkins! Change-Id: I23ad60a1d804c542d9b91454aaa20ce7be4ff289 --- diff --git a/.gitignore b/.gitignore index 7f1ac5e5b6..a47a454f77 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,9 @@ AdminSettings.php LocalSettings.php StartProfiler.php +# Building & testing +node_modules/ + # Operating systems ## Mac OS X .DS_Store diff --git a/.jshintignore b/.jshintignore index 026eaaaf85..3869deb913 100644 --- a/.jshintignore +++ b/.jshintignore @@ -1,4 +1,6 @@ -# upstream libs +# third-party libs +extensions/ +node_modules/ resources/jquery/jquery.appear.js resources/jquery/jquery.async.js resources/jquery/jquery.cycle.all.js @@ -13,9 +15,13 @@ resources/jquery/jquery.mockjax.js resources/jquery/jquery.qunit.js resources/jquery/jquery.validate.js resources/jquery/jquery.xmldom.js -resources/jquery.effects -resources/jquery.tipsy -resources/jquery.ui -resources/mediawiki.libs -tests/jasmine/lib/jasmine-1.0.1/jasmine-html.js -tests/jasmine/lib/jasmine-1.0.1/jasmine.js +resources/jquery.effects/ +resources/jquery.tipsy/ +resources/jquery.ui/ +resources/mediawiki.libs/ + +# legacy scripts +skins/common/ + +# github.com/jshint/jshint/issues/729 +tests/qunit/suites/resources/mediawiki/mediawiki.jscompat.test.js diff --git a/.jshintrc b/.jshintrc index b86ceb5f0c..7fa138d44f 100644 --- a/.jshintrc +++ b/.jshintrc @@ -1,20 +1,25 @@ { "predef": [ "mediaWiki", + "jQuery", "QUnit" ], "bitwise": true, + "camelcase": true, "curly": true, "eqeqeq": true, + "forin": false, "immed": true, "latedef": true, "newcap": true, "noarg": true, "noempty": true, "nonew": true, + "quotmark": "single", "regexp": false, "undef": true, + "unused": true, "strict": false, "trailing": true, @@ -23,7 +28,6 @@ "multistr": true, "browser": true, - "jquery": true, "nomen": true, "onevar": true diff --git a/resources/jquery/jquery.checkboxShiftClick.js b/resources/jquery/jquery.checkboxShiftClick.js index 1990dc0d73..aced0633dc 100644 --- a/resources/jquery/jquery.checkboxShiftClick.js +++ b/resources/jquery/jquery.checkboxShiftClick.js @@ -1,14 +1,16 @@ /** * jQuery checkboxShiftClick * - * This will enable checkboxes to be checked or unchecked in a row by clicking one, holding shift and clicking another one + * This will enable checkboxes to be checked or unchecked in a row by clicking one, + * holding shift and clicking another one. * - * @author Krinkle + * @author Timo Tijhof, 2011 - 2012 * @license GPL v2 */ ( function ( $ ) { - $.fn.checkboxShiftClick = function ( text ) { - var prevCheckbox = null, $box = this; + $.fn.checkboxShiftClick = function () { + var prevCheckbox = null, + $box = this; // When our boxes are clicked.. $box.click( function ( e ) { // And one has been clicked before... diff --git a/resources/jquery/jquery.client.js b/resources/jquery/jquery.client.js index 24f8959ee1..b35dbbbe1d 100644 --- a/resources/jquery/jquery.client.js +++ b/resources/jquery/jquery.client.js @@ -40,71 +40,74 @@ // Use the cached version if possible if ( profileCache[nav.userAgent] === undefined ) { - /* Configuration */ - - // Name of browsers or layout engines we don't recognize - var uk = 'unknown'; - // Generic version digit - var x = 'x'; - // Strings found in user agent strings that need to be conformed - var wildUserAgents = ['Opera', 'Navigator', 'Minefield', 'KHTML', 'Chrome', 'PLAYSTATION 3']; - // Translations for conforming user agent strings - var userAgentTranslations = [ - // Tons of browsers lie about being something they are not - [/(Firefox|MSIE|KHTML,\slike\sGecko|Konqueror)/, ''], - // Chrome lives in the shadow of Safari still - ['Chrome Safari', 'Chrome'], - // KHTML is the layout engine not the browser - LIES! - ['KHTML', 'Konqueror'], - // Firefox nightly builds - ['Minefield', 'Firefox'], - // This helps keep differnt versions consistent - ['Navigator', 'Netscape'], - // This prevents version extraction issues, otherwise translation would happen later - ['PLAYSTATION 3', 'PS3'] - ]; - // Strings which precede a version number in a user agent string - combined and used as match 1 in - // version detectection - var versionPrefixes = [ - 'camino', 'chrome', 'firefox', 'netscape', 'netscape6', 'opera', 'version', 'konqueror', - 'lynx', 'msie', 'safari', 'ps3' - ]; - // Used as matches 2, 3 and 4 in version extraction - 3 is used as actual version number - var versionSuffix = '(\\/|\\;?\\s|)([a-z0-9\\.\\+]*?)(\\;|dev|rel|\\)|\\s|$)'; - // Names of known browsers - var names = [ - 'camino', 'chrome', 'firefox', 'netscape', 'konqueror', 'lynx', 'msie', 'opera', - 'safari', 'ipod', 'iphone', 'blackberry', 'ps3', 'rekonq' - ]; - // Tanslations for conforming browser names - var nameTranslations = []; - // Names of known layout engines - var layouts = ['gecko', 'konqueror', 'msie', 'opera', 'webkit']; - // Translations for conforming layout names - var layoutTranslations = [['konqueror', 'khtml'], ['msie', 'trident'], ['opera', 'presto']]; - // Names of supported layout engines for version number - var layoutVersions = ['applewebkit', 'gecko']; - // Names of known operating systems - var platforms = ['win', 'mac', 'linux', 'sunos', 'solaris', 'iphone']; - // Translations for conforming operating system names - var platformTranslations = [['sunos', 'solaris']]; - - /* Methods */ - - /** - * Performs multiple replacements on a string - */ - var translate = function ( source, translations ) { - var i; - for ( i = 0; i < translations.length; i++ ) { - source = source.replace( translations[i][0], translations[i][1] ); - } - return source; - }; - - /* Pre-processing */ - - var ua = nav.userAgent, + var + versionNumber, + + /* Configuration */ + + // Name of browsers or layout engines we don't recognize + uk = 'unknown', + // Generic version digit + x = 'x', + // Strings found in user agent strings that need to be conformed + wildUserAgents = ['Opera', 'Navigator', 'Minefield', 'KHTML', 'Chrome', 'PLAYSTATION 3'], + // Translations for conforming user agent strings + userAgentTranslations = [ + // Tons of browsers lie about being something they are not + [/(Firefox|MSIE|KHTML,\slike\sGecko|Konqueror)/, ''], + // Chrome lives in the shadow of Safari still + ['Chrome Safari', 'Chrome'], + // KHTML is the layout engine not the browser - LIES! + ['KHTML', 'Konqueror'], + // Firefox nightly builds + ['Minefield', 'Firefox'], + // This helps keep differnt versions consistent + ['Navigator', 'Netscape'], + // This prevents version extraction issues, otherwise translation would happen later + ['PLAYSTATION 3', 'PS3'] + ], + // Strings which precede a version number in a user agent string - combined and used as match 1 in + // version detectection + versionPrefixes = [ + 'camino', 'chrome', 'firefox', 'netscape', 'netscape6', 'opera', 'version', 'konqueror', + 'lynx', 'msie', 'safari', 'ps3' + ], + // Used as matches 2, 3 and 4 in version extraction - 3 is used as actual version number + versionSuffix = '(\\/|\\;?\\s|)([a-z0-9\\.\\+]*?)(\\;|dev|rel|\\)|\\s|$)', + // Names of known browsers + names = [ + 'camino', 'chrome', 'firefox', 'netscape', 'konqueror', 'lynx', 'msie', 'opera', + 'safari', 'ipod', 'iphone', 'blackberry', 'ps3', 'rekonq' + ], + // Tanslations for conforming browser names + nameTranslations = [], + // Names of known layout engines + layouts = ['gecko', 'konqueror', 'msie', 'opera', 'webkit'], + // Translations for conforming layout names + layoutTranslations = [ ['konqueror', 'khtml'], ['msie', 'trident'], ['opera', 'presto'] ], + // Names of supported layout engines for version number + layoutVersions = ['applewebkit', 'gecko'], + // Names of known operating systems + platforms = ['win', 'mac', 'linux', 'sunos', 'solaris', 'iphone'], + // Translations for conforming operating system names + platformTranslations = [ ['sunos', 'solaris'] ], + + /* Methods */ + + /** + * Performs multiple replacements on a string + */ + translate = function ( source, translations ) { + var i; + for ( i = 0; i < translations.length; i++ ) { + source = source.replace( translations[i][0], translations[i][1] ); + } + return source; + }, + + /* Pre-processing */ + + ua = nav.userAgent, match, name = uk, layout = uk, @@ -147,7 +150,7 @@ if ( name === 'opera' && version >= 9.8) { version = ua.match( /version\/([0-9\.]*)/i )[1] || 10; } - var versionNumber = parseFloat( version, 10 ) || 0.0; + versionNumber = parseFloat( version, 10 ) || 0.0; /* Caching */ @@ -191,7 +194,7 @@ * @return Boolean true if browser known or assumed to be supported, false if blacklisted */ test: function ( map, profile ) { - /*jshint evil:true */ + /*jshint evil: true */ var conditions, dir, i, op, val; profile = $.isPlainObject( profile ) ? profile : $.client.profile(); diff --git a/resources/jquery/jquery.collapsibleTabs.js b/resources/jquery/jquery.collapsibleTabs.js index cb25796f83..9b8f8fc2a1 100644 --- a/resources/jquery/jquery.collapsibleTabs.js +++ b/resources/jquery/jquery.collapsibleTabs.js @@ -70,7 +70,10 @@ } return $settings; }, - handleResize: function ( e ) { + /** + * @param {jQuery.Event} e + */ + handleResize: function () { $.collapsibleTabs.instances.each( function () { var $el = $( this ), data = $.collapsibleTabs.getSettings( $el ); diff --git a/resources/jquery/jquery.colorUtil.js b/resources/jquery/jquery.colorUtil.js index c1fe7fe3f3..9c6f9ecbe1 100644 --- a/resources/jquery/jquery.colorUtil.js +++ b/resources/jquery/jquery.colorUtil.js @@ -113,17 +113,20 @@ * @return Array The HSL representation */ rgbToHsl: function ( R, G, B ) { - var r = R / 255, + var d, + r = R / 255, g = G / 255, - b = B / 255; - var max = Math.max( r, g, b ), min = Math.min( r, g, b ); - var h, s, l = (max + min) / 2; + b = B / 255, + max = Math.max( r, g, b ), min = Math.min( r, g, b ), + h, + s, + l = (max + min) / 2; if ( max === min ) { // achromatic h = s = 0; } else { - var d = max - min; + d = max - min; s = l > 0.5 ? d / (2 - max - min) : d / (max + min); switch ( max ) { case r: @@ -155,12 +158,12 @@ * @return Array The RGB representation */ hslToRgb: function ( h, s, l ) { - var r, g, b; + var r, g, b, hue2rgb, q, p; if ( s === 0 ) { r = g = b = l; // achromatic } else { - var hue2rgb = function ( p, q, t ) { + hue2rgb = function ( p, q, t ) { if ( t < 0 ) { t += 1; } @@ -179,8 +182,8 @@ return p; }; - var q = l < 0.5 ? l * (1 + s) : l + s - l * s; - var p = 2 * l - q; + q = l < 0.5 ? l * (1 + s) : l + s - l * s; + p = 2 * l - q; r = hue2rgb( p, q, h + 1/3 ); g = hue2rgb( p, q, h ); b = hue2rgb( p, q, h - 1/3 ); diff --git a/resources/jquery/jquery.expandableField.js b/resources/jquery/jquery.expandableField.js index 063f260934..9e532e527a 100644 --- a/resources/jquery/jquery.expandableField.js +++ b/resources/jquery/jquery.expandableField.js @@ -67,16 +67,16 @@ context = { config: { // callback function for before collapse - beforeCondense: function ( context ) {}, + beforeCondense: function () {}, // callback function for before expand - beforeExpand: function ( context ) {}, + beforeExpand: function () {}, // callback function for after collapse - afterCondense: function ( context ) {}, + afterCondense: function () {}, // callback function for after expand - afterExpand: function ( context ) {}, + afterExpand: function () {}, // Whether the field should expand to the left or the right -- defaults to left expandToLeft: true diff --git a/resources/jquery/jquery.hidpi.js b/resources/jquery/jquery.hidpi.js index b7335ffe86..70bfc4ea3c 100644 --- a/resources/jquery/jquery.hidpi.js +++ b/resources/jquery/jquery.hidpi.js @@ -105,9 +105,7 @@ $.matchSrcSet = function ( devicePixelRatio, srcset ) { if ( bits.length > 1 && bits[1].charAt( bits[1].length - 1 ) === 'x' ) { ratioStr = bits[1].substr( 0, bits[1].length - 1 ); ratio = parseFloat( ratioStr ); - if ( ratio > devicePixelRatio ) { - // Too big, skip! - } else if ( ratio > selectedRatio ) { + if ( ratio <= devicePixelRatio && ratio > selectedRatio ) { selectedRatio = ratio; selectedSrc = src; } diff --git a/resources/jquery/jquery.highlightText.js b/resources/jquery/jquery.highlightText.js index f720e07f0d..cda2765b78 100644 --- a/resources/jquery/jquery.highlightText.js +++ b/resources/jquery/jquery.highlightText.js @@ -29,7 +29,7 @@ // non latin characters can make regex think a new word has begun: do not use \b // http://stackoverflow.com/questions/3787072/regex-wordwrap-with-utf8-characters-in-js // look for an occurrence of our pattern and store the starting position - match = node.data.match( new RegExp( "(^|\\s)" + $.escapeRE( pat ), "i" ) ); + match = node.data.match( new RegExp( '(^|\\s)' + $.escapeRE( pat ), 'i' ) ); if ( match ) { pos = match.index + match[1].length; // include length of any matched spaces // create the span wrapper for the matched text diff --git a/resources/jquery/jquery.mw-jump.js b/resources/jquery/jquery.mw-jump.js index 36b6690c28..e2868341ab 100644 --- a/resources/jquery/jquery.mw-jump.js +++ b/resources/jquery/jquery.mw-jump.js @@ -1,12 +1,12 @@ /** * JavaScript to show jump links to motor-impaired users when they are focused. */ -jQuery( function( $ ) { +jQuery( function ( $ ) { - $('.mw-jump').delegate( 'a', 'focus blur', function( e ) { - // Confusingly jQuery leaves e.type as "focusout" for delegated blur events - if ( e.type === "blur" || e.type === "focusout" ) { - $( this ).closest( '.mw-jump' ).css({ height: '0' }); + $( '.mw-jump' ).on( 'focus blur', 'a', function ( e ) { + // Confusingly jQuery leaves e.type as focusout for delegated blur events + if ( e.type === 'blur' || e.type === 'focusout' ) { + $( this ).closest( '.mw-jump' ).css({ height: 0 }); } else { $( this ).closest( '.mw-jump' ).css({ height: 'auto' }); } diff --git a/resources/jquery/jquery.mwExtension.js b/resources/jquery/jquery.mwExtension.js index bbffd7b7df..de399788a5 100644 --- a/resources/jquery/jquery.mwExtension.js +++ b/resources/jquery/jquery.mwExtension.js @@ -15,12 +15,13 @@ return str.charAt( 0 ).toUpperCase() + str.substr( 1 ); }, escapeRE: function ( str ) { - return str.replace ( /([\\{}()|.?*+\-\^$\[\]])/g, "\\$1" ); + return str.replace ( /([\\{}()|.?*+\-\^$\[\]])/g, '\\$1' ); }, isDomElement: function ( el ) { return !!el && !!el.nodeType; }, isEmpty: function ( v ) { + var key; if ( v === '' || v === 0 || v === '0' || v === null || v === false || v === undefined ) { @@ -32,7 +33,7 @@ return true; } if ( typeof v === 'object' ) { - for ( var key in v ) { + for ( key in v ) { return false; } return true; diff --git a/resources/jquery/jquery.qunit.completenessTest.js b/resources/jquery/jquery.qunit.completenessTest.js index 1475af2a2d..ef28948a36 100644 --- a/resources/jquery/jquery.qunit.completenessTest.js +++ b/resources/jquery/jquery.qunit.completenessTest.js @@ -15,7 +15,7 @@ /*global jQuery, QUnit */ /*jshint eqeqeq:false, eqnull:false, forin:false */ ( function ( $ ) { - "use strict"; + 'use strict'; var util, hasOwn = Object.prototype.hasOwnProperty, diff --git a/resources/jquery/jquery.suggestions.js b/resources/jquery/jquery.suggestions.js index d80680fcfc..edc18a7940 100644 --- a/resources/jquery/jquery.suggestions.js +++ b/resources/jquery/jquery.suggestions.js @@ -13,11 +13,11 @@ * * Options: * - * fetch(query): Callback that should fetch suggestions and set the suggestions property. Executed in the context of the - * textbox + * fetch(query): Callback that should fetch suggestions and set the suggestions property. + * Executed in the context of the textbox * Type: Function - * cancel: Callback function to call when any pending asynchronous suggestions fetches should be canceled. - * Executed in the context of the textbox + * cancel: Callback function to call when any pending asynchronous suggestions fetches + * should be canceled. Executed in the context of the textbox * Type: Function * special: Set of callbacks for rendering and selecting * Type: Object of Functions 'render' and 'select' @@ -33,12 +33,12 @@ * Type: Number, Range: 0 - 1200, Default: 120 * submitOnClick: Whether to submit the form containing the textbox when a suggestion is clicked * Type: Boolean, Default: false - * maxExpandFactor: Maximum suggestions box width relative to the textbox width. If set to e.g. 2, the suggestions box - * will never be grown beyond 2 times the width of the textbox. + * maxExpandFactor: Maximum suggestions box width relative to the textbox width. If set + * to e.g. 2, the suggestions box will never be grown beyond 2 times the width of the textbox. * Type: Number, Range: 1 - infinity, Default: 3 * expandFrom: Which direction to offset the suggestion box from. - * Values 'start' and 'end' translate to left and right respectively depending on the directionality - * of the current document, according to $( 'html' ).css( 'direction' ). + * Values 'start' and 'end' translate to left and right respectively depending on the + * directionality of the current document, according to $( 'html' ).css( 'direction' ). * Type: String, default: 'auto', options: 'left', 'right', 'start', 'end', 'auto'. * positionFromLeft: Sets expandFrom=left, for backwards compatibility * Type: Boolean, Default: true @@ -60,18 +60,22 @@ $.suggestions = { context.config.cancel.call( context.data.$textbox ); } }, + /** - * Restore the text the user originally typed in the textbox, before it was overwritten by highlight(). This - * restores the value the currently displayed suggestions are based on, rather than the value just before + * Restore the text the user originally typed in the textbox, before it + * was overwritten by highlight(). This restores the value the currently + * displayed suggestions are based on, rather than the value just before * highlight() overwrote it; the former is arguably slightly more sensible. */ restore: function ( context ) { context.data.$textbox.val( context.data.prevText ); }, + /** - * Ask the user-specified callback for new suggestions. Any previous delayed call to this function still pending - * will be canceled. If the value in the textbox is empty or hasn't changed since the last time suggestions were fetched, this - * function does nothing. + * Ask the user-specified callback for new suggestions. Any previous delayed + * call to this function still pending will be canceled. If the value in the + * textbox is empty or hasn't changed since the last time suggestions were fetched, + * this function does nothing. * @param {Boolean} delayed Whether or not to delay this by the currently configured amount of time */ update: function ( context, delayed ) { @@ -101,6 +105,7 @@ $.suggestions = { } $.suggestions.special( context ); }, + special: function ( context ) { // Allow custom rendering - but otherwise don't do any rendering if ( typeof context.config.special.render === 'function' ) { @@ -112,13 +117,17 @@ $.suggestions = { }, 1 ); } }, + /** * Sets the value of a property, and updates the widget accordingly * @param property String Name of property * @param value Mixed Value to set property with */ configure: function ( context, property, value ) { - var newCSS; + var newCSS, + $autoEllipseMe, $result, $results, $span, + i, expWidth, matchedText, maxWidth, text; + // Validate creation using fallback values switch( property ) { case 'fetch': @@ -212,22 +221,24 @@ $.suggestions = { } context.data.$container.css( newCSS ); - var $results = context.data.$container.children( '.suggestions-results' ); + $results = context.data.$container.children( '.suggestions-results' ); $results.empty(); - var expWidth = -1; - var $autoEllipseMe = $( [] ); - var matchedText = null; - for ( var i = 0; i < context.config.suggestions.length; i++ ) { + expWidth = -1; + $autoEllipseMe = $( [] ); + matchedText = null; + for ( i = 0; i < context.config.suggestions.length; i++ ) { /*jshint loopfunc:true */ - var text = context.config.suggestions[i]; - var $result = $( '
' ) + text = context.config.suggestions[i]; + $result = $( '
' ) .addClass( 'suggestions-result' ) .attr( 'rel', i ) .data( 'text', context.config.suggestions[i] ) - .mousemove( function ( e ) { + .mousemove( function () { context.data.selectedWithMouse = true; $.suggestions.highlight( - context, $(this).closest( '.suggestions-results div' ), false + context, + $(this).closest( '.suggestions-results div' ), + false ); } ) .appendTo( $results ); @@ -246,7 +257,7 @@ $.suggestions = { // Widen results box if needed // New width is only calculated here, applied later - var $span = $result.children( 'span' ); + $span = $result.children( 'span' ); if ( $span.outerWidth() > $result.width() && $span.outerWidth() > expWidth ) { // factor in any padding, margin, or border space on the parent expWidth = $span.outerWidth() + ( context.data.$container.width() - $span.parent().width()); @@ -256,11 +267,15 @@ $.suggestions = { } // Apply new width for results box, if any if ( expWidth > context.data.$container.width() ) { - var maxWidth = context.config.maxExpandFactor*context.data.$textbox.width(); + maxWidth = context.config.maxExpandFactor*context.data.$textbox.width(); context.data.$container.width( Math.min( expWidth, maxWidth ) ); } // autoEllipse the results. Has to be done after changing the width - $autoEllipseMe.autoEllipsis( { hasSpan: true, tooltip: true, matchText: matchedText } ); + $autoEllipseMe.autoEllipsis( { + hasSpan: true, + tooltip: true, + matchText: matchedText + } ); } } break; @@ -280,6 +295,7 @@ $.suggestions = { break; } }, + /** * Highlight a result in the results table * @param result to highlight: jQuery object, or 'prev' or 'next' @@ -338,13 +354,16 @@ $.suggestions = { context.data.$textbox.trigger( 'change' ); } }, + /** * Respond to keypress event * @param key Integer Code of key pressed */ keypress: function ( e, context, key ) { - var wasVisible = context.data.$container.is( ':visible' ), + var selected, + wasVisible = context.data.$container.is( ':visible' ), preventDefault = false; + switch ( key ) { // Arrow down case 40: @@ -376,7 +395,7 @@ $.suggestions = { case 13: context.data.$container.hide(); preventDefault = wasVisible; - var selected = context.data.$container.find( '.suggestions-result-current' ); + selected = context.data.$container.find( '.suggestions-result-current' ); if ( selected.length === 0 || context.data.selectedWithMouse ) { // if nothing is selected OR if something was selected with the mouse, // cancel any current requests and submit the form @@ -420,18 +439,18 @@ $.fn.suggestions = function () { if ( context === undefined || context === null ) { context = { config: { - 'fetch' : function () {}, - 'cancel': function () {}, - 'special': {}, - 'result': {}, - '$region': $(this), - 'suggestions': [], - 'maxRows': 7, - 'delay': 120, - 'submitOnClick': false, - 'maxExpandFactor': 3, - 'expandFrom': 'auto', - 'highlightInput': false + fetch: function () {}, + cancel: function () {}, + special: {}, + result: {}, + $region: $(this), + suggestions: [], + maxRows: 7, + delay: 120, + submitOnClick: false, + maxExpandFactor: 3, + expandFrom: 'auto', + highlightInput: false } }; } @@ -480,14 +499,16 @@ $.fn.suggestions = function () { .addClass( 'suggestions' ) .append( $( '
' ).addClass( 'suggestions-results' ) - // Can't use click() because the container div is hidden when the textbox loses focus. Instead, - // listen for a mousedown followed by a mouseup on the same div + // Can't use click() because the container div is hidden when the + // textbox loses focus. Instead, listen for a mousedown followed + // by a mouseup on the same div. .mousedown( function ( e ) { context.data.mouseDownOn = $( e.target ).closest( '.suggestions-results div' ); } ) .mouseup( function ( e ) { - var $result = $( e.target ).closest( '.suggestions-results div' ); - var $other = context.data.mouseDownOn; + var $result = $( e.target ).closest( '.suggestions-results div' ), + $other = context.data.mouseDownOn; + context.data.mouseDownOn = $( [] ); if ( $result.get( 0 ) !== $other.get( 0 ) ) { return; @@ -502,14 +523,16 @@ $.fn.suggestions = function () { ) .append( $( '
' ).addClass( 'suggestions-special' ) - // Can't use click() because the container div is hidden when the textbox loses focus. Instead, - // listen for a mousedown followed by a mouseup on the same div + // Can't use click() because the container div is hidden when the + // textbox loses focus. Instead, listen for a mousedown followed + // by a mouseup on the same div. .mousedown( function ( e ) { context.data.mouseDownOn = $( e.target ).closest( '.suggestions-special' ); } ) .mouseup( function ( e ) { - var $special = $( e.target ).closest( '.suggestions-special' ); - var $other = context.data.mouseDownOn; + var $special = $( e.target ).closest( '.suggestions-special' ), + $other = context.data.mouseDownOn; + context.data.mouseDownOn = $( [] ); if ( $special.get( 0 ) !== $other.get( 0 ) ) { return; diff --git a/resources/jquery/jquery.textSelection.js b/resources/jquery/jquery.textSelection.js index abb0fa3f64..17fd0cd35f 100644 --- a/resources/jquery/jquery.textSelection.js +++ b/resources/jquery/jquery.textSelection.js @@ -25,6 +25,11 @@ } $.fn.textSelection = function ( command, options ) { + var fn, + context, + hasIframe, + needSave, + retval; /** * Helper function to get an IE TextRange object for an element @@ -52,7 +57,7 @@ } } - var fn = { + fn = { /** * Get the contents of the textarea */ @@ -168,16 +173,16 @@ range2.collapse(); range2.moveStart( 'character', -1 ); // FIXME: Which check is correct? - if ( range2.text !== "\r" && range2.text !== "\n" && range2.text !== "" ) { - insertText = "\n" + insertText; - pre += "\n"; + if ( range2.text !== '\r' && range2.text !== '\n' && range2.text !== '' ) { + insertText = '\n' + insertText; + pre += '\n'; } range3 = document.selection.createRange(); range3.collapse( false ); range3.moveEnd( 'character', 1 ); - if ( range3.text !== "\r" && range3.text !== "\n" && range3.text !== "" ) { - insertText += "\n"; - post += "\n"; + if ( range3.text !== '\r' && range3.text !== '\n' && range3.text !== '' ) { + insertText += '\n'; + post += '\n'; } } @@ -216,13 +221,13 @@ insertText = doSplitLines( selText, pre, post ); } if ( options.ownline ) { - if ( startPos !== 0 && this.value.charAt( startPos - 1 ) !== "\n" && this.value.charAt( startPos - 1 ) !== "\r" ) { - insertText = "\n" + insertText; - pre += "\n"; + if ( startPos !== 0 && this.value.charAt( startPos - 1 ) !== '\n' && this.value.charAt( startPos - 1 ) !== '\r' ) { + insertText = '\n' + insertText; + pre += '\n'; } - if ( this.value.charAt( endPos ) !== "\n" && this.value.charAt( endPos ) !== "\r" ) { - insertText += "\n"; - post += "\n"; + if ( this.value.charAt( endPos ) !== '\n' && this.value.charAt( endPos ) !== '\r' ) { + insertText += '\n'; + post += '\n'; } } this.value = this.value.substring( 0, startPos ) + insertText + @@ -230,9 +235,9 @@ // Setting this.value scrolls the textarea to the top, restore the scroll position this.scrollTop = scrollTop; if ( window.opera ) { - pre = pre.replace( /\r?\n/g, "\r\n" ); - selText = selText.replace( /\r?\n/g, "\r\n" ); - post = post.replace( /\r?\n/g, "\r\n" ); + pre = pre.replace( /\r?\n/g, '\r\n' ); + selText = selText.replace( /\r?\n/g, '\r\n' ); + post = post.replace( /\r?\n/g, '\r\n' ); } if ( isSample && options.selectPeri && !options.splitlines ) { this.selectionStart = startPos + pre.length; @@ -261,7 +266,21 @@ */ getCaretPosition: function ( options ) { function getCaret( e ) { - var caretPos = 0, endPos = 0; + var caretPos = 0, + endPos = 0, + preText, rawPreText, periText, + rawPeriText, postText, rawPostText, + // IE Support + preFinished, + periFinished, + postFinished, + // Range containing text in the selection + periRange, + // Range containing text before the selection + preRange, + // Range containing text after the selection + postRange; + if ( document.selection && document.selection.createRange ) { // IE doesn't properly report non-selected caret position through // the selection ranges when textarea isn't focused. This can @@ -269,20 +288,10 @@ // whatever we do later (bug 31847). activateElementOnIE( e ); - var - preText, rawPreText, periText, - rawPeriText, postText, rawPostText, - - // IE Support - preFinished = false, - periFinished = false, - postFinished = false, - // Range containing text in the selection - periRange = document.selection.createRange().duplicate(), - // Range containing text before the selection - preRange, - // Range containing text after the selection - postRange; + preFinished = false; + periFinished = false; + postFinished = false; + periRange = document.selection.createRange().duplicate(); preRange = rangeForElementIE( e ), // Move the end where we need it @@ -309,7 +318,7 @@ } else { preRange.moveEnd( 'character', -1 ); if ( preRange.text === preText ) { - rawPreText += "\r\n"; + rawPreText += '\r\n'; } else { preFinished = true; } @@ -321,7 +330,7 @@ } else { periRange.moveEnd( 'character', -1 ); if ( periRange.text === periText ) { - rawPeriText += "\r\n"; + rawPeriText += '\r\n'; } else { periFinished = true; } @@ -333,15 +342,15 @@ } else { postRange.moveEnd( 'character', -1 ); if ( postRange.text === postText ) { - rawPostText += "\r\n"; + rawPostText += '\r\n'; } else { postFinished = true; } } } } while ( ( !preFinished || !periFinished || !postFinished ) ); - caretPos = rawPreText.replace( /\r\n/g, "\n" ).length; - endPos = caretPos + rawPeriText.replace( /\r\n/g, "\n" ).length; + caretPos = rawPreText.replace( /\r\n/g, '\n' ).length; + endPos = caretPos + rawPeriText.replace( /\r\n/g, '\n' ).length; } else if ( e.selectionStart || e.selectionStart === 0 ) { // Firefox support caretPos = e.selectionStart; @@ -405,20 +414,22 @@ return Math.floor( e.scrollWidth / ( $.client.profile().platform === 'linux' ? 7 : 8 ) ); } function getCaretScrollPosition( e ) { - var i, j; // FIXME: This functions sucks and is off by a few lines most // of the time. It should be replaced by something decent. - var text = e.value.replace( /\r/g, '' ); - var caret = $( e ).textSelection( 'getCaretPosition' ); - var lineLength = getLineLength( e ); - var row = 0; - var charInLine = 0; - var lastSpaceInLine = 0; + var i, j, + nextSpace, + text = e.value.replace( /\r/g, '' ), + caret = $( e ).textSelection( 'getCaretPosition' ), + lineLength = getLineLength( e ), + row = 0, + charInLine = 0, + lastSpaceInLine = 0; + for ( i = 0; i < caret; i++ ) { charInLine++; if ( text.charAt( i ) === ' ' ) { lastSpaceInLine = charInLine; - } else if ( text.charAt( i ) === "\n" ) { + } else if ( text.charAt( i ) === '\n' ) { lastSpaceInLine = 0; charInLine = 0; row++; @@ -431,11 +442,11 @@ } } } - var nextSpace = 0; + nextSpace = 0; for ( j = caret; j < caret + lineLength; j++ ) { if ( text.charAt( j ) === ' ' || - text.charAt( j ) === "\n" || + text.charAt( j ) === '\n' || caret === text.length ) { nextSpace = j; @@ -542,16 +553,16 @@ break; } - var context = $(this).data( 'wikiEditor-context' ); - var hasIframe = typeof context !== 'undefined' && context && typeof context.$iframe !== 'undefined'; + context = $(this).data( 'wikiEditor-context' ); + hasIframe = context !== undefined && context && context.$iframe !== undefined; // IE selection restore voodoo - var needSave = false; + needSave = false; if ( hasIframe && context.savedSelection !== null ) { context.fn.restoreSelection(); needSave = true; } - var retval = ( hasIframe ? context.fn : fn )[command].call( this, options ); + retval = ( hasIframe ? context.fn : fn )[command].call( this, options ); if ( hasIframe && needSave ) { context.fn.saveSelection(); } diff --git a/resources/mediawiki.action/mediawiki.action.edit.js b/resources/mediawiki.action/mediawiki.action.edit.js index 1c51c97483..2835c9cccf 100644 --- a/resources/mediawiki.action/mediawiki.action.edit.js +++ b/resources/mediawiki.action/mediawiki.action.edit.js @@ -71,7 +71,7 @@ * Apply tagOpen/tagClose to selection in textarea, * use sampleText instead of selection if there is none. */ - insertTags: function ( tagOpen, tagClose, sampleText, selectText ) { + insertTags: function ( tagOpen, tagClose, sampleText ) { if ( currentFocused && currentFocused.length ) { currentFocused.textSelection( 'encapsulateSelection', { diff --git a/resources/mediawiki.language/mediawiki.cldr.js b/resources/mediawiki.language/mediawiki.cldr.js index 6660eca4f2..c3023cd5a0 100644 --- a/resources/mediawiki.language/mediawiki.cldr.js +++ b/resources/mediawiki.language/mediawiki.cldr.js @@ -1,8 +1,8 @@ /** - * CLDR related utility methods + * CLDR related utility methods. */ -( function( mw ) { - "use strict"; +( function ( mw ) { + 'use strict'; var cldr = { /** @@ -10,19 +10,20 @@ * In case none of the rules passed, we return pluralRules.length * That means it is the "other" form. * @param number - * @param pluralRules - * @return plural form index + * @param {Array} pluralRules + * @return {number} plural form index */ - getPluralForm: function( number, pluralRules ) { - var pluralFormIndex = 0; - for ( pluralFormIndex = 0; pluralFormIndex < pluralRules.length; pluralFormIndex++ ) { - if ( mw.libs.pluralRuleParser( pluralRules[pluralFormIndex], number ) ) { + getPluralForm: function ( number, pluralRules ) { + var i; + for ( i = 0; i < pluralRules.length; i++ ) { + if ( mw.libs.pluralRuleParser( pluralRules[i], number ) ) { break; } } - return pluralFormIndex; + return i; } }; mw.cldr = cldr; -} )( mediaWiki ); + +}( mediaWiki ) ); diff --git a/resources/mediawiki.language/mediawiki.language.init.js b/resources/mediawiki.language/mediawiki.language.init.js index 30307a37de..937b89bb17 100644 --- a/resources/mediawiki.language/mediawiki.language.init.js +++ b/resources/mediawiki.language/mediawiki.language.init.js @@ -2,7 +2,7 @@ * Base language object with methods for storing and getting * language data. */ -( function ( mw, $ ) { +( function ( mw ) { var language = { /** @@ -58,4 +58,4 @@ mw.language = language; -}( mediaWiki, jQuery ) ); +}( mediaWiki ) ); diff --git a/resources/mediawiki.language/mediawiki.language.js b/resources/mediawiki.language/mediawiki.language.js index 935d4ff6e6..514dbd5ef8 100644 --- a/resources/mediawiki.language/mediawiki.language.js +++ b/resources/mediawiki.language/mediawiki.language.js @@ -43,12 +43,14 @@ var language = { * @param forms array List of plural forms * @return string Correct form for quantifier in this language */ - convertPlural: function( count, forms ) { - var pluralFormIndex = 0; + convertPlural: function ( count, forms ) { + var pluralRules, + pluralFormIndex = 0; + if ( !forms || forms.length === 0 ) { return ''; } - var pluralRules = mw.language.getData( mw.config.get( 'wgUserLanguage' ), 'pluralRules' ); + pluralRules = mw.language.getData( mw.config.get( 'wgUserLanguage' ), 'pluralRules' ); if ( !pluralRules ) { // default fallback. return ( count === 1 ) ? forms[0] : forms[1]; @@ -78,8 +80,8 @@ var language = { * @param {num} number Value to be converted * @param {boolean} integer Convert the return value to an integer */ - convertNumber: function( num, integer ) { - var i, tmp, transformTable; + convertNumber: function ( num, integer ) { + var i, tmp, transformTable, numberString, convertedNumber; if ( !mw.language.digitTransformTable ) { return num; @@ -97,8 +99,8 @@ var language = { } transformTable = tmp; } - var numberString = '' + num; - var convertedNumber = ''; + numberString = '' + num; + convertedNumber = ''; for ( i = 0; i < numberString.length; i++ ) { if ( transformTable[ numberString[i] ] ) { convertedNumber += transformTable[numberString[i]]; @@ -121,7 +123,7 @@ var language = { * * @return string */ - gender: function( gender, forms ) { + gender: function ( gender, forms ) { if ( !forms || forms.length === 0 ) { return ''; } diff --git a/resources/mediawiki.page/mediawiki.page.ready.js b/resources/mediawiki.page/mediawiki.page.ready.js index 370c3a19c2..684f582f2f 100644 --- a/resources/mediawiki.page/mediawiki.page.ready.js +++ b/resources/mediawiki.page/mediawiki.page.ready.js @@ -1,24 +1,28 @@ -jQuery( document ).ready( function( $ ) { +( function ( mw, $ ) { + $( function () { + var $sortableTables; - /* Emulate placeholder if not supported by browser */ - if ( !( 'placeholder' in document.createElement( 'input' ) ) ) { - $( 'input[placeholder]' ).placeholder(); - } + /* Emulate placeholder if not supported by browser */ + if ( !( 'placeholder' in document.createElement( 'input' ) ) ) { + $( 'input[placeholder]' ).placeholder(); + } - /* Enable makeCollapsible */ - $( '.mw-collapsible' ).makeCollapsible(); + /* Enable makeCollapsible */ + $( '.mw-collapsible' ).makeCollapsible(); - /* Lazy load jquery.tablesorter */ - if ( $( 'table.sortable' ).length ) { - mw.loader.using( 'jquery.tablesorter', function() { - $( 'table.sortable' ).tablesorter(); - }); - } + /* Lazy load jquery.tablesorter */ + $sortableTables = $( 'table.sortable' ); + if ( $sortableTables.length ) { + mw.loader.using( 'jquery.tablesorter', function () { + $sortableTables.tablesorter(); + }); + } - /* Enable CheckboxShiftClick */ - $( 'input[type=checkbox]:not(.noshiftselect)' ).checkboxShiftClick(); + /* Enable CheckboxShiftClick */ + $( 'input[type=checkbox]:not(.noshiftselect)' ).checkboxShiftClick(); - /* Add accesskey hints to the tooltips */ - mw.util.updateTooltipAccessKeys(); + /* Add accesskey hints to the tooltips */ + mw.util.updateTooltipAccessKeys(); -} ); + } ); +}( mediaWiki, jQuery ) ); diff --git a/resources/mediawiki.page/mediawiki.page.watch.ajax.js b/resources/mediawiki.page/mediawiki.page.watch.ajax.js index a7e059c419..39574939ff 100644 --- a/resources/mediawiki.page/mediawiki.page.watch.ajax.js +++ b/resources/mediawiki.page/mediawiki.page.watch.ajax.js @@ -24,13 +24,14 @@ otherAction = action === 'watch' ? 'unwatch' : 'watch'; accesskeyTip = $link.attr( 'title' ).match( mw.util.tooltipAccessKeyRegexp ); $li = $link.closest( 'li' ); + /** * Trigger a 'watchpage' event for this List item. * Announce the otherAction value as the first param. * Used to monitor the state of watch link. * TODO: Revise when system wide hooks are implemented */ - if( state === undefined ) { + if ( state === undefined ) { $li.trigger( 'watchpage.mw', otherAction ); } @@ -96,7 +97,7 @@ // Expose local methods mw.page.watch = { - 'updateWatchLink': updateWatchLink + updateWatchLink: updateWatchLink }; $( document ).ready( function () { @@ -134,7 +135,9 @@ otherAction = action === 'watch' ? 'unwatch' : 'watch'; $li = $link.closest( 'li' ); - mw.notify( $.parseHTML( watchResponse.message ), { tag: 'watch-self' } ); + mw.notify( $.parseHTML( watchResponse.message ), { + tag: 'watch-self' + } ); // Set link to opposite updateWatchLink( $link, otherAction ); @@ -144,7 +147,7 @@ if ( watchResponse.watched !== undefined ) { $( '#wpWatchthis' ).prop( 'checked', true ); } else { - $( '#wpWatchthis' ).removeProp( 'checked' ); + $( '#wpWatchthis' ).prop( 'checked', false ); } }, // Error @@ -169,7 +172,7 @@ } ); - }); - }); + } ); + } ); }( mediaWiki, jQuery ) ); diff --git a/resources/mediawiki.special/mediawiki.special.block.js b/resources/mediawiki.special/mediawiki.special.block.js index 6f79929b5d..077adcdbb6 100644 --- a/resources/mediawiki.special/mediawiki.special.block.js +++ b/resources/mediawiki.special/mediawiki.special.block.js @@ -1,46 +1,49 @@ -/* JavaScript for Special:Block */ +/** + * JavaScript for Special:Block + */ +( function ( mw, $ ) { + $( function ( $ ) { -jQuery( function( $ ) { + var $blockTarget = $( '#mw-bi-target' ), + $anonOnlyRow = $( '#mw-input-wpHardBlock' ).closest( 'tr' ), + $enableAutoblockRow = $( '#mw-input-wpAutoBlock' ).closest( 'tr' ), + $hideUser = $( '#mw-input-wpHideUser' ).closest( 'tr' ), + $watchUser = $( '#mw-input-wpWatch' ).closest( 'tr' ); - var DO_INSTANT = true, - $blockTarget = $( '#mw-bi-target' ), - $anonOnlyRow = $( '#mw-input-wpHardBlock' ).closest( 'tr' ), - $enableAutoblockRow = $( '#mw-input-wpAutoBlock' ).closest( 'tr' ), - $hideUser = $( '#mw-input-wpHideUser' ).closest( 'tr' ), - $watchUser = $( '#mw-input-wpWatch' ).closest( 'tr' ); + function updateBlockOptions( instant ) { + if ( !$blockTarget.length ) { + return; + } - var updateBlockOptions = function( instant ) { - if ( !$blockTarget.length ) { - return; - } - - var blocktarget = $.trim( $blockTarget.val() ); - var isEmpty = ( blocktarget === '' ); - var isIp = mw.util.isIPv4Address( blocktarget, true ) || mw.util.isIPv6Address( blocktarget, true ); - var isIpRange = isIp && blocktarget.match( /\/\d+$/ ); + var blocktarget = $.trim( $blockTarget.val() ), + isEmpty = blocktarget === '', + isIp = mw.util.isIPv4Address( blocktarget, true ) || mw.util.isIPv6Address( blocktarget, true ), + isIpRange = isIp && blocktarget.match( /\/\d+$/ ); - if ( isIp && !isEmpty ) { - $enableAutoblockRow.goOut( instant ); - $hideUser.goOut( instant ); - } else { - $enableAutoblockRow.goIn( instant ); - $hideUser.goIn( instant ); - } - if ( !isIp && !isEmpty ) { - $anonOnlyRow.goOut( instant ); - } else { - $anonOnlyRow.goIn( instant ); + if ( isIp && !isEmpty ) { + $enableAutoblockRow.goOut( instant ); + $hideUser.goOut( instant ); + } else { + $enableAutoblockRow.goIn( instant ); + $hideUser.goIn( instant ); + } + if ( !isIp && !isEmpty ) { + $anonOnlyRow.goOut( instant ); + } else { + $anonOnlyRow.goIn( instant ); + } + if ( isIpRange && !isEmpty ) { + $watchUser.goOut( instant ); + } else { + $watchUser.goIn( instant ); + } } - if ( isIpRange && !isEmpty ) { - $watchUser.goOut( instant ); - } else { - $watchUser.goIn( instant ); - } - }; - // Bind functions so they're checked whenever stuff changes - $blockTarget.keyup( updateBlockOptions ); + // Bind functions so they're checked whenever stuff changes + $blockTarget.keyup( updateBlockOptions ); + + // Call them now to set initial state (ie. Special:Block/Foobar?wpBlockExpiry=2+hours) + updateBlockOptions( /* instant= */ true ); + } ); +}( mediaWiki, jQuery ) ); - // Call them now to set initial state (ie. Special:Block/Foobar?wpBlockExpiry=2+hours) - updateBlockOptions( DO_INSTANT ); -}); diff --git a/resources/mediawiki.special/mediawiki.special.javaScriptTest.js b/resources/mediawiki.special/mediawiki.special.javaScriptTest.js index 808d5fe89f..a560ca95aa 100644 --- a/resources/mediawiki.special/mediawiki.special.javaScriptTest.js +++ b/resources/mediawiki.special/mediawiki.special.javaScriptTest.js @@ -8,7 +8,7 @@ // (only if a framework was found, not on error pages). $( '#mw-javascripttest-summary.mw-javascripttest-frameworkfound' ).append( function () { - var $html = $( '

' ), diff --git a/resources/mediawiki.special/mediawiki.special.js b/resources/mediawiki.special/mediawiki.special.js index 3526cef413..8edb1cbe25 100644 --- a/resources/mediawiki.special/mediawiki.special.js +++ b/resources/mediawiki.special/mediawiki.special.js @@ -1 +1,5 @@ -mw.special = {}; +/* + * Namespace for mediawiki.special.* modules + */ + +mediaWiki.special = {}; diff --git a/resources/mediawiki.special/mediawiki.special.movePage.js b/resources/mediawiki.special/mediawiki.special.movePage.js index 68c2ed078e..7a55806607 100644 --- a/resources/mediawiki.special/mediawiki.special.movePage.js +++ b/resources/mediawiki.special/mediawiki.special.movePage.js @@ -1,5 +1,7 @@ -/* JavaScript for Special:MovePage */ +/** + * JavaScript for Special:MovePage + */ jQuery( function( $ ) { $( '#wpReason, #wpNewTitleMain' ).byteLimit(); -}); +} ); diff --git a/resources/mediawiki.special/mediawiki.special.preferences.js b/resources/mediawiki.special/mediawiki.special.preferences.js index 0804825089..989a986b22 100644 --- a/resources/mediawiki.special/mediawiki.special.preferences.js +++ b/resources/mediawiki.special/mediawiki.special.preferences.js @@ -2,174 +2,180 @@ * JavaScript for Special:Preferences */ jQuery( document ).ready( function ( $ ) { -$( '#prefsubmit' ).attr( 'id', 'prefcontrol' ); -var $preftoc = $('
    '); -var $preferences = $( '#preferences' ) - .addClass( 'jsprefs' ) - .before( $preftoc ); - -var $fieldsets = $preferences.children( 'fieldset' ) - .hide() - .addClass( 'prefsection' ); - -var $legends = $fieldsets.children( 'legend' ) - .addClass( 'mainLegend' ); - -/** - * It uses document.getElementById for security reasons (html injections in - * jQuery()). - * - * @param String name: the name of a tab without the prefix ("mw-prefsection-") - * @param String mode: [optional] A hash will be set according to the current - * open section. Set mode 'noHash' to surpress this. - */ -function switchPrefTab( name, mode ) { - var $tab, scrollTop; - // Handle hash manually to prevent jumping, - // therefore save and restore scrollTop to prevent jumping. - scrollTop = $( window ).scrollTop(); - if ( mode !== 'noHash' ) { - window.location.hash = '#mw-prefsection-' + name; - } - $( window ).scrollTop( scrollTop ); - - $preftoc.find( 'li' ).removeClass( 'selected' ); - $tab = $( document.getElementById( 'preftab-' + name ) ); - if ( $tab.length ) { - $tab.parent().addClass( 'selected' ); - $preferences.children( 'fieldset' ).hide(); - $( document.getElementById( 'mw-prefsection-' + name ) ).show(); + var $preftoc, $preferences, $fieldsets, $legends, + hash, + $tzSelect, $tzTextbox, $localtimeHolder, servertime; + + $( '#prefsubmit' ).attr( 'id', 'prefcontrol' ); + + $preftoc = $('
      '), + $preferences = $( '#preferences' ) + .addClass( 'jsprefs' ) + .before( $preftoc ), + $fieldsets = $preferences.children( 'fieldset' ) + .hide() + .addClass( 'prefsection' ), + $legends = $fieldsets + .children( 'legend' ) + .addClass( 'mainLegend' ); + + /** + * It uses document.getElementById for security reasons (html injections in + * jQuery()). + * + * @param String name: the name of a tab without the prefix ("mw-prefsection-") + * @param String mode: [optional] A hash will be set according to the current + * open section. Set mode 'noHash' to surpress this. + */ + function switchPrefTab( name, mode ) { + var $tab, scrollTop; + // Handle hash manually to prevent jumping, + // therefore save and restore scrollTop to prevent jumping. + scrollTop = $( window ).scrollTop(); + if ( mode !== 'noHash' ) { + window.location.hash = '#mw-prefsection-' + name; + } + $( window ).scrollTop( scrollTop ); + + $preftoc.find( 'li' ).removeClass( 'selected' ); + $tab = $( document.getElementById( 'preftab-' + name ) ); + if ( $tab.length ) { + $tab.parent().addClass( 'selected' ); + $preferences.children( 'fieldset' ).hide(); + $( document.getElementById( 'mw-prefsection-' + name ) ).show(); + } } -} -// Populate the prefToc -$legends.each( function ( i, legend ) { - var $legend = $(legend); - if ( i === 0 ) { - $legend.parent().show(); + // Populate the prefToc + $legends.each( function ( i, legend ) { + var $legend = $(legend), + ident, $li, $a; + if ( i === 0 ) { + $legend.parent().show(); + } + ident = $legend.parent().attr( 'id' ); + + $li = $( '
    • ' ) + .addClass( i === 0 ? 'selected' : '' ); + $a = $( '' ) + .attr( { + id: ident.replace( 'mw-prefsection', 'preftab' ), + href: '#' + ident + } ) + .text( $legend.text() ); + $li.append( $a ); + $preftoc.append( $li ); + } ); + + // If we've reloaded the page or followed an open-in-new-window, + // make the selected tab visible. + hash = window.location.hash; + if ( hash.match( /^#mw-prefsection-[\w\-]+/ ) ) { + switchPrefTab( hash.replace( '#mw-prefsection-' , '' ) ); } - var ident = $legend.parent().attr( 'id' ); - - var $li = $( '
    • ', { - 'class' : ( i === 0 ) ? 'selected' : null - }); - var $a = $( '', { - text : $legend.text(), - id : ident.replace( 'mw-prefsection', 'preftab' ), - href : '#' + ident - }); - $li.append( $a ); - $preftoc.append( $li ); -} ); -// If we've reloaded the page or followed an open-in-new-window, -// make the selected tab visible. -var hash = window.location.hash; -if ( hash.match( /^#mw-prefsection-[\w-]+/ ) ) { - switchPrefTab( hash.replace( '#mw-prefsection-' , '' ) ); -} - -// In browsers that support the onhashchange event we will not bind click -// handlers and instead let the browser do the default behavior (clicking the -// will naturally set the hash, handled by onhashchange. -// But other things that change the hash will also be catched (e.g. using -// the Back and Forward browser navigation). -if ( 'onhashchange' in window ) { - $(window).on( 'hashchange' , function () { - var hash = window.location.hash; - if ( hash.match( /^#mw-prefsection-[\w-]+/ ) ) { - switchPrefTab( hash.replace( '#mw-prefsection-', '' ) ); - } else if ( hash === '' ) { - switchPrefTab( 'personal', 'noHash' ); - } - }); -// In older browsers we'll bind a click handler as fallback. -// We must not have onhashchange *and* the click handlers, other wise -// the click handler calls switchPrefTab() which sets the hash value, -// which triggers onhashcange and calls switchPrefTab() again. -} else { - $preftoc.on( 'click', 'li a', function ( e ) { - switchPrefTab( $( this ).attr( 'href' ).replace( '#mw-prefsection-', '' ) ); - e.preventDefault(); - }); -} - -/** -* Timezone functions. -* Guesses Timezone from browser and updates fields onchange -*/ - -var $tzSelect = $( '#mw-input-wptimecorrection' ); -var $tzTextbox = $( '#mw-input-wptimecorrection-other' ); - -var $localtimeHolder = $( '#wpLocalTime' ); -var servertime = parseInt( $( 'input[name=wpServerTime]' ).val(), 10 ); -var minuteDiff = 0; - -var minutesToHours = function ( min ) { - var tzHour = Math.floor( Math.abs( min ) / 60 ); - var tzMin = Math.abs( min ) % 60; - var tzString = ( ( min >= 0 ) ? '' : '-' ) + ( ( tzHour < 10 ) ? '0' : '' ) + tzHour + - ':' + ( ( tzMin < 10 ) ? '0' : '' ) + tzMin; - return tzString; -}; - -var hoursToMinutes = function ( hour ) { - var arr = hour.split( ':' ); - arr[0] = parseInt( arr[0], 10 ); - - var minutes; - if ( arr.length == 1 ) { - // Specification is of the form [-]XX - minutes = arr[0] * 60; + // In browsers that support the onhashchange event we will not bind click + // handlers and instead let the browser do the default behavior (clicking the + // will naturally set the hash, handled by onhashchange. + // But other things that change the hash will also be catched (e.g. using + // the Back and Forward browser navigation). + if ( 'onhashchange' in window ) { + $(window).on( 'hashchange' , function () { + var hash = window.location.hash; + if ( hash.match( /^#mw-prefsection-[\w\-]+/ ) ) { + switchPrefTab( hash.replace( '#mw-prefsection-', '' ) ); + } else if ( hash === '' ) { + switchPrefTab( 'personal', 'noHash' ); + } + }); + // In older browsers we'll bind a click handler as fallback. + // We must not have onhashchange *and* the click handlers, other wise + // the click handler calls switchPrefTab() which sets the hash value, + // which triggers onhashcange and calls switchPrefTab() again. } else { - // Specification is of the form [-]XX:XX - minutes = Math.abs( arr[0] ) * 60 + parseInt( arr[1], 10 ); - if ( arr[0] < 0 ) { - minutes *= -1; - } + $preftoc.on( 'click', 'li a', function ( e ) { + switchPrefTab( $( this ).attr( 'href' ).replace( '#mw-prefsection-', '' ) ); + e.preventDefault(); + }); } - // Gracefully handle non-numbers. - if ( isNaN( minutes ) ) { - return 0; - } else { - return minutes; + + /** + * Timezone functions. + * Guesses Timezone from browser and updates fields onchange + */ + + $tzSelect = $( '#mw-input-wptimecorrection' ); + $tzTextbox = $( '#mw-input-wptimecorrection-other' ); + $localtimeHolder = $( '#wpLocalTime' ); + servertime = parseInt( $( 'input[name="wpServerTime"]' ).val(), 10 ); + + function minutesToHours( min ) { + var tzHour = Math.floor( Math.abs( min ) / 60 ), + tzMin = Math.abs( min ) % 60, + tzString = ( ( min >= 0 ) ? '' : '-' ) + ( ( tzHour < 10 ) ? '0' : '' ) + tzHour + + ':' + ( ( tzMin < 10 ) ? '0' : '' ) + tzMin; + return tzString; } -}; - -var updateTimezoneSelection = function () { - var type = $tzSelect.val(); - if ( type == 'guess' ) { - // Get browser timezone & fill it in - minuteDiff = -new Date().getTimezoneOffset(); - $tzTextbox.val( minutesToHours( minuteDiff ) ); - $tzSelect.val( 'other' ); - $tzTextbox.get( 0 ).disabled = false; - } else if ( type == 'other' ) { - // Grab data from the textbox, parse it. - minuteDiff = hoursToMinutes( $tzTextbox.val() ); - } else { - // Grab data from the $tzSelect value - minuteDiff = parseInt( type.split( '|' )[1], 10 ) || 0; - $tzTextbox.val( minutesToHours( minuteDiff ) ); + + function hoursToMinutes( hour ) { + var minutes, + arr = hour.split( ':' ); + + arr[0] = parseInt( arr[0], 10 ); + + if ( arr.length === 1 ) { + // Specification is of the form [-]XX + minutes = arr[0] * 60; + } else { + // Specification is of the form [-]XX:XX + minutes = Math.abs( arr[0] ) * 60 + parseInt( arr[1], 10 ); + if ( arr[0] < 0 ) { + minutes *= -1; + } + } + // Gracefully handle non-numbers. + if ( isNaN( minutes ) ) { + return 0; + } else { + return minutes; + } } - // Determine local time from server time and minutes difference, for display. - var localTime = servertime + minuteDiff; + function updateTimezoneSelection () { + var minuteDiff, localTime, + type = $tzSelect.val(); + + if ( type === 'guess' ) { + // Get browser timezone & fill it in + minuteDiff = -( new Date().getTimezoneOffset() ); + $tzTextbox.val( minutesToHours( minuteDiff ) ); + $tzSelect.val( 'other' ); + $tzTextbox.prop( 'disabled', false ); + } else if ( type === 'other' ) { + // Grab data from the textbox, parse it. + minuteDiff = hoursToMinutes( $tzTextbox.val() ); + } else { + // Grab data from the $tzSelect value + minuteDiff = parseInt( type.split( '|' )[1], 10 ) || 0; + $tzTextbox.val( minutesToHours( minuteDiff ) ); + } - // Bring time within the [0,1440) range. - while ( localTime < 0 ) { - localTime += 1440; + // Determine local time from server time and minutes difference, for display. + localTime = servertime + minuteDiff; + + // Bring time within the [0,1440) range. + while ( localTime < 0 ) { + localTime += 1440; + } + while ( localTime >= 1440 ) { + localTime -= 1440; + } + $localtimeHolder.text( minutesToHours( localTime ) ); } - while ( localTime >= 1440 ) { - localTime -= 1440; + + if ( $tzSelect.length && $tzTextbox.length ) { + $tzSelect.change( updateTimezoneSelection ); + $tzTextbox.blur( updateTimezoneSelection ); + updateTimezoneSelection(); } - $localtimeHolder.text( minutesToHours( localTime ) ); -}; - -if ( $tzSelect.length && $tzTextbox.length ) { - $tzSelect.change( function () { updateTimezoneSelection(); } ); - $tzTextbox.blur( function () { updateTimezoneSelection(); } ); - updateTimezoneSelection(); -} } ); diff --git a/resources/mediawiki.special/mediawiki.special.recentchanges.js b/resources/mediawiki.special/mediawiki.special.recentchanges.js index 7996d935bd..3cba9d3972 100644 --- a/resources/mediawiki.special/mediawiki.special.recentchanges.js +++ b/resources/mediawiki.special/mediawiki.special.recentchanges.js @@ -1,14 +1,12 @@ -/* JavaScript for Special:RecentChanges */ +/** + * JavaScript for Special:RecentChanges + */ ( function ( mw, $ ) { + var rc, + $checkboxes, + $select; - var checkboxes = [ 'nsassociated', 'nsinvert' ]; - - /** - * @var select {jQuery} - */ - var $select = null; - - var rc = mw.special.recentchanges = { + rc = { /** * Handler to disable/enable the namespace selector checkboxes when the @@ -16,24 +14,24 @@ */ updateCheckboxes: function () { // The option element for the 'all' namespace has an empty value - var isAllNS = $select.find('option:selected').val() === ''; + var isAllNS = $select.find( 'option:selected' ).val() === ''; // Iterates over checkboxes and propagate the selected option - $.each( checkboxes, function ( i, id ) { - $( '#' + id ).prop( 'disabled', isAllNS ); - }); + $checkboxes.prop( 'disabled', isAllNS ); }, init: function () { - // Populate $select = $( '#namespace' ); + $checkboxes = $( '#nsassociated, #nsinvert' ); // Bind to change event, and trigger once to set the initial state of the checkboxes. - $select.change( rc.updateCheckboxes ).change(); + rc.updateCheckboxes(); + $select.change( rc.updateCheckboxes ); } }; - // Run when document is ready $( rc.init ); + mw.special.recentchanges = rc; + }( mediaWiki, jQuery ) ); diff --git a/resources/mediawiki.special/mediawiki.special.search.js b/resources/mediawiki.special/mediawiki.special.search.js index 04954e8dc1..0cca26dba4 100644 --- a/resources/mediawiki.special/mediawiki.special.search.js +++ b/resources/mediawiki.special/mediawiki.special.search.js @@ -1,49 +1,53 @@ /* * JavaScript for Special:Search */ -( function( $, mw ) { $( function() { +( function ( $, mw ) { + $( function () { + var $checkboxes, $headerLinks; -// Emulate HTML5 autofocus behavior in non HTML5 compliant browsers -if ( !( 'autofocus' in document.createElement( 'input' ) ) ) { - $( 'input[autofocus]:first' ).focus(); -} + // Emulate HTML5 autofocus behavior in non HTML5 compliant browsers + if ( !( 'autofocus' in document.createElement( 'input' ) ) ) { + $( 'input[autofocus]' ).eq( 0 ).focus(); + } -// Create check all/none button -var $checkboxes = $('#powersearch input[id^=mw-search-ns]'); -$('#mw-search-togglebox').append( - $('' ) - .attr( { 'href': fb.title.getUrl(), 'target': '_blank' } ) - .css( { 'white-space': 'nowrap' } ); + $feedbackPageLink = $( '' ) + .attr( { + href: fb.title.getUrl(), + target: '_blank' + } ) + .css( { + whiteSpace: 'nowrap' + } ); - var $bugNoteLink = $( '' ).attr( { 'href': '#' } ).click( function () { + $bugNoteLink = $( '' ).attr( { href: '#' } ).click( function () { fb.displayBugs(); } ); - var $bugsListLink = $( '' ).attr( { 'href': fb.bugsListLink, 'target': '_blank' } ); + $bugsListLink = $( '' ).attr( { + href: fb.bugsListLink, + target: '_blank' + } ); // TODO: Use a stylesheet instead of these inline styles this.$dialog = @@ -108,7 +119,7 @@ ), $( '' ).append( mw.msg( 'feedback-adding' ), - $( '
      ' ), + $( '
      ' ), $( '' ) ), $( '' ).msg( @@ -148,9 +159,9 @@ }, displayBugs: function () { - var fb = this; + var fb = this, + bugsButtons = {}; this.display( 'bugs' ); - var bugsButtons = {}; bugsButtons[ mw.msg( 'feedback-bugnew' ) ] = function () { window.open( fb.bugsLink, '_blank' ); }; @@ -163,9 +174,9 @@ }, displayThanks: function () { - var fb = this; + var fb = this, + closeButton = {}; this.display( 'thanks' ); - var closeButton = {}; closeButton[ mw.msg( 'feedback-close' ) ] = function () { fb.$dialog.dialog( 'close' ); }; @@ -181,14 +192,14 @@ * message: {String} */ displayForm: function ( contents ) { - var fb = this; + var fb = this, + formButtons = {}; this.subjectInput.value = ( contents && contents.subject ) ? contents.subject : ''; this.messageInput.value = ( contents && contents.message ) ? contents.message : ''; this.display( 'form' ); // Set up buttons for dialog box. We have to do it the hard way since the json keys are localized - var formButtons = {}; formButtons[ mw.msg( 'feedback-submit' ) ] = function () { fb.submit(); }; @@ -199,10 +210,10 @@ }, displayError: function ( message ) { - var fb = this; + var fb = this, + closeButton = {}; this.display( 'error' ); this.$dialog.find( '.feedback-error-msg' ).msg( message ); - var closeButton = {}; closeButton[ mw.msg( 'feedback-close' ) ] = function () { fb.$dialog.dialog( 'close' ); }; @@ -231,7 +242,7 @@ } } - function err( code, info ) { + function err() { // ajax request failed fb.displayError( 'feedback-error3' ); } diff --git a/resources/mediawiki/mediawiki.hidpi.js b/resources/mediawiki/mediawiki.hidpi.js index 1979573047..ecee450c85 100644 --- a/resources/mediawiki/mediawiki.hidpi.js +++ b/resources/mediawiki/mediawiki.hidpi.js @@ -1,5 +1,5 @@ -$( function() { +jQuery( function ( $ ) { // Apply hidpi images on DOM-ready // Some may have already partly preloaded at low resolution. $( 'body' ).hidpi(); -} ); \ No newline at end of file +} ); diff --git a/resources/mediawiki/mediawiki.htmlform.js b/resources/mediawiki/mediawiki.htmlform.js index a4753b992c..83bf2e3a2a 100644 --- a/resources/mediawiki/mediawiki.htmlform.js +++ b/resources/mediawiki/mediawiki.htmlform.js @@ -1,64 +1,62 @@ /** - * Utility functions for jazzing up HTMLForm elements + * Utility functions for jazzing up HTMLForm elements. */ ( function ( $ ) { -/** - * jQuery plugin to fade or snap to visible state. - * - * @param boolean instantToggle (optional) - * @return jQuery - */ -$.fn.goIn = function ( instantToggle ) { - if ( instantToggle === true ) { - return $(this).show(); - } - return $(this).stop( true, true ).fadeIn(); -}; - -/** - * jQuery plugin to fade or snap to hiding state. - * - * @param boolean instantToggle (optional) - * @return jQuery - */ -$.fn.goOut = function ( instantToggle ) { - if ( instantToggle === true ) { - return $(this).hide(); - } - return $(this).stop( true, true ).fadeOut(); -}; - -/** - * Bind a function to the jQuery object via live(), and also immediately trigger - * the function on the objects with an 'instant' parameter set to true - * @param callback function taking one parameter, which is Bool true when the event - * is called immediately, and the EventArgs object when triggered from an event - */ -$.fn.liveAndTestAtStart = function ( callback ){ - $(this) - .live( 'change', callback ) - .each( function ( index, element ){ - callback.call( this, true ); - } ); -}; - -// Document ready: -$( function () { - - // Animate the SelectOrOther fields, to only show the text field when - // 'other' is selected. - $( '.mw-htmlform-select-or-other' ).liveAndTestAtStart( function ( instant ) { - var $other = $( '#' + $(this).attr( 'id' ) + '-other' ); - $other = $other.add( $other.siblings( 'br' ) ); - if ( $(this).val() === 'other' ) { - $other.goIn( instant ); - } else { - $other.goOut( instant ); + /** + * jQuery plugin to fade or snap to visible state. + * + * @param {boolean} instantToggle [optional] + * @return {jQuery} + */ + $.fn.goIn = function ( instantToggle ) { + if ( instantToggle === true ) { + return $(this).show(); } - }); - -}); - + return $(this).stop( true, true ).fadeIn(); + }; + + /** + * jQuery plugin to fade or snap to hiding state. + * + * @param {boolean} instantToggle [optional] + * @return jQuery + */ + $.fn.goOut = function ( instantToggle ) { + if ( instantToggle === true ) { + return $(this).hide(); + } + return $(this).stop( true, true ).fadeOut(); + }; + + /** + * Bind a function to the jQuery object via live(), and also immediately trigger + * the function on the objects with an 'instant' parameter set to true. + * @param {Function} callback Takes one parameter, which is {true} when the + * event is called immediately, and {jQuery.Event} when triggered from an event. + */ + $.fn.liveAndTestAtStart = function ( callback ){ + $(this) + .live( 'change', callback ) + .each( function () { + callback.call( this, true ); + } ); + }; + + $( function () { + + // Animate the SelectOrOther fields, to only show the text field when + // 'other' is selected. + $( '.mw-htmlform-select-or-other' ).liveAndTestAtStart( function ( instant ) { + var $other = $( '#' + $(this).attr( 'id' ) + '-other' ); + $other = $other.add( $other.siblings( 'br' ) ); + if ( $(this).val() === 'other' ) { + $other.goIn( instant ); + } else { + $other.goOut( instant ); + } + }); + + } ); }( jQuery ) ); diff --git a/resources/mediawiki/mediawiki.jqueryMsg.js b/resources/mediawiki/mediawiki.jqueryMsg.js index 7364829feb..6e2d3b410b 100644 --- a/resources/mediawiki/mediawiki.jqueryMsg.js +++ b/resources/mediawiki/mediawiki.jqueryMsg.js @@ -5,7 +5,8 @@ * @author neilk@wikimedia.org */ ( function ( mw, $ ) { - var slice = Array.prototype.slice, + var oldParser, + slice = Array.prototype.slice, parserDefaults = { magic : { 'SITENAME' : mw.config.get( 'wgSiteName' ) @@ -30,8 +31,8 @@ * @return {jQuery} */ return function ( args ) { - var key = args[0]; - var argsArray = $.isArray( args[1] ) ? args[1] : slice.call( args, 1 ); + var key = args[0], + argsArray = $.isArray( args[1] ) ? args[1] : slice.call( args, 1 ); try { return parser.parse( key, argsArray ); } catch ( e ) { @@ -63,11 +64,11 @@ * is equivalent to * somefunction(a, [b, c, d]) * - * @param {String} message key - * @param {Array} optional replacements (can also specify variadically) - * @return {String} rendered HTML as string + * @param {string} key Message key. + * @param {Array|mixed} replacements Optional variable replacements (variadically or an array). + * @return {string} Rendered HTML. */ - return function ( /* key, replacements */ ) { + return function () { return failableParserFn( arguments ).html(); }; }; @@ -93,11 +94,11 @@ * somefunction(a, [b, c, d]) * * We append to 'this', which in a jQuery plugin context will be the selected elements. - * @param {String} message key - * @param {Array} optional replacements (can also specify variadically) + * @param {string} key Message key. + * @param {Array|mixed} replacements Optional variable replacements (variadically or an array). * @return {jQuery} this */ - return function ( /* key, replacements */ ) { + return function () { var $target = this.empty(); $.each( failableParserFn( arguments ).contents(), function ( i, node ) { $target.append( node ); @@ -125,8 +126,8 @@ * Where the magic happens. * Parses a message from the key, and swaps in replacements as necessary, wraps in jQuery * If an error is thrown, returns original key, and logs the error - * @param {String} message key - * @param {Array} replacements for $1, $2... $n + * @param {String} key Message key. + * @param {Array} replacements Variable replacements for $1, $2... $n * @return {jQuery} */ parse: function ( key, replacements ) { @@ -142,7 +143,7 @@ if ( this.astCache[ key ] === undefined ) { var wikiText = this.settings.messages.get( key ); if ( typeof wikiText !== 'string' ) { - wikiText = "\\[" + key + "\\]"; + wikiText = '\\[' + key + '\\]'; } this.astCache[ key ] = this.wikiTextToAst( wikiText ); } @@ -159,18 +160,27 @@ * @return {Mixed} abstract syntax tree */ wikiTextToAst: function ( input ) { + var pos, + regularLiteral, regularLiteralWithoutBar, regularLiteralWithoutSpace, backslash, anyCharacter, + escapedOrLiteralWithoutSpace, escapedOrLiteralWithoutBar, escapedOrRegularLiteral, + whitespace, dollar, digits, + openExtlink, closeExtlink, openLink, closeLink, templateName, pipe, colon, + templateContents, openTemplate, closeTemplate, + nonWhitespaceExpression, paramExpression, expression, result; // Indicates current position in input as we parse through it. // Shared among all parsing functions below. - var pos = 0; + pos = 0; + // ========================================================= // parsing combinators - could be a library on its own // ========================================================= // Try parsers until one works, if none work return null function choice( ps ) { return function () { - for ( var i = 0; i < ps.length; i++ ) { - var result = ps[i](); + var i, result; + for ( i = 0; i < ps.length; i++ ) { + result = ps[i](); if ( result !== null ) { return result; } @@ -181,10 +191,11 @@ // try several ps in a row, all must succeed or return null // this is the only eager one function sequence( ps ) { - var originalPos = pos; - var result = []; - for ( var i = 0; i < ps.length; i++ ) { - var res = ps[i](); + var i, res, + originalPos = pos, + result = []; + for ( i = 0; i < ps.length; i++ ) { + res = ps[i](); if ( res === null ) { pos = originalPos; return null; @@ -197,9 +208,9 @@ // must succeed a minimum of n times or return null function nOrMore( n, p ) { return function () { - var originalPos = pos; - var result = []; - var parsed = p(); + var originalPos = pos, + result = [], + parsed = p(); while ( parsed !== null ) { result.push( parsed ); parsed = p(); @@ -258,11 +269,11 @@ // but some debuggers can't tell you exactly where they come from. Also the mutually // recursive functions seem not to work in all browsers then. (Tested IE6-7, Opera, Safari, FF) // This may be because, to save code, memoization was removed - var regularLiteral = makeRegexParser( /^[^{}\[\]$\\]/ ); - var regularLiteralWithoutBar = makeRegexParser(/^[^{}\[\]$\\|]/); - var regularLiteralWithoutSpace = makeRegexParser(/^[^{}\[\]$\s]/); - var backslash = makeStringParser( "\\" ); - var anyCharacter = makeRegexParser( /^./ ); + regularLiteral = makeRegexParser( /^[^{}\[\]$\\]/ ); + regularLiteralWithoutBar = makeRegexParser(/^[^{}\[\]$\\|]/); + regularLiteralWithoutSpace = makeRegexParser(/^[^{}\[\]$\s]/); + backslash = makeStringParser( '\\' ); + anyCharacter = makeRegexParser( /^./ ); function escapedLiteral() { var result = sequence( [ backslash, @@ -270,15 +281,15 @@ ] ); return result === null ? null : result[1]; } - var escapedOrLiteralWithoutSpace = choice( [ + escapedOrLiteralWithoutSpace = choice( [ escapedLiteral, regularLiteralWithoutSpace ] ); - var escapedOrLiteralWithoutBar = choice( [ + escapedOrLiteralWithoutBar = choice( [ escapedLiteral, regularLiteralWithoutBar ] ); - var escapedOrRegularLiteral = choice( [ + escapedOrRegularLiteral = choice( [ escapedLiteral, regularLiteral ] ); @@ -297,9 +308,9 @@ var result = nOrMore( 1, escapedOrRegularLiteral )(); return result === null ? null : result.join(''); } - var whitespace = makeRegexParser( /^\s+/ ); - var dollar = makeStringParser( '$' ); - var digits = makeRegexParser( /^\d+/ ); + whitespace = makeRegexParser( /^\s+/ ); + dollar = makeStringParser( '$' ); + digits = makeRegexParser( /^\d+/ ); function replacement() { var result = sequence( [ @@ -311,12 +322,13 @@ } return [ 'REPLACE', parseInt( result[1], 10 ) - 1 ]; } - var openExtlink = makeStringParser( '[' ); - var closeExtlink = makeStringParser( ']' ); + openExtlink = makeStringParser( '[' ); + closeExtlink = makeStringParser( ']' ); // this extlink MUST have inner text, e.g. [foo] not allowed; [foo bar] is allowed function extlink() { - var result = null; - var parsedResult = sequence( [ + var result, parsedResult; + result = null; + parsedResult = sequence( [ openExtlink, nonWhitespaceExpression, whitespace, @@ -343,11 +355,12 @@ } return [ 'LINKPARAM', parseInt( result[2], 10 ) - 1, result[4] ]; } - var openLink = makeStringParser( '[[' ); - var closeLink = makeStringParser( ']]' ); + openLink = makeStringParser( '[[' ); + closeLink = makeStringParser( ']]' ); function link() { - var result = null; - var parsedResult = sequence( [ + var result, parsedResult; + result = null; + parsedResult = sequence( [ openLink, expression, closeLink @@ -357,25 +370,26 @@ } return result; } - var templateName = transform( + templateName = transform( // see $wgLegalTitleChars // not allowing : due to the need to catch "PLURAL:$1" makeRegexParser( /^[ !"$&'()*,.\/0-9;=?@A-Z\^_`a-z~\x80-\xFF+\-]+/ ), function ( result ) { return result.toString(); } ); function templateParam() { - var result = sequence( [ + var expr, result; + result = sequence( [ pipe, nOrMore( 0, paramExpression ) ] ); if ( result === null ) { return null; } - var expr = result[1]; - // use a "CONCAT" operator if there are multiple nodes, otherwise return the first node, raw. - return expr.length > 1 ? [ "CONCAT" ].concat( expr ) : expr[0]; + expr = result[1]; + // use a CONCAT operator if there are multiple nodes, otherwise return the first node, raw. + return expr.length > 1 ? [ 'CONCAT' ].concat( expr ) : expr[0]; } - var pipe = makeStringParser( '|' ); + pipe = makeStringParser( '|' ); function templateWithReplacement() { var result = sequence( [ templateName, @@ -392,8 +406,8 @@ ] ); return result === null ? null : [ result[0], result[2] ]; } - var colon = makeStringParser(':'); - var templateContents = choice( [ + colon = makeStringParser(':'); + templateContents = choice( [ function () { var res = sequence( [ // templates can have placeholders for dynamic replacement eg: {{PLURAL:$1|one car|$1 cars}} @@ -414,8 +428,8 @@ return [ res[0] ].concat( res[1] ); } ] ); - var openTemplate = makeStringParser('{{'); - var closeTemplate = makeStringParser('}}'); + openTemplate = makeStringParser('{{'); + closeTemplate = makeStringParser('}}'); function template() { var result = sequence( [ openTemplate, @@ -424,7 +438,7 @@ ] ); return result === null ? null : result[1]; } - var nonWhitespaceExpression = choice( [ + nonWhitespaceExpression = choice( [ template, link, extLinkParam, @@ -432,7 +446,7 @@ replacement, literalWithoutSpace ] ); - var paramExpression = choice( [ + paramExpression = choice( [ template, link, extLinkParam, @@ -440,7 +454,7 @@ replacement, literalWithoutBar ] ); - var expression = choice( [ + expression = choice( [ template, link, extLinkParam, @@ -453,20 +467,20 @@ if ( result === null ) { return null; } - return [ "CONCAT" ].concat( result ); + return [ 'CONCAT' ].concat( result ); } // everything above this point is supposed to be stateless/static, but // I am deferring the work of turning it into prototypes & objects. It's quite fast enough // finally let's do some actual work... - var result = start(); + result = start(); /* * For success, the p must have gotten to the end of the input * and returned a non-null. * n.b. This is part of language infrastructure, so we do not throw an internationalizable message. */ - if (result === null || pos !== input.length) { - throw new Error( "Parse error at position " + pos.toString() + " in input: " + input ); + if ( result === null || pos !== input.length ) { + throw new Error( 'Parse error at position ' + pos.toString() + ' in input: ' + input ); } return result; } @@ -491,18 +505,20 @@ * @return {Mixed} single-string node or array of nodes suitable for jQuery appending */ this.emit = function ( node, replacements ) { - var ret = null; - var jmsg = this; + var ret, subnodes, operation, + jmsg = this; switch ( typeof node ) { case 'string': case 'number': ret = node; break; - case 'object': // node is an array of nodes - var subnodes = $.map( node.slice( 1 ), function ( n ) { + // typeof returns object for arrays + case 'object': + // node is an array of nodes + subnodes = $.map( node.slice( 1 ), function ( n ) { return jmsg.emit( n, replacements ); } ); - var operation = node[0].toLowerCase(); + operation = node[0].toLowerCase(); if ( typeof jmsg[operation] === 'function' ) { ret = jmsg[ operation ]( subnodes, replacements ); } else { @@ -579,8 +595,9 @@ /** * Transform wiki-link * TODO unimplemented + * @param nodes */ - wlink: function ( nodes ) { + wlink: function () { return 'unimplemented'; }, @@ -594,9 +611,9 @@ * @return {jQuery} */ link: function ( nodes ) { - var arg = nodes[0]; - var contents = nodes[1]; - var $el; + var $el, + arg = nodes[0], + contents = nodes[1]; if ( arg instanceof jQuery ) { $el = arg; } else { @@ -639,8 +656,9 @@ * @return {String} selected pluralized form according to current language */ plural: function ( nodes ) { - var count = parseFloat( this.language.convertNumber( nodes[0], true ) ); - var forms = nodes.slice(1); + var forms, count; + count = parseFloat( this.language.convertNumber( nodes[0], true ) ); + forms = nodes.slice(1); return forms.length ? this.language.convertPlural( count, forms ) : ''; }, @@ -674,8 +692,8 @@ * @return {String} selected grammatical form according to current language */ grammar: function ( nodes ) { - var form = nodes[0]; - var word = nodes[1]; + var form = nodes[0], + word = nodes[1]; return word && form && this.language.convertGrammar( word, form ); } }; @@ -687,7 +705,7 @@ $.fn.msg = mw.jqueryMsg.getPlugin(); // Replace the default message parser with jqueryMsg - var oldParser = mw.Message.prototype.parser; + oldParser = mw.Message.prototype.parser; mw.Message.prototype.parser = function () { // TODO: should we cache the message function so we don't create a new one every time? Benchmark this maybe? // Caching is somewhat problematic, because we do need different message functions for different maps, so diff --git a/resources/mediawiki/mediawiki.js b/resources/mediawiki/mediawiki.js index 690138c0ef..b0abc9e6f6 100644 --- a/resources/mediawiki/mediawiki.js +++ b/resources/mediawiki/mediawiki.js @@ -3,7 +3,7 @@ */ var mw = ( function ( $, undefined ) { - "use strict"; + 'use strict'; /* Private Members */ @@ -290,14 +290,14 @@ var mw = ( function ( $, undefined ) { * Gets a message object, similar to wfMessage() * * @param key string Key of message to get - * @param parameter_1 mixed First argument in a list of variadic arguments, + * @param parameter1 mixed First argument in a list of variadic arguments, * each a parameter for $N replacement in messages. * @return Message */ - message: function ( key, parameter_1 /* [, parameter_2] */ ) { + message: function ( key, parameter1 ) { var parameters; // Support variadic arguments - if ( parameter_1 !== undefined ) { + if ( parameter1 !== undefined ) { parameters = slice.call( arguments ); parameters.shift(); } else { @@ -473,32 +473,14 @@ var mw = ( function ( $, undefined ) { } } - function compare( a, b ) { - var i; - if ( a.length !== b.length ) { - return false; - } - for ( i = 0; i < b.length; i += 1 ) { - if ( $.isArray( a[i] ) ) { - if ( !compare( a[i], b[i] ) ) { - return false; - } - } - if ( a[i] !== b[i] ) { - return false; - } - } - return true; - } - /** * Generates an ISO8601 "basic" string from a UNIX timestamp */ function formatVersionNumber( timestamp ) { - var pad = function ( a, b, c ) { - return [a < 10 ? '0' + a : a, b < 10 ? '0' + b : b, c < 10 ? '0' + c : c].join( '' ); - }, - d = new Date(); + var d = new Date(); + function pad( a, b, c ) { + return [a < 10 ? '0' + a : a, b < 10 ? '0' + b : b, c < 10 ? '0' + c : c].join( '' ); + } d.setTime( timestamp * 1000 ); return [ pad( d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate() ), 'T', @@ -712,7 +694,7 @@ var mw = ( function ( $, undefined ) { j -= 1; try { if ( hasErrors ) { - throw new Error ("Module " + module + " failed."); + throw new Error( 'Module ' + module + ' failed.'); } else { if ( $.isFunction( job.ready ) ) { job.ready(); @@ -957,7 +939,7 @@ var mw = ( function ( $, undefined ) { * document ready has not yet occurred */ function request( dependencies, ready, error, async ) { - var regItemDeps, regItemDepLen, n; + var n; // Allow calling by single module name if ( typeof dependencies === 'string' ) { @@ -1029,7 +1011,7 @@ var mw = ( function ( $, undefined ) { */ function doRequest( moduleMap, currReqBase, sourceLoadScript, async ) { var request = $.extend( - { 'modules': buildModulesString( moduleMap ) }, + { modules: buildModulesString( moduleMap ) }, currReqBase ); request = sortQuery( request ); @@ -1126,7 +1108,7 @@ var mw = ( function ( $, undefined ) { currReqBase = $.extend( { version: formatVersionNumber( maxVersion ) }, reqBase ); // For user modules append a user name to the request. - if ( group === "user" && mw.config.get( 'wgUserName' ) !== null ) { + if ( group === 'user' && mw.config.get( 'wgUserName' ) !== null ) { currReqBase.user = mw.config.get( 'wgUserName' ); } currReqBaseLength = $.param( currReqBase ).length; @@ -1513,7 +1495,7 @@ var mw = ( function ( $, undefined ) { html: ( function () { function escapeCallback( s ) { switch ( s ) { - case "'": + case '\'': return '''; case '"': return '"'; diff --git a/resources/mediawiki/mediawiki.notification.js b/resources/mediawiki/mediawiki.notification.js index 58a3ab6ae2..35cc6d816e 100644 --- a/resources/mediawiki/mediawiki.notification.js +++ b/resources/mediawiki/mediawiki.notification.js @@ -4,7 +4,8 @@ ( function ( mw, $ ) { 'use strict'; - var isPageReady = false, + var notification, + isPageReady = false, isInitialized = false, preReadyNotifQueue = [], /** @@ -88,7 +89,9 @@ // Other notification elements matching the same tag $tagMatches, outerHeight, - placeholderHeight; + placeholderHeight, + autohideCount, + notif; if ( this.isOpen ) { return; @@ -164,10 +167,11 @@ } } ); + notif = this; + // Create a clear placeholder we can use to make the notifications around the notification that is being // replaced expand or contract gracefully to fit the height of the new notification. - var self = this; - self.$replacementPlaceholder = $( '
      ' ) + notif.$replacementPlaceholder = $( '
      ' ) // Set the height to the space the previous notification or placeholder took .css( 'height', outerHeight ) // Make sure that this placeholder is at the very end of this tagged notification group @@ -181,7 +185,7 @@ // Reset the notification position after we've finished the space animation // However do not do it if the placeholder was removed because another tagged // notification went and closed this one. - if ( self.$replacementPlaceholder ) { + if ( notif.$replacementPlaceholder ) { $notification.css( 'position', '' ); } // Finally, remove the placeholder from the DOM @@ -206,7 +210,7 @@ // By default a notification is paused. // If this notification is within the first {autoHideLimit} notifications then // start the auto-hide timer as soon as it's created. - var autohideCount = $area.find( '.mw-notification-autohide' ).length; + autohideCount = $area.find( '.mw-notification-autohide' ).length; if ( autohideCount <= notification.autoHideLimit ) { this.resume(); } @@ -371,7 +375,7 @@ } } - var notification = { + notification = { /** * Pause auto-hide timers for all notifications. * Notifications will not auto-hide until resume is called. diff --git a/resources/mediawiki/mediawiki.util.js b/resources/mediawiki/mediawiki.util.js index 4e515bc518..4bb004a31c 100644 --- a/resources/mediawiki/mediawiki.util.js +++ b/resources/mediawiki/mediawiki.util.js @@ -62,7 +62,8 @@ /* Fill $content var */ util.$content = ( function () { - var $content, selectors = [ + var i, l, $content, selectors; + selectors = [ // The preferred standard for setting $content (class="mw-body") // You may also use (class="mw-body mw-body-primary") if you use // mw-body in multiple locations. @@ -94,7 +95,7 @@ // not inserted bodytext yet. But in any case should always exist 'body' ]; - for ( var i = 0, l = selectors.length; i < l; i++ ) { + for ( i = 0, l = selectors.length; i < l; i++ ) { $content = $( selectors[i] ).first(); if ( $content.length ) { return $content; @@ -484,7 +485,7 @@ * is determined by validation. */ validateEmail: function ( mailtxt ) { - var rfc5322_atext, rfc1034_ldh_str, HTML5_email_regexp; + var rfc5322Atext, rfc1034LdhStr, html5EmailRegexp; if ( mailtxt === '' ) { return null; @@ -515,7 +516,7 @@ "|" / "}" / "~" */ - rfc5322_atext = "a-z0-9!#$%&'*+\\-/=?^_`{|}~"; + rfc5322Atext = 'a-z0-9!#$%&\'*+\\-/=?^_`{|}~'; /** * Next define the RFC 1034 'ldh-str' @@ -526,30 +527,30 @@ * ::= | "-" * ::= | */ - rfc1034_ldh_str = "a-z0-9\\-"; + rfc1034LdhStr = 'a-z0-9\\-'; - HTML5_email_regexp = new RegExp( + html5EmailRegexp = new RegExp( // start of string '^' + // User part which is liberal :p - '[' + rfc5322_atext + '\\.]+' + '[' + rfc5322Atext + '\\.]+' + // 'at' '@' + // Domain first part - '[' + rfc1034_ldh_str + ']+' + '[' + rfc1034LdhStr + ']+' + // Optional second part and following are separated by a dot - '(?:\\.[' + rfc1034_ldh_str + ']+)*' + '(?:\\.[' + rfc1034LdhStr + ']+)*' + // End of string '$', // RegExp is case insensitive 'i' ); - return (null !== mailtxt.match( HTML5_email_regexp ) ); + return (null !== mailtxt.match( html5EmailRegexp ) ); }, /** diff --git a/resources/startup.js b/resources/startup.js index 7951af06b8..deff7e6eff 100644 --- a/resources/startup.js +++ b/resources/startup.js @@ -3,19 +3,22 @@ * continue loading the jquery and mediawiki modules. This code should work on * even the most ancient of browsers, so be very careful when editing. */ + /** * Returns false when run in a black-listed browser * * This function will be deleted after it's used, so do not expand it to be - * generally useful beyond startup + * generally useful beyond startup. * - * jQuery has minimum requirements of: - * * Internet Explorer 6.0+ - * * Firefox 3.6+ - * * Safari 5.0+ - * * Opera 11+ - * * Chrome + * MediaWiki & jQuery compatibility: + * - Internet Explorer 6.0+ + * - Firefox 10+ + * - Safari 5.0+ + * - Opera 11+ + * - Chrome */ + +/*jshint unused: false */ function isCompatible() { // IE < 6.0 if ( navigator.appVersion.indexOf( 'MSIE' ) !== -1 @@ -23,11 +26,9 @@ function isCompatible() { { return false; } - // @todo FIXME: Firefox < 3.6 - // @todo FIXME: Safari < 5.0 - // @todo FIXME: Opera < 11 return true; } + /** - * The startUp() function will be generated and added here (at the bottom) + * The startUp() function will be auto-generated and added below. */ diff --git a/tests/qunit/data/callMwLoaderTestCallback.js b/tests/qunit/data/callMwLoaderTestCallback.js index 3f2ee92ffe..dd03411594 100644 --- a/tests/qunit/data/callMwLoaderTestCallback.js +++ b/tests/qunit/data/callMwLoaderTestCallback.js @@ -1 +1 @@ -mw.loader.testCallback(); +mediaWiki.loader.testCallback(); diff --git a/tests/qunit/data/generateJqueryMsgData.php b/tests/qunit/data/generateJqueryMsgData.php index 7079e0e7d7..911dc3ac0c 100644 --- a/tests/qunit/data/generateJqueryMsgData.php +++ b/tests/qunit/data/generateJqueryMsgData.php @@ -135,6 +135,9 @@ class GenerateJqueryMsgData extends Maintenance { . "// languages, and parser modes. Intended for use by a unit test framework by looping\n" . "// through the object and comparing its parser return value with the 'result' property.\n" . '// Last generated with ' . basename( __FILE__ ) . ' at ' . gmdate( 'r' ) . "\n" + // This file will contain unquoted JSON strings as javascript native object literals, + // flip the quotemark convention for this file. + . "/*jshint quotmark: double */\n" . "\n" . 'mediaWiki.libs.phpParserData = ' . FormatJson::encode( $phpParserData, true ) . ";\n"; diff --git a/tests/qunit/data/mediawiki.jqueryMsg.data.js b/tests/qunit/data/mediawiki.jqueryMsg.data.js index 05bb5c8284..776ee24f03 100644 --- a/tests/qunit/data/mediawiki.jqueryMsg.data.js +++ b/tests/qunit/data/mediawiki.jqueryMsg.data.js @@ -1,7 +1,8 @@ // This file stores the output from the PHP parser for various messages, arguments, // languages, and parser modes. Intended for use by a unit test framework by looping // through the object and comparing its parser return value with the 'result' property. -// Last generated with generateJqueryMsgData.php at Sun, 07 Oct 2012 07:30:16 +0000 +// Last generated with generateJqueryMsgData.php at Sat, 03 Nov 2012 21:32:01 +0000 +/*jshint quotmark: double */ mediaWiki.libs.phpParserData = { "messages": { diff --git a/tests/qunit/data/testrunner.js b/tests/qunit/data/testrunner.js index efa6549379..0c3d364a18 100644 --- a/tests/qunit/data/testrunner.js +++ b/tests/qunit/data/testrunner.js @@ -1,266 +1,268 @@ -( function ( $, mw, QUnit, undefined ) { /*global CompletenessTest */ -/*jshint evil:true */ -'use strict'; - -var mwTestIgnore, mwTester, addons; - -/** - * Add bogus to url to prevent IE crazy caching - * - * @param value {String} a relative path (eg. 'data/foo.js' - * or 'data/test.php?foo=bar'). - * @return {String} Such as 'data/foo.js?131031765087663960' - */ -QUnit.fixurl = function ( value ) { - return value + (/\?/.test( value ) ? '&' : '?') - + String( new Date().getTime() ) - + String( parseInt( Math.random() * 100000, 10 ) ); -}; - -/** - * Configuration - */ - -// When a test() indicates asynchronicity with stop(), -// allow 10 seconds to pass before killing the test(), -// and assuming failure. -QUnit.config.testTimeout = 10 * 1000; - -// Add a checkbox to QUnit header to toggle MediaWiki ResourceLoader debug mode. -QUnit.config.urlConfig.push( { - id: 'debug', - label: 'Enable ResourceLoaderDebug', - tooltip: 'Enable debug mode in ResourceLoader' -} ); - -/** - * Load TestSwarm agent - */ -// Only if the current url indicates that there is a TestSwarm instance watching us -// (TestSwarm appends swarmURL to the test suites url it loads in iframes). -// Otherwise this is just a simple view of Special:JavaScriptTest/qunit directly, -// no point in loading inject.js in that case. Also, make sure that this instance -// of MediaWiki has actually been configured with the required url to that inject.js -// script. By default it is false. -if ( QUnit.urlParams.swarmURL && mw.config.get( 'QUnitTestSwarmInjectJSPath' ) ) { - document.write( "" ); -} - -/** - * CompletenessTest - */ -// Adds toggle checkbox to header -QUnit.config.urlConfig.push( { - id: 'completenesstest', - label: 'Run CompletenessTest', - tooltip: 'Run the completeness test' -} ); - -// Initiate when enabled -if ( QUnit.urlParams.completenesstest ) { - - // Return true to ignore - mwTestIgnore = function ( val, tester, funcPath ) { - - // Don't record methods of the properties of constructors, - // to avoid getting into a loop (prototype.constructor.prototype..). - // Since we're therefor skipping any injection for - // "new mw.Foo()", manually set it to true here. - if ( val instanceof mw.Map ) { - tester.methodCallTracker.Map = true; - return true; - } - if ( val instanceof mw.Title ) { - tester.methodCallTracker.Title = true; - return true; - } - - // Don't record methods of the properties of a jQuery object - if ( val instanceof $ ) { - return true; - } - - return false; +/*jshint evil: true */ +( function ( $, mw, QUnit, undefined ) { + 'use strict'; + + var mwTestIgnore, mwTester, + addons, + envExecCount; + + /** + * Add bogus to url to prevent IE crazy caching + * + * @param value {String} a relative path (eg. 'data/foo.js' + * or 'data/test.php?foo=bar'). + * @return {String} Such as 'data/foo.js?131031765087663960' + */ + QUnit.fixurl = function ( value ) { + return value + (/\?/.test( value ) ? '&' : '?') + + String( new Date().getTime() ) + + String( parseInt( Math.random() * 100000, 10 ) ); }; - mwTester = new CompletenessTest( mw, mwTestIgnore ); -} - -/** - * Test environment recommended for all QUnit test modules - */ -// Whether to log environment changes to the console -QUnit.config.urlConfig.push( 'mwlogenv' ); - -/** - * Reset mw.config and others to a fresh copy of the live config for each test(), - * and restore it back to the live one afterwards. - * @param localEnv {Object} [optional] - * @example (see test suite at the bottom of this file) - * - */ -QUnit.newMwEnvironment = ( function () { - var log, liveConfig, liveMessages; - - liveConfig = mw.config.values; - liveMessages = mw.messages.values; - - function freshConfigCopy( custom ) { - // "deep=true" is important here. - // Otherwise we just create a new object with values referring to live config. - // e.g. mw.config.set( 'wgFileExtensions', [] ) would not effect liveConfig, - // but mw.config.get( 'wgFileExtensions' ).push( 'png' ) would as the array - // was passed by reference in $.extend's loop. - return $.extend( {}, liveConfig, custom, /*deep=*/true ); + /** + * Configuration + */ + + // When a test() indicates asynchronicity with stop(), + // allow 10 seconds to pass before killing the test(), + // and assuming failure. + QUnit.config.testTimeout = 10 * 1000; + + // Add a checkbox to QUnit header to toggle MediaWiki ResourceLoader debug mode. + QUnit.config.urlConfig.push( { + id: 'debug', + label: 'Enable ResourceLoaderDebug', + tooltip: 'Enable debug mode in ResourceLoader' + } ); + + /** + * Load TestSwarm agent + */ + // Only if the current url indicates that there is a TestSwarm instance watching us + // (TestSwarm appends swarmURL to the test suites url it loads in iframes). + // Otherwise this is just a simple view of Special:JavaScriptTest/qunit directly, + // no point in loading inject.js in that case. Also, make sure that this instance + // of MediaWiki has actually been configured with the required url to that inject.js + // script. By default it is false. + if ( QUnit.urlParams.swarmURL && mw.config.get( 'QUnitTestSwarmInjectJSPath' ) ) { + document.write( '' ); } - function freshMessagesCopy( custom ) { - return $.extend( {}, liveMessages, custom, /*deep=*/true ); + /** + * CompletenessTest + */ + // Adds toggle checkbox to header + QUnit.config.urlConfig.push( { + id: 'completenesstest', + label: 'Run CompletenessTest', + tooltip: 'Run the completeness test' + } ); + + // Initiate when enabled + if ( QUnit.urlParams.completenesstest ) { + + // Return true to ignore + mwTestIgnore = function ( val, tester ) { + + // Don't record methods of the properties of constructors, + // to avoid getting into a loop (prototype.constructor.prototype..). + // Since we're therefor skipping any injection for + // "new mw.Foo()", manually set it to true here. + if ( val instanceof mw.Map ) { + tester.methodCallTracker.Map = true; + return true; + } + if ( val instanceof mw.Title ) { + tester.methodCallTracker.Title = true; + return true; + } + + // Don't record methods of the properties of a jQuery object + if ( val instanceof $ ) { + return true; + } + + return false; + }; + + mwTester = new CompletenessTest( mw, mwTestIgnore ); } - log = QUnit.urlParams.mwlogenv ? mw.log : function () {}; + /** + * Test environment recommended for all QUnit test modules + */ + // Whether to log environment changes to the console + QUnit.config.urlConfig.push( 'mwlogenv' ); + + /** + * Reset mw.config and others to a fresh copy of the live config for each test(), + * and restore it back to the live one afterwards. + * @param localEnv {Object} [optional] + * @example (see test suite at the bottom of this file) + * + */ + QUnit.newMwEnvironment = ( function () { + var log, liveConfig, liveMessages; + + liveConfig = mw.config.values; + liveMessages = mw.messages.values; + + function freshConfigCopy( custom ) { + // "deep=true" is important here. + // Otherwise we just create a new object with values referring to live config. + // e.g. mw.config.set( 'wgFileExtensions', [] ) would not effect liveConfig, + // but mw.config.get( 'wgFileExtensions' ).push( 'png' ) would as the array + // was passed by reference in $.extend's loop. + return $.extend( {}, liveConfig, custom, /*deep=*/true ); + } - return function ( localEnv ) { - localEnv = $.extend( { - // QUnit - setup: $.noop, - teardown: $.noop, - // MediaWiki - config: {}, - messages: {} - }, localEnv ); + function freshMessagesCopy( custom ) { + return $.extend( {}, liveMessages, custom, /*deep=*/true ); + } - return { - setup: function () { - log( 'MwEnvironment> SETUP for "' + QUnit.config.current.module - + ': ' + QUnit.config.current.testName + '"' ); + log = QUnit.urlParams.mwlogenv ? mw.log : function () {}; + + return function ( localEnv ) { + localEnv = $.extend( { + // QUnit + setup: $.noop, + teardown: $.noop, + // MediaWiki + config: {}, + messages: {} + }, localEnv ); + + return { + setup: function () { + log( 'MwEnvironment> SETUP for "' + QUnit.config.current.module + + ': ' + QUnit.config.current.testName + '"' ); + + // Greetings, mock environment! + mw.config.values = freshConfigCopy( localEnv.config ); + mw.messages.values = freshMessagesCopy( localEnv.messages ); + + localEnv.setup(); + }, + + teardown: function () { + log( 'MwEnvironment> TEARDOWN for "' + QUnit.config.current.module + + ': ' + QUnit.config.current.testName + '"' ); + + localEnv.teardown(); + + // Farewell, mock environment! + mw.config.values = liveConfig; + mw.messages.values = liveMessages; + } + }; + }; + }() ); - // Greetings, mock environment! - mw.config.values = freshConfigCopy( localEnv.config ); - mw.messages.values = freshMessagesCopy( localEnv.messages ); + // $.when stops as soon as one fails, which makes sense in most + // practical scenarios, but not in a unit test where we really do + // need to wait until all of them are finished. + QUnit.whenPromisesComplete = function () { + var altPromises = []; - localEnv.setup(); - }, + $.each( arguments, function ( i, arg ) { + var alt = $.Deferred(); + altPromises.push( alt ); - teardown: function () { - log( 'MwEnvironment> TEARDOWN for "' + QUnit.config.current.module - + ': ' + QUnit.config.current.testName + '"' ); + // Whether this one fails or not, forwards it to + // the 'done' (resolve) callback of the alternative promise. + arg.always( alt.resolve ); + }); - localEnv.teardown(); + return $.when.apply( $, altPromises ); + }; - // Farewell, mock environment! - mw.config.values = liveConfig; - mw.messages.values = liveMessages; - } - }; + /** + * Add-on assertion helpers + */ + // Define the add-ons + addons = { + + // Expect boolean true + assertTrue: function ( actual, message ) { + QUnit.push( actual === true, actual, true, message ); + }, + + // Expect boolean false + assertFalse: function ( actual, message ) { + QUnit.push( actual === false, actual, false, message ); + }, + + // Expect numerical value less than X + lt: function ( actual, expected, message ) { + QUnit.push( actual < expected, actual, 'less than ' + expected, message ); + }, + + // Expect numerical value less than or equal to X + ltOrEq: function ( actual, expected, message ) { + QUnit.push( actual <= expected, actual, 'less than or equal to ' + expected, message ); + }, + + // Expect numerical value greater than X + gt: function ( actual, expected, message ) { + QUnit.push( actual > expected, actual, 'greater than ' + expected, message ); + }, + + // Expect numerical value greater than or equal to X + gtOrEq: function ( actual, expected, message ) { + QUnit.push( actual >= expected, actual, 'greater than or equal to ' + expected, message ); + } }; -}() ); -// $.when stops as soon as one fails, which makes sense in most -// practical scenarios, but not in a unit test where we really do -// need to wait until all of them are finished. -QUnit.whenPromisesComplete = function () { - var altPromises = []; + $.extend( QUnit.assert, addons ); + + /** + * Small test suite to confirm proper functionality of the utilities and + * initializations defined above in this file. + */ + envExecCount = 0; + QUnit.module( 'mediawiki.tests.qunit.testrunner', QUnit.newMwEnvironment({ + setup: function () { + envExecCount += 1; + this.mwHtmlLive = mw.html; + mw.html = { + escape: function () { + return 'mocked-' + envExecCount; + } + }; + }, + teardown: function () { + mw.html = this.mwHtmlLive; + }, + config: { + testVar: 'foo' + }, + messages: { + testMsg: 'Foo.' + } + }) ); - $.each( arguments, function ( i, arg ) { - var alt = $.Deferred(); - altPromises.push( alt ); + QUnit.test( 'Setup', 3, function ( assert ) { + assert.equal( mw.html.escape( 'foo' ), 'mocked-1', 'extra setup() callback was ran.' ); + assert.equal( mw.config.get( 'testVar' ), 'foo', 'config object applied' ); + assert.equal( mw.messages.get( 'testMsg' ), 'Foo.', 'messages object applied' ); - // Whether this one fails or not, forwards it to - // the 'done' (resolve) callback of the alternative promise. - arg.always( alt.resolve ); + mw.config.set( 'testVar', 'bar' ); + mw.messages.set( 'testMsg', 'Bar.' ); }); - return $.when.apply( $, altPromises ); -}; - -/** - * Add-on assertion helpers - */ -// Define the add-ons -addons = { - - // Expect boolean true - assertTrue: function ( actual, message ) { - QUnit.push( actual === true, actual, true, message ); - }, - - // Expect boolean false - assertFalse: function ( actual, message ) { - QUnit.push( actual === false, actual, false, message ); - }, - - // Expect numerical value less than X - lt: function ( actual, expected, message ) { - QUnit.push( actual < expected, actual, 'less than ' + expected, message ); - }, - - // Expect numerical value less than or equal to X - ltOrEq: function ( actual, expected, message ) { - QUnit.push( actual <= expected, actual, 'less than or equal to ' + expected, message ); - }, - - // Expect numerical value greater than X - gt: function ( actual, expected, message ) { - QUnit.push( actual > expected, actual, 'greater than ' + expected, message ); - }, - - // Expect numerical value greater than or equal to X - gtOrEq: function ( actual, expected, message ) { - QUnit.push( actual >= expected, actual, 'greater than or equal to ' + expected, message ); - } -}; - -$.extend( QUnit.assert, addons ); - -/** - * Small test suite to confirm proper functionality of the utilities and - * initializations in this file. - */ -var envExecCount = 0; -QUnit.module( 'mediawiki.tests.qunit.testrunner', QUnit.newMwEnvironment({ - setup: function () { - envExecCount += 1; - this.mwHtmlLive = mw.html; - mw.html = { - escape: function () { - return 'mocked-' + envExecCount; - } - }; - }, - teardown: function () { - mw.html = this.mwHtmlLive; - }, - config: { - testVar: 'foo' - }, - messages: { - testMsg: 'Foo.' - } -}) ); - -QUnit.test( 'Setup', 3, function ( assert ) { - assert.equal( mw.html.escape( 'foo' ), 'mocked-1', 'extra setup() callback was ran.' ); - assert.equal( mw.config.get( 'testVar' ), 'foo', 'config object applied' ); - assert.equal( mw.messages.get( 'testMsg' ), 'Foo.', 'messages object applied' ); - - mw.config.set( 'testVar', 'bar' ); - mw.messages.set( 'testMsg', 'Bar.' ); -}); - -QUnit.test( 'Teardown', 3, function ( assert ) { - assert.equal( mw.html.escape( 'foo' ), 'mocked-2', 'extra setup() callback was re-ran.' ); - assert.equal( mw.config.get( 'testVar' ), 'foo', 'config object restored and re-applied after test()' ); - assert.equal( mw.messages.get( 'testMsg' ), 'Foo.', 'messages object restored and re-applied after test()' ); -}); - -QUnit.module( 'mediawiki.tests.qunit.testrunner-after', QUnit.newMwEnvironment() ); - -QUnit.test( 'Teardown', 3, function ( assert ) { - assert.equal( mw.html.escape( '<' ), '<', 'extra teardown() callback was ran.' ); - assert.equal( mw.config.get( 'testVar' ), null, 'config object restored to live in next module()' ); - assert.equal( mw.messages.get( 'testMsg' ), null, 'messages object restored to live in next module()' ); -}); + QUnit.test( 'Teardown', 3, function ( assert ) { + assert.equal( mw.html.escape( 'foo' ), 'mocked-2', 'extra setup() callback was re-ran.' ); + assert.equal( mw.config.get( 'testVar' ), 'foo', 'config object restored and re-applied after test()' ); + assert.equal( mw.messages.get( 'testMsg' ), 'Foo.', 'messages object restored and re-applied after test()' ); + }); + + QUnit.module( 'mediawiki.tests.qunit.testrunner-after', QUnit.newMwEnvironment() ); + + QUnit.test( 'Teardown', 3, function ( assert ) { + assert.equal( mw.html.escape( '<' ), '<', 'extra teardown() callback was ran.' ); + assert.equal( mw.config.get( 'testVar' ), null, 'config object restored to live in next module()' ); + assert.equal( mw.messages.get( 'testMsg' ), null, 'messages object restored to live in next module()' ); + }); }( jQuery, mediaWiki, QUnit ) ); diff --git a/tests/qunit/suites/resources/jquery/jquery.autoEllipsis.test.js b/tests/qunit/suites/resources/jquery/jquery.autoEllipsis.test.js index 0dee2ef0ba..feeec559ad 100644 --- a/tests/qunit/suites/resources/jquery/jquery.autoEllipsis.test.js +++ b/tests/qunit/suites/resources/jquery/jquery.autoEllipsis.test.js @@ -1,52 +1,58 @@ -( function ( mw, $ ) { +( function ( $ ) { -QUnit.module( 'jquery.autoEllipsis', QUnit.newMwEnvironment() ); + QUnit.module( 'jquery.autoEllipsis', QUnit.newMwEnvironment() ); -function createWrappedDiv( text, width ) { - var $wrapper = $( '
      ' ).css( 'width', width ); - var $div = $( '
      ' ).text( text ); - $wrapper.append( $div ); - return $wrapper; -} - -function findDivergenceIndex( a, b ) { - var i = 0; - while ( i < a.length && i < b.length && a[i] === b[i] ) { - i++; + function createWrappedDiv( text, width ) { + var $wrapper = $( '
      ' ).css( 'width', width ), + $div = $( '
      ' ).text( text ); + $wrapper.append( $div ); + return $wrapper; } - return i; -} - -QUnit.test( 'Position right', 4, function ( assert ) { - // We need this thing to be visible, so append it to the DOM - var origText = 'This is a really long random string and there is no way it fits in 100 pixels.'; - var $wrapper = createWrappedDiv( origText, '100px' ); - $( '#qunit-fixture' ).append( $wrapper ); - $wrapper.autoEllipsis( { position: 'right' } ); - - // Verify that, and only one, span element was created - var $span = $wrapper.find( '> span' ); - assert.strictEqual( $span.length, 1, 'autoEllipsis wrapped the contents in a span element' ); - - // Check that the text fits by turning on word wrapping - $span.css( 'whiteSpace', 'nowrap' ); - assert.ltOrEq( $span.width(), $span.parent().width(), "Text fits (making the span 'white-space:nowrap' does not make it wider than its parent)" ); - - // Add two characters using scary black magic - var spanText = $span.text(); - var d = findDivergenceIndex( origText, spanText ); - var spanTextNew = spanText.substr( 0, d ) + origText[d] + origText[d] + '...'; - - assert.gt( spanTextNew.length, spanText.length, 'Verify that the new span-length is indeed greater' ); - - // Put this text in the span and verify it doesn't fit - $span.text( spanTextNew ); - // In IE6 width works like min-width, allow IE6's width to be "equal to" - if ( $.browser.msie && Number( $.browser.version ) === 6 ) { - assert.gtOrEq( $span.width(), $span.parent().width(), 'Fit is maximal (adding two characters makes it not fit any more) - IE6: Maybe equal to as well due to width behaving like min-width in IE6' ); - } else { - assert.gt( $span.width(), $span.parent().width(), 'Fit is maximal (adding two characters makes it not fit any more)' ); + + function findDivergenceIndex( a, b ) { + var i = 0; + while ( i < a.length && i < b.length && a[i] === b[i] ) { + i++; + } + return i; } -}); -}( mediaWiki, jQuery ) ); + QUnit.test( 'Position right', 4, function ( assert ) { + // We need this thing to be visible, so append it to the DOM + var $span, spanText, d, spanTextNew, + origText = 'This is a really long random string and there is no way it fits in 100 pixels.', + $wrapper = createWrappedDiv( origText, '100px' ); + + $( '#qunit-fixture' ).append( $wrapper ); + $wrapper.autoEllipsis( { position: 'right' } ); + + // Verify that, and only one, span element was created + $span = $wrapper.find( '> span' ); + assert.strictEqual( $span.length, 1, 'autoEllipsis wrapped the contents in a span element' ); + + // Check that the text fits by turning on word wrapping + $span.css( 'whiteSpace', 'nowrap' ); + assert.ltOrEq( + $span.width(), + $span.parent().width(), + 'Text fits (making the span "white-space: nowrap" does not make it wider than its parent)' + ); + + // Add two characters using scary black magic + spanText = $span.text(); + d = findDivergenceIndex( origText, spanText ); + spanTextNew = spanText.substr( 0, d ) + origText[d] + origText[d] + '...'; + + assert.gt( spanTextNew.length, spanText.length, 'Verify that the new span-length is indeed greater' ); + + // Put this text in the span and verify it doesn't fit + $span.text( spanTextNew ); + // In IE6 width works like min-width, allow IE6's width to be "equal to" + if ( $.browser.msie && Number( $.browser.version ) === 6 ) { + assert.gtOrEq( $span.width(), $span.parent().width(), 'Fit is maximal (adding two characters makes it not fit any more) - IE6: Maybe equal to as well due to width behaving like min-width in IE6' ); + } else { + assert.gt( $span.width(), $span.parent().width(), 'Fit is maximal (adding two characters makes it not fit any more)' ); + } + }); + +}( jQuery ) ); diff --git a/tests/qunit/suites/resources/jquery/jquery.byteLength.test.js b/tests/qunit/suites/resources/jquery/jquery.byteLength.test.js index 8d4ac034cc..378ea4b107 100644 --- a/tests/qunit/suites/resources/jquery/jquery.byteLength.test.js +++ b/tests/qunit/suites/resources/jquery/jquery.byteLength.test.js @@ -1,33 +1,35 @@ -QUnit.module( 'jquery.byteLength', QUnit.newMwEnvironment() ); +( function ( $ ) { + QUnit.module( 'jquery.byteLength', QUnit.newMwEnvironment() ); -QUnit.test( 'Simple text', 5, function ( assert ) { - var azLc = 'abcdefghijklmnopqrstuvwxyz', - azUc = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', - num = '0123456789', - x = '*', - space = ' '; + QUnit.test( 'Simple text', 5, function ( assert ) { + var azLc = 'abcdefghijklmnopqrstuvwxyz', + azUc = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', + num = '0123456789', + x = '*', + space = ' '; - assert.equal( $.byteLength( azLc ), 26, 'Lowercase a-z' ); - assert.equal( $.byteLength( azUc ), 26, 'Uppercase A-Z' ); - assert.equal( $.byteLength( num ), 10, 'Numbers 0-9' ); - assert.equal( $.byteLength( x ), 1, 'An asterisk' ); - assert.equal( $.byteLength( space ), 3, '3 spaces' ); + assert.equal( $.byteLength( azLc ), 26, 'Lowercase a-z' ); + assert.equal( $.byteLength( azUc ), 26, 'Uppercase A-Z' ); + assert.equal( $.byteLength( num ), 10, 'Numbers 0-9' ); + assert.equal( $.byteLength( x ), 1, 'An asterisk' ); + assert.equal( $.byteLength( space ), 3, '3 spaces' ); -} ); + } ); -QUnit.test( 'Special text', 5, function ( assert ) { - // http://en.wikipedia.org/wiki/UTF-8 - var U_0024 = '\u0024', - U_00A2 = '\u00A2', - U_20AC = '\u20AC', - U_024B62 = '\u024B62', - // The normal one doesn't display properly, try the below which is the same - // according to http://www.fileformat.info/info/unicode/char/24B62/index.htm - U_024B62_alt = '\uD852\uDF62'; + QUnit.test( 'Special text', 5, function ( assert ) { + // http://en.wikipedia.org/wiki/UTF-8 + var u0024 = '$', + u00A2 = '\u00A2', + u20AC = '\u20AC', + u024B62 = '\u024B62', + // The normal one doesn't display properly, try the below which is the same + // according to http://www.fileformat.info/info/unicode/char/24B62/index.htm + u024B62alt = '\uD852\uDF62'; - assert.strictEqual( $.byteLength( U_0024 ), 1, 'U+0024: 1 byte. \u0024 (dollar sign)' ); - assert.strictEqual( $.byteLength( U_00A2 ), 2, 'U+00A2: 2 bytes. \u00A2 (cent sign)' ); - assert.strictEqual( $.byteLength( U_20AC ), 3, 'U+20AC: 3 bytes. \u20AC (euro sign)' ); - assert.strictEqual( $.byteLength( U_024B62 ), 4, 'U+024B62: 4 bytes. \uD852\uDF62 (a Han character)' ); - assert.strictEqual( $.byteLength( U_024B62_alt ), 4, 'U+024B62: 4 bytes. \uD852\uDF62 (a Han character) - alternative method' ); -} ); + assert.strictEqual( $.byteLength( u0024 ), 1, 'U+0024: 1 byte. $ (dollar sign)' ); + assert.strictEqual( $.byteLength( u00A2 ), 2, 'U+00A2: 2 bytes. \u00A2 (cent sign)' ); + assert.strictEqual( $.byteLength( u20AC ), 3, 'U+20AC: 3 bytes. \u20AC (euro sign)' ); + assert.strictEqual( $.byteLength( u024B62 ), 4, 'U+024B62: 4 bytes. \uD852\uDF62 (a Han character)' ); + assert.strictEqual( $.byteLength( u024B62alt ), 4, 'U+024B62: 4 bytes. \uD852\uDF62 (a Han character) - alternative method' ); + } ); +}( jQuery ) ); diff --git a/tests/qunit/suites/resources/jquery/jquery.byteLimit.test.js b/tests/qunit/suites/resources/jquery/jquery.byteLimit.test.js index 4f86eb969a..4fe6770296 100644 --- a/tests/qunit/suites/resources/jquery/jquery.byteLimit.test.js +++ b/tests/qunit/suites/resources/jquery/jquery.byteLimit.test.js @@ -15,12 +15,13 @@ // Basic sendkey-implementation function addChars( $input, charstr ) { var c, len; + function x( $input, i ) { + // Add character to the value + return $input.val() + charstr.charAt( i ); + } for ( c = 0, len = charstr.length; c < len; c += 1 ) { $input - .val( function ( i, val ) { - // Add character to the value - return val + charstr.charAt( c ); - } ) + .val( x( $input, c ) ) .trigger( 'change' ); } } diff --git a/tests/qunit/suites/resources/jquery/jquery.client.test.js b/tests/qunit/suites/resources/jquery/jquery.client.test.js index bf62b39ab4..bbc88525a5 100644 --- a/tests/qunit/suites/resources/jquery/jquery.client.test.js +++ b/tests/qunit/suites/resources/jquery/jquery.client.test.js @@ -1,317 +1,321 @@ -QUnit.module( 'jquery.client', QUnit.newMwEnvironment() ); +( function ( $ ) { + var uacount, uas, testMap; -/** Number of user-agent defined */ -var uacount = 0; + QUnit.module( 'jquery.client', QUnit.newMwEnvironment() ); -var uas = (function () { + /** Number of user-agent defined */ + uacount = 0; - // Object keyed by userAgent. Value is an array (human-readable name, client-profile object, navigator.platform value) - // Info based on results from http://toolserver.org/~krinkle/testswarm/job/174/ - var uas = { - // Internet Explorer 6 - // Internet Explorer 7 - 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)': { - title: 'Internet Explorer 7', - platform: 'Win32', - profile: { - "name": "msie", - "layout": "trident", - "layoutVersion": "unknown", - "platform": "win", - "version": "7.0", - "versionBase": "7", - "versionNumber": 7 - }, - wikiEditor: { - ltr: true, - rtl: false - } - }, - // Internet Explorer 8 - // Internet Explorer 9 - // Internet Explorer 10 - 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)': { - title: 'Internet Explorer 10', - platform: 'Win32', - profile: { - "name": "msie", - "layout": "trident", - "layoutVersion": "unknown", // should be able to report 6? - "platform": "win", - "version": "10.0", - "versionBase": "10", - "versionNumber": 10 + uas = ( function () { + + // Object keyed by userAgent. Value is an array (human-readable name, client-profile object, navigator.platform value) + // Info based on results from http://toolserver.org/~krinkle/testswarm/job/174/ + var uas = { + // Internet Explorer 6 + // Internet Explorer 7 + 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)': { + title: 'Internet Explorer 7', + platform: 'Win32', + profile: { + name: 'msie', + layout: 'trident', + layoutVersion: 'unknown', + platform: 'win', + version: '7.0', + versionBase: '7', + versionNumber: 7 + }, + wikiEditor: { + ltr: true, + rtl: false + } }, - wikiEditor: { - ltr: true, - rtl: true - } - }, - // Firefox 2 - // Firefox 3.5 - 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1.19) Gecko/20110420 Firefox/3.5.19': { - title: 'Firefox 3.5', - platform: 'MacIntel', - profile: { - "name": "firefox", - "layout": "gecko", - "layoutVersion": 20110420, - "platform": "mac", - "version": "3.5.19", - "versionBase": "3", - "versionNumber": 3.5 + // Internet Explorer 8 + // Internet Explorer 9 + // Internet Explorer 10 + 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)': { + title: 'Internet Explorer 10', + platform: 'Win32', + profile: { + name: 'msie', + layout: 'trident', + layoutVersion: 'unknown', // should be able to report 6? + platform: 'win', + version: '10.0', + versionBase: '10', + versionNumber: 10 + }, + wikiEditor: { + ltr: true, + rtl: true + } }, - wikiEditor: { - ltr: true, - rtl: true - } - }, - // Firefox 3.6 - 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.17) Gecko/20110422 Ubuntu/10.10 (maverick) Firefox/3.6.17': { - title: 'Firefox 3.6', - platform: 'Linux i686', - profile: { - "name": "firefox", - "layout": "gecko", - "layoutVersion": 20110422, - "platform": "linux", - "version": "3.6.17", - "versionBase": "3", - "versionNumber": 3.6 + // Firefox 2 + // Firefox 3.5 + 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1.19) Gecko/20110420 Firefox/3.5.19': { + title: 'Firefox 3.5', + platform: 'MacIntel', + profile: { + name: 'firefox', + layout: 'gecko', + layoutVersion: 20110420, + platform: 'mac', + version: '3.5.19', + versionBase: '3', + versionNumber: 3.5 + }, + wikiEditor: { + ltr: true, + rtl: true + } }, - wikiEditor: { - ltr: true, - rtl: true - } - }, - // Firefox 4 - 'Mozilla/5.0 (Windows NT 6.0; rv:2.0.1) Gecko/20100101 Firefox/4.0.1': { - title: 'Firefox 4', - platform: 'Win32', - profile: { - "name": "firefox", - "layout": "gecko", - "layoutVersion": 20100101, - "platform": "win", - "version": "4.0.1", - "versionBase": "4", - "versionNumber": 4 + // Firefox 3.6 + 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.17) Gecko/20110422 Ubuntu/10.10 (maverick) Firefox/3.6.17': { + title: 'Firefox 3.6', + platform: 'Linux i686', + profile: { + name: 'firefox', + layout: 'gecko', + layoutVersion: 20110422, + platform: 'linux', + version: '3.6.17', + versionBase: '3', + versionNumber: 3.6 + }, + wikiEditor: { + ltr: true, + rtl: true + } }, - wikiEditor: { - ltr: true, - rtl: true - } - }, - // Firefox 10 nightly build - 'Mozilla/5.0 (X11; Linux x86_64; rv:10.0a1) Gecko/20111103 Firefox/10.0a1': { - title: 'Firefox 10 nightly', - platform: 'Linux', - profile: { - "name": "firefox", - "layout": "gecko", - "layoutVersion": 20111103, - "platform": "linux", - "version": "10.0a1", - "versionBase": "10", - "versionNumber": 10 + // Firefox 4 + 'Mozilla/5.0 (Windows NT 6.0; rv:2.0.1) Gecko/20100101 Firefox/4.0.1': { + title: 'Firefox 4', + platform: 'Win32', + profile: { + name: 'firefox', + layout: 'gecko', + layoutVersion: 20100101, + platform: 'win', + version: '4.0.1', + versionBase: '4', + versionNumber: 4 + }, + wikiEditor: { + ltr: true, + rtl: true + } }, - wikiEditor: { - ltr: true, - rtl: true - } - }, - // Firefox 5 - // Safari 3 - // Safari 4 - 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; nl-nl) AppleWebKit/531.22.7 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7': { - title: 'Safari 4', - platform: 'MacIntel', - profile: { - "name": "safari", - "layout": "webkit", - "layoutVersion": 531, - "platform": "mac", - "version": "4.0.5", - "versionBase": "4", - "versionNumber": 4 + // Firefox 10 nightly build + 'Mozilla/5.0 (X11; Linux x86_64; rv:10.0a1) Gecko/20111103 Firefox/10.0a1': { + title: 'Firefox 10 nightly', + platform: 'Linux', + profile: { + name: 'firefox', + layout: 'gecko', + layoutVersion: 20111103, + platform: 'linux', + version: '10.0a1', + versionBase: '10', + versionNumber: 10 + }, + wikiEditor: { + ltr: true, + rtl: true + } }, - wikiEditor: { - ltr: true, - rtl: true - } - }, - 'Mozilla/5.0 (Windows; U; Windows NT 6.0; cs-CZ) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7': { - title: 'Safari 4', - platform: 'Win32', - profile: { - "name": "safari", - "layout": "webkit", - "layoutVersion": 533, - "platform": "win", - "version": "4.0.5", - "versionBase": "4", - "versionNumber": 4 + // Firefox 5 + // Safari 3 + // Safari 4 + 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; nl-nl) AppleWebKit/531.22.7 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7': { + title: 'Safari 4', + platform: 'MacIntel', + profile: { + name: 'safari', + layout: 'webkit', + layoutVersion: 531, + platform: 'mac', + version: '4.0.5', + versionBase: '4', + versionNumber: 4 + }, + wikiEditor: { + ltr: true, + rtl: true + } }, - wikiEditor: { - ltr: true, - rtl: true - } - }, - // Safari 5 - // Opera 10 - // Chrome 5 - // Chrome 6 - // Chrome 7 - // Chrome 8 - // Chrome 9 - // Chrome 10 - // Chrome 11 - // Chrome 12 - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_5_8) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.112 Safari/534.30': { - title: 'Chrome 12', - platform: 'MacIntel', - profile: { - "name": "chrome", - "layout": "webkit", - "layoutVersion": 534, - "platform": "mac", - "version": "12.0.742.112", - "versionBase": "12", - "versionNumber": 12 + 'Mozilla/5.0 (Windows; U; Windows NT 6.0; cs-CZ) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7': { + title: 'Safari 4', + platform: 'Win32', + profile: { + name: 'safari', + layout: 'webkit', + layoutVersion: 533, + platform: 'win', + version: '4.0.5', + versionBase: '4', + versionNumber: 4 + }, + wikiEditor: { + ltr: true, + rtl: true + } }, - wikiEditor: { - ltr: true, - rtl: true - } - }, - 'Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.68 Safari/534.30': { - title: 'Chrome 12', - platform: 'Linux i686', - profile: { - "name": "chrome", - "layout": "webkit", - "layoutVersion": 534, - "platform": "linux", - "version": "12.0.742.68", - "versionBase": "12", - "versionNumber": 12 + // Safari 5 + // Opera 10 + // Chrome 5 + // Chrome 6 + // Chrome 7 + // Chrome 8 + // Chrome 9 + // Chrome 10 + // Chrome 11 + // Chrome 12 + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_5_8) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.112 Safari/534.30': { + title: 'Chrome 12', + platform: 'MacIntel', + profile: { + name: 'chrome', + layout: 'webkit', + layoutVersion: 534, + platform: 'mac', + version: '12.0.742.112', + versionBase: '12', + versionNumber: 12 + }, + wikiEditor: { + ltr: true, + rtl: true + } }, - wikiEditor: { - ltr: true, - rtl: true - } - }, - // Bug #34924 - 'Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.34 (KHTML, like Gecko) rekonq Safari/534.34': { - title: 'Rekonq', - platform: 'Linux i686', - profile: { - "name": "rekonq", - "layout": "webkit", - "layoutVersion": 534, - "platform": "linux", - "version": "534.34", - "versionBase": "534", - "versionNumber": 534.34 + 'Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.68 Safari/534.30': { + title: 'Chrome 12', + platform: 'Linux i686', + profile: { + name: 'chrome', + layout: 'webkit', + layoutVersion: 534, + platform: 'linux', + version: '12.0.742.68', + versionBase: '12', + versionNumber: 12 + }, + wikiEditor: { + ltr: true, + rtl: true + } }, - wikiEditor: { - ltr: true, - rtl: true + // Bug #34924 + 'Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.34 (KHTML, like Gecko) rekonq Safari/534.34': { + title: 'Rekonq', + platform: 'Linux i686', + profile: { + name: 'rekonq', + layout: 'webkit', + layoutVersion: 534, + platform: 'linux', + version: '534.34', + versionBase: '534', + versionNumber: 534.34 + }, + wikiEditor: { + ltr: true, + rtl: true + } } - } - }; - $.each( uas, function () { - uacount++; - }); - return uas; -}()); + }; + $.each( uas, function () { + uacount++; + }); + return uas; + }() ); -QUnit.test( 'profile userAgent support', uacount, function ( assert ) { - // Generate a client profile object and compare recursively - var uaTest = function( rawUserAgent, data ) { - var ret = $.client.profile( { - userAgent: rawUserAgent, - platform: data.platform - } ); - assert.deepEqual( ret, data.profile, 'Client profile support check for ' + data.title + ' (' + data.platform + '): ' + rawUserAgent ); - }; + QUnit.test( 'profile userAgent support', uacount, function ( assert ) { + // Generate a client profile object and compare recursively + var uaTest = function( rawUserAgent, data ) { + var ret = $.client.profile( { + userAgent: rawUserAgent, + platform: data.platform + } ); + assert.deepEqual( ret, data.profile, 'Client profile support check for ' + data.title + ' (' + data.platform + '): ' + rawUserAgent ); + }; - // Loop through and run tests - $.each( uas, uaTest ); -} ); + // Loop through and run tests + $.each( uas, uaTest ); + } ); -QUnit.test( 'profile return validation for current user agent', 7, function ( assert ) { - var p = $.client.profile(); - function unknownOrType( val, type, summary ) { - assert.ok( typeof val === type || val === 'unknown', summary ); - } + QUnit.test( 'profile return validation for current user agent', 7, function ( assert ) { + var p = $.client.profile(); + function unknownOrType( val, type, summary ) { + assert.ok( typeof val === type || val === 'unknown', summary ); + } - assert.equal( typeof p, 'object', 'profile returns an object' ); - unknownOrType( p.layout, 'string', 'p.layout is a string (or "unknown")' ); - unknownOrType( p.layoutVersion, 'number', 'p.layoutVersion is a number (or "unknown")' ); - unknownOrType( p.platform, 'string', 'p.platform is a string (or "unknown")' ); - unknownOrType( p.version, 'string', 'p.version is a string (or "unknown")' ); - unknownOrType( p.versionBase, 'string', 'p.versionBase is a string (or "unknown")' ); - assert.equal( typeof p.versionNumber, 'number', 'p.versionNumber is a number' ); -}); + assert.equal( typeof p, 'object', 'profile returns an object' ); + unknownOrType( p.layout, 'string', 'p.layout is a string (or "unknown")' ); + unknownOrType( p.layoutVersion, 'number', 'p.layoutVersion is a number (or "unknown")' ); + unknownOrType( p.platform, 'string', 'p.platform is a string (or "unknown")' ); + unknownOrType( p.version, 'string', 'p.version is a string (or "unknown")' ); + unknownOrType( p.versionBase, 'string', 'p.versionBase is a string (or "unknown")' ); + assert.equal( typeof p.versionNumber, 'number', 'p.versionNumber is a number' ); + }); -// Example from WikiEditor -// Make sure to use raw numbers, a string like "7.0" would fail on a -// version 10 browser since in string comparaison "10" is before "7.0" :) -var testMap = { - 'ltr': { - 'msie': [['>=', 7.0]], - 'firefox': [['>=', 2]], - 'opera': [['>=', 9.6]], - 'safari': [['>=', 3]], - 'chrome': [['>=', 3]], - 'netscape': [['>=', 9]], - 'blackberry': false, - 'ipod': false, - 'iphone': false - }, - 'rtl': { - 'msie': [['>=', 8]], - 'firefox': [['>=', 2]], - 'opera': [['>=', 9.6]], - 'safari': [['>=', 3]], - 'chrome': [['>=', 3]], - 'netscape': [['>=', 9]], - 'blackberry': false, - 'ipod': false, - 'iphone': false - } -}; + // Example from WikiEditor + // Make sure to use raw numbers, a string like "7.0" would fail on a + // version 10 browser since in string comparaison "10" is before "7.0" :) + testMap = { + 'ltr': { + 'msie': [['>=', 7.0]], + 'firefox': [['>=', 2]], + 'opera': [['>=', 9.6]], + 'safari': [['>=', 3]], + 'chrome': [['>=', 3]], + 'netscape': [['>=', 9]], + 'blackberry': false, + 'ipod': false, + 'iphone': false + }, + 'rtl': { + 'msie': [['>=', 8]], + 'firefox': [['>=', 2]], + 'opera': [['>=', 9.6]], + 'safari': [['>=', 3]], + 'chrome': [['>=', 3]], + 'netscape': [['>=', 9]], + 'blackberry': false, + 'ipod': false, + 'iphone': false + } + }; -QUnit.test( 'test', 1, function ( assert ) { - // .test() uses eval, make sure no exceptions are thrown - // then do a basic return value type check - var testMatch = $.client.test( testMap ); + QUnit.test( 'test', 1, function ( assert ) { + // .test() uses eval, make sure no exceptions are thrown + // then do a basic return value type check + var testMatch = $.client.test( testMap ); - assert.equal( typeof testMatch, 'boolean', 'test returns a boolean value' ); + assert.equal( typeof testMatch, 'boolean', 'test returns a boolean value' ); -}); + }); -QUnit.test( 'User-agent matches against WikiEditor\'s compatibility map', uacount * 2, function ( assert ) { - var $body = $( 'body' ), - bodyClasses = $body.attr( 'class' ); + QUnit.test( 'User-agent matches against WikiEditor\'s compatibility map', uacount * 2, function ( assert ) { + var $body = $( 'body' ), + bodyClasses = $body.attr( 'class' ); - // Loop through and run tests - $.each( uas, function ( agent, data ) { - $.each( ['ltr', 'rtl'], function ( i, dir ) { - $body.removeClass( 'ltr rtl' ).addClass( dir ); - var profile = $.client.profile( { - userAgent: agent, - platform: data.platform - } ); - var testMatch = $.client.test( testMap, profile ); - $body.removeClass( dir ); + // Loop through and run tests + $.each( uas, function ( agent, data ) { + $.each( ['ltr', 'rtl'], function ( i, dir ) { + var profile, testMatch; + $body.removeClass( 'ltr rtl' ).addClass( dir ); + profile = $.client.profile( { + userAgent: agent, + platform: data.platform + } ); + testMatch = $.client.test( testMap, profile ); + $body.removeClass( dir ); - assert.equal( testMatch, data.wikiEditor[dir], 'testing comparison based on ' + dir + ', ' + agent ); + assert.equal( testMatch, data.wikiEditor[dir], 'testing comparison based on ' + dir + ', ' + agent ); + }); }); - }); - - // Restore body classes - $body.attr( 'class', bodyClasses ); -}); + // Restore body classes + $body.attr( 'class', bodyClasses ); + }); +}( jQuery ) ); diff --git a/tests/qunit/suites/resources/jquery/jquery.colorUtil.test.js b/tests/qunit/suites/resources/jquery/jquery.colorUtil.test.js index 7b37f5a059..51a892ed84 100644 --- a/tests/qunit/suites/resources/jquery/jquery.colorUtil.test.js +++ b/tests/qunit/suites/resources/jquery/jquery.colorUtil.test.js @@ -1,58 +1,62 @@ -QUnit.module( 'jquery.colorUtil', QUnit.newMwEnvironment() ); - -QUnit.test( 'getRGB', 18, function ( assert ) { - assert.strictEqual( $.colorUtil.getRGB(), undefined, 'No arguments' ); - assert.strictEqual( $.colorUtil.getRGB( '' ), undefined, 'Empty string' ); - assert.deepEqual( $.colorUtil.getRGB( [0, 100, 255] ), [0, 100, 255], 'Parse array of rgb values' ); - assert.deepEqual( $.colorUtil.getRGB( 'rgb(0,100,255)' ), [0, 100, 255], 'Parse simple rgb string' ); - assert.deepEqual( $.colorUtil.getRGB( 'rgb(0, 100, 255)' ), [0, 100, 255], 'Parse simple rgb string with spaces' ); - assert.deepEqual( $.colorUtil.getRGB( 'rgb(0%,20%,40%)' ), [0, 51, 102], 'Parse rgb string with percentages' ); - assert.deepEqual( $.colorUtil.getRGB( 'rgb(0%, 20%, 40%)' ), [0, 51, 102], 'Parse rgb string with percentages and spaces' ); - assert.deepEqual( $.colorUtil.getRGB( '#f2ddee' ), [242, 221, 238], 'Hex string: 6 char lowercase' ); - assert.deepEqual( $.colorUtil.getRGB( '#f2DDEE' ), [242, 221, 238], 'Hex string: 6 char uppercase' ); - assert.deepEqual( $.colorUtil.getRGB( '#f2DdEe' ), [242, 221, 238], 'Hex string: 6 char mixed' ); - assert.deepEqual( $.colorUtil.getRGB( '#eee' ), [238, 238, 238], 'Hex string: 3 char lowercase' ); - assert.deepEqual( $.colorUtil.getRGB( '#EEE' ), [238, 238, 238], 'Hex string: 3 char uppercase' ); - assert.deepEqual( $.colorUtil.getRGB( '#eEe' ), [238, 238, 238], 'Hex string: 3 char mixed' ); - assert.deepEqual( $.colorUtil.getRGB( 'rgba(0, 0, 0, 0)' ), [255, 255, 255], 'Zero rgba for Safari 3; Transparent (whitespace)' ); - - // Perhaps this is a bug in colorUtil, but it is the current behaviour so, let's keep - // track of it, so we will know in case it would ever change. - assert.strictEqual( $.colorUtil.getRGB( 'rgba(0,0,0,0)' ), undefined, 'Zero rgba without whitespace' ); - - assert.deepEqual( $.colorUtil.getRGB( 'lightGreen' ), [144, 238, 144], 'Color names (lightGreen)' ); - assert.deepEqual( $.colorUtil.getRGB( 'transparent' ), [255, 255, 255], 'Color names (transparent)' ); - assert.strictEqual( $.colorUtil.getRGB( 'mediaWiki' ), undefined, 'Inexisting color name' ); -}); - -QUnit.test( 'rgbToHsl', 1, function ( assert ) { - var hsl = $.colorUtil.rgbToHsl( 144, 238, 144 ); - - // Cross-browser differences in decimals... - // Round to two decimals so they can be more reliably checked. - var dualDecimals = function(a,b){ - return Math.round(a*100)/100; - }; - // Re-create the rgbToHsl return array items, limited to two decimals. - var ret = [dualDecimals(hsl[0]), dualDecimals(hsl[1]), dualDecimals(hsl[2])]; - - assert.deepEqual( ret, [0.33, 0.73, 0.75], 'rgb(144, 238, 144): hsl(0.33, 0.73, 0.75)' ); -}); - -QUnit.test( 'hslToRgb', 1, function ( assert ) { - var rgb = $.colorUtil.hslToRgb( 0.3, 0.7, 0.8 ); - - // Cross-browser differences in decimals... - // Re-create the hslToRgb return array items, rounded to whole numbers. - var ret = [Math.round(rgb[0]), Math.round(rgb[1]), Math.round(rgb[2])]; - - assert.deepEqual( ret ,[183, 240, 168], 'hsl(0.3, 0.7, 0.8): rgb(183, 240, 168)' ); -}); - -QUnit.test( 'getColorBrightness', 2, function ( assert ) { - var a = $.colorUtil.getColorBrightness( 'red', +0.1 ); - assert.equal( a, 'rgb(255,50,50)', 'Start with named color "red", brighten 10%' ); - - var b = $.colorUtil.getColorBrightness( 'rgb(200,50,50)', -0.2 ); - assert.equal( b, 'rgb(118,29,29)', 'Start with rgb string "rgb(200,50,50)", darken 20%' ); -}); +( function ( $ ) { + QUnit.module( 'jquery.colorUtil', QUnit.newMwEnvironment() ); + + QUnit.test( 'getRGB', 18, function ( assert ) { + assert.strictEqual( $.colorUtil.getRGB(), undefined, 'No arguments' ); + assert.strictEqual( $.colorUtil.getRGB( '' ), undefined, 'Empty string' ); + assert.deepEqual( $.colorUtil.getRGB( [0, 100, 255] ), [0, 100, 255], 'Parse array of rgb values' ); + assert.deepEqual( $.colorUtil.getRGB( 'rgb(0,100,255)' ), [0, 100, 255], 'Parse simple rgb string' ); + assert.deepEqual( $.colorUtil.getRGB( 'rgb(0, 100, 255)' ), [0, 100, 255], 'Parse simple rgb string with spaces' ); + assert.deepEqual( $.colorUtil.getRGB( 'rgb(0%,20%,40%)' ), [0, 51, 102], 'Parse rgb string with percentages' ); + assert.deepEqual( $.colorUtil.getRGB( 'rgb(0%, 20%, 40%)' ), [0, 51, 102], 'Parse rgb string with percentages and spaces' ); + assert.deepEqual( $.colorUtil.getRGB( '#f2ddee' ), [242, 221, 238], 'Hex string: 6 char lowercase' ); + assert.deepEqual( $.colorUtil.getRGB( '#f2DDEE' ), [242, 221, 238], 'Hex string: 6 char uppercase' ); + assert.deepEqual( $.colorUtil.getRGB( '#f2DdEe' ), [242, 221, 238], 'Hex string: 6 char mixed' ); + assert.deepEqual( $.colorUtil.getRGB( '#eee' ), [238, 238, 238], 'Hex string: 3 char lowercase' ); + assert.deepEqual( $.colorUtil.getRGB( '#EEE' ), [238, 238, 238], 'Hex string: 3 char uppercase' ); + assert.deepEqual( $.colorUtil.getRGB( '#eEe' ), [238, 238, 238], 'Hex string: 3 char mixed' ); + assert.deepEqual( $.colorUtil.getRGB( 'rgba(0, 0, 0, 0)' ), [255, 255, 255], 'Zero rgba for Safari 3; Transparent (whitespace)' ); + + // Perhaps this is a bug in colorUtil, but it is the current behaviour so, let's keep + // track of it, so we will know in case it would ever change. + assert.strictEqual( $.colorUtil.getRGB( 'rgba(0,0,0,0)' ), undefined, 'Zero rgba without whitespace' ); + + assert.deepEqual( $.colorUtil.getRGB( 'lightGreen' ), [144, 238, 144], 'Color names (lightGreen)' ); + assert.deepEqual( $.colorUtil.getRGB( 'transparent' ), [255, 255, 255], 'Color names (transparent)' ); + assert.strictEqual( $.colorUtil.getRGB( 'mediaWiki' ), undefined, 'Inexisting color name' ); + }); + + QUnit.test( 'rgbToHsl', 1, function ( assert ) { + var hsl, ret; + + // Cross-browser differences in decimals... + // Round to two decimals so they can be more reliably checked. + function dualDecimals( a ) { + return Math.round( a * 100 ) / 100; + } + // Re-create the rgbToHsl return array items, limited to two decimals. + hsl = $.colorUtil.rgbToHsl( 144, 238, 144 ); + ret = [ dualDecimals( hsl[0] ), dualDecimals( hsl[1] ), dualDecimals( hsl[2] ) ]; + + assert.deepEqual( ret, [0.33, 0.73, 0.75], 'rgb(144, 238, 144): hsl(0.33, 0.73, 0.75)' ); + }); + + QUnit.test( 'hslToRgb', 1, function ( assert ) { + var rgb, ret; + rgb = $.colorUtil.hslToRgb( 0.3, 0.7, 0.8 ); + + // Re-create the hslToRgb return array items, rounded to whole numbers. + ret = [ Math.round(rgb[0]), Math.round(rgb[1]), Math.round(rgb[2]) ]; + + assert.deepEqual( ret ,[183, 240, 168], 'hsl(0.3, 0.7, 0.8): rgb(183, 240, 168)' ); + }); + + QUnit.test( 'getColorBrightness', 2, function ( assert ) { + var a, b; + a = $.colorUtil.getColorBrightness( 'red', +0.1 ); + assert.equal( a, 'rgb(255,50,50)', 'Start with named color "red", brighten 10%' ); + + b = $.colorUtil.getColorBrightness( 'rgb(200,50,50)', -0.2 ); + assert.equal( b, 'rgb(118,29,29)', 'Start with rgb string "rgb(200,50,50)", darken 20%' ); + }); +}( jQuery ) ); diff --git a/tests/qunit/suites/resources/jquery/jquery.delayedBind.test.js b/tests/qunit/suites/resources/jquery/jquery.delayedBind.test.js index a307983557..3e7d5ffce9 100644 --- a/tests/qunit/suites/resources/jquery/jquery.delayedBind.test.js +++ b/tests/qunit/suites/resources/jquery/jquery.delayedBind.test.js @@ -1,35 +1,37 @@ -QUnit.asyncTest('jquery.delayedBind with data option', 2, function ( assert ) { - var $fixture = $('
      ').appendTo('#qunit-fixture'), - data = { magic: "beeswax" }, - delay = 50; +( function ( $ ) { + QUnit.asyncTest('jquery.delayedBind with data option', 2, function ( assert ) { + var $fixture = $('
      ').appendTo('#qunit-fixture'), + data = { + magic: 'beeswax' + }, + delay = 50; - $fixture.delayedBind(delay, 'testevent', data, function ( e ) { - QUnit.start(); // continue! - assert.ok( true, 'testevent fired'); - assert.ok( e.data === data, 'data is passed through delayedBind'); + $fixture.delayedBind(delay, 'testevent', data, function ( e ) { + assert.ok( true, 'testevent fired'); + assert.ok( e.data === data, 'data is passed through delayedBind'); + QUnit.start(); + }); + + // We'll trigger it thrice, but it should only happen once. + $fixture.trigger( 'testevent', {} ); + $fixture.trigger( 'testevent', {} ); + $fixture.trigger( 'testevent', {} ); + $fixture.trigger( 'testevent', {} ); }); - // We'll trigger it thrice, but it should only happen once. - $fixture.trigger( 'testevent', {} ); - $fixture.trigger( 'testevent', {} ); - $fixture.trigger( 'testevent', {} ); - $fixture.trigger( 'testevent', {} ); -}); + QUnit.asyncTest('jquery.delayedBind without data option', 1, function ( assert ) { + var $fixture = $('
      ').appendTo('#qunit-fixture'), + delay = 50; -QUnit.asyncTest('jquery.delayedBind without data option', 1, function ( assert ) { - var $fixture = $('
      ').appendTo('#qunit-fixture'), - data = { magic: "beeswax" }, - delay = 50; + $fixture.delayedBind(delay, 'testevent', function () { + assert.ok(true, 'testevent fired'); + QUnit.start(); + }); - $fixture.delayedBind(delay, 'testevent', function ( e ) { - QUnit.start(); // continue! - assert.ok(true, 'testevent fired'); + // We'll trigger it thrice, but it should only happen once. + $fixture.trigger( 'testevent', {} ); + $fixture.trigger( 'testevent', {} ); + $fixture.trigger( 'testevent', {} ); + $fixture.trigger( 'testevent', {} ); }); - - // We'll trigger it thrice, but it should only happen once. - $fixture.trigger( 'testevent', {} ); - $fixture.trigger( 'testevent', {} ); - $fixture.trigger( 'testevent', {} ); - $fixture.trigger( 'testevent', {} ); -}); - +}( jQuery ) ); diff --git a/tests/qunit/suites/resources/jquery/jquery.getAttrs.test.js b/tests/qunit/suites/resources/jquery/jquery.getAttrs.test.js index 6eef1abba1..82566c2cb3 100644 --- a/tests/qunit/suites/resources/jquery/jquery.getAttrs.test.js +++ b/tests/qunit/suites/resources/jquery/jquery.getAttrs.test.js @@ -1,11 +1,13 @@ -QUnit.module( 'jquery.getAttrs', QUnit.newMwEnvironment() ); +( function ( $ ) { + QUnit.module( 'jquery.getAttrs', QUnit.newMwEnvironment() ); -QUnit.test( 'Check', 1, function ( assert ) { - var attrs = { - foo: 'bar', - 'class': 'lorem' - }, - $el = jQuery( '
      ', attrs ); + QUnit.test( 'Check', 1, function ( assert ) { + var attrs = { + foo: 'bar', + 'class': 'lorem' + }, + $el = $( '
      ' ).attr( attrs ); - assert.deepEqual( $el.getAttrs(), attrs, 'getAttrs() return object should match the attributes set, no more, no less' ); -} ); + assert.deepEqual( $el.getAttrs(), attrs, 'getAttrs() return object should match the attributes set, no more, no less' ); + } ); +}( jQuery ) ); diff --git a/tests/qunit/suites/resources/jquery/jquery.hidpi.test.js b/tests/qunit/suites/resources/jquery/jquery.hidpi.test.js index cf309df800..d75c3789a6 100644 --- a/tests/qunit/suites/resources/jquery/jquery.hidpi.test.js +++ b/tests/qunit/suites/resources/jquery/jquery.hidpi.test.js @@ -1,20 +1,22 @@ -QUnit.module( 'jquery.hidpi', QUnit.newMwEnvironment() ); +( function ( $ ) { + QUnit.module( 'jquery.hidpi', QUnit.newMwEnvironment() ); -QUnit.test( 'devicePixelRatio', function ( assert ) { - var devicePixelRatio = $.devicePixelRatio(); - assert.equal( typeof devicePixelRatio, 'number', '$.devicePixelRatio() returns a number' ); -}); + QUnit.test( 'devicePixelRatio', function ( assert ) { + var devicePixelRatio = $.devicePixelRatio(); + assert.equal( typeof devicePixelRatio, 'number', '$.devicePixelRatio() returns a number' ); + }); -QUnit.test( 'matchSrcSet', function ( assert ) { - var srcset = 'onefive.png 1.5x, two.png 2x'; + QUnit.test( 'matchSrcSet', function ( assert ) { + var srcset = 'onefive.png 1.5x, two.png 2x'; - // Nice exact matches - assert.equal( $.matchSrcSet( 1, srcset ), null, '1.0 gives no match' ); - assert.equal( $.matchSrcSet( 1.5, srcset ), 'onefive.png', '1.5 gives match' ); - assert.equal( $.matchSrcSet( 2, srcset ), 'two.png', '2 gives match' ); + // Nice exact matches + assert.equal( $.matchSrcSet( 1, srcset ), null, '1.0 gives no match' ); + assert.equal( $.matchSrcSet( 1.5, srcset ), 'onefive.png', '1.5 gives match' ); + assert.equal( $.matchSrcSet( 2, srcset ), 'two.png', '2 gives match' ); - // Non-exact matches; should return the next-biggest specified - assert.equal( $.matchSrcSet( 1.25, srcset ), null, '1.25 gives no match' ); - assert.equal( $.matchSrcSet( 1.75, srcset ), 'onefive.png', '1.75 gives match to 1.5' ); - assert.equal( $.matchSrcSet( 2.25, srcset ), 'two.png', '2.25 gives match to 2' ); -}); + // Non-exact matches; should return the next-biggest specified + assert.equal( $.matchSrcSet( 1.25, srcset ), null, '1.25 gives no match' ); + assert.equal( $.matchSrcSet( 1.75, srcset ), 'onefive.png', '1.75 gives match to 1.5' ); + assert.equal( $.matchSrcSet( 2.25, srcset ), 'two.png', '2.25 gives match to 2' ); + }); +}( jQuery ) ); diff --git a/tests/qunit/suites/resources/jquery/jquery.highlightText.test.js b/tests/qunit/suites/resources/jquery/jquery.highlightText.test.js index a94dca313a..e1fb96dceb 100644 --- a/tests/qunit/suites/resources/jquery/jquery.highlightText.test.js +++ b/tests/qunit/suites/resources/jquery/jquery.highlightText.test.js @@ -1,232 +1,235 @@ -QUnit.module( 'jquery.highlightText', QUnit.newMwEnvironment() ); +( function ( $ ) { + QUnit.module( 'jquery.highlightText', QUnit.newMwEnvironment() ); -QUnit.test( 'Check', function ( assert ) { - var $fixture, cases = [ - { - desc: 'Test 001', - text: 'Blue Öyster Cult', - highlight: 'Blue', - expected: 'Blue Öyster Cult' - }, - { - desc: 'Test 002', - text: 'Blue Öyster Cult', - highlight: 'Blue ', - expected: 'Blue Öyster Cult' - }, - { - desc: 'Test 003', - text: 'Blue Öyster Cult', - highlight: 'Blue Ö', - expected: 'Blue Öyster Cult' - }, - { - desc: 'Test 004', - text: 'Blue Öyster Cult', - highlight: 'Blue Öy', - expected: 'Blue Öyster Cult' - }, - { - desc: 'Test 005', - text: 'Blue Öyster Cult', - highlight: ' Blue', - expected: 'Blue Öyster Cult' - }, - { - desc: 'Test 006', - text: 'Blue Öyster Cult', - highlight: ' Blue ', - expected: 'Blue Öyster Cult' - }, - { - desc: 'Test 007', - text: 'Blue Öyster Cult', - highlight: ' Blue Ö', - expected: 'Blue Öyster Cult' - }, - { - desc: 'Test 008', - text: 'Blue Öyster Cult', - highlight: ' Blue Öy', - expected: 'Blue Öyster Cult' - }, - { - desc: 'Test 009: Highlighter broken on starting Umlaut?', - text: 'Österreich', - highlight: 'Österreich', - expected: 'Österreich' - }, - { - desc: 'Test 010: Highlighter broken on starting Umlaut?', - text: 'Österreich', - highlight: 'Ö', - expected: 'Österreich' - }, - { - desc: 'Test 011: Highlighter broken on starting Umlaut?', - text: 'Österreich', - highlight: 'Öst', - expected: 'Österreich' - }, - { - desc: 'Test 012: Highlighter broken on starting Umlaut?', - text: 'Österreich', - highlight: 'Oe', - expected: 'Österreich' - }, - { - desc: 'Test 013: Highlighter broken on punctuation mark?', - text: 'So good. To be there', - highlight: 'good', - expected: 'So good. To be there' - }, - { - desc: 'Test 014: Highlighter broken on space?', - text: 'So good. To be there', - highlight: 'be', - expected: 'So good. To be there' - }, - { - desc: 'Test 015: Highlighter broken on space?', - text: 'So good. To be there', - highlight: ' be', - expected: 'So good. To be there' - }, - { - desc: 'Test 016: Highlighter broken on space?', - text: 'So good. To be there', - highlight: 'be ', - expected: 'So good. To be there' - }, - { - desc: 'Test 017: Highlighter broken on space?', - text: 'So good. To be there', - highlight: ' be ', - expected: 'So good. To be there' - }, - { - desc: 'Test 018: en de Highlighter broken on special character at the end?', - text: 'So good. xbß', - highlight: 'xbß', - expected: 'So good. xbß' - }, - { - desc: 'Test 019: en de Highlighter broken on special character at the end?', - text: 'So good. xbß.', - highlight: 'xbß.', - expected: 'So good. xbß.' - }, - { - desc: 'Test 020: RTL he Hebrew', - text: 'חסיד אומות העולם', - highlight: 'חסיד אומות העולם', - expected: 'חסיד אומות העולם' - }, - { - desc: 'Test 021: RTL he Hebrew', - text: 'חסיד אומות העולם', - highlight: 'חסי', - expected: 'חסיד אומות העולם' - }, - { - desc: 'Test 022: ja Japanese', - text: '諸国民の中の正義の人', - highlight: '諸国民の中の正義の人', - expected: '諸国民の中の正義の人' - }, - { - desc: 'Test 023: ja Japanese', - text: '諸国民の中の正義の人', - highlight: '諸国', - expected: '諸国民の中の正義の人' - }, - { - desc: 'Test 024: fr French text and « french quotes » (guillemets)', - text: "« L'oiseau est sur l’île »", - highlight: "« L'oiseau est sur l’île »", - expected: '« L\'oiseau est sur l’île »' - }, - { - desc: 'Test 025: fr French text and « french quotes » (guillemets)', - text: "« L'oiseau est sur l’île »", - highlight: "« L'oise", - expected: '« L\'oiseau est sur l’île »' - }, - { - desc: 'Test 025a: fr French text and « french quotes » (guillemets) - does it match the single strings "«" and "L" separately?', - text: "« L'oiseau est sur l’île »", - highlight: "« L", - expected: '« L\'oiseau est sur l’île »' - }, - { - desc: 'Test 026: ru Russian', - text: 'Праведники мира', - highlight: 'Праведники мира', - expected: 'Праведники мира' - }, - { - desc: 'Test 027: ru Russian', - text: 'Праведники мира', - highlight: 'Праве', - expected: 'Праведники мира' - }, - { - desc: 'Test 028 ka Georgian', - text: 'მთავარი გვერდი', - highlight: 'მთავარი გვერდი', - expected: 'მთავარი გვერდი' - }, - { - desc: 'Test 029 ka Georgian', - text: 'მთავარი გვერდი', - highlight: 'მთა', - expected: 'მთავარი გვერდი' - }, - { - desc: 'Test 030 hy Armenian', - text: 'Նոնա Գափրինդաշվիլի', - highlight: 'Նոնա Գափրինդաշվիլի', - expected: 'Նոնա Գափրինդաշվիլի' - }, - { - desc: 'Test 031 hy Armenian', - text: 'Նոնա Գափրինդաշվիլի', - highlight: 'Նոն', - expected: 'Նոնա Գափրինդաշվիլի' - }, - { - desc: 'Test 032: th Thai', - text: 'พอล แอร์ดิช', - highlight: 'พอล แอร์ดิช', - expected: 'พอล แอร์ดิช' - }, - { - desc: 'Test 033: th Thai', - text: 'พอล แอร์ดิช', - highlight: 'พอ', - expected: 'พอล แอร์ดิช' - }, - { - desc: 'Test 034: RTL ar Arabic', - text: 'بول إيردوس', - highlight: 'بول إيردوس', - expected: 'بول إيردوس' - }, - { - desc: 'Test 035: RTL ar Arabic', - text: 'بول إيردوس', - highlight: 'بو', - expected: 'بول إيردوس' - } - ]; - QUnit.expect( cases.length ); + QUnit.test( 'Check', function ( assert ) { + var $fixture, cases = [ + { + desc: 'Test 001', + text: 'Blue Öyster Cult', + highlight: 'Blue', + expected: 'Blue Öyster Cult' + }, + { + desc: 'Test 002', + text: 'Blue Öyster Cult', + highlight: 'Blue ', + expected: 'Blue Öyster Cult' + }, + { + desc: 'Test 003', + text: 'Blue Öyster Cult', + highlight: 'Blue Ö', + expected: 'Blue Öyster Cult' + }, + { + desc: 'Test 004', + text: 'Blue Öyster Cult', + highlight: 'Blue Öy', + expected: 'Blue Öyster Cult' + }, + { + desc: 'Test 005', + text: 'Blue Öyster Cult', + highlight: ' Blue', + expected: 'Blue Öyster Cult' + }, + { + desc: 'Test 006', + text: 'Blue Öyster Cult', + highlight: ' Blue ', + expected: 'Blue Öyster Cult' + }, + { + desc: 'Test 007', + text: 'Blue Öyster Cult', + highlight: ' Blue Ö', + expected: 'Blue Öyster Cult' + }, + { + desc: 'Test 008', + text: 'Blue Öyster Cult', + highlight: ' Blue Öy', + expected: 'Blue Öyster Cult' + }, + { + desc: 'Test 009: Highlighter broken on starting Umlaut?', + text: 'Österreich', + highlight: 'Österreich', + expected: 'Österreich' + }, + { + desc: 'Test 010: Highlighter broken on starting Umlaut?', + text: 'Österreich', + highlight: 'Ö', + expected: 'Österreich' + }, + { + desc: 'Test 011: Highlighter broken on starting Umlaut?', + text: 'Österreich', + highlight: 'Öst', + expected: 'Österreich' + }, + { + desc: 'Test 012: Highlighter broken on starting Umlaut?', + text: 'Österreich', + highlight: 'Oe', + expected: 'Österreich' + }, + { + desc: 'Test 013: Highlighter broken on punctuation mark?', + text: 'So good. To be there', + highlight: 'good', + expected: 'So good. To be there' + }, + { + desc: 'Test 014: Highlighter broken on space?', + text: 'So good. To be there', + highlight: 'be', + expected: 'So good. To be there' + }, + { + desc: 'Test 015: Highlighter broken on space?', + text: 'So good. To be there', + highlight: ' be', + expected: 'So good. To be there' + }, + { + desc: 'Test 016: Highlighter broken on space?', + text: 'So good. To be there', + highlight: 'be ', + expected: 'So good. To be there' + }, + { + desc: 'Test 017: Highlighter broken on space?', + text: 'So good. To be there', + highlight: ' be ', + expected: 'So good. To be there' + }, + { + desc: 'Test 018: en de Highlighter broken on special character at the end?', + text: 'So good. xbß', + highlight: 'xbß', + expected: 'So good. xbß' + }, + { + desc: 'Test 019: en de Highlighter broken on special character at the end?', + text: 'So good. xbß.', + highlight: 'xbß.', + expected: 'So good. xbß.' + }, + { + desc: 'Test 020: RTL he Hebrew', + text: 'חסיד אומות העולם', + highlight: 'חסיד אומות העולם', + expected: 'חסיד אומות העולם' + }, + { + desc: 'Test 021: RTL he Hebrew', + text: 'חסיד אומות העולם', + highlight: 'חסי', + expected: 'חסיד אומות העולם' + }, + { + desc: 'Test 022: ja Japanese', + text: '諸国民の中の正義の人', + highlight: '諸国民の中の正義の人', + expected: '諸国民の中の正義の人' + }, + { + desc: 'Test 023: ja Japanese', + text: '諸国民の中の正義の人', + highlight: '諸国', + expected: '諸国民の中の正義の人' + }, + { + desc: 'Test 024: fr French text and « french quotes » (guillemets)', + text: '« L\'oiseau est sur l’île »', + highlight: '« L\'oiseau est sur l’île »', + expected: '« L\'oiseau est sur l’île »' + }, + { + desc: 'Test 025: fr French text and « french quotes » (guillemets)', + text: '« L\'oiseau est sur l’île »', + highlight: '« L\'oise', + expected: '« L\'oiseau est sur l’île »' + }, + { + desc: 'Test 025a: fr French text and « french quotes » (guillemets) - does it match the single strings "«" and "L" separately?', + text: '« L\'oiseau est sur l’île »', + highlight: '« L', + expected: '« L\'oiseau est sur l’île »' + }, + { + desc: 'Test 026: ru Russian', + text: 'Праведники мира', + highlight: 'Праведники мира', + expected: 'Праведники мира' + }, + { + desc: 'Test 027: ru Russian', + text: 'Праведники мира', + highlight: 'Праве', + expected: 'Праведники мира' + }, + { + desc: 'Test 028 ka Georgian', + text: 'მთავარი გვერდი', + highlight: 'მთავარი გვერდი', + expected: 'მთავარი გვერდი' + }, + { + desc: 'Test 029 ka Georgian', + text: 'მთავარი გვერდი', + highlight: 'მთა', + expected: 'მთავარი გვერდი' + }, + { + desc: 'Test 030 hy Armenian', + text: 'Նոնա Գափրինդաշվիլի', + highlight: 'Նոնա Գափրինդաշվիլի', + expected: 'Նոնա Գափրինդաշվիլի' + }, + { + desc: 'Test 031 hy Armenian', + text: 'Նոնա Գափրինդաշվիլի', + highlight: 'Նոն', + expected: 'Նոնա Գափրինդաշվիլի' + }, + { + desc: 'Test 032: th Thai', + text: 'พอล แอร์ดิช', + highlight: 'พอล แอร์ดิช', + expected: 'พอล แอร์ดิช' + }, + { + desc: 'Test 033: th Thai', + text: 'พอล แอร์ดิช', + highlight: 'พอ', + expected: 'พอล แอร์ดิช' + }, + { + desc: 'Test 034: RTL ar Arabic', + text: 'بول إيردوس', + highlight: 'بول إيردوس', + expected: 'بول إيردوس' + }, + { + desc: 'Test 035: RTL ar Arabic', + text: 'بول إيردوس', + highlight: 'بو', + expected: 'بول إيردوس' + } + ]; + QUnit.expect( cases.length ); - $.each( cases, function ( i, item ) { - $fixture = $( '

      ' ).text( item.text ).highlightText( item.highlight ); - assert.equal( - $fixture.html(), - $( '

      ' ).html( item.expected ).html(), // re-parse to normalize! - item.desc || undefined - ); + $.each( cases, function ( i, item ) { + $fixture = $( '

      ' ).text( item.text ).highlightText( item.highlight ); + assert.equal( + $fixture.html(), + // Re-parse to normalize + $( '

      ' ).html( item.expected ).html(), + item.desc || undefined + ); + } ); } ); -} ); +}( jQuery ) ); diff --git a/tests/qunit/suites/resources/jquery/jquery.localize.test.js b/tests/qunit/suites/resources/jquery/jquery.localize.test.js index c8e1d9f9a9..3810a042d0 100644 --- a/tests/qunit/suites/resources/jquery/jquery.localize.test.js +++ b/tests/qunit/suites/resources/jquery/jquery.localize.test.js @@ -1,133 +1,135 @@ -QUnit.module( 'jquery.localize', QUnit.newMwEnvironment() ); +( function ( $, mw ) { + QUnit.module( 'jquery.localize', QUnit.newMwEnvironment() ); -QUnit.test( 'Handle basic replacements', 4, function ( assert ) { - var html, $lc; - mw.messages.set( 'basic', 'Basic stuff' ); + QUnit.test( 'Handle basic replacements', 4, function ( assert ) { + var html, $lc; + mw.messages.set( 'basic', 'Basic stuff' ); - // Tag: html:msg - html = '

      '; - $lc = $( html ).localize().find( 'span' ); + // Tag: html:msg + html = '
      '; + $lc = $( html ).localize().find( 'span' ); - assert.strictEqual( $lc.text(), 'Basic stuff', 'Tag: html:msg' ); + assert.strictEqual( $lc.text(), 'Basic stuff', 'Tag: html:msg' ); - // Attribute: title-msg - html = '
      '; - $lc = $( html ).localize().find( 'span' ); + // Attribute: title-msg + html = '
      '; + $lc = $( html ).localize().find( 'span' ); - assert.strictEqual( $lc.attr( 'title' ), 'Basic stuff', 'Attribute: title-msg' ); + assert.strictEqual( $lc.attr( 'title' ), 'Basic stuff', 'Attribute: title-msg' ); - // Attribute: alt-msg - html = '
      '; - $lc = $( html ).localize().find( 'span' ); + // Attribute: alt-msg + html = '
      '; + $lc = $( html ).localize().find( 'span' ); - assert.strictEqual( $lc.attr( 'alt' ), 'Basic stuff', 'Attribute: alt-msg' ); + assert.strictEqual( $lc.attr( 'alt' ), 'Basic stuff', 'Attribute: alt-msg' ); - // Attribute: placeholder-msg - html = '
      '; - $lc = $( html ).localize().find( 'input' ); + // Attribute: placeholder-msg + html = '
      '; + $lc = $( html ).localize().find( 'input' ); - assert.strictEqual( $lc.attr( 'placeholder' ), 'Basic stuff', 'Attribute: placeholder-msg' ); -} ); + assert.strictEqual( $lc.attr( 'placeholder' ), 'Basic stuff', 'Attribute: placeholder-msg' ); + } ); + + QUnit.test( 'Proper escaping', 2, function ( assert ) { + var html, $lc; + mw.messages.set( 'properfoo', '' ); + + // This is handled by jQuery inside $.fn.localize, just a simple sanity checked + // making sure it is actually using text() and attr() (or something with the same effect) -QUnit.test( 'Proper escaping', 2, function ( assert ) { - var html, $lc; - mw.messages.set( 'properfoo', '' ); + // Text escaping + html = '
      '; + $lc = $( html ).localize().find( 'span' ); - // This is handled by jQuery inside $.fn.localize, just a simple sanity checked - // making sure it is actually using text() and attr() (or something with the same effect) + assert.strictEqual( $lc.text(), mw.msg( 'properfoo' ), 'Content is inserted as text, not as html.' ); - // Text escaping - html = '
      '; - $lc = $( html ).localize().find( 'span' ); + // Attribute escaping + html = '
      '; + $lc = $( html ).localize().find( 'span' ); - assert.strictEqual( $lc.text(), mw.msg( 'properfoo' ), 'Content is inserted as text, not as html.' ); + assert.strictEqual( $lc.attr( 'title' ), mw.msg( 'properfoo' ), 'Attributes are not inserted raw.' ); + } ); - // Attribute escaping - html = '
      '; - $lc = $( html ).localize().find( 'span' ); + QUnit.test( 'Options', 7, function ( assert ) { + mw.messages.set( { + 'foo-lorem': 'Lorem', + 'foo-ipsum': 'Ipsum', + 'foo-bar-title': 'Read more about bars', + 'foo-bar-label': 'The Bars', + 'foo-bazz-title': 'Read more about bazz at $1 (last modified: $2)', + 'foo-bazz-label': 'The Bazz ($1)', + 'foo-welcome': 'Welcome to $1! (last visit: $2)' + } ); + var html, $lc, x, sitename = 'Wikipedia'; + + // Message key prefix + html = '
      '; + $lc = $( html ).localize( { + prefix: 'foo-' + } ).find( 'span' ); + + assert.strictEqual( $lc.attr( 'title' ), 'Lorem', 'Message key prefix - attr' ); + assert.strictEqual( $lc.text(), 'Ipsum', 'Message key prefix - text' ); + + // Variable keys mapping + x = 'bar'; + html = '
      '; + $lc = $( html ).localize( { + keys: { + 'title': 'foo-' + x + '-title', + 'label': 'foo-' + x + '-label' + } + } ).find( 'span' ); + + assert.strictEqual( $lc.attr( 'title' ), 'Read more about bars', 'Variable keys mapping - attr' ); + assert.strictEqual( $lc.text(), 'The Bars', 'Variable keys mapping - text' ); + + // Passing parameteters to mw.msg + html = '
      '; + $lc = $( html ).localize( { + params: { + 'foo-welcome': [sitename, 'yesterday'] + } + } ).find( 'span' ); + + assert.strictEqual( $lc.text(), 'Welcome to Wikipedia! (last visit: yesterday)', 'Passing parameteters to mw.msg' ); + + // Combination of options prefix, params and keys + x = 'bazz'; + html = '
      '; + $lc = $( html ).localize( { + prefix: 'foo-', + keys: { + 'title': x + '-title', + 'label': x + '-label' + }, + params: { + 'title': [sitename, '3 minutes ago'], + 'label': [sitename, '3 minutes ago'] + + } + } ).find( 'span' ); + + assert.strictEqual( $lc.text(), 'The Bazz (Wikipedia)', 'Combination of options prefix, params and keys - text' ); + assert.strictEqual( $lc.attr( 'title' ), 'Read more about bazz at Wikipedia (last modified: 3 minutes ago)', 'Combination of options prefix, params and keys - attr' ); + } ); - assert.strictEqual( $lc.attr( 'title' ), mw.msg( 'properfoo' ), 'Attributes are not inserted raw.' ); -} ); + QUnit.test( 'Handle data text', 2, function ( assert ) { + var html, $lc; + mw.messages.set( 'option-one', 'Item 1' ); + mw.messages.set( 'option-two', 'Item 2' ); + html = ''; + $lc = $( html ).localize().find( 'option' ); + assert.strictEqual( $lc.eq( 0 ).text(), mw.msg( 'option-one' ), 'data-msg-text becomes text of options' ); + assert.strictEqual( $lc.eq( 1 ).text(), mw.msg( 'option-two' ), 'data-msg-text becomes text of options' ); + } ); -QUnit.test( 'Options', 7, function ( assert ) { - mw.messages.set( { - 'foo-lorem': 'Lorem', - 'foo-ipsum': 'Ipsum', - 'foo-bar-title': 'Read more about bars', - 'foo-bar-label': 'The Bars', - 'foo-bazz-title': 'Read more about bazz at $1 (last modified: $2)', - 'foo-bazz-label': 'The Bazz ($1)', - 'foo-welcome': 'Welcome to $1! (last visit: $2)' + QUnit.test( 'Handle data html', 2, function ( assert ) { + var html, $lc; + mw.messages.set( 'html', 'behold... there is a link here!!' ); + html = '
      '; + $lc = $( html ).localize().find( 'a' ); + assert.strictEqual( $lc.length, 1, 'link is created' ); + assert.strictEqual( $lc.text(), 'link', 'the link text got added' ); } ); - var html, $lc, attrs, x, sitename = 'Wikipedia'; - - // Message key prefix - html = '
      '; - $lc = $( html ).localize( { - prefix: 'foo-' - } ).find( 'span' ); - - assert.strictEqual( $lc.attr( 'title' ), 'Lorem', 'Message key prefix - attr' ); - assert.strictEqual( $lc.text(), 'Ipsum', 'Message key prefix - text' ); - - // Variable keys mapping - x = 'bar'; - html = '
      '; - $lc = $( html ).localize( { - keys: { - 'title': 'foo-' + x + '-title', - 'label': 'foo-' + x + '-label' - } - } ).find( 'span' ); - - assert.strictEqual( $lc.attr( 'title' ), 'Read more about bars', 'Variable keys mapping - attr' ); - assert.strictEqual( $lc.text(), 'The Bars', 'Variable keys mapping - text' ); - - // Passing parameteters to mw.msg - html = '
      '; - $lc = $( html ).localize( { - params: { - 'foo-welcome': [sitename, 'yesterday'] - } - } ).find( 'span' ); - - assert.strictEqual( $lc.text(), 'Welcome to Wikipedia! (last visit: yesterday)', 'Passing parameteters to mw.msg' ); - - // Combination of options prefix, params and keys - x = 'bazz'; - html = '
      '; - $lc = $( html ).localize( { - prefix: 'foo-', - keys: { - 'title': x + '-title', - 'label': x + '-label' - }, - params: { - 'title': [sitename, '3 minutes ago'], - 'label': [sitename, '3 minutes ago'] - - } - } ).find( 'span' ); - - assert.strictEqual( $lc.text(), 'The Bazz (Wikipedia)', 'Combination of options prefix, params and keys - text' ); - assert.strictEqual( $lc.attr( 'title' ), 'Read more about bazz at Wikipedia (last modified: 3 minutes ago)', 'Combination of options prefix, params and keys - attr' ); -} ); - -QUnit.test( 'Handle data text', 2, function ( assert ) { - var html, $lc; - mw.messages.set( 'option-one', 'Item 1' ); - mw.messages.set( 'option-two', 'Item 2' ); - html = ''; - $lc = $( html ).localize().find( 'option' ); - assert.strictEqual( $lc.eq( 0 ).text(), mw.msg( 'option-one' ), 'data-msg-text becomes text of options' ); - assert.strictEqual( $lc.eq( 1 ).text(), mw.msg( 'option-two' ), 'data-msg-text becomes text of options' ); -} ); - -QUnit.test( 'Handle data html', 2, function ( assert ) { - var html, $lc; - mw.messages.set( 'html', 'behold... there is a
      link here!!' ); - html = '
      '; - $lc = $( html ).localize().find( 'a' ); - assert.strictEqual( $lc.length, 1, 'link is created' ); - assert.strictEqual( $lc.text(), 'link', 'the link text got added' ); -} ); +}( jQuery, mediaWiki ) ) ; diff --git a/tests/qunit/suites/resources/jquery/jquery.mwExtension.test.js b/tests/qunit/suites/resources/jquery/jquery.mwExtension.test.js index 5b566ae0e8..3ffcbf5741 100644 --- a/tests/qunit/suites/resources/jquery/jquery.mwExtension.test.js +++ b/tests/qunit/suites/resources/jquery/jquery.mwExtension.test.js @@ -1,58 +1,60 @@ -QUnit.module( 'jquery.mwExtension', QUnit.newMwEnvironment() ); - -QUnit.test( 'String functions', function ( assert ) { - - assert.equal( $.trimLeft( ' foo bar ' ), 'foo bar ', 'trimLeft' ); - assert.equal( $.trimRight( ' foo bar ' ), ' foo bar', 'trimRight' ); - assert.equal( $.ucFirst( 'foo' ), 'Foo', 'ucFirst' ); - - assert.equal( $.escapeRE( '