r70197 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r70196‎ | r70197 | r70198 >
Date:18:26, 30 July 2010
Author:tparscal
Status:deferred
Tags:
Comment:
Ported more js from skins/common
Modified paths:
  • /branches/resourceloader/phase3/resources/mw/legacy/mw.legacy.IEFixes.js (modified) (history)
  • /branches/resourceloader/phase3/resources/mw/legacy/mw.legacy.ajax.js (modified) (history)
  • /branches/resourceloader/phase3/resources/mw/legacy/mw.legacy.ajaxwatch.js (modified) (history)
  • /branches/resourceloader/phase3/resources/mw/legacy/mw.legacy.block.js (modified) (history)
  • /branches/resourceloader/phase3/resources/mw/legacy/mw.legacy.changepassword.js (modified) (history)
  • /branches/resourceloader/phase3/resources/mw/legacy/mw.legacy.edit.js (modified) (history)
  • /branches/resourceloader/phase3/resources/mw/legacy/mw.legacy.enhancedchanges.js (modified) (history)
  • /branches/resourceloader/phase3/resources/mw/legacy/mw.legacy.history.js (modified) (history)
  • /branches/resourceloader/phase3/resources/mw/legacy/mw.legacy.htmlform.js (modified) (history)
  • /branches/resourceloader/phase3/resources/mw/legacy/mw.legacy.metadata.js (modified) (history)
  • /branches/resourceloader/phase3/resources/mw/legacy/mw.legacy.mwsuggest.js (modified) (history)
  • /branches/resourceloader/phase3/resources/mw/legacy/mw.legacy.prefs.js (modified) (history)
  • /branches/resourceloader/phase3/resources/mw/legacy/mw.legacy.preview.js (modified) (history)
  • /branches/resourceloader/phase3/resources/mw/legacy/mw.legacy.protect.js (modified) (history)
  • /branches/resourceloader/phase3/resources/mw/legacy/mw.legacy.rightclickedit.js (modified) (history)
  • /branches/resourceloader/phase3/resources/mw/legacy/mw.legacy.search.js (modified) (history)
  • /branches/resourceloader/phase3/resources/mw/legacy/mw.legacy.upload.js (modified) (history)
  • /branches/resourceloader/phase3/resources/mw/legacy/mw.legacy.wikibits.js (modified) (history)

Diff [purge]

