From 7420e8e4ab75ae090e34e3517e31f9cd6fcfb785 Mon Sep 17 00:00:00 2001 From: Timo Tijhof Date: Tue, 24 Jul 2012 13:45:20 -0700 Subject: [PATCH] Kranitor #2: Pass JSHint on resources/mediawiki/* * .jshintrc - Update properties to reflect our conventions more * Re-pass resources/jquery/* * Pass resources/mediawiki/* - Trailing whitespace - Whitelist the one usage of document.write with a local /*jshint evil:true */ in the function that we allow to use it. - Get rid of dangling _ in var names and undescriptive instances of '_this'. - More code conventions * Add a few documentation comments while at it Change-Id: Ic4f2b5d473a440667a40e4d5f12f40877386b02f --- .jshintrc | 9 +- resources/jquery/jquery.makeCollapsible.js | 6 +- resources/jquery/jquery.textSelection.js | 6 +- .../mediawiki.action/mediawiki.action.edit.js | 15 +- .../mediawiki.action.history.js | 6 +- .../mediawiki.action.view.dblClickEdit.js | 20 +- .../mediawiki.action.view.metadata.js | 70 +-- .../mediawiki.language.init.js | 4 +- .../mediawiki.language/mediawiki.language.js | 44 +- .../mediawiki.page/mediawiki.page.startup.js | 4 +- .../mediawiki.page.watch.ajax.js | 13 +- .../mediawiki.special.changeemail.js | 14 +- .../mediawiki.special.javaScriptTest.js | 54 +- .../mediawiki.special.recentchanges.js | 12 +- .../mediawiki.special.upload.js | 532 +++++++++--------- resources/mediawiki/mediawiki.Title.js | 93 +-- resources/mediawiki/mediawiki.Uri.js | 6 +- resources/mediawiki/mediawiki.debug.js | 21 +- resources/mediawiki/mediawiki.feedback.js | 158 +++--- resources/mediawiki/mediawiki.htmlform.js | 16 +- resources/mediawiki/mediawiki.jqueryMsg.js | 322 +++++------ resources/mediawiki/mediawiki.js | 37 +- resources/mediawiki/mediawiki.log.js | 6 +- resources/mediawiki/mediawiki.user.js | 15 +- resources/mediawiki/mediawiki.util.js | 6 +- 25 files changed, 747 insertions(+), 742 deletions(-) diff --git a/.jshintrc b/.jshintrc index 5fb1173fcf..efbe54d9e1 100644 --- a/.jshintrc +++ b/.jshintrc @@ -11,8 +11,12 @@ "immed": true, "latedef": true, "newcap": true, + "noarg": true, "noempty": true, + "nonew": true, + "regexp": false, "undef": true, + "strict": false, "trailing": true, "laxbreak": true, @@ -20,5 +24,8 @@ "multistr": true, "browser": true, - "jquery": true + "jquery": true, + + "nomen": true, + "onevar": false } diff --git a/resources/jquery/jquery.makeCollapsible.js b/resources/jquery/jquery.makeCollapsible.js index 77f2639c5c..33f875210b 100644 --- a/resources/jquery/jquery.makeCollapsible.js +++ b/resources/jquery/jquery.makeCollapsible.js @@ -19,7 +19,7 @@ $.fn.makeCollapsible = function () { return this.each(function () { - var _fn = 'jquery.makeCollapsible> '; + var lpx = 'jquery.makeCollapsible> '; // Define reused variables and functions var $toggle, @@ -248,7 +248,7 @@ $.fn.makeCollapsible = function () { var thatId = $that.attr( 'id' ), $customTogglers = $( '.' + thatId.replace( 'mw-customcollapsible', 'mw-customtoggle' ) ); - mw.log( _fn + 'Found custom collapsible: #' + thatId ); + mw.log( lpx + 'Found custom collapsible: #' + thatId ); // Double check that there is actually a customtoggle link if ( $customTogglers.length ) { @@ -256,7 +256,7 @@ $.fn.makeCollapsible = function () { toggleLinkCustom( $(this), e, $that ); } ); } else { - mw.log( _fn + '#' + thatId + ': Missing toggler!' ); + mw.log( lpx + '#' + thatId + ': Missing toggler!' ); } // Initial state diff --git a/resources/jquery/jquery.textSelection.js b/resources/jquery/jquery.textSelection.js index 9f1f3a4f3c..583c1eda85 100644 --- a/resources/jquery/jquery.textSelection.js +++ b/resources/jquery/jquery.textSelection.js @@ -8,16 +8,16 @@ // On IE, patch the focus() method to restore the windows' scroll position // (bug 32241) $.fn.extend({ - focus : ( function ( _focus ) { + focus: ( function ( jqFocus ) { return function () { if ( arguments.length === 0 ) { var $w = $( window ); var state = {top: $w.scrollTop(), left: $w.scrollLeft()}; - var result = _focus.apply( this, arguments ); + var result = jqFocus.apply( this, arguments ); window.scrollTo( state.top, state.left ); return result; } - return _focus.apply( this, arguments ); + return jqFocus.apply( this, arguments ); }; }( $.fn.focus ) ) }); diff --git a/resources/mediawiki.action/mediawiki.action.edit.js b/resources/mediawiki.action/mediawiki.action.edit.js index bd07cd09bb..1c51c97483 100644 --- a/resources/mediawiki.action/mediawiki.action.edit.js +++ b/resources/mediawiki.action/mediawiki.action.edit.js @@ -24,7 +24,7 @@ selectText: selectText }; } - var $image = $('', { + var $image = $( '', { width : 23, height: 22, src : b.imageFile, @@ -96,7 +96,7 @@ mw.toolbar = toolbar; $( document ).ready( function () { - var buttons, i, b, iframe; + var buttons, i, b, $iframe; // currentFocus is used to determine where to insert tags currentFocused = $( '#wpTextbox1' ); @@ -154,13 +154,14 @@ // HACK: make currentFocused work with the usability iframe // With proper focus detection support (HTML 5!) this'll be much cleaner - iframe = $( '.wikiEditor-ui-text iframe' ); - if ( iframe.length > 0 ) { - $( iframe.get( 0 ).contentWindow.document ) + // TODO: Get rid of this WikiEditor code from MediaWiki core! + $iframe = $( '.wikiEditor-ui-text iframe' ); + if ( $iframe.length > 0 ) { + $( $iframe.get( 0 ).contentWindow.document ) // for IE - .add( iframe.get( 0 ).contentWindow.document.body ) + .add( $iframe.get( 0 ).contentWindow.document.body ) .focus( function () { - currentFocused = iframe; + currentFocused = $iframe; } ); } }); diff --git a/resources/mediawiki.action/mediawiki.action.history.js b/resources/mediawiki.action/mediawiki.action.history.js index 76b0e6cde0..55f799e598 100644 --- a/resources/mediawiki.action/mediawiki.action.history.js +++ b/resources/mediawiki.action/mediawiki.action.history.js @@ -30,20 +30,20 @@ jQuery( document ).ready( function ( $ ) { return true; } - if ( $oldidRadio.prop( 'checked' ) ) { + if ( $oldidRadio.prop( 'checked' ) ) { oldLi = true; $li.addClass( 'selected' ); $oldidRadio.css( 'visibility', 'visible' ); $diffRadio.css( 'visibility', 'hidden' ); - } else if ( $diffRadio.prop( 'checked' ) ) { + } else if ( $diffRadio.prop( 'checked' ) ) { diffLi = true; $li.addClass( 'selected' ); $oldidRadio.css( 'visibility', 'hidden' ); $diffRadio.css( 'visibility', 'visible' ); // This list item has neither checked - } else { + } else { // We're below the selected radios if ( diffLi && oldLi ) { $oldidRadio.css( 'visibility', 'visible' ); diff --git a/resources/mediawiki.action/mediawiki.action.view.dblClickEdit.js b/resources/mediawiki.action/mediawiki.action.view.dblClickEdit.js index b1d906f67d..7a9ceee577 100644 --- a/resources/mediawiki.action/mediawiki.action.view.dblClickEdit.js +++ b/resources/mediawiki.action/mediawiki.action.view.dblClickEdit.js @@ -1,12 +1,14 @@ /** * This module enables double-click-to-edit functionality */ -jQuery( document ).ready( function( $ ) { - var url = $( '#ca-edit a' ).attr( 'href' ); - if ( url ) { - mw.util.$content.dblclick( function( e ) { - e.preventDefault(); - window.location = url; - } ); - } -} ); +( function ( mw, $ ) { + $( function () { + var url = $( '#ca-edit a' ).attr( 'href' ); + if ( url ) { + mw.util.$content.dblclick( function ( e ) { + e.preventDefault(); + window.location = url; + } ); + } + } ); +}( mediaWiki, jQuery ) ); diff --git a/resources/mediawiki.action/mediawiki.action.view.metadata.js b/resources/mediawiki.action/mediawiki.action.view.metadata.js index b791cabd87..ce3c674b8b 100644 --- a/resources/mediawiki.action/mediawiki.action.view.metadata.js +++ b/resources/mediawiki.action/mediawiki.action.view.metadata.js @@ -1,39 +1,43 @@ -// Exif metadata display for MediaWiki file uploads -// -// Add an expand/collapse link and collapse by default if set to -// (with JS disabled, user will see all items) -// +/** + * Exif metadata display for MediaWiki file uploads + * + * Add an expand/collapse link and collapse by default if set to + * (with JS disabled, user will see all items) + */ +( function ( mw, $ ) { + $( function () { + var $row, $col, $link, + showText = mw.msg( 'metadata-expand' ), + hideText = mw.msg( 'metadata-collapse' ), + $table = $( '#mw_metadata' ), + $tbody = $table.find( 'tbody' ); -jQuery( document ).ready( function( $ ) { - var showText = mw.msg( 'metadata-expand' ); - var hideText = mw.msg( 'metadata-collapse' ); + if ( !$tbody.length ) { + return; + } - var $table = $( '#mw_metadata' ); - var $tbody = $table.find( 'tbody' ); - if ( !$tbody.length ) { - return; - } + $row = $( '' ); + $col = $( '' ); - var $row = $( '' ); - var $col = $( '' ); + $link = $( '', { + text: showText, + href: '#' + }).click(function () { + if ( $table.hasClass( 'collapsed' ) ) { + $( this ).text( hideText ); + } else { + $( this ).text( showText ); + } + $table.toggleClass( 'expanded collapsed' ); + return false; + }); - var $link = $( '', { - 'text': showText, - 'href': '#' - }).click(function() { - if ( $table.hasClass( 'collapsed' ) ) { - $( this ).text( hideText ); - } else { - $( this ).text( showText ); - } - $table.toggleClass( 'expanded collapsed' ); - return false; - }); + $col.append( $link ); + $row.append( $col ); + $tbody.append( $row ); - $col.append( $link ); - $row.append( $col ); - $tbody.append( $row ); + // And collapse! + $table.addClass( 'collapsed' ); + } ); - // And collapse! - $table.addClass( 'collapsed' ); -} ); +}( mediaWiki, jQuery ) ); \ No newline at end of file diff --git a/resources/mediawiki.language/mediawiki.language.init.js b/resources/mediawiki.language/mediawiki.language.init.js index b49823c842..8d2ffae06b 100644 --- a/resources/mediawiki.language/mediawiki.language.init.js +++ b/resources/mediawiki.language/mediawiki.language.init.js @@ -2,7 +2,7 @@ * Base language object with methods for storing and getting * language data. */ -( function( $, mw ) { +( function ( mw, $ ) { var language = { /** @@ -58,4 +58,4 @@ mw.language = language; -} )( jQuery, mediaWiki ); +}( mediaWiki, jQuery ) ); diff --git a/resources/mediawiki.language/mediawiki.language.js b/resources/mediawiki.language/mediawiki.language.js index 78a63098ee..1234637afe 100644 --- a/resources/mediawiki.language/mediawiki.language.js +++ b/resources/mediawiki.language/mediawiki.language.js @@ -3,7 +3,7 @@ * Language.php in MediaWiki. * This adds methods for transforming message text. */ -( function( $, mw ) { +( function ( mw, $ ) { var language = { @@ -12,13 +12,13 @@ var language = { * * @param {object} template Template object * @format template - * { - * 'title': [title of template], - * 'parameters': [template parameters] - * } + * { + * 'title': [title of template], + * 'parameters': [template parameters] + * } * @example {{Template:title|params}} */ - 'procPLURAL': function( template ) { + procPLURAL: function ( template ) { if ( template.title && template.parameters && mw.language.convertPlural ) { // Check if we have forms to replace if ( template.parameters.length === 0 ) { @@ -35,6 +35,7 @@ var language = { } return ''; }, + /** * Plural form transformations, needed for some languages. * @@ -42,12 +43,13 @@ var language = { * @param forms array List of plural forms * @return string Correct form for quantifier in this language */ - 'convertPlural': function( count, forms ){ + convertPlural: function ( count, forms ){ if ( !forms || forms.length === 0 ) { return ''; } - return ( parseInt( count, 10 ) == 1 ) ? forms[0] : forms[1]; + return ( parseInt( count, 10 ) === 1 ) ? forms[0] : forms[1]; }, + /** * Pads an array to a specific length by copying the last one element. * @@ -55,38 +57,41 @@ var language = { * @param count integer Number of forms required * @return array Padded array of forms */ - 'preConvertPlural': function( forms, count ) { + preConvertPlural: function ( forms, count ) { while ( forms.length < count ) { forms.push( forms[ forms.length-1 ] ); } return forms; }, + /** * Converts a number using digitTransformTable. * * @param {num} number Value to be converted * @param {boolean} integer Convert the return value to an integer */ - 'convertNumber': function( num, integer ) { + convertNumber: function( num, integer ) { + var i, tmp, transformTable; + if ( !mw.language.digitTransformTable ) { return num; } // Set the target Transform table: - var transformTable = mw.language.digitTransformTable; + transformTable = mw.language.digitTransformTable; // Check if the "restore" to Latin number flag is set: if ( integer ) { - if ( parseInt( num, 10 ) == num ) { + if ( parseInt( num, 10 ) === num ) { return num; } - var tmp = []; - for ( var i in transformTable ) { + tmp = []; + for ( i in transformTable ) { tmp[ transformTable[ i ] ] = i; } transformTable = tmp; } - var numberString = '' + num; + var numberString = '' + num; var convertedNumber = ''; - for ( var i = 0; i < numberString.length; i++ ) { + for ( i = 0; i < numberString.length; i++ ) { if ( transformTable[ numberString[i] ] ) { convertedNumber += transformTable[numberString[i]]; } else { @@ -95,6 +100,7 @@ var language = { } return integer ? parseInt( convertedNumber, 10 ) : convertedNumber; }, + /** * Provides an alternative text depending on specified gender. * Usage {{gender:[gender|user object]|masculine|feminine|neutral}}. @@ -107,7 +113,7 @@ var language = { * * @return string */ - 'gender': function( gender, forms ) { + gender: function( gender, forms ) { if ( !forms || forms.length === 0 ) { return ''; } @@ -140,9 +146,9 @@ var language = { }, // Digit Transform Table, populated by language classes where applicable - 'digitTransformTable': mw.language.getData( mw.config.get( 'wgUserLanguage' ), 'digitTransformTable' ) + digitTransformTable: mw.language.getData( mw.config.get( 'wgUserLanguage' ), 'digitTransformTable' ) }; $.extend( mw.language, language ); -} )( jQuery, mediaWiki ); +}( mediaWiki, jQuery ) ); diff --git a/resources/mediawiki.page/mediawiki.page.startup.js b/resources/mediawiki.page/mediawiki.page.startup.js index a5541ef90a..6a11d3e15f 100644 --- a/resources/mediawiki.page/mediawiki.page.startup.js +++ b/resources/mediawiki.page/mediawiki.page.startup.js @@ -1,4 +1,4 @@ -( function ( $ ) { +( function ( mw, $ ) { mw.page = {}; @@ -15,4 +15,4 @@ // is defined for them. $( mw.util.init ); -} )( jQuery ); +}( mediaWiki, jQuery ) ); diff --git a/resources/mediawiki.page/mediawiki.page.watch.ajax.js b/resources/mediawiki.page/mediawiki.page.watch.ajax.js index d3f84336d5..14557d41a1 100644 --- a/resources/mediawiki.page/mediawiki.page.watch.ajax.js +++ b/resources/mediawiki.page/mediawiki.page.watch.ajax.js @@ -2,16 +2,12 @@ * Animate watch/unwatch links to use asynchronous API requests to * watch pages, rather than navigating to a different URI. */ -( function ( $, mw, undefined ) { +( function ( mw, $ ) { /** * The name of the page to watch or unwatch. */ var title = mw.config.get( 'wgRelevantPageName', mw.config.get( 'wgPageName' ) ); - // Expose local methods - mw.page.watch = { - 'updateWatchLink': updateWatchLink - }; /** * Update the link text, link href attribute and (if applicable) * "loading" class. @@ -98,6 +94,11 @@ return 'view'; } + // Expose local methods + mw.page.watch = { + 'updateWatchLink': updateWatchLink + }; + $( document ).ready( function () { var $links = $( '.mw-watchlink a, a.mw-watchlink, ' + '#ca-watch a, #ca-unwatch a, #mw-unwatch-link1, ' + @@ -171,4 +172,4 @@ }); }); -}( jQuery, mediaWiki ) ); +}( mediaWiki, jQuery ) ); diff --git a/resources/mediawiki.special/mediawiki.special.changeemail.js b/resources/mediawiki.special/mediawiki.special.changeemail.js index 00e14fab3d..cab0bbd3ee 100644 --- a/resources/mediawiki.special/mediawiki.special.changeemail.js +++ b/resources/mediawiki.special/mediawiki.special.changeemail.js @@ -1,12 +1,12 @@ /* * JavaScript for Special:ChangeEmail */ -( function( $, mw ) { +( function ( mw, $ ) { /** * Given an email validity status (true, false, null) update the label CSS class */ -var updateMailValidityLabel = function( mail ) { +function updateMailValidityLabel( mail ) { var isValid = mw.util.validateEmail( mail ), $label = $( '#mw-emailaddress-validity' ); @@ -22,21 +22,21 @@ var updateMailValidityLabel = function( mail ) { } else { $label.text( mw.msg( 'email-address-validity-invalid' ) ).addClass( 'invalid' ).removeClass( 'valid' ); } -}; +} -$( document ).ready( function() { +$( document ).ready( function () { // Lame tip to let user know if its email is valid. See bug 22449 // Only bind once for 'blur' so that the user can fill it in without errors // After that look at every keypress for direct feedback if it was invalid onblur - $( '#wpNewEmail' ).one( 'blur', function() { + $( '#wpNewEmail' ).one( 'blur', function () { if ( $( '#mw-emailaddress-validity' ).length === 0 ) { $(this).after( '' ); } updateMailValidityLabel( $(this).val() ); - $(this).keyup( function() { + $(this).keyup( function () { updateMailValidityLabel( $(this).val() ); } ); } ); } ); -} )( jQuery, mediaWiki ); +}( mediaWiki, jQuery ) ); diff --git a/resources/mediawiki.special/mediawiki.special.javaScriptTest.js b/resources/mediawiki.special/mediawiki.special.javaScriptTest.js index d413f602cb..808d5fe89f 100644 --- a/resources/mediawiki.special/mediawiki.special.javaScriptTest.js +++ b/resources/mediawiki.special/mediawiki.special.javaScriptTest.js @@ -1,33 +1,37 @@ -/* +/** * JavaScript for Special:JavaScriptTest */ -jQuery( document ).ready( function( $ ) { +( function ( mw, $ ) { + $( function () { - // Create useskin dropdown menu and reload onchange to the selected skin - // (only if a framework was found, not on error pages). - $( '#mw-javascripttest-summary.mw-javascripttest-frameworkfound' ).append( function() { + // Create useskin dropdown menu and reload onchange to the selected skin + // (only if a framework was found, not on error pages). + $( '#mw-javascripttest-summary.mw-javascripttest-frameworkfound' ).append( function () { - var $html = $( '

