Lint: Go-go-gadget jshint! Passing entire JS code base (again).
authorTimo Tijhof <ttijhof@wikimedia.org>
Wed, 26 Sep 2012 07:14:52 +0000 (09:14 +0200)
committerTimo Tijhof <ttijhof@wikimedia.org>
Sat, 10 Nov 2012 11:23:43 +0000 (12:23 +0100)
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

68 files changed:
.gitignore
.jshintignore
.jshintrc
resources/jquery/jquery.checkboxShiftClick.js
resources/jquery/jquery.client.js
resources/jquery/jquery.collapsibleTabs.js
resources/jquery/jquery.colorUtil.js
resources/jquery/jquery.expandableField.js
resources/jquery/jquery.hidpi.js
resources/jquery/jquery.highlightText.js
resources/jquery/jquery.mw-jump.js
resources/jquery/jquery.mwExtension.js
resources/jquery/jquery.qunit.completenessTest.js
resources/jquery/jquery.suggestions.js
resources/jquery/jquery.textSelection.js
resources/mediawiki.action/mediawiki.action.edit.js
resources/mediawiki.language/mediawiki.cldr.js
resources/mediawiki.language/mediawiki.language.init.js
resources/mediawiki.language/mediawiki.language.js
resources/mediawiki.page/mediawiki.page.ready.js
resources/mediawiki.page/mediawiki.page.watch.ajax.js
resources/mediawiki.special/mediawiki.special.block.js
resources/mediawiki.special/mediawiki.special.javaScriptTest.js
resources/mediawiki.special/mediawiki.special.js
resources/mediawiki.special/mediawiki.special.movePage.js
resources/mediawiki.special/mediawiki.special.preferences.js
resources/mediawiki.special/mediawiki.special.recentchanges.js
resources/mediawiki.special/mediawiki.special.search.js
resources/mediawiki.special/mediawiki.special.undelete.js
resources/mediawiki.special/mediawiki.special.upload.js
resources/mediawiki/mediawiki.Title.js
resources/mediawiki/mediawiki.feedback.js
resources/mediawiki/mediawiki.hidpi.js
resources/mediawiki/mediawiki.htmlform.js
resources/mediawiki/mediawiki.jqueryMsg.js
resources/mediawiki/mediawiki.js
resources/mediawiki/mediawiki.notification.js
resources/mediawiki/mediawiki.util.js
resources/startup.js
tests/qunit/data/callMwLoaderTestCallback.js
tests/qunit/data/generateJqueryMsgData.php
tests/qunit/data/mediawiki.jqueryMsg.data.js
tests/qunit/data/testrunner.js
tests/qunit/suites/resources/jquery/jquery.autoEllipsis.test.js
tests/qunit/suites/resources/jquery/jquery.byteLength.test.js
tests/qunit/suites/resources/jquery/jquery.byteLimit.test.js
tests/qunit/suites/resources/jquery/jquery.client.test.js
tests/qunit/suites/resources/jquery/jquery.colorUtil.test.js
tests/qunit/suites/resources/jquery/jquery.delayedBind.test.js
tests/qunit/suites/resources/jquery/jquery.getAttrs.test.js
tests/qunit/suites/resources/jquery/jquery.hidpi.test.js
tests/qunit/suites/resources/jquery/jquery.highlightText.test.js
tests/qunit/suites/resources/jquery/jquery.localize.test.js
tests/qunit/suites/resources/jquery/jquery.mwExtension.test.js
tests/qunit/suites/resources/jquery/jquery.tabIndex.test.js
tests/qunit/suites/resources/jquery/jquery.tablesorter.test.js
tests/qunit/suites/resources/jquery/jquery.textSelection.test.js
tests/qunit/suites/resources/mediawiki.api/mediawiki.api.parse.test.js
tests/qunit/suites/resources/mediawiki.api/mediawiki.api.test.js
tests/qunit/suites/resources/mediawiki.special/mediawiki.special.recentchanges.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.Title.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.Uri.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.cldr.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.jscompat.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.language.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.user.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.util.test.js

index 7f1ac5e..a47a454 100644 (file)
@@ -31,6 +31,9 @@ AdminSettings.php
 LocalSettings.php
 StartProfiler.php
 
+# Building & testing
+node_modules/
+
 # Operating systems
 ## Mac OS X
 .DS_Store
index 026eaaa..3869deb 100644 (file)
@@ -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
index b86ceb5..7fa138d 100644 (file)
--- 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
index 1990dc0..aced063 100644 (file)
@@ -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 <krinklemail@gmail.com>
+ * @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...
index 24f8959..b35dbbb 100644 (file)
                        // 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,
                                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 */
 
                 * @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();
index cb25796..9b8f8fc 100644 (file)
                        }
                        return $settings;
                },
-               handleResize: function ( e ) {
+               /**
+                * @param {jQuery.Event} e
+                */
+               handleResize: function () {
                        $.collapsibleTabs.instances.each( function () {
                                var $el = $( this ),
                                        data = $.collapsibleTabs.getSettings( $el );
index c1fe7fe..9c6f9ec 100644 (file)
                 * @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:
                 * @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;
                                        }
                                        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 );
index 063f260..9e532e5 100644 (file)
                                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
index b7335ff..70bfc4e 100644 (file)
@@ -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;
                        }
index f720e07..cda2765 100644 (file)
@@ -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
index 36b6690..e286834 100644 (file)
@@ -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' });
                }
index bbffd7b..de39978 100644 (file)
                        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;
index 1475af2..ef28948 100644 (file)
@@ -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,
index d80680f..edc18a7 100644 (file)
  *
  * 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'
  *             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 = $( '<div>' )
+                                                       text = context.config.suggestions[i];
+                                                       $result = $( '<div>' )
                                                                .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 <tr> 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(
                                        $( '<div>' ).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(
                                        $( '<div>' ).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;
index abb0fa3..17fd0cd 100644 (file)
        }
 
        $.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
                         */
                                                        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';
                                                        }
                                                }
 
                                                        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 +
                                                // 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;
                         */
                         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
                                                // 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
                                                                } else {
                                                                        preRange.moveEnd( 'character', -1 );
                                                                        if ( preRange.text === preText ) {
-                                                                               rawPreText += "\r\n";
+                                                                               rawPreText += '\r\n';
                                                                        } else {
                                                                                preFinished = true;
                                                                        }
                                                                } else {
                                                                        periRange.moveEnd( 'character', -1 );
                                                                        if ( periRange.text === periText ) {
-                                                                               rawPeriText += "\r\n";
+                                                                               rawPeriText += '\r\n';
                                                                        } else {
                                                                                periFinished = true;
                                                                        }
                                                                } 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;
                                        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++;
                                                        }
                                                }
                                        }
-                                       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;
                                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();
                }
index 1c51c97..2835c9c 100644 (file)
@@ -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', {
index 6660eca..c3023cd 100644 (file)
@@ -1,8 +1,8 @@
 /**
- *  CLDR related utility methods
+ *  CLDR related utility methods.
  */
-( function( mw ) {
-       "use strict";
+( function ( mw ) {
+       'use strict';
 
        var cldr = {
                /**
                 * 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 ) );
index 30307a3..937b89b 100644 (file)
@@ -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 ) );
index 935d4ff..514dbd5 100644 (file)
@@ -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 '';
                }
index 370c3a1..684f582 100644 (file)
@@ -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 ) );
index a7e059c..3957493 100644 (file)
                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 () {
                                        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 );
                                        if ( watchResponse.watched !== undefined ) {
                                                $( '#wpWatchthis' ).prop( 'checked', true );
                                        } else {
-                                               $( '#wpWatchthis' ).removeProp( 'checked' );
+                                               $( '#wpWatchthis' ).prop( 'checked', false );
                                        }
                                },
                                // Error
 
                                }
                        );
-               });
-       });
+               } );
+       } );
 
 }( mediaWiki, jQuery ) );
index 6f79929..077adcd 100644 (file)
@@ -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 );
-});
index 808d5fe..a560ca9 100644 (file)
@@ -8,7 +8,7 @@
                // (only if a framework was found, not on error pages).
                $( '#mw-javascripttest-summary.mw-javascripttest-frameworkfound' ).append( function () {
 
-                       var     $html = $( '<p><label for="useskin">'
+                       var $html = $( '<p><label for="useskin">'
                                        + mw.message( 'javascripttest-pagetext-skins' ).escaped()
                                        + ' '
                                        + '</label></p>' ),
index 3526cef..8edb1cb 100644 (file)
@@ -1 +1,5 @@
-mw.special = {};
+/*
+ * Namespace for mediawiki.special.* modules
+ */
+
+mediaWiki.special = {};
index 68c2ed0..7a55806 100644 (file)
@@ -1,5 +1,7 @@
-/* JavaScript for Special:MovePage */
+/**
+ * JavaScript for Special:MovePage
+ */
 
 jQuery( function( $ ) {
        $( '#wpReason, #wpNewTitleMain' ).byteLimit();
-});
+} );
index 0804825..989a986 100644 (file)
  * JavaScript for Special:Preferences
  */
 jQuery( document ).ready( function ( $ ) {
-$( '#prefsubmit' ).attr( 'id', 'prefcontrol' );
-var $preftoc = $('<ul id="preftoc"></ul>');
-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 = $('<ul id="preftoc"></ul>'),
+               $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 = $( '<li>' )
+                       .addClass( i === 0 ? 'selected' : '' );
+               $a = $( '<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 = $( '<li/>', {
-               'class' : ( i === 0 ) ? 'selected' : null
-       });
-       var $a = $( '<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
-// <a href="#.."> 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
+       // <a href="#.."> 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();
-}
 } );
index 7996d93..3cba9d3 100644 (file)
@@ -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
                 */
                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 ) );
index 04954e8..0cca26d 100644 (file)
@@ -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(
-       $('<label />')
-               .text(mw.msg('powersearch-togglelabel'))
-).append(
-       $('<input type="button" />')
-               .attr('id', 'mw-search-toggleall')
-               .attr('value', mw.msg('powersearch-toggleall'))
-               .click( function() {
-                       $checkboxes.prop('checked', true);
-               } )
-).append(
-       $('<input type="button" />')
-               .attr('id', 'mw-search-togglenone')
-               .attr('value', mw.msg('powersearch-togglenone'))
-               .click( function() {
-                       $checkboxes.prop('checked', false);
-               } )
-);
+               // Create check all/none button
+               $checkboxes = $('#powersearch input[id^=mw-search-ns]');
+               $('#mw-search-togglebox').append(
+                       $('<label>')
+                               .text(mw.msg('powersearch-togglelabel'))
+               ).append(
+                       $('<input type="button" />')
+                               .attr( 'id', 'mw-search-toggleall' )
+                               .prop( 'value', mw.msg('powersearch-toggleall' ) )
+                               .click( function () {
+                                       $checkboxes.prop('checked', true);
+                               } )
+               ).append(
+                       $('<input type="button" />')
+                               .attr( 'id', 'mw-search-togglenone' )
+                               .prop( 'value', mw.msg('powersearch-togglenone' ) )
+                               .click( function() {
+                                       $checkboxes.prop( 'checked', false );
+                               } )
+               );
 
-// Change the header search links to what user entered
-var headerLinks = $('.search-types a');
-$('#searchText, #powerSearchText').change(function() {
-       var searchterm = $(this).val();
-       headerLinks.each( function() {
-               var parts = $(this).attr('href').split( 'search=' );
-               var lastpart = '';
-               var prefix = 'search=';
-               if( parts.length > 1 && parts[1].indexOf('&') >= 0 ) {
-                       lastpart = parts[1].substring( parts[1].indexOf('&') );
-               } else {
-                       prefix = '&search=';
-               }
-               this.href = parts[0] + prefix + encodeURIComponent( searchterm ) + lastpart;
-       });
-}).trigger('change');
+               // Change the header search links to what user entered
+               $headerLinks = $( '.search-types a' );
+               $( '#searchText, #powerSearchText' ).change( function () {
+                       var searchterm = $(this).val();
+                       $headerLinks.each( function () {
+                               var parts = $(this).attr('href').split( 'search=' ),
+                                       lastpart = '',
+                                       prefix = 'search=';
+                               if ( parts.length > 1 && parts[1].indexOf('&') >= 0 ) {
+                                       lastpart = parts[1].substring( parts[1].indexOf('&') );
+                               } else {
+                                       prefix = '&search=';
+                               }
+                               this.href = parts[0] + prefix + encodeURIComponent( searchterm ) + lastpart;
+                       });
+               }).trigger( 'change' );
+
+       } );
 
-} ); } )( jQuery, mediaWiki );
+}( jQuery, mediaWiki ) );
index 33b8027..c6e5d61 100644 (file)
@@ -1,10 +1,11 @@
 /*
  * JavaScript for Specical:Undelete
  */