Index: branches/resourceloader/phase3/resources/mw/legacy/mw.legacy.prefs.js
@@ -10,7 +10,7 @@
1111
1212 /* Extension */
1313
14 -$.extend( mw.legacy, {
 14+$.extend( true, mw.legacy, {
1515
1616 /* Functions */
1717
Index: branches/resourceloader/phase3/resources/mw/legacy/mw.legacy.metadata.js
@@ -6,7 +6,7 @@
77
88 /* Extension */
99
10 -$.extend( mw.legacy, {
 10+$.extend( true, mw.legacy, {
1111 /**
1212 * Exif metadata display for MediaWiki file uploads
1313 *
Index: branches/resourceloader/phase3/resources/mw/legacy/mw.legacy.protect.js
@@ -6,7 +6,7 @@
77
88 /* Extension */
99
10 -$.extend( mw.legacy, {
 10+$.extend( true, mw.legacy, {
1111
1212 /* Global Variables */
1313
Index: branches/resourceloader/phase3/resources/mw/legacy/mw.legacy.preview.js
@@ -8,7 +8,7 @@
99
1010 /* Extension */
1111
12 -$.extend( mw.legacy, {
 12+$.extend( true, mw.legacy, {
1313
1414 /* Functions */
1515
Index: branches/resourceloader/phase3/resources/mw/legacy/mw.legacy.ajax.js
@@ -10,7 +10,7 @@
1111
1212 /* Extension */
1313
14 -$.extend( mw.legacy, {
 14+$.extend( true, mw.legacy, {
1515
1616 /* Global Variables */
1717
Index: branches/resourceloader/phase3/resources/mw/legacy/mw.legacy.upload.js
@@ -6,7 +6,7 @@
77
88 /* Extension */
99
10 -$.extend( mw.legacy, {
 10+$.extend( true, mw.legacy, {
1111
1212 /* Global Variables */
1313
@@ -26,7 +26,7 @@
2727 if ( !wgAjaxUploadDestCheck || !sajax_init_object() ) return;
2828
2929 // Find file to upload
30 - var destFile = document.getElementById('wpDestFile');
 30+ var destFile = document.getElementById( 'wpDestFile' );
3131 var warningElt = document.getElementById( 'wpDestFile-warning' );
3232 if ( !destFile || !warningElt ) return ;
3333 this.nameToCheck = destFile.value ;
@@ -35,15 +35,15 @@
3636 window.clearTimeout( this.timeoutID );
3737 }
3838 // Check response cache
39 - for (cached in this.responseCache) {
40 - if (this.nameToCheck == cached) {
41 - this.setWarning(this.responseCache[this.nameToCheck]);
 39+ for ( cached in this.responseCache ) {
 40+ if ( this.nameToCheck == cached ) {
 41+ this.setWarning( this.responseCache[this.nameToCheck] );
4242 return;
4343 }
4444 }
4545 this.timeoutID = window.setTimeout( 'wgUploadWarningObj.timeout()', this.delay );
4646 },
47 - 'checkNow': function (fname) {
 47+ 'checkNow': function ( fname ) {
4848 if ( !wgAjaxUploadDestCheck || !sajax_init_object() ) return;
4949 if ( this.timeoutID ) {
5050 window.clearTimeout( this.timeoutID );
@@ -61,21 +61,21 @@
6262 var obj = this;
6363 var fileName = this.nameToCheck;
6464 sajax_do_call( 'SpecialUpload::ajaxGetExistsWarning', [this.nameToCheck],
65 - function (result) {
66 - obj.processResult(result, fileName)
 65+ function ( result ) {
 66+ obj.processResult( result, fileName )
6767 }
68 - );
 68+ );
6969 },
70 - 'processResult' : function (result, fileName) {
 70+ 'processResult' : function ( result, fileName ) {
7171 removeSpinner( 'destcheck' );
72 - this.setWarning(result.responseText);
 72+ this.setWarning( result.responseText );
7373 this.responseCache[fileName] = result.responseText;
7474 },
75 - 'setWarning' : function (warning) {
 75+ 'setWarning' : function ( warning ) {
7676 var warningElt = document.getElementById( 'wpDestFile-warning' );
7777 var ackElt = document.getElementsByName( 'wpDestFileWarningAck' );
7878
79 - this.setInnerHTML(warningElt, warning);
 79+ this.setInnerHTML( warningElt, warning );
8080
8181 // Set a value in the form indicating that the warning is acknowledged and
8282 // doesn't need to be redisplayed post-upload
@@ -85,9 +85,9 @@
8686 ackElt[0].value = '1';
8787 }
8888 },
89 - 'setInnerHTML' : function (element, text) {
 89+ 'setInnerHTML' : function ( element, text ) {
9090 // Check for no change to avoid flicker in IE 7
91 - if (element.innerHTML != text) {
 91+ if ( element.innerHTML != text ) {
9292 element.innerHTML = text;
9393 }
9494 }
@@ -101,15 +101,15 @@
102102 /* Functions */
103103
104104 'fetchPreview': function( license ) {
105 - if( !wgAjaxLicensePreview ) return;
106 - for (cached in this.responseCache) {
107 - if (cached == license) {
 105+ if ( !wgAjaxLicensePreview ) return;
 106+ for ( cached in this.responseCache ) {
 107+ if ( cached == license ) {
108108 this.showPreview( this.responseCache[license] );
109109 return;
110110 }
111111 }
112112 injectSpinner( document.getElementById( 'wpLicense' ), 'license' );
113 - var title = document.getElementById('wpDestFile').value;
 113+ var title = document.getElementById( 'wpDestFile' ).value;
114114 if ( !title ) title = 'File:Sample.jpg';
115115 var url = wgScriptPath + '/api' + wgScriptExtension
116116 + '?action=parse&text={{' + encodeURIComponent( license ) + '}}'
@@ -118,7 +118,7 @@
119119 var req = sajax_init_object();
120120 req.onreadystatechange = function() {
121121 if ( req.readyState == 4 && req.status == 200 )
122 - wgUploadLicenseObj.processResult( eval( '(' + req.responseText + ')' ), license );
 122+ wgUploadLicenseObj.processResult( eval( '( ' + req.responseText + ' )' ), license );
123123 };
124124 req.open( 'GET', url, true );
125125 req.send( '' );
@@ -138,10 +138,10 @@
139139 /* Functions */
140140
141141 'licenseSelectorCheck': function() {
142 - var selector = document.getElementById( "wpLicense" );
 142+ var selector = document.getElementById( 'wpLicense' );
143143 var selection = selector.options[selector.selectedIndex].value;
144144 if( selector.selectedIndex > 0 ) {
145 - if( selection == "" ) {
 145+ if( selection == '' ) {
146146 // Option disabled, but browser is broken and doesn't respect this
147147 selector.selectedIndex = 0;
148148 }
@@ -155,18 +155,18 @@
156156 if( e ) {
157157 if( !e.checked ) {
158158 var ein = document.getElementById( 'wpUploadFileURL' );
159 - if(ein)
 159+ if( ein )
160160 ein.setAttribute( 'disabled', 'disabled' );
161161 }
162162 }
163163 // For MSIE/Mac: non-breaking spaces cause the <option> not to render.
164164 // But for some reason, setting the text to itself works
165 - var selector = document.getElementById("wpLicense");
166 - if (selector) {
 165+ var selector = document.getElementById( 'wpLicense' );
 166+ if ( selector ) {
167167 var ua = navigator.userAgent;
168 - var isMacIe = (ua.indexOf("MSIE") != -1) && (ua.indexOf("Mac") != -1);
169 - if (isMacIe) {
170 - for (var i = 0; i < selector.options.length; i++) {
 168+ var isMacIe = ( ua.indexOf( 'MSIE' ) != -1 ) && ( ua.indexOf( 'Mac' ) != -1 );
 169+ if ( isMacIe ) {
 170+ for ( var i = 0; i < selector.options.length; i++ ) {
171171 selector.options[i].text = selector.options[i].text;
172172 }
173173 }
@@ -181,9 +181,9 @@
182182 // Insert an event handler that fetches upload warnings when wpDestFile
183183 // has been changed
184184 document.getElementById( 'wpDestFile' ).onchange = function ( e ) {
185 - wgUploadWarningObj.checkNow(this.value);
 185+ wgUploadWarningObj.checkNow( this.value );
186186 };
187 - // Insert a row where the warnings will be displayed just below the
 187+ // Insert a row where the warnings will be displayed just below the
188188 // wpDestFile row
189189 var optionsTable = document.getElementById( 'mw-htmlform-description' ).tBodies[0];
190190 var row = optionsTable.insertRow( 1 );
@@ -209,14 +209,15 @@
210210 }
211211 // fillDestFile setup
212212 for ( var i = 0; i < wgUploadSourceIds.length; i++ )
213 - document.getElementById( wgUploadSourceIds[i] ).onchange = function (e) {
 213+ document.getElementById( wgUploadSourceIds[i] ).onchange = function ( e ) {
214214 fillDestFilename( this.id );
215215 };
216216 },
217217 /**
218218 * Iterate over all upload source fields and disable all except the selected one.
219219 *
220 - * @param enabledId The id of the selected radio button
 220+ * @param enabledId
 221+ * The id of the selected radio button
221222 * @return emptiness
222223 */
223224 'toggleUploadInputs': function() {
@@ -243,16 +244,16 @@
244245 }
245246 // Disable all unselected rows
246247 for ( var j = 0; j < inputs.length; j++ ) {
247 - if ( inputs[j].type != 'radio')
 248+ if ( inputs[j].type != 'radio' )
248249 inputs[j].disabled = !isChecked;
249250 }
250251 }
251252 },
252 - 'fillDestFilename': function(id) {
253 - if (!wgUploadAutoFill) {
 253+ 'fillDestFilename': function( id ) {
 254+ if ( !wgUploadAutoFill ) {
254255 return;
255256 }
256 - if (!document.getElementById) {
 257+ if ( !document.getElementById ) {
257258 return;
258259 }
259260 // Remove any previously flagged errors
@@ -260,62 +261,62 @@
261262 if( e ) e.className = '';
262263 var e = document.getElementById( 'mw-upload-prohibited' );
263264 if( e ) e.className = '';
264 - var path = document.getElementById(id).value;
 265+ var path = document.getElementById( id ).value;
265266 // Find trailing part
266 - var slash = path.lastIndexOf('/');
267 - var backslash = path.lastIndexOf('\\');
 267+ var slash = path.lastIndexOf( '/' );
 268+ var backslash = path.lastIndexOf( '\\' );
268269 var fname;
269 - if (slash == -1 && backslash == -1) {
 270+ if ( slash == -1 && backslash == -1 ) {
270271 fname = path;
271 - } else if (slash > backslash) {
272 - fname = path.substring(slash+1, 10000);
 272+ } else if ( slash > backslash ) {
 273+ fname = path.substring( slash+1, 10000 );
273274 } else {
274 - fname = path.substring(backslash+1, 10000);
 275+ fname = path.substring( backslash+1, 10000 );
275276 }
276277 // Clear the filename if it does not have a valid extension.
277 - // URLs are less likely to have a useful extension, so don't include them in the
 278+ // URLs are less likely to have a useful extension, so don't include them in the
278279 // extension check.
279280 if( wgStrictFileExtensions && wgFileExtensions && id != 'wpUploadFileURL' ) {
280281 var found = false;
281 - if( fname.lastIndexOf( '.' ) != -1 ) {
 282+ if ( fname.lastIndexOf( '.' ) != -1 ) {
282283 var ext = fname.substr( fname.lastIndexOf( '.' ) + 1 );
283 - for( var i = 0; i < wgFileExtensions.length; i++ ) {
284 - if( wgFileExtensions[i].toLowerCase() == ext.toLowerCase() ) {
 284+ for ( var i = 0; i < wgFileExtensions.length; i++ ) {
 285+ if ( wgFileExtensions[i].toLowerCase() == ext.toLowerCase() ) {
285286 found = true;
286287 break;
287288 }
288289 }
289290 }
290 - if( !found ) {
 291+ if ( !found ) {
291292 // Not a valid extension
292293 // Clear the upload and set mw-upload-permitted to error
293 - document.getElementById(id).value = '';
 294+ document.getElementById( id ).value = '';
294295 var e = document.getElementById( 'mw-upload-permitted' );
295 - if( e ) e.className = 'error';
 296+ if ( e ) e.className = 'error';
296297 var e = document.getElementById( 'mw-upload-prohibited' );
297 - if( e ) e.className = 'error';
 298+ if ( e ) e.className = 'error';
298299 // Clear wpDestFile as well
299300 var e = document.getElementById( 'wpDestFile' )
300 - if( e ) e.value = '';
 301+ if ( e ) e.value = '';
301302
302303 return false;
303304 }
304305 }
305306 // Capitalise first letter and replace spaces by underscores
306307 // FIXME: $wgCapitalizedNamespaces
307 - fname = fname.charAt(0).toUpperCase().concat(fname.substring(1,10000)).replace(/ /g, '_');
 308+ fname = fname.charAt( 0 ).toUpperCase().concat( fname.substring( 1,10000 ) ).replace( / /g, '_' );
308309 // Output result
309 - var destFile = document.getElementById('wpDestFile');
310 - if (destFile) {
 310+ var destFile = document.getElementById( 'wpDestFile' );
 311+ if ( destFile ) {
311312 destFile.value = fname;
312 - wgUploadWarningObj.checkNow(fname) ;
 313+ wgUploadWarningObj.checkNow( fname ) ;
313314 }
314315 },
315316 'toggleFilenameFiller': function() {
316 - if(!document.getElementById) return;
317 - var upfield = document.getElementById('wpUploadFile');
318 - var destName = document.getElementById('wpDestFile').value;
319 - if (destName=='' || destName==' ') {
 317+ if ( !document.getElementById ) return;
 318+ var upfield = document.getElementById( 'wpUploadFile' );
 319+ var destName = document.getElementById( 'wpDestFile' ).value;
 320+ if ( destName=='' || destName==' ' ) {
320321 wgUploadAutoFill = true;
321322 } else {
322323 wgUploadAutoFill = false;
Index: branches/resourceloader/phase3/resources/mw/legacy/mw.legacy.wikibits.js
@@ -2,20 +2,1089 @@
33 * Legacy emulation for the now depricated skins/common/wikibits.js
44 *
55 * MediaWiki JavaScript support functions
 6+ *
 7+ * Global external objects used by this script: ta, stylepath, skin
68 */
79
810 ( function( $, mw ) {
911
1012 /* Extension */
1113
12 -$.extend( mw.legacy, {
13 - //
 14+/*
 15+ * Scary user-agent detection stuff
 16+ */
 17+mw.legacy.clientPC = navigator.userAgent.toLowerCase(); // Get client info
 18+mw.legacy.is_gecko = /gecko/.test( mw.legacy.clientPC ) && !/khtml|spoofer|netscape\/7\.0/.test(mw.legacy.clientPC);
 19+mw.legacy.webkit_match = mw.legacy.clientPC.match(/applewebkit\/(\d+)/);
 20+if (mw.legacy.webkit_match) {
 21+ mw.legacy.is_safari = mw.legacy.clientPC.indexOf('applewebkit') != -1 &&
 22+ mw.legacy.clientPC.indexOf('spoofer') == -1;
 23+ mw.legacy.is_safari_win = mw.legacy.is_safari && mw.legacy.clientPC.indexOf('windows') != -1;
 24+ mw.legacy.webkit_version = parseInt(mw.legacy.webkit_match[1]);
 25+ // Tests for chrome here, to avoid breaking old scripts safari left alone
 26+ // This is here for accesskeys
 27+ mw.legacy.is_chrome = mw.legacy.clientPC.indexOf('chrome') !== -1 &&
 28+ mw.legacy.clientPC.indexOf('spoofer') === -1;
 29+ mw.legacy.is_chrome_mac = mw.legacy.is_chrome && mw.legacy.clientPC.indexOf('mac') !== -1
 30+}
 31+// For accesskeys; note that FF3+ is included here!
 32+mw.legacy.is_ff2 = /firefox\/[2-9]|minefield\/3/.test( mw.legacy.clientPC );
 33+mw.legacy.ff2_bugs = /firefox\/2/.test( mw.legacy.clientPC );
 34+// These aren't used here, but some custom scripts rely on them
 35+mw.legacy.is_ff2_win = mw.legacy.is_ff2 && mw.legacy.clientPC.indexOf('windows') != -1;
 36+mw.legacy.is_ff2_x11 = mw.legacy.is_ff2 && mw.legacy.clientPC.indexOf('x11') != -1;
 37+if (mw.legacy.clientPC.indexOf('opera') != -1) {
 38+ mw.legacy.is_opera = true;
 39+ mw.legacy.is_opera_preseven = window.opera && !document.childNodes;
 40+ mw.legacy.is_opera_seven = window.opera && document.childNodes;
 41+ mw.legacy.is_opera_95 = /opera\/(9\.[5-9]|[1-9][0-9])/.test( mw.legacy.clientPC );
 42+ mw.legacy.opera6_bugs = mw.legacy.is_opera_preseven;
 43+ mw.legacy.opera7_bugs = mw.legacy.is_opera_seven && !mw.legacy.is_opera_95;
 44+ mw.legacy.opera95_bugs = /opera\/(9\.5)/.test( mw.legacy.clientPC );
 45+}
 46+// As recommended by <http://msdn.microsoft.com/en-us/library/ms537509.aspx>,
 47+// avoiding false positives from moronic extensions that append to the IE UA
 48+// string (bug 23171)
 49+mw.legacy.ie6_bugs = false;
 50+if ( /MSIE ([0-9]{1,}[\.0-9]{0,})/.exec( mw.legacy.clientPC ) != null && parseFloat( RegExp.$1 ) <= 6.0 ) {
 51+ mw.legacy.ie6_bugs = true;
 52+}
 53+
 54+$.extend( true, mw.legacy, {
 55+
 56+ /*
 57+ * Events
 58+ *
 59+ * Add any onload functions in this hook (please don't hard-code any events in the xhtml source)
 60+ */
 61+
 62+ /* Global Variables */
 63+
 64+ 'doneOnloadHook': null,
 65+ 'onloadFuncts': [],
 66+
 67+ /* Functions */
 68+
 69+ 'addOnloadHook': function( hookFunct ) {
 70+ // Allows add-on scripts to add onload functions
 71+ if( !mw.legacy.doneOnloadHook ) {
 72+ mw.legacy.onloadFuncts[mw.legacy.onloadFuncts.length] = hookFunct;
 73+ } else {
 74+ hookFunct(); // bug in MSIE script loading
 75+ }
 76+ },
 77+ 'hookEvent': function( hookName, hookFunct ) {
 78+ addHandler( window, hookName, hookFunct );
 79+ },
 80+ 'killEvt': function( evt ) {
 81+ evt = evt || window.event || window.Event; // W3C, IE, Netscape
 82+ if ( typeof ( evt.preventDefault ) != 'undefined' ) {
 83+ evt.preventDefault(); // Don't follow the link
 84+ evt.stopPropagation();
 85+ } else {
 86+ evt.cancelBubble = true; // IE
 87+ }
 88+ return false; // Don't follow the link (IE)
 89+ },
 90+
 91+ /*
 92+ * Dynamic loading
 93+ */
 94+
 95+ /* Global Variables */
 96+
 97+ 'loadedScripts': {},
 98+
 99+ /* Functions */
 100+
 101+ 'importScript': function( page ) {
 102+ // TODO: might want to introduce a utility function to match wfUrlencode() in PHP
 103+ var uri = wgScript + '?title=' +
 104+ encodeURIComponent(page.replace(/ /g,'_')).replace(/%2F/ig,'/').replace(/%3A/ig,':') +
 105+ '&action=raw&ctype=text/javascript';
 106+ return importScriptURI( uri );
 107+ },
 108+ 'importScriptURI': function( url ) {
 109+ if ( mw.legacy.loadedScripts[url] ) {
 110+ return null;
 111+ }
 112+ mw.legacy.loadedScripts[url] = true;
 113+ var s = document.createElement( 'script' );
 114+ s.setAttribute( 'src', url );
 115+ s.setAttribute( 'type', 'text/javascript' );
 116+ document.getElementsByTagName('head')[0].appendChild( s );
 117+ return s;
 118+ },
 119+ 'importStylesheet': function( page ) {
 120+ return importStylesheetURI( wgScript + '?action=raw&ctype=text/css&title=' + encodeURIComponent( page.replace(/ /g,'_') ) );
 121+ },
 122+ 'importStylesheetURI': function( url, media ) {
 123+ var l = document.createElement( 'link' );
 124+ l.type = 'text/css';
 125+ l.rel = 'stylesheet';
 126+ l.href = url;
 127+ if( media ) {
 128+ l.media = media;
 129+ }
 130+ document.getElementsByTagName('head')[0].appendChild( l );
 131+ return l;
 132+ },
 133+ 'appendCSS': function( text ) {
 134+ var s = document.createElement( 'style' );
 135+ s.type = 'text/css';
 136+ s.rel = 'stylesheet';
 137+ if ( s.styleSheet ) {
 138+ s.styleSheet.cssText = text; // IE
 139+ } else {
 140+ s.appendChild( document.createTextNode( text + '' ) ); // Safari sometimes borks on null
 141+ }
 142+ document.getElementsByTagName('head')[0].appendChild( s );
 143+ return s;
 144+ },
 145+ 'runOnloadHook': function() {
 146+ // don't run anything below this for non-dom browsers
 147+ if ( mw.legacy.doneOnloadHook || !( document.getElementById && document.getElementsByTagName ) ) {
 148+ return;
 149+ }
 150+ // set this before running any hooks, since any errors below
 151+ // might cause the function to terminate prematurely
 152+ mw.legacy.doneOnloadHook = true;
 153+ updateTooltipAccessKeys( null );
 154+ setupCheckboxShiftClick();
 155+ sortables_init();
 156+ // Run any added-on functions
 157+ for ( var i = 0; i < mw.legacy.onloadFuncts.length; i++ ) {
 158+ mw.legacy.onloadFuncts[i]();
 159+ }
 160+ },
 161+ /**
 162+ * Add an event handler to an element
 163+ *
 164+ * @param Element element Element to add handler to
 165+ * @param String attach Event to attach to
 166+ * @param callable handler Event handler callback
 167+ */
 168+ 'addHandler': function( element, attach, handler ) {
 169+ if( window.addEventListener ) {
 170+ element.addEventListener( attach, handler, false );
 171+ } else if( window.attachEvent ) {
 172+ element.attachEvent( 'on' + attach, handler );
 173+ }
 174+ },
 175+ /**
 176+ * Add a click event handler to an element
 177+ *
 178+ * @param Element element Element to add handler to
 179+ * @param callable handler Event handler callback
 180+ */
 181+ 'addClickHandler': function( element, handler ) {
 182+ addHandler( element, 'click', handler );
 183+ },
 184+ /**
 185+ * Removes an event handler from an element
 186+ *
 187+ * @param Element element Element to remove handler from
 188+ * @param String remove Event to remove
 189+ * @param callable handler Event handler callback to remove
 190+ */
 191+ 'removeHandler': function( element, remove, handler ) {
 192+ if( window.removeEventListener ) {
 193+ element.removeEventListener( remove, handler, false );
 194+ } else if( window.detachEvent ) {
 195+ element.detachEvent( 'on' + remove, handler );
 196+ }
 197+ },
 198+
 199+ /*
 200+ * Toolbar
 201+ */
 202+
 203+ /* Global Variables */
 204+
 205+ 'mwEditButtons': [],
 206+ 'mwCustomEditButtons': [],
 207+
 208+ /**
 209+ * Tooltips and access-keys
 210+ */
 211+
 212+ /* Global Variables */
 213+
 214+ /**
 215+ * Set the accesskey prefix based on browser detection.
 216+ */
 217+ 'tooltipAccessKeyPrefix': ( function() {
 218+ if ( mw.legacy.is_opera ) {
 219+ return 'shift-esc-';
 220+ } else if ( mw.legacy.is_chrome ) {
 221+ return mw.legacy.is_chrome_mac ? 'ctrl-option-' : 'alt-';
 222+ } else if ( !mw.legacy.is_safari_win && mw.legacy.is_safari && mw.legacy.webkit_version > 526 ) {
 223+ return 'ctrl-alt-';
 224+ } else if (
 225+ !mw.legacy.is_safari_win &&
 226+ (
 227+ mw.legacy.is_safari ||
 228+ mw.legacy.clientPC.indexOf( 'mac' ) != -1 ||
 229+ mw.legacy.clientPC.indexOf( 'konqueror' ) != -1
 230+ )
 231+ ) {
 232+ return = 'ctrl-';
 233+ } else if ( mw.legacy.is_ff2 ) {
 234+ return = 'alt-shift-';
 235+ }
 236+ return 'alt-';
 237+ } )(),
 238+ 'tooltipAccessKeyRegexp': /\[(ctrl-)?(alt-)?(shift-)?(esc-)?(.)\]$/,
 239+ // Dummy for deprecated function
 240+ 'ta': [],
 241+
 242+ /* Functions */
 243+
 244+ /**
 245+ * Add the appropriate prefix to the accesskey shown in the tooltip.
 246+ * If the nodeList parameter is given, only those nodes are updated;
 247+ * otherwise, all the nodes that will probably have accesskeys by
 248+ * default are updated.
 249+ *
 250+ * @param Array nodeList -- list of elements to update
 251+ */
 252+ 'updateTooltipAccessKeys': function( nodeList ) {
 253+ if ( !nodeList ) {
 254+ // Rather than scan all links on the whole page, we can just scan these
 255+ // containers which contain the relevant links. This is really just an
 256+ // optimization technique.
 257+ var linkContainers = [
 258+ 'column-one', // Monobook and Modern
 259+ 'mw-head', 'mw-panel', 'p-logo' // Vector
 260+ ];
 261+ for ( var i in linkContainers ) {
 262+ var linkContainer = document.getElementById( linkContainers[i] );
 263+ if ( linkContainer ) {
 264+ updateTooltipAccessKeys( linkContainer.getElementsByTagName( 'a' ) );
 265+ }
 266+ }
 267+ // these are rare enough that no such optimization is needed
 268+ updateTooltipAccessKeys( document.getElementsByTagName( 'input' ) );
 269+ updateTooltipAccessKeys( document.getElementsByTagName( 'label' ) );
 270+ return;
 271+ }
 272+ for ( var i = 0; i < nodeList.length; i++ ) {
 273+ var element = nodeList[i];
 274+ var tip = element.getAttribute( 'title' );
 275+ if ( tip && mw.legacy.tooltipAccessKeyRegexp.exec( tip ) ) {
 276+ tip = tip.replace(mw.legacy.tooltipAccessKeyRegexp,
 277+ '[' + mw.legacy.tooltipAccessKeyPrefix + "$5]");
 278+ element.setAttribute( 'title', tip );
 279+ }
 280+ }
 281+ },
 282+ // Dummy function for depricated feature
 283+ 'akeytt': function( doId ) { },
 284+
 285+ /*
 286+ * Checkboxes
 287+ */
 288+
 289+ /* Global Varibles */
 290+
 291+ 'checkboxes': null,
 292+ 'lastCheckbox': null,
 293+
 294+ /* Functions */
 295+
 296+ 'setupCheckboxShiftClick': function() {
 297+ mw.legacy.checkboxes = [];
 298+ mw.legacy.lastCheckbox = null;
 299+ var inputs = document.getElementsByTagName( 'input' );
 300+ addCheckboxClickHandlers( inputs );
 301+ },
 302+ 'addCheckboxClickHandlers': function( inputs, start ) {
 303+ if ( !start ) {
 304+ start = 0;
 305+ }
 306+ var finish = start + 250;
 307+ if ( finish > inputs.length ) {
 308+ finish = inputs.length;
 309+ }
 310+ for ( var i = start; i < finish; i++ ) {
 311+ var cb = inputs[i];
 312+ if ( !cb.type || cb.type.toLowerCase() != 'checkbox' ) {
 313+ continue;
 314+ }
 315+ var end = mw.legacy.checkboxes.length;
 316+ mw.legacy.checkboxes[end] = cb;
 317+ cb.index = end;
 318+ addClickHandler( cb, checkboxClickHandler );
 319+ }
 320+ if ( finish < inputs.length ) {
 321+ setTimeout( function() {
 322+ addCheckboxClickHandlers( inputs, finish );
 323+ }, 200 );
 324+ }
 325+ },
 326+ 'checkboxClickHandler': function( e ) {
 327+ if ( typeof e == 'undefined' ) {
 328+ e = window.event;
 329+ }
 330+ if ( !e.shiftKey || mw.legacy.lastCheckbox === null ) {
 331+ mw.legacy.lastCheckbox = this.index;
 332+ return true;
 333+ }
 334+ var endState = this.checked;
 335+ var start, finish;
 336+ if ( this.index < mw.legacy.lastCheckbox ) {
 337+ start = this.index + 1;
 338+ finish = mw.legacy.lastCheckbox;
 339+ } else {
 340+ start = mw.legacy.lastCheckbox;
 341+ finish = this.index - 1;
 342+ }
 343+ for ( var i = start; i <= finish; ++i ) {
 344+ mw.legacy.checkboxes[i].checked = endState;
 345+ if( i > start && typeof mw.legacy.checkboxes[i].onchange == 'function' ) {
 346+ mw.legacy.checkboxes[i].onchange(); // fire triggers
 347+ }
 348+ }
 349+ mw.legacy.lastCheckbox = this.index;
 350+ return true;
 351+ },
 352+
 353+ /*
 354+ * Table of contents
 355+ */
 356+
 357+ /* Functions */
 358+
 359+ 'showTocToggle': function() {
 360+ if ( document.createTextNode ) {
 361+ // Uses DOM calls to avoid document.write + XHTML issues
 362+ var linkHolder = document.getElementById( 'toctitle' );
 363+ var existingLink = document.getElementById( 'togglelink' );
 364+ if ( !linkHolder || existingLink ) {
 365+ // Don't add the toggle link twice
 366+ return;
 367+ }
 368+ var outerSpan = document.createElement( 'span' );
 369+ outerSpan.className = 'toctoggle';
 370+ var toggleLink = document.createElement( 'a' );
 371+ toggleLink.id = 'togglelink';
 372+ toggleLink.className = 'internal';
 373+ toggleLink.href = '#';
 374+ addClickHandler( toggleLink, function( evt ) { toggleToc(); return killEvt( evt ); } );
 375+ toggleLink.appendChild( document.createTextNode( tocHideText ) );
 376+ outerSpan.appendChild( document.createTextNode( '[' ) );
 377+ outerSpan.appendChild( toggleLink );
 378+ outerSpan.appendChild( document.createTextNode( ']' ) );
 379+ linkHolder.appendChild( document.createTextNode( ' ' ) );
 380+ linkHolder.appendChild( outerSpan );
 381+ var cookiePos = document.cookie.indexOf( "hidetoc=" );
 382+ if ( cookiePos > -1 && document.cookie.charAt( cookiePos + 8 ) == 1 ) {
 383+ toggleToc();
 384+ }
 385+ }
 386+ },
 387+ 'toggleToc': function() {
 388+ var tocmain = document.getElementById( 'toc' );
 389+ var toc = document.getElementById('toc').getElementsByTagName('ul')[0];
 390+ var toggleLink = document.getElementById( 'togglelink' );
 391+
 392+ if ( toc && toggleLink && toc.style.display == 'none' ) {
 393+ changeText( toggleLink, tocHideText );
 394+ toc.style.display = 'block';
 395+ document.cookie = "hidetoc=0";
 396+ tocmain.className = 'toc';
 397+ } else {
 398+ changeText( toggleLink, tocShowText );
 399+ toc.style.display = 'none';
 400+ document.cookie = "hidetoc=1";
 401+ tocmain.className = 'toc tochidden';
 402+ }
 403+ return false;
 404+ },
 405+
 406+ /*
 407+ * Table sorting
 408+ *
 409+ * Script based on one (c) 1997-2006 Stuart Langridge and Joost de Valk:
 410+ * http://www.joostdevalk.nl/code/sortable-table/
 411+ * http://www.kryogenix.org/code/browser/sorttable/
 412+ *
 413+ * @todo don't break on colspans/rowspans (bug 8028)
 414+ * @todo language-specific digit grouping/decimals (bug 8063)
 415+ * @todo support all accepted date formats (bug 8226)
 416+ */
 417+
 418+ /* Global Variables */
 419+
 420+ 'ts_image_path': mw.legacy.stylepath + '/common/images/',
 421+ 'ts_image_up': 'sort_up.gif',
 422+ 'ts_image_down': 'sort_down.gif',
 423+ 'ts_image_none': 'sort_none.gif',
 424+ // The non-American-inclined can change to "true"
 425+ 'ts_europeandate': mw.legacy.wgContentLanguage != 'en',
 426+ 'ts_alternate_row_colors': false,
 427+ 'ts_number_transform_table': null,
 428+ 'ts_number_regex': null,
 429+
 430+ /* Functions */
 431+
 432+ 'sortables_init': function() {
 433+ var idnum = 0;
 434+ // Find all tables with class sortable and make them sortable
 435+ var tables = getElementsByClassName( document, 'table', 'sortable' );
 436+ for ( var ti = 0; ti < tables.length ; ti++ ) {
 437+ if ( !tables[ti].id ) {
 438+ tables[ti].setAttribute( 'id', 'sortable_table_id_' + idnum );
 439+ ++idnum;
 440+ }
 441+ mw.legacy.ts_makeSortable( tables[ti] );
 442+ }
 443+ },
 444+ 'ts_makeSortable': function( table ) {
 445+ var firstRow;
 446+ if ( table.rows && table.rows.length > 0 ) {
 447+ if ( table.tHead && table.tHead.rows.length > 0 ) {
 448+ firstRow = table.tHead.rows[table.tHead.rows.length-1];
 449+ } else {
 450+ firstRow = table.rows[0];
 451+ }
 452+ }
 453+ if ( !firstRow ) {
 454+ return;
 455+ }
 456+ // We have a first row: assume it's the header, and make its contents clickable links
 457+ for ( var i = 0; i < firstRow.cells.length; i++ ) {
 458+ var cell = firstRow.cells[i];
 459+ if ( (' ' + cell.className + ' ').indexOf(' unsortable ') == -1 ) {
 460+ cell.innerHTML += '<a href="#" class="sortheader" '
 461+ + 'onclick="mw.legacy.ts_resortTable(this);return false;">'
 462+ + '<span class="sortarrow">'
 463+ + '<img src="'
 464+ + mw.legacy.ts_image_path
 465+ + mw.legacy.ts_image_none
 466+ + '" alt="&darr;"/></span></a>';
 467+ }
 468+ }
 469+ if ( mw.legacy.ts_alternate_row_colors ) {
 470+ mw.legacy.ts_alternate( table );
 471+ }
 472+ },
 473+ 'ts_getInnerText': function( el ) {
 474+ return getInnerText( el );
 475+ },
 476+ 'ts_resortTable': function( lnk ) {
 477+ // get the span
 478+ var span = lnk.getElementsByTagName('span')[0];
 479+ var td = lnk.parentNode;
 480+ var tr = td.parentNode;
 481+ var column = td.cellIndex;
 482+ var table = tr.parentNode;
 483+ while ( table && !( table.tagName && table.tagName.toLowerCase() == 'table' ) ) {
 484+ table = table.parentNode;
 485+ }
 486+ if ( !table ) {
 487+ return;
 488+ }
 489+ if ( table.rows.length <= 1 ) {
 490+ return;
 491+ }
 492+ // Generate the number transform table if it's not done already
 493+ if ( mw.legacy.ts_number_transform_table === null ) {
 494+ mw.legacy.ts_initTransformTable();
 495+ }
 496+ // Work out a type for the column
 497+ // Skip the first row if that's where the headings are
 498+ var rowStart = ( table.tHead && table.tHead.rows.length > 0 ? 0 : 1 );
 499+ var bodyRows = 0;
 500+ if (rowStart == 0 && table.tBodies) {
 501+ for (var i=0; i < table.tBodies.length; i++ ) {
 502+ bodyRows += table.tBodies[i].rows.length;
 503+ }
 504+ if (bodyRows < table.rows.length)
 505+ rowStart = 1;
 506+ }
 507+ var itm = '';
 508+ for ( var i = rowStart; i < table.rows.length; i++ ) {
 509+ if ( table.rows[i].cells.length > column ) {
 510+ itm = mw.legacy.ts_getInnerText(table.rows[i].cells[column]);
 511+ itm = itm.replace(/^[\s\xa0]+/, '').replace(/[\s\xa0]+$/, '');
 512+ if ( itm != '' ) {
 513+ break;
 514+ }
 515+ }
 516+ }
 517+ // TODO: bug 8226, localised date formats
 518+ var sortfn = mw.legacy.ts_sort_generic;
 519+ var preprocessor = mw.legacy.ts_toLowerCase;
 520+ if ( /^\d\d[\/. -][a-zA-Z]{3}[\/. -]\d\d\d\d$/.test( itm ) ) {
 521+ preprocessor = mw.legacy.ts_dateToSortKey;
 522+ } else if ( /^\d\d[\/.-]\d\d[\/.-]\d\d\d\d$/.test( itm ) ) {
 523+ preprocessor = mw.legacy.ts_dateToSortKey;
 524+ } else if ( /^\d\d[\/.-]\d\d[\/.-]\d\d$/.test( itm ) ) {
 525+ preprocessor = mw.legacy.ts_dateToSortKey;
 526+ // (minus sign)([pound dollar euro yen currency]|cents)
 527+ } else if ( /(^([-\u2212] *)?[\u00a3$\u20ac\u00a4\u00a5]|\u00a2$)/.test( itm ) ) {
 528+ preprocessor = mw.legacy.ts_currencyToSortKey;
 529+ } else if ( mw.legacy.ts_number_regex.test( itm ) ) {
 530+ preprocessor = mw.legacy.ts_parseFloat;
 531+ }
 532+ var reverse = ( span.getAttribute( 'sortdir' ) == 'down' );
 533+ var newRows = new Array();
 534+ var staticRows = new Array();
 535+ for ( var j = rowStart; j < table.rows.length; j++ ) {
 536+ var row = table.rows[j];
 537+ if( (' ' + row.className + ' ').indexOf(' unsortable ') < 0 ) {
 538+ var keyText = mw.legacy.ts_getInnerText( row.cells[column] );
 539+ if( keyText === undefined ) {
 540+ keyText = '';
 541+ }
 542+ var oldIndex = ( reverse ? -j : j );
 543+ var preprocessed = preprocessor( keyText.replace(/^[\s\xa0]+/, '').replace(/[\s\xa0]+$/, '') );
 544+ newRows[newRows.length] = new Array( row, preprocessed, oldIndex );
 545+ } else {
 546+ staticRows[staticRows.length] = new Array( row, false, j-rowStart );
 547+ }
 548+ }
 549+ newRows.sort( sortfn );
 550+ var arrowHTML;
 551+ if ( reverse ) {
 552+ arrowHTML = '<img src="' + mw.legacy.ts_image_path + mw.legacy.ts_image_down + '" alt="&darr;"/>';
 553+ newRows.reverse();
 554+ span.setAttribute( 'sortdir', 'up' );
 555+ } else {
 556+ arrowHTML = '<img src="' + mw.legacy.ts_image_path + mw.legacy.ts_image_up + '" alt="&uarr;"/>';
 557+ span.setAttribute( 'sortdir', 'down' );
 558+ }
 559+ for ( var i = 0; i < staticRows.length; i++ ) {
 560+ var row = staticRows[i];
 561+ newRows.splice( row[2], 0, row );
 562+ }
 563+ // We appendChild rows that already exist to the tbody, so it moves them rather than creating new ones
 564+ // don't do sortbottom rows
 565+ for ( var i = 0; i < newRows.length; i++ ) {
 566+ if ( ( ' ' + newRows[i][0].className + ' ').indexOf(' sortbottom ') == -1 ) {
 567+ table.tBodies[0].appendChild( newRows[i][0] );
 568+ }
 569+ }
 570+ // do sortbottom rows only
 571+ for ( var i = 0; i < newRows.length; i++ ) {
 572+ if ( ( ' ' + newRows[i][0].className + ' ').indexOf(' sortbottom ') != -1 ) {
 573+ table.tBodies[0].appendChild( newRows[i][0] );
 574+ }
 575+ }
 576+ // Delete any other arrows there may be showing
 577+ var spans = getElementsByClassName( tr, 'span', 'sortarrow' );
 578+ for ( var i = 0; i < spans.length; i++ ) {
 579+ spans[i].innerHTML = '<img src="' + mw.legacy.ts_image_path + mw.legacy.ts_image_none + '" alt="&darr;"/>';
 580+ }
 581+ span.innerHTML = arrowHTML;
 582+
 583+ if ( mw.legacy.ts_alternate_row_colors ) {
 584+ mw.legacy.ts_alternate( table );
 585+ }
 586+ },
 587+ 'ts_initTransformTable': function() {
 588+ if ( typeof wgSeparatorTransformTable == 'undefined'
 589+ || ( wgSeparatorTransformTable[0] == '' && wgDigitTransformTable[2] == '' ) )
 590+ {
 591+ var digitClass = "[0-9,.]";
 592+ mw.legacy.ts_number_transform_table = false;
 593+ } else {
 594+ mw.legacy.ts_number_transform_table = {};
 595+ // Unpack the transform table
 596+ // Separators
 597+ var ascii = wgSeparatorTransformTable[0].split("\t");
 598+ var localised = wgSeparatorTransformTable[1].split("\t");
 599+ for ( var i = 0; i < ascii.length; i++ ) {
 600+ mw.legacy.ts_number_transform_table[localised[i]] = ascii[i];
 601+ }
 602+ // Digits
 603+ ascii = wgDigitTransformTable[0].split("\t");
 604+ localised = wgDigitTransformTable[1].split("\t");
 605+ for ( var i = 0; i < ascii.length; i++ ) {
 606+ mw.legacy.ts_number_transform_table[localised[i]] = ascii[i];
 607+ }
 608+ // Construct regex for number identification
 609+ var digits = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ',', '\\.'];
 610+ var maxDigitLength = 1;
 611+ for ( var digit in mw.legacy.ts_number_transform_table ) {
 612+ // Escape regex metacharacters
 613+ digits.push(
 614+ digit.replace( /[\\\\$\*\+\?\.\(\)\|\{\}\[\]\-]/,
 615+ function( s ) { return '\\' + s; } )
 616+ );
 617+ if ( digit.length > maxDigitLength ) {
 618+ maxDigitLength = digit.length;
 619+ }
 620+ }
 621+ if ( maxDigitLength > 1 ) {
 622+ var digitClass = '[' + digits.join( '', digits ) + ']';
 623+ } else {
 624+ var digitClass = '(' + digits.join( '|', digits ) + ')';
 625+ }
 626+ }
 627+ // We allow a trailing percent sign, which we just strip. This works fine
 628+ // if percents and regular numbers aren't being mixed.
 629+ mw.legacy.ts_number_regex = new RegExp(
 630+ "^(" +
 631+ "[-+\u2212]?[0-9][0-9,]*(\\.[0-9,]*)?(E[-+\u2212]?[0-9][0-9,]*)?" + // Fortran-style scientific
 632+ "|" +
 633+ "[-+\u2212]?" + digitClass + "+%?" + // Generic localised
 634+ ")$", "i"
 635+ );
 636+ },
 637+ 'ts_toLowerCase': function( s ) {
 638+ return s.toLowerCase();
 639+ },
 640+ 'ts_dateToSortKey': function( date ) {
 641+ // y2k notes: two digit years less than 50 are treated as 20XX, greater than 50 are treated as 19XX
 642+ if ( date.length == 11 ) {
 643+ switch ( date.substr( 3, 3 ).toLowerCase() ) {
 644+ case 'jan':
 645+ var month = '01';
 646+ break;
 647+ case 'feb':
 648+ var month = '02';
 649+ break;
 650+ case 'mar':
 651+ var month = '03';
 652+ break;
 653+ case 'apr':
 654+ var month = '04';
 655+ break;
 656+ case 'may':
 657+ var month = '05';
 658+ break;
 659+ case 'jun':
 660+ var month = '06';
 661+ break;
 662+ case 'jul':
 663+ var month = '07';
 664+ break;
 665+ case 'aug':
 666+ var month = '08';
 667+ break;
 668+ case 'sep':
 669+ var month = '09';
 670+ break;
 671+ case 'oct':
 672+ var month = '10';
 673+ break;
 674+ case 'nov':
 675+ var month = '11';
 676+ break;
 677+ case 'dec':
 678+ var month = '12';
 679+ break;
 680+ // default: var month = '00';
 681+ }
 682+ return date.substr( 7, 4 ) + month + date.substr( 0, 2 );
 683+ } else if ( date.length == 10 ) {
 684+ if ( mw.legacy.ts_europeandate == false ) {
 685+ return date.substr( 6, 4 ) + date.substr( 0, 2 ) + date.substr( 3, 2 );
 686+ } else {
 687+ return date.substr( 6, 4 ) + date.substr( 3, 2 ) + date.substr( 0, 2 );
 688+ }
 689+ } else if ( date.length == 8 ) {
 690+ var yr = date.substr( 6, 2 );
 691+ if ( parseInt( yr ) < 50 ) {
 692+ yr = '20' + yr;
 693+ } else {
 694+ yr = '19' + yr;
 695+ }
 696+ if ( mw.legacy.ts_europeandate == true ) {
 697+ return yr + date.substr( 3, 2 ) + date.substr( 0, 2 );
 698+ } else {
 699+ return yr + date.substr( 0, 2 ) + date.substr( 3, 2 );
 700+ }
 701+ }
 702+ return '00000000';
 703+ },
 704+ 'ts_parseFloat': function( s ) {
 705+ if ( !s ) {
 706+ return 0;
 707+ }
 708+ if ( mw.legacy.ts_number_transform_table != false ) {
 709+ var newNum = '', c;
 710+
 711+ for ( var p = 0; p < s.length; p++ ) {
 712+ c = s.charAt( p );
 713+ if ( c in mw.legacy.ts_number_transform_table ) {
 714+ newNum += mw.legacy.ts_number_transform_table[c];
 715+ } else {
 716+ newNum += c;
 717+ }
 718+ }
 719+ s = newNum;
 720+ }
 721+ var num = parseFloat( s.replace(/[, ]/g, '').replace("\u2212", '-') );
 722+ return ( isNaN( num ) ? -Infinity : num );
 723+ },
 724+ 'ts_currencyToSortKey': function( s ) {
 725+ return mw.legacy.ts_parseFloat(s.replace(/[^-\u22120-9.,]/g,''));
 726+ },
 727+ 'ts_sort_generic': function( a, b ) {
 728+ return a[1] < b[1] ? -1 : a[1] > b[1] ? 1 : a[2] - b[2];
 729+ },
 730+ 'ts_alternate': function( table ) {
 731+ // Take object table and get all it's tbodies.
 732+ var tableBodies = table.getElementsByTagName( 'tbody' );
 733+ // Loop through these tbodies
 734+ for ( var i = 0; i < tableBodies.length; i++ ) {
 735+ // Take the tbody, and get all it's rows
 736+ var tableRows = tableBodies[i].getElementsByTagName( 'tr' );
 737+ // Loop through these rows
 738+ // Start at 1 because we want to leave the heading row untouched
 739+ for ( var j = 0; j < tableRows.length; j++ ) {
 740+ // Check if j is even, and apply classes for both possible results
 741+ var oldClasses = tableRows[j].className.split(' ');
 742+ var newClassName = '';
 743+ for ( var k = 0; k < oldClasses.length; k++ ) {
 744+ if ( oldClasses[k] != '' && oldClasses[k] != 'even' && oldClasses[k] != 'odd' ) {
 745+ newClassName += oldClasses[k] + ' ';
 746+ }
 747+ }
 748+ tableRows[j].className = newClassName + ( j % 2 == 0 ? 'even' : 'odd' );
 749+ }
 750+ }
 751+ },
 752+
 753+ /*
 754+ * Skins
 755+ */
 756+
 757+ /* Functions */
 758+
 759+ 'changeText': function( el, newText ) {
 760+ // Safari work around
 761+ if ( el.innerText ) {
 762+ el.innerText = newText;
 763+ } else if ( el.firstChild && el.firstChild.nodeValue ) {
 764+ el.firstChild.nodeValue = newText;
 765+ }
 766+ },
 767+ 'escapeQuotes': function( text ) {
 768+ var re = new RegExp( "'", "g" );
 769+ text = text.replace( re, "\\'" );
 770+ re = new RegExp( "\\n", "g" );
 771+ text = text.replace( re, "\\n" );
 772+ return escapeQuotesHTML( text );
 773+ },
 774+ 'escapeQuotesHTML': function( text ) {
 775+ var re = new RegExp( '&', "g" );
 776+ text = text.replace( re, "&amp;" );
 777+ re = new RegExp( '"', "g" );
 778+ text = text.replace( re, "&quot;" );
 779+ re = new RegExp( '<', "g" );
 780+ text = text.replace( re, "&lt;" );
 781+ re = new RegExp( '>', "g" );
 782+ text = text.replace( re, "&gt;" );
 783+ return text;
 784+ },
 785+ /**
 786+ * Add a link to one of the portlet menus on the page, including:
 787+ *
 788+ * p-cactions: Content actions (shown as tabs above the main content in Monobook)
 789+ * p-personal: Personal tools (shown at the top right of the page in Monobook)
 790+ * p-navigation: Navigation
 791+ * p-tb: Toolbox
 792+ *
 793+ * This function exists for the convenience of custom JS authors. All
 794+ * but the first three parameters are optional, though providing at
 795+ * least an id and a tooltip is recommended.
 796+ *
 797+ * By default the new link will be added to the end of the list. To
 798+ * add the link before a given existing item, pass the DOM node of
 799+ * that item (easily obtained with document.getElementById()) as the
 800+ * nextnode parameter; to add the link _after_ an existing item, pass
 801+ * the node's nextSibling instead.
 802+ *
 803+ * @param String portlet -- id of the target portlet ("p-cactions", "p-personal", "p-navigation" or "p-tb")
 804+ * @param String href -- link URL
 805+ * @param String text -- link text (will be automatically lowercased by CSS for p-cactions in Monobook)
 806+ * @param String id -- id of the new item, should be unique and preferably have the appropriate prefix ("ca-", "pt-", "n-" or "t-")
 807+ * @param String tooltip -- text to show when hovering over the link, without accesskey suffix
 808+ * @param String accesskey -- accesskey to activate this link (one character, try to avoid conflicts)
 809+ * @param Node nextnode -- the DOM node before which the new item should be added, should be another item in the same list
 810+ *
 811+ * @return Node -- the DOM node of the new item (an LI element) or null
 812+ */
 813+ 'addPortletLink': function( portlet, href, text, id, tooltip, accesskey, nextnode ) {
 814+ var root = document.getElementById( portlet );
 815+ if ( !root ) {
 816+ return null;
 817+ }
 818+ var uls = root.getElementsByTagName( 'ul' );
 819+ var node;
 820+ if ( uls.length > 0 ) {
 821+ node = uls[0];
 822+ } else {
 823+ node = document.createElement( 'ul' );
 824+ var lastElementChild = null;
 825+ for ( var i = 0; i < root.childNodes.length; ++i ) { /* get root.lastElementChild */
 826+ if ( root.childNodes[i].nodeType == 1 ) {
 827+ lastElementChild = root.childNodes[i];
 828+ }
 829+ }
 830+ if ( lastElementChild && lastElementChild.nodeName.match( /div/i ) ) {
 831+ /* Insert into the menu divs */
 832+ lastElementChild.appendChild( node );
 833+ } else {
 834+ root.appendChild( node );
 835+ }
 836+ }
 837+ if ( !node ) {
 838+ return null;
 839+ }
 840+ // unhide portlet if it was hidden before
 841+ root.className = root.className.replace( /(^| )emptyPortlet( |$)/, "$2" );
 842+ var span = document.createElement( 'span' );
 843+ span.appendChild( document.createTextNode( text ) );
 844+ var link = document.createElement( 'a' );
 845+ link.appendChild( span );
 846+ link.href = href;
 847+ var item = document.createElement( 'li' );
 848+ item.appendChild( link );
 849+ if ( id ) {
 850+ item.id = id;
 851+ }
 852+ if ( accesskey ) {
 853+ link.setAttribute( 'accesskey', accesskey );
 854+ tooltip += ' [' + accesskey + ']';
 855+ }
 856+ if ( tooltip ) {
 857+ link.setAttribute( 'title', tooltip );
 858+ }
 859+ if ( accesskey && tooltip ) {
 860+ updateTooltipAccessKeys( new Array( link ) );
 861+ }
 862+ if ( nextnode && nextnode.parentNode == node ) {
 863+ node.insertBefore( item, nextnode );
 864+ } else {
 865+ node.appendChild( item ); // IE compatibility (?)
 866+ }
 867+ return item;
 868+ },
 869+ /**
 870+ * Add a cute little box at the top of the screen to inform the user of
 871+ * something, replacing any preexisting message.
 872+ *
 873+ * @param String -or- Dom Object message HTML to be put inside the right div
 874+ * @param String className Used in adding a class; should be different for each
 875+ * call to allow CSS/JS to hide different boxes. null = no class used.
 876+ * @return Boolean True on success, false on failure
 877+ */
 878+ 'jsMsg': function( message, className ) {
 879+ if ( !document.getElementById ) {
 880+ return false;
 881+ }
 882+ // We special-case skin structures provided by the software. Skins that
 883+ // choose to abandon or significantly modify our formatting can just define
 884+ // an mw-js-message div to start with.
 885+ var messageDiv = document.getElementById( 'mw-js-message' );
 886+ if ( !messageDiv ) {
 887+ messageDiv = document.createElement( 'div' );
 888+ if ( document.getElementById( 'column-content' )
 889+ && document.getElementById( 'content' ) ) {
 890+ // MonoBook, presumably
 891+ document.getElementById( 'content' ).insertBefore(
 892+ messageDiv,
 893+ document.getElementById( 'content' ).firstChild
 894+ );
 895+ } else if ( document.getElementById( 'content' )
 896+ && document.getElementById( 'article' ) ) {
 897+ // Non-Monobook but still recognizable (old-style)
 898+ document.getElementById( 'article').insertBefore(
 899+ messageDiv,
 900+ document.getElementById( 'article' ).firstChild
 901+ );
 902+ } else {
 903+ return false;
 904+ }
 905+ }
 906+ messageDiv.setAttribute( 'id', 'mw-js-message' );
 907+ messageDiv.style.display = 'block';
 908+ if( className ) {
 909+ messageDiv.setAttribute( 'class', 'mw-js-message-' + className );
 910+ }
 911+ if ( typeof message === 'object' ) {
 912+ while ( messageDiv.hasChildNodes() ) { // Remove old content
 913+ messageDiv.removeChild( messageDiv.firstChild );
 914+ }
 915+ messageDiv.appendChild( message ); // Append new content
 916+ } else {
 917+ messageDiv.innerHTML = message;
 918+ }
 919+ return true;
 920+ },
 921+ /**
 922+ * Inject a cute little progress spinner after the specified element
 923+ *
 924+ * @param element Element to inject after
 925+ * @param id Identifier string (for use with removeSpinner(), below)
 926+ */
 927+ 'injectSpinner': function( element, id ) {
 928+ var spinner = document.createElement( 'img' );
 929+ spinner.id = 'mw-spinner-' + id;
 930+ spinner.src = mw.legacy.stylepath + '/common/images/spinner.gif';
 931+ spinner.alt = spinner.title = '...';
 932+ if( element.nextSibling ) {
 933+ element.parentNode.insertBefore( spinner, element.nextSibling );
 934+ } else {
 935+ element.parentNode.appendChild( spinner );
 936+ }
 937+ },
 938+ /**
 939+ * Remove a progress spinner added with injectSpinner()
 940+ *
 941+ * @param id Identifier string
 942+ */
 943+ 'removeSpinner': function( id ) {
 944+ var spinner = document.getElementById( 'mw-spinner-' + id );
 945+ if( spinner ) {
 946+ spinner.parentNode.removeChild( spinner );
 947+ }
 948+ },
 949+
 950+ /*
 951+ * DOM manipulation and traversal
 952+ */
 953+
 954+ /* Functions */
 955+
 956+ 'getInnerText': function( el ) {
 957+ if ( typeof el == 'string' ) {
 958+ return el;
 959+ }
 960+ if ( typeof el == 'undefined' ) {
 961+ return el;
 962+ }
 963+ if ( el.textContent ) {
 964+ return el.textContent; // not needed but it is faster
 965+ }
 966+ if ( el.innerText ) {
 967+ return el.innerText; // IE doesn't have textContent
 968+ }
 969+ var str = '';
 970+ var cs = el.childNodes;
 971+ var l = cs.length;
 972+ for ( var i = 0; i < l; i++ ) {
 973+ switch ( cs[i].nodeType ) {
 974+ case 1: // ELEMENT_NODE
 975+ str += mw.legacy.ts_getInnerText( cs[i] );
 976+ break;
 977+ case 3: // TEXT_NODE
 978+ str += cs[i].nodeValue;
 979+ break;
 980+ }
 981+ }
 982+ return str;
 983+ },
 984+ /**
 985+ * Written by Jonathan Snook, http://www.snook.ca/jonathan
 986+ * Add-ons by Robert Nyman, http://www.robertnyman.com
 987+ * Author says "The credit comment is all it takes, no license. Go crazy with it!:-)"
 988+ * From http://www.robertnyman.com/2005/11/07/the-ultimate-getelementsbyclassname/
 989+ */
 990+ 'getElementsByClassName': function( oElm, strTagName, oClassNames ) {
 991+ var arrReturnElements = new Array();
 992+ if ( typeof( oElm.getElementsByClassName ) == 'function' ) {
 993+ /* Use a native implementation where possible FF3, Saf3.2, Opera 9.5 */
 994+ var arrNativeReturn = oElm.getElementsByClassName( oClassNames );
 995+ if ( strTagName == '*' ) {
 996+ return arrNativeReturn;
 997+ }
 998+ for ( var h = 0; h < arrNativeReturn.length; h++ ) {
 999+ if( arrNativeReturn[h].tagName.toLowerCase() == strTagName.toLowerCase() ) {
 1000+ arrReturnElements[arrReturnElements.length] = arrNativeReturn[h];
 1001+ }
 1002+ }
 1003+ return arrReturnElements;
 1004+ }
 1005+ var arrElements = ( strTagName == '*' && oElm.all ) ? oElm.all : oElm.getElementsByTagName( strTagName );
 1006+ var arrRegExpClassNames = new Array();
 1007+ if( typeof oClassNames == 'object' ) {
 1008+ for( var i = 0; i < oClassNames.length; i++ ) {
 1009+ arrRegExpClassNames[arrRegExpClassNames.length] =
 1010+ new RegExp("(^|\\s)" + oClassNames[i].replace(/\-/g, "\\-") + "(\\s|$)");
 1011+ }
 1012+ } else {
 1013+ arrRegExpClassNames[arrRegExpClassNames.length] =
 1014+ new RegExp("(^|\\s)" + oClassNames.replace(/\-/g, "\\-") + "(\\s|$)");
 1015+ }
 1016+ var oElement;
 1017+ var bMatchesAll;
 1018+ for( var j = 0; j < arrElements.length; j++ ) {
 1019+ oElement = arrElements[j];
 1020+ bMatchesAll = true;
 1021+ for( var k = 0; k < arrRegExpClassNames.length; k++ ) {
 1022+ if( !arrRegExpClassNames[k].test( oElement.className ) ) {
 1023+ bMatchesAll = false;
 1024+ break;
 1025+ }
 1026+ }
 1027+ if( bMatchesAll ) {
 1028+ arrReturnElements[arrReturnElements.length] = oElement;
 1029+ }
 1030+ }
 1031+ return ( arrReturnElements );
 1032+ },
 1033+ 'redirectToFragment': function( fragment ) {
 1034+ var match = navigator.userAgent.match(/AppleWebKit\/(\d+)/);
 1035+ if ( match ) {
 1036+ var webKitVersion = parseInt( match[1] );
 1037+ if ( webKitVersion < 420 ) {
 1038+ // Released Safari w/ WebKit 418.9.1 messes up horribly
 1039+ // Nightlies of 420+ are ok
 1040+ return;
 1041+ }
 1042+ }
 1043+ if ( window.location.hash == '' ) {
 1044+ window.location.hash = fragment;
 1045+ // Mozilla needs to wait until after load, otherwise the window doesn't
 1046+ // scroll. See <https://bugzilla.mozilla.org/show_bug.cgi?id=516293>.
 1047+ // There's no obvious way to detect this programmatically, so we use
 1048+ // version-testing. If Firefox fixes the bug, they'll jump twice, but
 1049+ // better twice than not at all, so make the fix hit future versions as
 1050+ // well.
 1051+ if ( mw.legacy.is_gecko ) {
 1052+ addOnloadHook(function() {
 1053+ if ( window.location.hash == fragment ) {
 1054+ window.location.hash = fragment;
 1055+ }
 1056+ });
 1057+ }
 1058+ }
 1059+ }
141060 } );
151061
161062 /* Initialization */
171063
181064 $( document ).ready( function() {
19 - //
 1065+ if ( wgBreakFrames ) {
 1066+ // Un-trap us from framesets
 1067+ if ( window.top != window ) {
 1068+ window.top.location = window.location;
 1069+ }
 1070+ }
 1071+ // Special stylesheet links for Monobook only (see bug 14717)
 1072+ if ( typeof stylepath != 'undefined' && skin == 'monobook' ) {
 1073+ if ( mw.legacy.opera6_bugs ) {
 1074+ importStylesheetURI( stylepath + '/' + skin + '/Opera6Fixes.css' );
 1075+ } else if ( mw.legacy.opera7_bugs ) {
 1076+ importStylesheetURI( stylepath + '/' + skin + '/Opera7Fixes.css' );
 1077+ } else if ( mw.legacy.opera95_bugs ) {
 1078+ importStylesheetURI( stylepath + '/' + skin + '/Opera9Fixes.css' );
 1079+ } else if ( mw.legacy.ff2_bugs ) {
 1080+ importStylesheetURI( stylepath + '/' + skin + '/FF2Fixes.css' );
 1081+ }
 1082+ }
 1083+ if ( mw.legacy.ie6_bugs ) {
 1084+ importScriptURI( mw.legacy.stylepath + '/common/IEFixes.js' );
 1085+ }
 1086+ // NOTE: All skins should call runOnloadHook() at the end of html output, so this should be redundant - it's here
 1087+ // just in case
 1088+ runOnloadHook();
201089 } );
211090
221091 } )( jQuery, MediaWiki );
\ No newline at end of file
Index: branches/resourceloader/phase3/resources/mw/legacy/mw.legacy.edit.js
@@ -6,7 +6,7 @@
77
88 /* Extension */
99
10 -$.extend( mw.legacy, {
 10+$.extend( true, mw.legacy, {
1111
1212 /* Global Variables */
1313
Index: branches/resourceloader/phase3/resources/mw/legacy/mw.legacy.mwsuggest.js
@@ -12,7 +12,7 @@
1313
1414 /* Extension */
1515
16 -$.extend( mw.legacy, {
 16+$.extend( true, mw.legacy, {
1717
1818 /* Global Variables */
1919
Index: branches/resourceloader/phase3/resources/mw/legacy/mw.legacy.htmlform.js
@@ -6,7 +6,7 @@
77
88 /* Extension */
99
10 -$.extend( mw.legacy, {
 10+$.extend( true, mw.legacy, {
1111
1212 /* Global Variables */
1313
Index: branches/resourceloader/phase3/resources/mw/legacy/mw.legacy.rightclickedit.js
@@ -6,7 +6,7 @@
77
88 /* Extension */
99
10 -$.extend( mw.legacy, {
 10+$.extend( true, mw.legacy, {
1111
1212 /* Functions */
1313
@@ -40,7 +40,6 @@
4141 targ = targ.parentNode;
4242 }
4343 // targ is now the target element
44 -
4544 // We don't want to deprive the noble reader of a context menu
4645 // for the section edit link, do we? (Might want to extend this
4746 // to all <a>'s?)
Index: branches/resourceloader/phase3/resources/mw/legacy/mw.legacy.block.js
@@ -6,7 +6,7 @@
77
88 /* Extension */
99
10 -$.extend( mw.legacy, {
 10+$.extend( true, mw.legacy, {
1111
1212 /* Functions */
1313
Index: branches/resourceloader/phase3/resources/mw/legacy/mw.legacy.IEFixes.js
@@ -23,7 +23,7 @@
2424
2525 /* Extension */
2626
27 -$.extend( mw.legacy, {
 27+$.extend( true, mw.legacy, {
2828
2929 /* Global Variables */
3030
Index: branches/resourceloader/phase3/resources/mw/legacy/mw.legacy.enhancedchanges.js
@@ -6,7 +6,7 @@
77
88 /* Extension */
99
10 -$.extend( mw.legacy, {
 10+$.extend( true, mw.legacy, {
1111
1212 /* Functions */
1313
Index: branches/resourceloader/phase3/resources/mw/legacy/mw.legacy.history.js
@@ -6,7 +6,7 @@
77
88 /* Extension */
99
10 -$.extend( mw.legacy, {
 10+$.extend( true, mw.legacy, {
1111
1212 /* Functions */
1313
Index: branches/resourceloader/phase3/resources/mw/legacy/mw.legacy.search.js
@@ -8,34 +8,34 @@
99
1010 /* Extension */
1111
12 -$.extend( mw.legacy, {
 12+$.extend( true, mw.legacy, {
1313
1414 /* Functions */
1515
1616 /**
1717 * Change the search link to what user entered
1818 */
19 - function mwSearchHeaderClick( obj ) {
 19+ 'mwSearchHeaderClick': function( obj ) {
2020 var searchbox = document.getElementById( 'searchText' );
21 - if( searchbox === null ) {
 21+ if ( searchbox === null ) {
2222 searchbox = document.getElementById( 'powerSearchText' );
2323 }
24 - if( searchbox === null ) {
 24+ if ( searchbox === null ) {
2525 return; // should always have either normal or advanced search
2626 }
2727 var searchterm = searchbox.value;
2828 var parts = obj.href.split( 'search=' );
2929 var lastpart = '';
3030 var prefix = 'search=';
31 - if( parts.length > 1 && parts[1].indexOf('&') >= 0 ) {
 31+ if ( parts.length > 1 && parts[1].indexOf('&') >= 0 ) {
3232 lastpart = parts[1].substring( parts[1].indexOf('&') );
3333 } else {
3434 prefix = '&search=';
3535 }
3636 obj.href = parts[0] + prefix + encodeURIComponent( searchterm ) + lastpart;
37 - }
38 - function mwToggleSearchCheckboxes( btn ) {
39 - if( !document.getElementById ) {
 37+ },
 38+ 'mwToggleSearchCheckboxes': function( btn ) {
 39+ if ( !document.getElementById ) {
4040 return;
4141 }
4242 var nsInputs = document.getElementById( 'powersearch' ).getElementsByTagName( 'input' );
Index: branches/resourceloader/phase3/resources/mw/legacy/mw.legacy.changepassword.js
@@ -6,7 +6,7 @@
77
88 /* Extension */
99
10 -$.extend( mw.legacy, {
 10+$.extend( true, mw.legacy, {
1111
1212 /* Functions */
1313
Index: branches/resourceloader/phase3/resources/mw/legacy/mw.legacy.ajaxwatch.js
@@ -10,7 +10,7 @@
1111
1212 /* Extension */
1313
14 -$.extend( mw.legacy, {
 14+$.extend( true, mw.legacy, {
1515
1616 /* Global Variables */
1717

Status & tagging log