' ), - select = ''; - // Build '; + // Build '; - // Bind onchange event handler and append to form - $html.append( - $( select ).change( function() { - window.location = QUnit.url( { useskin: $(this).val() } ); - } ) - ); + // Bind onchange event handler and append to form + $html.append( + $( select ).change( function () { + window.location = QUnit.url( { useskin: $(this).val() } ); + } ) + ); - return $html; + return $html; + } ); } ); -} ); + +}( mediaWiki, jQuery ) ); + diff --git a/resources/mediawiki.special/mediawiki.special.recentchanges.js b/resources/mediawiki.special/mediawiki.special.recentchanges.js index 3d520f5e5f..7996d935bd 100644 --- a/resources/mediawiki.special/mediawiki.special.recentchanges.js +++ b/resources/mediawiki.special/mediawiki.special.recentchanges.js @@ -1,5 +1,5 @@ /* JavaScript for Special:RecentChanges */ -( function( $ ) { +( function ( mw, $ ) { var checkboxes = [ 'nsassociated', 'nsinvert' ]; @@ -14,17 +14,17 @@ * Handler to disable/enable the namespace selector checkboxes when the * special 'all' namespace is selected/unselected respectively. */ - updateCheckboxes: function() { + 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 ) { + $.each( checkboxes, function ( i, id ) { $( '#' + id ).prop( 'disabled', isAllNS ); }); }, - init: function() { + init: function () { // Populate $select = $( '#namespace' ); @@ -36,4 +36,4 @@ // Run when document is ready $( rc.init ); -})( jQuery ); +}( mediaWiki, jQuery ) ); diff --git a/resources/mediawiki.special/mediawiki.special.upload.js b/resources/mediawiki.special/mediawiki.special.upload.js index f7a4b1f76e..63e8971308 100644 --- a/resources/mediawiki.special/mediawiki.special.upload.js +++ b/resources/mediawiki.special/mediawiki.special.upload.js @@ -1,297 +1,299 @@ -/* +/** * JavaScript for Special:Upload * Note that additional code still lives in skins/common/upload.js */ - -/** - * Add a preview to the upload form - */ -jQuery( function( $ ) { - /** - * Is the FileAPI available with sufficient functionality? - */ - function hasFileAPI(){ - return typeof window.FileReader !== 'undefined'; - } - +( function ( mw, $ ) { /** - * Check if this is a recognizable image type... - * Also excludes files over 10M to avoid going insane on memory usage. - * - * @todo is there a way we can ask the browser what's supported in s? - * @todo put SVG back after working around Firefox 7 bug - * - * @param {File} file - * @return boolean + * Add a preview to the upload form */ - function fileIsPreviewable( file ) { - var known = ['image/png', 'image/gif', 'image/jpeg', 'image/svg+xml'], - tooHuge = 10 * 1024 * 1024; - return ( $.inArray( file.type, known ) !== -1 ) && file.size > 0 && file.size < tooHuge; - } + $( function ( $ ) { + /** + * Is the FileAPI available with sufficient functionality? + */ + function hasFileAPI() { + return typeof window.FileReader !== 'undefined'; + } - /** - * Show a thumbnail preview of PNG, JPEG, GIF, and SVG files prior to upload - * in browsers supporting HTML5 FileAPI. - * - * As of this writing, known good: - * - Firefox 3.6+ - * - Chrome 7.something - * - * @todo check file size limits and warn of likely failures - * - * @param {File} file - */ - function showPreview( file ) { - var previewSize = 180, - thumb = $( '
' + - '
' + - '
' + - '
' + - '
' + - '
' ); - thumb.find( '.filename' ).text( file.name ).end() - .find( '.fileinfo' ).text( prettySize( file.size ) ).end(); + /** + * Check if this is a recognizable image type... + * Also excludes files over 10M to avoid going insane on memory usage. + * + * @todo is there a way we can ask the browser what's supported in s? + * @todo put SVG back after working around Firefox 7 bug + * + * @param {File} file + * @return boolean + */ + function fileIsPreviewable( file ) { + var known = ['image/png', 'image/gif', 'image/jpeg', 'image/svg+xml'], + tooHuge = 10 * 1024 * 1024; + return ( $.inArray( file.type, known ) !== -1 ) && file.size > 0 && file.size < tooHuge; + } - var $canvas = $(''), - ctx = $canvas[0].getContext( '2d' ); - $( '#mw-htmlform-source' ).parent().prepend( thumb ); + /** + * Show a thumbnail preview of PNG, JPEG, GIF, and SVG files prior to upload + * in browsers supporting HTML5 FileAPI. + * + * As of this writing, known good: + * - Firefox 3.6+ + * - Chrome 7.something + * + * @todo check file size limits and warn of likely failures + * + * @param {File} file + */ + function showPreview( file ) { + var previewSize = 180, + thumb = $( '
' + + '
' + + '
' + + '
' + + '
' + + '
' ); + thumb.find( '.filename' ).text( file.name ).end() + .find( '.fileinfo' ).text( prettySize( file.size ) ).end(); - var meta; - fetchPreview( file, function( dataURL ) { - var img = new Image(), - rotation = 0; + var $canvas = $(''), + ctx = $canvas[0].getContext( '2d' ); + $( '#mw-htmlform-source' ).parent().prepend( thumb ); - if ( meta && meta.tiff && meta.tiff.Orientation ) { - rotation = (360 - function () { - // See includes/media/Bitmap.php - switch ( meta.tiff.Orientation.value ) { - case 8: - return 90; - case 3: - return 180; - case 6: - return 270; - default: - return 0; - } - }() ) % 360; - } + var meta; + fetchPreview( file, function( dataURL ) { + var img = new Image(), + rotation = 0; - img.onload = function() { - var width, height, x, y, dx, dy, logicalWidth, logicalHeight; - // Fit the image within the previewSizexpreviewSize box - if ( img.width > img.height ) { - width = previewSize; - height = img.height / img.width * previewSize; - } else { - height = previewSize; - width = img.width / img.height * previewSize; + if ( meta && meta.tiff && meta.tiff.Orientation ) { + rotation = ( 360 - ( function () { + // See includes/media/Bitmap.php + switch ( meta.tiff.Orientation.value ) { + case 8: + return 90; + case 3: + return 180; + case 6: + return 270; + default: + return 0; + } + }() ) ) % 360; } - // Determine the offset required to center the image - dx = (180 - width) / 2; - dy = (180 - height) / 2; - switch ( rotation ) { - // If a rotation is applied, the direction of the axis - // changes as well. You can derive the values below by - // drawing on paper an axis system, rotate it and see - // where the positive axis direction is - case 0: - x = dx; - y = dy; - logicalWidth = img.width; - logicalHeight = img.height; - break; - case 90: - x = dx; - y = dy - previewSize; - logicalWidth = img.height; - logicalHeight = img.width; - break; - case 180: - x = dx - previewSize; - y = dy - previewSize; - logicalWidth = img.width; - logicalHeight = img.height; - break; - case 270: - x = dx - previewSize; - y = dy; - logicalWidth = img.height; - logicalHeight = img.width; - break; - } - - ctx.clearRect( 0, 0, 180, 180 ); - ctx.rotate( rotation / 180 * Math.PI ); - ctx.drawImage( img, x, y, width, height ); - thumb.find('.mw-small-spinner').replaceWith($canvas); + img.onload = function () { + var width, height, x, y, dx, dy, logicalWidth, logicalHeight; + // Fit the image within the previewSizexpreviewSize box + if ( img.width > img.height ) { + width = previewSize; + height = img.height / img.width * previewSize; + } else { + height = previewSize; + width = img.width / img.height * previewSize; + } + // Determine the offset required to center the image + dx = (180 - width) / 2; + dy = (180 - height) / 2; + switch ( rotation ) { + // If a rotation is applied, the direction of the axis + // changes as well. You can derive the values below by + // drawing on paper an axis system, rotate it and see + // where the positive axis direction is + case 0: + x = dx; + y = dy; + logicalWidth = img.width; + logicalHeight = img.height; + break; + case 90: - // Image size - var info = mw.msg( 'widthheight', logicalWidth, logicalHeight ) + - ', ' + prettySize( file.size ); - $( '#mw-upload-thumbnail .fileinfo' ).text( info ); - }; - img.src = dataURL; - }, mw.config.get( 'wgFileCanRotate' ) ? function ( data ) { - try { - meta = mw.libs.jpegmeta( data, file.fileName ); - meta._binary_data = null; - } catch ( e ) { - meta = null; - } - } : null ); - } + x = dx; + y = dy - previewSize; + logicalWidth = img.height; + logicalHeight = img.width; + break; + case 180: + x = dx - previewSize; + y = dy - previewSize; + logicalWidth = img.width; + logicalHeight = img.height; + break; + case 270: + x = dx - previewSize; + y = dy; + logicalWidth = img.height; + logicalHeight = img.width; + break; + } - /** - * Start loading a file into memory; when complete, pass it as a - * data URL to the callback function. If the callbackBinary is set it will - * first be read as binary and afterwards as data URL. Useful if you want - * to do preprocessing on the binary data first. - * - * @param {File} file - * @param {function} callback - * @param {function} callbackBinary - */ - function fetchPreview( file, callback, callbackBinary ) { - var reader = new FileReader(); - if ( callbackBinary && 'readAsBinaryString' in reader ) { - // To fetch JPEG metadata we need a binary string; start there. - // todo: - reader.onload = function() { - callbackBinary( reader.result ); + ctx.clearRect( 0, 0, 180, 180 ); + ctx.rotate( rotation / 180 * Math.PI ); + ctx.drawImage( img, x, y, width, height ); + thumb.find('.mw-small-spinner').replaceWith($canvas); - // Now run back through the regular code path. - fetchPreview( file, callback ); - }; - reader.readAsBinaryString( file ); - } else if ( callbackBinary && 'readAsArrayBuffer' in reader ) { - // readAsArrayBuffer replaces readAsBinaryString - // 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 ), - string = ''; - for ( var i = 0; i < buffer.byteLength; i++ ) { - string += String.fromCharCode( buffer[i] ); + // Image size + var info = mw.msg( 'widthheight', logicalWidth, logicalHeight ) + + ', ' + prettySize( file.size ); + $( '#mw-upload-thumbnail .fileinfo' ).text( info ); + }; + img.src = dataURL; + }, mw.config.get( 'wgFileCanRotate' ) ? function ( data ) { + try { + meta = mw.libs.jpegmeta( data, file.fileName ); + meta._binary_data = null; + } catch ( e ) { + meta = null; } - callbackBinary( string ); - - // Now run back through the regular code path. - fetchPreview( file, callback ); - }; - reader.readAsArrayBuffer( file ); - } else if ( 'URL' in window && 'createObjectURL' in window.URL ) { - // Supported in Firefox 4.0 and above - // WebKit has it in a namespace for now but that's ok. ;) - // - // Lifetime of this URL is until document close, which is fine - // for Special:Upload -- if this code gets used on longer-running - // pages, add a revokeObjectURL() when it's no longer needed. - // - // Prefer this over readAsDataURL for Firefox 7 due to bug reading - // some SVG files from data URIs - callback( window.URL.createObjectURL( file ) ); - } else { - // This ends up decoding the file to base-64 and back again, which - // feels horribly inefficient. - reader.onload = function() { - callback( reader.result ); - }; - reader.readAsDataURL( file ); + } : null ); } - } - /** - * Format a file size attractively. - * @todo match numeric formatting - * - * @param {number} s - * @return string - */ - function prettySize( s ) { - var sizes = ['size-bytes', 'size-kilobytes', 'size-megabytes', 'size-gigabytes']; - while ( s >= 1024 && sizes.length > 1 ) { - s /= 1024; - sizes = sizes.slice( 1 ); - } - return mw.msg( sizes[0], Math.round( s ) ); - } + /** + * Start loading a file into memory; when complete, pass it as a + * data URL to the callback function. If the callbackBinary is set it will + * first be read as binary and afterwards as data URL. Useful if you want + * to do preprocessing on the binary data first. + * + * @param {File} file + * @param {function} callback + * @param {function} callbackBinary + */ + function fetchPreview( file, callback, callbackBinary ) { + var reader = new FileReader(); + if ( callbackBinary && 'readAsBinaryString' in reader ) { + // To fetch JPEG metadata we need a binary string; start there. + // todo: + reader.onload = function() { + callbackBinary( reader.result ); - /** - * Clear the file upload preview area. - */ - function clearPreview() { - $( '#mw-upload-thumbnail' ).remove(); - } + // Now run back through the regular code path. + fetchPreview( file, callback ); + }; + reader.readAsBinaryString( file ); + } else if ( callbackBinary && 'readAsArrayBuffer' in reader ) { + // readAsArrayBuffer replaces readAsBinaryString + // 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 ), + string = ''; + for ( var i = 0; i < buffer.byteLength; i++ ) { + string += String.fromCharCode( buffer[i] ); + } + callbackBinary( string ); - /** - * Check if the file does not exceed the maximum size - */ - function checkMaxUploadSize( file ) { - function getMaxUploadSize( type ) { - var sizes = mw.config.get( 'wgMaxUploadSize' ); - if ( sizes[type] !== undefined ) { - return sizes[type]; + // Now run back through the regular code path. + fetchPreview( file, callback ); + }; + reader.readAsArrayBuffer( file ); + } else if ( 'URL' in window && 'createObjectURL' in window.URL ) { + // Supported in Firefox 4.0 and above + // WebKit has it in a namespace for now but that's ok. ;) + // + // Lifetime of this URL is until document close, which is fine + // for Special:Upload -- if this code gets used on longer-running + // pages, add a revokeObjectURL() when it's no longer needed. + // + // Prefer this over readAsDataURL for Firefox 7 due to bug reading + // some SVG files from data URIs + callback( window.URL.createObjectURL( file ) ); + } else { + // This ends up decoding the file to base-64 and back again, which + // feels horribly inefficient. + reader.onload = function() { + callback( reader.result ); + }; + reader.readAsDataURL( file ); } - return sizes['*']; } - $( '.mw-upload-source-error' ).remove(); - var maxSize = getMaxUploadSize( 'file' ); - if ( file.size > maxSize ) { - var error = $( '

' + - mw.message( 'largefileserver', file.size, maxSize ).escaped() + '

' ); - $( '#wpUploadFile' ).after( error ); - return false; + /** + * Format a file size attractively. + * @todo match numeric formatting + * + * @param {number} s + * @return string + */ + function prettySize( s ) { + var sizeMsgs = ['size-bytes', 'size-kilobytes', 'size-megabytes', 'size-gigabytes']; + while ( s >= 1024 && sizeMsgs.length > 1 ) { + s /= 1024; + sizeMsgs = sizeMsgs.slice( 1 ); + } + return mw.msg( sizeMsgs[0], Math.round( s ) ); } - return true; - } - - /** - * Initialization - */ - if ( hasFileAPI() ) { - // Update thumbnail when the file selection control is updated. - $( '#wpUploadFile' ).change( function() { - clearPreview(); - if ( this.files && this.files.length ) { - // Note: would need to be updated to handle multiple files. - var file = this.files[0]; + /** + * Clear the file upload preview area. + */ + function clearPreview() { + $( '#mw-upload-thumbnail' ).remove(); + } - if ( !checkMaxUploadSize( file ) ) { - return; + /** + * Check if the file does not exceed the maximum size + */ + function checkMaxUploadSize( file ) { + function getMaxUploadSize( type ) { + var sizes = mw.config.get( 'wgMaxUploadSize' ); + if ( sizes[type] !== undefined ) { + return sizes[type]; } + return sizes['*']; + } + $( '.mw-upload-source-error' ).remove(); - if ( fileIsPreviewable( file ) ) { - showPreview( file ); - } + var maxSize = getMaxUploadSize( 'file' ); + if ( file.size > maxSize ) { + var error = $( '

' + + mw.message( 'largefileserver', file.size, maxSize ).escaped() + '

' ); + $( '#wpUploadFile' ).after( error ); + return false; } - } ); - } -} ); + return true; + } -/** - * Disable all upload source fields except the selected one - */ -jQuery( function ( $ ) { - var rows = $( '.mw-htmlform-field-UploadSourceField' ); - for ( var i = rows.length; i; i-- ) { - var 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', 'disabled' ); - // Re-enable the current one - $( 'input', currentRow ).prop( 'disabled', false ); + + /** + * Initialization + */ + if ( hasFileAPI() ) { + // Update thumbnail when the file selection control is updated. + $( '#wpUploadFile' ).change( function () { + clearPreview(); + if ( this.files && this.files.length ) { + // Note: would need to be updated to handle multiple files. + var file = this.files[0]; + + if ( !checkMaxUploadSize( file ) ) { + return; + } + + if ( fileIsPreviewable( file ) ) { + showPreview( file ); + } } - }; - }() ); - } -} ); + } ); + } + } ); + + /** + * 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 ); + } + }; + }() ) ); + } + } ); +}( mediaWiki, jQuery ) ); diff --git a/resources/mediawiki/mediawiki.Title.js b/resources/mediawiki/mediawiki.Title.js index 8d7996cb77..6b6e58686d 100644 --- a/resources/mediawiki/mediawiki.Title.js +++ b/resources/mediawiki/mediawiki.Title.js @@ -7,7 +7,7 @@ * * Relies on: mw.config (wgFormattedNamespaces, wgNamespaceIds, wgCaseSensitiveNamespaces), mw.util.wikiGetlink */ -( function( $ ) { +( function ( mw, $ ) { /* Local space */ @@ -20,20 +20,21 @@ * @param namespace {Number} (optional) Namespace id. If given, title will be taken as-is. * @return {Title} this */ -var Title = function( title, namespace ) { - this._ns = 0; // integer namespace id - this._name = null; // name in canonical 'database' form - this._ext = null; // extension + function Title( title, namespace ) { + this.ns = 0; // integer namespace id + this.name = null; // name in canonical 'database' form + this.ext = null; // extension if ( arguments.length === 2 ) { setNameAndExtension( this, title ); - this._ns = fixNsId( namespace ); + this.ns = fixNsId( namespace ); } else if ( arguments.length === 1 ) { setAll( this, title ); } return this; - }, + } +var /** * Strip some illegal chars: control chars, colon, less than, greater than, * brackets, braces, pipe, whitespace and normal spaces. This still leaves some insanity @@ -41,7 +42,7 @@ var Title = function( title, namespace ) { * @param s {String} * @return {String} */ - clean = function( s ) { + clean = function ( s ) { if ( s !== undefined ) { return s.replace( /[\x00-\x1f\x23\x3c\x3e\x5b\x5d\x7b\x7c\x7d\x7f\s]+/g, '_' ); } @@ -63,14 +64,14 @@ var Title = function( title, namespace ) { /** * Sanitize name. */ - fixName = function( s ) { + fixName = function ( s ) { return clean( $.trim( s ) ); }, /** * Sanitize name. */ - fixExt = function( s ) { + fixExt = function ( s ) { return clean( s ); }, @@ -79,7 +80,7 @@ var Title = function( title, namespace ) { * @param id {Number} Namespace id. * @return {Number|Boolean} The id as-is or boolean false if invalid. */ - fixNsId = function( id ) { + fixNsId = function ( id ) { // wgFormattedNamespaces is an object of *string* key-vals (ie. arr["0"] not arr[0] ) var ns = mw.config.get( 'wgFormattedNamespaces' )[id.toString()]; @@ -98,9 +99,13 @@ var Title = function( title, namespace ) { * @param ns {String} Namespace name (case insensitive, leading/trailing space ignored). * @return {Number|Boolean} Namespace id or boolean false if unrecognized. */ - getNsIdByName = function( ns ) { - // toLowerCase throws exception on null/undefined. Return early. - if ( ns == null ) { + getNsIdByName = function ( ns ) { + // Don't cast non-strings to strings, because null or undefined + // should not result in returning the id of a potential namespace + // called "Null:" (e.g. on nullwiki.example.org) + // Also, toLowerCase throws exception on null/undefined, because + // it is a String.prototype method. + if ( typeof ns !== 'string' ) { return false; } ns = clean( $.trim( ns.toLowerCase() ) ); // Normalize @@ -119,7 +124,7 @@ var Title = function( title, namespace ) { * @param raw {String} * @return {mw.Title} */ - setAll = function( title, s ) { + setAll = function ( title, s ) { // In normal browsers the match-array contains null/undefined if there's no match, // IE returns an empty string. var matches = s.match( /^(?:([^:]+):)?(.*?)(?:\.(\w{1,5}))?$/ ), @@ -127,14 +132,14 @@ var Title = function( title, namespace ) { // 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; - title._name = fixName( matches[2] ); + title.ns = ns_match; + title.name = fixName( matches[2] ); if ( typeof matches[3] === 'string' && matches[3] !== '' ) { - title._ext = fixExt( matches[3] ); + title.ext = fixExt( matches[3] ); } } else { // Consistency with MediaWiki PHP: Unknown namespace -> fallback to main namespace. - title._ns = 0; + title.ns = 0; setNameAndExtension( title, s ); } return title; @@ -147,16 +152,16 @@ var Title = function( title, namespace ) { * @param raw {String} * @return {mw.Title} */ - setNameAndExtension = function( title, raw ) { + setNameAndExtension = function ( title, raw ) { // In normal browsers the match-array contains null/undefined if there's no match, // IE returns an empty string. var matches = raw.match( /^(?:)?(.*?)(?:\.(\w{1,5}))?$/ ); // Title must be a non-empty string. if ( typeof matches[1] === 'string' && matches[1] !== '' ) { - title._name = fixName( matches[1] ); + title.name = fixName( matches[1] ); if ( typeof matches[2] === 'string' && matches[2] !== '' ) { - title._ext = fixExt( matches[2] ); + title.ext = fixExt( matches[2] ); } } else { throw new Error( 'mw.Title: Could not parse title "' + raw + '"' ); @@ -172,7 +177,7 @@ var Title = function( title, namespace ) { * @param title {mixed} prefixed db-key name (string) or instance of Title * @return {mixed} Boolean true/false if the information is available. Otherwise null. */ - Title.exists = function( title ) { + Title.exists = function ( title ) { var type = $.type( title ), obj = Title.exist.pages, match; if ( type === 'string' ) { match = obj[title]; @@ -203,7 +208,7 @@ var Title = function( title, namespace ) { * @param state {Boolean} (optional) State of the given titles. Defaults to true. * @return {Boolean} */ - set: function( titles, state ) { + set: function ( titles, state ) { titles = $.isArray( titles ) ? titles : [titles]; state = state === undefined ? true : !!state; var pages = this.pages, i, len = titles.length; @@ -223,8 +228,8 @@ var Title = function( title, namespace ) { * Get the namespace number. * @return {Number} */ - getNamespaceId: function(){ - return this._ns; + getNamespaceId: function (){ + return this.ns; }, /** @@ -232,19 +237,19 @@ var Title = function( title, namespace ) { * In NS_MAIN this is '', otherwise namespace name plus ':' * @return {String} */ - getNamespacePrefix: function(){ - return mw.config.get( 'wgFormattedNamespaces' )[this._ns].replace( / /g, '_' ) + (this._ns === 0 ? '' : ':'); + getNamespacePrefix: function (){ + return mw.config.get( 'wgFormattedNamespaces' )[this.ns].replace( / /g, '_' ) + (this.ns === 0 ? '' : ':'); }, /** * The name, like "Foo_bar" * @return {String} */ - getName: function() { - if ( $.inArray( this._ns, mw.config.get( 'wgCaseSensitiveNamespaces' ) ) !== -1 ) { - return this._name; + getName: function () { + if ( $.inArray( this.ns, mw.config.get( 'wgCaseSensitiveNamespaces' ) ) !== -1 ) { + return this.name; } else { - return $.ucFirst( this._name ); + return $.ucFirst( this.name ); } }, @@ -252,7 +257,7 @@ var Title = function( title, namespace ) { * The name, like "Foo bar" * @return {String} */ - getNameText: function() { + getNameText: function () { return text( this.getName() ); }, @@ -260,7 +265,7 @@ var Title = function( title, namespace ) { * Get full name in prefixed DB form, like File:Foo_bar.jpg, * most useful for API calls, anything that must identify the "title". */ - getPrefixedDb: function() { + getPrefixedDb: function () { return this.getNamespacePrefix() + this.getMain(); }, @@ -268,7 +273,7 @@ var Title = function( title, namespace ) { * Get full name in text form, like "File:Foo bar.jpg". * @return {String} */ - getPrefixedText: function() { + getPrefixedText: function () { return text( this.getPrefixedDb() ); }, @@ -276,7 +281,7 @@ var Title = function( title, namespace ) { * The main title (without namespace), like "Foo_bar.jpg" * @return {String} */ - getMain: function() { + getMain: function () { return this.getName() + this.getDotExtension(); }, @@ -284,7 +289,7 @@ var Title = function( title, namespace ) { * The "text" form, like "Foo bar.jpg" * @return {String} */ - getMainText: function() { + getMainText: function () { return text( this.getMain() ); }, @@ -292,23 +297,23 @@ var Title = function( title, namespace ) { * Get the extension (returns null if there was none) * @return {String|null} extension */ - getExtension: function() { - return this._ext; + getExtension: function () { + return this.ext; }, /** * Convenience method: return string like ".jpg", or "" if no extension * @return {String} */ - getDotExtension: function() { - return this._ext === null ? '' : '.' + this._ext; + getDotExtension: function () { + return this.ext === null ? '' : '.' + this.ext; }, /** * Return the URL to this title * @return {String} */ - getUrl: function() { + getUrl: function () { return mw.util.wikiGetlink( this.toString() ); }, @@ -316,7 +321,7 @@ var Title = function( title, namespace ) { * Whether this title exists on the wiki. * @return {mixed} Boolean true/false if the information is available. Otherwise null. */ - exists: function() { + exists: function () { return Title.exists( this ); } }; @@ -331,4 +336,4 @@ var Title = function( title, namespace ) { // Expose mw.Title = Title; -})(jQuery); +}( mediaWiki, jQuery ) ); diff --git a/resources/mediawiki/mediawiki.Uri.js b/resources/mediawiki/mediawiki.Uri.js index 95e5e80079..bd12b2147b 100644 --- a/resources/mediawiki/mediawiki.Uri.js +++ b/resources/mediawiki/mediawiki.Uri.js @@ -272,7 +272,7 @@ var args = []; $.each( this.query, function ( key, val ) { var k = Uri.encode( key ), - vals = val === null ? [ null ] : $.makeArray( val ); + vals = $.isArray( val ) ? val : [ val ]; $.each( vals, function ( i, v ) { args.push( k + ( v === null ? '' : '=' + Uri.encode( v ) ) ); } ); @@ -317,11 +317,11 @@ defaultUri = new Uri( documentLocation ); - return Uri; + return Uri; }; // if we are running in a browser, inject the current document location, for relative URLs - if ( document && document.location && document.location.href ) { + if ( document && document.location && document.location.href ) { mw.Uri = mw.UriRelative( document.location.href ); } diff --git a/resources/mediawiki/mediawiki.debug.js b/resources/mediawiki/mediawiki.debug.js index e631c76014..36628eb4fa 100644 --- a/resources/mediawiki/mediawiki.debug.js +++ b/resources/mediawiki/mediawiki.debug.js @@ -5,12 +5,13 @@ * @since 1.19 */ -( function ( $, mw, undefined ) { -"use strict"; +( function ( mw, $ ) { + 'use strict'; - var hovzer = $.getFootHovzer(); + var debug, + hovzer = $.getFootHovzer(); - var debug = mw.Debug = { + debug = mw.Debug = { /** * Toolbar container element * @@ -160,10 +161,10 @@ paneTriggerBitDiv( 'includes', 'PHP includes', this.data.includes.length ); var gitInfo = ''; - if ( this.data.gitRevision != false ) { + if ( this.data.gitRevision !== false ) { gitInfo = '(' + this.data.gitRevision.substring( 0, 7 ) + ')'; - if ( this.data.gitViewUrl != false ) { - gitInfo = $( '' ).attr( 'href', this.data.gitViewUrl ).text( gitInfo ); + if ( this.data.gitViewUrl !== false ) { + gitInfo = $( '' ).attr( 'href', this.data.gitViewUrl ).text( gitInfo ); } } @@ -172,7 +173,7 @@ .append( ': ' + this.data.mwVersion + ' ' ) .append( gitInfo ); - if ( this.data.gitBranch != false ) { + if ( this.data.gitBranch !== false ) { bitDiv( 'gitbranch' ).text( 'Git branch: ' + this.data.gitBranch ); } @@ -223,7 +224,7 @@ $table = $( '' ); - $('').css( 'width', /*padding=*/20 + ( 10*/*fontSize*/11 ) ).appendTo( $table ); + $('').css( 'width', /*padding=*/20 + ( 10 * /*fontSize*/11 ) ).appendTo( $table ); $('').appendTo( $table ); $('').css( 'width', 350 ).appendTo( $table ); @@ -361,4 +362,4 @@ } }; -} )( jQuery, mediaWiki ); +}( mediaWiki, jQuery ) ); diff --git a/resources/mediawiki/mediawiki.feedback.js b/resources/mediawiki/mediawiki.feedback.js index 9a4a729827..f0f78c2e04 100644 --- a/resources/mediawiki/mediawiki.feedback.js +++ b/resources/mediawiki/mediawiki.feedback.js @@ -22,22 +22,22 @@ * Minimal example in how to use it: * * var feedback = new mw.Feedback(); - * $( '#myButton' ).click( function() { feedback.launch(); } ); + * $( '#myButton' ).click( function () { feedback.launch(); } ); * * You can also launch the feedback form with a prefilled subject and body. * See the docs for the launch() method. */ -( function( mw, $, undefined ) { +( function ( mw, $ ) { /** * Thingy for collecting user feedback on a wiki page * @param {Array} options -- optional, all properties optional. - * api: {mw.Api} if omitted, will just create a standard API - * title: {mw.Title} the title of the page where you collect feedback. Defaults to "Feedback". - * dialogTitleMessageKey: {String} message key for the title of the dialog box - * bugsLink: {mw.Uri|String} url where bugs can be posted - * bugsListLink: {mw.Uri|String} url where bugs can be listed + * api: {mw.Api} if omitted, will just create a standard API + * title: {mw.Title} the title of the page where you collect feedback. Defaults to "Feedback". + * dialogTitleMessageKey: {String} message key for the title of the dialog box + * bugsLink: {mw.Uri|String} url where bugs can be posted + * bugsListLink: {mw.Uri|String} url where bugs can be listed */ - mw.Feedback = function( options ) { + mw.Feedback = function ( options ) { if ( options === undefined ) { options = {}; } @@ -67,64 +67,69 @@ }; mw.Feedback.prototype = { - setup: function() { - var _this = this; + setup: function () { + var fb = this; - var $feedbackPageLink = $( '' ) - .attr( { 'href': _this.title.getUrl(), 'target': '_blank' } ) + var $feedbackPageLink = $( '' ) + .attr( { 'href': fb.title.getUrl(), 'target': '_blank' } ) .css( { 'white-space': 'nowrap' } ); - var $bugNoteLink = $( '' ).attr( { 'href': '#' } ).click( function() { _this.displayBugs(); } ); + var $bugNoteLink = $( '' ).attr( { 'href': '#' } ).click( function () { + fb.displayBugs(); + } ); - var $bugsListLink = $( '' ).attr( { 'href': _this.bugsListLink, 'target': '_blank' } ); + var $bugsListLink = $( '' ).attr( { 'href': fb.bugsListLink, 'target': '_blank' } ); + // TODO: Use a stylesheet instead of these inline styles this.$dialog = - $( '
' ).append( + $( '
' ).append( $( '' ).append( - $( '' ).append( - $( '

' ).msg( + $( '' ).append( + $( '

' ).msg( 'feedback-bugornote', $bugNoteLink, - _this.title.getNameText(), + fb.title.getNameText(), $feedbackPageLink.clone() ) ), - $( '

' ).append( + $( '
' ).append( mw.msg( 'feedback-subject' ), - $( '
' ), - $( '' ) + $( '
' ), + $( '' ) ), - $( '
' ).append( + $( '
' ).append( mw.msg( 'feedback-message' ), - $( '
' ), - $( '' ) + $( '
' ), + $( '' ) ) ), $( '' ).append( $( '

' ).msg( 'feedback-bugcheck', $bugsListLink ) ), - $( '

' ).append( + $( '' ).append( mw.msg( 'feedback-adding' ), $( '
' ), $( '' ) ), - $( '' ).msg( - 'feedback-thanks', _this.title.getNameText(), $feedbackPageLink.clone() + $( '' ).msg( + 'feedback-thanks', fb.title.getNameText(), $feedbackPageLink.clone() ), - $( '' ).append( - $( '' ) + $( '' ).append( + $( '' ) ) ); // undo some damage from dialog css - this.$dialog.find( 'a' ).css( { 'color': '#0645ad' } ); + this.$dialog.find( 'a' ).css( { + color: '#0645ad' + } ); this.$dialog.dialog({ width: 500, autoOpen: false, title: mw.msg( this.dialogTitleMessageKey ), modal: true, - buttons: _this.buttons + buttons: fb.buttons }); this.subjectInput = this.$dialog.find( 'input.feedback-subject' ).get(0); @@ -132,94 +137,113 @@ }, - display: function( s ) { + display: function ( s ) { this.$dialog.dialog( { buttons:{} } ); // hide the buttons this.$dialog.find( '.feedback-mode' ).hide(); // hide everything this.$dialog.find( '.feedback-' + s ).show(); // show the desired div }, - displaySubmitting: function() { + displaySubmitting: function () { this.display( 'submitting' ); }, - displayBugs: function() { - var _this = this; + displayBugs: function () { + var fb = this; this.display( 'bugs' ); var bugsButtons = {}; - bugsButtons[ mw.msg( 'feedback-bugnew' ) ] = function() { window.open( _this.bugsLink, '_blank' ); }; - bugsButtons[ mw.msg( 'feedback-cancel' ) ] = function() { _this.cancel(); }; - this.$dialog.dialog( { buttons: bugsButtons } ); + bugsButtons[ mw.msg( 'feedback-bugnew' ) ] = function () { + window.open( fb.bugsLink, '_blank' ); + }; + bugsButtons[ mw.msg( 'feedback-cancel' ) ] = function () { + fb.cancel(); + }; + this.$dialog.dialog( { + buttons: bugsButtons + } ); }, - displayThanks: function() { - var _this = this; + displayThanks: function () { + var fb = this; this.display( 'thanks' ); var closeButton = {}; - closeButton[ mw.msg( 'feedback-close' ) ] = function() { _this.$dialog.dialog( 'close' ); }; - this.$dialog.dialog( { buttons: closeButton } ); + closeButton[ mw.msg( 'feedback-close' ) ] = function () { + fb.$dialog.dialog( 'close' ); + }; + this.$dialog.dialog( { + buttons: closeButton + } ); }, /** * Display the feedback form * @param {Object} optional prefilled contents for the feedback form. Object with properties: - * subject: {String} - * message: {String} + * subject: {String} + * message: {String} */ - displayForm: function( contents ) { - var _this = this; - this.subjectInput.value = (contents && contents.subject) ? contents.subject : ''; - this.messageInput.value = (contents && contents.message) ? contents.message : ''; + displayForm: function ( contents ) { + var fb = this; + 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() { _this.submit(); }; - formButtons[ mw.msg( 'feedback-cancel' ) ] = function() { _this.cancel(); }; + formButtons[ mw.msg( 'feedback-submit' ) ] = function () { + fb.submit(); + }; + formButtons[ mw.msg( 'feedback-cancel' ) ] = function () { + fb.cancel(); + }; this.$dialog.dialog( { buttons: formButtons } ); // put the buttons back }, - displayError: function( message ) { - var _this = this; + displayError: function ( message ) { + var fb = this; this.display( 'error' ); this.$dialog.find( '.feedback-error-msg' ).msg( message ); var closeButton = {}; - closeButton[ mw.msg( 'feedback-close' ) ] = function() { _this.$dialog.dialog( 'close' ); }; + closeButton[ mw.msg( 'feedback-close' ) ] = function () { + fb.$dialog.dialog( 'close' ); + }; this.$dialog.dialog( { buttons: closeButton } ); }, - cancel: function() { + cancel: function () { this.$dialog.dialog( 'close' ); }, - submit: function() { - var _this = this; + submit: function () { + var fb = this; // get the values to submit var subject = this.subjectInput.value; - var message = "User agent: " + navigator.userAgent + "\n\n" + var message = 'User agent: ' + mw.html.escape( navigator.userAgent ) + '\n\n' + this.messageInput.value; - if ( message.indexOf( '~~~' ) == -1 ) { - message += " ~~~~"; + if ( message.indexOf( '~~~' ) === -1 ) { + message += ' ~~~~'; } this.displaySubmitting(); - var ok = function( result ) { + var ok = function ( result ) { if ( result.edit !== undefined ) { if ( result.edit.result === 'Success' ) { - _this.displayThanks(); + fb.displayThanks(); } else { - _this.displayError( 'feedback-error1' ); // unknown API result + // unknown API result + fb.displayError( 'feedback-error1' ); } } else { - _this.displayError( 'feedback-error2' ); // edit failed + // edit failed + fb.displayError( 'feedback-error2' ); } }; - var err = function( code, info ) { - _this.displayError( 'feedback-error3' ); // ajax request failed + var err = function ( code, info ) { + // ajax request failed + fb.displayError( 'feedback-error3' ); }; this.api.newSection( this.title, subject, message, ok, err ); @@ -231,7 +255,7 @@ * subject: {String} * message: {String} */ - launch: function( contents ) { + launch: function ( contents ) { this.displayForm( contents ); this.$dialog.dialog( 'open' ); this.subjectInput.focus(); @@ -239,4 +263,4 @@ }; -} )( window.mediaWiki, jQuery ); +}( mediaWiki, jQuery ) ); diff --git a/resources/mediawiki/mediawiki.htmlform.js b/resources/mediawiki/mediawiki.htmlform.js index 17a02cf461..df62105944 100644 --- a/resources/mediawiki/mediawiki.htmlform.js +++ b/resources/mediawiki/mediawiki.htmlform.js @@ -1,7 +1,7 @@ /** * Utility functions for jazzing up HTMLForm elements */ -( function( $ ) { +( function ( $ ) { /** * jQuery plugin to fade or snap to visible state. @@ -9,7 +9,7 @@ * @param boolean instantToggle (optional) * @return jQuery */ -$.fn.goIn = function( instantToggle ) { +$.fn.goIn = function ( instantToggle ) { if ( instantToggle === true ) { return $(this).show(); } @@ -22,7 +22,7 @@ $.fn.goIn = function( instantToggle ) { * @param boolean instantToggle (optional) * @return jQuery */ -$.fn.goOut = function( instantToggle ) { +$.fn.goOut = function ( instantToggle ) { if ( instantToggle === true ) { return $(this).hide(); } @@ -35,20 +35,20 @@ $.fn.goOut = function( instantToggle ) { * @param callback function taking one paramter, which is Bool true when the event * is called immediately, and the EventArgs object when triggered from an event */ -$.fn.liveAndTestAtStart = function( callback ){ +$.fn.liveAndTestAtStart = function ( callback ){ $(this) .live( 'change', callback ) - .each( function( index, element ){ + .each( function ( index, element ){ callback.call( this, true ); } ); }; // Document ready: -$( function() { +$( function () { // Animate the SelectOrOther fields, to only show the text field when // 'other' is selected. - $( '.mw-htmlform-select-or-other' ).liveAndTestAtStart( function( instant ) { + $( '.mw-htmlform-select-or-other' ).liveAndTestAtStart( function ( instant ) { var $other = $( '#' + $(this).attr( 'id' ) + '-other' ); $other = $other.add( $other.siblings( 'br' ) ); if ( $(this).val() === 'other' ) { @@ -61,4 +61,4 @@ $( function() { }); -})( jQuery ); +}( jQuery ) ); diff --git a/resources/mediawiki/mediawiki.jqueryMsg.js b/resources/mediawiki/mediawiki.jqueryMsg.js index f690a1d30e..def122502e 100644 --- a/resources/mediawiki/mediawiki.jqueryMsg.js +++ b/resources/mediawiki/mediawiki.jqueryMsg.js @@ -1,22 +1,27 @@ /** - * Experimental advanced wikitext parser-emitter. - * See: http://www.mediawiki.org/wiki/Extension:UploadWizard/MessageParser for docs - * - * @author neilk@wikimedia.org - */ - -( function( mw, $, undefined ) { - - mw.jqueryMsg = {}; +* Experimental advanced wikitext parser-emitter. +* See: http://www.mediawiki.org/wiki/Extension:UploadWizard/MessageParser for docs +* +* @author neilk@wikimedia.org +*/ +( function ( mw, $ ) { + var slice = Array.prototype.slice, + parserDefaults = { + magic : { + 'SITENAME' : mw.config.get( 'wgSiteName' ) + }, + messages : mw.messages, + language : mw.language + }; /** * Given parser options, return a function that parses a key and replacements, returning jQuery object * @param {Object} parser options * @return {Function} accepting ( String message key, String replacement1, String replacement2 ... ) and returning {jQuery} */ - function getFailableParserFn( options ) { - var parser = new mw.jqueryMsg.parser( options ); - /** + function getFailableParserFn( options ) { + var parser = new mw.jqueryMsg.parser( options ); + /** * Try to parse a key and optional replacements, returning a jQuery object that may be a tree of jQuery nodes. * If there was an error parsing, return the key and the error message (wrapped in jQuery). This should put the error right into * the interface, without causing the page to halt script execution, and it hopefully should be clearer how to fix it. @@ -24,9 +29,9 @@ * @param {Array} first element is the key, replacements may be in array in 2nd element, or remaining elements. * @return {jQuery} */ - return function( args ) { + return function ( args ) { var key = args[0]; - var argsArray = $.isArray( args[1] ) ? args[1] : $.makeArray( args ).slice( 1 ); + var argsArray = $.isArray( args[1] ) ? args[1] : slice.call( args, 1 ); try { return parser.parse( key, argsArray ); } catch ( e ) { @@ -35,10 +40,12 @@ }; } + mw.jqueryMsg = {}; + /** - * Class method. + * Class method. * Returns a function suitable for use as a global, to construct strings from the message key (and optional replacements). - * e.g. + * e.g. * window.gM = mediaWiki.parser.getMessageFunction( options ); * $( 'p#headline' ).html( gM( 'hello-user', username ) ); * @@ -48,77 +55,68 @@ * @param {Array} parser options * @return {Function} function suitable for assigning to window.gM */ - mw.jqueryMsg.getMessageFunction = function( options ) { + mw.jqueryMsg.getMessageFunction = function ( options ) { var failableParserFn = getFailableParserFn( options ); - /** + /** * N.B. replacements are variadic arguments or an array in second parameter. In other words: - * somefunction(a, b, c, d) - * is equivalent to + * somefunction(a, b, c, d) + * 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 */ - return function( /* key, replacements */ ) { + return function ( /* key, replacements */ ) { return failableParserFn( arguments ).html(); }; }; /** - * Class method. - * Returns a jQuery plugin which parses the message in the message key, doing replacements optionally, and appends the nodes to - * the current selector. Bindings to passed-in jquery elements are preserved. Functions become click handlers for [$1 linktext] links. - * e.g. + * Class method. + * Returns a jQuery plugin which parses the message in the message key, doing replacements optionally, and appends the nodes to + * the current selector. Bindings to passed-in jquery elements are preserved. Functions become click handlers for [$1 linktext] links. + * e.g. * $.fn.msg = mediaWiki.parser.getJqueryPlugin( options ); - * var userlink = $( '
' ).click( function() { alert( "hello!!") } ); + * var userlink = $( '' ).click( function () { alert( "hello!!") } ); * $( 'p#headline' ).msg( 'hello-user', userlink ); * * @param {Array} parser options * @return {Function} function suitable for assigning to jQuery plugin, such as $.fn.msg */ - mw.jqueryMsg.getPlugin = function( options ) { + mw.jqueryMsg.getPlugin = function ( options ) { var failableParserFn = getFailableParserFn( options ); - /** + /** * N.B. replacements are variadic arguments or an array in second parameter. In other words: - * somefunction(a, b, c, d) - * is equivalent to + * somefunction(a, b, c, d) + * is equivalent to * 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) * @return {jQuery} this */ - return function( /* key, replacements */ ) { + return function ( /* key, replacements */ ) { var $target = this.empty(); - $.each( failableParserFn( arguments ).contents(), function( i, node ) { + $.each( failableParserFn( arguments ).contents(), function ( i, node ) { $target.append( node ); } ); return $target; }; }; - var parserDefaults = { - 'magic' : { - 'SITENAME' : mw.config.get( 'wgSiteName' ) - }, - 'messages' : mw.messages, - 'language' : mw.language - }; - /** * The parser itself. * Describes an object, whose primary duty is to .parse() message keys. * @param {Array} options */ - mw.jqueryMsg.parser = function( options ) { + mw.jqueryMsg.parser = function ( options ) { this.settings = $.extend( {}, parserDefaults, options ); this.emitter = new mw.jqueryMsg.htmlEmitter( this.settings.language, this.settings.magic ); }; mw.jqueryMsg.parser.prototype = { - // cache, map of mediaWiki message key to the AST of the message. In most cases, the message is a string so this is identical. // (This is why we would like to move this functionality server-side). astCache: {}, @@ -131,51 +129,46 @@ * @param {Array} replacements for $1, $2... $n * @return {jQuery} */ - parse: function( key, replacements ) { + parse: function ( key, replacements ) { return this.emitter.emit( this.getAst( key ), replacements ); }, - /** * Fetch the message string associated with a key, return parsed structure. Memoized. - * Note that we pass '[' + key + ']' back for a missing message here. + * Note that we pass '[' + key + ']' back for a missing message here. * @param {String} key * @return {String|Array} string of '[key]' if message missing, simple string if possible, array of arrays if needs parsing */ - getAst: function( key ) { - if ( this.astCache[ key ] === undefined ) { + getAst: function ( key ) { + if ( this.astCache[ key ] === undefined ) { var wikiText = this.settings.messages.get( key ); if ( typeof wikiText !== 'string' ) { wikiText = "\\[" + key + "\\]"; } this.astCache[ key ] = this.wikiTextToAst( wikiText ); } - return this.astCache[ key ]; + return this.astCache[ key ]; }, - /* * Parses the input wikiText into an abstract syntax tree, essentially an s-expression. * * CAVEAT: This does not parse all wikitext. It could be more efficient, but it's pretty good already. * n.b. We want to move this functionality to the server. Nothing here is required to be on the client. - * + * * @param {String} message string wikitext * @throws Error * @return {Mixed} abstract syntax tree */ - wikiTextToAst: function( input ) { - - // Indicates current position in input as we parse through it. - // Shared among all parsing functions below. - var pos = 0; + wikiTextToAst: function ( input ) { + // Indicates current position in input as we parse through it. + // Shared among all parsing functions below. + var pos = 0; // ========================================================= // parsing combinators - could be a library on its own // ========================================================= - - - // Try parsers until one works, if none work return null + // Try parsers until one works, if none work return null function choice( ps ) { - return function() { + return function () { for ( var i = 0; i < ps.length; i++ ) { var result = ps[i](); if ( result !== null ) { @@ -185,27 +178,25 @@ return null; }; } - // 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++ ) { + for ( var i = 0; i < ps.length; i++ ) { var res = ps[i](); if ( res === null ) { pos = originalPos; return null; - } + } result.push( res ); } return result; } - // run the same parser over and over until it fails. // must succeed a minimum of n times or return null function nOrMore( n, p ) { - return function() { + return function () { var originalPos = pos; var result = []; var parsed = p(); @@ -216,26 +207,23 @@ if ( result.length < n ) { pos = originalPos; return null; - } + } return result; }; } - // There is a general pattern -- parse a thing, if that worked, apply transform, otherwise return null. // But using this as a combinator seems to cause problems when combined with nOrMore(). // May be some scoping issue function transform( p, fn ) { - return function() { + return function () { var result = p(); return result === null ? null : fn( result ); }; } - // Helpers -- just make ps out of simpler JS builtin types - - function makeStringParser( s ) { + function makeStringParser( s ) { var len = s.length; - return function() { + return function () { var result = null; if ( input.substr( pos, len ) === s ) { result = s; @@ -244,105 +232,87 @@ return result; }; } - function makeRegexParser( regex ) { - return function() { + return function () { var matches = input.substr( pos ).match( regex ); - if ( matches === null ) { + if ( matches === null ) { return null; - } + } pos += matches[0].length; return matches[0]; }; } - - /** - * =================================================================== + /** + * =================================================================== * General patterns above this line -- wikitext specific parsers below - * =================================================================== + * =================================================================== */ - // Parsing functions follow. All parsing functions work like this: // They don't accept any arguments. // Instead, they just operate non destructively on the string 'input' // As they can consume parts of the string, they advance the shared variable pos, // and return tokens (or whatever else they want to return). - // some things are defined as closures and other things as ordinary functions // converting everything to a closure makes it a lot harder to debug... errors pop up // 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 regularLiteral = makeRegexParser( /^[^{}\[\]$\\]/ ); + var regularLiteralWithoutBar = makeRegexParser(/^[^{}\[\]$\\|]/); + var regularLiteralWithoutSpace = makeRegexParser(/^[^{}\[\]$\s]/); var backslash = makeStringParser( "\\" ); var anyCharacter = makeRegexParser( /^./ ); - function escapedLiteral() { var result = sequence( [ - backslash, + backslash, anyCharacter ] ); return result === null ? null : result[1]; } - var escapedOrLiteralWithoutSpace = choice( [ escapedLiteral, regularLiteralWithoutSpace ] ); - var escapedOrLiteralWithoutBar = choice( [ escapedLiteral, regularLiteralWithoutBar ] ); - - var escapedOrRegularLiteral = choice( [ + var escapedOrRegularLiteral = choice( [ escapedLiteral, regularLiteral ] ); - // Used to define "literals" without spaces, in space-delimited situations function literalWithoutSpace() { var result = nOrMore( 1, escapedOrLiteralWithoutSpace )(); return result === null ? null : result.join(''); } - - // Used to define "literals" within template parameters. The pipe character is the parameter delimeter, so by default + // Used to define "literals" within template parameters. The pipe character is the parameter delimeter, so by default // it is not a literal in the parameter function literalWithoutBar() { var result = nOrMore( 1, escapedOrLiteralWithoutBar )(); return result === null ? null : result.join(''); } - function literal() { var result = nOrMore( 1, escapedOrRegularLiteral )(); return result === null ? null : result.join(''); } - - var whitespace = makeRegexParser( /^\s+/ ); + var whitespace = makeRegexParser( /^\s+/ ); var dollar = makeStringParser( '$' ); - var digits = makeRegexParser( /^\d+/ ); + var digits = makeRegexParser( /^\d+/ ); function replacement() { var result = sequence( [ dollar, digits ] ); - if ( result === null ) { + if ( result === null ) { return null; } return [ 'REPLACE', parseInt( result[1], 10 ) - 1 ]; } - - var openExtlink = makeStringParser( '[' ); var closeExtlink = makeStringParser( ']' ); - // this extlink MUST have inner text, e.g. [foo] not allowed; [foo bar] is allowed function extlink() { var result = null; @@ -358,7 +328,6 @@ } return result; } - // this is the same as the above extlink, except that the url is being passed on as a parameter function extLinkParam() { var result = sequence( [ @@ -374,10 +343,8 @@ } return [ 'LINKPARAM', parseInt( result[2], 10 ) - 1, result[4] ]; } - var openLink = makeStringParser( '[[' ); var closeLink = makeStringParser( ']]' ); - function link() { var result = null; var parsedResult = sequence( [ @@ -390,16 +357,14 @@ } return result; } - - var templateName = transform( + var 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(); } + makeRegexParser( /^[ !"$&'()*,.\/0-9;=?@A-Z\^_`a-z~\x80-\xFF+\-]+/ ), + function ( result ) { return result.toString(); } ); - function templateParam() { - var result = sequence( [ + var result = sequence( [ pipe, nOrMore( 0, paramExpression ) ] ); @@ -410,9 +375,7 @@ // 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( '|' ); - function templateWithReplacement() { var result = sequence( [ templateName, @@ -421,7 +384,6 @@ ] ); return result === null ? null : [ result[0], result[2] ]; } - function templateWithOutReplacement() { var result = sequence( [ templateName, @@ -430,11 +392,9 @@ ] ); return result === null ? null : [ result[0], result[2] ]; } - var colon = makeStringParser(':'); - var templateContents = choice( [ - function() { + function () { var res = sequence( [ // templates can have placeholders for dynamic replacement eg: {{PLURAL:$1|one car|$1 cars}} // or no placeholders eg: {{GRAMMAR:genitive|{{SITENAME}}} @@ -443,10 +403,10 @@ ] ); return res === null ? null : res[0].concat( res[1] ); }, - function() { + function () { var res = sequence( [ templateName, - nOrMore( 0, templateParam ) + nOrMore( 0, templateParam ) ] ); if ( res === null ) { return null; @@ -454,10 +414,8 @@ return [ res[0] ].concat( res[1] ); } ] ); - var openTemplate = makeStringParser('{{'); var closeTemplate = makeStringParser('}}'); - function template() { var result = sequence( [ openTemplate, @@ -466,7 +424,6 @@ ] ); return result === null ? null : result[1]; } - var nonWhitespaceExpression = choice( [ template, link, @@ -475,7 +432,6 @@ replacement, literalWithoutSpace ] ); - var paramExpression = choice( [ template, link, @@ -484,16 +440,14 @@ replacement, literalWithoutBar ] ); - - var expression = choice( [ + var expression = choice( [ template, link, extLinkParam, extlink, replacement, - literal + literal ] ); - function start() { var result = nOrMore( 0, expression )(); if ( result === null ) { @@ -501,16 +455,13 @@ } 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(); - + /* - * For success, the p must have gotten to the end of the input + * 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. */ @@ -519,20 +470,19 @@ } return result; } - - }; + }; /** * htmlEmitter - object which primarily exists to emit HTML from parser ASTs */ - mw.jqueryMsg.htmlEmitter = function( language, magic ) { + mw.jqueryMsg.htmlEmitter = function ( language, magic ) { this.language = language; - var _this = this; - - $.each( magic, function( key, val ) { - _this[ key.toLowerCase() ] = function() { return val; }; + var jmsg = this; + $.each( magic, function ( key, val ) { + jmsg[ key.toLowerCase() ] = function () { + return val; + }; } ); - /** * (We put this method definition here, and not in prototype, to make sure it's not overwritten by any magic.) * Walk entire node structure, applying replacements and template functions when appropriate @@ -540,23 +490,23 @@ * @param {Array} replacements for $1, $2, ... $n * @return {Mixed} single-string node or array of nodes suitable for jQuery appending */ - this.emit = function( node, replacements ) { + this.emit = function ( node, replacements ) { var ret = null; - var _this = this; - switch( typeof node ) { + var 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 ) { - return _this.emit( n, replacements ); + var subnodes = $.map( node.slice( 1 ), function ( n ) { + return jmsg.emit( n, replacements ); } ); var operation = node[0].toLowerCase(); - if ( typeof _this[operation] === 'function' ) { - ret = _this[ operation ]( subnodes, replacements ); + if ( typeof jmsg[operation] === 'function' ) { + ret = jmsg[ operation ]( subnodes, replacements ); } else { - throw new Error( 'unknown operation "' + operation + '"' ); + throw new Error( 'Unknown operation "' + operation + '"' ); } break; case 'undefined': @@ -566,21 +516,18 @@ ret = ''; break; default: - throw new Error( 'unexpected type in AST: ' + typeof node ); + throw new Error( 'Unexpected type in AST: ' + typeof node ); } return ret; }; - }; - // For everything in input that follows double-open-curly braces, there should be an equivalent parser - // function. For instance {{PLURAL ... }} will be processed by 'plural'. + // function. For instance {{PLURAL ... }} will be processed by 'plural'. // If you have 'magic words' then configure the parser to have them upon creation. // // An emitter method takes the parent node, the array of subnodes and the array of replacements (the values that $1, $2... should translate to). // Note: all such functions must be pure, with the exception of referring to other pure functions via this.language (convertPlural and so on) mw.jqueryMsg.htmlEmitter.prototype = { - /** * Parsing has been applied depth-first we can assume that all nodes here are single nodes * Must return a single node to parents -- a jQuery with synthetic span @@ -588,19 +535,19 @@ * @param {Array} nodes - mixed, some single nodes, some arrays of nodes * @return {jQuery} */ - concat: function( nodes ) { - var span = $( '' ).addClass( 'mediaWiki_htmlEmitter' ); - $.each( nodes, function( i, node ) { + concat: function ( nodes ) { + var $span = $( '' ).addClass( 'mediaWiki_htmlEmitter' ); + $.each( nodes, function ( i, node ) { if ( node instanceof jQuery && node.hasClass( 'mediaWiki_htmlEmitter' ) ) { - $.each( node.contents(), function( j, childNode ) { - span.append( childNode ); + $.each( node.contents(), function ( j, childNode ) { + $span.append( childNode ); } ); } else { // strings, integers, anything else - span.append( node ); + $span.append( node ); } } ); - return span; + return $span; }, /** @@ -612,9 +559,9 @@ * @param {Array} of one element, integer, n >= 0 * @return {String} replacement */ - replace: function( nodes, replacements ) { + replace: function ( nodes, replacements ) { var index = parseInt( nodes[0], 10 ); - + if ( index < replacements.length ) { if ( typeof arg === 'string' ) { // replacement is a string, escape it @@ -629,12 +576,12 @@ } }, - /** + /** * Transform wiki-link - * TODO unimplemented + * TODO unimplemented */ - wlink: function( nodes ) { - return "unimplemented"; + wlink: function ( nodes ) { + return 'unimplemented'; }, /** @@ -642,14 +589,14 @@ * If the href is a jQuery object, treat it as "enclosing" the link text. * ... function, treat it as the click handler * ... string, treat it as a URI - * TODO: throw an error if nodes.length > 2 ? + * TODO: throw an error if nodes.length > 2 ? * @param {Array} of two elements, {jQuery|Function|String} and {String} * @return {jQuery} */ - link: function( nodes ) { + link: function ( nodes ) { var arg = nodes[0]; var contents = nodes[1]; - var $el; + var $el; if ( arg instanceof jQuery ) { $el = arg; } else { @@ -660,7 +607,7 @@ $el.attr( 'href', arg.toString() ); } } - $el.append( contents ); + $el.append( contents ); return $el; }, @@ -673,7 +620,7 @@ * @param {Array} of one element, integer, n >= 0 * @return {String} replacement */ - linkparam: function( nodes, replacements ) { + linkparam: function ( nodes, replacements ) { var replacement, index = parseInt( nodes[0], 10 ); if ( index < replacements.length) { @@ -688,10 +635,10 @@ * Transform parsed structure into pluralization * n.b. The first node may be a non-integer (for instance, a string representing an Arabic number). * So convert it back with the current language's convertNumber. - * @param {Array} of nodes, [ {String|Number}, {String}, {String} ... ] + * @param {Array} of nodes, [ {String|Number}, {String}, {String} ... ] * @return {String} selected pluralized form according to current language */ - plural: function( nodes ) { + plural: function ( nodes ) { var count = parseInt( this.language.convertNumber( nodes[0], true ), 10 ); var forms = nodes.slice(1); return forms.length ? this.language.convertPlural( count, forms ) : ''; @@ -700,10 +647,10 @@ /** * Transform parsed structure into gender * Usage {{gender:[gender| mw.user object ] | masculine|feminine|neutral}}. - * @param {Array} of nodes, [ {String|mw.User}, {String}, {String} , {String} ] + * @param {Array} of nodes, [ {String|mw.User}, {String}, {String} , {String} ] * @return {String} selected gender form according to current language */ - gender: function( nodes ) { + gender: function ( nodes ) { var gender; if ( nodes[0] && nodes[0].options instanceof mw.Map ){ gender = nodes[0].options.get( 'gender' ); @@ -720,35 +667,32 @@ * @param {Array} of nodes [{Grammar case eg: genitive}, {String word}] * @return {String} selected grammatical form according to current language */ - grammar: function( nodes ) { + grammar: function ( nodes ) { var form = nodes[0]; var word = nodes[1]; return word && form && this.language.convertGrammar( word, form ); } }; - - // deprecated! don't rely on gM existing. - // the window.gM ought not to be required - or if required, not required here. But moving it to extensions breaks it (?!) + // Deprecated! don't rely on gM existing. + // The window.gM ought not to be required - or if required, not required here. + // But moving it to extensions breaks it (?!) // Need to fix plugin so it could do attributes as well, then will be okay to remove this. - window.gM = mw.jqueryMsg.getMessageFunction(); - + window.gM = mw.jqueryMsg.getMessageFunction(); $.fn.msg = mw.jqueryMsg.getPlugin(); - + // Replace the default message parser with jqueryMsg var oldParser = mw.Message.prototype.parser; - mw.Message.prototype.parser = function() { + 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 // we'd have to cache the parser as a member of this.map, which sounds a bit ugly. - // Do not use mw.jqueryMsg unless required if ( this.map.get( this.key ).indexOf( '{{' ) < 0 ) { // Fall back to mw.msg's simple parser return oldParser.apply( this ); } - var messageFunction = mw.jqueryMsg.getMessageFunction( { 'messages': this.map } ); return messageFunction( this.key, this.parameters ); }; -} )( mediaWiki, jQuery ); +}( mediaWiki, jQuery ) ); diff --git a/resources/mediawiki/mediawiki.js b/resources/mediawiki/mediawiki.js index 41a5078102..e0c65e80f0 100644 --- a/resources/mediawiki/mediawiki.js +++ b/resources/mediawiki/mediawiki.js @@ -1,5 +1,3 @@ -/*jslint browser: true, continue: true, white: true, forin: true*/ -/*global jQuery*/ /* * Core MediaWiki JavaScript Library */ @@ -9,7 +7,9 @@ var mw = ( function ( $, undefined ) { /* Private Members */ - var hasOwn = Object.prototype.hasOwnProperty; + var hasOwn = Object.prototype.hasOwnProperty, + slice = Array.prototype.slice; + /* Object constructors */ /** @@ -45,7 +45,7 @@ var mw = ( function ( $, undefined ) { var results, i; if ( $.isArray( selection ) ) { - selection = $.makeArray( selection ); + selection = slice.call( selection ); results = {}; for ( i = 0; i < selection.length; i += 1 ) { results[selection[i]] = this.get( selection[i], fallback ); @@ -130,7 +130,7 @@ var mw = ( function ( $, undefined ) { this.format = 'plain'; this.map = map; this.key = key; - this.parameters = parameters === undefined ? [] : $.makeArray( parameters ); + this.parameters = parameters === undefined ? [] : slice.call( parameters ); return this; } @@ -141,7 +141,7 @@ var mw = ( function ( $, undefined ) { * * This function will not be called for nonexistent messages. */ - parser: function() { + parser: function () { var parameters = this.parameters; return this.map.get( this.key ).replace( /\$(\d+)/g, function ( str, match ) { var index = parseInt( match, 10 ) - 1; @@ -168,7 +168,7 @@ var mw = ( function ( $, undefined ) { * * @return string Message as a string in the current form or if key does not exist. */ - toString: function() { + toString: function () { var text; if ( !this.exists() ) { @@ -205,7 +205,7 @@ var mw = ( function ( $, undefined ) { * * @return {string} String form of parsed message */ - parse: function() { + parse: function () { this.format = 'parse'; return this.toString(); }, @@ -215,7 +215,7 @@ var mw = ( function ( $, undefined ) { * * @return {string} String form of plain message */ - plain: function() { + plain: function () { this.format = 'plain'; return this.toString(); }, @@ -225,7 +225,7 @@ var mw = ( function ( $, undefined ) { * * @return {string} String form of html escaped message */ - escaped: function() { + escaped: function () { this.format = 'escaped'; return this.toString(); }, @@ -235,7 +235,7 @@ var mw = ( function ( $, undefined ) { * * @return {string} String form of parsed message */ - exists: function() { + exists: function () { return this.map.exists( this.key ); } }; @@ -247,7 +247,7 @@ var mw = ( function ( $, undefined ) { * Dummy function which in debug mode can be replaced with a function that * emulates console.log in console-less environments. */ - log: function() { }, + log: function () { }, /** * @var constructor Make the Map constructor publicly available. @@ -298,7 +298,7 @@ var mw = ( function ( $, undefined ) { var parameters; // Support variadic arguments if ( parameter_1 !== undefined ) { - parameters = $.makeArray( arguments ); + parameters = slice.call( arguments ); parameters.shift(); } else { parameters = []; @@ -341,7 +341,7 @@ var mw = ( function ( $, undefined ) { * { * 'moduleName': { * 'version': ############## (unix timestamp), - * 'dependencies': ['required.foo', 'bar.also', ...], (or) function() {} + * 'dependencies': ['required.foo', 'bar.also', ...], (or) function () {} * 'group': 'somegroup', (or) null, * 'source': 'local', 'someforeignwiki', (or) null * 'state': 'registered', 'loading', 'loaded', 'ready', 'error' or 'missing' @@ -717,6 +717,7 @@ var mw = ( function ( $, undefined ) { * @param callback Function: Optional callback which will be run when the script is done */ function addScript( src, callback, async ) { + /*jshint evil:true */ var script, head, done = false; @@ -731,7 +732,7 @@ var mw = ( function ( $, undefined ) { script.setAttribute( 'type', 'text/javascript' ); if ( $.isFunction( callback ) ) { // Attach handlers for all browsers (based on jQuery.ajax) - script.onload = script.onreadystatechange = function() { + script.onload = script.onreadystatechange = function () { if ( !done @@ -768,7 +769,7 @@ var mw = ( function ( $, undefined ) { // scripts only start loading after the document has been rendered, // but so be it. Opera users don't deserve faster web pages if their // browser makes it impossible - $( function() { document.body.appendChild( script ); } ); + $( function () { document.body.appendChild( script ); } ); } else { // IE-safe way of getting the . document.documentElement.head doesn't // work in scripts that run in the @@ -830,7 +831,7 @@ var mw = ( function ( $, undefined ) { // Execute script try { script = registry[module].script; - markModuleReady = function() { + markModuleReady = function () { registry[module].state = 'ready'; handlePending( module ); }; @@ -843,7 +844,7 @@ var mw = ( function ( $, undefined ) { return; } - addScript( arr[i], function() { + addScript( arr[i], function () { nestedAddScript( arr, callback, async, i + 1 ); }, async ); }; diff --git a/resources/mediawiki/mediawiki.log.js b/resources/mediawiki/mediawiki.log.js index ad4c73df74..9ce83054a1 100644 --- a/resources/mediawiki/mediawiki.log.js +++ b/resources/mediawiki/mediawiki.log.js @@ -6,7 +6,7 @@ * @author Trevor Parscal */ -( function ( $ ) { +( function ( mw, $ ) { /** * Logs a message to the console. @@ -17,7 +17,7 @@ * * @param {String} First in list of variadic messages to output to console. */ - mw.log = function( /* logmsg, logmsg, */ ) { + mw.log = function ( /* logmsg, logmsg, */ ) { // Turn arguments into an array var args = Array.prototype.slice.call( arguments ), // Allow log messages to use a configured prefix to identify the source window (ie. frame) @@ -67,4 +67,4 @@ } ); }; -})( jQuery ); +}( mediaWiki, jQuery ) ); diff --git a/resources/mediawiki/mediawiki.user.js b/resources/mediawiki/mediawiki.user.js index 48f30df7f0..b5f124ba60 100644 --- a/resources/mediawiki/mediawiki.user.js +++ b/resources/mediawiki/mediawiki.user.js @@ -2,7 +2,7 @@ * Implementation for mediaWiki.user */ -( function ( $ ) { +( function ( mw, $ ) { /** * User object @@ -138,7 +138,10 @@ id = generateId(); } // Set cookie if not set, or renew it if already set - $.cookie( 'mediaWiki.user.id', id, { 'expires': 365, 'path': '/' } ); + $.cookie( 'mediaWiki.user.id', id, { + expires: 365, + path: '/' + } ); return id; }; @@ -165,7 +168,7 @@ * 'expires': 7 * } ); */ - this.bucket = function( key, options ) { + this.bucket = function ( key, options ) { options = $.extend( { 'buckets': {}, 'version': 0, @@ -178,7 +181,7 @@ // Bucket information is stored as 2 integers, together as version:bucket like: "1:2" if ( typeof cookie === 'string' && cookie.length > 2 && cookie.indexOf( ':' ) > 0 ) { var parts = cookie.split( ':' ); - if ( parts.length > 1 && parts[0] == options.version ) { + if ( parts.length > 1 && Number( parts[0] ) === options.version ) { version = Number( parts[0] ); bucket = String( parts[1] ); } @@ -205,7 +208,7 @@ } } if ( options.tracked ) { - mw.loader.using( 'jquery.clickTracking', function() { + mw.loader.using( 'jquery.clickTracking', function () { $.trackAction( 'mediaWiki.user.bucket:' + key + '@' + version + ':' + bucket ); @@ -239,4 +242,4 @@ // This is kind of ugly but we're stuck with this for b/c reasons mw.user = new User( mw.user.options, mw.user.tokens ); -}( jQuery ) ); +}( mediaWiki, jQuery ) ); diff --git a/resources/mediawiki/mediawiki.util.js b/resources/mediawiki/mediawiki.util.js index 541c77a4df..c3e5e98eb2 100644 --- a/resources/mediawiki/mediawiki.util.js +++ b/resources/mediawiki/mediawiki.util.js @@ -1,8 +1,8 @@ /** * Implements mediaWiki.util library */ -( function ( $, mw ) { - "use strict"; +( function ( mw, $ ) { + 'use strict'; // Local cache and alias var util = { @@ -601,4 +601,4 @@ mw.util = util; -} )( jQuery, mediaWiki ); +}( mediaWiki, jQuery ) ); -- 2.20.1