-jQuery( document ).ready( function( $ ) {
-       $( '#mw-undelete-invert' ).click( function( e ) {
+jQuery( document ).ready( function ( $ ) {
+       $( '#mw-undelete-invert' ).click( function ( e ) {
                e.preventDefault();
-               $( '#undelete' ).find( 'input:checkbox' )
-                       .prop( 'checked', function( i, val ) { return !val; } );
+               $( '#undelete input[type="checkbox"]' ).prop( 'checked', function ( i, val ) {
+                       return !val;
+               } );
        } );
 } );
index 63e8971..b05fd71 100644 (file)
@@ -11,7 +11,7 @@
                 * Is the FileAPI available with sufficient functionality?
                 */
                function hasFileAPI() {
-                       return typeof window.FileReader !== 'undefined';
+                       return window.FileReader !== undefined;
                }
 
                /**
                 * @param {File} file
                 */
                function showPreview( file ) {
-                       var     previewSize = 180,
+                       var $canvas,
+                               ctx,
+                               meta,
+                               previewSize = 180,
                                thumb = $( '<div id="mw-upload-thumbnail" class="thumb tright">' +
                                                        '<div class="thumbinner">' +
                                                                '<div class="mw-small-spinner" style="width: 180px; height: 180px"></div>' +
                                                                '<div class="thumbcaption"><div class="filename"></div><div class="fileinfo"></div></div>' +
                                                        '</div>' +
                                                '</div>' );
+
                        thumb.find( '.filename' ).text( file.name ).end()
                                .find( '.fileinfo' ).text( prettySize( file.size ) ).end();
 
-                       var     $canvas = $('<canvas width="' + previewSize + '" height="' + previewSize + '" ></canvas>'),
-                               ctx = $canvas[0].getContext( '2d' );
+                       $canvas = $('<canvas width="' + previewSize + '" height="' + previewSize + '" ></canvas>');
+                       ctx = $canvas[0].getContext( '2d' );
                        $( '#mw-htmlform-source' ).parent().prepend( thumb );
 
-                       var meta;
-                       fetchPreview( file, function( dataURL ) {
+                       fetchPreview( file, function ( dataURL ) {
                                var     img = new Image(),
                                        rotation = 0;
 
@@ -79,7 +82,8 @@
                                }
 
                                img.onload = function () {
-                                       var width, height, x, y, dx, dy, logicalWidth, logicalHeight;
+                                       var info, width, height, x, y, dx, dy, logicalWidth, logicalHeight;
+
                                        // Fit the image within the previewSizexpreviewSize box
                                        if ( img.width > img.height ) {
                                                width = previewSize;
                                        thumb.find('.mw-small-spinner').replaceWith($canvas);
 
                                        // Image size
-                                       var info = mw.msg( 'widthheight', logicalWidth, logicalHeight ) +
+                                       info = mw.msg( 'widthheight', logicalWidth, logicalHeight ) +
                                                ', ' + prettySize( file.size );
+
                                        $( '#mw-upload-thumbnail .fileinfo' ).text( info );
                                };
                                img.src = dataURL;
                        }, mw.config.get( 'wgFileCanRotate' ) ? function ( data ) {
+                               /*jshint camelcase: false, nomen: false */
                                try {
                                        meta = mw.libs.jpegmeta( data, file.fileName );
                                        meta._binary_data = null;
                                // However, our JPEG metadata library wants a string.
                                // So, this is going to be an ugly conversion.
                                reader.onload = function() {
-                                       var buffer = new Uint8Array( reader.result ),
+                                       var i,
+                                               buffer = new Uint8Array( reader.result ),
                                                string = '';
-                                       for ( var i = 0; i < buffer.byteLength; i++ ) {
+                                       for ( i = 0; i < buffer.byteLength; i++ ) {
                                                string += String.fromCharCode( buffer[i] );
                                        }
                                        callbackBinary( string );
                        } else {
                                // This ends up decoding the file to base-64 and back again, which
                                // feels horribly inefficient.
-                               reader.onload = function() {
+                               reader.onload = function () {
                                        callback( reader.result );
                                };
                                reader.readAsDataURL( file );
                 * Check if the file does not exceed the maximum size
                 */
                function checkMaxUploadSize( file ) {
+                       var maxSize, $error;
+
                        function getMaxUploadSize( type ) {
                                var sizes = mw.config.get( 'wgMaxUploadSize' );
+
                                if ( sizes[type] !== undefined ) {
                                        return sizes[type];
                                }
                                return sizes['*'];
                        }
+
                        $( '.mw-upload-source-error' ).remove();
 
-                       var maxSize = getMaxUploadSize( 'file' );
+                       maxSize = getMaxUploadSize( 'file' );
                        if ( file.size > maxSize ) {
-                               var error = $( '<p class="error mw-upload-source-error" id="wpSourceTypeFile-error">' +
-                                               mw.message( 'largefileserver', file.size, maxSize ).escaped() + '</p>' );
-                               $( '#wpUploadFile' ).after( error );
+                               $error = $( '<p class="error mw-upload-source-error" id="wpSourceTypeFile-error">' +
+                                       mw.message( 'largefileserver', file.size, maxSize ).escaped() + '</p>' );
+
+                               $( '#wpUploadFile' ).after( $error );
+
                                return false;
                        }
+
                        return true;
                }
 
         * Disable all upload source fields except the selected one
         */
        $( function ( $ ) {
-               var i, row,
-                       rows = $( '.mw-htmlform-field-UploadSourceField' );
-               for ( i = rows.length; i; i-- ) {
-                       row = rows[i - 1];
-                       $( 'input[name="wpSourceType"]', row ).change( ( function () {
-                               var currentRow = row; // Store current row in our own scope
-                               return function () {
-                                       $( '.mw-upload-source-error' ).remove();
-                                       if ( this.checked ) {
-                                               // Disable all inputs
-                                               $( 'input[name!="wpSourceType"]', rows ).prop( 'disabled', true );
-                                               // Re-enable the current one
-                                               $( 'input', currentRow ).prop( 'disabled', false );
-                                       }
-                               };
-                       }() ) );
+               var i, $row,
+                       $rows = $( '.mw-htmlform-field-UploadSourceField' );
+
+               function createHandler( $currentRow ) {
+                       /**
+                        * @param {jQuery.Event}
+                        */
+                       return function () {
+                               $( '.mw-upload-source-error' ).remove();
+                               if ( this.checked ) {
+                                       // Disable all inputs
+                                       $rows.find( 'input[name!="wpSourceType"]' ).prop( 'disabled', true );
+                                       // Re-enable the current one
+                                       $currentRow.find( 'input' ).prop( 'disabled', false );
+                               }
+                       };
+               }
+
+               for ( i = $rows.length; i; i-- ) {
+                       $row = $rows.eq(i - 1);
+                       $row
+                               .find( 'input[name="wpSourceType"]' )
+                               .change( createHandler( $row ) );
                }
        } );
 
index 33cca58..dad6021 100644 (file)
@@ -133,11 +133,11 @@ var
                // In normal browsers the match-array contains null/undefined if there's no match,
                // IE returns an empty string.
                var matches = s.match( /^(?:([^:]+):)?(.*?)(?:\.(\w+))?$/ ),
-                       ns_match = getNsIdByName( matches[1] );
+                       nsMatch = getNsIdByName( matches[1] );
 
                // Namespace must be valid, and title must be a non-empty string.
-               if ( ns_match && typeof matches[2] === 'string' && matches[2] !== '' ) {
-                       title.ns = ns_match;
+               if ( nsMatch && typeof matches[2] === 'string' && matches[2] !== '' ) {
+                       title.ns = nsMatch;
                        title.name = fixName( matches[2] );
                        if ( typeof matches[3] === 'string' && matches[3] !== '' ) {
                                title.ext = fixExt( matches[3] );
index 634d02b..1afe51e 100644 (file)
@@ -1,5 +1,5 @@
 /**
- * mediawiki.Feedback
+ * mediawiki.feedback
  *
  * @author Ryan Kaldari, 2010
  * @author Neil Kandalgaonkar, 2010-11
 
        mw.Feedback.prototype = {
                setup: function () {
-                       var fb = this;
+                       var $feedbackPageLink,
+                               $bugNoteLink,
+                               $bugsListLink,
+                               fb = this;
 
-                       var $feedbackPageLink = $( '<a>' )
-                               .attr( { 'href': fb.title.getUrl(), 'target': '_blank' } )
-                               .css( { 'white-space': 'nowrap' } );
+                       $feedbackPageLink = $( '<a>' )
+                               .attr( {
+                                       href: fb.title.getUrl(),
+                                       target: '_blank'
+                               } )
+                               .css( {
+                                       whiteSpace: 'nowrap'
+                               } );
 
-                       var $bugNoteLink = $( '<a>' ).attr( { 'href': '#' } ).click( function () {
+                       $bugNoteLink = $( '<a>' ).attr( { href: '#' } ).click( function () {
                                fb.displayBugs();
                        } );
 
-                       var $bugsListLink = $( '<a>' ).attr( { 'href': fb.bugsListLink, 'target': '_blank' } );
+                       $bugsListLink = $( '<a>' ).attr( {
+                               href: fb.bugsListLink,
+                               target: '_blank'
+                       } );
 
                        // TODO: Use a stylesheet instead of these inline styles
                        this.$dialog =
                                        ),
                                        $( '<div class="feedback-mode feedback-submitting" style="text-align: center; margin: 3em 0;"></div>' ).append(
                                                mw.msg( 'feedback-adding' ),
-                                               $( '<br/>' ),
+                                               $( '<br>' ),
                                                $( '<span class="feedback-spinner"></span>' )
                                        ),
                                        $( '<div class="feedback-mode feedback-thanks" style="text-align: center; margin:1em"></div>' ).msg(
                },
 
                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' );
                        };
                },
 
                displayThanks: function () {
-                       var fb = this;
+                       var fb = this,
+                               closeButton = {};
                        this.display( 'thanks' );
-                       var closeButton = {};
                        closeButton[ mw.msg( 'feedback-close' ) ] = function () {
                                fb.$dialog.dialog( 'close' );
                        };
                 *      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();
                        };
                },
 
                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' );
                        };
                                }
                        }
 
-                       function err( code, info ) {
+                       function err() {
                                // ajax request failed
                                fb.displayError( 'feedback-error3' );
                        }
index 1979573..ecee450 100644 (file)
@@ -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
+} );
index a4753b9..83bf2e3 100644 (file)
@@ -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 ) );
index 7364829..6e2d3b4 100644 (file)
@@ -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 ) {
                 * 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();
                };
        };
                 *    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 );
                 * 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 ) {
                        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 );
                        }
                 * @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;
                                                }
                        // 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;
                        // 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();
                        // 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,
                                ] );
                                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
                        ] );
                                 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( [
                                }
                                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,
                                }
                                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
                                }
                                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,
                                ] );
                                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}}
                                        return [ res[0] ].concat( res[1] );
                                }
                        ] );
-                       var openTemplate = makeStringParser('{{');
-                       var closeTemplate = makeStringParser('}}');
+                       openTemplate = makeStringParser('{{');
+                       closeTemplate = makeStringParser('}}');
                        function template() {
                                var result = sequence( [
                                        openTemplate,
                                ] );
                                return result === null ? null : result[1];
                        }
-                       var nonWhitespaceExpression = choice( [
+                       nonWhitespaceExpression = choice( [
                                template,
                                link,
                                extLinkParam,
                                replacement,
                                literalWithoutSpace
                        ] );
-                       var paramExpression = choice( [
+                       paramExpression = choice( [
                                template,
                                link,
                                extLinkParam,
                                replacement,
                                literalWithoutBar
                        ] );
-                       var expression = choice( [
+                       expression = choice( [
                                template,
                                link,
                                extLinkParam,
                                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;
                }
                 * @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 {
                /**
                 * Transform wiki-link
                 * TODO unimplemented
+                * @param nodes
                 */
-               wlink: function ( nodes ) {
+               wlink: function () {
                        return 'unimplemented';
                },
 
                 * @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 {
                 * @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 ) : '';
                },
 
                 * @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 );
                }
        };
        $.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
index 690138c..b0abc9e 100644 (file)
@@ -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 '&#039;';
                                        case '"':
                                                return '&quot;';
index 58a3ab6..35cc6d8 100644 (file)
@@ -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;
                                                }
                                        } );
 
+                       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 = $( '<div>' )
+                       notif.$replacementPlaceholder = $( '<div>' )
                                // 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
                                                        // 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
                // 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();
                }
                }
        }
 
-       var notification = {
+       notification = {
                /**
                 * Pause auto-hide timers for all notifications.
                 * Notifications will not auto-hide until resume is called.
index 4e515bc..4bb004a 100644 (file)
@@ -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 <body> 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;
                 * is determined by validation.
                 */
                validateEmail: function ( mailtxt ) {
-                       var rfc5322_atext, rfc1034_ldh_str, HTML5_email_regexp;
+                       var rfc5322Atext, rfc1034LdhStr, html5EmailRegexp;
 
                        if ( mailtxt === '' ) {
                                return null;
                                                 "|" / "}" /
                                                 "~"
                        */
-                       rfc5322_atext = "a-z0-9!#$%&'*+\\-/=?^_`{|}~";
+                       rfc5322Atext = 'a-z0-9!#$%&\'*+\\-/=?^_`{|}~';
 
                        /**
                         * Next define the RFC 1034 'ldh-str'
                         *      <let-dig-hyp> ::= <let-dig> | "-"
                         *      <let-dig> ::= <letter> | <digit>
                         */
-                       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 ) );
                },
 
                /**
index 7951af0..deff7e6 100644 (file)
@@ -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.
  */
index 7079e0e..911dc3a 100644 (file)
@@ -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";
 
index 05bb5c8..776ee24 100644 (file)
@@ -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": {
index efa6549..0c3d364 100644 (file)
-( 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( "<scr" + "ipt src='" + QUnit.fixurl( mw.config.get( 'QUnitTestSwarmInjectJSPath' ) ) + "'></scr" + "ipt>" );
-}
-
-/**
- * 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)
- * </code>
- */
-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( '<scr' + 'ipt src="' + QUnit.fixurl( mw.config.get( 'QUnitTestSwarmInjectJSPath' ) ) + '"></scr' + 'ipt>' );
        }
 
-       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)
+        * </code>
+        */
+       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( '<' ), '&lt;', '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( '<' ), '&lt;', '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 ) );
index 0dee2ef..feeec55 100644 (file)
@@ -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 = $( '<div>' ).css( 'width', width );
-       var $div = $( '<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 = $( '<div>' ).css( 'width', width ),
+                       $div = $( '<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 ) );
index 8d4ac03..378ea4b 100644 (file)
@@ -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 ) );
index 4f86eb9..4fe6770 100644 (file)
        // 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' );
                }
        }
index bf62b39..bbc8852 100644 (file)
-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 ) );
index 7b37f5a..51a892e 100644 (file)
@@ -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 ) );
index a307983..3e7d5ff 100644 (file)
@@ -1,35 +1,37 @@
-QUnit.asyncTest('jquery.delayedBind with data option', 2, function ( assert ) {
-       var $fixture = $('<div>').appendTo('#qunit-fixture'),
-               data = { magic: "beeswax" },
-               delay = 50;
+( function ( $ ) {
+       QUnit.asyncTest('jquery.delayedBind with data option', 2, function ( assert ) {
+               var $fixture = $('<div>').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 = $('<div>').appendTo('#qunit-fixture'),
+                       delay = 50;
 
-QUnit.asyncTest('jquery.delayedBind without data option', 1, function ( assert ) {
-       var $fixture = $('<div>').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 ) );
index 6eef1ab..82566c2 100644 (file)
@@ -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( '<div>', attrs );
+       QUnit.test( 'Check', 1, function ( assert ) {
+               var     attrs = {
+                               foo: 'bar',
+                               'class': 'lorem'
+                       },
+                       $el = $( '<div>' ).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 ) );
index cf309df..d75c378 100644 (file)
@@ -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 ) );
index a94dca3..e1fb96d 100644 (file)
-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: '<span class="highlight">Blue</span> Öyster Cult'
-               },
-               {
-                       desc: 'Test 002',
-                       text: 'Blue Öyster Cult',
-                       highlight: 'Blue ',
-                       expected: '<span class="highlight">Blue</span> Öyster Cult'
-               },
-               {
-                       desc: 'Test 003',
-                       text: 'Blue Öyster Cult',
-                       highlight: 'Blue Ö',
-                       expected: '<span class="highlight">Blue</span> <span class="highlight">Ö</span>yster Cult'
-               },
-               {
-                       desc: 'Test 004',
-                       text: 'Blue Öyster Cult',
-                       highlight: 'Blue Öy',
-                       expected: '<span class="highlight">Blue</span> <span class="highlight">Öy</span>ster Cult'
-               },
-               {
-                       desc: 'Test 005',
-                       text: 'Blue Öyster Cult',
-                       highlight: ' Blue',
-                       expected: '<span class="highlight">Blue</span> Öyster Cult'
-               },
-               {
-                       desc: 'Test 006',
-                       text: 'Blue Öyster Cult',
-                       highlight: ' Blue ',
-                       expected: '<span class="highlight">Blue</span> Öyster Cult'
-               },
-               {
-                       desc: 'Test 007',
-                       text: 'Blue Öyster Cult',
-                       highlight: ' Blue Ö',
-                       expected: '<span class="highlight">Blue</span> <span class="highlight">Ö</span>yster Cult'
-               },
-               {
-                       desc: 'Test 008',
-                       text: 'Blue Öyster Cult',
-                       highlight: ' Blue Öy',
-                       expected: '<span class="highlight">Blue</span> <span class="highlight">Öy</span>ster Cult'
-               },
-               {
-                       desc: 'Test 009: Highlighter broken on starting Umlaut?',
-                       text: 'Österreich',
-                       highlight: 'Österreich',
-                       expected: '<span class="highlight">Österreich</span>'
-               },
-               {
-                       desc: 'Test 010: Highlighter broken on starting Umlaut?',
-                       text: 'Österreich',
-                       highlight: 'Ö',
-                       expected: '<span class="highlight">Ö</span>sterreich'
-               },
-               {
-                       desc: 'Test 011: Highlighter broken on starting Umlaut?',
-                       text: 'Österreich',
-                       highlight: 'Öst',
-                       expected: '<span class="highlight">Öst</span>erreich'
-               },
-               {
-                       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 <span class="highlight">good</span>. To be there'
-               },
-               {
-                       desc: 'Test 014: Highlighter broken on space?',
-                       text: 'So good. To be there',
-                       highlight: 'be',
-                       expected: 'So good. To <span class="highlight">be</span> there'
-               },
-               {
-                       desc: 'Test 015: Highlighter broken on space?',
-                       text: 'So good. To be there',
-                       highlight: ' be',
-                       expected: 'So good. To <span class="highlight">be</span> there'
-               },
-               {
-                       desc: 'Test 016: Highlighter broken on space?',
-                       text: 'So good. To be there',
-                       highlight: 'be ',
-                       expected: 'So good. To <span class="highlight">be</span> there'
-               },
-               {
-                       desc: 'Test 017: Highlighter broken on space?',
-                       text: 'So good. To be there',
-                       highlight: ' be ',
-                       expected: 'So good. To <span class="highlight">be</span> there'
-               },
-               {
-                       desc: 'Test 018: en de Highlighter broken on special character at the end?',
-                       text: 'So good. xbß',
-                       highlight: 'xbß',
-                       expected: 'So good. <span class="highlight">xbß</span>'
-               },
-               {
-                       desc: 'Test 019: en de Highlighter broken on special character at the end?',
-                       text: 'So good. xbß.',
-                       highlight: 'xbß.',
-                       expected: 'So good. <span class="highlight">xbß.</span>'
-               },
-               {
-                       desc: 'Test 020: RTL he Hebrew',
-                       text: 'חסיד אומות העולם',
-                       highlight: 'חסיד אומות העולם',
-                       expected: '<span class="highlight">חסיד</span> <span class="highlight">אומות</span> <span class="highlight">העולם</span>'
-               },
-               {
-                       desc: 'Test 021: RTL he Hebrew',
-                       text: 'חסיד אומות העולם',
-                       highlight: 'חסי',
-                       expected: '<span class="highlight">חסי</span>ד אומות העולם'
-               },
-               {
-                       desc: 'Test 022: ja Japanese',
-                       text: '諸国民の中の正義の人',
-                       highlight: '諸国民の中の正義の人',
-                       expected: '<span class="highlight">諸国民の中の正義の人</span>'
-               },
-               {
-                       desc: 'Test 023: ja Japanese',
-                       text: '諸国民の中の正義の人',
-                       highlight: '諸国',
-                       expected: '<span class="highlight">諸国</span>民の中の正義の人'
-               },
-               {
-                       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: '<span class="highlight">«</span> <span class="highlight">L\'oiseau</span> <span class="highlight">est</span> <span class="highlight">sur</span> <span class="highlight">l’île</span> <span class="highlight">»</span>'
-               },
-               {
-                       desc: 'Test 025: fr French text and « french quotes » (guillemets)',
-                       text: "« L'oiseau est sur l’île »",
-                       highlight: "« L'oise",
-                       expected: '<span class="highlight">«</span> <span class="highlight">L\'oise</span>au 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: '<span class="highlight">«</span> <span class="highlight">L</span>\'oiseau est sur <span class="highlight">l</span>’île »'
-               },
-               {
-                       desc: 'Test 026: ru Russian',
-                       text: 'Праведники мира',
-                       highlight: 'Праведники мира',
-                       expected: '<span class="highlight">Праведники</span> <span class="highlight">мира</span>'
-               },
-               {
-                       desc: 'Test 027: ru Russian',
-                       text: 'Праведники мира',
-                       highlight: 'Праве',
-                       expected: '<span class="highlight">Праве</span>дники мира'
-               },
-               {
-                       desc: 'Test 028 ka Georgian',
-                       text: 'მთავარი გვერდი',
-                       highlight: 'მთავარი გვერდი',
-                       expected: '<span class="highlight">მთავარი</span> <span class="highlight">გვერდი</span>'
-               },
-               {
-                       desc: 'Test 029 ka Georgian',
-                       text: 'მთავარი გვერდი',
-                       highlight: 'მთა',
-                       expected: '<span class="highlight">მთა</span>ვარი გვერდი'
-               },
-               {
-                       desc: 'Test 030 hy Armenian',
-                       text: 'Նոնա Գափրինդաշվիլի',
-                       highlight: 'Նոնա Գափրինդաշվիլի',
-                       expected: '<span class="highlight">Նոնա</span> <span class="highlight">Գափրինդաշվիլի</span>'
-               },
-               {
-                       desc: 'Test 031 hy Armenian',
-                       text: 'Նոնա Գափրինդաշվիլի',
-                       highlight: 'Նոն',
-                       expected: '<span class="highlight">Նոն</span>ա Գափրինդաշվիլի'
-               },
-               {
-                       desc: 'Test 032: th Thai',
-                       text: 'พอล แอร์ดิช',
-                       highlight: 'พอล แอร์ดิช',
-                       expected: '<span class="highlight">พอล</span> <span class="highlight">แอร์ดิช</span>'
-               },
-               {
-                       desc: 'Test 033: th Thai',
-                       text: 'พอล แอร์ดิช',
-                       highlight: 'พอ',
-                       expected: '<span class="highlight">พอ</span>ล แอร์ดิช'
-               },
-               {
-                       desc: 'Test 034: RTL ar Arabic',
-                       text: 'بول إيردوس',
-                       highlight: 'بول إيردوس',
-                       expected: '<span class="highlight">بول</span> <span class="highlight">إيردوس</span>'
-               },
-               {
-                       desc: 'Test 035: RTL ar Arabic',
-                       text: 'بول إيردوس',
-                       highlight: 'بو',
-                       expected: '<span class="highlight">بو</span>ل إيردوس'
-               }
-       ];
-       QUnit.expect( cases.length );
+       QUnit.test( 'Check', function ( assert ) {
+               var $fixture, cases = [
+                       {
+                               desc: 'Test 001',
+                               text: 'Blue Öyster Cult',
+                               highlight: 'Blue',
+                               expected: '<span class="highlight">Blue</span> Öyster Cult'
+                       },
+                       {
+                               desc: 'Test 002',
+                               text: 'Blue Öyster Cult',
+                               highlight: 'Blue ',
+                               expected: '<span class="highlight">Blue</span> Öyster Cult'
+                       },
+                       {
+                               desc: 'Test 003',
+                               text: 'Blue Öyster Cult',
+                               highlight: 'Blue Ö',
+                               expected: '<span class="highlight">Blue</span> <span class="highlight">Ö</span>yster Cult'
+                       },
+                       {
+                               desc: 'Test 004',
+                               text: 'Blue Öyster Cult',
+                               highlight: 'Blue Öy',
+                               expected: '<span class="highlight">Blue</span> <span class="highlight">Öy</span>ster Cult'
+                       },
+                       {
+                               desc: 'Test 005',
+                               text: 'Blue Öyster Cult',
+                               highlight: ' Blue',
+                               expected: '<span class="highlight">Blue</span> Öyster Cult'
+                       },
+                       {
+                               desc: 'Test 006',
+                               text: 'Blue Öyster Cult',
+                               highlight: ' Blue ',
+                               expected: '<span class="highlight">Blue</span> Öyster Cult'
+                       },
+                       {
+                               desc: 'Test 007',
+                               text: 'Blue Öyster Cult',
+                               highlight: ' Blue Ö',
+                               expected: '<span class="highlight">Blue</span> <span class="highlight">Ö</span>yster Cult'
+                       },
+                       {
+                               desc: 'Test 008',
+                               text: 'Blue Öyster Cult',
+                               highlight: ' Blue Öy',
+                               expected: '<span class="highlight">Blue</span> <span class="highlight">Öy</span>ster Cult'
+                       },
+                       {
+                               desc: 'Test 009: Highlighter broken on starting Umlaut?',
+                               text: 'Österreich',
+                               highlight: 'Österreich',
+                               expected: '<span class="highlight">Österreich</span>'
+                       },
+                       {
+                               desc: 'Test 010: Highlighter broken on starting Umlaut?',
+                               text: 'Österreich',
+                               highlight: 'Ö',
+                               expected: '<span class="highlight">Ö</span>sterreich'
+                       },
+                       {
+                               desc: 'Test 011: Highlighter broken on starting Umlaut?',
+                               text: 'Österreich',
+                               highlight: 'Öst',
+                               expected: '<span class="highlight">Öst</span>erreich'
+                       },
+                       {
+                               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 <span class="highlight">good</span>. To be there'
+                       },
+                       {
+                               desc: 'Test 014: Highlighter broken on space?',
+                               text: 'So good. To be there',
+                               highlight: 'be',
+                               expected: 'So good. To <span class="highlight">be</span> there'
+                       },
+                       {
+                               desc: 'Test 015: Highlighter broken on space?',
+                               text: 'So good. To be there',
+                               highlight: ' be',
+                               expected: 'So good. To <span class="highlight">be</span> there'
+                       },
+                       {
+                               desc: 'Test 016: Highlighter broken on space?',
+                               text: 'So good. To be there',
+                               highlight: 'be ',
+                               expected: 'So good. To <span class="highlight">be</span> there'
+                       },
+                       {
+                               desc: 'Test 017: Highlighter broken on space?',
+                               text: 'So good. To be there',
+                               highlight: ' be ',
+                               expected: 'So good. To <span class="highlight">be</span> there'
+                       },
+                       {
+                               desc: 'Test 018: en de Highlighter broken on special character at the end?',
+                               text: 'So good. xbß',
+                               highlight: 'xbß',
+                               expected: 'So good. <span class="highlight">xbß</span>'
+                       },
+                       {
+                               desc: 'Test 019: en de Highlighter broken on special character at the end?',
+                               text: 'So good. xbß.',
+                               highlight: 'xbß.',
+                               expected: 'So good. <span class="highlight">xbß.</span>'
+                       },
+                       {
+                               desc: 'Test 020: RTL he Hebrew',
+                               text: 'חסיד אומות העולם',
+                               highlight: 'חסיד אומות העולם',
+                               expected: '<span class="highlight">חסיד</span> <span class="highlight">אומות</span> <span class="highlight">העולם</span>'
+                       },
+                       {
+                               desc: 'Test 021: RTL he Hebrew',
+                               text: 'חסיד אומות העולם',
+                               highlight: 'חסי',
+                               expected: '<span class="highlight">חסי</span>ד אומות העולם'
+                       },
+                       {
+                               desc: 'Test 022: ja Japanese',
+                               text: '諸国民の中の正義の人',
+                               highlight: '諸国民の中の正義の人',
+                               expected: '<span class="highlight">諸国民の中の正義の人</span>'
+                       },
+                       {
+                               desc: 'Test 023: ja Japanese',
+                               text: '諸国民の中の正義の人',
+                               highlight: '諸国',
+                               expected: '<span class="highlight">諸国</span>民の中の正義の人'
+                       },
+                       {
+                               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: '<span class="highlight">«</span> <span class="highlight">L\'oiseau</span> <span class="highlight">est</span> <span class="highlight">sur</span> <span class="highlight">l’île</span> <span class="highlight">»</span>'
+                       },
+                       {
+                               desc: 'Test 025: fr French text and « french quotes » (guillemets)',
+                               text: '« L\'oiseau est sur l’île »',
+                               highlight: '« L\'oise',
+                               expected: '<span class="highlight">«</span> <span class="highlight">L\'oise</span>au 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: '<span class="highlight">«</span> <span class="highlight">L</span>\'oiseau est sur <span class="highlight">l</span>’île »'
+                       },
+                       {
+                               desc: 'Test 026: ru Russian',
+                               text: 'Праведники мира',
+                               highlight: 'Праведники мира',
+                               expected: '<span class="highlight">Праведники</span> <span class="highlight">мира</span>'
+                       },
+                       {
+                               desc: 'Test 027: ru Russian',
+                               text: 'Праведники мира',
+                               highlight: 'Праве',
+                               expected: '<span class="highlight">Праве</span>дники мира'
+                       },
+                       {
+                               desc: 'Test 028 ka Georgian',
+                               text: 'მთავარი გვერდი',
+                               highlight: 'მთავარი გვერდი',
+                               expected: '<span class="highlight">მთავარი</span> <span class="highlight">გვერდი</span>'
+                       },
+                       {
+                               desc: 'Test 029 ka Georgian',
+                               text: 'მთავარი გვერდი',
+                               highlight: 'მთა',
+                               expected: '<span class="highlight">მთა</span>ვარი გვერდი'
+                       },
+                       {
+                               desc: 'Test 030 hy Armenian',
+                               text: 'Նոնա Գափրինդաշվիլի',
+                               highlight: 'Նոնա Գափրինդաշվիլի',
+                               expected: '<span class="highlight">Նոնա</span> <span class="highlight">Գափրինդաշվիլի</span>'
+                       },
+                       {
+                               desc: 'Test 031 hy Armenian',
+                               text: 'Նոնա Գափրինդաշվիլի',
+                               highlight: 'Նոն',
+                               expected: '<span class="highlight">Նոն</span>ա Գափրինդաշվիլի'
+                       },
+                       {
+                               desc: 'Test 032: th Thai',
+                               text: 'พอล แอร์ดิช',
+                               highlight: 'พอล แอร์ดิช',
+                               expected: '<span class="highlight">พอล</span> <span class="highlight">แอร์ดิช</span>'
+                       },
+                       {
+                               desc: 'Test 033: th Thai',
+                               text: 'พอล แอร์ดิช',
+                               highlight: 'พอ',
+                               expected: '<span class="highlight">พอ</span>ล แอร์ดิช'
+                       },
+                       {
+                               desc: 'Test 034: RTL ar Arabic',
+                               text: 'بول إيردوس',
+                               highlight: 'بول إيردوس',
+                               expected: '<span class="highlight">بول</span> <span class="highlight">إيردوس</span>'
+                       },
+                       {
+                               desc: 'Test 035: RTL ar Arabic',
+                               text: 'بول إيردوس',
+                               highlight: 'بو',
+                               expected: '<span class="highlight">بو</span>ل إيردوس'
+                       }
+               ];
+               QUnit.expect( cases.length );
 
-       $.each( cases, function ( i, item ) {
-               $fixture = $( '<p>' ).text( item.text ).highlightText( item.highlight );
-               assert.equal(
-                       $fixture.html(),
-                       $( '<p>' ).html( item.expected ).html(), // re-parse to normalize!
-                       item.desc || undefined
-               );
+               $.each( cases, function ( i, item ) {
+                       $fixture = $( '<p>' ).text( item.text ).highlightText( item.highlight );
+                       assert.equal(
+                               $fixture.html(),
+                               // Re-parse to normalize
+                               $( '<p>' ).html( item.expected ).html(),
+                               item.desc || undefined
+                       );
+               } );
        } );
-} );
+}( jQuery ) );
index c8e1d9f..3810a04 100644 (file)
-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 = '<div><span><html:msg key="basic" /></span></div>';
-       $lc = $( html ).localize().find( 'span' );
+               // Tag: html:msg
+               html = '<div><span><html:msg key="basic" /></span></div>';
+               $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 = '<div><span title-msg="basic"></span></div>';
-       $lc = $( html ).localize().find( 'span' );
+               // Attribute: title-msg
+               html = '<div><span title-msg="basic"></span></div>';
+               $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 = '<div><span alt-msg="basic"></span></div>';
-       $lc = $( html ).localize().find( 'span' );
+               // Attribute: alt-msg
+               html = '<div><span alt-msg="basic"></span></div>';
+               $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 = '<div><input placeholder-msg="basic" /></div>';
-       $lc = $( html ).localize().find( 'input' );
+               // Attribute: placeholder-msg
+               html = '<div><input placeholder-msg="basic" /></div>';
+               $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', '<proper esc="test">' );
+
+               // 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', '<proper esc="test">' );
+               // Text escaping
+               html = '<div><span><html:msg key="properfoo"></span></div>';
+               $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 = '<div><span><html:msg key="properfoo"></span></div>';
-       $lc = $( html ).localize().find( 'span' );
+               // Attribute escaping
+               html = '<div><span title-msg="properfoo"></span></div>';
+               $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 = '<div><span title-msg="properfoo"></span></div>';
-       $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 = '<div><span title-msg="lorem"><html:msg key="ipsum"></span></div>';
+               $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 = '<div><span title-msg="title"><html:msg key="label"></span></div>';
+               $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 = '<div><span><html:msg key="foo-welcome"></span></div>';
+               $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 = '<div><span title-msg="title"><html:msg key="label"></span></div>';
+               $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 = '<select><option data-msg-text="option-one"></option><option data-msg-text="option-two"></option></select>';
+               $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 <a>link</a> here!!' );
+               html = '<div><div data-msg-html="html"></div></div>';
+               $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 = '<div><span title-msg="lorem"><html:msg key="ipsum"></span></div>';
-       $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 = '<div><span title-msg="title"><html:msg key="label"></span></div>';
-       $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 = '<div><span><html:msg key="foo-welcome"></span></div>';
-       $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 = '<div><span title-msg="title"><html:msg key="label"></span></div>';
-       $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 = '<select><option data-msg-text="option-one"></option><option data-msg-text="option-two"></option></select>';
-       $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 <a>link</a> here!!' );
-       html = '<div><div data-msg-html="html"></div></div>';
-       $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 ) ) ;
index 5b566ae..3ffcbf5 100644 (file)
@@ -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( '<!-- ([{+mW+}]) $^|?>' ),
-        '<!\\-\\- \\(\\[\\{\\+mW\\+\\}\\]\\) \\$\\^\\|\\?>', 'escapeRE - Escape specials' );
-       assert.equal( $.escapeRE( 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' ),
-        'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'escapeRE - Leave uppercase alone' );
-       assert.equal( $.escapeRE( 'abcdefghijklmnopqrstuvwxyz' ),
-        'abcdefghijklmnopqrstuvwxyz', 'escapeRE - Leave lowercase alone' );
-       assert.equal( $.escapeRE( '0123456789' ), '0123456789', 'escapeRE - Leave numbers alone' );
-});
-
-QUnit.test( 'Is functions', function ( assert ) {
-
-       assert.strictEqual( $.isDomElement( document.getElementById( 'qunit-header' ) ), true,
-        'isDomElement: #qunit-header Node' );
-       assert.strictEqual( $.isDomElement( document.getElementById( 'random-name' ) ), false,
-        'isDomElement: #random-name (null)' );
-       assert.strictEqual( $.isDomElement( document.getElementsByTagName( 'div' ) ), false,
-        'isDomElement: getElementsByTagName Array' );
-       assert.strictEqual( $.isDomElement( document.getElementsByTagName( 'div' )[0] ), true,
-        'isDomElement: getElementsByTagName(..)[0] Node' );
-       assert.strictEqual( $.isDomElement( $( 'div' ) ), false,
-        'isDomElement: jQuery object' );
-       assert.strictEqual( $.isDomElement( $( 'div' ).get(0) ), true,
-        'isDomElement: jQuery object > Get node' );
-       assert.strictEqual( $.isDomElement( document.createElement( 'div' ) ), true,
-        'isDomElement: createElement' );
-       assert.strictEqual( $.isDomElement( { foo: 1 } ), false,
-        'isDomElement: Object' );
-
-       assert.strictEqual( $.isEmpty( 'string' ), false, 'isEmptry: "string"' );
-       assert.strictEqual( $.isEmpty( '0' ), true, 'isEmptry: "0"' );
-       assert.strictEqual( $.isEmpty( '' ), true, 'isEmptry: ""' );
-       assert.strictEqual( $.isEmpty( 1 ), false, 'isEmptry: 1' );
-       assert.strictEqual( $.isEmpty( [] ), true, 'isEmptry: []' );
-       assert.strictEqual( $.isEmpty( {} ), true, 'isEmptry: {}' );
-
-       // Documented behaviour
-       assert.strictEqual( $.isEmpty( { length: 0 } ), true, 'isEmptry: { length: 0 }' );
-});
-
-QUnit.test( 'Comparison functions', function ( assert ) {
-
-       assert.ok( $.compareArray( [0, 'a', [], [2, 'b'] ], [0, "a", [], [2, "b"] ] ),
-        'compareArray: Two deep arrays that are excactly the same' );
-       assert.ok( !$.compareArray( [1], [2] ), 'compareArray: Two different arrays (false)' );
-
-       assert.ok( $.compareObject( {}, {} ), 'compareObject: Two empty objects' );
-       assert.ok( $.compareObject( { foo: 1 }, { foo: 1 } ), 'compareObject: Two the same objects' );
-       assert.ok( !$.compareObject( { bar: true }, { baz: false } ),
-        'compareObject: Two different objects (false)' );
-});
+( function ( $ ) {
+       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( '<!-- ([{+mW+}]) $^|?>' ),
+                '<!\\-\\- \\(\\[\\{\\+mW\\+\\}\\]\\) \\$\\^\\|\\?>', 'escapeRE - Escape specials' );
+               assert.equal( $.escapeRE( 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' ),
+                'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'escapeRE - Leave uppercase alone' );
+               assert.equal( $.escapeRE( 'abcdefghijklmnopqrstuvwxyz' ),
+                'abcdefghijklmnopqrstuvwxyz', 'escapeRE - Leave lowercase alone' );
+               assert.equal( $.escapeRE( '0123456789' ), '0123456789', 'escapeRE - Leave numbers alone' );
+       });
+
+       QUnit.test( 'Is functions', function ( assert ) {
+
+               assert.strictEqual( $.isDomElement( document.getElementById( 'qunit-header' ) ), true,
+                'isDomElement: #qunit-header Node' );
+               assert.strictEqual( $.isDomElement( document.getElementById( 'random-name' ) ), false,
+                'isDomElement: #random-name (null)' );
+               assert.strictEqual( $.isDomElement( document.getElementsByTagName( 'div' ) ), false,
+                'isDomElement: getElementsByTagName Array' );
+               assert.strictEqual( $.isDomElement( document.getElementsByTagName( 'div' )[0] ), true,
+                'isDomElement: getElementsByTagName(..)[0] Node' );
+               assert.strictEqual( $.isDomElement( $( 'div' ) ), false,
+                'isDomElement: jQuery object' );
+               assert.strictEqual( $.isDomElement( $( 'div' ).get(0) ), true,
+                'isDomElement: jQuery object > Get node' );
+               assert.strictEqual( $.isDomElement( document.createElement( 'div' ) ), true,
+                'isDomElement: createElement' );
+               assert.strictEqual( $.isDomElement( { foo: 1 } ), false,
+                'isDomElement: Object' );
+
+               assert.strictEqual( $.isEmpty( 'string' ), false, 'isEmptry: "string"' );
+               assert.strictEqual( $.isEmpty( '0' ), true, 'isEmptry: "0"' );
+               assert.strictEqual( $.isEmpty( '' ), true, 'isEmptry: ""' );
+               assert.strictEqual( $.isEmpty( 1 ), false, 'isEmptry: 1' );
+               assert.strictEqual( $.isEmpty( [] ), true, 'isEmptry: []' );
+               assert.strictEqual( $.isEmpty( {} ), true, 'isEmptry: {}' );
+
+               // Documented behaviour
+               assert.strictEqual( $.isEmpty( { length: 0 } ), true, 'isEmptry: { length: 0 }' );
+       });
+
+       QUnit.test( 'Comparison functions', function ( assert ) {
+
+               assert.ok( $.compareArray( [0, 'a', [], [2, 'b'] ], [0, 'a', [], [2, 'b'] ] ),
+                'compareArray: Two deep arrays that are excactly the same' );
+               assert.ok( !$.compareArray( [1], [2] ), 'compareArray: Two different arrays (false)' );
+
+               assert.ok( $.compareObject( {}, {} ), 'compareObject: Two empty objects' );
+               assert.ok( $.compareObject( { foo: 1 }, { foo: 1 } ), 'compareObject: Two the same objects' );
+               assert.ok( !$.compareObject( { bar: true }, { baz: false } ),
+                'compareObject: Two different objects (false)' );
+       });
+}( jQuery ) );
index 161f0cd..e31fc63 100644 (file)
@@ -1,33 +1,37 @@
-QUnit.module( 'jquery.tabIndex', QUnit.newMwEnvironment() );
+( function ( $ ) {
+       QUnit.module( 'jquery.tabIndex', QUnit.newMwEnvironment() );
 
-QUnit.test( 'firstTabIndex', 2, function ( assert ) {
-       var testEnvironment =
-'<form>' +
-       '<input tabindex="7" />' +
-       '<input tabindex="9" />' +
-       '<textarea tabindex="2">Foobar</textarea>' +
-       '<textarea tabindex="5">Foobar</textarea>' +
-'</form>';
+       QUnit.test( 'firstTabIndex', 2, function ( assert ) {
+               var html, $testA, $testB;
+               html =
+       '<form>' +
+               '<input tabindex="7" />' +
+               '<input tabindex="9" />' +
+               '<textarea tabindex="2">Foobar</textarea>' +
+               '<textarea tabindex="5">Foobar</textarea>' +
+       '</form>';
 
-       var $testA = $( '<div>' ).html( testEnvironment ).appendTo( '#qunit-fixture' );
-       assert.strictEqual( $testA.firstTabIndex(), 2, 'First tabindex should be 2 within this context.' );
+               $testA = $( '<div>' ).html( html ).appendTo( '#qunit-fixture' );
+               assert.strictEqual( $testA.firstTabIndex(), 2, 'First tabindex should be 2 within this context.' );
 
-       var $testB = $( '<div>' );
-       assert.strictEqual( $testB.firstTabIndex(), null, 'Return null if none available.' );
-});
+               $testB = $( '<div>' );
+               assert.strictEqual( $testB.firstTabIndex(), null, 'Return null if none available.' );
+       });
 
-QUnit.test( 'lastTabIndex', 2, function ( assert ) {
-       var testEnvironment =
-'<form>' +
-       '<input tabindex="7" />' +
-       '<input tabindex="9" />' +
-       '<textarea tabindex="2">Foobar</textarea>' +
-       '<textarea tabindex="5">Foobar</textarea>' +
-'</form>';
+       QUnit.test( 'lastTabIndex', 2, function ( assert ) {
+               var html, $testA, $testB;
+               html =
+       '<form>' +
+               '<input tabindex="7" />' +
+               '<input tabindex="9" />' +
+               '<textarea tabindex="2">Foobar</textarea>' +
+               '<textarea tabindex="5">Foobar</textarea>' +
+       '</form>';
 
-       var $testA = $( '<div>' ).html( testEnvironment ).appendTo( '#qunit-fixture' );
-       assert.strictEqual( $testA.lastTabIndex(), 9, 'Last tabindex should be 9 within this context.' );
+               $testA = $( '<div>' ).html( html ).appendTo( '#qunit-fixture' );
+               assert.strictEqual( $testA.lastTabIndex(), 9, 'Last tabindex should be 9 within this context.' );
 
-       var $testB = $( '<div>' );
-       assert.strictEqual( $testB.lastTabIndex(), null, 'Return null if none available.' );
-});
+               $testB = $( '<div>' );
+               assert.strictEqual( $testB.lastTabIndex(), null, 'Return null if none available.' );
+       });
+}( jQuery ) );
index 291c6b8..b04a213 100644 (file)
 ( function ( $, mw ) {
-
-var config = {
-       wgMonthNames: ['', 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
-       wgMonthNamesShort: ['', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
-       wgDefaultDateFormat: 'dmy',
-       wgContentLanguage: 'en'
-};
-
-QUnit.module( 'jquery.tablesorter', QUnit.newMwEnvironment({ config: config }) );
-
-/**
- * Create an HTML table from an array of row arrays containing text strings.
- * First row will be header row. No fancy rowspan/colspan stuff.
- *
- * @param {String[]} header
- * @param {String[][]} data
- * @return jQuery
- */
-function tableCreate(  header, data ) {
-       var i,
-               $table = $( '<table class="sortable"><thead></thead><tbody></tbody></table>' ),
-               $thead = $table.find( 'thead' ),
-               $tbody = $table.find( 'tbody' ),
-               $tr = $( '<tr>' );
-
-       $.each( header, function ( i, str ) {
-               var $th = $( '<th>' );
-               $th.text( str ).appendTo( $tr );
-       });
-       $tr.appendTo( $thead );
-
-       for ( i = 0; i < data.length; i++ ) {
-               $tr = $( '<tr>' );
-               $.each( data[i], function ( j, str ) {
-                       var $td = $( '<td>' );
-                       $td.text( str ).appendTo( $tr );
+       /*jshint onevar: false */
+
+       var config = {
+               wgMonthNames: ['', 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
+               wgMonthNamesShort: ['', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
+               wgDefaultDateFormat: 'dmy',
+               wgContentLanguage: 'en'
+       };
+
+       QUnit.module( 'jquery.tablesorter', QUnit.newMwEnvironment({ config: config }) );
+
+       /**
+        * Create an HTML table from an array of row arrays containing text strings.
+        * First row will be header row. No fancy rowspan/colspan stuff.
+        *
+        * @param {String[]} header
+        * @param {String[][]} data
+        * @return jQuery
+        */
+       function tableCreate(  header, data ) {
+               var i,
+                       $table = $( '<table class="sortable"><thead></thead><tbody></tbody></table>' ),
+                       $thead = $table.find( 'thead' ),
+                       $tbody = $table.find( 'tbody' ),
+                       $tr = $( '<tr>' );
+
+               $.each( header, function ( i, str ) {
+                       var $th = $( '<th>' );
+                       $th.text( str ).appendTo( $tr );
                });
-               $tr.appendTo( $tbody );
+               $tr.appendTo( $thead );
+
+               for ( i = 0; i < data.length; i++ ) {
+                       /*jshint loopfunc: true */
+                       $tr = $( '<tr>' );
+                       $.each( data[i], function ( j, str ) {
+                               var $td = $( '<td>' );
+                               $td.text( str ).appendTo( $tr );
+                       });
+                       $tr.appendTo( $tbody );
+               }
+               return $table;
        }
-       return $table;
-}
-
-/**
- * Extract text from table.
- *
- * @param {jQuery} $table
- * @return String[][]
- */
-function tableExtract( $table ) {
-       var data = [];
-
-       $table.find( 'tbody' ).find( 'tr' ).each( function( i, tr ) {
-               var row = [];
-               $( tr ).find( 'td,th' ).each( function( i, td ) {
-                       row.push( $( td ).text() );
+
+       /**
+        * Extract text from table.
+        *
+        * @param {jQuery} $table
+        * @return String[][]
+        */
+       function tableExtract( $table ) {
+               var data = [];
+
+               $table.find( 'tbody' ).find( 'tr' ).each( function( i, tr ) {
+                       var row = [];
+                       $( tr ).find( 'td,th' ).each( function( i, td ) {
+                               row.push( $( td ).text() );
+                       });
+                       data.push( row );
                });
-               data.push( row );
-       });
-       return data;
-}
-
-/**
- * Run a table test by building a table with the given data,
- * running some callback on it, then checking the results.
- *
- * @param {String} msg text to pass on to qunit for the comparison
- * @param {String[]} header cols to make the table
- * @param {String[][]} data rows/cols to make the table
- * @param {String[][]} expected rows/cols to compare against at end
- * @param {function($table)} callback something to do with the table before we compare
- */
-function tableTest( msg, header, data, expected, callback ) {
-       QUnit.test( msg, 1, function ( assert ) {
-               var $table = tableCreate( header, data );
-
-               // Give caller a chance to set up sorting and manipulate the table.
-               callback( $table );
-
-               // Table sorting is done synchronously; if it ever needs to change back
-               // to asynchronous, we'll need a timeout or a callback here.
-               var extracted = tableExtract( $table );
-               assert.deepEqual( extracted, expected, msg );
-       });
-}
-
-function reversed(arr) {
-       // Clone array
-       var arr2 = arr.slice(0);
-
-       arr2.reverse();
-
-       return arr2;
-}
-
-// Sample data set using planets named and their radius
-var header  = [ 'Planet' , 'Radius (km)'],
-       mercury = [ 'Mercury', '2439.7' ],
-       venus   = [ 'Venus'  , '6051.8' ],
-       earth   = [ 'Earth'  , '6371.0' ],
-       mars    = [ 'Mars'   , '3390.0' ],
-       jupiter = [ 'Jupiter',  '69911' ],
-       saturn  = [ 'Saturn' ,  '58232' ];
-
-// Initial data set
-var planets         = [mercury, venus, earth, mars, jupiter, saturn];
-var ascendingName   = [earth, jupiter, mars, mercury, saturn, venus];
-var ascendingRadius = [mercury, mars, venus, earth, saturn, jupiter];
-
-tableTest(
-       'Basic planet table: sorting initially - ascending by name',
-       header,
-       planets,
-       ascendingName,
-       function ( $table ) {
-               $table.tablesorter( { sortList: [ { 0: 'asc' } ] } );
-       }
-);
-tableTest(
-       'Basic planet table: sorting initially - descending by radius',
-       header,
-       planets,
-       reversed(ascendingRadius),
-       function ( $table ) {
-               $table.tablesorter( { sortList: [ { 1: 'desc' } ] } );
-       }
-);
-tableTest(
-       'Basic planet table: ascending by name',
-       header,
-       planets,
-       ascendingName,
-       function ( $table ) {
-               $table.tablesorter();
-               $table.find( '.headerSort:eq(0)' ).click();
-       }
-);
-tableTest(
-       'Basic planet table: ascending by name a second time',
-       header,
-       planets,
-       ascendingName,
-       function ( $table ) {
-               $table.tablesorter();
-               $table.find( '.headerSort:eq(0)' ).click();
-       }
-);
-tableTest(
-       'Basic planet table: descending by name',
-       header,
-       planets,
-       reversed(ascendingName),
-       function ( $table ) {
-               $table.tablesorter();
-               $table.find( '.headerSort:eq(0)' ).click().click();
+               return data;
        }
-);
-tableTest(
-       'Basic planet table: ascending radius',
-       header,
-       planets,
-       ascendingRadius,
-       function ( $table ) {
-               $table.tablesorter();
-               $table.find( '.headerSort:eq(1)' ).click();
+
+       /**
+        * Run a table test by building a table with the given data,
+        * running some callback on it, then checking the results.
+        *
+        * @param {String} msg text to pass on to qunit for the comparison
+        * @param {String[]} header cols to make the table
+        * @param {String[][]} data rows/cols to make the table
+        * @param {String[][]} expected rows/cols to compare against at end
+        * @param {function($table)} callback something to do with the table before we compare
+        */
+       function tableTest( msg, header, data, expected, callback ) {
+               QUnit.test( msg, 1, function ( assert ) {
+                       var $table = tableCreate( header, data );
+
+                       // Give caller a chance to set up sorting and manipulate the table.
+                       callback( $table );
+
+                       // Table sorting is done synchronously; if it ever needs to change back
+                       // to asynchronous, we'll need a timeout or a callback here.
+                       var extracted = tableExtract( $table );
+                       assert.deepEqual( extracted, expected, msg );
+               });
        }
-);
-tableTest(
-       'Basic planet table: descending radius',
-       header,
-       planets,
-       reversed(ascendingRadius),
-       function ( $table ) {
-               $table.tablesorter();
-               $table.find( '.headerSort:eq(1)' ).click().click();
+
+       function reversed(arr) {
+               // Clone array
+               var arr2 = arr.slice(0);
+
+               arr2.reverse();
+
+               return arr2;
        }
-);
-
-// Sample data set to test multiple column sorting
-var header  = [ 'column1' , 'column2'],
-       a1 = [ 'A', '1' ],
-       a2 = [ 'A', '2' ],
-       a3 = [ 'A', '3' ],
-       b1 = [ 'B', '1' ],
-       b2 = [ 'B', '2' ],
-       b3 = [ 'B', '3' ];
-var initial = [a2, b3, a1, a3, b2, b1];
-var asc = [a1, a2, a3, b1, b2, b3];
-var descasc = [b1, b2, b3, a1, a2, a3];
-
-tableTest(
-       'Sorting multiple columns by passing sort list',
-       header,
-       initial,
-       asc,
-       function ( $table ) {
+
+       // Sample data set using planets named and their radius
+       var header  = [ 'Planet' , 'Radius (km)'],
+               mercury = [ 'Mercury', '2439.7' ],
+               venus   = [ 'Venus'  , '6051.8' ],
+               earth   = [ 'Earth'  , '6371.0' ],
+               mars    = [ 'Mars'   , '3390.0' ],
+               jupiter = [ 'Jupiter',  '69911' ],
+               saturn  = [ 'Saturn' ,  '58232' ];
+
+       // Initial data set
+       var planets         = [mercury, venus, earth, mars, jupiter, saturn];
+       var ascendingName   = [earth, jupiter, mars, mercury, saturn, venus];
+       var ascendingRadius = [mercury, mars, venus, earth, saturn, jupiter];
+
+       tableTest(
+               'Basic planet table: sorting initially - ascending by name',
+               header,
+               planets,
+               ascendingName,
+               function ( $table ) {
+                       $table.tablesorter( { sortList: [ { 0: 'asc' } ] } );
+               }
+       );
+       tableTest(
+               'Basic planet table: sorting initially - descending by radius',
+               header,
+               planets,
+               reversed(ascendingRadius),
+               function ( $table ) {
+                       $table.tablesorter( { sortList: [ { 1: 'desc' } ] } );
+               }
+       );
+       tableTest(
+               'Basic planet table: ascending by name',
+               header,
+               planets,
+               ascendingName,
+               function ( $table ) {
+                       $table.tablesorter();
+                       $table.find( '.headerSort:eq(0)' ).click();
+               }
+       );
+       tableTest(
+               'Basic planet table: ascending by name a second time',
+               header,
+               planets,
+               ascendingName,
+               function ( $table ) {
+                       $table.tablesorter();
+                       $table.find( '.headerSort:eq(0)' ).click();
+               }
+       );
+       tableTest(
+               'Basic planet table: descending by name',
+               header,
+               planets,
+               reversed(ascendingName),
+               function ( $table ) {
+                       $table.tablesorter();
+                       $table.find( '.headerSort:eq(0)' ).click().click();
+               }
+       );
+       tableTest(
+               'Basic planet table: ascending radius',
+               header,
+               planets,
+               ascendingRadius,
+               function ( $table ) {
+                       $table.tablesorter();
+                       $table.find( '.headerSort:eq(1)' ).click();
+               }
+       );
+       tableTest(
+               'Basic planet table: descending radius',
+               header,
+               planets,
+               reversed(ascendingRadius),
+               function ( $table ) {
+                       $table.tablesorter();
+                       $table.find( '.headerSort:eq(1)' ).click().click();
+               }
+       );
+
+       // Sample data set to test multiple column sorting
+       header = [ 'column1' , 'column2'];
+       var
+               a1 = [ 'A', '1' ],
+               a2 = [ 'A', '2' ],
+               a3 = [ 'A', '3' ],
+               b1 = [ 'B', '1' ],
+               b2 = [ 'B', '2' ],
+               b3 = [ 'B', '3' ];
+       var initial = [a2, b3, a1, a3, b2, b1];
+       var asc = [a1, a2, a3, b1, b2, b3];
+       var descasc = [b1, b2, b3, a1, a2, a3];
+
+       tableTest(
+               'Sorting multiple columns by passing sort list',
+               header,
+               initial,
+               asc,
+               function ( $table ) {
+                       $table.tablesorter(
+                               { sortList: [ { 0: 'asc' }, { 1: 'asc' } ] }
+                       );
+               }
+       );
+       tableTest(
+               'Sorting multiple columns by programmatically triggering sort()',
+               header,
+               initial,
+               descasc,
+               function ( $table ) {
+                       $table.tablesorter();
+                       $table.data( 'tablesorter' ).sort(
+                               [ { 0: 'desc' }, { 1: 'asc' } ]
+                       );
+               }
+       );
+       tableTest(
+               'Reset to initial sorting by triggering sort() without any parameters',
+               header,
+               initial,
+               asc,
+               function ( $table ) {
+                       $table.tablesorter(
+                               { sortList: [ { 0: 'asc' }, { 1: 'asc' } ] }
+                       );
+                       $table.data( 'tablesorter' ).sort(
+                               [ { 0: 'desc' }, { 1: 'asc' } ]
+                       );
+                       $table.data( 'tablesorter' ).sort();
+               }
+       );
+       QUnit.test( 'Reset sorting making table appear unsorted', 3, function ( assert ) {
+               var $table = tableCreate( header, initial );
                $table.tablesorter(
-                       { sortList: [ { 0: 'asc' }, { 1: 'asc' } ] }
+                       { sortList: [ { 0: 'desc' }, { 1: 'asc' } ] }
                );
-       }
-);
-tableTest(
-       'Sorting multiple columns by programmatically triggering sort()',
-       header,
-       initial,
-       descasc,
-       function ( $table ) {
-               $table.tablesorter();
-               $table.data( 'tablesorter' ).sort(
-                       [ { 0: 'desc' }, { 1: 'asc' } ]
+               $table.data( 'tablesorter' ).sort( [] );
+
+               assert.equal(
+                       $table.find( 'th.headerSortUp' ).length + $table.find( 'th.headerSortDown' ).length,
+                       0,
+                       'No sort specific sort classes addign to header cells'
                );
-       }
-);
-tableTest(
-       'Reset to initial sorting by triggering sort() without any parameters',
-       header,
-       initial,
-       asc,
-       function ( $table ) {
-               $table.tablesorter(
-                       { sortList: [ { 0: 'asc' }, { 1: 'asc' } ] }
+
+               assert.equal(
+                       $table.find( 'th' ).first().attr( 'title' ),
+                       mw.msg( 'sort-ascending' ),
+                       'First header cell has default title'
                );
-               $table.data( 'tablesorter' ).sort(
-                       [ { 0: 'desc' }, { 1: 'asc' } ]
+
+               assert.equal(
+                       $table.find( 'th' ).first().attr( 'title' ),
+                       $table.find( 'th' ).last().attr( 'title' ),
+                       'Both header cells\' titles match'
                );
-               $table.data( 'tablesorter' ).sort();
-       }
-);
-QUnit.test( 'Reset sorting making table appear unsorted', 3, function ( assert ) {
-       var $table = tableCreate( header, initial );
-       $table.tablesorter(
-               { sortList: [ { 0: 'desc' }, { 1: 'asc' } ] }
+       } );
+
+       // Regression tests!
+       tableTest(
+               'Bug 28775: German-style (dmy) short numeric dates',
+               ['Date'],
+               [ // German-style dates are day-month-year
+                       ['11.11.2011'],
+                       ['01.11.2011'],
+                       ['02.10.2011'],
+                       ['03.08.2011'],
+                       ['09.11.2011']
+               ],
+               [ // Sorted by ascending date
+                       ['03.08.2011'],
+                       ['02.10.2011'],
+                       ['01.11.2011'],
+                       ['09.11.2011'],
+                       ['11.11.2011']
+               ],
+               function ( $table ) {
+                       mw.config.set( 'wgDefaultDateFormat', 'dmy' );
+                       mw.config.set( 'wgContentLanguage', 'de' );
+
+                       $table.tablesorter();
+                       $table.find( '.headerSort:eq(0)' ).click();
+               }
        );
-       $table.data( 'tablesorter' ).sort( [] );
 
-       assert.equal(
-               $table.find( 'th.headerSortUp' ).length + $table.find( 'th.headerSortDown' ).length,
-               0,
-               'No sort specific sort classes addign to header cells'
+       tableTest(
+               'Bug 28775: American-style (mdy) short numeric dates',
+               ['Date'],
+               [ // American-style dates are month-day-year
+                       ['11.11.2011'],
+                       ['01.11.2011'],
+                       ['02.10.2011'],
+                       ['03.08.2011'],
+                       ['09.11.2011']
+               ],
+               [ // Sorted by ascending date
+                       ['01.11.2011'],
+                       ['02.10.2011'],
+                       ['03.08.2011'],
+                       ['09.11.2011'],
+                       ['11.11.2011']
+               ],
+               function ( $table ) {
+                       mw.config.set( 'wgDefaultDateFormat', 'mdy' );
+
+                       $table.tablesorter();
+                       $table.find( '.headerSort:eq(0)' ).click();
+               }
        );
 
-       assert.equal(
-               $table.find( 'th' ).first().attr( 'title' ),
-               mw.msg( 'sort-ascending' ),
-               'First header cell has default title'
+       var ipv4 = [
+               // Some randomly generated fake IPs
+               ['45.238.27.109'],
+               ['44.172.9.22'],
+               ['247.240.82.209'],
+               ['204.204.132.158'],
+               ['170.38.91.162'],
+               ['197.219.164.9'],
+               ['45.68.154.72'],
+               ['182.195.149.80']
+       ];
+       var ipv4Sorted = [
+               // Sort order should go octet by octet
+               ['44.172.9.22'],
+               ['45.68.154.72'],
+               ['45.238.27.109'],
+               ['170.38.91.162'],
+               ['182.195.149.80'],
+               ['197.219.164.9'],
+               ['204.204.132.158'],
+               ['247.240.82.209']
+       ];
+
+       tableTest(
+               'Bug 17141: IPv4 address sorting',
+               ['IP'],
+               ipv4,
+               ipv4Sorted,
+               function ( $table ) {
+                       $table.tablesorter();
+                       $table.find( '.headerSort:eq(0)' ).click();
+               }
        );
-
-       assert.equal(
-               $table.find( 'th' ).first().attr( 'title' ),
-               $table.find( 'th' ).last().attr( 'title' ),
-               'Both header cells\' titles match'
+       tableTest(
+               'Bug 17141: IPv4 address sorting (reverse)',
+               ['IP'],
+               ipv4,
+               reversed(ipv4Sorted),
+               function ( $table ) {
+                       $table.tablesorter();
+                       $table.find( '.headerSort:eq(0)' ).click().click();
+               }
        );
-} );
-
-// Regression tests!
-tableTest(
-       'Bug 28775: German-style (dmy) short numeric dates',
-       ['Date'],
-       [ // German-style dates are day-month-year
-               ['11.11.2011'],
-               ['01.11.2011'],
-               ['02.10.2011'],
-               ['03.08.2011'],
-               ['09.11.2011']
-       ],
-       [ // Sorted by ascending date
-               ['03.08.2011'],
-               ['02.10.2011'],
-               ['01.11.2011'],
-               ['09.11.2011'],
-               ['11.11.2011']
-       ],
-       function ( $table ) {
-               mw.config.set( 'wgDefaultDateFormat', 'dmy' );
-               mw.config.set( 'wgContentLanguage', 'de' );
 
-               $table.tablesorter();
-               $table.find( '.headerSort:eq(0)' ).click();
-       }
-);
-
-tableTest(
-       'Bug 28775: American-style (mdy) short numeric dates',
-       ['Date'],
-       [ // American-style dates are month-day-year
-               ['11.11.2011'],
-               ['01.11.2011'],
-               ['02.10.2011'],
-               ['03.08.2011'],
-               ['09.11.2011']
-       ],
-       [ // Sorted by ascending date
-               ['01.11.2011'],
-               ['02.10.2011'],
-               ['03.08.2011'],
-               ['09.11.2011'],
-               ['11.11.2011']
-       ],
-       function ( $table ) {
-               mw.config.set( 'wgDefaultDateFormat', 'mdy' );
-
-               $table.tablesorter();
-               $table.find( '.headerSort:eq(0)' ).click();
-       }
-);
-
-var ipv4 = [
-       // Some randomly generated fake IPs
-       ['45.238.27.109'],
-       ['44.172.9.22'],
-       ['247.240.82.209'],
-       ['204.204.132.158'],
-       ['170.38.91.162'],
-       ['197.219.164.9'],
-       ['45.68.154.72'],
-       ['182.195.149.80']
-];
-var ipv4Sorted = [
-       // Sort order should go octet by octet
-       ['44.172.9.22'],
-       ['45.68.154.72'],
-       ['45.238.27.109'],
-       ['170.38.91.162'],
-       ['182.195.149.80'],
-       ['197.219.164.9'],
-       ['204.204.132.158'],
-       ['247.240.82.209']
-];
-
-tableTest(
-       'Bug 17141: IPv4 address sorting',
-       ['IP'],
-       ipv4,
-       ipv4Sorted,
-       function ( $table ) {
-               $table.tablesorter();
-               $table.find( '.headerSort:eq(0)' ).click();
-       }
-);
-tableTest(
-       'Bug 17141: IPv4 address sorting (reverse)',
-       ['IP'],
-       ipv4,
-       reversed(ipv4Sorted),
-       function ( $table ) {
-               $table.tablesorter();
-               $table.find( '.headerSort:eq(0)' ).click().click();
-       }
-);
-
-var umlautWords = [
-       // Some words with Umlauts
-       ['Günther'],
-       ['Peter'],
-       ['Björn'],
-       ['Bjorn'],
-       ['Apfel'],
-       ['Äpfel'],
-       ['Strasse'],
-       ['Sträßschen']
-];
-
-var umlautWordsSorted = [
-       // Some words with Umlauts
-       ['Äpfel'],
-       ['Apfel'],
-       ['Björn'],
-       ['Bjorn'],
-       ['Günther'],
-       ['Peter'],
-       ['Sträßschen'],
-       ['Strasse']
-];
-
-tableTest(
-       'Accented Characters with custom collation',
-       ['Name'],
-       umlautWords,
-       umlautWordsSorted,
-       function ( $table ) {
-               mw.config.set( 'tableSorterCollation', {
-                       'ä': 'ae',
-                       'ö': 'oe',
-                       'ß': 'ss',
-                       'ü':'ue'
-               } );
-
-               $table.tablesorter();
-               $table.find( '.headerSort:eq(0)' ).click();
-       }
-);
-
-var planetsRowspan = [["Earth","6051.8"], jupiter, ["Mars","6051.8"], mercury, saturn, venus];
-var planetsRowspanII = [jupiter, mercury, saturn, venus, ['Venus', '6371.0'], ['Venus', '3390.0']];
-
-tableTest(
-       'Basic planet table: same value for multiple rows via rowspan',
-       header,
-       planets,
-       planetsRowspan,
-       function ( $table ) {
-               // Modify the table to have a multiple-row-spanning cell:
-               // - Remove 2nd cell of 4th row, and, 2nd cell or 5th row.
-               $table.find( 'tr:eq(3) td:eq(1), tr:eq(4) td:eq(1)' ).remove();
-               // - Set rowspan for 2nd cell of 3rd row to 3.
-               //   This covers the removed cell in the 4th and 5th row.
-               $table.find( 'tr:eq(2) td:eq(1)' ).prop( 'rowspan', '3' );
-
-               $table.tablesorter();
-               $table.find( '.headerSort:eq(0)' ).click();
-       }
-);
-tableTest(
-       'Basic planet table: same value for multiple rows via rowspan (sorting initially)',
-       header,
-       planets,
-       planetsRowspan,
-       function ( $table ) {
-               // Modify the table to have a multiple-row-spanning cell:
-               // - Remove 2nd cell of 4th row, and, 2nd cell or 5th row.
-               $table.find( 'tr:eq(3) td:eq(1), tr:eq(4) td:eq(1)' ).remove();
-               // - Set rowspan for 2nd cell of 3rd row to 3.
-               //   This covers the removed cell in the 4th and 5th row.
-               $table.find( 'tr:eq(2) td:eq(1)' ).prop( 'rowspan', '3' );
-
-               $table.tablesorter( { sortList: [ { 0: 'asc' } ] } );
-       }
-);
-tableTest(
-       'Basic planet table: Same value for multiple rows via rowspan II',
-       header,
-       planets,
-       planetsRowspanII,
-       function ( $table ) {
-               // Modify the table to have a multiple-row-spanning cell:
-               // - Remove 1st cell of 4th row, and, 1st cell or 5th row.
-               $table.find( 'tr:eq(3) td:eq(0), tr:eq(4) td:eq(0)' ).remove();
-               // - Set rowspan for 1st cell of 3rd row to 3.
-               //   This covers the removed cell in the 4th and 5th row.
-               $table.find( 'tr:eq(2) td:eq(0)' ).prop( 'rowspan', '3' );
-
-               $table.tablesorter();
-               $table.find( '.headerSort:eq(0)' ).click();
-       }
-);
-
-var complexMDYDates = [
-       // Some words with Umlauts
-       ['January, 19 2010'],
-       ['April 21 1991'],
-       ['04 22 1991'],
-       ['5.12.1990'],
-       ['December 12 \'10']
-];
-
-var complexMDYSorted = [
-       ['5.12.1990'],
-       ['April 21 1991'],
-       ['04 22 1991'],
-       ['January, 19 2010'],
-       ['December 12 \'10']
-];
-
-tableTest(
-       'Complex date parsing I',
-       ['date'],
-       complexMDYDates,
-       complexMDYSorted,
-       function ( $table ) {
-               mw.config.set( 'wgDefaultDateFormat', 'mdy' );
+       var umlautWords = [
+               // Some words with Umlauts
+               ['Günther'],
+               ['Peter'],
+               ['Björn'],
+               ['Bjorn'],
+               ['Apfel'],
+               ['Äpfel'],
+               ['Strasse'],
+               ['Sträßschen']
+       ];
+
+       var umlautWordsSorted = [
+               // Some words with Umlauts
+               ['Äpfel'],
+               ['Apfel'],
+               ['Björn'],
+               ['Bjorn'],
+               ['Günther'],
+               ['Peter'],
+               ['Sträßschen'],
+               ['Strasse']
+       ];
+
+       tableTest(
+               'Accented Characters with custom collation',
+               ['Name'],
+               umlautWords,
+               umlautWordsSorted,
+               function ( $table ) {
+                       mw.config.set( 'tableSorterCollation', {
+                               'ä': 'ae',
+                               'ö': 'oe',
+                               'ß': 'ss',
+                               'ü':'ue'
+                       } );
 
-               $table.tablesorter();
-               $table.find( '.headerSort:eq(0)' ).click();
-       }
-);
-
-var currencyUnsorted = [
-       ['1.02 $'],
-       ['$ 3.00'],
-       ['€ 2,99'],
-       ['$ 1.00'],
-       ['$3.50'],
-       ['$ 1.50'],
-       ['€ 0.99']
-];
-
-var currencySorted = [
-       ['€ 0.99'],
-       ['$ 1.00'],
-       ['1.02 $'],
-       ['$ 1.50'],
-       ['$ 3.00'],
-       ['$3.50'],
-       // Comma's sort after dots
-       // Not intentional but test to detect changes
-       ['€ 2,99']
-];
-
-tableTest(
-       'Currency parsing I',
-       ['currency'],
-       currencyUnsorted,
-       currencySorted,
-       function ( $table ) {
-               $table.tablesorter();
-               $table.find( '.headerSort:eq(0)' ).click();
-       }
-);
-
-var ascendingNameLegacy = ascendingName.slice(0);
-ascendingNameLegacy[4] = ascendingNameLegacy[5];
-ascendingNameLegacy.pop();
-
-tableTest(
-       'Legacy compat with .sortbottom',
-       header,
-       planets,
-       ascendingNameLegacy,
-       function( $table ) {
-               $table.find( 'tr:last' ).addClass( 'sortbottom' );
-               $table.tablesorter();
-               $table.find( '.headerSort:eq(0)' ).click();
-       }
-);
-
-QUnit.test( 'Test detection routine', function ( assert ) {
-       var $table;
-       $table = $(
-               '<table class="sortable">' +
-               '<caption>CAPTION</caption>' +
-               '<tr><th>THEAD</th></tr>' +
-               '<tr><td>1</td></tr>' +
-               '<tr class="sortbottom"><td>text</td></tr>' +
-               '</table>'
+                       $table.tablesorter();
+                       $table.find( '.headerSort:eq(0)' ).click();
+               }
        );
-       $table.tablesorter();
 
-       assert.equal(
-               $table.data( 'tablesorter' ).config.parsers[0].id,
-               'number',
-               'Correctly detected column content skipping sortbottom'
+       var planetsRowspan = [ [ 'Earth', '6051.8' ], jupiter, [ 'Mars', '6051.8' ], mercury, saturn, venus ];
+       var planetsRowspanII = [ jupiter, mercury, saturn, venus, [ 'Venus', '6371.0' ], [ 'Venus', '3390.0' ] ];
+
+       tableTest(
+               'Basic planet table: same value for multiple rows via rowspan',
+               header,
+               planets,
+               planetsRowspan,
+               function ( $table ) {
+                       // Modify the table to have a multiple-row-spanning cell:
+                       // - Remove 2nd cell of 4th row, and, 2nd cell or 5th row.
+                       $table.find( 'tr:eq(3) td:eq(1), tr:eq(4) td:eq(1)' ).remove();
+                       // - Set rowspan for 2nd cell of 3rd row to 3.
+                       //   This covers the removed cell in the 4th and 5th row.
+                       $table.find( 'tr:eq(2) td:eq(1)' ).prop( 'rowspan', '3' );
+
+                       $table.tablesorter();
+                       $table.find( '.headerSort:eq(0)' ).click();
+               }
        );
-} );
-
-/** FIXME: the diff output is not very readeable. */
-QUnit.test( 'bug 32047 - caption must be before thead', function ( assert ) {
-       var $table;
-       $table = $(
-               '<table class="sortable">' +
-               '<caption>CAPTION</caption>' +
-               '<tr><th>THEAD</th></tr>' +
-               '<tr><td>A</td></tr>' +
-               '<tr><td>B</td></tr>' +
-               '<tr class="sortbottom"><td>TFOOT</td></tr>' +
-               '</table>'
-               );
-       $table.tablesorter();
-
-       assert.equal(
-               $table.children( ).get( 0 ).nodeName,
-               'CAPTION',
-               'First element after <thead> must be <caption> (bug 32047)'
+       tableTest(
+               'Basic planet table: same value for multiple rows via rowspan (sorting initially)',
+               header,
+               planets,
+               planetsRowspan,
+               function ( $table ) {
+                       // Modify the table to have a multiple-row-spanning cell:
+                       // - Remove 2nd cell of 4th row, and, 2nd cell or 5th row.
+                       $table.find( 'tr:eq(3) td:eq(1), tr:eq(4) td:eq(1)' ).remove();
+                       // - Set rowspan for 2nd cell of 3rd row to 3.
+                       //   This covers the removed cell in the 4th and 5th row.
+                       $table.find( 'tr:eq(2) td:eq(1)' ).prop( 'rowspan', '3' );
+
+                       $table.tablesorter( { sortList: [ { 0: 'asc' } ] } );
+               }
        );
-});
-
-QUnit.test( 'data-sort-value attribute, when available, should override sorting position', function ( assert ) {
-       var $table, data;
-
-       // Example 1: All cells except one cell without data-sort-value,
-       // which should be sorted at it's text content value.
-       $table = $(
-               '<table class="sortable"><thead><tr><th>Data</th></tr></thead>' +
-                       '<tbody>' +
-                       '<tr><td>Cheetah</td></tr>' +
-                       '<tr><td data-sort-value="Apple">Bird</td></tr>' +
-                       '<tr><td data-sort-value="Bananna">Ferret</td></tr>' +
-                       '<tr><td data-sort-value="Drupe">Elephant</td></tr>' +
-                       '<tr><td data-sort-value="Cherry">Dolphin</td></tr>' +
-               '</tbody></table>'
+       tableTest(
+               'Basic planet table: Same value for multiple rows via rowspan II',
+               header,
+               planets,
+               planetsRowspanII,
+               function ( $table ) {
+                       // Modify the table to have a multiple-row-spanning cell:
+                       // - Remove 1st cell of 4th row, and, 1st cell or 5th row.
+                       $table.find( 'tr:eq(3) td:eq(0), tr:eq(4) td:eq(0)' ).remove();
+                       // - Set rowspan for 1st cell of 3rd row to 3.
+                       //   This covers the removed cell in the 4th and 5th row.
+                       $table.find( 'tr:eq(2) td:eq(0)' ).prop( 'rowspan', '3' );
+
+                       $table.tablesorter();
+                       $table.find( '.headerSort:eq(0)' ).click();
+               }
        );
-       $table.tablesorter().find( '.headerSort:eq(0)' ).click();
-
-       data = [];
-       $table.find( 'tbody > tr' ).each( function( i, tr ) {
-               $( tr ).find( 'td' ).each( function( i, td ) {
-                       data.push( {
-                               data: $( td ).data( 'sortValue' ),
-                               text: $( td ).text()
-                       } );
-               });
-       });
 
-       assert.deepEqual( data, [
-               {
-                       data: 'Apple',
-                       text: 'Bird'
-               }, {
-                       data: 'Bananna',
-                       text: 'Ferret'
-               }, {
-                       data: undefined,
-                       text: 'Cheetah'
-               }, {
-                       data: 'Cherry',
-                       text: 'Dolphin'
-               }, {
-                       data: 'Drupe',
-                       text: 'Elephant'
+       var complexMDYDates = [
+               // Some words with Umlauts
+               ['January, 19 2010'],
+               ['April 21 1991'],
+               ['04 22 1991'],
+               ['5.12.1990'],
+               ['December 12 \'10']
+       ];
+
+       var complexMDYSorted = [
+               ['5.12.1990'],
+               ['April 21 1991'],
+               ['04 22 1991'],
+               ['January, 19 2010'],
+               ['December 12 \'10']
+       ];
+
+       tableTest(
+               'Complex date parsing I',
+               ['date'],
+               complexMDYDates,
+               complexMDYSorted,
+               function ( $table ) {
+                       mw.config.set( 'wgDefaultDateFormat', 'mdy' );
+
+                       $table.tablesorter();
+                       $table.find( '.headerSort:eq(0)' ).click();
                }
-       ], 'Order matches expected order (based on data-sort-value attribute values)' );
-
-       // Example 2
-       $table = $(
-               '<table class="sortable"><thead><tr><th>Data</th></tr></thead>' +
-                       '<tbody>' +
-                       '<tr><td>D</td></tr>' +
-                       '<tr><td data-sort-value="E">A</td></tr>' +
-                       '<tr><td>B</td></tr>' +
-                       '<tr><td>G</td></tr>' +
-                       '<tr><td data-sort-value="F">C</td></tr>' +
-               '</tbody></table>'
        );
-       $table.tablesorter().find( '.headerSort:eq(0)' ).click();
-
-       data = [];
-       $table.find( 'tbody > tr' ).each( function ( i, tr ) {
-               $( tr ).find( 'td' ).each( function ( i, td ) {
-                       data.push( {
-                               data: $( td ).data( 'sortValue' ),
-                               text: $( td ).text()
-                       } );
-               });
-       });
 
-       assert.deepEqual( data, [
-               {
-                       data: undefined,
-                       text: 'B'
-               }, {
-                       data: undefined,
-                       text: 'D'
-               }, {
-                       data: 'E',
-                       text: 'A'
-               }, {
-                       data: 'F',
-                       text: 'C'
-               }, {
-                       data: undefined,
-                       text: 'G'
+       var currencyUnsorted = [
+               ['1.02 $'],
+               ['$ 3.00'],
+               ['€ 2,99'],
+               ['$ 1.00'],
+               ['$3.50'],
+               ['$ 1.50'],
+               ['€ 0.99']
+       ];
+
+       var currencySorted = [
+               ['€ 0.99'],
+               ['$ 1.00'],
+               ['1.02 $'],
+               ['$ 1.50'],
+               ['$ 3.00'],
+               ['$3.50'],
+               // Comma's sort after dots
+               // Not intentional but test to detect changes
+               ['€ 2,99']
+       ];
+
+       tableTest(
+               'Currency parsing I',
+               ['currency'],
+               currencyUnsorted,
+               currencySorted,
+               function ( $table ) {
+                       $table.tablesorter();
+                       $table.find( '.headerSort:eq(0)' ).click();
                }
-       ], 'Order matches expected order (based on data-sort-value attribute values)' );
-
-       // Example 3: Test that live changes are used from data-sort-value,
-       // even if they change after the tablesorter is constructed (bug 38152).
-       $table = $(
-               '<table class="sortable"><thead><tr><th>Data</th></tr></thead>' +
-                       '<tbody>' +
-                       '<tr><td>D</td></tr>' +
-                       '<tr><td data-sort-value="1">A</td></tr>' +
-                       '<tr><td>B</td></tr>' +
-                       '<tr><td data-sort-value="2">G</td></tr>' +
-                       '<tr><td>C</td></tr>' +
-               '</tbody></table>'
        );
-       // initialize table sorter and sort once
-       $table
-               .tablesorter()
-               .find( '.headerSort:eq(0)' ).click();
-
-       // Change the sortValue data properties (bug 38152)
-       // - change data
-       $table.find( 'td:contains(A)' ).data( 'sortValue', 3 );
-       // - add data
-       $table.find( 'td:contains(B)' ).data( 'sortValue', 1 );
-       // - remove data, bring back attribute: 2
-       $table.find( 'td:contains(G)' ).removeData( 'sortValue' );
-
-       // Now sort again (twice, so it is back at Ascending)
-       $table.find( '.headerSort:eq(0)' ).click();
-       $table.find( '.headerSort:eq(0)' ).click();
-
-       data = [];
-       $table.find( 'tbody > tr' ).each( function( i, tr ) {
-               $( tr ).find( 'td' ).each( function( i, td ) {
-                       data.push( {
-                               data: $( td ).data( 'sortValue' ),
-                               text: $( td ).text()
-                       } );
-               });
-       });
 
-       assert.deepEqual( data, [
-               {
-                       data: 1,
-                       text: "B"
-               }, {
-                       data: 2,
-                       text: "G"
-               }, {
-                       data: 3,
-                       text: "A"
-               }, {
-                       data: undefined,
-                       text: "C"
-               }, {
-                       data: undefined,
-                       text: "D"
+       var ascendingNameLegacy = ascendingName.slice(0);
+       ascendingNameLegacy[4] = ascendingNameLegacy[5];
+       ascendingNameLegacy.pop();
+
+       tableTest(
+               'Legacy compat with .sortbottom',
+               header,
+               planets,
+               ascendingNameLegacy,
+               function( $table ) {
+                       $table.find( 'tr:last' ).addClass( 'sortbottom' );
+                       $table.tablesorter();
+                       $table.find( '.headerSort:eq(0)' ).click();
                }
-       ], 'Order matches expected order, using the current sortValue in $.data()' );
-
-});
-
-var numbers = [
-       [ '12'    ],
-       [  '7'    ],
-       [ '13,000'],
-       [  '9'    ],
-       [ '14'    ],
-       [  '8.0'  ]
-];
-var numbersAsc = [
-       [  '7'    ],
-       [  '8.0'  ],
-       [  '9'    ],
-       [ '12'    ],
-       [ '14'    ],
-       [ '13,000']
-];
-
-tableTest( 'bug 8115: sort numbers with commas (ascending)',
-       ['Numbers'], numbers, numbersAsc,
-       function( $table ) {
+       );
+
+       QUnit.test( 'Test detection routine', function ( assert ) {
+               var $table;
+               $table = $(
+                       '<table class="sortable">' +
+                       '<caption>CAPTION</caption>' +
+                       '<tr><th>THEAD</th></tr>' +
+                       '<tr><td>1</td></tr>' +
+                       '<tr class="sortbottom"><td>text</td></tr>' +
+                       '</table>'
+               );
                $table.tablesorter();
-               $table.find( '.headerSort:eq(0)' ).click();
-       }
-);
 
-tableTest( 'bug 8115: sort numbers with commas (descending)',
-       ['Numbers'], numbers, reversed(numbersAsc),
-       function( $table ) {
+               assert.equal(
+                       $table.data( 'tablesorter' ).config.parsers[0].id,
+                       'number',
+                       'Correctly detected column content skipping sortbottom'
+               );
+       } );
+
+       /** FIXME: the diff output is not very readeable. */
+       QUnit.test( 'bug 32047 - caption must be before thead', function ( assert ) {
+               var $table;
+               $table = $(
+                       '<table class="sortable">' +
+                       '<caption>CAPTION</caption>' +
+                       '<tr><th>THEAD</th></tr>' +
+                       '<tr><td>A</td></tr>' +
+                       '<tr><td>B</td></tr>' +
+                       '<tr class="sortbottom"><td>TFOOT</td></tr>' +
+                       '</table>'
+                       );
                $table.tablesorter();
-               $table.find( '.headerSort:eq(0)' ).click().click();
-       }
-);
-// TODO add numbers sorting tests for bug 8115 with a different language
-
-QUnit.test( 'bug 32888 - Tables inside a tableheader cell', 2, function ( assert ) {
-       var $table;
-       $table = $(
-               '<table class="sortable" id="mw-bug-32888">' +
-               '<tr><th>header<table id="mw-bug-32888-2">'+
-                       '<tr><th>1</th><th>2</th></tr>' +
-               '</table></th></tr>' +
-               '<tr><td>A</td></tr>' +
-               '<tr><td>B</td></tr>' +
-               '</table>'
+
+               assert.equal(
+                       $table.children( ).get( 0 ).nodeName,
+                       'CAPTION',
+                       'First element after <thead> must be <caption> (bug 32047)'
                );
-       $table.tablesorter();
+       });
 
-       assert.equal(
-               $table.find('> thead:eq(0) > tr > th.headerSort').length,
-               1,
-               'Child tables inside a headercell should not interfere with sortable headers (bug 32888)'
-       );
-       assert.equal(
-               $( '#mw-bug-32888-2' ).find('th.headerSort').length,
-               0,
-               'The headers of child tables inside a headercell should not be sortable themselves (bug 32888)'
-       );
-});
+       QUnit.test( 'data-sort-value attribute, when available, should override sorting position', function ( assert ) {
+               var $table, data;
+
+               // Example 1: All cells except one cell without data-sort-value,
+               // which should be sorted at it's text content value.
+               $table = $(
+                       '<table class="sortable"><thead><tr><th>Data</th></tr></thead>' +
+                               '<tbody>' +
+                               '<tr><td>Cheetah</td></tr>' +
+                               '<tr><td data-sort-value="Apple">Bird</td></tr>' +
+                               '<tr><td data-sort-value="Bananna">Ferret</td></tr>' +
+                               '<tr><td data-sort-value="Drupe">Elephant</td></tr>' +
+                               '<tr><td data-sort-value="Cherry">Dolphin</td></tr>' +
+                       '</tbody></table>'
+               );
+               $table.tablesorter().find( '.headerSort:eq(0)' ).click();
+
+               data = [];
+               $table.find( 'tbody > tr' ).each( function( i, tr ) {
+                       $( tr ).find( 'td' ).each( function( i, td ) {
+                               data.push( {
+                                       data: $( td ).data( 'sortValue' ),
+                                       text: $( td ).text()
+                               } );
+                       });
+               });
 
+               assert.deepEqual( data, [
+                       {
+                               data: 'Apple',
+                               text: 'Bird'
+                       }, {
+                               data: 'Bananna',
+                               text: 'Ferret'
+                       }, {
+                               data: undefined,
+                               text: 'Cheetah'
+                       }, {
+                               data: 'Cherry',
+                               text: 'Dolphin'
+                       }, {
+                               data: 'Drupe',
+                               text: 'Elephant'
+                       }
+               ], 'Order matches expected order (based on data-sort-value attribute values)' );
+
+               // Example 2
+               $table = $(
+                       '<table class="sortable"><thead><tr><th>Data</th></tr></thead>' +
+                               '<tbody>' +
+                               '<tr><td>D</td></tr>' +
+                               '<tr><td data-sort-value="E">A</td></tr>' +
+                               '<tr><td>B</td></tr>' +
+                               '<tr><td>G</td></tr>' +
+                               '<tr><td data-sort-value="F">C</td></tr>' +
+                       '</tbody></table>'
+               );
+               $table.tablesorter().find( '.headerSort:eq(0)' ).click();
+
+               data = [];
+               $table.find( 'tbody > tr' ).each( function ( i, tr ) {
+                       $( tr ).find( 'td' ).each( function ( i, td ) {
+                               data.push( {
+                                       data: $( td ).data( 'sortValue' ),
+                                       text: $( td ).text()
+                               } );
+                       });
+               });
 
-var correctDateSorting1 = [
-       ['01 January 2010'],
-       ['05 February 2010'],
-       ['16 January 2010']
-];
+               assert.deepEqual( data, [
+                       {
+                               data: undefined,
+                               text: 'B'
+                       }, {
+                               data: undefined,
+                               text: 'D'
+                       }, {
+                               data: 'E',
+                               text: 'A'
+                       }, {
+                               data: 'F',
+                               text: 'C'
+                       }, {
+                               data: undefined,
+                               text: 'G'
+                       }
+               ], 'Order matches expected order (based on data-sort-value attribute values)' );
+
+               // Example 3: Test that live changes are used from data-sort-value,
+               // even if they change after the tablesorter is constructed (bug 38152).
+               $table = $(
+                       '<table class="sortable"><thead><tr><th>Data</th></tr></thead>' +
+                               '<tbody>' +
+                               '<tr><td>D</td></tr>' +
+                               '<tr><td data-sort-value="1">A</td></tr>' +
+                               '<tr><td>B</td></tr>' +
+                               '<tr><td data-sort-value="2">G</td></tr>' +
+                               '<tr><td>C</td></tr>' +
+                       '</tbody></table>'
+               );
+               // initialize table sorter and sort once
+               $table
+                       .tablesorter()
+                       .find( '.headerSort:eq(0)' ).click();
+
+               // Change the sortValue data properties (bug 38152)
+               // - change data
+               $table.find( 'td:contains(A)' ).data( 'sortValue', 3 );
+               // - add data
+               $table.find( 'td:contains(B)' ).data( 'sortValue', 1 );
+               // - remove data, bring back attribute: 2
+               $table.find( 'td:contains(G)' ).removeData( 'sortValue' );
+
+               // Now sort again (twice, so it is back at Ascending)
+               $table.find( '.headerSort:eq(0)' ).click();
+               $table.find( '.headerSort:eq(0)' ).click();
 
-var correctDateSortingSorted1 = [
-       ['01 January 2010'],
-       ['16 January 2010'],
-       ['05 February 2010']
-];
+               data = [];
+               $table.find( 'tbody > tr' ).each( function( i, tr ) {
+                       $( tr ).find( 'td' ).each( function( i, td ) {
+                               data.push( {
+                                       data: $( td ).data( 'sortValue' ),
+                                       text: $( td ).text()
+                               } );
+                       });
+               });
 
-tableTest(
-       'Correct date sorting I',
-       ['date'],
-       correctDateSorting1,
-       correctDateSortingSorted1,
-       function ( $table ) {
-               mw.config.set( 'wgDefaultDateFormat', 'mdy' );
+               assert.deepEqual( data, [
+                       {
+                               data: 1,
+                               text: 'B'
+                       }, {
+                               data: 2,
+                               text: 'G'
+                       }, {
+                               data: 3,
+                               text: 'A'
+                       }, {
+                               data: undefined,
+                               text: 'C'
+                       }, {
+                               data: undefined,
+                               text: 'D'
+                       }
+               ], 'Order matches expected order, using the current sortValue in $.data()' );
 
-               $table.tablesorter();
-               $table.find( '.headerSort:eq(0)' ).click();
-       }
-);
-
-var correctDateSorting2 = [
-       ['January 01 2010'],
-       ['February 05 2010'],
-       ['January 16 2010']
-];
-
-var correctDateSortingSorted2 = [
-       ['January 01 2010'],
-       ['January 16 2010'],
-       ['February 05 2010']
-];
-
-tableTest(
-       'Correct date sorting II',
-       ['date'],
-       correctDateSorting2,
-       correctDateSortingSorted2,
-       function ( $table ) {
-               mw.config.set( 'wgDefaultDateFormat', 'dmy' );
+       });
 
+       var numbers = [
+               [ '12'    ],
+               [  '7'    ],
+               [ '13,000'],
+               [  '9'    ],
+               [ '14'    ],
+               [  '8.0'  ]
+       ];
+       var numbersAsc = [
+               [  '7'    ],
+               [  '8.0'  ],
+               [  '9'    ],
+               [ '12'    ],
+               [ '14'    ],
+               [ '13,000']
+       ];
+
+       tableTest( 'bug 8115: sort numbers with commas (ascending)',
+               ['Numbers'], numbers, numbersAsc,
+               function( $table ) {
+                       $table.tablesorter();
+                       $table.find( '.headerSort:eq(0)' ).click();
+               }
+       );
+
+       tableTest( 'bug 8115: sort numbers with commas (descending)',
+               ['Numbers'], numbers, reversed(numbersAsc),
+               function( $table ) {
+                       $table.tablesorter();
+                       $table.find( '.headerSort:eq(0)' ).click().click();
+               }
+       );
+       // TODO add numbers sorting tests for bug 8115 with a different language
+
+       QUnit.test( 'bug 32888 - Tables inside a tableheader cell', 2, function ( assert ) {
+               var $table;
+               $table = $(
+                       '<table class="sortable" id="mw-bug-32888">' +
+                       '<tr><th>header<table id="mw-bug-32888-2">'+
+                               '<tr><th>1</th><th>2</th></tr>' +
+                       '</table></th></tr>' +
+                       '<tr><td>A</td></tr>' +
+                       '<tr><td>B</td></tr>' +
+                       '</table>'
+                       );
                $table.tablesorter();
-               $table.find( '.headerSort:eq(0)' ).click();
-       }
-);
+
+               assert.equal(
+                       $table.find('> thead:eq(0) > tr > th.headerSort').length,
+                       1,
+                       'Child tables inside a headercell should not interfere with sortable headers (bug 32888)'
+               );
+               assert.equal(
+                       $( '#mw-bug-32888-2' ).find('th.headerSort').length,
+                       0,
+                       'The headers of child tables inside a headercell should not be sortable themselves (bug 32888)'
+               );
+       });
+
+
+       var correctDateSorting1 = [
+               ['01 January 2010'],
+               ['05 February 2010'],
+               ['16 January 2010']
+       ];
+
+       var correctDateSortingSorted1 = [
+               ['01 January 2010'],
+               ['16 January 2010'],
+               ['05 February 2010']
+       ];
+
+       tableTest(
+               'Correct date sorting I',
+               ['date'],
+               correctDateSorting1,
+               correctDateSortingSorted1,
+               function ( $table ) {
+                       mw.config.set( 'wgDefaultDateFormat', 'mdy' );
+
+                       $table.tablesorter();
+                       $table.find( '.headerSort:eq(0)' ).click();
+               }
+       );
+
+       var correctDateSorting2 = [
+               ['January 01 2010'],
+               ['February 05 2010'],
+               ['January 16 2010']
+       ];
+
+       var correctDateSortingSorted2 = [
+               ['January 01 2010'],
+               ['January 16 2010'],
+               ['February 05 2010']
+       ];
+
+       tableTest(
+               'Correct date sorting II',
+               ['date'],
+               correctDateSorting2,
+               correctDateSortingSorted2,
+               function ( $table ) {
+                       mw.config.set( 'wgDefaultDateFormat', 'dmy' );
+
+                       $table.tablesorter();
+                       $table.find( '.headerSort:eq(0)' ).click();
+               }
+       );
 
 }( jQuery, mediaWiki ) );
index f0a210f..1795b95 100644 (file)
-QUnit.module( 'jquery.textSelection', QUnit.newMwEnvironment() );
-
-/**
- * Test factory for $.fn.textSelection( 'encapsulateText' )
- *
- * @param options {object} associative array containing:
- *   description {string}
- *   input {string}
- *   output {string}
- *   start {int} starting char for selection
- *   end {int} ending char for selection
- *   params {object} add'l parameters for $().textSelection( 'encapsulateText' )
- */
-function encapsulateTest( options ) {
-       var opt = $.extend({
-               description: '',
-               before: {},
-               after: {},
-               replace: {}
-       }, options);
-
-       opt.before = $.extend({
-               text: '',
-               start: 0,
-               end: 0
-       }, opt.before);
-       opt.after = $.extend({
-               text: '',
-               selected: null
-       }, opt.after);
-
-       QUnit.test( opt.description, function ( assert ) {
-               var tests = 1;
-               if ( opt.after.selected !== null ) {
-                       tests++;
-               }
-               QUnit.expect( tests );
-
-               var $textarea = $( '<textarea>' );
-
-               $( '#qunit-fixture' ).append( $textarea );
-
-               //$textarea.textSelection( 'setContents', opt.before.text); // this method is actually missing atm...
-               $textarea.val( opt.before.text ); // won't work with the WikiEditor iframe?
-
-               var     start = opt.before.start,
-                       end = opt.before.end;
-               if ( window.opera ) {
-                       // Compensate for Opera's craziness converting "\n" to "\r\n" and counting that as two chars
-                       var     newLinesBefore = opt.before.text.substring( 0, start ).split( "\n" ).length - 1,
-                               newLinesInside = opt.before.text.substring( start, end ).split( "\n" ).length - 1;
-                       start += newLinesBefore;
-                       end += newLinesBefore + newLinesInside;
-               }
-
-               var options = $.extend( {}, opt.replace ); // Clone opt.replace
-               options.selectionStart = start;
-               options.selectionEnd = end;
-               $textarea.textSelection( 'encapsulateSelection', options );
-
-               var text = $textarea.textSelection( 'getContents' ).replace( /\r\n/g, "\n" );
-
-               assert.equal( text, opt.after.text, 'Checking full text after encapsulation' );
-
-               if (opt.after.selected !== null) {
-                       var selected = $textarea.textSelection( 'getSelection' );
-                       assert.equal( selected, opt.after.selected, 'Checking selected text after encapsulation.' );
-               }
-
-       } );
-}
-
-var sig = {
-       'pre': "--~~~~"
-}, bold = {
-       pre: "'''",
-       peri: 'Bold text',
-       post: "'''"
-}, h2 = {
-       'pre': '== ',
-       'peri': 'Heading 2',
-       'post': ' ==',
-       'regex': /^(\s*)(={1,6})(.*?)\2(\s*)$/,
-       'regexReplace': "$1==$3==$4",
-       'ownline': true
-}, ulist = {
-       'pre': "* ",
-       'peri': 'Bulleted list item',
-       'post': "",
-       'ownline': true,
-       'splitlines': true
-};
-
-encapsulateTest({
-       description: "Adding sig to end of text",
-       before: {
-               text: "Wikilove dude! ",
-               start: 15,
-               end: 15
-       },
-       after: {
-               text: "Wikilove dude! --~~~~",
-               selected: ""
-       },
-       replace: sig
-});
-
-encapsulateTest({
-       description: "Adding bold to empty",
-       before: {
-               text: "",
-               start: 0,
-               end: 0
-       },
-       after: {
-               text: "'''Bold text'''",
-               selected: "Bold text" // selected because it's the default
-       },
-       replace: bold
-});
-
-encapsulateTest({
-       description: "Adding bold to existing text",
-       before: {
-               text: "Now is the time for all good men to come to the aid of their country",
-               start: 20,
-               end: 32
-       },
-       after: {
-               text: "Now is the time for '''all good men''' to come to the aid of their country",
-               selected: "" // empty because it's not the default'
-       },
-       replace: bold
-});
-
-encapsulateTest({
-       description: "ownline option: adding new h2",
-       before: {
-               text:"Before\nAfter",
-               start: 7,
-               end: 7
-       },
-       after: {
-               text: "Before\n== Heading 2 ==\nAfter",
-               selected: "Heading 2"
-       },
-       replace: h2
-});
-
-encapsulateTest({
-       description: "ownline option: turn a whole line into new h2",
-       before: {
-               text:"Before\nMy heading\nAfter",
-               start: 7,
-               end: 17
-       },
-       after: {
-               text: "Before\n== My heading ==\nAfter",
-               selected: ""
-       },
-       replace: h2
-});
-
-
-encapsulateTest({
-       description: "ownline option: turn a partial line into new h2",
-       before: {
-               text:"BeforeMy headingAfter",
-               start: 6,
-               end: 16
-       },
-       after: {
-               text: "Before\n== My heading ==\nAfter",
-               selected: ""
-       },
-       replace: h2
-});
-
-
-encapsulateTest({
-       description: "splitlines option: no selection, insert new list item",
-       before: {
-               text: "Before\nAfter",
-               start: 7,
-               end: 7
-       },
-       after: {
-               text: "Before\n* Bulleted list item\nAfter"
-       },
-       replace: ulist
-});
-
-encapsulateTest({
-       description: "splitlines option: single partial line selection, insert new list item",
-       before: {
-               text: "BeforeMy List ItemAfter",
-               start: 6,
-               end: 18
-       },
-       after: {
-               text: "Before\n* My List Item\nAfter"
-       },
-       replace: ulist
-});
-
-encapsulateTest({
-       description: "splitlines option: multiple lines",
-       before: {
-               text: "Before\nFirst\nSecond\nThird\nAfter",
-               start: 7,
-               end: 25
-       },
-       after: {
-               text: "Before\n* First\n* Second\n* Third\nAfter"
-       },
-       replace: ulist
-});
-
-
-function caretTest( options ) {
-       QUnit.test( options.description, 2, function ( assert ) {
-               var $textarea = $( '<textarea>' ).text( options.text );
-
-               $( '#qunit-fixture' ).append( $textarea );
-
-               if ( options.mode === 'set' ) {
-                       $textarea.textSelection('setSelection', {
-                               start: options.start,
-                               end: options.end
-                       });
-               }
-
-               function among( actual, expected, message ) {
-                       if ( $.isArray( expected ) ) {
-                               assert.ok( $.inArray( actual, expected ) !== -1 , message + ' (got ' + actual + '; expected one of ' + expected.join(', ') + ')' );
-                       } else {
-                               assert.equal( actual, expected, message );
+( function ( $ ) {
+
+       QUnit.module( 'jquery.textSelection', QUnit.newMwEnvironment() );
+
+       /**
+        * Test factory for $.fn.textSelection( 'encapsulateText' )
+        *
+        * @param options {object} associative array containing:
+        *   description {string}
+        *   input {string}
+        *   output {string}
+        *   start {int} starting char for selection
+        *   end {int} ending char for selection
+        *   params {object} add'l parameters for $().textSelection( 'encapsulateText' )
+        */
+       function encapsulateTest( options ) {
+               var opt = $.extend({
+                       description: '',
+                       before: {},
+                       after: {},
+                       replace: {}
+               }, options);
+
+               opt.before = $.extend({
+                       text: '',
+                       start: 0,
+                       end: 0
+               }, opt.before);
+               opt.after = $.extend({
+                       text: '',
+                       selected: null
+               }, opt.after);
+
+               QUnit.test( opt.description, function ( assert ) {
+                       /*jshint onevar: false */
+                       var tests = 1;
+                       if ( opt.after.selected !== null ) {
+                               tests++;
+                       }
+                       QUnit.expect( tests );
+
+                       var $textarea = $( '<textarea>' );
+
+                       $( '#qunit-fixture' ).append( $textarea );
+
+                       //$textarea.textSelection( 'setContents', opt.before.text); // this method is actually missing atm...
+                       $textarea.val( opt.before.text ); // won't work with the WikiEditor iframe?
+
+                       var     start = opt.before.start,
+                               end = opt.before.end;
+                       if ( window.opera ) {
+                               // Compensate for Opera's craziness converting \n to \r\n and counting that as two chars
+                               var     newLinesBefore = opt.before.text.substring( 0, start ).split( '\n' ).length - 1,
+                                       newLinesInside = opt.before.text.substring( start, end ).split( '\n' ).length - 1;
+                               start += newLinesBefore;
+                               end += newLinesBefore + newLinesInside;
+                       }
+
+                       var options = $.extend( {}, opt.replace ); // Clone opt.replace
+                       options.selectionStart = start;
+                       options.selectionEnd = end;
+                       $textarea.textSelection( 'encapsulateSelection', options );
+
+                       var text = $textarea.textSelection( 'getContents' ).replace( /\r\n/g, '\n' );
+
+                       assert.equal( text, opt.after.text, 'Checking full text after encapsulation' );
+
+                       if ( opt.after.selected !== null ) {
+                               var selected = $textarea.textSelection( 'getSelection' );
+                               assert.equal( selected, opt.after.selected, 'Checking selected text after encapsulation.' );
+                       }
+
+               } );
+       }
+
+       var caretSample,
+               sig = {
+                       pre: '--~~~~'
+               },
+               bold = {
+                       pre: '\'\'\'',
+                       peri: 'Bold text',
+                       post: '\'\'\''
+               },
+               h2 = {
+                       pre: '== ',
+                       peri: 'Heading 2',
+                       post: ' ==',
+                       regex: /^(\s*)(={1,6})(.*?)\2(\s*)$/,
+                       regexReplace: '$1==$3==$4',
+                       ownline: true
+               },
+               ulist = {
+                       pre: '* ',
+                       peri: 'Bulleted list item',
+                       post: '',
+                       ownline: true,
+                       splitlines: true
+               };
+
+       encapsulateTest({
+               description: 'Adding sig to end of text',
+               before: {
+                       text: 'Wikilove dude! ',
+                       start: 15,
+                       end: 15
+               },
+               after: {
+                       text: 'Wikilove dude! --~~~~',
+                       selected: ''
+               },
+               replace: sig
+       });
+
+       encapsulateTest({
+               description: 'Adding bold to empty',
+               before: {
+                       text: '',
+                       start: 0,
+                       end: 0
+               },
+               after: {
+                       text: '\'\'\'Bold text\'\'\'',
+                       selected: 'Bold text' // selected because it's the default
+               },
+               replace: bold
+       });
+
+       encapsulateTest({
+               description: 'Adding bold to existing text',
+               before: {
+                       text: 'Now is the time for all good men to come to the aid of their country',
+                       start: 20,
+                       end: 32
+               },
+               after: {
+                       text: 'Now is the time for \'\'\'all good men\'\'\' to come to the aid of their country',
+                       selected: '' // empty because it's not the default'
+               },
+               replace: bold
+       });
+
+       encapsulateTest({
+               description: 'ownline option: adding new h2',
+               before: {
+                       text:'Before\nAfter',
+                       start: 7,
+                       end: 7
+               },
+               after: {
+                       text: 'Before\n== Heading 2 ==\nAfter',
+                       selected: 'Heading 2'
+               },
+               replace: h2
+       });
+
+       encapsulateTest({
+               description: 'ownline option: turn a whole line into new h2',
+               before: {
+                       text:'Before\nMy heading\nAfter',
+                       start: 7,
+                       end: 17
+               },
+               after: {
+                       text: 'Before\n== My heading ==\nAfter',
+                       selected: ''
+               },
+               replace: h2
+       });
+
+
+       encapsulateTest({
+               description: 'ownline option: turn a partial line into new h2',
+               before: {
+                       text:'BeforeMy headingAfter',
+                       start: 6,
+                       end: 16
+               },
+               after: {
+                       text: 'Before\n== My heading ==\nAfter',
+                       selected: ''
+               },
+               replace: h2
+       });
+
+
+       encapsulateTest({
+               description: 'splitlines option: no selection, insert new list item',
+               before: {
+                       text: 'Before\nAfter',
+                       start: 7,
+                       end: 7
+               },
+               after: {
+                       text: 'Before\n* Bulleted list item\nAfter'
+               },
+               replace: ulist
+       });
+
+       encapsulateTest({
+               description: 'splitlines option: single partial line selection, insert new list item',
+               before: {
+                       text: 'BeforeMy List ItemAfter',
+                       start: 6,
+                       end: 18
+               },
+               after: {
+                       text: 'Before\n* My List Item\nAfter'
+               },
+               replace: ulist
+       });
+
+       encapsulateTest({
+               description: 'splitlines option: multiple lines',
+               before: {
+                       text: 'Before\nFirst\nSecond\nThird\nAfter',
+                       start: 7,
+                       end: 25
+               },
+               after: {
+                       text: 'Before\n* First\n* Second\n* Third\nAfter'
+               },
+               replace: ulist
+       });
+
+
+       function caretTest( options ) {
+               QUnit.test( options.description, 2, function ( assert ) {
+                       var pos, $textarea = $( '<textarea>' ).text( options.text );
+
+                       $( '#qunit-fixture' ).append( $textarea );
+
+                       if ( options.mode === 'set' ) {
+                               $textarea.textSelection('setSelection', {
+                                       start: options.start,
+                                       end: options.end
+                               });
+                       }
+
+                       function among( actual, expected, message ) {
+                               if ( $.isArray( expected ) ) {
+                                       assert.ok( $.inArray( actual, expected ) !== -1 , message + ' (got ' + actual + '; expected one of ' + expected.join(', ') + ')' );
+                               } else {
+                                       assert.equal( actual, expected, message );
+                               }
                        }
-               }
 
-               var pos = $textarea.textSelection('getCaretPosition', {startAndEnd: true});
-               among(pos[0], options.start, 'Caret start should be where we set it.');
-               among(pos[1], options.end, 'Caret end should be where we set it.');
+                       pos = $textarea.textSelection( 'getCaretPosition', { startAndEnd: true });
+                       among(pos[0], options.start, 'Caret start should be where we set it.');
+                       among(pos[1], options.end, 'Caret end should be where we set it.');
+               });
+       }
+
+       caretSample = 'Some big text that we like to work with. Nothing fancy... you know what I mean?';
+
+       /*
+       // @broken: Disabled per bug 34820
+       caretTest({
+               description: 'getCaretPosition with original/empty selection - bug 31847 with IE 6/7/8',
+               text: caretSample,
+               start: [0, caretSample.length], // Opera and Firefox (prior to FF 6.0) default caret to the end of the box (caretSample.length)
+               end: [0, caretSample.length], // Other browsers default it to the beginning (0), so check both.
+               mode: 'get'
+       });
+       */
+
+       caretTest({
+               description: 'set/getCaretPosition with forced empty selection',
+               text: caretSample,
+               start: 7,
+               end: 7,
+               mode: 'set'
        });
-}
-
-var caretSample = "Some big text that we like to work with. Nothing fancy... you know what I mean?";
-
-/*
- // @broken: Disabled per bug 34820
-caretTest({
-       description: 'getCaretPosition with original/empty selection - bug 31847 with IE 6/7/8',
-       text: caretSample,
-       start: [0, caretSample.length], // Opera and Firefox (prior to FF 6.0) default caret to the end of the box (caretSample.length)
-       end: [0, caretSample.length], // Other browsers default it to the beginning (0), so check both.
-       mode: 'get'
-});
-*/
-
-caretTest({
-       description: 'set/getCaretPosition with forced empty selection',
-       text: caretSample,
-       start: 7,
-       end: 7,
-       mode: 'set'
-});
-
-caretTest({
-       description: 'set/getCaretPosition with small selection',
-       text: caretSample,
-       start: 6,
-       end: 11,
-       mode: 'set'
-});
 
+       caretTest({
+               description: 'set/getCaretPosition with small selection',
+               text: caretSample,
+               start: 6,
+               end: 11,
+               mode: 'set'
+       });
+}( jQuery ) );
index 3d3f630..ec7179e 100644 (file)
@@ -1,26 +1,28 @@
-QUnit.module( 'mediawiki.api.parse', QUnit.newMwEnvironment() );
+( function ( mw, $ ) {
+       QUnit.module( 'mediawiki.api.parse', QUnit.newMwEnvironment() );
 
-QUnit.asyncTest( 'Hello world', function ( assert ) {
-       var api;
-       QUnit.expect( 6 );
+       QUnit.asyncTest( 'Hello world', function ( assert ) {
+               var api;
+               QUnit.expect( 6 );
 
-       api = new mw.Api();
+               api = new mw.Api();
 
-       api.parse( "'''Hello world'''" )
-               .done( function ( html ) {
-                       // Parse into a document fragment instead of comparing HTML, due to
-                       // presence of Tidy influencing whitespace.
-                       // Html also contains "NewPP report" comment.
-                       var $res = $( '<div>' ).html( html ).children(),
-                               res = $res.get( 0 );
-                       assert.equal( $res.length, 1, 'Response contains 1 element' );
-                       assert.equal( res.nodeName.toLowerCase(), 'p', 'Response is a paragraph' );
-                       assert.equal( $res.children().length, 1, 'Response has 1 child element' );
-                       assert.equal( $res.children().get( 0 ).nodeName.toLowerCase(), 'b', 'Child element is a bold tag' );
-                       // Trim since Tidy may or may not mess with the spacing here
-                       assert.equal( $.trim( $res.text() ), 'Hello world', 'Response contains given text' );
-                       assert.equal( $res.find( 'b' ).text(), 'Hello world', 'Bold tag wraps the entire, same, text' );
+               api.parse( '\'\'\'Hello world\'\'\'' )
+                       .done( function ( html ) {
+                               // Parse into a document fragment instead of comparing HTML, due to
+                               // presence of Tidy influencing whitespace.
+                               // Html also contains "NewPP report" comment.
+                               var $res = $( '<div>' ).html( html ).children(),
+                                       res = $res.get( 0 );
+                               assert.equal( $res.length, 1, 'Response contains 1 element' );
+                               assert.equal( res.nodeName.toLowerCase(), 'p', 'Response is a paragraph' );
+                               assert.equal( $res.children().length, 1, 'Response has 1 child element' );
+                               assert.equal( $res.children().get( 0 ).nodeName.toLowerCase(), 'b', 'Child element is a bold tag' );
+                               // Trim since Tidy may or may not mess with the spacing here
+                               assert.equal( $.trim( $res.text() ), 'Hello world', 'Response contains given text' );
+                               assert.equal( $res.find( 'b' ).text(), 'Hello world', 'Bold tag wraps the entire, same, text' );
 
-                       QUnit.start();
-               });
-});
+                               QUnit.start();
+                       });
+       });
+}( mediaWiki, jQuery ) );
index 79bd730..50bbfd7 100644 (file)
@@ -1,59 +1,61 @@
-QUnit.module( 'mediawiki.api', QUnit.newMwEnvironment() );
+( function ( mw ) {
+       QUnit.module( 'mediawiki.api', QUnit.newMwEnvironment() );
+
+       QUnit.asyncTest( 'Basic functionality', function ( assert ) {
+               var api, d1, d2, d3;
+               QUnit.expect( 3 );
+
+               api = new mw.Api();
+
+               d1 = api.get( {} )
+                       .done( function ( data ) {
+                               assert.deepEqual( data, [], 'If request succeeds without errors, resolve deferred' );
+                       });
+
+               d2 = api.get({
+                               action: 'doesntexist'
+                       })
+                       .fail( function ( errorCode ) {
+                               assert.equal( errorCode, 'unknown_action', 'API error (e.g. "unknown_action") should reject the deferred' );
+                       });
+
+               d3 = api.post( {} )
+                       .done( function ( data ) {
+                               assert.deepEqual( data, [], 'Simple POST request' );
+                       });
+
+               // After all are completed, continue the test suite.
+               QUnit.whenPromisesComplete( d1, d2, d3 ).always( function () {
+                       QUnit.start();
+               });
+       });
 
-QUnit.asyncTest( 'Basic functionality', function ( assert ) {
-       var api, d1, d2, d3;
-       QUnit.expect( 3 );
+       QUnit.asyncTest( 'Deprecated callback methods', function ( assert ) {
+               var api, d1, d2, d3;
+               QUnit.expect( 3 );
 
-       api = new mw.Api();
+               api = new mw.Api();
 
-       d1 = api.get( {} )
-               .done( function ( data ) {
-                       assert.deepEqual( data, [], 'If request succeeds without errors, resolve deferred' );
+               d1 = api.get( {}, function () {
+                       assert.ok( true, 'Function argument treated as success callback.' );
                });
 
-       d2 = api.get({
-                       action: 'doesntexist'
-               })
-               .fail( function ( errorCode, details ) {
-                       assert.equal( errorCode, 'unknown_action', 'API error (e.g. "unknown_action") should reject the deferred' );
+               d2 = api.get( {}, {
+                       ok: function () {
+                               assert.ok( true, '"ok" property treated as success callback.' );
+                       }
                });
 
-       d3 = api.post( {} )
-               .done( function ( data ) {
-                       assert.deepEqual( data, [], 'Simple POST request' );
+               d3 = api.get({
+                               action: 'doesntexist'
+                       }, {
+                       err: function () {
+                               assert.ok( true, '"err" property treated as error callback.' );
+                       }
                });
 
-       // After all are completed, continue the test suite.
-       QUnit.whenPromisesComplete( d1, d2, d3 ).always( function () {
-               QUnit.start();
-       });
-});
-
-QUnit.asyncTest( 'Deprecated callback methods', function ( assert ) {
-       var api, d1, d2, d3;
-       QUnit.expect( 3 );
-
-       api = new mw.Api();
-
-       d1 = api.get( {}, function () {
-               assert.ok( true, 'Function argument treated as success callback.' );
-       });
-
-       d2 = api.get( {}, {
-               ok: function ( data ) {
-                       assert.ok( true, '"ok" property treated as success callback.' );
-               }
-       });
-
-       d3 = api.get({
-                       action: 'doesntexist'
-               }, {
-               err: function ( data ) {
-                       assert.ok( true, '"err" property treated as error callback.' );
-               }
-       });
-
-       QUnit.whenPromisesComplete( d1, d2, d3 ).always( function () {
-               QUnit.start();
+               QUnit.whenPromisesComplete( d1, d2, d3 ).always( function () {
+                       QUnit.start();
+               });
        });
-});
+}( mediaWiki ) );
index 7fe7baf..d7b0cb7 100644 (file)
@@ -1,62 +1,65 @@
-QUnit.module( 'mediawiki.special.recentchanges', QUnit.newMwEnvironment() );
-
-// TODO: verify checkboxes == [ 'nsassociated', 'nsinvert' ]
-
-QUnit.test( '"all" namespace disable checkboxes', function ( assert ) {
-
-       // from Special:Recentchanges
-       var select =
-       '<select id="namespace" name="namespace" class="namespaceselector">'
-       + '<option value="" selected="selected">all</option>'
-       + '<option value="0">(Main)</option>'
-       + '<option value="1">Talk</option>'
-       + '<option value="2">User</option>'
-       + '<option value="3">User talk</option>'
-       + '<option value="4">ProjectName</option>'
-       + '<option value="5">ProjectName talk</option>'
-       + '</select>'
-       + '<input name="invert" type="checkbox" value="1" id="nsinvert" title="no title" />'
-       + '<label for="nsinvert" title="no title">Invert selection</label>'
-       + '<input name="associated" type="checkbox" value="1" id="nsassociated" title="no title" />'
-       + '<label for="nsassociated" title="no title">Associated namespace</label>'
-       + '<input type="submit" value="Go" />'
-       + '<input type="hidden" value="Special:RecentChanges" name="title" />'
-       ;
-
-       var $env = $( '<div>' ).html( select ).appendTo( 'body' );
-
-       // TODO abstract the double strictEquals
-
-       // At first checkboxes are enabled
-       assert.strictEqual( $( '#nsinvert' ).prop( 'disabled' ), false );
-       assert.strictEqual( $( '#nsassociated' ).prop( 'disabled' ), false );
-
-       // Initiate the recentchanges module
-       mw.special.recentchanges.init();
-
-       // By default
-       assert.strictEqual( $( '#nsinvert' ).prop( 'disabled' ), true );
-       assert.strictEqual( $( '#nsassociated' ).prop( 'disabled' ), true );
-
-       // select second option...
-       var $options = $( '#namespace' ).find( 'option' );
-       $options.eq(0).removeProp( 'selected' );
-       $options.eq(1).prop( 'selected', true );
-       $( '#namespace' ).change();
-
-       // ... and checkboxes should be enabled again
-       assert.strictEqual( $( '#nsinvert' ).prop( 'disabled' ), false );
-       assert.strictEqual( $( '#nsassociated' ).prop( 'disabled' ), false );
-
-       // select first option ( 'all' namespace)...
-       $options.eq(1).removeProp( 'selected' );
-       $options.eq(0).prop( 'selected', true );
-       $( '#namespace' ).change();
-
-       // ... and checkboxes should now be disabled
-       assert.strictEqual( $( '#nsinvert' ).prop( 'disabled' ), true );
-       assert.strictEqual( $( '#nsassociated' ).prop( 'disabled' ), true );
-
-       // DOM cleanup
-       $env.remove();
-});
+( function ( mw, $ ) {
+       QUnit.module( 'mediawiki.special.recentchanges', QUnit.newMwEnvironment() );
+
+       // TODO: verify checkboxes == [ 'nsassociated', 'nsinvert' ]
+
+       QUnit.test( '"all" namespace disable checkboxes', function ( assert ) {
+               var selectHtml, $env, $options;
+
+               // from Special:Recentchanges
+               selectHtml =
+               '<select id="namespace" name="namespace" class="namespaceselector">'
+               + '<option value="" selected="selected">all</option>'
+               + '<option value="0">(Main)</option>'
+               + '<option value="1">Talk</option>'
+               + '<option value="2">User</option>'
+               + '<option value="3">User talk</option>'
+               + '<option value="4">ProjectName</option>'
+               + '<option value="5">ProjectName talk</option>'
+               + '</select>'
+               + '<input name="invert" type="checkbox" value="1" id="nsinvert" title="no title" />'
+               + '<label for="nsinvert" title="no title">Invert selection</label>'
+               + '<input name="associated" type="checkbox" value="1" id="nsassociated" title="no title" />'
+               + '<label for="nsassociated" title="no title">Associated namespace</label>'
+               + '<input type="submit" value="Go" />'
+               + '<input type="hidden" value="Special:RecentChanges" name="title" />'
+               ;
+
+               $env = $( '<div>' ).html( selectHtml ).appendTo( 'body' );
+
+               // TODO abstract the double strictEquals
+
+               // At first checkboxes are enabled
+               assert.strictEqual( $( '#nsinvert' ).prop( 'disabled' ), false );
+               assert.strictEqual( $( '#nsassociated' ).prop( 'disabled' ), false );
+
+               // Initiate the recentchanges module
+               mw.special.recentchanges.init();
+
+               // By default
+               assert.strictEqual( $( '#nsinvert' ).prop( 'disabled' ), true );
+               assert.strictEqual( $( '#nsassociated' ).prop( 'disabled' ), true );
+
+               // select second option...
+               $options = $( '#namespace' ).find( 'option' );
+               $options.eq(0).removeProp( 'selected' );
+               $options.eq(1).prop( 'selected', true );
+               $( '#namespace' ).change();
+
+               // ... and checkboxes should be enabled again
+               assert.strictEqual( $( '#nsinvert' ).prop( 'disabled' ), false );
+               assert.strictEqual( $( '#nsassociated' ).prop( 'disabled' ), false );
+
+               // select first option ( 'all' namespace)...
+               $options.eq(1).removeProp( 'selected' );
+               $options.eq(0).prop( 'selected', true );
+               $( '#namespace' ).change();
+
+               // ... and checkboxes should now be disabled
+               assert.strictEqual( $( '#nsinvert' ).prop( 'disabled' ), true );
+               assert.strictEqual( $( '#nsassociated' ).prop( 'disabled' ), true );
+
+               // DOM cleanup
+               $env.remove();
+       });
+}( mediaWiki, jQuery ) );
index a736e12..52f0538 100644 (file)
@@ -1,58 +1,59 @@
-( function () {
+( function ( mw ) {
 
 // mw.Title relies on these three config vars
 // Restore them after each test run
 var config = {
-       "wgFormattedNamespaces": {
-               "-2": "Media",
-               "-1": "Special",
-               "0": "",
-               "1": "Talk",
-               "2": "User",
-               "3": "User talk",
-               "4": "Wikipedia",
-               "5": "Wikipedia talk",
-               "6": "File",
-               "7": "File talk",
-               "8": "MediaWiki",
-               "9": "MediaWiki talk",
-               "10": "Template",
-               "11": "Template talk",
-               "12": "Help",
-               "13": "Help talk",
-               "14": "Category",
-               "15": "Category talk",
+       wgFormattedNamespaces: {
+               '-2': 'Media',
+               '-1': 'Special',
+               0: '',
+               1: 'Talk',
+               2: 'User',
+               3: 'User talk',
+               4: 'Wikipedia',
+               5: 'Wikipedia talk',
+               6: 'File',
+               7: 'File talk',
+               8: 'MediaWiki',
+               9: 'MediaWiki talk',
+               10: 'Template',
+               11: 'Template talk',
+               12: 'Help',
+               13: 'Help talk',
+               14: 'Category',
+               15: 'Category talk',
                // testing custom / localized namespace
-               "100": "Penguins"
+               100: 'Penguins'
        },
-       "wgNamespaceIds": {
-               "media": -2,
-               "special": -1,
-               "": 0,
-               "talk": 1,
-               "user": 2,
-               "user_talk": 3,
-               "wikipedia": 4,
-               "wikipedia_talk": 5,
-               "file": 6,
-               "file_talk": 7,
-               "mediawiki": 8,
-               "mediawiki_talk": 9,
-               "template": 10,
-               "template_talk": 11,
-               "help": 12,
-               "help_talk": 13,
-               "category": 14,
-               "category_talk": 15,
-               "image": 6,
-               "image_talk": 7,
-               "project": 4,
-               "project_talk": 5,
+       wgNamespaceIds: {
+               /*jshint camelcase: false */
+               media: -2,
+               special: -1,
+               '': 0,
+               talk: 1,
+               user: 2,
+               user_talk: 3,
+               wikipedia: 4,
+               wikipedia_talk: 5,
+               file: 6,
+               file_talk: 7,
+               mediawiki: 8,
+               mediawiki_talk: 9,
+               template: 10,
+               template_talk: 11,
+               help: 12,
+               help_talk: 13,
+               category: 14,
+               category_talk: 15,
+               image: 6,
+               image_talk: 7,
+               project: 4,
+               project_talk: 5,
                /* testing custom / alias */
-               "penguins": 100,
-               "antarctic_waterfowl": 100
+               penguins: 100,
+               antarctic_waterfowl: 100
        },
-       "wgCaseSensitiveNamespaces": []
+       wgCaseSensitiveNamespaces: []
 };
 
 QUnit.module( 'mediawiki.Title', QUnit.newMwEnvironment({ config: config }) );
@@ -78,7 +79,7 @@ QUnit.test( 'Transformation', 8, function ( assert ) {
 
        title = new mw.Title( '   MediaWiki:  Foo   bar   .js   ' );
        // Don't ask why, it's the way the backend works. One space is kept of each set
-       assert.equal( title.getName(), 'Foo_bar_.js', "Merge multiple spaces to a single space." );
+       assert.equal( title.getName(), 'Foo_bar_.js', 'Merge multiple spaces to a single space.' );
 });
 
 QUnit.test( 'Main text for filename', 8, function ( assert ) {
@@ -116,7 +117,7 @@ QUnit.test( 'Namespace detection and conversion', 6, function ( assert ) {
 
 QUnit.test( 'Throw error on invalid title', 1, function ( assert ) {
        assert.throws(function () {
-               var title = new mw.Title( '' );
+               return new mw.Title( '' );
        }, 'Throw error on empty string' );
 });
 
@@ -197,4 +198,4 @@ QUnit.test( 'getUrl', 2, function ( assert ) {
        assert.equal( title.getUrl(), '/wiki/User_talk:John_Doe', 'Escaping in title and namespace for urls' );
 });
 
-}() );
\ No newline at end of file
+}( mediaWiki ) );
\ No newline at end of file
index b2026c1..f9927f0 100644 (file)
-QUnit.module( 'mediawiki.Uri', QUnit.newMwEnvironment({
-       setup: function () {
-               this.mwUriOrg = mw.Uri;
-               mw.Uri = mw.UriRelative( 'http://example.org/w/index.php' );
-       },
-       teardown: function () {
-               mw.Uri = this.mwUriOrg;
-               delete this.mwUriOrg;
-       }
-}) );
-
-$.each( [true, false], function ( i, strictMode ) {
-       QUnit.test( 'Basic mw.Uri object test in ' + ( strictMode ? '' : 'non-' ) + 'strict mode for a simple HTTP URI', 2, function ( assert ) {
-               var uriString, uri;
-               uriString = 'http://www.ietf.org/rfc/rfc2396.txt';
-               uri = new mw.Uri( uriString, {
-                       strictMode: strictMode
+( function ( mw, $ ) {
+       QUnit.module( 'mediawiki.Uri', QUnit.newMwEnvironment({
+               setup: function () {
+                       this.mwUriOrg = mw.Uri;
+                       mw.Uri = mw.UriRelative( 'http://example.org/w/index.php' );
+               },
+               teardown: function () {
+                       mw.Uri = this.mwUriOrg;
+                       delete this.mwUriOrg;
+               }
+       }) );
+
+       $.each( [true, false], function ( i, strictMode ) {
+               QUnit.test( 'Basic mw.Uri object test in ' + ( strictMode ? '' : 'non-' ) + 'strict mode for a simple HTTP URI', 2, function ( assert ) {
+                       var uriString, uri;
+                       uriString = 'http://www.ietf.org/rfc/rfc2396.txt';
+                       uri = new mw.Uri( uriString, {
+                               strictMode: strictMode
+                       });
+
+                       assert.deepEqual(
+                               {
+                                       protocol: uri.protocol,
+                                       host: uri.host,
+                                       port: uri.port,
+                                       path: uri.path,
+                                       query: uri.query,
+                                       fragment: uri.fragment
+                               }, {
+                                       protocol: 'http',
+                                       host: 'www.ietf.org',
+                                       port: undefined,
+                                       path: '/rfc/rfc2396.txt',
+                                       query: {},
+                                       fragment: undefined
+                               },
+                               'basic object properties'
+                       );
+
+                       assert.deepEqual(
+                               {
+                                       userInfo: uri.getUserInfo(),
+                                       authority: uri.getAuthority(),
+                                       hostPort: uri.getHostPort(),
+                                       queryString: uri.getQueryString(),
+                                       relativePath: uri.getRelativePath(),
+                                       toString: uri.toString()
+                               },
+                               {
+                                       userInfo: '',
+                                       authority: 'www.ietf.org',
+                                       hostPort: 'www.ietf.org',
+                                       queryString: '',
+                                       relativePath: '/rfc/rfc2396.txt',
+                                       toString: uriString
+                               },
+                               'construct composite components of URI on request'
+                       );
+
                });
+       });
+
+       QUnit.test( 'Parse an ftp URI correctly with user and password', 1, function ( assert ) {
+               var uri = new mw.Uri( 'ftp://usr:pwd@192.0.2.16/' );
 
                assert.deepEqual(
                        {
                                protocol: uri.protocol,
+                               user: uri.user,
+                               password: uri.password,
                                host: uri.host,
                                port: uri.port,
                                path: uri.path,
                                query: uri.query,
                                fragment: uri.fragment
-                       }, {
-                               protocol: 'http',
-                               host: 'www.ietf.org',
+                       },
+                       {
+                               protocol: 'ftp',
+                               user: 'usr',
+                               password: 'pwd',
+                               host: '192.0.2.16',
                                port: undefined,
-                               path: '/rfc/rfc2396.txt',
+                               path: '/',
                                query: {},
                                fragment: undefined
                        },
                        'basic object properties'
                );
+       } );
+
+       QUnit.test( 'Parse a uri with simple querystring', 1, function ( assert ) {
+               var uri = new mw.Uri( 'http://www.google.com/?q=uri' );
 
                assert.deepEqual(
                        {
-                               userInfo: uri.getUserInfo(),
-                               authority: uri.getAuthority(),
-                               hostPort: uri.getHostPort(),
-                               queryString: uri.getQueryString(),
-                               relativePath: uri.getRelativePath(),
-                               toString: uri.toString()
+                               protocol: uri.protocol,
+                               host: uri.host,
+                               port: uri.port,
+                               path: uri.path,
+                               query: uri.query,
+                               fragment: uri.fragment,
+                               queryString: uri.getQueryString()
                        },
                        {
-                               userInfo: '',
-                               authority: 'www.ietf.org',
-                               hostPort: 'www.ietf.org',
-                               queryString: '',
-                               relativePath: '/rfc/rfc2396.txt',
-                               toString: uriString
+                               protocol: 'http',
+                               host: 'www.google.com',
+                               port: undefined,
+                               path: '/',
+                               query: { q: 'uri' },
+                               fragment: undefined,
+                               queryString: 'q=uri'
                        },
-                       'construct composite components of URI on request'
+                       'basic object properties'
                );
+       } );
 
-       });
-});
-
-QUnit.test( 'Parse an ftp URI correctly with user and password', 1, function ( assert ) {
-       var uri = new mw.Uri( 'ftp://usr:pwd@192.0.2.16/' );
-
-       assert.deepEqual(
-               {
-                       protocol: uri.protocol,
-                       user: uri.user,
-                       password: uri.password,
-                       host: uri.host,
-                       port: uri.port,
-                       path: uri.path,
-                       query: uri.query,
-                       fragment: uri.fragment
-               },
-               {
-                       protocol: 'ftp',
-                       user: 'usr',
-                       password: 'pwd',
-                       host: '192.0.2.16',
-                       port: undefined,
-                       path: '/',
-                       query: {},
-                       fragment: undefined
-               },
-               'basic object properties'
-       );
-} );
-
-QUnit.test( 'Parse a uri with simple querystring', 1, function ( assert ) {
-       var uri = new mw.Uri( 'http://www.google.com/?q=uri' );
-
-       assert.deepEqual(
-               {
-                       protocol: uri.protocol,
-                       host: uri.host,
-                       port: uri.port,
-                       path: uri.path,
-                       query: uri.query,
-                       fragment: uri.fragment,
-                       queryString: uri.getQueryString()
-               },
-               {
-                       protocol: 'http',
-                       host: 'www.google.com',
-                       port: undefined,
-                       path: '/',
-                       query: { q: 'uri' },
-                       fragment: undefined,
-                       queryString: 'q=uri'
-               },
-               'basic object properties'
-       );
-} );
+       QUnit.test( 'Handle multiple query parameter (overrideKeys on)', 5, function ( assert ) {
+               var uri = new mw.Uri( 'http://www.example.com/dir/?m=foo&m=bar&n=1', {
+                       overrideKeys: true
+               });
 
-QUnit.test( 'Handle multiple query parameter (overrideKeys on)', 5, function ( assert ) {
-       var uri = new mw.Uri( 'http://www.example.com/dir/?m=foo&m=bar&n=1', {
-               overrideKeys: true
-       });
+               assert.equal( uri.query.n, '1', 'multiple parameters are parsed' );
+               assert.equal( uri.query.m, 'bar', 'last key overrides earlier keys' );
 
-       assert.equal( uri.query.n, '1', 'multiple parameters are parsed' );
-       assert.equal( uri.query.m, 'bar', 'last key overrides earlier keys' );
+               uri.query.n = [ 'x', 'y', 'z' ];
 
-       uri.query.n = [ 'x', 'y', 'z' ];
+               // Verify parts and total length instead of entire string because order
+               // of iteration can vary.
+               assert.ok( uri.toString().indexOf( 'm=bar' ), 'toString preserves other values' );
+               assert.ok( uri.toString().indexOf( 'n=x&n=y&n=z' ), 'toString parameter includes all values of an array query parameter' );
+               assert.equal( uri.toString().length, 'http://www.example.com/dir/?m=bar&n=x&n=y&n=z'.length, 'toString matches expected string' );
+       } );
 
-       // Verify parts and total length instead of entire string because order
-       // of iteration can vary.
-       assert.ok( uri.toString().indexOf( 'm=bar' ), 'toString preserves other values' );
-       assert.ok( uri.toString().indexOf( 'n=x&n=y&n=z' ), 'toString parameter includes all values of an array query parameter' );
-       assert.equal( uri.toString().length, 'http://www.example.com/dir/?m=bar&n=x&n=y&n=z'.length, 'toString matches expected string' );
-} );
+       QUnit.test( 'Handle multiple query parameter (overrideKeys off)', 9, function ( assert ) {
+               var uri = new mw.Uri( 'http://www.example.com/dir/?m=foo&m=bar&n=1', {
+                       overrideKeys: false
+               });
 
-QUnit.test( 'Handle multiple query parameter (overrideKeys off)', 9, function ( assert ) {
-       var uri = new mw.Uri( 'http://www.example.com/dir/?m=foo&m=bar&n=1', {
-               overrideKeys: false
-       });
+               // Strict comparison so that types are also verified (n should be string '1')
+               assert.strictEqual( uri.query.m.length, 2, 'multi-value query should be an array with 2 items' );
+               assert.strictEqual( uri.query.m[0], 'foo', 'order and value is correct' );
+               assert.strictEqual( uri.query.m[1], 'bar', 'order and value is correct' );
+               assert.strictEqual( uri.query.n, '1', 'n=1 is parsed with the correct value of the expected type' );
 
-       // Strict comparison so that types are also verified (n should be string '1')
-       assert.strictEqual( uri.query.m.length, 2, 'multi-value query should be an array with 2 items' );
-       assert.strictEqual( uri.query.m[0], 'foo', 'order and value is correct' );
-       assert.strictEqual( uri.query.m[1], 'bar', 'order and value is correct' );
-       assert.strictEqual( uri.query.n, '1', 'n=1 is parsed with the correct value of the expected type' );
-
-       // Change query values
-       uri.query.n = [ 'x', 'y', 'z' ];
-
-       // Verify parts and total length instead of entire string because order
-       // of iteration can vary.
-       assert.ok( uri.toString().indexOf( 'm=foo&m=bar' ) >= 0, 'toString preserves other values' );
-       assert.ok( uri.toString().indexOf( 'n=x&n=y&n=z' ) >= 0, 'toString parameter includes all values of an array query parameter' );
-       assert.equal( uri.toString().length, 'http://www.example.com/dir/?m=foo&m=bar&n=x&n=y&n=z'.length, 'toString matches expected string' );
-
-       // Remove query values
-       uri.query.m.splice( 0, 1 );
-       delete uri.query.n;
-
-       assert.equal( uri.toString(), 'http://www.example.com/dir/?m=bar', 'deletion properties' );
-
-       // Remove more query values, leaving an empty array
-       uri.query.m.splice( 0, 1 );
-       assert.equal( uri.toString(), 'http://www.example.com/dir/', 'empty array value is ommitted' );
-} );
-
-QUnit.test( 'All-dressed URI with everything', 11, function ( assert ) {
-       var uri, queryString, relativePath;
-
-       uri = new mw.Uri( 'http://auth@www.example.com:81/dir/dir.2/index.htm?q1=0&&test1&test2=value+%28escaped%29#top' );
-
-       assert.deepEqual(
-               {
-                       protocol: uri.protocol,
-                       user: uri.user,
-                       password: uri.password,
-                       host: uri.host,
-                       port: uri.port,
-                       path: uri.path,
-                       query: uri.query,
-                       fragment: uri.fragment
-               },
-               {
-                       protocol: 'http',
-                       user: 'auth',
-                       password: undefined,
-                       host: 'www.example.com',
-                       port: '81',
-                       path: '/dir/dir.2/index.htm',
-                       query: { q1: '0', test1: null, test2: 'value (escaped)' },
-                       fragment: 'top'
-               },
-               'basic object properties'
-       );
+               // Change query values
+               uri.query.n = [ 'x', 'y', 'z' ];
 
-       assert.equal( uri.getUserInfo(), 'auth', 'user info' );
+               // Verify parts and total length instead of entire string because order
+               // of iteration can vary.
+               assert.ok( uri.toString().indexOf( 'm=foo&m=bar' ) >= 0, 'toString preserves other values' );
+               assert.ok( uri.toString().indexOf( 'n=x&n=y&n=z' ) >= 0, 'toString parameter includes all values of an array query parameter' );
+               assert.equal( uri.toString().length, 'http://www.example.com/dir/?m=foo&m=bar&n=x&n=y&n=z'.length, 'toString matches expected string' );
 
-       assert.equal( uri.getAuthority(), 'auth@www.example.com:81', 'authority equal to auth@hostport' );
+               // Remove query values
+               uri.query.m.splice( 0, 1 );
+               delete uri.query.n;
 
-       assert.equal( uri.getHostPort(), 'www.example.com:81', 'hostport equal to host:port' );
+               assert.equal( uri.toString(), 'http://www.example.com/dir/?m=bar', 'deletion properties' );
 
-       queryString = uri.getQueryString();
-       assert.ok( queryString.indexOf( 'q1=0' ) >= 0, 'query param with numbers' );
-       assert.ok( queryString.indexOf( 'test1' ) >= 0, 'query param with null value is included' );
-       assert.ok( queryString.indexOf( 'test1=' ) === -1, 'query param with null value does not generate equals sign' );
-       assert.ok( queryString.indexOf( 'test2=value+%28escaped%29' ) >= 0, 'query param is url escaped' );
+               // Remove more query values, leaving an empty array
+               uri.query.m.splice( 0, 1 );
+               assert.equal( uri.toString(), 'http://www.example.com/dir/', 'empty array value is ommitted' );
+       } );
 
-       relativePath = uri.getRelativePath();
-       assert.ok( relativePath.indexOf( uri.path ) >= 0, 'path in relative path' );
-       assert.ok( relativePath.indexOf( uri.getQueryString() ) >= 0, 'query string in relative path' );
-       assert.ok( relativePath.indexOf( uri.fragment ) >= 0, 'fragement in relative path' );
-} );
+       QUnit.test( 'All-dressed URI with everything', 11, function ( assert ) {
+               var uri, queryString, relativePath;
 
-QUnit.test( 'Cloning', 6, function ( assert ) {
-       var original, clone;
+               uri = new mw.Uri( 'http://auth@www.example.com:81/dir/dir.2/index.htm?q1=0&&test1&test2=value+%28escaped%29#top' );
 
-       original = new mw.Uri( 'http://foo.example.org/index.php?one=1&two=2' );
-       clone = original.clone();
+               assert.deepEqual(
+                       {
+                               protocol: uri.protocol,
+                               user: uri.user,
+                               password: uri.password,
+                               host: uri.host,
+                               port: uri.port,
+                               path: uri.path,
+                               query: uri.query,
+                               fragment: uri.fragment
+                       },
+                       {
+                               protocol: 'http',
+                               user: 'auth',
+                               password: undefined,
+                               host: 'www.example.com',
+                               port: '81',
+                               path: '/dir/dir.2/index.htm',
+                               query: { q1: '0', test1: null, test2: 'value (escaped)' },
+                               fragment: 'top'
+                       },
+                       'basic object properties'
+               );
 
-       assert.deepEqual( clone, original, 'clone has equivalent properties' );
-       assert.equal( original.toString(), clone.toString(), 'toString matches original' );
+               assert.equal( uri.getUserInfo(), 'auth', 'user info' );
 
-       assert.notStrictEqual( clone, original, 'clone is a different object when compared by reference' );
+               assert.equal( uri.getAuthority(), 'auth@www.example.com:81', 'authority equal to auth@hostport' );
 
-       clone.host = 'bar.example.org';
-       assert.notEqual( original.host, clone.host, 'manipulating clone did not effect original' );
-       assert.notEqual( original.toString(), clone.toString(), 'Stringified url no longer matches original' );
+               assert.equal( uri.getHostPort(), 'www.example.com:81', 'hostport equal to host:port' );
 
-       clone.query.three = 3;
+               queryString = uri.getQueryString();
+               assert.ok( queryString.indexOf( 'q1=0' ) >= 0, 'query param with numbers' );
+               assert.ok( queryString.indexOf( 'test1' ) >= 0, 'query param with null value is included' );
+               assert.ok( queryString.indexOf( 'test1=' ) === -1, 'query param with null value does not generate equals sign' );
+               assert.ok( queryString.indexOf( 'test2=value+%28escaped%29' ) >= 0, 'query param is url escaped' );
 
-       assert.deepEqual(
-               original.query,
-               { 'one': '1', 'two': '2' },
-               'Properties is deep cloned (bug 37708)'
-       );
-} );
+               relativePath = uri.getRelativePath();
+               assert.ok( relativePath.indexOf( uri.path ) >= 0, 'path in relative path' );
+               assert.ok( relativePath.indexOf( uri.getQueryString() ) >= 0, 'query string in relative path' );
+               assert.ok( relativePath.indexOf( uri.fragment ) >= 0, 'fragement in relative path' );
+       } );
 
-QUnit.test( 'Constructing mw.Uri from plain object', 3, function ( assert ) {
-       var uri = new mw.Uri({
-               protocol: 'http',
-               host: 'www.foo.local',
-               path: '/this'
-       });
-       assert.equal( uri.toString(), 'http://www.foo.local/this', 'Basic properties' );
-
-       uri = new mw.Uri({
-               protocol: 'http',
-               host: 'www.foo.local',
-               path: '/this',
-               query: { hi: 'there' },
-               fragment: 'blah'
-       });
-       assert.equal( uri.toString(), 'http://www.foo.local/this?hi=there#blah', 'More complex properties' );
+       QUnit.test( 'Cloning', 6, function ( assert ) {
+               var original, clone;
 
-       assert.throws(
-               function () {
-                       var uri = new mw.Uri({
-                               protocol: 'http',
-                               host: 'www.foo.local'
-                       });
-               },
-               function ( e ) {
-                       return e.message === 'Bad constructor arguments';
-               },
-               'Construction failed when missing required properties'
-       );
-} );
+               original = new mw.Uri( 'http://foo.example.org/index.php?one=1&two=2' );
+               clone = original.clone();
 
-QUnit.test( 'Manipulate properties', 8, function ( assert ) {
-       var uriBase, uri;
+               assert.deepEqual( clone, original, 'clone has equivalent properties' );
+               assert.equal( original.toString(), clone.toString(), 'toString matches original' );
 
-       uriBase = new mw.Uri( 'http://en.wiki.local/w/api.php' );
+               assert.notStrictEqual( clone, original, 'clone is a different object when compared by reference' );
 
-       uri = uriBase.clone();
-       uri.fragment = 'frag';
-       assert.equal( uri.toString(), 'http://en.wiki.local/w/api.php#frag', 'add a fragment' );
+               clone.host = 'bar.example.org';
+               assert.notEqual( original.host, clone.host, 'manipulating clone did not effect original' );
+               assert.notEqual( original.toString(), clone.toString(), 'Stringified url no longer matches original' );
 
-       uri = uriBase.clone();
-       uri.host = 'fr.wiki.local';
-       uri.port = '8080';
-       assert.equal( uri.toString(), 'http://fr.wiki.local:8080/w/api.php', 'change host and port' );
+               clone.query.three = 3;
 
-       uri = uriBase.clone();
-       uri.query.foo = 'bar';
-       assert.equal( uri.toString(), 'http://en.wiki.local/w/api.php?foo=bar', 'add query arguments' );
+               assert.deepEqual(
+                       original.query,
+                       { 'one': '1', 'two': '2' },
+                       'Properties is deep cloned (bug 37708)'
+               );
+       } );
 
-       delete uri.query.foo;
-       assert.equal( uri.toString(), 'http://en.wiki.local/w/api.php', 'delete query arguments' );
+       QUnit.test( 'Constructing mw.Uri from plain object', 3, function ( assert ) {
+               var uri = new mw.Uri({
+                       protocol: 'http',
+                       host: 'www.foo.local',
+                       path: '/this'
+               });
+               assert.equal( uri.toString(), 'http://www.foo.local/this', 'Basic properties' );
 
-       uri = uriBase.clone();
-       uri.query.foo = 'bar';
-       assert.equal( uri.toString(), 'http://en.wiki.local/w/api.php?foo=bar', 'extend query arguments' );
-       uri.extend({
-               foo: 'quux',
-               pif: 'paf'
-       });
-       assert.ok( uri.toString().indexOf( 'foo=quux' ) >= 0, 'extend query arguments' );
-       assert.ok( uri.toString().indexOf( 'foo=bar' ) === -1, 'extend query arguments' );
-       assert.ok( uri.toString().indexOf( 'pif=paf' ) >= 0 , 'extend query arguments' );
-} );
+               uri = new mw.Uri({
+                       protocol: 'http',
+                       host: 'www.foo.local',
+                       path: '/this',
+                       query: { hi: 'there' },
+                       fragment: 'blah'
+               });
+               assert.equal( uri.toString(), 'http://www.foo.local/this?hi=there#blah', 'More complex properties' );
+
+               assert.throws(
+                       function () {
+                               return new mw.Uri({
+                                       protocol: 'http',
+                                       host: 'www.foo.local'
+                               });
+                       },
+                       function ( e ) {
+                               return e.message === 'Bad constructor arguments';
+                       },
+                       'Construction failed when missing required properties'
+               );
+       } );
 
-QUnit.test( 'Handle protocol-relative URLs', 5, function ( assert ) {
-       var UriRel, uri;
+       QUnit.test( 'Manipulate properties', 8, function ( assert ) {
+               var uriBase, uri;
 
-       UriRel = mw.UriRelative( 'glork://en.wiki.local/foo.php' );
+               uriBase = new mw.Uri( 'http://en.wiki.local/w/api.php' );
 
-       uri = new UriRel( '//en.wiki.local/w/api.php' );
-       assert.equal( uri.protocol, 'glork', 'create protocol-relative URLs with same protocol as document' );
+               uri = uriBase.clone();
+               uri.fragment = 'frag';
+               assert.equal( uri.toString(), 'http://en.wiki.local/w/api.php#frag', 'add a fragment' );
 
-       uri = new UriRel( '/foo.com' );
-       assert.equal( uri.toString(), 'glork://en.wiki.local/foo.com', 'handle absolute paths by supplying protocol and host from document in loose mode' );
+               uri = uriBase.clone();
+               uri.host = 'fr.wiki.local';
+               uri.port = '8080';
+               assert.equal( uri.toString(), 'http://fr.wiki.local:8080/w/api.php', 'change host and port' );
 
-       uri = new UriRel( 'http:/foo.com' );
-       assert.equal( uri.toString(), 'http://en.wiki.local/foo.com', 'handle absolute paths by supplying host from document in loose mode' );
+               uri = uriBase.clone();
+               uri.query.foo = 'bar';
+               assert.equal( uri.toString(), 'http://en.wiki.local/w/api.php?foo=bar', 'add query arguments' );
 
-       uri = new UriRel( '/foo.com', true );
-       assert.equal( uri.toString(), 'glork://en.wiki.local/foo.com', 'handle absolute paths by supplying protocol and host from document in strict mode' );
+               delete uri.query.foo;
+               assert.equal( uri.toString(), 'http://en.wiki.local/w/api.php', 'delete query arguments' );
 
-       uri = new UriRel( 'http:/foo.com', true );
-       assert.equal( uri.toString(), 'http://en.wiki.local/foo.com', 'handle absolute paths by supplying host from document in strict mode' );
-} );
+               uri = uriBase.clone();
+               uri.query.foo = 'bar';
+               assert.equal( uri.toString(), 'http://en.wiki.local/w/api.php?foo=bar', 'extend query arguments' );
+               uri.extend({
+                       foo: 'quux',
+                       pif: 'paf'
+               });
+               assert.ok( uri.toString().indexOf( 'foo=quux' ) >= 0, 'extend query arguments' );
+               assert.ok( uri.toString().indexOf( 'foo=bar' ) === -1, 'extend query arguments' );
+               assert.ok( uri.toString().indexOf( 'pif=paf' ) >= 0 , 'extend query arguments' );
+       } );
 
-QUnit.test( 'Bad calls', 3, function ( assert ) {
-       var uri;
+       QUnit.test( 'Handle protocol-relative URLs', 5, function ( assert ) {
+               var UriRel, uri;
 
-       assert.throws(
-               function () {
-                       new mw.Uri( 'glaswegian penguins' );
-               },
-               function ( e ) {
-                       return e.message === 'Bad constructor arguments';
-               },
-               'throw error on non-URI as argument to constructor'
-       );
+               UriRel = mw.UriRelative( 'glork://en.wiki.local/foo.php' );
 
-       assert.throws(
-               function () {
-                       new mw.Uri( 'foo.com/bar/baz', {
-                               strictMode: true
-                       });
-               },
-               function ( e ) {
-                       return e.message === 'Bad constructor arguments';
-               },
-               'throw error on URI without protocol or // or leading / in strict mode'
-       );
+               uri = new UriRel( '//en.wiki.local/w/api.php' );
+               assert.equal( uri.protocol, 'glork', 'create protocol-relative URLs with same protocol as document' );
+
+               uri = new UriRel( '/foo.com' );
+               assert.equal( uri.toString(), 'glork://en.wiki.local/foo.com', 'handle absolute paths by supplying protocol and host from document in loose mode' );
+
+               uri = new UriRel( 'http:/foo.com' );
+               assert.equal( uri.toString(), 'http://en.wiki.local/foo.com', 'handle absolute paths by supplying host from document in loose mode' );
+
+               uri = new UriRel( '/foo.com', true );
+               assert.equal( uri.toString(), 'glork://en.wiki.local/foo.com', 'handle absolute paths by supplying protocol and host from document in strict mode' );
+
+               uri = new UriRel( 'http:/foo.com', true );
+               assert.equal( uri.toString(), 'http://en.wiki.local/foo.com', 'handle absolute paths by supplying host from document in strict mode' );
+       } );
 
-       uri = new mw.Uri( 'foo.com/bar/baz', {
-               strictMode: false
+       QUnit.test( 'Bad calls', 3, function ( assert ) {
+               var uri;
+
+               assert.throws(
+                       function () {
+                               return new mw.Uri( 'glaswegian penguins' );
+                       },
+                       function ( e ) {
+                               return e.message === 'Bad constructor arguments';
+                       },
+                       'throw error on non-URI as argument to constructor'
+               );
+
+               assert.throws(
+                       function () {
+                               return new mw.Uri( 'foo.com/bar/baz', {
+                                       strictMode: true
+                               });
+                       },
+                       function ( e ) {
+                               return e.message === 'Bad constructor arguments';
+                       },
+                       'throw error on URI without protocol or // or leading / in strict mode'
+               );
+
+               uri = new mw.Uri( 'foo.com/bar/baz', {
+                       strictMode: false
+               });
+               assert.equal( uri.toString(), 'http://foo.com/bar/baz', 'normalize URI without protocol or // in loose mode' );
        });
-       assert.equal( uri.toString(), 'http://foo.com/bar/baz', 'normalize URI without protocol or // in loose mode' );
-});
 
-QUnit.test( 'bug 35658', 2, function ( assert ) {
-       var testProtocol, testServer, testPort, testPath, UriClass, uri, href;
+       QUnit.test( 'bug 35658', 2, function ( assert ) {
+               var testProtocol, testServer, testPort, testPath, UriClass, uri, href;
 
-       testProtocol = 'https://';
-       testServer = 'foo.example.org';
-       testPort = '3004';
-       testPath = '/!1qy';
+               testProtocol = 'https://';
+               testServer = 'foo.example.org';
+               testPort = '3004';
+               testPath = '/!1qy';
 
-       UriClass = mw.UriRelative( testProtocol + testServer + '/some/path/index.html' );
-       uri = new UriClass( testPath );
-       href = uri.toString();
-       assert.equal( href, testProtocol + testServer + testPath, 'Root-relative URL gets host & protocol supplied' );
+               UriClass = mw.UriRelative( testProtocol + testServer + '/some/path/index.html' );
+               uri = new UriClass( testPath );
+               href = uri.toString();
+               assert.equal( href, testProtocol + testServer + testPath, 'Root-relative URL gets host & protocol supplied' );
 
-       UriClass = mw.UriRelative( testProtocol + testServer + ':' + testPort + '/some/path.php' );
-       uri = new UriClass( testPath );
-       href = uri.toString();
-       assert.equal( href, testProtocol + testServer + ':' + testPort + testPath, 'Root-relative URL gets host, protocol, and port supplied' );
+               UriClass = mw.UriRelative( testProtocol + testServer + ':' + testPort + '/some/path.php' );
+               uri = new UriClass( testPath );
+               href = uri.toString();
+               assert.equal( href, testProtocol + testServer + ':' + testPort + testPath, 'Root-relative URL gets host, protocol, and port supplied' );
 
-} );
+       } );
 
-QUnit.test( 'Constructor falls back to default location', 4, function ( assert ) {
-       var testuri, MyUri, uri;
+       QUnit.test( 'Constructor falls back to default location', 4, function ( assert ) {
+               var testuri, MyUri, uri;
 
-       testuri = 'http://example.org/w/index.php';
-       MyUri = mw.UriRelative( testuri );
+               testuri = 'http://example.org/w/index.php';
+               MyUri = mw.UriRelative( testuri );
 
-       uri = new MyUri();
-       assert.equal( uri.toString(), testuri, 'no arguments' );
+               uri = new MyUri();
+               assert.equal( uri.toString(), testuri, 'no arguments' );
 
-       uri = new MyUri( undefined );
-       assert.equal( uri.toString(), testuri, 'undefined' );
+               uri = new MyUri( undefined );
+               assert.equal( uri.toString(), testuri, 'undefined' );
 
-       uri = new MyUri( null );
-       assert.equal( uri.toString(), testuri, 'null' );
+               uri = new MyUri( null );
+               assert.equal( uri.toString(), testuri, 'null' );
 
-       uri = new MyUri( '' );
-       assert.equal( uri.toString(), testuri, 'empty string' );
-} );
+               uri = new MyUri( '' );
+               assert.equal( uri.toString(), testuri, 'empty string' );
+       } );
+}( mediaWiki, jQuery ) );
index e2c6668..b745fb4 100644 (file)
@@ -1,74 +1,76 @@
-QUnit.module( 'mediawiki.cldr', QUnit.newMwEnvironment() );
+( function ( mw, $ ) {
+       QUnit.module( 'mediawiki.cldr', QUnit.newMwEnvironment() );
 
-var pluralTestcases = {
-       /*
-        * Sample:
-        * "languagecode" : [
-        *   [ number, [ "form1", "form2", ... ],  "expected", "description" ]
-        * ];
-        */
-       "en": [
-               [ 0, [ "one", "other" ], "other", "English plural test- 0 is other" ],
-               [ 1, [ "one", "other" ], "one", "English plural test- 1 is one" ]
-       ],
-       "fa": [
-               [ 0, [ "one", "other" ], "other", "Persian plural test- 0 is other" ],
-               [ 1, [ "one", "other" ], "one", "Persian plural test- 1 is one" ],
-               [ 2, [ "one", "other" ], "other", "Persian plural test- 2 is other" ]
-       ],
-       "fr": [
-               [ 0, [ "one", "other" ], "other", "French plural test- 0 is other" ],
-               [ 1, [ "one", "other" ], "one", "French plural test- 1 is one" ]
-       ],
-       "hi": [
-               [ 0, [ "one", "other" ], "one", "Hindi plural test- 0 is one" ],
-               [ 1, [ "one", "other" ], "one", "Hindi plural test- 1 is one" ],
-               [ 2, [ "one", "other" ], "other", "Hindi plural test- 2 is other" ]
-       ],
-       "he": [
-               [ 0, [ "one", "other" ], "other", "Hebrew plural test- 0 is other" ],
-               [ 1, [ "one", "other" ], "one", "Hebrew plural test- 1 is one" ],
-               [ 2, [ "one", "other" ], "other", "Hebrew plural test- 2 is other with 2 forms" ],
-               [ 2, [ "one", "dual", "other" ], "dual", "Hebrew plural test- 2 is dual with 3 forms" ]
-       ],
-       "hu": [
-               [ 0, [ "one", "other" ], "other", "Hungarian plural test- 0 is other" ],
-               [ 1, [ "one", "other" ], "one", "Hungarian plural test- 1 is one" ],
-               [ 2, [ "one", "other" ], "other", "Hungarian plural test- 2 is other" ]
-       ],
-       "ar": [
-               [ 0, [ "zero", "one", "two", "few", "many", "other" ], "zero", "Arabic plural test - 0 is zero" ],
-               [ 1, [ "zero", "one", "two", "few", "many", "other" ], "one", "Arabic plural test - 1 is one" ],
-               [ 2, [ "zero", "one", "two", "few", "many", "other" ], "two", "Arabic plural test - 2 is two" ],
-               [ 3, [ "zero", "one", "two", "few", "many", "other" ], "few", "Arabic plural test - 3 is few" ],
-               [ 9, [ "zero", "one", "two", "few", "many", "other" ], "few", "Arabic plural test - 9 is few" ],
-               [ "9", [ "zero", "one", "two", "few", "many", "other" ], "few", "Arabic plural test - 9 is few" ],
-               [ 110, [ "zero", "one", "two", "few", "many", "other" ], "few", "Arabic plural test - 110 is few" ],
-               [ 11, [ "zero", "one", "two", "few", "many", "other" ], "many", "Arabic plural test - 11 is many" ],
-               [ 15, [ "zero", "one", "two", "few", "many", "other" ], "many", "Arabic plural test - 15 is many" ],
-               [ 99, [ "zero", "one", "two", "few", "many", "other" ], "many", "Arabic plural test - 99 is many" ],
-               [ 9999, [ "zero", "one", "two", "few", "many", "other" ], "many", "Arabic plural test - 9999 is many" ],
-               [ 100, [ "zero", "one", "two", "few", "many", "other" ], "other", "Arabic plural test - 100 is other" ],
-               [ 102, [ "zero", "one", "two", "few", "many", "other" ], "other", "Arabic plural test - 102 is other" ],
-               [ 1000, [ "zero", "one", "two", "few", "many", "other" ], "other", "Arabic plural test - 1000 is other" ],
-               [ 1.7, [ "zero", "one", "two", "few", "many", "other" ], "other", "Arabic plural test - 1.7 is other" ]
-       ]
-};
+       var pluralTestcases = {
+               /*
+                * Sample:
+                * languagecode : [
+                *   [ number, [ 'form1', 'form2', ... ],  'expected', 'description' ]
+                * ];
+                */
+               en: [
+                       [ 0, [ 'one', 'other' ], 'other', 'English plural test- 0 is other' ],
+                       [ 1, [ 'one', 'other' ], 'one', 'English plural test- 1 is one' ]
+               ],
+               fa: [
+                       [ 0, [ 'one', 'other' ], 'other', 'Persian plural test- 0 is other' ],
+                       [ 1, [ 'one', 'other' ], 'one', 'Persian plural test- 1 is one' ],
+                       [ 2, [ 'one', 'other' ], 'other', 'Persian plural test- 2 is other' ]
+               ],
+               fr: [
+                       [ 0, [ 'one', 'other' ], 'other', 'French plural test- 0 is other' ],
+                       [ 1, [ 'one', 'other' ], 'one', 'French plural test- 1 is one' ]
+               ],
+               hi: [
+                       [ 0, [ 'one', 'other' ], 'one', 'Hindi plural test- 0 is one' ],
+                       [ 1, [ 'one', 'other' ], 'one', 'Hindi plural test- 1 is one' ],
+                       [ 2, [ 'one', 'other' ], 'other', 'Hindi plural test- 2 is other' ]
+               ],
+               he: [
+                       [ 0, [ 'one', 'other' ], 'other', 'Hebrew plural test- 0 is other' ],
+                       [ 1, [ 'one', 'other' ], 'one', 'Hebrew plural test- 1 is one' ],
+                       [ 2, [ 'one', 'other' ], 'other', 'Hebrew plural test- 2 is other with 2 forms' ],
+                       [ 2, [ 'one', 'dual', 'other' ], 'dual', 'Hebrew plural test- 2 is dual with 3 forms' ]
+               ],
+               hu: [
+                       [ 0, [ 'one', 'other' ], 'other', 'Hungarian plural test- 0 is other' ],
+                       [ 1, [ 'one', 'other' ], 'one', 'Hungarian plural test- 1 is one' ],
+                       [ 2, [ 'one', 'other' ], 'other', 'Hungarian plural test- 2 is other' ]
+               ],
+               ar: [
+                       [ 0, [ 'zero', 'one', 'two', 'few', 'many', 'other' ], 'zero', 'Arabic plural test - 0 is zero' ],
+                       [ 1, [ 'zero', 'one', 'two', 'few', 'many', 'other' ], 'one', 'Arabic plural test - 1 is one' ],
+                       [ 2, [ 'zero', 'one', 'two', 'few', 'many', 'other' ], 'two', 'Arabic plural test - 2 is two' ],
+                       [ 3, [ 'zero', 'one', 'two', 'few', 'many', 'other' ], 'few', 'Arabic plural test - 3 is few' ],
+                       [ 9, [ 'zero', 'one', 'two', 'few', 'many', 'other' ], 'few', 'Arabic plural test - 9 is few' ],
+                       [ '9', [ 'zero', 'one', 'two', 'few', 'many', 'other' ], 'few', 'Arabic plural test - 9 is few' ],
+                       [ 110, [ 'zero', 'one', 'two', 'few', 'many', 'other' ], 'few', 'Arabic plural test - 110 is few' ],
+                       [ 11, [ 'zero', 'one', 'two', 'few', 'many', 'other' ], 'many', 'Arabic plural test - 11 is many' ],
+                       [ 15, [ 'zero', 'one', 'two', 'few', 'many', 'other' ], 'many', 'Arabic plural test - 15 is many' ],
+                       [ 99, [ 'zero', 'one', 'two', 'few', 'many', 'other' ], 'many', 'Arabic plural test - 99 is many' ],
+                       [ 9999, [ 'zero', 'one', 'two', 'few', 'many', 'other' ], 'many', 'Arabic plural test - 9999 is many' ],
+                       [ 100, [ 'zero', 'one', 'two', 'few', 'many', 'other' ], 'other', 'Arabic plural test - 100 is other' ],
+                       [ 102, [ 'zero', 'one', 'two', 'few', 'many', 'other' ], 'other', 'Arabic plural test - 102 is other' ],
+                       [ 1000, [ 'zero', 'one', 'two', 'few', 'many', 'other' ], 'other', 'Arabic plural test - 1000 is other' ],
+                       [ 1.7, [ 'zero', 'one', 'two', 'few', 'many', 'other' ], 'other', 'Arabic plural test - 1.7 is other' ]
+               ]
+       };
 
-function pluralTest( langCode, tests ) {
-       QUnit.test( 'Plural Test for ' + langCode, tests.length, function ( assert ) {
-               for ( var i = 0; i < tests.length; i++ ) {
-                       assert.equal(
-                               mw.language.convertPlural( tests[i][0], tests[i][1] ),
-                               tests[i][2],
-                               tests[i][3]
-                       );
+       function pluralTest( langCode, tests ) {
+               QUnit.test( 'Plural Test for ' + langCode, tests.length, function ( assert ) {
+                       for ( var i = 0; i < tests.length; i++ ) {
+                               assert.equal(
+                                       mw.language.convertPlural( tests[i][0], tests[i][1] ),
+                                       tests[i][2],
+                                       tests[i][3]
+                               );
+                       }
+               } );
+       }
+
+       $.each( pluralTestcases, function ( langCode, tests ) {
+               if ( langCode === mw.config.get( 'wgUserLanguage' ) ) {
+                       pluralTest( langCode, tests );
                }
        } );
-}
-
-$.each( pluralTestcases, function ( langCode, tests ) {
-       if ( langCode === mw.config.get( 'wgUserLanguage' ) ) {
-               pluralTest( langCode, tests );
-       }
-} );
+}( mediaWiki, jQuery ) );
index 2baa4f3..bc6fafe 100644 (file)
@@ -1,62 +1,70 @@
-/* Some misc JavaScript compatibility tests, just to make sure the environments we run in are consistent */
-
-QUnit.module( 'mediawiki.jscompat', QUnit.newMwEnvironment() );
-
-QUnit.test( 'Variable with Unicode letter in name', 3, function ( assert ) {
-       var orig = "some token";
-       var ŝablono = orig;
-
-       assert.deepEqual( ŝablono, orig, 'ŝablono' );
-       assert.deepEqual( \u015dablono, orig, '\\u015dablono' );
-       assert.deepEqual( \u015Dablono, orig, '\\u015Dablono' );
-});
-
-/*
-// Not that we need this. ;)
-// This fails on IE 6-8
-// Works on IE 9, Firefox 6, Chrome 14
-QUnit.test( 'Keyword workaround: "if" as variable name using Unicode escapes', function ( assert ) {
-       var orig = "another token";
-       \u0069\u0066 = orig;
-       assert.deepEqual( \u0069\u0066, orig, '\\u0069\\u0066' );
-});
-*/
-
-/*
-// Not that we need this. ;)
-// This fails on IE 6-9
-// Works on Firefox 6, Chrome 14
-QUnit.test( 'Keyword workaround: "if" as member variable name using Unicode escapes', function ( assert ) {
-       var orig = "another token";
-       var foo = {};
-       foo.\u0069\u0066 = orig;
-       assert.deepEqual( foo.\u0069\u0066, orig, 'foo.\\u0069\\u0066' );
-});
-*/
-
-QUnit.test( 'Stripping of single initial newline from textarea\'s literal contents (bug 12130)', function ( assert ) {
-       var maxn = 4;
-       QUnit.expect( maxn * 2 );
-
-       function repeat( str, n ) {
-               if ( n <= 0 ) {
-                       return '';
-               } else {
-                       var out = new Array(n);
-                       for ( var i = 0; i < n; i++ ) {
-                               out[i] = str;
+/**
+ * Some misc JavaScript compatibility tests,
+ * just to make sure the environments we run in are consistent.
+ */
+( function ( $ ) {
+       QUnit.module( 'mediawiki.jscompat', QUnit.newMwEnvironment() );
+
+       QUnit.test( 'Variable with Unicode letter in name', 3, function ( assert ) {
+               var orig, ŝablono;
+
+               orig = 'some token';
+               ŝablono = orig;
+
+               assert.deepEqual( ŝablono, orig, 'ŝablono' );
+               assert.deepEqual( \u015dablono, orig, '\\u015dablono' );
+               assert.deepEqual( \u015Dablono, orig, '\\u015Dablono' );
+       });
+
+       /*
+       // Not that we need this. ;)
+       // This fails on IE 6-8
+       // Works on IE 9, Firefox 6, Chrome 14
+       QUnit.test( 'Keyword workaround: "if" as variable name using Unicode escapes', function ( assert ) {
+               var orig = "another token";
+               \u0069\u0066 = orig;
+               assert.deepEqual( \u0069\u0066, orig, '\\u0069\\u0066' );
+       });
+       */
+
+       /*
+       // Not that we need this. ;)
+       // This fails on IE 6-9
+       // Works on Firefox 6, Chrome 14
+       QUnit.test( 'Keyword workaround: "if" as member variable name using Unicode escapes', function ( assert ) {
+               var orig = "another token";
+               var foo = {};
+               foo.\u0069\u0066 = orig;
+               assert.deepEqual( foo.\u0069\u0066, orig, 'foo.\\u0069\\u0066' );
+       });
+       */
+
+       QUnit.test( 'Stripping of single initial newline from textarea\'s literal contents (bug 12130)', function ( assert ) {
+               var maxn, n,
+                       expected, $textarea;
+
+               maxn = 4;
+               QUnit.expect( maxn * 2 );
+
+               function repeat( str, n ) {
+                       var out;
+                       if ( n <= 0 ) {
+                               return '';
+                       } else {
+                               out = [];
+                               out.length = n + 1;
+                               return out.join( str );
                        }
-                       return out.join('');
                }
-       }
 
-       for ( var n = 0; n < maxn; n++ ) {
-               var expected = repeat('\n', n) + 'some text';
+               for ( n = 0; n < maxn; n++ ) {
+                       expected = repeat('\n', n) + 'some text';
 
-               var $textarea = $('<textarea>\n' + expected + '</textarea>');
-               assert.equal( $textarea.val(), expected, 'Expecting ' + n + ' newlines (HTML contained ' + (n + 1) + ')' );
+                       $textarea = $('<textarea>\n' + expected + '</textarea>');
+                       assert.equal( $textarea.val(), expected, 'Expecting ' + n + ' newlines (HTML contained ' + (n + 1) + ')' );
 
-               var $textarea2 = $('<textarea>').val(expected);
-               assert.equal( $textarea2.val(), expected, 'Expecting ' + n + ' newlines (from DOM set with ' + n + ')' );
-       }
-});
+                       $textarea = $('<textarea>').val( expected );
+                       assert.equal( $textarea.val(), expected, 'Expecting ' + n + ' newlines (from DOM set with ' + n + ')' );
+               }
+       });
+}( jQuery ) );
index 3fa2b09..869ebe4 100644 (file)
-QUnit.module( 'mediawiki.language', QUnit.newMwEnvironment({
-       setup: function () {
-               this.liveLangData = mw.language.data.values;
-               mw.language.data.values = $.extend( true, {}, this.liveLangData );
-       },
-       teardown: function () {
-               // Restore
-               mw.language.data.values = this.liveLangData;
-       }
-}) );
+( function ( mw, $ ) {
 
-QUnit.test( 'mw.language getData and setData', function ( assert ) {
-       QUnit.expect( 2 );
+       QUnit.module( 'mediawiki.language', QUnit.newMwEnvironment({
+               setup: function () {
+                       this.liveLangData = mw.language.data.values;
+                       mw.language.data.values = $.extend( true, {}, this.liveLangData );
+               },
+               teardown: function () {
+                       mw.language.data.values = this.liveLangData;
+               }
+       }) );
 
-       mw.language.setData( 'en', 'testkey', 'testvalue' );
-       assert.equal(  mw.language.getData( 'en', 'testkey' ), 'testvalue', 'Getter setter test for mw.language' );
-       assert.equal(  mw.language.getData( 'en', 'invalidkey' ), undefined, 'Getter setter test for mw.language with invalid key' );
-} );
+       QUnit.test( 'mw.language getData and setData', 2, function ( assert ) {
+               mw.language.setData( 'en', 'testkey', 'testvalue' );
+               assert.equal(  mw.language.getData( 'en', 'testkey' ), 'testvalue', 'Getter setter test for mw.language' );
+               assert.equal(  mw.language.getData( 'en', 'invalidkey' ), undefined, 'Getter setter test for mw.language with invalid key' );
+       } );
 
-function grammarTest( langCode, test ) {
-       // The test works only if the content language is opt.language
-       // because it requires [lang].js to be loaded.
-       QUnit.test( 'Grammar test for lang=' + langCode, function ( assert ) {
-               QUnit.expect( test.length );
+       function grammarTest( langCode, test ) {
+               // The test works only if the content language is opt.language
+               // because it requires [lang].js to be loaded.
+               QUnit.test( 'Grammar test for lang=' + langCode, function ( assert ) {
+                       QUnit.expect( test.length );
 
-               for ( var i = 0 ; i < test.length; i++ ) {
-                       assert.equal(
-                               mw.language.convertGrammar( test[i].word, test[i].grammarForm ),
-                               test[i].expected,
-                               test[i].description
-                       );
-               }
-       });
-}
+                       for ( var i = 0 ; i < test.length; i++ ) {
+                               assert.equal(
+                                       mw.language.convertGrammar( test[i].word, test[i].grammarForm ),
+                                       test[i].expected,
+                                       test[i].description
+                               );
+                       }
+               });
+       }
 
-var grammarTests = {
-       bs: [
-               {
-                       word: 'word',
-                       grammarForm: 'instrumental',
-                       expected: 's word',
-                       description: 'Grammar test for instrumental case'
-               },
-               {
-                       word: 'word',
-                       grammarForm: 'lokativ',
-                       expected: 'o word',
-                       description: 'Grammar test for lokativ case'
-               }
-       ],
+       var grammarTests = {
+               bs: [
+                       {
+                               word: 'word',
+                               grammarForm: 'instrumental',
+                               expected: 's word',
+                               description: 'Grammar test for instrumental case'
+                       },
+                       {
+                               word: 'word',
+                               grammarForm: 'lokativ',
+                               expected: 'o word',
+                               description: 'Grammar test for lokativ case'
+                       }
+               ],
 
-       he: [
-               {
-                       word: "ויקיפדיה",
-                       grammarForm: 'prefixed',
-                       expected: "וויקיפדיה",
-                       description: 'Duplicate the "Waw" if prefixed'
-               },
-               {
-                       word: "וולפגנג",
-                       grammarForm: 'prefixed',
-                       expected: "וולפגנג",
-                       description: 'Duplicate the "Waw" if prefixed, but not if it is already duplicated.'
-               },
-               {
-                       word: "הקובץ",
-                       grammarForm: 'prefixed',
-                       expected: "קובץ",
-                       description: 'Remove the "He" if prefixed'
-               },
-               {
-                       word: 'Wikipedia',
-                       grammarForm: 'תחילית',
-                       expected: '־Wikipedia',
-                       description: 'GAdd a hyphen (maqaf) before non-Hebrew letters'
-               },
-               {
-                       word: '1995',
-                       grammarForm: 'תחילית',
-                       expected: '־1995',
-                       description: 'Add a hyphen (maqaf) before numbers'
-               }
-       ],
+               he: [
+                       {
+                               word: 'ויקיפדיה',
+                               grammarForm: 'prefixed',
+                               expected: 'וויקיפדיה',
+                               description: 'Duplicate the "Waw" if prefixed'
+                       },
+                       {
+                               word: 'וולפגנג',
+                               grammarForm: 'prefixed',
+                               expected: 'וולפגנג',
+                               description: 'Duplicate the "Waw" if prefixed, but not if it is already duplicated.'
+                       },
+                       {
+                               word: 'הקובץ',
+                               grammarForm: 'prefixed',
+                               expected: 'קובץ',
+                               description: 'Remove the "He" if prefixed'
+                       },
+                       {
+                               word: 'Wikipedia',
+                               grammarForm: 'תחילית',
+                               expected: '־Wikipedia',
+                               description: 'GAdd a hyphen (maqaf) before non-Hebrew letters'
+                       },
+                       {
+                               word: '1995',
+                               grammarForm: 'תחילית',
+                               expected: '־1995',
+                               description: 'Add a hyphen (maqaf) before numbers'
+                       }
+               ],
 
-       hsb: [
-               {
-                       word: 'word',
-                       grammarForm: 'instrumental',
-                       expected: 'z word',
-                       description: 'Grammar test for instrumental case'
-               },
-               {
-                       word: 'word',
-                       grammarForm: 'lokatiw',
-                       expected: 'wo word',
-                       description: 'Grammar test for lokatiw case'
-               }
-       ],
+               hsb: [
+                       {
+                               word: 'word',
+                               grammarForm: 'instrumental',
+                               expected: 'z word',
+                               description: 'Grammar test for instrumental case'
+                       },
+                       {
+                               word: 'word',
+                               grammarForm: 'lokatiw',
+                               expected: 'wo word',
+                               description: 'Grammar test for lokatiw case'
+                       }
+               ],
 
-       dsb: [
-               {
-                       word: 'word',
-                       grammarForm: 'instrumental',
-                       expected: 'z word',
-                       description: 'Grammar test for instrumental case'
-               },
-               {
-                       word: 'word',
-                       grammarForm: 'lokatiw',
-                       expected: 'wo word',
-                       description: 'Grammar test for lokatiw case'
-               }
-       ],
+               dsb: [
+                       {
+                               word: 'word',
+                               grammarForm: 'instrumental',
+                               expected: 'z word',
+                               description: 'Grammar test for instrumental case'
+                       },
+                       {
+                               word: 'word',
+                               grammarForm: 'lokatiw',
+                               expected: 'wo word',
+                               description: 'Grammar test for lokatiw case'
+                       }
+               ],
 
-       hy: [
-               {
-                       word: 'Մաունա',
-                       grammarForm: 'genitive',
-                       expected: 'Մաունայի',
-                       description: 'Grammar test for genitive case'
-               },
-               {
-                       word: 'հետո',
-                       grammarForm: 'genitive',
-                       expected: 'հետոյի',
-                       description: 'Grammar test for genitive case'
-               },
-               {
-                       word: 'գիրք',
-                       grammarForm: 'genitive',
-                       expected: 'գրքի',
-                       description: 'Grammar test for genitive case'
-               },
-               {
-                       word: 'ժամանակի',
-                       grammarForm: 'genitive',
-                       expected: 'ժամանակիի',
-                       description: 'Grammar test for genitive case'
-               }
-       ],
+               hy: [
+                       {
+                               word: 'Մաունա',
+                               grammarForm: 'genitive',
+                               expected: 'Մաունայի',
+                               description: 'Grammar test for genitive case'
+                       },
+                       {
+                               word: 'հետո',
+                               grammarForm: 'genitive',
+                               expected: 'հետոյի',
+                               description: 'Grammar test for genitive case'
+                       },
+                       {
+                               word: 'գիրք',
+                               grammarForm: 'genitive',
+                               expected: 'գրքի',
+                               description: 'Grammar test for genitive case'
+                       },
+                       {
+                               word: 'ժամանակի',
+                               grammarForm: 'genitive',
+                               expected: 'ժամանակիի',
+                               description: 'Grammar test for genitive case'
+                       }
+               ],
 
-       fi: [
-               {
-                       word: 'talo',
-                       grammarForm: 'genitive',
-                       expected: 'talon',
-                       description: 'Grammar test for genitive case'
-               },
-               {
-                       word: 'linux',
-                       grammarForm: 'genitive',
-                       expected: 'linuxin',
-                       description: 'Grammar test for genitive case'
-               },
-               {
-                       word: 'talo',
-                       grammarForm: 'elative',
-                       expected: 'talosta',
-                       description: 'Grammar test for elative case'
-               },
-               {
-                       word: 'pastöroitu',
-                       grammarForm: 'partitive',
-                       expected: 'pastöroitua',
-                       description: 'Grammar test for partitive case'
-               },
-               {
-                       word: 'talo',
-                       grammarForm: 'partitive',
-                       expected: 'taloa',
-                       description: 'Grammar test for partitive case'
-               },
-               {
-                       word: 'talo',
-                       grammarForm: 'illative',
-                       expected: 'taloon',
-                       description: 'Grammar test for illative case'
-               },
-               {
-                       word: 'linux',
-                       grammarForm: 'inessive',
-                       expected: 'linuxissa',
-                       description: 'Grammar test for inessive case'
-               }
-       ],
+               fi: [
+                       {
+                               word: 'talo',
+                               grammarForm: 'genitive',
+                               expected: 'talon',
+                               description: 'Grammar test for genitive case'
+                       },
+                       {
+                               word: 'linux',
+                               grammarForm: 'genitive',
+                               expected: 'linuxin',
+                               description: 'Grammar test for genitive case'
+                       },
+                       {
+                               word: 'talo',
+                               grammarForm: 'elative',
+                               expected: 'talosta',
+                               description: 'Grammar test for elative case'
+                       },
+                       {
+                               word: 'pastöroitu',
+                               grammarForm: 'partitive',
+                               expected: 'pastöroitua',
+                               description: 'Grammar test for partitive case'
+                       },
+                       {
+                               word: 'talo',
+                               grammarForm: 'partitive',
+                               expected: 'taloa',
+                               description: 'Grammar test for partitive case'
+                       },
+                       {
+                               word: 'talo',
+                               grammarForm: 'illative',
+                               expected: 'taloon',
+                               description: 'Grammar test for illative case'
+                       },
+                       {
+                               word: 'linux',
+                               grammarForm: 'inessive',
+                               expected: 'linuxissa',
+                               description: 'Grammar test for inessive case'
+                       }
+               ],
 
-       ru: [
-               {
-                       word: 'тесть',
-                       grammarForm: 'genitive',
-                       expected: 'тестя',
-                       description: 'Grammar test for genitive case'
-               },
-               {
-                       word: 'привилегия',
-                       grammarForm: 'genitive',
-                       expected: 'привилегии',
-                       description: 'Grammar test for genitive case'
-               },
-               {
-                       word: 'установка',
-                       grammarForm: 'genitive',
-                       expected: 'установки',
-                       description: 'Grammar test for genitive case'
-               },
-               {
-                       word: 'похоти',
-                       grammarForm: 'genitive',
-                       expected: 'похотей',
-                       description: 'Grammar test for genitive case'
-               },
-               {
-                       word: 'доводы',
-                       grammarForm: 'genitive',
-                       expected: 'доводов',
-                       description: 'Grammar test for genitive case'
-               },
-               {
-                       word: 'песчаник',
-                       grammarForm: 'genitive',
-                       expected: 'песчаника',
-                       description: 'Grammar test for genitive case'
-               }
-       ],
+               ru: [
+                       {
+                               word: 'тесть',
+                               grammarForm: 'genitive',
+                               expected: 'тестя',
+                               description: 'Grammar test for genitive case'
+                       },
+                       {
+                               word: 'привилегия',
+                               grammarForm: 'genitive',
+                               expected: 'привилегии',
+                               description: 'Grammar test for genitive case'
+                       },
+                       {
+                               word: 'установка',
+                               grammarForm: 'genitive',
+                               expected: 'установки',
+                               description: 'Grammar test for genitive case'
+                       },
+                       {
+                               word: 'похоти',
+                               grammarForm: 'genitive',
+                               expected: 'похотей',
+                               description: 'Grammar test for genitive case'
+                       },
+                       {
+                               word: 'доводы',
+                               grammarForm: 'genitive',
+                               expected: 'доводов',
+                               description: 'Grammar test for genitive case'
+                       },
+                       {
+                               word: 'песчаник',
+                               grammarForm: 'genitive',
+                               expected: 'песчаника',
+                               description: 'Grammar test for genitive case'
+                       }
+               ],
 
 
-       hu: [
-               {
-                       word: 'Wikipédiá',
-                       grammarForm: 'rol',
-                       expected: 'Wikipédiáról',
-                       description: 'Grammar test for rol case'
-               },
-               {
-                       word: 'Wikipédiá',
-                       grammarForm: 'ba',
-                       expected: 'Wikipédiába',
-                       description: 'Grammar test for ba case'
-               },
-               {
-                       word: 'Wikipédiá',
-                       grammarForm: 'k',
-                       expected: 'Wikipédiák',
-                       description: 'Grammar test for k case'
-               }
-       ],
+               hu: [
+                       {
+                               word: 'Wikipédiá',
+                               grammarForm: 'rol',
+                               expected: 'Wikipédiáról',
+                               description: 'Grammar test for rol case'
+                       },
+                       {
+                               word: 'Wikipédiá',
+                               grammarForm: 'ba',
+                               expected: 'Wikipédiába',
+                               description: 'Grammar test for ba case'
+                       },
+                       {
+                               word: 'Wikipédiá',
+                               grammarForm: 'k',
+                               expected: 'Wikipédiák',
+                               description: 'Grammar test for k case'
+                       }
+               ],
 
-       ga: [
-               {
-                       word: 'an Domhnach',
-                       grammarForm: 'ainmlae',
-                       expected: 'Dé Domhnaigh',
-                       description: 'Grammar test for ainmlae case'
-               },
-               {
-                       word: 'an Luan',
-                       grammarForm: 'ainmlae',
-                       expected: 'Dé Luain',
-                       description: 'Grammar test for ainmlae case'
-               },
-               {
-                       word: 'an Satharn',
-                       grammarForm: 'ainmlae',
-                       expected: 'Dé Sathairn',
-                       description: 'Grammar test for ainmlae case'
-               }
-       ],
+               ga: [
+                       {
+                               word: 'an Domhnach',
+                               grammarForm: 'ainmlae',
+                               expected: 'Dé Domhnaigh',
+                               description: 'Grammar test for ainmlae case'
+                       },
+                       {
+                               word: 'an Luan',
+                               grammarForm: 'ainmlae',
+                               expected: 'Dé Luain',
+                               description: 'Grammar test for ainmlae case'
+                       },
+                       {
+                               word: 'an Satharn',
+                               grammarForm: 'ainmlae',
+                               expected: 'Dé Sathairn',
+                               description: 'Grammar test for ainmlae case'
+                       }
+               ],
 
-       uk: [
-               {
-                       word: 'тесть',
-                       grammarForm: 'genitive',
-                       expected: 'тестя',
-                       description: 'Grammar test for genitive case'
-               },
-               {
-                       word: 'Вікіпедія',
-                       grammarForm: 'genitive',
-                       expected: 'Вікіпедії',
-                       description: 'Grammar test for genitive case'
-               },
-               {
-                       word: 'установка',
-                       grammarForm: 'genitive',
-                       expected: 'установки',
-                       description: 'Grammar test for genitive case'
-               },
-               {
-                       word: 'похоти',
-                       grammarForm: 'genitive',
-                       expected: 'похотей',
-                       description: 'Grammar test for genitive case'
-               },
-               {
-                       word: 'доводы',
-                       grammarForm: 'genitive',
-                       expected: 'доводов',
-                       description: 'Grammar test for genitive case'
-               },
-               {
-                       word: 'песчаник',
-                       grammarForm: 'genitive',
-                       expected: 'песчаника',
-                       description: 'Grammar test for genitive case'
-               },
-               {
-                       word: 'Вікіпедія',
-                       grammarForm: 'accusative',
-                       expected: 'Вікіпедію',
-                       description: 'Grammar test for accusative case'
-               }
-       ],
+               uk: [
+                       {
+                               word: 'тесть',
+                               grammarForm: 'genitive',
+                               expected: 'тестя',
+                               description: 'Grammar test for genitive case'
+                       },
+                       {
+                               word: 'Вікіпедія',
+                               grammarForm: 'genitive',
+                               expected: 'Вікіпедії',
+                               description: 'Grammar test for genitive case'
+                       },
+                       {
+                               word: 'установка',
+                               grammarForm: 'genitive',
+                               expected: 'установки',
+                               description: 'Grammar test for genitive case'
+                       },
+                       {
+                               word: 'похоти',
+                               grammarForm: 'genitive',
+                               expected: 'похотей',
+                               description: 'Grammar test for genitive case'
+                       },
+                       {
+                               word: 'доводы',
+                               grammarForm: 'genitive',
+                               expected: 'доводов',
+                               description: 'Grammar test for genitive case'
+                       },
+                       {
+                               word: 'песчаник',
+                               grammarForm: 'genitive',
+                               expected: 'песчаника',
+                               description: 'Grammar test for genitive case'
+                       },
+                       {
+                               word: 'Вікіпедія',
+                               grammarForm: 'accusative',
+                               expected: 'Вікіпедію',
+                               description: 'Grammar test for accusative case'
+                       }
+               ],
 
-       sl: [
-               {
-                       word: 'word',
-                       grammarForm: 'orodnik',
-                       expected: 'z word',
-                       description: 'Grammar test for orodnik case'
-               },
-               {
-                       word: 'word',
-                       grammarForm: 'mestnik',
-                       expected: 'o word',
-                       description: 'Grammar test for mestnik case'
-               }
-       ],
+               sl: [
+                       {
+                               word: 'word',
+                               grammarForm: 'orodnik',
+                               expected: 'z word',
+                               description: 'Grammar test for orodnik case'
+                       },
+                       {
+                               word: 'word',
+                               grammarForm: 'mestnik',
+                               expected: 'o word',
+                               description: 'Grammar test for mestnik case'
+                       }
+               ],
 
-       os: [
-               {
-                       word: 'бæстæ',
-                       grammarForm: 'genitive',
-                       expected: 'бæсты',
-                       description: 'Grammar test for genitive case'
-               },
-               {
-                       word: 'бæстæ',
-                       grammarForm: 'allative',
-                       expected: 'бæстæм',
-                       description: 'Grammar test for allative case'
-               },
-               {
-                       word: 'Тигр',
-                       grammarForm: 'dative',
-                       expected: 'Тигрæн',
-                       description: 'Grammar test for dative case'
-               },
-               {
-                       word: 'цъити',
-                       grammarForm: 'dative',
-                       expected: 'цъитийæн',
-                       description: 'Grammar test for dative case'
-               },
-               {
-                       word: 'лæппу',
-                       grammarForm: 'genitive',
-                       expected: 'лæппуйы',
-                       description: 'Grammar test for genitive case'
-               },
-               {
-                       word: '2011',
-                       grammarForm: 'equative',
-                       expected: '2011-ау',
-                       description: 'Grammar test for equative case'
-               }
-       ],
+               os: [
+                       {
+                               word: 'бæстæ',
+                               grammarForm: 'genitive',
+                               expected: 'бæсты',
+                               description: 'Grammar test for genitive case'
+                       },
+                       {
+                               word: 'бæстæ',
+                               grammarForm: 'allative',
+                               expected: 'бæстæм',
+                               description: 'Grammar test for allative case'
+                       },
+                       {
+                               word: 'Тигр',
+                               grammarForm: 'dative',
+                               expected: 'Тигрæн',
+                               description: 'Grammar test for dative case'
+                       },
+                       {
+                               word: 'цъити',
+                               grammarForm: 'dative',
+                               expected: 'цъитийæн',
+                               description: 'Grammar test for dative case'
+                       },
+                       {
+                               word: 'лæппу',
+                               grammarForm: 'genitive',
+                               expected: 'лæппуйы',
+                               description: 'Grammar test for genitive case'
+                       },
+                       {
+                               word: '2011',
+                               grammarForm: 'equative',
+                               expected: '2011-ау',
+                               description: 'Grammar test for equative case'
+                       }
+               ],
 
-       la: [
-               {
-                       word: 'Translatio',
-                       grammarForm: 'genitive',
-                       expected: 'Translationis',
-                       description: 'Grammar test for genitive case'
-               },
-               {
-                       word: 'Translatio',
-                       grammarForm: 'accusative',
-                       expected: 'Translationem',
-                       description: 'Grammar test for accusative case'
-               },
-               {
-                       word: 'Translatio',
-                       grammarForm: 'ablative',
-                       expected: 'Translatione',
-                       description: 'Grammar test for ablative case'
-               }
-       ]
-};
+               la: [
+                       {
+                               word: 'Translatio',
+                               grammarForm: 'genitive',
+                               expected: 'Translationis',
+                               description: 'Grammar test for genitive case'
+                       },
+                       {
+                               word: 'Translatio',
+                               grammarForm: 'accusative',
+                               expected: 'Translationem',
+                               description: 'Grammar test for accusative case'
+                       },
+                       {
+                               word: 'Translatio',
+                               grammarForm: 'ablative',
+                               expected: 'Translatione',
+                               description: 'Grammar test for ablative case'
+                       }
+               ]
+       };
 
-$.each( grammarTests, function ( langCode, test ) {
-       if ( langCode === mw.config.get( 'wgUserLanguage' ) ) {
-               grammarTest( langCode, test );
-       }
-});
+       $.each( grammarTests, function ( langCode, test ) {
+               if ( langCode === mw.config.get( 'wgUserLanguage' ) ) {
+                       grammarTest( langCode, test );
+               }
+       });
+}( mediaWiki, jQuery ) );
index be59f1a..9286558 100644 (file)
@@ -1,4 +1,4 @@
-( function ( mw ) {
+( function ( mw, $ ) {
 
 QUnit.module( 'mediawiki', QUnit.newMwEnvironment() );
 
@@ -215,7 +215,7 @@ QUnit.asyncTest( 'mw.loader', 2, function ( assert ) {
 
                // /sample/awesome.js declares the "mw.loader.testCallback" function
                // which contains a call to start() and ok()
-               assert.strictEqual( isAwesomeDone, true, "test.callback module should've caused isAwesomeDone to be true" );
+               assert.strictEqual( isAwesomeDone, true, 'test.callback module should\'ve caused isAwesomeDone to be true' );
                delete mw.loader.testCallback;
 
        }, function () {
@@ -481,8 +481,8 @@ QUnit.test( 'mw.loader missing dependency', 13, function ( assert ) {
        mw.loader.using(
                ['test.module7'],
                function () {
-                       assert.ok( false, "Success fired despite missing dependency" );
-                       assert.ok( true , "QUnit expected() count dummy" );
+                       assert.ok( false, 'Success fired despite missing dependency' );
+                       assert.ok( true , 'QUnit expected() count dummy' );
                },
                function ( e, dependencies ) {
                        assert.strictEqual( $.isArray( dependencies ), true, 'Expected array of dependencies' );
@@ -492,8 +492,8 @@ QUnit.test( 'mw.loader missing dependency', 13, function ( assert ) {
        mw.loader.using(
                ['test.module9'],
                function () {
-                       assert.ok( false, "Success fired despite missing dependency" );
-                       assert.ok( true , "QUnit expected() count dummy" );
+                       assert.ok( false, 'Success fired despite missing dependency' );
+                       assert.ok( true , 'QUnit expected() count dummy' );
                },
                function ( e, dependencies ) {
                        assert.strictEqual( $.isArray( dependencies ), true, 'Expected array of dependencies' );
@@ -646,4 +646,4 @@ QUnit.test( 'mw.html', 13, function ( assert ) {
 
 });
 
-}( mediaWiki ) );
+}( mediaWiki, jQuery ) );
index 16c97df..a9bbbf7 100644 (file)
@@ -1,4 +1,4 @@
-( function ( mw ) {
+( function ( mw, $ ) {
 
 QUnit.module( 'mediawiki.user', QUnit.newMwEnvironment() );
 
@@ -47,10 +47,9 @@ QUnit.asyncTest( 'getGroups', 3, function ( assert ) {
 
 QUnit.asyncTest( 'getRights', 1, function ( assert ) {
        mw.user.getRights( function ( rights ) {
-               // First group should always be '*'
                assert.equal( $.type( rights ), 'array', 'Callback gets an array' );
                QUnit.start();
        });
 });
 
-}( mediaWiki ) );
+}( mediaWiki, jQuery ) );
index ababa8d..c4212df 100644 (file)
-QUnit.module( 'mediawiki.util', QUnit.newMwEnvironment() );
-
-QUnit.test( 'rawurlencode', 1, function ( assert ) {
-       assert.equal( mw.util.rawurlencode( 'Test:A & B/Here' ), 'Test%3AA%20%26%20B%2FHere' );
-});
-
-QUnit.test( 'wikiUrlencode', 1, function ( assert ) {
-       assert.equal( mw.util.wikiUrlencode( 'Test:A & B/Here' ), 'Test:A_%26_B/Here' );
-});
-
-QUnit.test( 'wikiGetlink', 3, function ( assert ) {
-       // Not part of startUp module
-       mw.config.set( 'wgArticlePath', '/wiki/$1' );
-       mw.config.set( 'wgPageName', 'Foobar' );
-
-       var hrefA = mw.util.wikiGetlink( 'Sandbox' );
-       assert.equal( hrefA, '/wiki/Sandbox', 'Simple title; Get link for "Sandbox"' );
-
-       var hrefB = mw.util.wikiGetlink( 'Foo:Sandbox ? 5+5=10 ! (test)/subpage' );
-       assert.equal( hrefB, '/wiki/Foo:Sandbox_%3F_5%2B5%3D10_%21_%28test%29/subpage',
-               'Advanced title; Get link for "Foo:Sandbox ? 5+5=10 ! (test)/subpage"' );
-
-       var hrefC = mw.util.wikiGetlink();
-       assert.equal( hrefC, '/wiki/Foobar', 'Default title; Get link for current page ("Foobar")' );
-});
-
-QUnit.test( 'wikiScript', 4, function ( assert ) {
-       mw.config.set({
-               'wgScript': '/w/i.php', // customized wgScript for bug 39103
-               'wgLoadScript': '/w/l.php', // customized wgLoadScript for bug 39103
-               'wgScriptPath': '/w',
-               'wgScriptExtension': '.php'
+( function ( mw, $ ) {
+       QUnit.module( 'mediawiki.util', QUnit.newMwEnvironment() );
+
+       QUnit.test( 'rawurlencode', 1, function ( assert ) {
+               assert.equal( mw.util.rawurlencode( 'Test:A & B/Here' ), 'Test%3AA%20%26%20B%2FHere' );
+       });
+
+       QUnit.test( 'wikiUrlencode', 1, function ( assert ) {
+               assert.equal( mw.util.wikiUrlencode( 'Test:A & B/Here' ), 'Test:A_%26_B/Here' );
        });
 
-       assert.equal( mw.util.wikiScript(), mw.config.get( 'wgScript' ), 'wikiScript() returns wgScript' );
-       assert.equal( mw.util.wikiScript( 'index' ), mw.config.get( 'wgScript' ), "wikiScript( 'index' ) returns wgScript" );
-       assert.equal( mw.util.wikiScript( 'load' ), mw.config.get( 'wgLoadScript' ), "wikiScript( 'load' ) returns wgLoadScript" );
-       assert.equal( mw.util.wikiScript( 'api' ), '/w/api.php', 'API path' );
-});
-
-QUnit.test( 'addCSS', 3, function ( assert ) {
-       var $testEl = $( '<div>' ).attr( 'id', 'mw-addcsstest' ).appendTo( '#qunit-fixture' );
-
-       var style = mw.util.addCSS( '#mw-addcsstest { visibility: hidden; }' );
-       assert.equal( typeof style, 'object', 'addCSS returned an object' );
-       assert.strictEqual( style.disabled, false, 'property "disabled" is available and set to false' );
-
-       assert.equal( $testEl.css( 'visibility' ), 'hidden', 'Added style properties are in effect' );
-
-       // Clean up
-       $( style.ownerNode ).remove();
-});
-
-QUnit.asyncTest( 'toggleToc', 4, function ( assert ) {
-       assert.strictEqual( mw.util.toggleToc(), null, 'Return null if there is no table of contents on the page.' );
-
-       var     tocHtml =
-       '<table id="toc" class="toc"><tr><td>' +
-               '<div id="toctitle">' +
-                       '<h2>Contents</h2>' +
-                       '<span class="toctoggle">&nbsp;[<a href="#" class="internal" id="togglelink">Hide</a>&nbsp;]</span>' +
-               '</div>' +
-               '<ul><li></li></ul>' +
-       '</td></tr></table>',
-               $toc = $(tocHtml).appendTo( '#qunit-fixture' ),
+       QUnit.test( 'wikiGetlink', 3, function ( assert ) {
+               // Not part of startUp module
+               mw.config.set( 'wgArticlePath', '/wiki/$1' );
+               mw.config.set( 'wgPageName', 'Foobar' );
+
+               var href = mw.util.wikiGetlink( 'Sandbox' );
+               assert.equal( href, '/wiki/Sandbox', 'Simple title; Get link for "Sandbox"' );
+
+               href = mw.util.wikiGetlink( 'Foo:Sandbox ? 5+5=10 ! (test)/subpage' );
+               assert.equal( href, '/wiki/Foo:Sandbox_%3F_5%2B5%3D10_%21_%28test%29/subpage',
+                       'Advanced title; Get link for "Foo:Sandbox ? 5+5=10 ! (test)/subpage"' );
+
+               href = mw.util.wikiGetlink();
+               assert.equal( href, '/wiki/Foobar', 'Default title; Get link for current page ("Foobar")' );
+       });
+
+       QUnit.test( 'wikiScript', 4, function ( assert ) {
+               mw.config.set({
+                       'wgScript': '/w/i.php', // customized wgScript for bug 39103
+                       'wgLoadScript': '/w/l.php', // customized wgLoadScript for bug 39103
+                       'wgScriptPath': '/w',
+                       'wgScriptExtension': '.php'
+               });
+
+               assert.equal( mw.util.wikiScript(), mw.config.get( 'wgScript' ),
+                       'wikiScript() returns wgScript'
+               );
+               assert.equal( mw.util.wikiScript( 'index' ), mw.config.get( 'wgScript' ),
+                       'wikiScript( index ) returns wgScript'
+               );
+               assert.equal( mw.util.wikiScript( 'load' ), mw.config.get( 'wgLoadScript' ),
+                       'wikiScript( load ) returns wgLoadScript'
+               );
+               assert.equal( mw.util.wikiScript( 'api' ), '/w/api.php', 'API path' );
+       });
+
+       QUnit.test( 'addCSS', 3, function ( assert ) {
+               var $el, style;
+               $el = $( '<div>' ).attr( 'id', 'mw-addcsstest' ).appendTo( '#qunit-fixture' );
+
+               style = mw.util.addCSS( '#mw-addcsstest { visibility: hidden; }' );
+               assert.equal( typeof style, 'object', 'addCSS returned an object' );
+               assert.strictEqual( style.disabled, false, 'property "disabled" is available and set to false' );
+
+               assert.equal( $el.css( 'visibility' ), 'hidden', 'Added style properties are in effect' );
+
+               // Clean up
+               $( style.ownerNode ).remove();
+       });
+
+       QUnit.asyncTest( 'toggleToc', 4, function ( assert ) {
+               var tocHtml, $toggleLink;
+
+               function actionC() {
+                       QUnit.start();
+               }
+
+               function actionB() {
+                       assert.strictEqual( mw.util.toggleToc( $toggleLink, actionC ), true, 'Return boolean true if the TOC is now visible.' );
+               }
+
+               function actionA() {
+                       assert.strictEqual( mw.util.toggleToc( $toggleLink, actionB ), false, 'Return boolean false if the TOC is now hidden.' );
+               }
+
+               assert.strictEqual( mw.util.toggleToc(), null, 'Return null if there is no table of contents on the page.' );
+
+               tocHtml =
+                       '<table id="toc" class="toc"><tr><td>' +
+                               '<div id="toctitle">' +
+                                       '<h2>Contents</h2>' +
+                                       '<span class="toctoggle">&nbsp;[<a href="#" class="internal" id="togglelink">Hide</a>&nbsp;]</span>' +
+                               '</div>' +
+                               '<ul><li></li></ul>' +
+                       '</td></tr></table>';
+               $(tocHtml).appendTo( '#qunit-fixture' ),
                $toggleLink = $( '#togglelink' );
 
-       assert.strictEqual( $toggleLink.length, 1, 'Toggle link is appended to the page.' );
-
-       var actionC = function() {
-               QUnit.start();
-       };
-       var actionB = function() {
-               assert.strictEqual( mw.util.toggleToc( $toggleLink, actionC ), true, 'Return boolean true if the TOC is now visible.' );
-       };
-       var actionA = function() {
-               assert.strictEqual( mw.util.toggleToc( $toggleLink, actionB ), false, 'Return boolean false if the TOC is now hidden.' );
-       };
-
-       actionA();
-});
-
-QUnit.test( 'getParamValue', 5, function ( assert ) {
-       var     url1 = 'http://example.org/?foo=wrong&foo=right#&foo=bad';
-
-       assert.equal( mw.util.getParamValue( 'foo', url1 ), 'right', 'Use latest one, ignore hash' );
-       assert.strictEqual( mw.util.getParamValue( 'bar', url1 ), null, 'Return null when not found' );
-
-       var url2 = 'http://example.org/#&foo=bad';
-       assert.strictEqual( mw.util.getParamValue( 'foo', url2 ), null, 'Ignore hash if param is not in querystring but in hash (bug 27427)' );
-
-       var url3 = 'example.org?' + $.param({ 'TEST': 'a b+c' });
-       assert.strictEqual( mw.util.getParamValue( 'TEST', url3 ), 'a b+c', 'Bug 30441: getParamValue must understand "+" encoding of space' );
-
-       var url4 = 'example.org?' + $.param({ 'TEST': 'a b+c d' }); // check for sloppy code from r95332 :)
-       assert.strictEqual( mw.util.getParamValue( 'TEST', url4 ), 'a b+c d', 'Bug 30441: getParamValue must understand "+" encoding of space (multiple spaces)' );
-});
-
-QUnit.test( 'tooltipAccessKey', 3, function ( assert ) {
-       assert.equal( typeof mw.util.tooltipAccessKeyPrefix, 'string', 'mw.util.tooltipAccessKeyPrefix must be a string' );
-       assert.ok( mw.util.tooltipAccessKeyRegexp instanceof RegExp, 'mw.util.tooltipAccessKeyRegexp instance of RegExp' );
-       assert.ok( mw.util.updateTooltipAccessKeys, 'mw.util.updateTooltipAccessKeys' );
-});
-
-QUnit.test( '$content', 2, function ( assert ) {
-       assert.ok( mw.util.$content instanceof jQuery, 'mw.util.$content instance of jQuery' );
-       assert.strictEqual( mw.util.$content.length, 1, 'mw.util.$content must have length of 1' );
-});
-
-
-/**
- * Portlet names are prefixed with 'p-test' to avoid conflict with core
- * when running the test suite under a wiki page.
- * Previously, test elements where invisible to the selector since only
- * one element can have a given id.
- */
-QUnit.test( 'addPortletLink', 8, function ( assert ) {
-       var pTestTb, pCustom, vectorTabs, tbRL, cuQuux, $cuQuux, tbMW, $tbMW, tbRLDM, caFoo;
-       pTestTb = '\
-       <div class="portlet" id="p-test-tb">\
-               <h5>Toolbox</h5>\
-               <ul class="body"></ul>\
-       </div>';
-       pCustom = '\
-       <div class="portlet" id="p-test-custom">\
-               <h5>Views</h5>\
-               <ul class="body">\
-                       <li id="c-foo"><a href="#">Foo</a></li>\
-                       <li id="c-barmenu">\
-                               <ul>\
-                                       <li id="c-bar-baz"><a href="#">Baz</a></a>\
-                               </ul>\
-                       </li>\
-               </ul>\
-       </div>';
-       vectorTabs = '\
-       <div id="p-test-views" class="vectorTabs">\
-               <h5>Views</h5>\
-               <ul></ul>\
-       </div>';
-
-       $( '#qunit-fixture' ).append( pTestTb, pCustom, vectorTabs );
-
-       tbRL = mw.util.addPortletLink( 'p-test-tb', '//mediawiki.org/wiki/ResourceLoader',
-               'ResourceLoader', 't-rl', 'More info about ResourceLoader on MediaWiki.org ', 'l' );
-
-       assert.ok( $.isDomElement( tbRL ), 'addPortletLink returns a valid DOM Element according to $.isDomElement' );
-
-       tbMW = mw.util.addPortletLink( 'p-test-tb', '//mediawiki.org/',
-               'MediaWiki.org', 't-mworg', 'Go to MediaWiki.org ', 'm', tbRL );
-       $tbMW = $( tbMW );
-
-
-       assert.equal( $tbMW.attr( 'id' ), 't-mworg', 'Link has correct ID set' );
-       assert.equal( $tbMW.closest( '.portlet' ).attr( 'id' ), 'p-test-tb', 'Link was inserted within correct portlet' );
-       assert.equal( $tbMW.next().attr( 'id' ), 't-rl', 'Link is in the correct position (by passing nextnode)' );
-
-       cuQuux = mw.util.addPortletLink( 'p-test-custom', '#', 'Quux' );
-       $cuQuux = $(cuQuux);
-
-       assert.equal(
-               $( '#p-test-custom #c-barmenu ul li' ).length,
-               1,
-               'addPortletLink did not add the item to all <ul> elements in the portlet (bug 35082)'
-       );
-
-       tbRLDM = mw.util.addPortletLink( 'p-test-tb', '//mediawiki.org/wiki/RL/DM',
-               'Default modules', 't-rldm', 'List of all default modules ', 'd', '#t-rl' );
-
-       assert.equal( $( tbRLDM ).next().attr( 'id' ), 't-rl', 'Link is in the correct position (by passing CSS selector)' );
-
-       caFoo = mw.util.addPortletLink( 'p-test-views', '#', 'Foo' );
-
-       assert.strictEqual( $tbMW.find( 'span').length, 0, 'No <span> element should be added for porlets without vectorTabs class.' );
-       assert.strictEqual( $( caFoo ).find( 'span').length, 1, 'A <span> element should be added for porlets with vectorTabs class.' );
-});
-
-QUnit.test( 'jsMessage', 1, function ( assert ) {
-       var a = mw.util.jsMessage( "MediaWiki is <b>Awesome</b>." );
-       assert.ok( a, 'Basic checking of return value' );
-
-       // Clean up
-       $( '#mw-js-message' ).remove();
-});
-
-QUnit.test( 'validateEmail', 6, function ( assert ) {
-       assert.strictEqual( mw.util.validateEmail( "" ), null, 'Should return null for empty string ' );
-       assert.strictEqual( mw.util.validateEmail( "user@localhost" ), true, 'Return true for a valid e-mail address' );
-
-       // testEmailWithCommasAreInvalids
-       assert.strictEqual( mw.util.validateEmail( "user,foo@example.org" ), false, 'Emails with commas are invalid' );
-       assert.strictEqual( mw.util.validateEmail( "userfoo@ex,ample.org" ), false, 'Emails with commas are invalid' );
-
-       // testEmailWithHyphens
-       assert.strictEqual( mw.util.validateEmail( "user-foo@example.org" ), true, 'Emails may contain a hyphen' );
-       assert.strictEqual( mw.util.validateEmail( "userfoo@ex-ample.org" ), true, 'Emails may contain a hyphen' );
-});
-
-QUnit.test( 'isIPv6Address', 40, function ( assert ) {
-       // Shortcuts
-       function assertFalseIPv6( addy, summary ) {
-               return assert.strictEqual( mw.util.isIPv6Address( addy ), false, summary );
-       }
-       function assertTrueIPv6( addy, summary ) {
-               return assert.strictEqual( mw.util.isIPv6Address( addy ), true, summary );
-       }
-
-       // Based on IPTest.php > testisIPv6
-       assertFalseIPv6( ':fc:100::', 'IPv6 starting with lone ":"' );
-       assertFalseIPv6( 'fc:100:::', 'IPv6 ending with a ":::"' );
-       assertFalseIPv6( 'fc:300', 'IPv6 with only 2 words' );
-       assertFalseIPv6( 'fc:100:300', 'IPv6 with only 3 words' );
-
-       $.each(
-       ['fc:100::',
-       'fc:100:a::',
-       'fc:100:a:d::',
-       'fc:100:a:d:1::',
-       'fc:100:a:d:1:e::',
-       'fc:100:a:d:1:e:ac::'], function ( i, addy ){
-               assertTrueIPv6( addy, addy + ' is a valid IP' );
+               assert.strictEqual( $toggleLink.length, 1, 'Toggle link is appended to the page.' );
+
+               actionA();
        });
 
-       assertFalseIPv6( 'fc:100:a:d:1:e:ac:0::', 'IPv6 with 8 words ending with "::"' );
-       assertFalseIPv6( 'fc:100:a:d:1:e:ac:0:1::', 'IPv6 with 9 words ending with "::"' );
-
-       assertFalseIPv6( ':::' );
-       assertFalseIPv6( '::0:', 'IPv6 ending in a lone ":"' );
-
-       assertTrueIPv6( '::', 'IPv6 zero address' );
-       $.each(
-       ['::0',
-       '::fc',
-       '::fc:100',
-       '::fc:100:a',
-       '::fc:100:a:d',
-       '::fc:100:a:d:1',
-       '::fc:100:a:d:1:e',
-       '::fc:100:a:d:1:e:ac',
-
-       'fc:100:a:d:1:e:ac:0'], function ( i, addy ){
-               assertTrueIPv6( addy, addy + ' is a valid IP' );
+       QUnit.test( 'getParamValue', 5, function ( assert ) {
+               var     url;
+
+               url = 'http://example.org/?foo=wrong&foo=right#&foo=bad';
+               assert.equal( mw.util.getParamValue( 'foo', url ), 'right', 'Use latest one, ignore hash' );
+               assert.strictEqual( mw.util.getParamValue( 'bar', url ), null, 'Return null when not found' );
+
+               url = 'http://example.org/#&foo=bad';
+               assert.strictEqual( mw.util.getParamValue( 'foo', url ), null, 'Ignore hash if param is not in querystring but in hash (bug 27427)' );
+
+               url = 'example.org?' + $.param({ 'TEST': 'a b+c' });
+               assert.strictEqual( mw.util.getParamValue( 'TEST', url ), 'a b+c', 'Bug 30441: getParamValue must understand "+" encoding of space' );
+
+               url = 'example.org?' + $.param({ 'TEST': 'a b+c d' }); // check for sloppy code from r95332 :)
+               assert.strictEqual( mw.util.getParamValue( 'TEST', url ), 'a b+c d', 'Bug 30441: getParamValue must understand "+" encoding of space (multiple spaces)' );
+       });
+
+       QUnit.test( 'tooltipAccessKey', 3, function ( assert ) {
+               assert.equal( typeof mw.util.tooltipAccessKeyPrefix, 'string', 'mw.util.tooltipAccessKeyPrefix must be a string' );
+               assert.ok( mw.util.tooltipAccessKeyRegexp instanceof RegExp, 'mw.util.tooltipAccessKeyRegexp instance of RegExp' );
+               assert.ok( mw.util.updateTooltipAccessKeys, 'mw.util.updateTooltipAccessKeys' );
+       });
+
+       QUnit.test( '$content', 2, function ( assert ) {
+               assert.ok( mw.util.$content instanceof jQuery, 'mw.util.$content instance of jQuery' );
+               assert.strictEqual( mw.util.$content.length, 1, 'mw.util.$content must have length of 1' );
+       });
+
+
+       /**
+        * Portlet names are prefixed with 'p-test' to avoid conflict with core
+        * when running the test suite under a wiki page.
+        * Previously, test elements where invisible to the selector since only
+        * one element can have a given id.
+        */
+       QUnit.test( 'addPortletLink', 8, function ( assert ) {
+               var pTestTb, pCustom, vectorTabs, tbRL, cuQuux, $cuQuux, tbMW, $tbMW, tbRLDM, caFoo;
+
+               pTestTb = '\
+               <div class="portlet" id="p-test-tb">\
+                       <h5>Toolbox</h5>\
+                       <ul class="body"></ul>\
+               </div>';
+               pCustom = '\
+               <div class="portlet" id="p-test-custom">\
+                       <h5>Views</h5>\
+                       <ul class="body">\
+                               <li id="c-foo"><a href="#">Foo</a></li>\
+                               <li id="c-barmenu">\
+                                       <ul>\
+                                               <li id="c-bar-baz"><a href="#">Baz</a></a>\
+                                       </ul>\
+                               </li>\
+                       </ul>\
+               </div>';
+               vectorTabs = '\
+               <div id="p-test-views" class="vectorTabs">\
+                       <h5>Views</h5>\
+                       <ul></ul>\
+               </div>';
+
+               $( '#qunit-fixture' ).append( pTestTb, pCustom, vectorTabs );
+
+               tbRL = mw.util.addPortletLink( 'p-test-tb', '//mediawiki.org/wiki/ResourceLoader',
+                       'ResourceLoader', 't-rl', 'More info about ResourceLoader on MediaWiki.org ', 'l' );
+
+               assert.ok( $.isDomElement( tbRL ), 'addPortletLink returns a valid DOM Element according to $.isDomElement' );
+
+               tbMW = mw.util.addPortletLink( 'p-test-tb', '//mediawiki.org/',
+                       'MediaWiki.org', 't-mworg', 'Go to MediaWiki.org ', 'm', tbRL );
+               $tbMW = $( tbMW );
+
+
+               assert.equal( $tbMW.attr( 'id' ), 't-mworg', 'Link has correct ID set' );
+               assert.equal( $tbMW.closest( '.portlet' ).attr( 'id' ), 'p-test-tb', 'Link was inserted within correct portlet' );
+               assert.equal( $tbMW.next().attr( 'id' ), 't-rl', 'Link is in the correct position (by passing nextnode)' );
+
+               cuQuux = mw.util.addPortletLink( 'p-test-custom', '#', 'Quux' );
+               $cuQuux = $(cuQuux);
+
+               assert.equal(
+                       $( '#p-test-custom #c-barmenu ul li' ).length,
+                       1,
+                       'addPortletLink did not add the item to all <ul> elements in the portlet (bug 35082)'
+               );
+
+               tbRLDM = mw.util.addPortletLink( 'p-test-tb', '//mediawiki.org/wiki/RL/DM',
+                       'Default modules', 't-rldm', 'List of all default modules ', 'd', '#t-rl' );
+
+               assert.equal( $( tbRLDM ).next().attr( 'id' ), 't-rl', 'Link is in the correct position (by passing CSS selector)' );
+
+               caFoo = mw.util.addPortletLink( 'p-test-views', '#', 'Foo' );
+
+               assert.strictEqual( $tbMW.find( 'span').length, 0, 'No <span> element should be added for porlets without vectorTabs class.' );
+               assert.strictEqual( $( caFoo ).find( 'span').length, 1, 'A <span> element should be added for porlets with vectorTabs class.' );
        });
 
-       assertFalseIPv6( '::fc:100:a:d:1:e:ac:0', 'IPv6 with "::" and 8 words' );
-       assertFalseIPv6( '::fc:100:a:d:1:e:ac:0:1', 'IPv6 with 9 words' );
-
-       assertFalseIPv6( ':fc::100', 'IPv6 starting with lone ":"' );
-       assertFalseIPv6( 'fc::100:', 'IPv6 ending with lone ":"' );
-       assertFalseIPv6( 'fc:::100', 'IPv6 with ":::" in the middle' );
-
-       assertTrueIPv6( 'fc::100', 'IPv6 with "::" and 2 words' );
-       assertTrueIPv6( 'fc::100:a', 'IPv6 with "::" and 3 words' );
-       assertTrueIPv6( 'fc::100:a:d', 'IPv6 with "::" and 4 words' );
-       assertTrueIPv6( 'fc::100:a:d:1', 'IPv6 with "::" and 5 words' );
-       assertTrueIPv6( 'fc::100:a:d:1:e', 'IPv6 with "::" and 6 words' );
-       assertTrueIPv6( 'fc::100:a:d:1:e:ac', 'IPv6 with "::" and 7 words' );
-       assertTrueIPv6( '2001::df', 'IPv6 with "::" and 2 words' );
-       assertTrueIPv6( '2001:5c0:1400:a::df', 'IPv6 with "::" and 5 words' );
-       assertTrueIPv6( '2001:5c0:1400:a::df:2', 'IPv6 with "::" and 6 words' );
-
-       assertFalseIPv6( 'fc::100:a:d:1:e:ac:0', 'IPv6 with "::" and 8 words' );
-       assertFalseIPv6( 'fc::100:a:d:1:e:ac:0:1', 'IPv6 with 9 words' );
-});
-
-QUnit.test( 'isIPv4Address', 11, function ( assert ) {
-       // Shortcuts
-       function assertFalseIPv4( addy, summary ) {
-               assert.strictEqual( mw.util.isIPv4Address( addy ), false, summary );
-       }
-       function assertTrueIPv4( addy, summary ) {
-               assert.strictEqual( mw.util.isIPv4Address( addy ), true, summary );
-       }
-
-       // Based on IPTest.php > testisIPv4
-       assertFalseIPv4( false, 'Boolean false is not an IP' );
-       assertFalseIPv4( true, 'Boolean true is not an IP' );
-       assertFalseIPv4( '', 'Empty string is not an IP' );
-       assertFalseIPv4( 'abc', '"abc" is not an IP' );
-       assertFalseIPv4( ':', 'Colon is not an IP' );
-       assertFalseIPv4( '124.24.52', 'IPv4 not enough quads' );
-       assertFalseIPv4( '24.324.52.13', 'IPv4 out of range' );
-       assertFalseIPv4( '.24.52.13', 'IPv4 starts with period' );
-
-       assertTrueIPv4( '124.24.52.13', '124.24.52.134 is a valid IP' );
-       assertTrueIPv4( '1.24.52.13', '1.24.52.13 is a valid IP' );
-       assertFalseIPv4( '74.24.52.13/20', 'IPv4 ranges are not recogzized as valid IPs' );
-});
+       QUnit.test( 'jsMessage', 1, function ( assert ) {
+               var a = mw.util.jsMessage( 'MediaWiki is <b>Awesome</b>.' );
+               assert.ok( a, 'Basic checking of return value' );
+
+               // Clean up
+               $( '#mw-js-message' ).remove();
+       });
+
+       QUnit.test( 'validateEmail', 6, function ( assert ) {
+               assert.strictEqual( mw.util.validateEmail( '' ), null, 'Should return null for empty string ' );
+               assert.strictEqual( mw.util.validateEmail( 'user@localhost' ), true, 'Return true for a valid e-mail address' );
+
+               // testEmailWithCommasAreInvalids
+               assert.strictEqual( mw.util.validateEmail( 'user,foo@example.org' ), false, 'Emails with commas are invalid' );
+               assert.strictEqual( mw.util.validateEmail( 'userfoo@ex,ample.org' ), false, 'Emails with commas are invalid' );
+
+               // testEmailWithHyphens
+               assert.strictEqual( mw.util.validateEmail( 'user-foo@example.org' ), true, 'Emails may contain a hyphen' );
+               assert.strictEqual( mw.util.validateEmail( 'userfoo@ex-ample.org' ), true, 'Emails may contain a hyphen' );
+       });
+
+       QUnit.test( 'isIPv6Address', 40, function ( assert ) {
+               // Shortcuts
+               function assertFalseIPv6( addy, summary ) {
+                       return assert.strictEqual( mw.util.isIPv6Address( addy ), false, summary );
+               }
+               function assertTrueIPv6( addy, summary ) {
+                       return assert.strictEqual( mw.util.isIPv6Address( addy ), true, summary );
+               }
+
+               // Based on IPTest.php > testisIPv6
+               assertFalseIPv6( ':fc:100::', 'IPv6 starting with lone ":"' );
+               assertFalseIPv6( 'fc:100:::', 'IPv6 ending with a ":::"' );
+               assertFalseIPv6( 'fc:300', 'IPv6 with only 2 words' );
+               assertFalseIPv6( 'fc:100:300', 'IPv6 with only 3 words' );
+
+               $.each(
+               ['fc:100::',
+               'fc:100:a::',
+               'fc:100:a:d::',
+               'fc:100:a:d:1::',
+               'fc:100:a:d:1:e::',
+               'fc:100:a:d:1:e:ac::'], function ( i, addy ){
+                       assertTrueIPv6( addy, addy + ' is a valid IP' );
+               });
+
+               assertFalseIPv6( 'fc:100:a:d:1:e:ac:0::', 'IPv6 with 8 words ending with "::"' );
+               assertFalseIPv6( 'fc:100:a:d:1:e:ac:0:1::', 'IPv6 with 9 words ending with "::"' );
+
+               assertFalseIPv6( ':::' );
+               assertFalseIPv6( '::0:', 'IPv6 ending in a lone ":"' );
+
+               assertTrueIPv6( '::', 'IPv6 zero address' );
+               $.each(
+               ['::0',
+               '::fc',
+               '::fc:100',
+               '::fc:100:a',
+               '::fc:100:a:d',
+               '::fc:100:a:d:1',
+               '::fc:100:a:d:1:e',
+               '::fc:100:a:d:1:e:ac',
+
+               'fc:100:a:d:1:e:ac:0'], function ( i, addy ){
+                       assertTrueIPv6( addy, addy + ' is a valid IP' );
+               });
+
+               assertFalseIPv6( '::fc:100:a:d:1:e:ac:0', 'IPv6 with "::" and 8 words' );
+               assertFalseIPv6( '::fc:100:a:d:1:e:ac:0:1', 'IPv6 with 9 words' );
+
+               assertFalseIPv6( ':fc::100', 'IPv6 starting with lone ":"' );
+               assertFalseIPv6( 'fc::100:', 'IPv6 ending with lone ":"' );
+               assertFalseIPv6( 'fc:::100', 'IPv6 with ":::" in the middle' );
+
+               assertTrueIPv6( 'fc::100', 'IPv6 with "::" and 2 words' );
+               assertTrueIPv6( 'fc::100:a', 'IPv6 with "::" and 3 words' );
+               assertTrueIPv6( 'fc::100:a:d', 'IPv6 with "::" and 4 words' );
+               assertTrueIPv6( 'fc::100:a:d:1', 'IPv6 with "::" and 5 words' );
+               assertTrueIPv6( 'fc::100:a:d:1:e', 'IPv6 with "::" and 6 words' );
+               assertTrueIPv6( 'fc::100:a:d:1:e:ac', 'IPv6 with "::" and 7 words' );
+               assertTrueIPv6( '2001::df', 'IPv6 with "::" and 2 words' );
+               assertTrueIPv6( '2001:5c0:1400:a::df', 'IPv6 with "::" and 5 words' );
+               assertTrueIPv6( '2001:5c0:1400:a::df:2', 'IPv6 with "::" and 6 words' );
+
+               assertFalseIPv6( 'fc::100:a:d:1:e:ac:0', 'IPv6 with "::" and 8 words' );
+               assertFalseIPv6( 'fc::100:a:d:1:e:ac:0:1', 'IPv6 with 9 words' );
+       });
+
+       QUnit.test( 'isIPv4Address', 11, function ( assert ) {
+               // Shortcuts
+               function assertFalseIPv4( addy, summary ) {
+                       assert.strictEqual( mw.util.isIPv4Address( addy ), false, summary );
+               }
+               function assertTrueIPv4( addy, summary ) {
+                       assert.strictEqual( mw.util.isIPv4Address( addy ), true, summary );
+               }
+
+               // Based on IPTest.php > testisIPv4
+               assertFalseIPv4( false, 'Boolean false is not an IP' );
+               assertFalseIPv4( true, 'Boolean true is not an IP' );
+               assertFalseIPv4( '', 'Empty string is not an IP' );
+               assertFalseIPv4( 'abc', '"abc" is not an IP' );
+               assertFalseIPv4( ':', 'Colon is not an IP' );
+               assertFalseIPv4( '124.24.52', 'IPv4 not enough quads' );
+               assertFalseIPv4( '24.324.52.13', 'IPv4 out of range' );
+               assertFalseIPv4( '.24.52.13', 'IPv4 starts with period' );
+
+               assertTrueIPv4( '124.24.52.13', '124.24.52.134 is a valid IP' );
+               assertTrueIPv4( '1.24.52.13', '1.24.52.13 is a valid IP' );
+               assertFalseIPv4( '74.24.52.13/20', 'IPv4 ranges are not recogzized as valid IPs' );
+       });
+}( mediaWiki, jQuery ) );