Index: branches/resourceloader/phase3/resources/Resources.php |
— | — | @@ -9,64 +9,76 @@ |
10 | 10 | 'script' => 'resources/jquery/jquery.tabIndex.js', |
11 | 11 | 'raw' => true, |
12 | 12 | ), |
13 | | - 'mw' => array( |
14 | | - 'script' => 'resources/mw/mw.js', |
| 13 | + 'mediawiki' => array( |
| 14 | + 'script' => 'resources/mediawiki/mediawiki.js', |
15 | 15 | 'raw' => true, |
16 | 16 | ), |
17 | | - 'mw.legacy.ajax' => array( |
18 | | - 'script' => 'resources/mw/legacy/mw.legacy.ajax.js', |
| 17 | + 'mediawiki.legacy.ajax' => array( |
| 18 | + 'script' => 'resources/mediawiki/legacy/mediawiki.legacy.ajax.js', |
19 | 19 | ), |
20 | | - 'mw.legacy.ajaxwatch' => array( |
21 | | - 'script' => 'resources/mw/legacy/mw.legacy.ajaxwatch.js', |
| 20 | + 'mediawiki.legacy.ajaxwatch' => array( |
| 21 | + 'script' => 'resources/mediawiki/legacy/mediawiki.legacy.ajaxwatch.js', |
22 | 22 | ), |
23 | | - 'mw.legacy.block' => array( |
24 | | - 'script' => 'resources/mw/legacy/mw.legacy.block.js', |
| 23 | + 'mediawiki.legacy.block' => array( |
| 24 | + 'script' => 'resources/mediawiki/legacy/mediawiki.legacy.block.js', |
25 | 25 | ), |
26 | | - 'mw.legacy.changepassword' => array( |
27 | | - 'script' => 'resources/mw/legacy/mw.legacy.changepassword.js', |
| 26 | + 'mediawiki.legacy.changepassword' => array( |
| 27 | + 'script' => 'resources/mediawiki/legacy/mediawiki.legacy.changepassword.js', |
28 | 28 | ), |
29 | | - 'mw.legacy.edit' => array( |
30 | | - 'script' => 'resources/mw/legacy/mw.legacy.edit.js', |
| 29 | + 'mediawiki.legacy.edit' => array( |
| 30 | + 'script' => 'resources/mediawiki/legacy/mediawiki.legacy.edit.js', |
31 | 31 | ), |
32 | | - 'mw.legacy.enhancedchanges' => array( |
33 | | - 'script' => 'resources/mw/legacy/mw.legacy.enhancedchanges.js', |
| 32 | + 'mediawiki.legacy.enhancedchanges' => array( |
| 33 | + 'script' => 'resources/mediawiki/legacy/mediawiki.legacy.enhancedchanges.js', |
34 | 34 | ), |
35 | | - 'mw.legacy.history' => array( |
36 | | - 'script' => 'resources/mw/legacy/mw.legacy.history.js', |
| 35 | + 'mediawiki.legacy.history' => array( |
| 36 | + 'script' => 'resources/mediawiki/legacy/mediawiki.legacy.history.js', |
37 | 37 | ), |
38 | | - 'mw.legacy.htmlform' => array( |
39 | | - 'script' => 'resources/mw/legacy/mw.legacy.htmlform.js', |
| 38 | + 'mediawiki.legacy.htmlform' => array( |
| 39 | + 'script' => 'resources/mediawiki/legacy/mediawiki.legacy.htmlform.js', |
40 | 40 | ), |
41 | | - 'mw.legacy.IEFixes' => array( |
42 | | - 'script' => 'resources/mw/legacy/mw.legacy.IEFixes.js', |
| 41 | + 'mediawiki.legacy.IEFixes' => array( |
| 42 | + 'script' => 'resources/mediawiki/legacy/mediawiki.legacy.IEFixes.js', |
43 | 43 | ), |
44 | | - 'mw.legacy.metadata' => array( |
45 | | - 'script' => 'resources/mw/legacy/mw.legacy.metadata.js', |
| 44 | + 'mediawiki.legacy.metadata' => array( |
| 45 | + 'script' => 'resources/mediawiki/legacy/mediawiki.legacy.metadata.js', |
46 | 46 | ), |
47 | | - 'mw.legacy.mwsuggest' => array( |
48 | | - 'script' => 'resources/mw/legacy/mw.legacy.mwsuggest.js', |
| 47 | + 'mediawiki.legacy.mwsuggest' => array( |
| 48 | + 'script' => 'resources/mediawiki/legacy/mediawiki.legacy.mwsuggest.js', |
49 | 49 | ), |
50 | | - 'mw.legacy.prefs' => array( |
51 | | - 'script' => 'resources/mw/legacy/mw.legacy.prefs.js', |
| 50 | + 'mediawiki.legacy.prefs' => array( |
| 51 | + 'script' => 'resources/mediawiki/legacy/mediawiki.legacy.prefs.js', |
52 | 52 | ), |
53 | | - 'mw.legacy.preview' => array( |
54 | | - 'script' => 'resources/mw/legacy/mw.legacy.preview.js', |
| 53 | + 'mediawiki.legacy.preview' => array( |
| 54 | + 'script' => 'resources/mediawiki/legacy/mediawiki.legacy.preview.js', |
55 | 55 | ), |
56 | | - 'mw.legacy.protect' => array( |
57 | | - 'script' => 'resources/mw/legacy/mw.legacy.protect.js', |
| 56 | + 'mediawiki.legacy.protect' => array( |
| 57 | + 'script' => 'resources/mediawiki/legacy/mediawiki.legacy.protect.js', |
58 | 58 | ), |
59 | | - 'mw.legacy.rightclickedit' => array( |
60 | | - 'script' => 'resources/mw/legacy/mw.legacy.rightclickedit.js', |
| 59 | + 'mediawiki.legacy.rightclickedit' => array( |
| 60 | + 'script' => 'resources/mediawiki/legacy/mediawiki.legacy.rightclickedit.js', |
61 | 61 | ), |
62 | | - 'mw.legacy.search' => array( |
63 | | - 'script' => 'resources/mw/legacy/mw.legacy.search.js', |
| 62 | + 'mediawiki.legacy.search' => array( |
| 63 | + 'script' => 'resources/mediawiki/legacy/mediawiki.legacy.search.js', |
64 | 64 | ), |
65 | | - 'mw.legacy.upload' => array( |
66 | | - 'script' => 'resources/mw/legacy/mw.legacy.upload.js', |
| 65 | + 'mediawiki.legacy.upload' => array( |
| 66 | + 'script' => 'resources/mediawiki/legacy/mediawiki.legacy.upload.js', |
67 | 67 | ), |
68 | | - 'mw.legacy.wikibits' => array( |
69 | | - 'script' => 'resources/mw/legacy/mw.legacy.wikibits.js', |
| 68 | + 'mediawiki.legacy.wikibits' => array( |
| 69 | + 'script' => 'resources/mediawiki/legacy/mediawiki.legacy.wikibits.js', |
70 | 70 | ), |
| 71 | + |
| 72 | + 'mediawiki.utilities.client' => array( |
| 73 | + 'script' => 'resources/mediawiki/utilities/mediawiki.utilities.client.js', |
| 74 | + ), |
| 75 | + |
| 76 | + 'mediawiki.views.diff' => array( |
| 77 | + 'script' => 'resources/mediawiki/views/mediawiki.views.diff.js', |
| 78 | + ), |
| 79 | + 'mediawiki.views.install' => array( |
| 80 | + 'script' => 'resources/mediawiki/views/mediawiki.views.install.js', |
| 81 | + ), |
| 82 | + |
71 | 83 | 'test' => array( |
72 | 84 | 'script' => 'resources/test/test.js', |
73 | 85 | 'loader' => 'resources/test/loader.js', |
Index: branches/resourceloader/phase3/resources/mediawiki/legacy/mediawiki.legacy.history.js |
— | — | @@ -0,0 +1,115 @@ |
| 2 | +/* |
| 3 | + * Legacy emulation for the now depricated skins/common/history.js |
| 4 | + */ |
| 5 | + |
| 6 | +( function( $, mw ) { |
| 7 | + |
| 8 | +/* Extension */ |
| 9 | + |
| 10 | +$.extend( true, mw.legacy, { |
| 11 | + |
| 12 | + /* Functions */ |
| 13 | + |
| 14 | + 'historyRadios': function( parent ) { |
| 15 | + var inputs = parent.getElementsByTagName('input'); |
| 16 | + var radios = []; |
| 17 | + for (var i = 0; i < inputs.length; i++) { |
| 18 | + if (inputs[i].name == 'diff' || inputs[i].name == 'oldid') { |
| 19 | + radios[radios.length] = inputs[i]; |
| 20 | + } |
| 21 | + } |
| 22 | + return radios; |
| 23 | + }, |
| 24 | + /* |
| 25 | + * Check selection and tweak visibility/class onclick |
| 26 | + */ |
| 27 | + 'diffcheck': function() { |
| 28 | + var dli = false; // the li where the diff radio is checked |
| 29 | + var oli = false; // the li where the oldid radio is checked |
| 30 | + var hf = document.getElementById('pagehistory'); |
| 31 | + if (!hf) { |
| 32 | + return true; |
| 33 | + } |
| 34 | + var lis = hf.getElementsByTagName('li'); |
| 35 | + for (var i=0;i<lis.length;i++) { |
| 36 | + var inputs = historyRadios(lis[i]); |
| 37 | + if (inputs[1] && inputs[0]) { |
| 38 | + if (inputs[1].checked || inputs[0].checked) { // this row has a checked radio button |
| 39 | + if (inputs[1].checked && inputs[0].checked && inputs[0].value == inputs[1].value) { |
| 40 | + return false; |
| 41 | + } |
| 42 | + if (oli) { // it's the second checked radio |
| 43 | + if (inputs[1].checked) { |
| 44 | + if ( (typeof oli.className) != 'undefined') { |
| 45 | + oli.classNameOriginal = oli.className.replace( 'selected', '' ); |
| 46 | + } else { |
| 47 | + oli.classNameOriginal = ''; |
| 48 | + } |
| 49 | + |
| 50 | + oli.className = 'selected '+oli.classNameOriginal; |
| 51 | + return false; |
| 52 | + } |
| 53 | + } else if (inputs[0].checked) { |
| 54 | + return false; |
| 55 | + } |
| 56 | + if (inputs[0].checked) { |
| 57 | + dli = lis[i]; |
| 58 | + } |
| 59 | + if (!oli) { |
| 60 | + inputs[0].style.visibility = 'hidden'; |
| 61 | + } |
| 62 | + if (dli) { |
| 63 | + inputs[1].style.visibility = 'hidden'; |
| 64 | + } |
| 65 | + if ( (typeof lis[i].className) != 'undefined') { |
| 66 | + lis[i].classNameOriginal = lis[i].className.replace( 'selected', '' ); |
| 67 | + } else { |
| 68 | + lis[i].classNameOriginal = ''; |
| 69 | + } |
| 70 | + |
| 71 | + lis[i].className = 'selected '+lis[i].classNameOriginal; |
| 72 | + oli = lis[i]; |
| 73 | + } else { // no radio is checked in this row |
| 74 | + if (!oli) { |
| 75 | + inputs[0].style.visibility = 'hidden'; |
| 76 | + } else { |
| 77 | + inputs[0].style.visibility = 'visible'; |
| 78 | + } |
| 79 | + if (dli) { |
| 80 | + inputs[1].style.visibility = 'hidden'; |
| 81 | + } else { |
| 82 | + inputs[1].style.visibility = 'visible'; |
| 83 | + } |
| 84 | + if ( typeof lis[i].classNameOriginal != 'undefined' ) { |
| 85 | + lis[i].className = lis[i].classNameOriginal; |
| 86 | + } |
| 87 | + } |
| 88 | + } |
| 89 | + } |
| 90 | + return true; |
| 91 | + }, |
| 92 | + /* |
| 93 | + * Attach event handlers to the input elements on history page |
| 94 | + */ |
| 95 | + 'histrowinit': function() { |
| 96 | + var hf = document.getElementById('pagehistory'); |
| 97 | + if (!hf) return; |
| 98 | + var lis = hf.getElementsByTagName('li'); |
| 99 | + for (var i = 0; i < lis.length; i++) { |
| 100 | + var inputs = historyRadios(lis[i]); |
| 101 | + if (inputs[0] && inputs[1]) { |
| 102 | + inputs[0].onclick = diffcheck; |
| 103 | + inputs[1].onclick = diffcheck; |
| 104 | + } |
| 105 | + } |
| 106 | + diffcheck(); |
| 107 | + } |
| 108 | +} ); |
| 109 | + |
| 110 | +/* Initialization */ |
| 111 | + |
| 112 | +$( document ).ready( function() { |
| 113 | + mw.legacy.histrowinit(); |
| 114 | +} ); |
| 115 | + |
| 116 | +} )( jQuery, MediaWiki ); |
\ No newline at end of file |
Property changes on: branches/resourceloader/phase3/resources/mediawiki/legacy/mediawiki.legacy.history.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 117 | + native |
Index: branches/resourceloader/phase3/resources/mediawiki/legacy/mediawiki.legacy.search.js |
— | — | @@ -0,0 +1,63 @@ |
| 2 | +/* |
| 3 | + * Legacy emulation for the now depricated skins/common/search.js |
| 4 | + * |
| 5 | + * Progressive enhancement for Special:Search |
| 6 | + */ |
| 7 | + |
| 8 | +( function( $, mw ) { |
| 9 | + |
| 10 | +/* Extension */ |
| 11 | + |
| 12 | +$.extend( true, mw.legacy, { |
| 13 | + |
| 14 | + /* Functions */ |
| 15 | + |
| 16 | + /** |
| 17 | + * Change the search link to what user entered |
| 18 | + */ |
| 19 | + 'mwSearchHeaderClick': function( obj ) { |
| 20 | + var searchbox = document.getElementById( 'searchText' ); |
| 21 | + if ( searchbox === null ) { |
| 22 | + searchbox = document.getElementById( 'powerSearchText' ); |
| 23 | + } |
| 24 | + if ( searchbox === null ) { |
| 25 | + return; // should always have either normal or advanced search |
| 26 | + } |
| 27 | + var searchterm = searchbox.value; |
| 28 | + var parts = obj.href.split( 'search=' ); |
| 29 | + var lastpart = ''; |
| 30 | + var prefix = 'search='; |
| 31 | + if ( parts.length > 1 && parts[1].indexOf('&') >= 0 ) { |
| 32 | + lastpart = parts[1].substring( parts[1].indexOf('&') ); |
| 33 | + } else { |
| 34 | + prefix = '&search='; |
| 35 | + } |
| 36 | + obj.href = parts[0] + prefix + encodeURIComponent( searchterm ) + lastpart; |
| 37 | + }, |
| 38 | + 'mwToggleSearchCheckboxes': function( btn ) { |
| 39 | + if ( !document.getElementById ) { |
| 40 | + return; |
| 41 | + } |
| 42 | + var nsInputs = document.getElementById( 'powersearch' ).getElementsByTagName( 'input' ); |
| 43 | + var isChecked = false; |
| 44 | + for ( var i = 0; i < nsInputs.length; i++ ) { |
| 45 | + var pattern = /^ns/; |
| 46 | + if ( ( nsInputs[i].type == 'checkbox' ) && ( pattern.test( nsInputs[i].name ) ) ) { |
| 47 | + switch ( btn ) { |
| 48 | + case 'none': |
| 49 | + if ( nsInputs[i].checked ) { |
| 50 | + nsInputs[i].checked = false; |
| 51 | + } |
| 52 | + break; |
| 53 | + case 'all': |
| 54 | + if ( !nsInputs[i].checked ) { |
| 55 | + nsInputs[i].checked = true; |
| 56 | + } |
| 57 | + break; |
| 58 | + } |
| 59 | + } |
| 60 | + } |
| 61 | + } |
| 62 | +} ); |
| 63 | + |
| 64 | +} )( jQuery, MediaWiki ); |
\ No newline at end of file |
Property changes on: branches/resourceloader/phase3/resources/mediawiki/legacy/mediawiki.legacy.search.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 65 | + native |
Index: branches/resourceloader/phase3/resources/mediawiki/legacy/mediawiki.legacy.changepassword.js |
— | — | @@ -0,0 +1,29 @@ |
| 2 | +/* |
| 3 | + * Legacy emulation for the now depricated skins/common/changepassword.js |
| 4 | + */ |
| 5 | + |
| 6 | +( function( $, mw ) { |
| 7 | + |
| 8 | +/* Extension */ |
| 9 | + |
| 10 | +$.extend( true, mw.legacy, { |
| 11 | + |
| 12 | + /* Functions */ |
| 13 | + |
| 14 | + 'onNameChange': function() { |
| 15 | + var state = mw.legacy.wgUserName != $( '#wpName' ).val(); |
| 16 | + $( '#wpPassword' ).attr( 'disabled', state ); |
| 17 | + $( '#wpComment' ).attr( 'disabled', !state ); |
| 18 | + }, |
| 19 | + 'onNameChangeHook': function() { |
| 20 | + $( '#wpName' ).blur( mw.legacy.onNameChange ); |
| 21 | + } |
| 22 | +} ); |
| 23 | + |
| 24 | +/* Initialization */ |
| 25 | + |
| 26 | +$( document ).ready( function() { |
| 27 | + mw.legacy.onNameChangeHook(); |
| 28 | +} ); |
| 29 | + |
| 30 | +} )( jQuery, MediaWiki ); |
\ No newline at end of file |
Property changes on: branches/resourceloader/phase3/resources/mediawiki/legacy/mediawiki.legacy.changepassword.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 31 | + native |
Index: branches/resourceloader/phase3/resources/mediawiki/legacy/mediawiki.legacy.ajaxwatch.js |
— | — | @@ -0,0 +1,136 @@ |
| 2 | +/* |
| 3 | + * Legacy emulation for the now depricated skins/common/ajaxwatch.js |
| 4 | + * |
| 5 | + * AJAX functionality for the watch/unwatch link |
| 6 | + * |
| 7 | + * @depends mw.legacy.jsMsg() from mw.legacy.wikibits.js |
| 8 | + */ |
| 9 | + |
| 10 | +( function( $, mw ) { |
| 11 | + |
| 12 | +/* Extension */ |
| 13 | + |
| 14 | +$.extend( true, mw.legacy, { |
| 15 | + |
| 16 | + /* Global Variables */ |
| 17 | + |
| 18 | + 'wgAjaxWatch': { |
| 19 | + |
| 20 | + /* Global Variables */ |
| 21 | + |
| 22 | + 'watchMsg': 'Watch', |
| 23 | + 'unwatchMsg': 'Unwatch', |
| 24 | + 'watchingMsg': 'Watching...', |
| 25 | + 'unwatchingMsg': 'Unwatching...', |
| 26 | + 'tooltip-ca-watchMsg': 'Add this page to your watchlist', |
| 27 | + 'tooltip-ca-unwatchMsg': 'Remove this page from your watchlist', |
| 28 | + |
| 29 | + /* Functions */ |
| 30 | + |
| 31 | + /** |
| 32 | + * Sets the text of the watch/unwatch link |
| 33 | + * |
| 34 | + * @param object link DOM node or jQuery selection of link to set text of |
| 35 | + * @param string action message to use ('watch', 'unwatch', 'watching' or 'unwatching') |
| 36 | + */ |
| 37 | + 'setLinkText': function( link, action ) { |
| 38 | + var $link = $( link ); |
| 39 | + if ( action == 'watch' || action == 'unwatch' ) { |
| 40 | + // save the accesskey from the title |
| 41 | + var keyCommand = $link.attr( 'title' ).match( /\[.*?\]$/ ) ? |
| 42 | + $link.attr( 'title' ).match( /\[.*?\]$/ )[0] : ''; |
| 43 | + $link.attr( 'title', wgAjaxWatch['tooltip-ca-' + action + 'Msg'] + ' ' + keyCommand ); |
| 44 | + } |
| 45 | + if ( $link.data( 'icon' ) ) { |
| 46 | + $link.attr( 'alt', wgAjaxWatch[action + 'Msg'] ); |
| 47 | + if ( action == 'watching' || action == 'unwatching' ) { |
| 48 | + $link.addClass( 'loading' ); |
| 49 | + } else { |
| 50 | + $link.removeClass( 'loading' ); |
| 51 | + } |
| 52 | + } else { |
| 53 | + $link.html( wgAjaxWatch[action+'Msg'] ); |
| 54 | + } |
| 55 | + }, |
| 56 | + /** |
| 57 | + * Processes responses from the server |
| 58 | + * |
| 59 | + * @param object response data from server |
| 60 | + */ |
| 61 | + 'processResult': function( response ) { |
| 62 | + response = response.watch; |
| 63 | + var $link = $(this); |
| 64 | + // To ensure we set the same status for all watch links with the same target we trigger a custom event on |
| 65 | + // *all* watch links. |
| 66 | + if ( response.watched !== undefined ) { |
| 67 | + wgAjaxWatch.$links.trigger( 'mw-ajaxwatch', [response.title, 'watch'] ); |
| 68 | + } else if ( response.unwatched !== undefined ){ |
| 69 | + wgAjaxWatch.$links.trigger( 'mw-ajaxwatch', [response.title, 'unwatch'] ); |
| 70 | + } else { |
| 71 | + // Either we got an error code or it just plain broke. |
| 72 | + window.location.href = $link.attr( 'href' ); |
| 73 | + return; |
| 74 | + } |
| 75 | + mw.legacy.jsMsg( response.message, 'watch' ); |
| 76 | + // Bug 12395 - update the watch checkbox on edit pages when the page is watched or unwatched via the tab. |
| 77 | + if ( response.watched !== undefined ) { |
| 78 | + $j( '#wpWatchthis' ).attr( 'checked', '1' ); |
| 79 | + } else { |
| 80 | + $j( '#wpWatchthis' ).removeAttr( 'checked' ); |
| 81 | + } |
| 82 | + } |
| 83 | + } ); |
| 84 | +} ); |
| 85 | + |
| 86 | +/* Initialization */ |
| 87 | + |
| 88 | +$( document ).ready( function() { |
| 89 | + var $links = $( '.mw-watchlink a, a.mw-watchlink' ); |
| 90 | + // BC with older skins... |
| 91 | + $links = $links |
| 92 | + .add( $( '#ca-watch a, #ca-unwatch a, a#mw-unwatch-link1' ) ) |
| 93 | + .add( $( 'a#mw-unwatch-link2, a#mw-watch-link2, a#mw-watch-link1' ) ); |
| 94 | + // ...allowing people to add inline animated links is a little scary |
| 95 | + $links = $links.filter( ':not( #bodyContent *, #content * )' ); |
| 96 | + $links.each( function() { |
| 97 | + var $link = $(this); |
| 98 | + $link |
| 99 | + .data( 'icon', $link.parent().hasClass( 'icon' ) ) |
| 100 | + .data( 'action', $link.attr( 'href' ).match( /[\?\&]action=unwatch/i ) ? 'unwatch' : 'watch' ); |
| 101 | + var title = $link.attr( 'href' ).match( /[\?\&]title=(.*?)&/i )[1]; |
| 102 | + $link.data( 'target', decodeURIComponent( title ).replace( /_/g, ' ' ) ); |
| 103 | + } ); |
| 104 | + $links.click( function( event ) { |
| 105 | + var $link = $(this); |
| 106 | + if ( mw.legacy.wgAjaxWatch.supported === false || !mw.legacy.wgEnableWriteAPI || !mw.legacy.wfSupportsAjax() ) { |
| 107 | + // Lazy initialization so we don't toss up ActiveX warnings on initial page load for IE 6 users with |
| 108 | + // security settings. |
| 109 | + mw.legacy.wgAjaxWatch.$links.unbind( 'click' ); |
| 110 | + return true; |
| 111 | + } |
| 112 | + mw.legacy.wgAjaxWatch.setLinkText( $link, $link.data( 'action' ) + 'ing' ); |
| 113 | + var url = mw.legacy.wgScriptPath + '/api' + mw.legacy.wgScriptExtension + '?action=watch&format=json&title=' |
| 114 | + + encodeURIComponent( $link.data( 'target' ) ) + ( $link.data( 'action' ) == 'unwatch' ? '&unwatch' : '' ); |
| 115 | + $.get( url, {}, mw.legacy.wgAjaxWatch.processResult, 'json' ); |
| 116 | + return false; |
| 117 | + } ); |
| 118 | + // When a request returns, a custom event 'mw-ajaxwatch' is triggered on *all* watch links, so they can be updated |
| 119 | + // if necessary |
| 120 | + $links.bind( 'mw-ajaxwatch', function( event, target, action ) { |
| 121 | + var $link = $(this); |
| 122 | + var foo = $link.data( 'target' ); |
| 123 | + if ( $link.data( 'target' ) == target ) { |
| 124 | + var otheraction = action == 'watch' ? 'unwatch' : 'watch'; |
| 125 | + $link.data( 'action', otheraction ); |
| 126 | + wgAjaxWatch.setLinkText( $link, otheraction ); |
| 127 | + $link.attr( 'href', $link.attr( 'href' ).replace( '/&action=' + action + '/', '&action=' + otheraction ) ); |
| 128 | + if ( $link.parent().attr( 'id' ) == 'ca-' + action ){ |
| 129 | + $link.parent().attr( 'id', 'ca-' + otheraction ); |
| 130 | + } |
| 131 | + } |
| 132 | + return false; |
| 133 | + } ); |
| 134 | + mw.legacy.wgAjaxWatch.$links = $links; |
| 135 | +} ); |
| 136 | + |
| 137 | +} )( jQuery, MediaWiki ); |
\ No newline at end of file |
Property changes on: branches/resourceloader/phase3/resources/mediawiki/legacy/mediawiki.legacy.ajaxwatch.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 138 | + native |
Index: branches/resourceloader/phase3/resources/mediawiki/legacy/mediawiki.legacy.prefs.js |
— | — | @@ -0,0 +1,233 @@ |
| 2 | +/* |
| 3 | + * Legacy emulation for the now depricated skins/common/prefs.js |
| 4 | + * |
| 5 | + * Generate toc from prefs form, fold sections |
| 6 | + * |
| 7 | + * FIXME: Needs testing on IE/Mac and Safari |
| 8 | + */ |
| 9 | + |
| 10 | +( function( $, mw ) { |
| 11 | + |
| 12 | +/* Extension */ |
| 13 | + |
| 14 | +$.extend( true, mw.legacy, { |
| 15 | + |
| 16 | + /* Functions */ |
| 17 | + |
| 18 | + 'tabbedprefs': function() { |
| 19 | + var prefform = document.getElementById( 'preferences' ); |
| 20 | + if ( !prefform || !document.createElement ) { |
| 21 | + return; |
| 22 | + } |
| 23 | + if ( prefform.nodeName.toLowerCase() == 'a' ) { |
| 24 | + return; // Occasional IE problem |
| 25 | + } |
| 26 | + prefform.className = prefform.className + 'jsprefs'; |
| 27 | + var sections = []; |
| 28 | + var children = prefform.childNodes; |
| 29 | + var seci = 0; |
| 30 | + for ( var i = 0; i < children.length; i++ ) { |
| 31 | + if ( children[i].nodeName.toLowerCase() == 'fieldset' ) { |
| 32 | + children[i].id = 'prefsection-' + seci; |
| 33 | + children[i].className = 'prefsection'; |
| 34 | + if ( is_opera ) { |
| 35 | + children[i].className = 'prefsection operaprefsection'; |
| 36 | + } |
| 37 | + var legends = children[i].getElementsByTagName('legend'); |
| 38 | + sections[seci] = {}; |
| 39 | + if ( legends[0] ) { |
| 40 | + legends[0].className = 'mainLegend'; |
| 41 | + } |
| 42 | + if ( legends[0] && legends[0].firstChild.nodeValue ) { |
| 43 | + sections[seci].text = legends[0].firstChild.nodeValue; |
| 44 | + } else { |
| 45 | + sections[seci].text = '# ' + seci; |
| 46 | + } |
| 47 | + sections[seci].secid = children[i].id; |
| 48 | + seci++; |
| 49 | + if ( sections.length != 1 ) { |
| 50 | + children[i].style.display = 'none'; |
| 51 | + } else { |
| 52 | + var selectedid = children[i].id; |
| 53 | + } |
| 54 | + } |
| 55 | + } |
| 56 | + var toc = document.createElement( 'ul' ); |
| 57 | + toc.id = 'preftoc'; |
| 58 | + toc.selectedid = selectedid; |
| 59 | + for ( i = 0; i < sections.length; i++ ) { |
| 60 | + var li = document.createElement( 'li' ); |
| 61 | + if ( i === 0 ) { |
| 62 | + li.className = 'selected'; |
| 63 | + } |
| 64 | + var a = document.createElement( 'a' ); |
| 65 | + a.href = '#' + sections[i].secid; |
| 66 | + a.onmousedown = a.onclick = uncoversection; |
| 67 | + a.appendChild( document.createTextNode( sections[i].text ) ); |
| 68 | + a.secid = sections[i].secid; |
| 69 | + li.appendChild( a ); |
| 70 | + toc.appendChild( li ); |
| 71 | + } |
| 72 | + prefform.parentNode.insertBefore( toc, prefform.parentNode.childNodes[0] ); |
| 73 | + document.getElementById( 'prefsubmit' ).id = 'prefcontrol'; |
| 74 | + }, |
| 75 | + 'uncoversection': function() { |
| 76 | + var oldsecid = this.parentNode.parentNode.selectedid; |
| 77 | + var newsec = document.getElementById( this.secid ); |
| 78 | + if ( oldsecid != this.secid ) { |
| 79 | + var ul = document.getElementById( 'preftoc' ); |
| 80 | + document.getElementById( oldsecid ).style.display = 'none'; |
| 81 | + newsec.style.display = 'block'; |
| 82 | + ul.selectedid = this.secid; |
| 83 | + var lis = ul.getElementsByTagName( 'li' ); |
| 84 | + for ( var i = 0; i< lis.length; i++ ) { |
| 85 | + lis[i].className = ''; |
| 86 | + } |
| 87 | + this.parentNode.className = 'selected'; |
| 88 | + } |
| 89 | + return false; |
| 90 | + }, |
| 91 | + /** |
| 92 | + * Timezone stuff tz in format [+-]HHMM |
| 93 | + */ |
| 94 | + 'checkTimezone': function( tz, msg ) { |
| 95 | + var localclock = new Date(); |
| 96 | + // returns negative offset from GMT in minutes |
| 97 | + var tzRaw = localclock.getTimezoneOffset(); |
| 98 | + var tzHour = Math.floor( Math.abs( tzRaw ) / 60 ); |
| 99 | + var tzMin = Math.abs( tzRaw ) % 60; |
| 100 | + var tzString = ( ( tzRaw >= 0 ) ? '-' : '+' ) + ( ( tzHour < 10 ) ? '0' : '' ) + tzHour + ( ( tzMin < 10 ) ? '0' : '' ) + tzMin; |
| 101 | + if ( tz != tzString ) { |
| 102 | + var junk = msg.split('$1'); |
| 103 | + document.write( junk[0] + 'UTC' + tzString + junk[1] ); |
| 104 | + } |
| 105 | + }, |
| 106 | + 'timezoneSetup': function() { |
| 107 | + var tzSelect = document.getElementById( 'mw-input-timecorrection' ); |
| 108 | + var tzTextbox = document.getElementById( 'mw-input-timecorrection-other' ); |
| 109 | + |
| 110 | + if ( tzSelect && tzTextbox ) { |
| 111 | + addHandler( tzSelect, 'change', function( e ) { updateTimezoneSelection( false ); } ); |
| 112 | + addHandler( tzTextbox, 'blur', function( e ) { updateTimezoneSelection( true ); } ); |
| 113 | + } |
| 114 | + |
| 115 | + updateTimezoneSelection( false ); |
| 116 | + }, |
| 117 | + /** |
| 118 | + * Timezone stuff tz in format [-]HH:MM - won't yet work with non-even tzs |
| 119 | + */ |
| 120 | + 'fetchTimezone': function() { |
| 121 | + // FIXME: work around Safari bug |
| 122 | + var localclock = new Date(); |
| 123 | + // returns negative offset from GMT in minutes |
| 124 | + var tzRaw = localclock.getTimezoneOffset(); |
| 125 | + var tzHour = Math.floor( Math.abs( tzRaw ) / 60 ); |
| 126 | + var tzMin = Math.abs( tzRaw ) % 60; |
| 127 | + var tzString = ( ( tzRaw >= 0 ) ? '-' : '' ) + ( ( tzHour < 10 ) ? '0' : '' ) + tzHour + |
| 128 | + ':' + ( ( tzMin < 10 ) ? '0' : '' ) + tzMin; |
| 129 | + return tzString; |
| 130 | + }, |
| 131 | + 'guessTimezone': function() { |
| 132 | + var textbox = document.getElementById( 'mw-input-timecorrection-other' ); |
| 133 | + var selector = document.getElementById( 'mw-input-timecorrection' ); |
| 134 | + |
| 135 | + selector.value = 'other'; |
| 136 | + textbox.value = fetchTimezone(); |
| 137 | + textbox.disabled = false; // The changed handler doesn't trip, obviously. |
| 138 | + updateTimezoneSelection( true ); |
| 139 | + }, |
| 140 | + 'updateTimezoneSelection': function( force_offset ) { |
| 141 | + var selector = document.getElementById( 'mw-input-timecorrection' ); |
| 142 | + |
| 143 | + if ( selector.value == 'guess' ) { |
| 144 | + return guessTimezone(); |
| 145 | + } |
| 146 | + |
| 147 | + var textbox = document.getElementById( 'mw-input-timecorrection-other' ); |
| 148 | + var localtimeHolder = document.getElementById( 'wpLocalTime' ); |
| 149 | + var servertime = document.getElementsByName( 'wpServerTime' )[0].value; |
| 150 | + var minDiff = 0; |
| 151 | + |
| 152 | + // Compatibility code. |
| 153 | + if ( !selector.value ) { |
| 154 | + selector.value = selector.options[selector.selectedIndex].value; |
| 155 | + } |
| 156 | + |
| 157 | + // Handle force_offset |
| 158 | + if ( force_offset ) { |
| 159 | + selector.value = 'other'; |
| 160 | + } |
| 161 | + |
| 162 | + // Get min_diff |
| 163 | + if ( selector.value == 'other' ) { |
| 164 | + // Grab data from the textbox, parse it. |
| 165 | + var diffArr = textbox.value.split(':'); |
| 166 | + if ( diffArr.length == 1 ) { |
| 167 | + // Specification is of the form [-]XX |
| 168 | + minDiff = parseInt( diffArr[0], 10 ) * 60; |
| 169 | + } else { |
| 170 | + // Specification is of the form [-]XX:XX |
| 171 | + minDiff = Math.abs( parseInt( diffArr[0], 10 ) ) * 60 + parseInt( diffArr[1], 10 ); |
| 172 | + if ( parseInt( diffArr[0], 10 ) < 0 ) { |
| 173 | + minDiff = -minDiff; |
| 174 | + } |
| 175 | + } |
| 176 | + } else { |
| 177 | + // Grab data from the selector value |
| 178 | + var diffArr = selector.value.split('|'); |
| 179 | + minDiff = parseInt( diffArr[1], 10 ); |
| 180 | + } |
| 181 | + |
| 182 | + // Gracefully handle non-numbers. |
| 183 | + if ( isNaN( minDiff ) ) { |
| 184 | + minDiff = 0; |
| 185 | + } |
| 186 | + |
| 187 | + // Determine local time from server time and minutes difference, for display. |
| 188 | + var localTime = parseInt( servertime, 10 ) + minDiff; |
| 189 | + |
| 190 | + // Bring time within the [0,1440) range. |
| 191 | + while ( localTime < 0 ) { |
| 192 | + localTime += 1440; |
| 193 | + } |
| 194 | + while ( localTime >= 1440 ) { |
| 195 | + localTime -= 1440; |
| 196 | + } |
| 197 | + |
| 198 | + // Split to hour and minute |
| 199 | + var hour = String( Math.floor( localTime / 60 ) ); |
| 200 | + if ( hour.length < 2 ) { |
| 201 | + hour = '0' + hour; |
| 202 | + } |
| 203 | + var min = String(localTime%60); |
| 204 | + if ( min.length < 2 ) { |
| 205 | + min = '0' + min; |
| 206 | + } |
| 207 | + changeText( localtimeHolder, hour + ':' + min ); |
| 208 | + |
| 209 | + // If the user selected from the drop-down, fill the offset field. |
| 210 | + if ( selector.value != 'other' ) { |
| 211 | + hour = String( Math.abs( Math.floor( minDiff / 60 ) ) ); |
| 212 | + if ( hour.length < 2 ) { |
| 213 | + hour = '0' + hour; |
| 214 | + } |
| 215 | + if ( minDiff < 0 ) { |
| 216 | + hour = '-' + hour; |
| 217 | + } |
| 218 | + min = String(minDiff%60); |
| 219 | + if ( min.length < 2 ) { |
| 220 | + min = '0' + min; |
| 221 | + } |
| 222 | + textbox.value = hour + ':' + min; |
| 223 | + } |
| 224 | + } |
| 225 | +} ); |
| 226 | + |
| 227 | +/* Initialization */ |
| 228 | + |
| 229 | +$( document ).ready( function() { |
| 230 | + mw.legacy.timezoneSetup(); |
| 231 | + mw.legacy.tabbedprefs(); |
| 232 | +} ); |
| 233 | + |
| 234 | +} )( jQuery, MediaWiki ); |
\ No newline at end of file |
Property changes on: branches/resourceloader/phase3/resources/mediawiki/legacy/mediawiki.legacy.prefs.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 235 | + native |
Index: branches/resourceloader/phase3/resources/mediawiki/legacy/mediawiki.legacy.metadata.js |
— | — | @@ -0,0 +1,53 @@ |
| 2 | +/* |
| 3 | + * Legacy emulation for the now depricated skins/common/metadata.js |
| 4 | + */ |
| 5 | + |
| 6 | +( function( $, mw ) { |
| 7 | + |
| 8 | +/* Extension */ |
| 9 | + |
| 10 | +$.extend( true, mw.legacy, { |
| 11 | + /** |
| 12 | + * Exif metadata display for MediaWiki file uploads |
| 13 | + * |
| 14 | + * Add an expand/collapse link and collapse by default if set to (with JS disabled, user will see all items) |
| 15 | + * |
| 16 | + * Example: |
| 17 | + * attachMetadataToggle( 'mw_metadata', 'More...', 'Fewer...' ); |
| 18 | + */ |
| 19 | + 'attachMetadataToggle': function( tableId, showText, hideText ) { |
| 20 | + if ( document.createTextNode ) { |
| 21 | + var box = document.getElementById( tableId ); |
| 22 | + if ( !box ) { |
| 23 | + return false; |
| 24 | + } |
| 25 | + var tbody = box.getElementsByTagName('tbody')[0]; |
| 26 | + var row = document.createElement( 'tr' ); |
| 27 | + var col = document.createElement( 'td' ); |
| 28 | + col.colSpan = 2; |
| 29 | + var link = document.createElement( 'a' ); |
| 30 | + link.href = '#'; |
| 31 | + link.onclick = function() { |
| 32 | + if ( box.className == 'mw_metadata collapsed' ) { |
| 33 | + changeText( link, hideText ); |
| 34 | + box.className = 'mw_metadata expanded'; |
| 35 | + } else { |
| 36 | + changeText( link, showText ); |
| 37 | + box.className = 'mw_metadata collapsed'; |
| 38 | + } |
| 39 | + return false; |
| 40 | + }; |
| 41 | + var text = document.createTextNode( hideText ); |
| 42 | + link.appendChild( text ); |
| 43 | + col.appendChild( link ); |
| 44 | + row.appendChild( col ); |
| 45 | + tbody.appendChild( row ); |
| 46 | + // And collapse! |
| 47 | + link.onclick(); |
| 48 | + return true; |
| 49 | + } |
| 50 | + return false; |
| 51 | + } |
| 52 | +} ); |
| 53 | + |
| 54 | +} )( jQuery, MediaWiki ); |
\ No newline at end of file |
Property changes on: branches/resourceloader/phase3/resources/mediawiki/legacy/mediawiki.legacy.metadata.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 55 | + native |
Index: branches/resourceloader/phase3/resources/mediawiki/legacy/mediawiki.legacy.protect.js |
— | — | @@ -0,0 +1,346 @@ |
| 2 | +/* |
| 3 | + * Legacy emulation for the now depricated skins/common/protect.js |
| 4 | + */ |
| 5 | + |
| 6 | +( function( $, mw ) { |
| 7 | + |
| 8 | +/* Extension */ |
| 9 | + |
| 10 | +$.extend( true, mw.legacy, { |
| 11 | + |
| 12 | + /* Global Variables */ |
| 13 | + |
| 14 | + 'ProtectionForm': { |
| 15 | + |
| 16 | + /* Global Variables */ |
| 17 | + |
| 18 | + 'existingMatch': false, |
| 19 | + |
| 20 | + /* Functions */ |
| 21 | + |
| 22 | + /** |
| 23 | + * Set up the protection chaining interface (i.e. "unlock move permissions" checkbox) |
| 24 | + * on the protection form |
| 25 | + * |
| 26 | + * @param Object opts : parameters with members: |
| 27 | + * tableId Identifier of the table containing UI bits |
| 28 | + * labelText Text to use for the checkbox label |
| 29 | + * numTypes The number of protection types |
| 30 | + * existingMatch True if all the existing expiry times match |
| 31 | + */ |
| 32 | + 'init': function( opts ) { |
| 33 | + if( !( document.createTextNode && document.getElementById && document.getElementsByTagName ) ) |
| 34 | + return false; |
| 35 | + var box = document.getElementById( opts.tableId ); |
| 36 | + if( !box ) |
| 37 | + return false; |
| 38 | + var boxbody = box.getElementsByTagName('tbody')[0] |
| 39 | + var row = document.createElement( 'tr' ); |
| 40 | + boxbody.insertBefore( row, boxbody.firstChild.nextSibling ); |
| 41 | + this.existingMatch = opts.existingMatch; |
| 42 | + var cell = document.createElement( 'td' ); |
| 43 | + row.appendChild( cell ); |
| 44 | + // If there is only one protection type, there is nothing to chain |
| 45 | + if( opts.numTypes > 1 ) { |
| 46 | + var check = document.createElement( 'input' ); |
| 47 | + check.id = 'mwProtectUnchained'; |
| 48 | + check.type = 'checkbox'; |
| 49 | + cell.appendChild( check ); |
| 50 | + addClickHandler( check, function() { ProtectionForm.onChainClick(); } ); |
| 51 | + |
| 52 | + cell.appendChild( document.createTextNode( ' ' ) ); |
| 53 | + var label = document.createElement( 'label' ); |
| 54 | + label.htmlFor = 'mwProtectUnchained'; |
| 55 | + label.appendChild( document.createTextNode( opts.labelText ) ); |
| 56 | + cell.appendChild( label ); |
| 57 | + |
| 58 | + check.checked = !this.areAllTypesMatching(); |
| 59 | + this.enableUnchainedInputs( check.checked ); |
| 60 | + } |
| 61 | + this.updateCascadeCheckbox(); |
| 62 | + return true; |
| 63 | + }, |
| 64 | + |
| 65 | + /** |
| 66 | + * Sets the disabled attribute on the cascade checkbox depending on the current selected levels |
| 67 | + */ |
| 68 | + 'updateCascadeCheckbox': function() { |
| 69 | + // For non-existent titles, there is no cascade option |
| 70 | + if( !document.getElementById( 'mwProtect-cascade' ) ) { |
| 71 | + return; |
| 72 | + } |
| 73 | + var lists = this.getLevelSelectors(); |
| 74 | + for( var i = 0; i < lists.length; i++ ) { |
| 75 | + if( lists[i].selectedIndex > -1 ) { |
| 76 | + var items = lists[i].getElementsByTagName( 'option' ); |
| 77 | + var selected = items[ lists[i].selectedIndex ].value; |
| 78 | + if( !this.isCascadeableLevel(selected) ) { |
| 79 | + document.getElementById( 'mwProtect-cascade' ).checked = false; |
| 80 | + document.getElementById( 'mwProtect-cascade' ).disabled = true; |
| 81 | + return; |
| 82 | + } |
| 83 | + } |
| 84 | + } |
| 85 | + document.getElementById( 'mwProtect-cascade' ).disabled = false; |
| 86 | + }, |
| 87 | + /** |
| 88 | + * Is this protection level cascadeable? |
| 89 | + * @param String level |
| 90 | + * |
| 91 | + * @return boolean |
| 92 | + * |
| 93 | + */ |
| 94 | + 'isCascadeableLevel': function( level ) { |
| 95 | + for (var k = 0; k < wgCascadeableLevels.length; k++) { |
| 96 | + if ( wgCascadeableLevels[k] == level ) { |
| 97 | + return true; |
| 98 | + } |
| 99 | + } |
| 100 | + return false; |
| 101 | + }, |
| 102 | + /** |
| 103 | + * When protection levels are locked together, update the rest |
| 104 | + * when one action's level changes |
| 105 | + * |
| 106 | + * @param Element source Level selector that changed |
| 107 | + */ |
| 108 | + 'updateLevels': function(source) { |
| 109 | + if( !this.isUnchained() ) |
| 110 | + this.setAllSelectors( source.selectedIndex ); |
| 111 | + this.updateCascadeCheckbox(); |
| 112 | + }, |
| 113 | + /** |
| 114 | + * When protection levels are locked together, update the |
| 115 | + * expiries when one changes |
| 116 | + * |
| 117 | + * @param Element source expiry input that changed |
| 118 | + */ |
| 119 | + 'updateExpiry': function(source) { |
| 120 | + if( !this.isUnchained() ) { |
| 121 | + var expiry = source.value; |
| 122 | + this.forEachExpiryInput(function(element) { |
| 123 | + element.value = expiry; |
| 124 | + }); |
| 125 | + } |
| 126 | + var listId = source.id.replace( /^mwProtect-(\w+)-expires$/, 'mwProtectExpirySelection-$1' ); |
| 127 | + var list = document.getElementById( listId ); |
| 128 | + if (list && list.value != 'othertime' ) { |
| 129 | + if ( this.isUnchained() ) { |
| 130 | + list.value = 'othertime'; |
| 131 | + } else { |
| 132 | + this.forEachExpirySelector(function(element) { |
| 133 | + element.value = 'othertime'; |
| 134 | + }); |
| 135 | + } |
| 136 | + } |
| 137 | + }, |
| 138 | + /** |
| 139 | + * When protection levels are locked together, update the |
| 140 | + * expiry lists when one changes and clear the custom inputs |
| 141 | + * |
| 142 | + * @param Element source expiry selector that changed |
| 143 | + */ |
| 144 | + 'updateExpiryList': function(source) { |
| 145 | + if( !this.isUnchained() ) { |
| 146 | + var expiry = source.value; |
| 147 | + this.forEachExpirySelector(function(element) { |
| 148 | + element.value = expiry; |
| 149 | + }); |
| 150 | + this.forEachExpiryInput(function(element) { |
| 151 | + element.value = ''; |
| 152 | + }); |
| 153 | + } |
| 154 | + }, |
| 155 | + /** |
| 156 | + * Update chain status and enable/disable various bits of the UI |
| 157 | + * when the user changes the "unlock move permissions" checkbox |
| 158 | + */ |
| 159 | + 'onChainClick': function() { |
| 160 | + if( this.isUnchained() ) { |
| 161 | + this.enableUnchainedInputs( true ); |
| 162 | + } else { |
| 163 | + this.setAllSelectors( this.getMaxLevel() ); |
| 164 | + this.enableUnchainedInputs( false ); |
| 165 | + } |
| 166 | + this.updateCascadeCheckbox(); |
| 167 | + }, |
| 168 | + /** |
| 169 | + * Returns true if the named attribute in all objects in the given array are matching |
| 170 | + */ |
| 171 | + 'matchAttribute' : function( objects, attrName ) { |
| 172 | + var value = null; |
| 173 | + |
| 174 | + // Check levels |
| 175 | + for ( var i = 0; i < objects.length; i++ ) { |
| 176 | + var element = objects[i]; |
| 177 | + if ( value == null ) { |
| 178 | + value = element[attrName]; |
| 179 | + } else { |
| 180 | + if ( value != element[attrName] ) { |
| 181 | + return false; |
| 182 | + } |
| 183 | + } |
| 184 | + } |
| 185 | + return true; |
| 186 | + }, |
| 187 | + /** |
| 188 | + * Are all actions protected at the same level, with the same expiry time? |
| 189 | + * |
| 190 | + * @return boolean |
| 191 | + */ |
| 192 | + 'areAllTypesMatching': function() { |
| 193 | + return this.existingMatch |
| 194 | + && this.matchAttribute( this.getLevelSelectors(), 'selectedIndex' ) |
| 195 | + && this.matchAttribute( this.getExpirySelectors(), 'selectedIndex' ) |
| 196 | + && this.matchAttribute( this.getExpiryInputs(), 'value' ); |
| 197 | + }, |
| 198 | + /** |
| 199 | + * Is protection chaining off? |
| 200 | + * |
| 201 | + * @return bool |
| 202 | + */ |
| 203 | + 'isUnchained': function() { |
| 204 | + var element = document.getElementById( 'mwProtectUnchained' ); |
| 205 | + return element |
| 206 | + ? element.checked |
| 207 | + : true; // No control, so we need to let the user set both levels |
| 208 | + }, |
| 209 | + /** |
| 210 | + * Find the highest protection level in any selector |
| 211 | + */ |
| 212 | + 'getMaxLevel': function() { |
| 213 | + var maxIndex = -1; |
| 214 | + this.forEachLevelSelector(function(element) { |
| 215 | + if (element.selectedIndex > maxIndex) { |
| 216 | + maxIndex = element.selectedIndex; |
| 217 | + } |
| 218 | + }); |
| 219 | + return maxIndex; |
| 220 | + }, |
| 221 | + /** |
| 222 | + * Protect all actions at the specified level |
| 223 | + * |
| 224 | + * @param int index Protection level |
| 225 | + */ |
| 226 | + 'setAllSelectors': function(index) { |
| 227 | + this.forEachLevelSelector(function(element) { |
| 228 | + if (element.selectedIndex != index) { |
| 229 | + element.selectedIndex = index; |
| 230 | + } |
| 231 | + }); |
| 232 | + }, |
| 233 | + /** |
| 234 | + * Apply a callback to each protection selector |
| 235 | + * |
| 236 | + * @param callable func Callback function |
| 237 | + */ |
| 238 | + 'forEachLevelSelector': function(func) { |
| 239 | + var selectors = this.getLevelSelectors(); |
| 240 | + for (var i = 0; i < selectors.length; i++) { |
| 241 | + func(selectors[i]); |
| 242 | + } |
| 243 | + }, |
| 244 | + /** |
| 245 | + * Get a list of all protection selectors on the page |
| 246 | + * |
| 247 | + * @return Array |
| 248 | + */ |
| 249 | + 'getLevelSelectors': function() { |
| 250 | + var all = document.getElementsByTagName('select'); |
| 251 | + var ours = new Array(); |
| 252 | + for (var i = 0; i < all.length; i++) { |
| 253 | + var element = all[i]; |
| 254 | + if (element.id.match(/^mwProtect-level-/)) { |
| 255 | + ours[ours.length] = element; |
| 256 | + } |
| 257 | + } |
| 258 | + return ours; |
| 259 | + }, |
| 260 | + /** |
| 261 | + * Apply a callback to each expiry input |
| 262 | + * |
| 263 | + * @param callable func Callback function |
| 264 | + */ |
| 265 | + 'forEachExpiryInput': function(func) { |
| 266 | + var inputs = this.getExpiryInputs(); |
| 267 | + for (var i = 0; i < inputs.length; i++) { |
| 268 | + func(inputs[i]); |
| 269 | + } |
| 270 | + }, |
| 271 | + /** |
| 272 | + * Get a list of all expiry inputs on the page |
| 273 | + * |
| 274 | + * @return Array |
| 275 | + */ |
| 276 | + 'getExpiryInputs': function() { |
| 277 | + var all = document.getElementsByTagName('input'); |
| 278 | + var ours = new Array(); |
| 279 | + for (var i = 0; i < all.length; i++) { |
| 280 | + var element = all[i]; |
| 281 | + if (element.name.match(/^mwProtect-expiry-/)) { |
| 282 | + ours[ours.length] = element; |
| 283 | + } |
| 284 | + } |
| 285 | + return ours; |
| 286 | + }, |
| 287 | + /** |
| 288 | + * Apply a callback to each expiry selector list |
| 289 | + * @param callable func Callback function |
| 290 | + */ |
| 291 | + 'forEachExpirySelector': function(func) { |
| 292 | + var inputs = this.getExpirySelectors(); |
| 293 | + for (var i = 0; i < inputs.length; i++) { |
| 294 | + func(inputs[i]); |
| 295 | + } |
| 296 | + }, |
| 297 | + /** |
| 298 | + * Get a list of all expiry selector lists on the page |
| 299 | + * |
| 300 | + * @return Array |
| 301 | + */ |
| 302 | + 'getExpirySelectors': function() { |
| 303 | + var all = document.getElementsByTagName('select'); |
| 304 | + var ours = new Array(); |
| 305 | + for (var i = 0; i < all.length; i++) { |
| 306 | + var element = all[i]; |
| 307 | + if (element.id.match(/^mwProtectExpirySelection-/)) { |
| 308 | + ours[ours.length] = element; |
| 309 | + } |
| 310 | + } |
| 311 | + return ours; |
| 312 | + }, |
| 313 | + /** |
| 314 | + * Enable/disable protection selectors and expiry inputs |
| 315 | + * |
| 316 | + * @param boolean val Enable? |
| 317 | + */ |
| 318 | + 'enableUnchainedInputs': function(val) { |
| 319 | + var first = true; |
| 320 | + this.forEachLevelSelector(function(element) { |
| 321 | + if (first) { |
| 322 | + first = false; |
| 323 | + } else { |
| 324 | + element.disabled = !val; |
| 325 | + } |
| 326 | + }); |
| 327 | + first = true; |
| 328 | + this.forEachExpiryInput(function(element) { |
| 329 | + if (first) { |
| 330 | + first = false; |
| 331 | + } else { |
| 332 | + element.disabled = !val; |
| 333 | + } |
| 334 | + }); |
| 335 | + first = true; |
| 336 | + this.forEachExpirySelector(function(element) { |
| 337 | + if (first) { |
| 338 | + first = false; |
| 339 | + } else { |
| 340 | + element.disabled = !val; |
| 341 | + } |
| 342 | + }); |
| 343 | + } |
| 344 | + } |
| 345 | +} ); |
| 346 | + |
| 347 | +} )( jQuery, MediaWiki ); |
\ No newline at end of file |
Property changes on: branches/resourceloader/phase3/resources/mediawiki/legacy/mediawiki.legacy.protect.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 348 | + native |
Index: branches/resourceloader/phase3/resources/mediawiki/legacy/mediawiki.legacy.preview.js |
— | — | @@ -0,0 +1,121 @@ |
| 2 | +/* |
| 3 | + * Legacy emulation for the now depricated skins/common/preview.js |
| 4 | + * |
| 5 | + * Inline ("Live") preview |
| 6 | + */ |
| 7 | + |
| 8 | +( function( $, mw ) { |
| 9 | + |
| 10 | +/* Extension */ |
| 11 | + |
| 12 | +$.extend( true, mw.legacy, { |
| 13 | + |
| 14 | + /* Functions */ |
| 15 | + |
| 16 | + 'doLivePreview': function( e ) { |
| 17 | + e.preventDefault(); |
| 18 | + $j( mw ).trigger( 'LivePreviewPrepare' ); |
| 19 | + var postData = $j('#editform').formToArray(); |
| 20 | + postData.push( { 'name' : 'wpPreview', 'value' : '1' } ); |
| 21 | + // Hide active diff, used templates, old preview if shown |
| 22 | + var copyElements = ['#wikiPreview', '.templatesUsed', '.hiddencats', '#catlinks']; |
| 23 | + var copySelector = copyElements.join(','); |
| 24 | + $j.each( copyElements, function(k,v) { $j(v).fadeOut('fast'); } ); |
| 25 | + // Display a loading graphic |
| 26 | + var loadSpinner = $j('<div class="mw-ajax-loader"/>'); |
| 27 | + $j('#wikiPreview').before( loadSpinner ); |
| 28 | + var page = $j('<div/>'); |
| 29 | + var target = $j('#editform').attr('action'); |
| 30 | + if ( !target ) { |
| 31 | + target = window.location.href; |
| 32 | + } |
| 33 | + page.load( target + ' ' + copySelector, postData, function() { |
| 34 | + for ( var i=0; i<copyElements.length; ++i) { |
| 35 | + // For all the specified elements, find the elements in the loaded page |
| 36 | + // and the real page, empty the element in the real page, and fill it |
| 37 | + // with the content of the loaded page |
| 38 | + var copyContent = page.find( copyElements[i] ).contents(); |
| 39 | + $j(copyElements[i]).empty().append( copyContent ); |
| 40 | + var newClasses = page.find( copyElements[i] ).attr('class'); |
| 41 | + $j(copyElements[i]).attr( 'class', newClasses ); |
| 42 | + } |
| 43 | + $j.each( copyElements, function(k,v) { |
| 44 | + // Don't belligerently show elements that are supposed to be hidden |
| 45 | + $j(v).fadeIn( 'fast', function() { $j(this).css('display', ''); } ); |
| 46 | + } ); |
| 47 | + loadSpinner.remove(); |
| 48 | + $j( mw ).trigger( 'LivePreviewDone', [copyElements] ); |
| 49 | + } ); |
| 50 | + } |
| 51 | +} ); |
| 52 | + |
| 53 | +/* Initialization */ |
| 54 | + |
| 55 | +$( document ).ready( function() { |
| 56 | + // Shamelessly stolen from the jQuery form plugin, which is licensed under the GPL. |
| 57 | + // http://jquery.malsup.com/form/#download |
| 58 | + $j.fn.formToArray = function() { |
| 59 | + var a = []; |
| 60 | + if (this.length == 0) return a; |
| 61 | + var form = this[0]; |
| 62 | + var els = form.elements; |
| 63 | + if (!els) return a; |
| 64 | + for(var i=0, max=els.length; i < max; i++) { |
| 65 | + var el = els[i]; |
| 66 | + var n = el.name; |
| 67 | + if (!n) continue; |
| 68 | + var v = $j.fieldValue(el, true); |
| 69 | + if (v && v.constructor == Array) { |
| 70 | + for(var j=0, jmax=v.length; j < jmax; j++) |
| 71 | + a.push({name: n, value: v[j]}); |
| 72 | + } |
| 73 | + else if (v !== null && typeof v != 'undefined') |
| 74 | + a.push({name: n, value: v}); |
| 75 | + } |
| 76 | + if (form.clk) { |
| 77 | + // input type=='image' are not found in elements array! handle it here |
| 78 | + var $input = $(form.clk), input = $input[0], n = input.name; |
| 79 | + if (n && !input.disabled && input.type == 'image') { |
| 80 | + a.push({name: n, value: $input.val()}); |
| 81 | + a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y}); |
| 82 | + } |
| 83 | + } |
| 84 | + return a; |
| 85 | + } |
| 86 | + /** |
| 87 | + * Returns the value of the field element. |
| 88 | + */ |
| 89 | + $j.fieldValue = function(el, successful) { |
| 90 | + var n = el.name, t = el.type, tag = el.tagName.toLowerCase(); |
| 91 | + if (typeof successful == 'undefined') successful = true; |
| 92 | + if (successful && (!n || el.disabled || t == 'reset' || t == 'button' || |
| 93 | + (t == 'checkbox' || t == 'radio') && !el.checked || |
| 94 | + (t == 'submit' || t == 'image') && el.form && el.form.clk != el || |
| 95 | + tag == 'select' && el.selectedIndex == -1)) |
| 96 | + return null; |
| 97 | + if (tag == 'select') { |
| 98 | + var index = el.selectedIndex; |
| 99 | + if (index < 0) return null; |
| 100 | + var a = [], ops = el.options; |
| 101 | + var one = (t == 'select-one'); |
| 102 | + var max = (one ? index+1 : ops.length); |
| 103 | + for(var i=(one ? index : 0); i < max; i++) { |
| 104 | + var op = ops[i]; |
| 105 | + if (op.selected) { |
| 106 | + var v = op.value; |
| 107 | + if (!v) // extra pain for IE... |
| 108 | + v = (op.attributes && op.attributes['value'] && |
| 109 | + !(op.attributes['value'].specified)) |
| 110 | + ? op.text : op.value; |
| 111 | + if (one) return v; |
| 112 | + a.push(v); |
| 113 | + } |
| 114 | + } |
| 115 | + return a; |
| 116 | + } |
| 117 | + return el.value; |
| 118 | + }; |
| 119 | + $j('#wpPreview').click( doLivePreview ); |
| 120 | +} ); |
| 121 | + |
| 122 | +} )( jQuery, MediaWiki ); |
\ No newline at end of file |
Property changes on: branches/resourceloader/phase3/resources/mediawiki/legacy/mediawiki.legacy.preview.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 123 | + native |
Index: branches/resourceloader/phase3/resources/mediawiki/legacy/mediawiki.legacy.ajax.js |
— | — | @@ -0,0 +1,176 @@ |
| 2 | +/* |
| 3 | + * Legacy emulation for the now depricated skins/common/ajax.js |
| 4 | + * |
| 5 | + * Original licensing information: |
| 6 | + * Remote Scripting Library |
| 7 | + * (c) Copyright 2005 ModernMethod, Inc. |
| 8 | + */ |
| 9 | + |
| 10 | +( function( $, mw ) { |
| 11 | + |
| 12 | +/* Extension */ |
| 13 | + |
| 14 | +$.extend( true, mw.legacy, { |
| 15 | + |
| 16 | + /* Global Variables */ |
| 17 | + |
| 18 | + 'sajax_debug_mode': false, |
| 19 | + 'sajax_debug_mode': 'GET', |
| 20 | + |
| 21 | + /* Functions */ |
| 22 | + |
| 23 | + /** |
| 24 | + * If sajax_debug_mode is true, this function outputs given the message into the element with id = sajax_debug; if no |
| 25 | + * such element exists in the document, it is injected |
| 26 | + * |
| 27 | + * @param string text debug message to append to log |
| 28 | + * @return boolean true when in debug mode, false when not |
| 29 | + */ |
| 30 | + 'sajax_debug': function( text ) { |
| 31 | + if ( mw.legacy.sajax_debug_mode ) { |
| 32 | + var $e = $( '#sajax_debug' ); |
| 33 | + if ( !$e.length ) { |
| 34 | + $e = $( '<p class="sajax_debug" id="sajax_debug"></p>' ).prependTo( $( 'body' ) ); |
| 35 | + } |
| 36 | + $e.append( $( '<div></div>' ).text( text ) ); |
| 37 | + return true; |
| 38 | + } |
| 39 | + return false; |
| 40 | + }, |
| 41 | + /** |
| 42 | + * Gets an XMLHttpRequest or equivilant ActiveXObject |
| 43 | + * |
| 44 | + * @reuturn mixed request object on success, boolean false on failure |
| 45 | + */ |
| 46 | + 'sajax_init_object': function() { |
| 47 | + mw.legacy.sajax_debug( 'sajax_init_object() called..' ); |
| 48 | + var request = false; |
| 49 | + try { |
| 50 | + // Try the 'new' style before ActiveX so we don't unnecessarily trigger warnings in IE 7 when the user's |
| 51 | + // security settings are set to prompt about ActiveX usage |
| 52 | + request = new XMLHttpRequest(); |
| 53 | + } catch ( e ) { |
| 54 | + try { |
| 55 | + request = new ActiveXObject( 'Msxml2.XMLHTTP' ); |
| 56 | + } catch ( e ) { |
| 57 | + try { |
| 58 | + request = new ActiveXObject( 'Microsoft.XMLHTTP' ); |
| 59 | + } catch ( oc ) { |
| 60 | + request = null; |
| 61 | + } |
| 62 | + } |
| 63 | + } |
| 64 | + if ( !request ) { |
| 65 | + mw.legacy.sajax_debug( 'Could not create connection object.' ); |
| 66 | + } |
| 67 | + return request; |
| 68 | + }, |
| 69 | + /** |
| 70 | + * Performs an ajax call to mediawiki. Calls are handeled by AjaxDispatcher.php |
| 71 | + * |
| 72 | + * @param string method name of the function to call. Must be registered in $wgAjaxExportList |
| 73 | + * @param array arguments arguments to that function |
| 74 | + * @param mixed target the target that will handle the result of the call. If this is a function, if will be called |
| 75 | + * with the XMLHttpRequest as a parameter; if it's an input element, its value will be set to the resultText; if |
| 76 | + * it's another type of element, its innerHTML will be set to the resultText. |
| 77 | + * |
| 78 | + * @example |
| 79 | + * // This will call the doFoo function via MediaWiki's AjaxDispatcher, with (1, 2, 3) as the parameter list, |
| 80 | + * // and will show the result in the element with id = showFoo |
| 81 | + * sajax_do_call( 'doFoo', [1, 2, 3], document.getElementById( 'showFoo' ) ); |
| 82 | + */ |
| 83 | + 'sajax_do_call': function( method, arguments, target ) { |
| 84 | + var post_data; |
| 85 | + var uri = mw.legacy.wgServer + |
| 86 | + ( ( mw.legacy.wgScript == null ) ? ( mw.legacy.wgScriptPath + '/index.php' ) : mw.legacy.wgScript ) + |
| 87 | + '?action=ajax'; |
| 88 | + if ( mw.legacy.sajax_request_type == 'GET' ) { |
| 89 | + if ( uri.indexOf( '?' ) == -1 ) { |
| 90 | + uri = uri + '?rs=' + encodeURIComponent( method ); |
| 91 | + } else { |
| 92 | + uri = uri + '&rs=' + encodeURIComponent( method ); |
| 93 | + } |
| 94 | + for ( var i = 0; i < arguments.length; i++ ) { |
| 95 | + uri = uri + '&rsargs[]=' + encodeURIComponent( arguments[i] ); |
| 96 | + } |
| 97 | + post_data = null; |
| 98 | + } else { |
| 99 | + post_data = 'rs=' + encodeURIComponent( method ); |
| 100 | + for ( var i = 0; i < arguments.length; i++ ) { |
| 101 | + post_data = post_data + '&rsargs[]=' + encodeURIComponent( arguments[i] ); |
| 102 | + } |
| 103 | + } |
| 104 | + var request = mw.legacy.sajax_init_object(); |
| 105 | + if ( !request ) { |
| 106 | + alert( 'AJAX not supported' ); |
| 107 | + return false; |
| 108 | + } |
| 109 | + try { |
| 110 | + request.open( mw.legacy.sajax_request_type, uri, true ); |
| 111 | + } catch ( e ) { |
| 112 | + if ( window.location.hostname == 'localhost' ) { |
| 113 | + alert( |
| 114 | + 'Your browser blocks XMLHttpRequest to \'localhost\', ' + |
| 115 | + 'try using a real hostname for development/testing.' |
| 116 | + ); |
| 117 | + } |
| 118 | + throw e; |
| 119 | + } |
| 120 | + if ( mw.legacy.sajax_request_type == 'POST' ) { |
| 121 | + request.setRequestHeader( 'Method', 'POST ' + uri + ' HTTP/1.1' ); |
| 122 | + request.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded' ); |
| 123 | + } |
| 124 | + request.setRequestHeader( 'Pragma', 'cache=yes' ); |
| 125 | + request.setRequestHeader( 'Cache-Control', 'no-transform' ); |
| 126 | + request.onreadystatechange = function() { |
| 127 | + if ( request.readyState != 4 ) { |
| 128 | + return; |
| 129 | + } |
| 130 | + mw.legacy.sajax_debug( |
| 131 | + 'received (' + request.status + ' ' + request.statusText + ') ' + request.responseText |
| 132 | + ); |
| 133 | + if ( typeof( target ) == 'function' ) { |
| 134 | + target( request ); |
| 135 | + } else if ( typeof( target ) == 'object' ) { |
| 136 | + $target = $( target ); |
| 137 | + if ( $target.is( 'input' ) ) { |
| 138 | + if ( request.status == 200 ) { |
| 139 | + $target.val(); |
| 140 | + } |
| 141 | + } else { |
| 142 | + if ( request.status == 200 ) { |
| 143 | + $target.html( request.responseText ); |
| 144 | + } else { |
| 145 | + $target.html( |
| 146 | + '<div class="error">' + |
| 147 | + 'Error: ' + request.status + ' ' + request.statusText + |
| 148 | + ' (' + request.responseText + ')' + |
| 149 | + '</div>' |
| 150 | + ); |
| 151 | + } |
| 152 | + } |
| 153 | + } else { |
| 154 | + alert( 'Bad target for sajax_do_call: not a function or object: ' + target ); |
| 155 | + } |
| 156 | + return; |
| 157 | + } |
| 158 | + mw.legacy.sajax_debug( method + ' uri = ' + uri + ' / post = ' + post_data ); |
| 159 | + request.send( post_data ); |
| 160 | + mw.legacy.sajax_debug( method + ' waiting..' ); |
| 161 | + delete x; |
| 162 | + return true; |
| 163 | + }, |
| 164 | + /** |
| 165 | + * Ajax compatibility test |
| 166 | + * |
| 167 | + * @return boolean whether the browser supports XMLHttpRequest |
| 168 | + */ |
| 169 | + 'wfSupportsAjax': function() { |
| 170 | + var request = mw.legacy.sajax_init_object(); |
| 171 | + var result = request ? true : false; |
| 172 | + delete request; |
| 173 | + return result; |
| 174 | + } |
| 175 | +} ); |
| 176 | + |
| 177 | +} )( jQuery, MediaWiki ); |
\ No newline at end of file |
Property changes on: branches/resourceloader/phase3/resources/mediawiki/legacy/mediawiki.legacy.ajax.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 178 | + native |
Index: branches/resourceloader/phase3/resources/mediawiki/legacy/mediawiki.legacy.upload.js |
— | — | @@ -0,0 +1,333 @@ |
| 2 | +/* |
| 3 | + * Legacy emulation for the now depricated skins/common/upload.js |
| 4 | + */ |
| 5 | + |
| 6 | +( function( $, mw ) { |
| 7 | + |
| 8 | +/* Extension */ |
| 9 | + |
| 10 | +$.extend( true, mw.legacy, { |
| 11 | + |
| 12 | + /* Global Variables */ |
| 13 | + |
| 14 | + 'wgUploadWarningObj': { |
| 15 | + |
| 16 | + /* Global Variables */ |
| 17 | + |
| 18 | + 'responseCache' : { '' : ' ' }, |
| 19 | + 'nameToCheck' : '', |
| 20 | + 'typing': false, |
| 21 | + 'delay': 500, // ms |
| 22 | + 'timeoutID': false, |
| 23 | + |
| 24 | + /* Functions */ |
| 25 | + |
| 26 | + 'keypress': function () { |
| 27 | + if ( !wgAjaxUploadDestCheck || !sajax_init_object() ) return; |
| 28 | + |
| 29 | + // Find file to upload |
| 30 | + var destFile = document.getElementById( 'wpDestFile' ); |
| 31 | + var warningElt = document.getElementById( 'wpDestFile-warning' ); |
| 32 | + if ( !destFile || !warningElt ) return ; |
| 33 | + this.nameToCheck = destFile.value ; |
| 34 | + // Clear timer |
| 35 | + if ( this.timeoutID ) { |
| 36 | + window.clearTimeout( this.timeoutID ); |
| 37 | + } |
| 38 | + // Check response cache |
| 39 | + for ( cached in this.responseCache ) { |
| 40 | + if ( this.nameToCheck == cached ) { |
| 41 | + this.setWarning( this.responseCache[this.nameToCheck] ); |
| 42 | + return; |
| 43 | + } |
| 44 | + } |
| 45 | + this.timeoutID = window.setTimeout( 'wgUploadWarningObj.timeout()', this.delay ); |
| 46 | + }, |
| 47 | + 'checkNow': function ( fname ) { |
| 48 | + if ( !wgAjaxUploadDestCheck || !sajax_init_object() ) return; |
| 49 | + if ( this.timeoutID ) { |
| 50 | + window.clearTimeout( this.timeoutID ); |
| 51 | + } |
| 52 | + this.nameToCheck = fname; |
| 53 | + this.timeout(); |
| 54 | + }, |
| 55 | + 'timeout' : function() { |
| 56 | + if ( !wgAjaxUploadDestCheck || !sajax_init_object() ) return; |
| 57 | + injectSpinner( document.getElementById( 'wpDestFile' ), 'destcheck' ); |
| 58 | + |
| 59 | + // Get variables into local scope so that they will be preserved for the |
| 60 | + // anonymous callback. fileName is copied so that multiple overlapping |
| 61 | + // ajax requests can be supported. |
| 62 | + var obj = this; |
| 63 | + var fileName = this.nameToCheck; |
| 64 | + sajax_do_call( 'SpecialUpload::ajaxGetExistsWarning', [this.nameToCheck], |
| 65 | + function ( result ) { |
| 66 | + obj.processResult( result, fileName ) |
| 67 | + } |
| 68 | + ); |
| 69 | + }, |
| 70 | + 'processResult' : function ( result, fileName ) { |
| 71 | + removeSpinner( 'destcheck' ); |
| 72 | + this.setWarning( result.responseText ); |
| 73 | + this.responseCache[fileName] = result.responseText; |
| 74 | + }, |
| 75 | + 'setWarning' : function ( warning ) { |
| 76 | + var warningElt = document.getElementById( 'wpDestFile-warning' ); |
| 77 | + var ackElt = document.getElementsByName( 'wpDestFileWarningAck' ); |
| 78 | + |
| 79 | + this.setInnerHTML( warningElt, warning ); |
| 80 | + |
| 81 | + // Set a value in the form indicating that the warning is acknowledged and |
| 82 | + // doesn't need to be redisplayed post-upload |
| 83 | + if ( warning == '' || warning == ' ' ) { |
| 84 | + ackElt[0].value = ''; |
| 85 | + } else { |
| 86 | + ackElt[0].value = '1'; |
| 87 | + } |
| 88 | + }, |
| 89 | + 'setInnerHTML' : function ( element, text ) { |
| 90 | + // Check for no change to avoid flicker in IE 7 |
| 91 | + if ( element.innerHTML != text ) { |
| 92 | + element.innerHTML = text; |
| 93 | + } |
| 94 | + } |
| 95 | + }, |
| 96 | + var wgUploadLicenseObj = { |
| 97 | + |
| 98 | + /* Global Variables */ |
| 99 | + |
| 100 | + 'responseCache' : { '' : '' }, |
| 101 | + |
| 102 | + /* Functions */ |
| 103 | + |
| 104 | + 'fetchPreview': function( license ) { |
| 105 | + if ( !wgAjaxLicensePreview ) return; |
| 106 | + for ( cached in this.responseCache ) { |
| 107 | + if ( cached == license ) { |
| 108 | + this.showPreview( this.responseCache[license] ); |
| 109 | + return; |
| 110 | + } |
| 111 | + } |
| 112 | + injectSpinner( document.getElementById( 'wpLicense' ), 'license' ); |
| 113 | + var title = document.getElementById( 'wpDestFile' ).value; |
| 114 | + if ( !title ) title = 'File:Sample.jpg'; |
| 115 | + var url = wgScriptPath + '/api' + wgScriptExtension |
| 116 | + + '?action=parse&text={{' + encodeURIComponent( license ) + '}}' |
| 117 | + + '&title=' + encodeURIComponent( title ) |
| 118 | + + '&prop=text&pst&format=json'; |
| 119 | + var req = sajax_init_object(); |
| 120 | + req.onreadystatechange = function() { |
| 121 | + if ( req.readyState == 4 && req.status == 200 ) |
| 122 | + wgUploadLicenseObj.processResult( eval( '( ' + req.responseText + ' )' ), license ); |
| 123 | + }; |
| 124 | + req.open( 'GET', url, true ); |
| 125 | + req.send( '' ); |
| 126 | + }, |
| 127 | + 'processResult' : function( result, license ) { |
| 128 | + removeSpinner( 'license' ); |
| 129 | + this.responseCache[license] = result['parse']['text']['*']; |
| 130 | + this.showPreview( this.responseCache[license] ); |
| 131 | + }, |
| 132 | + 'showPreview' : function( preview ) { |
| 133 | + var previewPanel = document.getElementById( 'mw-license-preview' ); |
| 134 | + if( previewPanel.innerHTML != preview ) |
| 135 | + previewPanel.innerHTML = preview; |
| 136 | + } |
| 137 | + }, |
| 138 | + |
| 139 | + /* Functions */ |
| 140 | + |
| 141 | + 'licenseSelectorCheck': function() { |
| 142 | + var selector = document.getElementById( 'wpLicense' ); |
| 143 | + var selection = selector.options[selector.selectedIndex].value; |
| 144 | + if( selector.selectedIndex > 0 ) { |
| 145 | + if( selection == '' ) { |
| 146 | + // Option disabled, but browser is broken and doesn't respect this |
| 147 | + selector.selectedIndex = 0; |
| 148 | + } |
| 149 | + } |
| 150 | + // We might show a preview |
| 151 | + wgUploadLicenseObj.fetchPreview( selection ); |
| 152 | + }, |
| 153 | + 'wgUploadSetup': function() { |
| 154 | + // Disable URL box if the URL copy upload source type is not selected |
| 155 | + var e = document.getElementById( 'wpSourceTypeurl' ); |
| 156 | + if( e ) { |
| 157 | + if( !e.checked ) { |
| 158 | + var ein = document.getElementById( 'wpUploadFileURL' ); |
| 159 | + if( ein ) |
| 160 | + ein.setAttribute( 'disabled', 'disabled' ); |
| 161 | + } |
| 162 | + } |
| 163 | + // For MSIE/Mac: non-breaking spaces cause the <option> not to render. |
| 164 | + // But for some reason, setting the text to itself works |
| 165 | + var selector = document.getElementById( 'wpLicense' ); |
| 166 | + if ( selector ) { |
| 167 | + 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++ ) { |
| 171 | + selector.options[i].text = selector.options[i].text; |
| 172 | + } |
| 173 | + } |
| 174 | + } |
| 175 | + // Toggle source type |
| 176 | + var sourceTypeCheckboxes = document.getElementsByName( 'wpSourceType' ); |
| 177 | + for ( var i = 0; i < sourceTypeCheckboxes.length; i++ ) { |
| 178 | + sourceTypeCheckboxes[i].onchange = toggleUploadInputs; |
| 179 | + } |
| 180 | + // AJAX wpDestFile warnings |
| 181 | + if ( wgAjaxUploadDestCheck ) { |
| 182 | + // Insert an event handler that fetches upload warnings when wpDestFile |
| 183 | + // has been changed |
| 184 | + document.getElementById( 'wpDestFile' ).onchange = function ( e ) { |
| 185 | + wgUploadWarningObj.checkNow( this.value ); |
| 186 | + }; |
| 187 | + // Insert a row where the warnings will be displayed just below the |
| 188 | + // wpDestFile row |
| 189 | + var optionsTable = document.getElementById( 'mw-htmlform-description' ).tBodies[0]; |
| 190 | + var row = optionsTable.insertRow( 1 ); |
| 191 | + var td = document.createElement( 'td' ); |
| 192 | + td.id = 'wpDestFile-warning'; |
| 193 | + td.colSpan = 2; |
| 194 | + row.appendChild( td ); |
| 195 | + } |
| 196 | + if ( wgAjaxLicensePreview ) { |
| 197 | + // License selector check |
| 198 | + document.getElementById( 'wpLicense' ).onchange = licenseSelectorCheck; |
| 199 | + // License selector table row |
| 200 | + var wpLicense = document.getElementById( 'wpLicense' ); |
| 201 | + var wpLicenseRow = wpLicense.parentNode.parentNode; |
| 202 | + var wpLicenseTbody = wpLicenseRow.parentNode; |
| 203 | + var row = document.createElement( 'tr' ); |
| 204 | + var td = document.createElement( 'td' ); |
| 205 | + row.appendChild( td ); |
| 206 | + td = document.createElement( 'td' ); |
| 207 | + td.id = 'mw-license-preview'; |
| 208 | + row.appendChild( td ); |
| 209 | + wpLicenseTbody.insertBefore( row, wpLicenseRow.nextSibling ); |
| 210 | + } |
| 211 | + // fillDestFile setup |
| 212 | + for ( var i = 0; i < wgUploadSourceIds.length; i++ ) |
| 213 | + document.getElementById( wgUploadSourceIds[i] ).onchange = function ( e ) { |
| 214 | + fillDestFilename( this.id ); |
| 215 | + }; |
| 216 | + }, |
| 217 | + /** |
| 218 | + * Iterate over all upload source fields and disable all except the selected one. |
| 219 | + * |
| 220 | + * @param enabledId |
| 221 | + * The id of the selected radio button |
| 222 | + * @return emptiness |
| 223 | + */ |
| 224 | + 'toggleUploadInputs': function() { |
| 225 | + // Iterate over all rows with UploadSourceField |
| 226 | + var rows; |
| 227 | + if ( document.getElementsByClassName ) { |
| 228 | + rows = document.getElementsByClassName( 'mw-htmlform-field-UploadSourceField' ); |
| 229 | + } else { |
| 230 | + // Older browsers don't support getElementsByClassName |
| 231 | + rows = new Array(); |
| 232 | + var allRows = document.getElementsByTagName( 'tr' ); |
| 233 | + for ( var i = 0; i < allRows.length; i++ ) { |
| 234 | + if ( allRows[i].className == 'mw-htmlform-field-UploadSourceField' ) |
| 235 | + rows.push( allRows[i] ); |
| 236 | + } |
| 237 | + } |
| 238 | + for ( var i = 0; i < rows.length; i++ ) { |
| 239 | + var inputs = rows[i].getElementsByTagName( 'input' ); |
| 240 | + // Check if this row is selected |
| 241 | + var isChecked = true; // Default true in case wpSourceType is not found |
| 242 | + for ( var j = 0; j < inputs.length; j++ ) { |
| 243 | + if ( inputs[j].name == 'wpSourceType' ) |
| 244 | + isChecked = inputs[j].checked; |
| 245 | + } |
| 246 | + // Disable all unselected rows |
| 247 | + for ( var j = 0; j < inputs.length; j++ ) { |
| 248 | + if ( inputs[j].type != 'radio' ) |
| 249 | + inputs[j].disabled = !isChecked; |
| 250 | + } |
| 251 | + } |
| 252 | + }, |
| 253 | + 'fillDestFilename': function( id ) { |
| 254 | + if ( !wgUploadAutoFill ) { |
| 255 | + return; |
| 256 | + } |
| 257 | + if ( !document.getElementById ) { |
| 258 | + return; |
| 259 | + } |
| 260 | + // Remove any previously flagged errors |
| 261 | + var e = document.getElementById( 'mw-upload-permitted' ); |
| 262 | + if( e ) e.className = ''; |
| 263 | + var e = document.getElementById( 'mw-upload-prohibited' ); |
| 264 | + if( e ) e.className = ''; |
| 265 | + var path = document.getElementById( id ).value; |
| 266 | + // Find trailing part |
| 267 | + var slash = path.lastIndexOf( '/' ); |
| 268 | + var backslash = path.lastIndexOf( '\\' ); |
| 269 | + var fname; |
| 270 | + if ( slash == -1 && backslash == -1 ) { |
| 271 | + fname = path; |
| 272 | + } else if ( slash > backslash ) { |
| 273 | + fname = path.substring( slash+1, 10000 ); |
| 274 | + } else { |
| 275 | + fname = path.substring( backslash+1, 10000 ); |
| 276 | + } |
| 277 | + // Clear the filename if it does not have a valid extension. |
| 278 | + // URLs are less likely to have a useful extension, so don't include them in the |
| 279 | + // extension check. |
| 280 | + if( wgStrictFileExtensions && wgFileExtensions && id != 'wpUploadFileURL' ) { |
| 281 | + var found = false; |
| 282 | + if ( fname.lastIndexOf( '.' ) != -1 ) { |
| 283 | + var ext = fname.substr( fname.lastIndexOf( '.' ) + 1 ); |
| 284 | + for ( var i = 0; i < wgFileExtensions.length; i++ ) { |
| 285 | + if ( wgFileExtensions[i].toLowerCase() == ext.toLowerCase() ) { |
| 286 | + found = true; |
| 287 | + break; |
| 288 | + } |
| 289 | + } |
| 290 | + } |
| 291 | + if ( !found ) { |
| 292 | + // Not a valid extension |
| 293 | + // Clear the upload and set mw-upload-permitted to error |
| 294 | + document.getElementById( id ).value = ''; |
| 295 | + var e = document.getElementById( 'mw-upload-permitted' ); |
| 296 | + if ( e ) e.className = 'error'; |
| 297 | + var e = document.getElementById( 'mw-upload-prohibited' ); |
| 298 | + if ( e ) e.className = 'error'; |
| 299 | + // Clear wpDestFile as well |
| 300 | + var e = document.getElementById( 'wpDestFile' ) |
| 301 | + if ( e ) e.value = ''; |
| 302 | + |
| 303 | + return false; |
| 304 | + } |
| 305 | + } |
| 306 | + // Capitalise first letter and replace spaces by underscores |
| 307 | + // FIXME: $wgCapitalizedNamespaces |
| 308 | + fname = fname.charAt( 0 ).toUpperCase().concat( fname.substring( 1,10000 ) ).replace( / /g, '_' ); |
| 309 | + // Output result |
| 310 | + var destFile = document.getElementById( 'wpDestFile' ); |
| 311 | + if ( destFile ) { |
| 312 | + destFile.value = fname; |
| 313 | + wgUploadWarningObj.checkNow( fname ) ; |
| 314 | + } |
| 315 | + }, |
| 316 | + 'toggleFilenameFiller': function() { |
| 317 | + if ( !document.getElementById ) return; |
| 318 | + var upfield = document.getElementById( 'wpUploadFile' ); |
| 319 | + var destName = document.getElementById( 'wpDestFile' ).value; |
| 320 | + if ( destName=='' || destName==' ' ) { |
| 321 | + wgUploadAutoFill = true; |
| 322 | + } else { |
| 323 | + wgUploadAutoFill = false; |
| 324 | + } |
| 325 | + } |
| 326 | +} ); |
| 327 | + |
| 328 | +/* Initialization */ |
| 329 | + |
| 330 | +$( document ).ready( function() { |
| 331 | + mw.legacy.wgUploadSetup(); |
| 332 | +} ); |
| 333 | + |
| 334 | +} )( jQuery, MediaWiki ); |
\ No newline at end of file |
Property changes on: branches/resourceloader/phase3/resources/mediawiki/legacy/mediawiki.legacy.upload.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 335 | + native |
Index: branches/resourceloader/phase3/resources/mediawiki/legacy/mediawiki.legacy.wikibits.js |
— | — | @@ -0,0 +1,1090 @@ |
| 2 | +/* |
| 3 | + * Legacy emulation for the now depricated skins/common/wikibits.js |
| 4 | + * |
| 5 | + * MediaWiki JavaScript support functions |
| 6 | + * |
| 7 | + * Global external objects used by this script: ta, stylepath, skin |
| 8 | + */ |
| 9 | + |
| 10 | +( function( $, mw ) { |
| 11 | + |
| 12 | +/* Extension */ |
| 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="↓"/></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="↓"/>'; |
| 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="↑"/>'; |
| 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="↓"/>'; |
| 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, '&' ); |
| 777 | + re = new RegExp( '\'', 'g' ); |
| 778 | + text = text.replace( re, '"' ); |
| 779 | + re = new RegExp( '<', 'g' ); |
| 780 | + text = text.replace( re, '<' ); |
| 781 | + re = new RegExp( '>', 'g' ); |
| 782 | + text = text.replace( re, '>' ); |
| 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 | + } |
| 1060 | +} ); |
| 1061 | + |
| 1062 | +/* Initialization */ |
| 1063 | + |
| 1064 | +$( document ).ready( function() { |
| 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(); |
| 1089 | +} ); |
| 1090 | + |
| 1091 | +} )( jQuery, MediaWiki ); |
\ No newline at end of file |
Property changes on: branches/resourceloader/phase3/resources/mediawiki/legacy/mediawiki.legacy.wikibits.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 1092 | + native |
Index: branches/resourceloader/phase3/resources/mediawiki/legacy/mediawiki.legacy.edit.js |
— | — | @@ -0,0 +1,264 @@ |
| 2 | +/* |
| 3 | + * Legacy emulation for the now depricated skins/common/edit.js |
| 4 | + */ |
| 5 | + |
| 6 | +( function( $, mw ) { |
| 7 | + |
| 8 | +/* Extension */ |
| 9 | + |
| 10 | +$.extend( true, mw.legacy, { |
| 11 | + |
| 12 | + /* Global Variables */ |
| 13 | + |
| 14 | + 'currentFocused': null, |
| 15 | + |
| 16 | + /* Functions */ |
| 17 | + |
| 18 | + /** |
| 19 | + * Generates the actual toolbar buttons with localized text we use it to avoid creating the toolbar |
| 20 | + * where javascript is not enabled |
| 21 | + */ |
| 22 | + 'addButton': function( imageFile, speedTip, tagOpen, tagClose, sampleText, imageId ) { |
| 23 | + // Don't generate buttons for browsers which don't fully support it. |
| 24 | + mw.legacy.mwEditButtons.push( { |
| 25 | + 'imageId': imageId, |
| 26 | + 'imageFile': imageFile, |
| 27 | + 'speedTip': speedTip, |
| 28 | + 'tagOpen': tagOpen, |
| 29 | + 'tagClose': tagClose, |
| 30 | + 'sampleText': sampleText |
| 31 | + } ); |
| 32 | + }, |
| 33 | + /** |
| 34 | + * Generates the actual toolbar buttons with localized text we use it to avoid creating the toolbar where JavaScript |
| 35 | + * is not enabled |
| 36 | + */ |
| 37 | + 'mwInsertEditButton': function( parent, item ) { |
| 38 | + var $image = $( '<img />' ) |
| 39 | + .attr( { |
| 40 | + 'width': 23, |
| 41 | + 'height': 22, |
| 42 | + 'class': 'mw-toolbar-editbutton', |
| 43 | + 'id': item.imageId ? item.imageId : null, |
| 44 | + 'src': = item.imageFile, |
| 45 | + 'border': 0, |
| 46 | + 'alt': item.speedTip, |
| 47 | + 'title': item.speedTip |
| 48 | + } ) |
| 49 | + .css( 'cursor', 'pointer' ) |
| 50 | + .click( function() { |
| 51 | + mw.legacy.insertTags( item.tagOpen, item.tagClose, item.sampleText ); |
| 52 | + // Click tracking |
| 53 | + if ( typeof $.trackAction != 'undefined' ) { |
| 54 | + $.trackAction( 'oldedit.' + item.speedTip.replace( / /g, '-' ) ); |
| 55 | + } |
| 56 | + return false; |
| 57 | + } ) |
| 58 | + .appendTo( $( parent ) ); |
| 59 | + return true; |
| 60 | + }, |
| 61 | + /** |
| 62 | + * Sets up the toolbar |
| 63 | + */ |
| 64 | + 'mwSetupToolbar': function() { |
| 65 | + var $toolbar = $( '#toolbar' ); |
| 66 | + var $textbox = $( 'textarea' ).get( 0 ); |
| 67 | + if ( !$toolbar.length || !$textbox.length ) { |
| 68 | + return false; |
| 69 | + } |
| 70 | + // Only check for selection capability if the textarea is visible - errors will occur otherwise - just because |
| 71 | + // the textarea is not visible, doesn't mean we shouldn't build out the toolbar though - it might have been |
| 72 | + // replaced with some other kind of control |
| 73 | + if ( |
| 74 | + $textbox.is( ':visible' ) && |
| 75 | + !( document.selection && document.selection.createRange ) && |
| 76 | + textboxes[0].selectionStart === null |
| 77 | + ) { |
| 78 | + return false; |
| 79 | + } |
| 80 | + for ( var i = 0; i < mw.legacy.mwEditButtons.length; i++ ) { |
| 81 | + mw.legacy.mwInsertEditButton( $toolbar, mw.legacy.mwEditButtons[i] ); |
| 82 | + } |
| 83 | + for ( var i = 0; i < mw.legacy.mwCustomEditButtons.length; i++ ) { |
| 84 | + mw.legacy.mwInsertEditButton( $toolbar, mw.legacy.mwCustomEditButtons[i] ); |
| 85 | + } |
| 86 | + return true; |
| 87 | + }, |
| 88 | + /** |
| 89 | + * Apply tagOpen/tagClose to selection in textarea, use sampleText instead of selection if there is none |
| 90 | + */ |
| 91 | + 'insertTags': function( tagOpen, tagClose, sampleText ) { |
| 92 | + function checkSelectedText() { |
| 93 | + if ( !selText ) { |
| 94 | + selText = sampleText; |
| 95 | + isSample = true; |
| 96 | + } else if ( selText.charAt( selText.length - 1 ) == ' ' ) { // exclude ending space char |
| 97 | + selText = selText.substring( 0, selText.length - 1 ); |
| 98 | + tagClose += ' '; |
| 99 | + } |
| 100 | + } |
| 101 | + var currentFocused = $( mw.legacy.currentFocused ); |
| 102 | + if ( |
| 103 | + typeof $.fn.textSelection != 'undefined' && |
| 104 | + ( $currentFocused.name().toLowerCase() == 'iframe' || $currentFocused.attr( 'id' ) == 'wpTextbox1' ) |
| 105 | + ) { |
| 106 | + $j( '#wpTextbox1' ).textSelection( |
| 107 | + 'encapsulateSelection', { 'pre': tagOpen, 'peri': sampleText, 'post': tagClose } |
| 108 | + ); |
| 109 | + return; |
| 110 | + } |
| 111 | + var $textarea; |
| 112 | + if ( $( 'form[name=editform]' ) { |
| 113 | + $textarea = $currentFocused; |
| 114 | + } else { |
| 115 | + // Some alternate form? take the first one we can find |
| 116 | + $textarea = $( 'textarea' ).get( 0 ); |
| 117 | + } |
| 118 | + var selText, isSample = false; |
| 119 | + // Text selection implementation for IE and Opera |
| 120 | + if ( document.selection && document.selection.createRange ) { |
| 121 | + // Save window scroll position |
| 122 | + if ( document.documentElement && document.documentElement.scrollTop ) { |
| 123 | + var winScroll = document.documentElement.scrollTop |
| 124 | + } else if ( document.body ) { |
| 125 | + var winScroll = document.body.scrollTop; |
| 126 | + } |
| 127 | + // Get current selection |
| 128 | + $textarea.focus(); |
| 129 | + var range = document.selection.createRange(); |
| 130 | + selText = range.text; |
| 131 | + // Insert tags |
| 132 | + checkSelectedText(); |
| 133 | + range.text = tagOpen + selText + tagClose; |
| 134 | + // Mark sample text as selected |
| 135 | + if ( isSample && range.moveStart ) { |
| 136 | + if ( window.opera ) { |
| 137 | + tagClose = tagClose.replace( /\n/g,'' ); |
| 138 | + } |
| 139 | + range.moveStart( 'character', - tagClose.length - selText.length ); |
| 140 | + range.moveEnd( 'character', - tagClose.length ); |
| 141 | + } |
| 142 | + range.select(); |
| 143 | + // Restore window scroll position |
| 144 | + if ( document.documentElement && document.documentElement.scrollTop ) { |
| 145 | + document.documentElement.scrollTop = winScroll; |
| 146 | + } else if ( document.body ) { |
| 147 | + document.body.scrollTop = winScroll; |
| 148 | + } |
| 149 | + } |
| 150 | + // Text selection implementation for Mozilla, Chrome and Safari |
| 151 | + else if ( $textarea[0].selectionStart || $textarea[0].selectionStart == '0' ) { |
| 152 | + // Save textarea scroll position |
| 153 | + var textScroll = $textarea.scrollTop; |
| 154 | + // Get current selection |
| 155 | + $textarea.focus(); |
| 156 | + var startPos = $textarea[0].selectionStart; |
| 157 | + var endPos = $textarea[0].selectionEnd; |
| 158 | + selText = $textarea.value.substring( startPos, endPos ); |
| 159 | + // Insert tags |
| 160 | + checkSelectedText(); |
| 161 | + $textarea.val( |
| 162 | + $textarea.val().substring( 0, startPos ) + |
| 163 | + tagOpen + selText + tagClose + |
| 164 | + $textarea.val().substring( endPos, $textarea.val().length ) |
| 165 | + ); |
| 166 | + // Set new selection |
| 167 | + if ( isSample ) { |
| 168 | + $textarea[0].selectionStart = startPos + tagOpen.length; |
| 169 | + $textarea[0].selectionEnd = startPos + tagOpen.length + selText.length; |
| 170 | + } else { |
| 171 | + $textarea[0].selectionStart = startPos + tagOpen.length + selText.length + tagClose.length; |
| 172 | + $textarea[0].selectionEnd = $textarea[0].selectionStart; |
| 173 | + } |
| 174 | + // Restore textarea scroll position |
| 175 | + $textarea[0].scrollTop = textScroll; |
| 176 | + } |
| 177 | + }, |
| 178 | + /** |
| 179 | + * Restore the edit box scroll state following a preview operation, |
| 180 | + * and set up a form submission handler to remember this state |
| 181 | + */ |
| 182 | + 'scrollEditBox': function() { |
| 183 | + var $textbox = $( '#wpTextbox1' ); |
| 184 | + var $scrollTop = $( '#wpScrolltop' ); |
| 185 | + var $editForm = $( '#editform' ); |
| 186 | + if ( $editForm.length && $textbox.length && $scrollTop.length ) { |
| 187 | + if ( scrollTop.val() ) { |
| 188 | + $textbox.scrollTop = $scrollTop.val(); |
| 189 | + } |
| 190 | + $editForm.submit( function() { |
| 191 | + $scrollTop.val( $textbox.scrollTop ); |
| 192 | + } ); |
| 193 | + } |
| 194 | + } |
| 195 | +} ); |
| 196 | + |
| 197 | +/* Initialization */ |
| 198 | + |
| 199 | +$( document ).ready( function() { |
| 200 | + mw.legacy.scrollEditBox(); |
| 201 | + mw.legacy.mwSetupToolbar(); |
| 202 | + mw.legacy.currentFocused = $( '#wpTextbox1' ).get( 0 ); |
| 203 | + // http://www.quirksmode.org/blog/archives/2008/04/delegating_the.html focus does not bubble normally, but using a |
| 204 | + // trick we can do event delegation on the focus event on all text inputs to make the toolbox usable on all of them |
| 205 | + $( '#editform' ).focus( function() { |
| 206 | + $(this).each( function( e ) { |
| 207 | + var elm = e.target || e.srcElement; |
| 208 | + if ( !elm ) { |
| 209 | + return; |
| 210 | + } |
| 211 | + var tagName = elm.tagName.toLowerCase(); |
| 212 | + var type = elm.type || ''; |
| 213 | + if ( tagName !== 'textarea' && tagName !== 'input' ) { |
| 214 | + return; |
| 215 | + } |
| 216 | + if ( tagName === 'input' && type.toLowerCase() !== 'text' ) { |
| 217 | + return; |
| 218 | + } |
| 219 | + mw.legacy.currentFocused = elm; |
| 220 | + } ); |
| 221 | + } ); |
| 222 | + // HACK: make currentFocused work with the usability iframe - with proper focus detection support (HTML 5!) this'll |
| 223 | + // be much cleaner |
| 224 | + var $iframe = $j( '.wikiEditor-ui-text iframe' ); |
| 225 | + if ( $iframe.length > 0 ) { |
| 226 | + $j( $iframe.get( 0 ).contentWindow.document ) |
| 227 | + // For IE |
| 228 | + .add( $iframe.get( 0 ).contentWindow.document.body ) |
| 229 | + .focus( function() { mw.legacy.currentFocused = $iframe.get( 0 ); } ); |
| 230 | + } |
| 231 | + // Make sure edit summary does not exceed byte limit |
| 232 | + var $summary = $( '#wpSummary' ); |
| 233 | + if ( !$summary.length ) { |
| 234 | + return; |
| 235 | + } |
| 236 | + // L must be capitalized in length |
| 237 | + $summary.get( 0 ).maxLength = 250; |
| 238 | + $summary.keypress( function( e ) { |
| 239 | + // First check to see if this is actually a character key being pressed. Based on key-event info from |
| 240 | + // http://unixpapa.com/js/key.html note === sign, if undefined, still could be a real key |
| 241 | + if ( e.which === 0 || e.charCode === 0 || e.ctrlKey || e.altKey || e.metaKey ) { |
| 242 | + // A special key (backspace, etc) so don't interefere. |
| 243 | + return true; |
| 244 | + } |
| 245 | + // This basically figures out how many bytes a utf-16 string (which is what js sees) will take in utf-8 by |
| 246 | + // replacing a 2 byte character with 2 *'s, etc, and counting that. Note, surogate (\uD800-\uDFFF) characters |
| 247 | + // are counted as 2 bytes, since theres two of them and the actual character takes 4 bytes in utf-8 (2*2=4). |
| 248 | + // Might not work perfectly in edge cases such as such as illegal sequences, but that should never happen. |
| 249 | + len = summary.value |
| 250 | + .replace(/[\u0080-\u07FF\uD800-\uDFFF]/g, '**') |
| 251 | + .replace(/[\u0800-\uD7FF\uE000-\uFFFF]/g, '***') |
| 252 | + .length; |
| 253 | + // 247 as this doesn't count character about to be inserted. |
| 254 | + if ( len > 247 ) { |
| 255 | + if ( e.preventDefault ) { |
| 256 | + e.preventDefault(); |
| 257 | + } |
| 258 | + // IE |
| 259 | + e.returnValue = false; |
| 260 | + return false; |
| 261 | + } |
| 262 | + } ); |
| 263 | +} ); |
| 264 | + |
| 265 | +} )( jQuery, MediaWiki ); |
\ No newline at end of file |
Property changes on: branches/resourceloader/phase3/resources/mediawiki/legacy/mediawiki.legacy.edit.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 266 | + native |
Index: branches/resourceloader/phase3/resources/mediawiki/legacy/mediawiki.legacy.mwsuggest.js |
— | — | @@ -0,0 +1,1031 @@ |
| 2 | +/* |
| 3 | + * Legacy emulation for the now depricated skins/common/mwsuggest.js |
| 4 | + * |
| 5 | + * OpenSearch ajax suggestion engine for MediaWiki |
| 6 | + * |
| 7 | + * Uses core MediaWiki open search support to fetch suggestions and show them below search boxes and other inputs |
| 8 | + * |
| 9 | + * by Robert Stojnic (April 2008) |
| 10 | + */ |
| 11 | + |
| 12 | +( function( $, mw ) { |
| 13 | + |
| 14 | +/* Extension */ |
| 15 | + |
| 16 | +$.extend( true, mw.legacy, { |
| 17 | + |
| 18 | + /* Global Variables */ |
| 19 | + |
| 20 | + // search_box_id -> Results object |
| 21 | + 'os_map': {}, |
| 22 | + // cached data, url -> json_text |
| 23 | + 'os_cache': {}, |
| 24 | + // global variables for suggest_keypress |
| 25 | + 'os_cur_keypressed': 0, |
| 26 | + 'os_keypressed_count': 0, |
| 27 | + // type: Timer |
| 28 | + 'os_timer': null, |
| 29 | + // tie mousedown/up events |
| 30 | + 'os_mouse_pressed': false, |
| 31 | + 'os_mouse_num': -1, |
| 32 | + // if true, the last change was made by mouse (and not keyboard) |
| 33 | + 'os_mouse_moved': false, |
| 34 | + // delay between keypress and suggestion (in ms) |
| 35 | + 'os_search_timeout': 250, |
| 36 | + // these pairs of inputs/forms will be autoloaded at startup |
| 37 | + 'os_autoload_inputs': new Array('searchInput', 'searchInput2', 'powerSearchText', 'searchText'), |
| 38 | + 'os_autoload_forms': new Array('searchform', 'searchform2', 'powersearch', 'search' ), |
| 39 | + // if we stopped the service |
| 40 | + 'os_is_stopped': false, |
| 41 | + // max lines to show in suggest table |
| 42 | + 'os_max_lines_per_suggest': 7, |
| 43 | + // number of steps to animate expansion/contraction of container width |
| 44 | + 'os_animation_steps': 6, |
| 45 | + // num of pixels of smallest step |
| 46 | + 'os_animation_min_step': 2, |
| 47 | + // delay between steps (in ms) |
| 48 | + 'os_animation_delay': 30, |
| 49 | + // max width of container in percent of normal size (1 == 100%) |
| 50 | + 'os_container_max_width': 2, |
| 51 | + // currently active animation timer |
| 52 | + 'os_animation_timer': null, |
| 53 | + // <datalist> is a new HTML5 element that allows you to manually supply |
| 54 | + // suggestion lists and have them rendered according to the right platform |
| 55 | + // conventions. However, the only shipping browser as of early 2010 is Opera, |
| 56 | + // and that has a fatal problem: the suggestion lags behind what the user types |
| 57 | + // by one keypress. (Reported as DSK-276870 to Opera's secret bug tracker.) |
| 58 | + // The code here otherwise seems to work, though, so this can be flipped on |
| 59 | + // (maybe with a UA check) when some browser has a better implementation. |
| 60 | + // 'os_use_datalist': 'list' in document.createElement( 'input' ), |
| 61 | + 'os_use_datalist': false, |
| 62 | + |
| 63 | + /* Functions */ |
| 64 | + |
| 65 | + /** |
| 66 | + * Timeout timer class that will fetch the results |
| 67 | + */ |
| 68 | + 'os_Timer': function( id, r, query ) { |
| 69 | + this.id = id; |
| 70 | + this.r = r; |
| 71 | + this.query = query; |
| 72 | + }, |
| 73 | + /** |
| 74 | + * Property class for single search box |
| 75 | + */ |
| 76 | + 'os_Results': function( name, formname ) { |
| 77 | + this.searchform = formname; // id of the searchform |
| 78 | + this.searchbox = name; // id of the searchbox |
| 79 | + this.container = name + 'Suggest'; // div that holds results |
| 80 | + this.resultTable = name + 'Result'; // id base for the result table (+num = table row) |
| 81 | + this.resultText = name + 'ResultText'; // id base for the spans within result tables (+num) |
| 82 | + this.toggle = name + 'Toggle'; // div that has the toggle (enable/disable) link |
| 83 | + this.query = null; // last processed query |
| 84 | + this.results = null; // parsed titles |
| 85 | + this.resultCount = 0; // number of results |
| 86 | + this.original = null; // query that user entered |
| 87 | + this.selected = -1; // which result is selected |
| 88 | + this.containerCount = 0; // number of results visible in container |
| 89 | + this.containerRow = 0; // height of result field in the container |
| 90 | + this.containerTotal = 0; // total height of the container will all results |
| 91 | + this.visible = false; // if container is visible |
| 92 | + this.stayHidden = false; // don't try to show if lost focus |
| 93 | + }, |
| 94 | + /** |
| 95 | + * Timer user to animate expansion/contraction of container width |
| 96 | + */ |
| 97 | + 'os_AnimationTimer': function( r, target ) { |
| 98 | + this.r = r; |
| 99 | + var current = document.getElementById(r.container).offsetWidth; |
| 100 | + this.inc = Math.round( ( target - current ) / os_animation_steps ); |
| 101 | + if( this.inc < os_animation_min_step && this.inc >=0 ) { |
| 102 | + this.inc = os_animation_min_step; // minimal animation step |
| 103 | + } |
| 104 | + if( this.inc > -os_animation_min_step && this.inc < 0 ) { |
| 105 | + this.inc = -os_animation_min_step; |
| 106 | + } |
| 107 | + this.target = target; |
| 108 | + }, |
| 109 | + |
| 110 | + /* Intialization Functions */ |
| 111 | + |
| 112 | + /** |
| 113 | + * Initialization, call upon page onload |
| 114 | + */ |
| 115 | + 'os_MWSuggestInit': function() { |
| 116 | + for( i = 0; i < os_autoload_inputs.length; i++ ) { |
| 117 | + var id = os_autoload_inputs[i]; |
| 118 | + var form = os_autoload_forms[i]; |
| 119 | + element = document.getElementById( id ); |
| 120 | + if( element != null ) { |
| 121 | + os_initHandlers( id, form, element ); |
| 122 | + } |
| 123 | + } |
| 124 | + }, |
| 125 | + /** |
| 126 | + * Init Result objects and event handlers |
| 127 | + */ |
| 128 | + 'os_initHandlers': function( name, formname, element ) { |
| 129 | + var r = new os_Results( name, formname ); |
| 130 | + var formElement = document.getElementById( formname ); |
| 131 | + if( !formElement ) { |
| 132 | + // Older browsers (Opera 8) cannot get form elements |
| 133 | + return; |
| 134 | + } |
| 135 | + // event handler |
| 136 | + os_hookEvent( element, 'keyup', function( event ) { os_eventKeyup( event ); } ); |
| 137 | + os_hookEvent( element, 'keydown', function( event ) { os_eventKeydown( event ); } ); |
| 138 | + os_hookEvent( element, 'keypress', function( event ) { os_eventKeypress( event ); } ); |
| 139 | + if ( !os_use_datalist ) { |
| 140 | + // These are needed for the div hack to hide it if the user blurs. |
| 141 | + os_hookEvent( element, 'blur', function( event ) { os_eventBlur( event ); } ); |
| 142 | + os_hookEvent( element, 'focus', function( event ) { os_eventFocus( event ); } ); |
| 143 | + // We don't want browser auto-suggestions interfering with our div, but |
| 144 | + // autocomplete must be on for datalist to work (at least in Opera |
| 145 | + // 10.10). |
| 146 | + element.setAttribute( 'autocomplete', 'off' ); |
| 147 | + } |
| 148 | + // stopping handler |
| 149 | + os_hookEvent( formElement, 'submit', function( event ) { return os_eventOnsubmit( event ); } ); |
| 150 | + os_map[name] = r; |
| 151 | + // toggle link |
| 152 | + if( document.getElementById( r.toggle ) == null ) { |
| 153 | + // TODO: disable this while we figure out a way for this to work in all browsers |
| 154 | + /* if( name == 'searchInput' ) { |
| 155 | + // special case: place above the main search box |
| 156 | + var t = os_createToggle( r, 'os-suggest-toggle' ); |
| 157 | + var searchBody = document.getElementById( 'searchBody' ); |
| 158 | + var first = searchBody.parentNode.firstChild.nextSibling.appendChild(t); |
| 159 | + } else { |
| 160 | + // default: place below search box to the right |
| 161 | + var t = os_createToggle( r, 'os-suggest-toggle-def' ); |
| 162 | + var top = element.offsetTop + element.offsetHeight; |
| 163 | + var left = element.offsetLeft + element.offsetWidth; |
| 164 | + t.style.position = 'absolute'; |
| 165 | + t.style.top = top + 'px'; |
| 166 | + t.style.left = left + 'px'; |
| 167 | + element.parentNode.appendChild( t ); |
| 168 | + // only now width gets calculated, shift right |
| 169 | + left -= t.offsetWidth; |
| 170 | + t.style.left = left + 'px'; |
| 171 | + t.style.visibility = 'visible'; |
| 172 | + } */ |
| 173 | + } |
| 174 | + }, |
| 175 | + 'os_hookEvent': function( element, hookName, hookFunct ) { |
| 176 | + if ( element.addEventListener ) { |
| 177 | + element.addEventListener( hookName, hookFunct, false ); |
| 178 | + } else if ( window.attachEvent ) { |
| 179 | + element.attachEvent( 'on' + hookName, hookFunct ); |
| 180 | + } |
| 181 | + }, |
| 182 | + |
| 183 | + /* Keyboard Event Functions */ |
| 184 | + |
| 185 | + /** |
| 186 | + * Event handler that will fetch results on keyup |
| 187 | + */ |
| 188 | + 'os_eventKeyup': function( e ) { |
| 189 | + var targ = os_getTarget( e ); |
| 190 | + var r = os_map[targ.id]; |
| 191 | + if( r == null ) { |
| 192 | + return; // not our event |
| 193 | + } |
| 194 | + |
| 195 | + // some browsers won't generate keypressed for arrow keys, catch it |
| 196 | + if( os_keypressed_count == 0 ) { |
| 197 | + os_processKey( r, os_cur_keypressed, targ ); |
| 198 | + } |
| 199 | + var query = targ.value; |
| 200 | + os_fetchResults( r, query, os_search_timeout ); |
| 201 | + }, |
| 202 | + /** |
| 203 | + * Catch arrows up/down and escape to hide the suggestions |
| 204 | + */ |
| 205 | + 'os_processKey': function( r, keypressed, targ ) { |
| 206 | + if ( keypressed == 40 && !r.visible && os_timer == null ) { |
| 207 | + // If the user hits the down arrow, fetch results immediately if none |
| 208 | + // are already displayed. |
| 209 | + r.query = ''; |
| 210 | + os_fetchResults( r, targ.value, 0 ); |
| 211 | + } |
| 212 | + // Otherwise, if we're not using datalist, we need to handle scrolling and |
| 213 | + // so on. |
| 214 | + if ( os_use_datalist ) { |
| 215 | + return; |
| 216 | + } |
| 217 | + if ( keypressed == 40 ) { // Arrow Down |
| 218 | + if ( r.visible ) { |
| 219 | + os_changeHighlight( r, r.selected, r.selected + 1, true ); |
| 220 | + } |
| 221 | + } else if ( keypressed == 38 ) { // Arrow Up |
| 222 | + if ( r.visible ) { |
| 223 | + os_changeHighlight( r, r.selected, r.selected - 1, true ); |
| 224 | + } |
| 225 | + } else if( keypressed == 27 ) { // Escape |
| 226 | + document.getElementById( r.searchbox ).value = r.original; |
| 227 | + r.query = r.original; |
| 228 | + os_hideResults( r ); |
| 229 | + } else if( r.query != document.getElementById( r.searchbox ).value ) { |
| 230 | + // os_hideResults( r ); // don't show old suggestions |
| 231 | + } |
| 232 | + }, |
| 233 | + /** |
| 234 | + * When keys is held down use a timer to output regular events |
| 235 | + */ |
| 236 | + 'os_eventKeypress': function( e ) { |
| 237 | + var targ = os_getTarget( e ); |
| 238 | + var r = os_map[targ.id]; |
| 239 | + if( r == null ) { |
| 240 | + return; // not our event |
| 241 | + } |
| 242 | + |
| 243 | + var keypressed = os_cur_keypressed; |
| 244 | + |
| 245 | + os_keypressed_count++; |
| 246 | + os_processKey( r, keypressed, targ ); |
| 247 | + }, |
| 248 | + /** |
| 249 | + * Catch the key code (Firefox bug) |
| 250 | + */ |
| 251 | + 'os_eventKeydown': function( e ) { |
| 252 | + if ( !e ) { |
| 253 | + e = window.event; |
| 254 | + } |
| 255 | + var targ = os_getTarget( e ); |
| 256 | + var r = os_map[targ.id]; |
| 257 | + if( r == null ) { |
| 258 | + return; // not our event |
| 259 | + } |
| 260 | + |
| 261 | + os_mouse_moved = false; |
| 262 | + |
| 263 | + os_cur_keypressed = ( e.keyCode == undefined ) ? e.which : e.keyCode; |
| 264 | + os_keypressed_count = 0; |
| 265 | + }, |
| 266 | + /** |
| 267 | + * When the form is submitted hide everything, cancel updates... |
| 268 | + */ |
| 269 | + 'os_eventOnsubmit': function( e ) { |
| 270 | + var targ = os_getTarget( e ); |
| 271 | + |
| 272 | + os_is_stopped = true; |
| 273 | + // kill timed requests |
| 274 | + if( os_timer != null && os_timer.id != null ) { |
| 275 | + clearTimeout( os_timer.id ); |
| 276 | + os_timer = null; |
| 277 | + } |
| 278 | + // Hide all suggestions |
| 279 | + for( i = 0; i < os_autoload_inputs.length; i++ ) { |
| 280 | + var r = os_map[os_autoload_inputs[i]]; |
| 281 | + if( r != null ) { |
| 282 | + var b = document.getElementById( r.searchform ); |
| 283 | + if( b != null && b == targ ) { |
| 284 | + // set query value so the handler won't try to fetch additional results |
| 285 | + r.query = document.getElementById( r.searchbox ).value; |
| 286 | + } |
| 287 | + os_hideResults( r ); |
| 288 | + } |
| 289 | + } |
| 290 | + return true; |
| 291 | + }, |
| 292 | + /** |
| 293 | + * Hide results from the user, either making the div visibility=hidden or detaching the datalist from the input. |
| 294 | + */ |
| 295 | + 'os_hideResults': function( r ) { |
| 296 | + if ( os_use_datalist ) { |
| 297 | + document.getElementById( r.searchbox ).setAttribute( 'list', '' ); |
| 298 | + } else { |
| 299 | + var c = document.getElementById( r.container ); |
| 300 | + if ( c != null ) { |
| 301 | + c.style.visibility = 'hidden'; |
| 302 | + } |
| 303 | + } |
| 304 | + r.visible = false; |
| 305 | + r.selected = -1; |
| 306 | + }, |
| 307 | + 'os_decodeValue': function( value ) { |
| 308 | + if ( decodeURIComponent ) { |
| 309 | + return decodeURIComponent( value ); |
| 310 | + } |
| 311 | + if( unescape ) { |
| 312 | + return unescape( value ); |
| 313 | + } |
| 314 | + return null; |
| 315 | + }, |
| 316 | + 'os_encodeQuery': function( value ) { |
| 317 | + if ( encodeURIComponent ) { |
| 318 | + return encodeURIComponent( value ); |
| 319 | + } |
| 320 | + if( escape ) { |
| 321 | + return escape( value ); |
| 322 | + } |
| 323 | + return null; |
| 324 | + }, |
| 325 | + /** |
| 326 | + * Handles data from XMLHttpRequest, and updates the suggest results |
| 327 | + */ |
| 328 | + 'os_updateResults': function( r, query, text, cacheKey ) { |
| 329 | + os_cache[cacheKey] = text; |
| 330 | + r.query = query; |
| 331 | + r.original = query; |
| 332 | + if( text == '' ) { |
| 333 | + r.results = null; |
| 334 | + r.resultCount = 0; |
| 335 | + os_hideResults( r ); |
| 336 | + } else { |
| 337 | + try { |
| 338 | + var p = eval( '(' + text + ')' ); // simple json parse, could do a safer one |
| 339 | + if( p.length < 2 || p[1].length == 0 ) { |
| 340 | + r.results = null; |
| 341 | + r.resultCount = 0; |
| 342 | + os_hideResults( r ); |
| 343 | + return; |
| 344 | + } |
| 345 | + if ( os_use_datalist ) { |
| 346 | + os_setupDatalist( r, p[1] ); |
| 347 | + } else { |
| 348 | + os_setupDiv( r, p[1] ); |
| 349 | + } |
| 350 | + } catch( e ) { |
| 351 | + // bad response from server or such |
| 352 | + os_hideResults( r ); |
| 353 | + os_cache[cacheKey] = null; |
| 354 | + } |
| 355 | + } |
| 356 | + }, |
| 357 | + /** |
| 358 | + * Create and populate a <datalist>. |
| 359 | + * |
| 360 | + * @param r os_Result object |
| 361 | + * @param results Array of the new results to replace existing ones |
| 362 | + */ |
| 363 | + 'os_setupDatalist': function( r, results ) { |
| 364 | + var s = document.getElementById( r.searchbox ); |
| 365 | + var c = document.getElementById( r.container ); |
| 366 | + if ( c == null ) { |
| 367 | + c = document.createElement( 'datalist' ); |
| 368 | + c.setAttribute( 'id', r.container ); |
| 369 | + document.body.appendChild( c ); |
| 370 | + } else { |
| 371 | + c.innerHTML = ''; |
| 372 | + } |
| 373 | + s.setAttribute( 'list', r.container ); |
| 374 | + |
| 375 | + r.results = new Array(); |
| 376 | + r.resultCount = results.length; |
| 377 | + r.visible = true; |
| 378 | + for ( i = 0; i < results.length; i++ ) { |
| 379 | + var title = os_decodeValue( results[i] ); |
| 380 | + var opt = document.createElement( 'option' ); |
| 381 | + opt.value = title; |
| 382 | + r.results[i] = title; |
| 383 | + c.appendChild( opt ); |
| 384 | + } |
| 385 | + }, |
| 386 | + /** |
| 387 | + * Fetch namespaces from checkboxes or hidden fields in the search form, if none defined use wgSearchNamespaces |
| 388 | + * global |
| 389 | + */ |
| 390 | + 'os_getNamespaces': function( r ) { |
| 391 | + var namespaces = ''; |
| 392 | + var elements = document.forms[r.searchform].elements; |
| 393 | + for( i = 0; i < elements.length; i++ ) { |
| 394 | + var name = elements[i].name; |
| 395 | + if( typeof name != 'undefined' && name.length > 2 && name[0] == 'n' && |
| 396 | + name[1] == 's' && ( |
| 397 | + ( elements[i].type == 'checkbox' && elements[i].checked ) || |
| 398 | + ( elements[i].type == 'hidden' && elements[i].value == '1' ) |
| 399 | + ) |
| 400 | + ) { |
| 401 | + if( namespaces != '' ) { |
| 402 | + namespaces += '|'; |
| 403 | + } |
| 404 | + namespaces += name.substring( 2 ); |
| 405 | + } |
| 406 | + } |
| 407 | + if( namespaces == '' ) { |
| 408 | + namespaces = wgSearchNamespaces.join('|'); |
| 409 | + } |
| 410 | + return namespaces; |
| 411 | + }, |
| 412 | + /** |
| 413 | + * Update results if user hasn't already typed something else |
| 414 | + */ |
| 415 | + 'os_updateIfRelevant': function( r, query, text, cacheKey ) { |
| 416 | + var t = document.getElementById( r.searchbox ); |
| 417 | + if( t != null && t.value == query ) { // check if response is still relevant |
| 418 | + os_updateResults( r, query, text, cacheKey ); |
| 419 | + } |
| 420 | + r.query = query; |
| 421 | + }, |
| 422 | + /** |
| 423 | + * Fetch results after some timeout |
| 424 | + */ |
| 425 | + 'os_delayedFetch': function() { |
| 426 | + if( os_timer == null ) { |
| 427 | + return; |
| 428 | + } |
| 429 | + var r = os_timer.r; |
| 430 | + var query = os_timer.query; |
| 431 | + os_timer = null; |
| 432 | + var path = wgMWSuggestTemplate.replace( '{namespaces}', os_getNamespaces( r ) ) |
| 433 | + .replace( '{dbname}', wgDBname ) |
| 434 | + .replace( '{searchTerms}', os_encodeQuery( query ) ); |
| 435 | + // try to get from cache, if not fetch using ajax |
| 436 | + var cached = os_cache[path]; |
| 437 | + if( cached != null && cached != undefined ) { |
| 438 | + os_updateIfRelevant( r, query, cached, path ); |
| 439 | + } else { |
| 440 | + var xmlhttp = sajax_init_object(); |
| 441 | + if( xmlhttp ) { |
| 442 | + try { |
| 443 | + xmlhttp.open( 'GET', path, true ); |
| 444 | + xmlhttp.onreadystatechange = function() { |
| 445 | + if ( xmlhttp.readyState == 4 && typeof os_updateIfRelevant == 'function' ) { |
| 446 | + os_updateIfRelevant( r, query, xmlhttp.responseText, path ); |
| 447 | + } |
| 448 | + }; |
| 449 | + xmlhttp.send( null ); |
| 450 | + } catch ( e ) { |
| 451 | + if ( window.location.hostname == 'localhost' ) { |
| 452 | + alert( 'Your browser blocks XMLHttpRequest to "localhost", try using a real hostname for development/testing.' ); |
| 453 | + } |
| 454 | + throw e; |
| 455 | + } |
| 456 | + } |
| 457 | + } |
| 458 | + }, |
| 459 | + /** |
| 460 | + * Init timed update via os_delayedUpdate() |
| 461 | + */ |
| 462 | + 'os_fetchResults': function( r, query, timeout ) { |
| 463 | + if( query == '' ) { |
| 464 | + r.query = ''; |
| 465 | + os_hideResults( r ); |
| 466 | + return; |
| 467 | + } else if( query == r.query ) { |
| 468 | + return; // no change |
| 469 | + } |
| 470 | + |
| 471 | + os_is_stopped = false; // make sure we're running |
| 472 | + |
| 473 | + // cancel any pending fetches |
| 474 | + if( os_timer != null && os_timer.id != null ) { |
| 475 | + clearTimeout( os_timer.id ); |
| 476 | + } |
| 477 | + // schedule delayed fetching of results |
| 478 | + if( timeout != 0 ) { |
| 479 | + os_timer = new os_Timer( setTimeout( 'os_delayedFetch()', timeout ), r, query ); |
| 480 | + } else { |
| 481 | + os_timer = new os_Timer( null, r, query ); |
| 482 | + os_delayedFetch(); // do it now! |
| 483 | + } |
| 484 | + }, |
| 485 | + /** |
| 486 | + * Find event target |
| 487 | + */ |
| 488 | + 'os_getTarget': function( e ) { |
| 489 | + if ( !e ) { |
| 490 | + e = window.event; |
| 491 | + } |
| 492 | + if ( e.target ) { |
| 493 | + return e.target; |
| 494 | + } else if ( e.srcElement ) { |
| 495 | + return e.srcElement; |
| 496 | + } else { |
| 497 | + return null; |
| 498 | + } |
| 499 | + }, |
| 500 | + /** |
| 501 | + * Check if x is a valid integer |
| 502 | + */ |
| 503 | + 'os_isNumber': function( x ) { |
| 504 | + if( x == '' || isNaN( x ) ) { |
| 505 | + return false; |
| 506 | + } |
| 507 | + for( var i = 0; i < x.length; i++ ) { |
| 508 | + var c = x.charAt( i ); |
| 509 | + if( !( c >= '0' && c <= '9' ) ) { |
| 510 | + return false; |
| 511 | + } |
| 512 | + } |
| 513 | + return true; |
| 514 | + }, |
| 515 | + /** |
| 516 | + * Call this to enable suggestions on input (id=inputId), on a form (name=formName) |
| 517 | + */ |
| 518 | + 'os_enableSuggestionsOn': function( inputId, formName ) { |
| 519 | + os_initHandlers( inputId, formName, document.getElementById( inputId ) ); |
| 520 | + }, |
| 521 | + /** |
| 522 | + * Call this to disable suggestios on input box (id=inputId) |
| 523 | + */ |
| 524 | + 'os_disableSuggestionsOn': function( inputId ) { |
| 525 | + r = os_map[inputId]; |
| 526 | + if( r != null ) { |
| 527 | + // cancel/hide results |
| 528 | + os_timer = null; |
| 529 | + os_hideResults( r ); |
| 530 | + // turn autocomplete on ! |
| 531 | + document.getElementById( inputId ).setAttribute( 'autocomplete', 'on' ); |
| 532 | + // remove descriptor |
| 533 | + os_map[inputId] = null; |
| 534 | + } |
| 535 | + |
| 536 | + // Remove the element from the os_autoload_* arrays |
| 537 | + var index = os_autoload_inputs.indexOf( inputId ); |
| 538 | + if ( index >= 0 ) { |
| 539 | + os_autoload_inputs[index] = os_autoload_forms[index] = ''; |
| 540 | + } |
| 541 | + }, |
| 542 | + |
| 543 | + /* Div-only Functions */ |
| 544 | + |
| 545 | + /** |
| 546 | + * Event: loss of focus of input box |
| 547 | + */ |
| 548 | + 'os_eventBlur': function( e ) { |
| 549 | + var targ = os_getTarget( e ); |
| 550 | + var r = os_map[targ.id]; |
| 551 | + if( r == null ) { |
| 552 | + return; // not our event |
| 553 | + } |
| 554 | + if( !os_mouse_pressed ) { |
| 555 | + os_hideResults( r ); |
| 556 | + // force canvas to stay hidden |
| 557 | + r.stayHidden = true; |
| 558 | + // cancel any pending fetches |
| 559 | + if( os_timer != null && os_timer.id != null ) { |
| 560 | + clearTimeout( os_timer.id ); |
| 561 | + } |
| 562 | + os_timer = null; |
| 563 | + } |
| 564 | + }, |
| 565 | + /** |
| 566 | + * Event: focus (catch only when stopped) |
| 567 | + */ |
| 568 | + 'os_eventFocus': function( e ) { |
| 569 | + var targ = os_getTarget( e ); |
| 570 | + var r = os_map[targ.id]; |
| 571 | + if( r == null ) { |
| 572 | + return; // not our event |
| 573 | + } |
| 574 | + r.stayHidden = false; |
| 575 | + }, |
| 576 | + /** |
| 577 | + * Create and populate a <div>, for non-<datalist>-supporting browsers. |
| 578 | + * |
| 579 | + * @param r os_Result object |
| 580 | + * @param results Array of the new results to replace existing ones |
| 581 | + */ |
| 582 | + 'os_setupDiv': function( r, results ) { |
| 583 | + var c = document.getElementById( r.container ); |
| 584 | + if ( c == null ) { |
| 585 | + c = os_createContainer( r ); |
| 586 | + } |
| 587 | + c.innerHTML = os_createResultTable( r, results ); |
| 588 | + // init container table sizes |
| 589 | + var t = document.getElementById( r.resultTable ); |
| 590 | + r.containerTotal = t.offsetHeight; |
| 591 | + r.containerRow = t.offsetHeight / r.resultCount; |
| 592 | + os_fitContainer( r ); |
| 593 | + os_trimResultText( r ); |
| 594 | + os_showResults( r ); |
| 595 | + }, |
| 596 | + /** |
| 597 | + * Create the result table to be placed in the container div |
| 598 | + */ |
| 599 | + 'os_createResultTable': function( r, results ) { |
| 600 | + var c = document.getElementById( r.container ); |
| 601 | + var width = c.offsetWidth - os_operaWidthFix( c.offsetWidth ); |
| 602 | + var html = '<table class="os-suggest-results" id="' + r.resultTable + '" style="width: ' + width + 'px;">'; |
| 603 | + r.results = new Array(); |
| 604 | + r.resultCount = results.length; |
| 605 | + for( i = 0; i < results.length; i++ ) { |
| 606 | + var title = os_decodeValue( results[i] ); |
| 607 | + r.results[i] = title; |
| 608 | + html += '<tr><td class="os-suggest-result" id="' + r.resultTable + i + '"><span id="' + r.resultText + i + '">' + title + '</span></td></tr>'; |
| 609 | + } |
| 610 | + html += '</table>'; |
| 611 | + return html; |
| 612 | + }, |
| 613 | + /** |
| 614 | + * Show results div |
| 615 | + */ |
| 616 | + 'os_showResults': function( r ) { |
| 617 | + if( os_is_stopped ) { |
| 618 | + return; |
| 619 | + } |
| 620 | + if( r.stayHidden ) { |
| 621 | + return; |
| 622 | + } |
| 623 | + os_fitContainer( r ); |
| 624 | + var c = document.getElementById( r.container ); |
| 625 | + r.selected = -1; |
| 626 | + if( c != null ) { |
| 627 | + c.scrollTop = 0; |
| 628 | + c.style.visibility = 'visible'; |
| 629 | + r.visible = true; |
| 630 | + } |
| 631 | + }, |
| 632 | + 'os_operaWidthFix': function( x ) { |
| 633 | + // For browsers that don't understand overflow-x, estimate scrollbar width |
| 634 | + if( typeof document.body.style.overflowX != 'string' ) { |
| 635 | + return 30; |
| 636 | + } |
| 637 | + return 0; |
| 638 | + }, |
| 639 | + |
| 640 | + /* Brower-dependent Functions */ |
| 641 | + |
| 642 | + 'f_clientWidth': function() { |
| 643 | + return f_filterResults( |
| 644 | + window.innerWidth ? window.innerWidth : 0, |
| 645 | + document.documentElement ? document.documentElement.clientWidth : 0, |
| 646 | + document.body ? document.body.clientWidth : 0 |
| 647 | + ); |
| 648 | + }, |
| 649 | + 'f_clientHeight': function() { |
| 650 | + return f_filterResults( |
| 651 | + window.innerHeight ? window.innerHeight : 0, |
| 652 | + document.documentElement ? document.documentElement.clientHeight : 0, |
| 653 | + document.body ? document.body.clientHeight : 0 |
| 654 | + ); |
| 655 | + }, |
| 656 | + 'f_scrollLeft': function() { |
| 657 | + return f_filterResults( |
| 658 | + window.pageXOffset ? window.pageXOffset : 0, |
| 659 | + document.documentElement ? document.documentElement.scrollLeft : 0, |
| 660 | + document.body ? document.body.scrollLeft : 0 |
| 661 | + ); |
| 662 | + }, |
| 663 | + 'f_scrollTop': function() { |
| 664 | + return f_filterResults( |
| 665 | + window.pageYOffset ? window.pageYOffset : 0, |
| 666 | + document.documentElement ? document.documentElement.scrollTop : 0, |
| 667 | + document.body ? document.body.scrollTop : 0 |
| 668 | + ); |
| 669 | + }, |
| 670 | + 'f_filterResults': function( n_win, n_docel, n_body ) { |
| 671 | + var n_result = n_win ? n_win : 0; |
| 672 | + if ( n_docel && ( !n_result || ( n_result > n_docel ) ) ) { |
| 673 | + n_result = n_docel; |
| 674 | + } |
| 675 | + return n_body && ( !n_result || ( n_result > n_body ) ) ? n_body : n_result; |
| 676 | + }, |
| 677 | + /** |
| 678 | + * Get the height available for the results container |
| 679 | + */ |
| 680 | + 'os_availableHeight': function( r ) { |
| 681 | + var absTop = document.getElementById( r.container ).style.top; |
| 682 | + var px = absTop.lastIndexOf( 'px' ); |
| 683 | + if( px > 0 ) { |
| 684 | + absTop = absTop.substring( 0, px ); |
| 685 | + } |
| 686 | + return f_clientHeight() - ( absTop - f_scrollTop() ); |
| 687 | + }, |
| 688 | + /** |
| 689 | + * Get element absolute position {left,top} |
| 690 | + */ |
| 691 | + 'os_getElementPosition': function( elemID ) { |
| 692 | + var offsetTrail = document.getElementById( elemID ); |
| 693 | + var offsetLeft = 0; |
| 694 | + var offsetTop = 0; |
| 695 | + while ( offsetTrail ) { |
| 696 | + offsetLeft += offsetTrail.offsetLeft; |
| 697 | + offsetTop += offsetTrail.offsetTop; |
| 698 | + offsetTrail = offsetTrail.offsetParent; |
| 699 | + } |
| 700 | + if ( navigator.userAgent.indexOf('Mac') != -1 && typeof document.body.leftMargin != 'undefined' ) { |
| 701 | + offsetLeft += document.body.leftMargin; |
| 702 | + offsetTop += document.body.topMargin; |
| 703 | + } |
| 704 | + return { left:offsetLeft, top:offsetTop }; |
| 705 | + }, |
| 706 | + /** |
| 707 | + * Create the container div that will hold the suggested titles |
| 708 | + */ |
| 709 | + 'os_createContainer': function( r ) { |
| 710 | + var c = document.createElement( 'div' ); |
| 711 | + var s = document.getElementById( r.searchbox ); |
| 712 | + var pos = os_getElementPosition( r.searchbox ); |
| 713 | + var left = pos.left; |
| 714 | + var top = pos.top + s.offsetHeight; |
| 715 | + c.className = 'os-suggest'; |
| 716 | + c.setAttribute( 'id', r.container ); |
| 717 | + document.body.appendChild( c ); |
| 718 | + |
| 719 | + // dynamically generated style params |
| 720 | + // IE workaround, cannot explicitely set "style" attribute |
| 721 | + c = document.getElementById( r.container ); |
| 722 | + c.style.top = top + 'px'; |
| 723 | + c.style.left = left + 'px'; |
| 724 | + c.style.width = s.offsetWidth + 'px'; |
| 725 | + |
| 726 | + // mouse event handlers |
| 727 | + c.onmouseover = function( event ) { os_eventMouseover( r.searchbox, event ); }; |
| 728 | + c.onmousemove = function( event ) { os_eventMousemove( r.searchbox, event ); }; |
| 729 | + c.onmousedown = function( event ) { return os_eventMousedown( r.searchbox, event ); }; |
| 730 | + c.onmouseup = function( event ) { os_eventMouseup( r.searchbox, event ); }; |
| 731 | + return c; |
| 732 | + }, |
| 733 | + /** |
| 734 | + * Change container height to fit to screen |
| 735 | + */ |
| 736 | + 'os_fitContainer': function( r ) { |
| 737 | + var c = document.getElementById( r.container ); |
| 738 | + var h = os_availableHeight( r ) - 20; |
| 739 | + var inc = r.containerRow; |
| 740 | + h = parseInt( h / inc ) * inc; |
| 741 | + if( h < ( 2 * inc ) && r.resultCount > 1 ) { // min: two results |
| 742 | + h = 2 * inc; |
| 743 | + } |
| 744 | + if( ( h / inc ) > os_max_lines_per_suggest ) { |
| 745 | + h = inc * os_max_lines_per_suggest; |
| 746 | + } |
| 747 | + if( h < r.containerTotal ) { |
| 748 | + c.style.height = h + 'px'; |
| 749 | + r.containerCount = parseInt( Math.round( h / inc ) ); |
| 750 | + } else { |
| 751 | + c.style.height = r.containerTotal + 'px'; |
| 752 | + r.containerCount = r.resultCount; |
| 753 | + } |
| 754 | + }, |
| 755 | + /** |
| 756 | + * If some entries are longer than the box, replace text with "..." |
| 757 | + */ |
| 758 | + 'os_trimResultText': function( r ) { |
| 759 | + // find max width, first see if we could expand the container to fit it |
| 760 | + var maxW = 0; |
| 761 | + for( var i = 0; i < r.resultCount; i++ ) { |
| 762 | + var e = document.getElementById( r.resultText + i ); |
| 763 | + if( e.offsetWidth > maxW ) { |
| 764 | + maxW = e.offsetWidth; |
| 765 | + } |
| 766 | + } |
| 767 | + var w = document.getElementById( r.container ).offsetWidth; |
| 768 | + var fix = 0; |
| 769 | + if( r.containerCount < r.resultCount ) { |
| 770 | + fix = 20; // give 20px for scrollbar |
| 771 | + } else { |
| 772 | + fix = os_operaWidthFix( w ); |
| 773 | + } |
| 774 | + if( fix < 4 ) { |
| 775 | + fix = 4; // basic padding |
| 776 | + } |
| 777 | + maxW += fix; |
| 778 | + |
| 779 | + // resize container to fit more data if permitted |
| 780 | + var normW = document.getElementById( r.searchbox ).offsetWidth; |
| 781 | + var prop = maxW / normW; |
| 782 | + if( prop > os_container_max_width ) { |
| 783 | + prop = os_container_max_width; |
| 784 | + } else if( prop < 1 ) { |
| 785 | + prop = 1; |
| 786 | + } |
| 787 | + var newW = Math.round( normW * prop ); |
| 788 | + if( w != newW ) { |
| 789 | + w = newW; |
| 790 | + if( os_animation_timer != null ) { |
| 791 | + clearInterval( os_animation_timer.id ); |
| 792 | + } |
| 793 | + os_animation_timer = new os_AnimationTimer( r, w ); |
| 794 | + os_animation_timer.id = setInterval( 'os_animateChangeWidth()', os_animation_delay ); |
| 795 | + w -= fix; // this much is reserved |
| 796 | + } |
| 797 | + |
| 798 | + // trim results |
| 799 | + if( w < 10 ) { |
| 800 | + return; |
| 801 | + } |
| 802 | + for( var i = 0; i < r.resultCount; i++ ) { |
| 803 | + var e = document.getElementById( r.resultText + i ); |
| 804 | + var replace = 1; |
| 805 | + var lastW = e.offsetWidth + 1; |
| 806 | + var iteration = 0; |
| 807 | + var changedText = false; |
| 808 | + while( e.offsetWidth > w && ( e.offsetWidth < lastW || iteration < 2 ) ) { |
| 809 | + changedText = true; |
| 810 | + lastW = e.offsetWidth; |
| 811 | + var l = e.innerHTML; |
| 812 | + e.innerHTML = l.substring( 0, l.length - replace ) + '...'; |
| 813 | + iteration++; |
| 814 | + replace = 4; // how many chars to replace |
| 815 | + } |
| 816 | + if( changedText ) { |
| 817 | + // show hint for trimmed titles |
| 818 | + document.getElementById( r.resultTable + i ).setAttribute( 'title', r.results[i] ); |
| 819 | + } |
| 820 | + } |
| 821 | + }, |
| 822 | + /** |
| 823 | + * Invoked on timer to animate change in container width |
| 824 | + */ |
| 825 | + 'os_animateChangeWidth': function() { |
| 826 | + var r = os_animation_timer.r; |
| 827 | + var c = document.getElementById( r.container ); |
| 828 | + var w = c.offsetWidth; |
| 829 | + var normW = document.getElementById( r.searchbox ).offsetWidth; |
| 830 | + var normL = os_getElementPosition( r.searchbox ).left; |
| 831 | + var inc = os_animation_timer.inc; |
| 832 | + var target = os_animation_timer.target; |
| 833 | + var nw = w + inc; |
| 834 | + if( ( inc > 0 && nw >= target ) || ( inc <= 0 && nw <= target ) ) { |
| 835 | + // finished ! |
| 836 | + c.style.width = target + 'px'; |
| 837 | + clearInterval( os_animation_timer.id ); |
| 838 | + os_animation_timer = null; |
| 839 | + } else { |
| 840 | + // in-progress |
| 841 | + c.style.width = nw + 'px'; |
| 842 | + if( document.documentElement.dir == 'rtl' ) { |
| 843 | + c.style.left = ( normL + normW + ( target - nw ) - os_animation_timer.target - 1 ) + 'px'; |
| 844 | + } |
| 845 | + } |
| 846 | + }, |
| 847 | + /** |
| 848 | + * Change the highlighted row (i.e. suggestion), from position cur to next |
| 849 | + */ |
| 850 | + 'os_changeHighlight': function( r, cur, next, updateSearchBox ) { |
| 851 | + if ( next >= r.resultCount ) { |
| 852 | + next = r.resultCount - 1; |
| 853 | + } |
| 854 | + if ( next < -1 ) { |
| 855 | + next = -1; |
| 856 | + } |
| 857 | + r.selected = next; |
| 858 | + if ( cur == next ) { |
| 859 | + return; // nothing to do. |
| 860 | + } |
| 861 | + |
| 862 | + if( cur >= 0 ) { |
| 863 | + var curRow = document.getElementById( r.resultTable + cur ); |
| 864 | + if( curRow != null ) { |
| 865 | + curRow.className = 'os-suggest-result'; |
| 866 | + } |
| 867 | + } |
| 868 | + var newText; |
| 869 | + if( next >= 0 ) { |
| 870 | + var nextRow = document.getElementById( r.resultTable + next ); |
| 871 | + if( nextRow != null ) { |
| 872 | + nextRow.className = os_HighlightClass(); |
| 873 | + } |
| 874 | + newText = r.results[next]; |
| 875 | + } else { |
| 876 | + newText = r.original; |
| 877 | + } |
| 878 | + |
| 879 | + // adjust the scrollbar if any |
| 880 | + if( r.containerCount < r.resultCount ) { |
| 881 | + var c = document.getElementById( r.container ); |
| 882 | + var vStart = c.scrollTop / r.containerRow; |
| 883 | + var vEnd = vStart + r.containerCount; |
| 884 | + if( next < vStart ) { |
| 885 | + c.scrollTop = next * r.containerRow; |
| 886 | + } else if( next >= vEnd ) { |
| 887 | + c.scrollTop = ( next - r.containerCount + 1 ) * r.containerRow; |
| 888 | + } |
| 889 | + } |
| 890 | + |
| 891 | + // update the contents of the search box |
| 892 | + if( updateSearchBox ) { |
| 893 | + os_updateSearchQuery( r, newText ); |
| 894 | + } |
| 895 | + }, |
| 896 | + 'os_HighlightClass': function() { |
| 897 | + var match = navigator.userAgent.match(/AppleWebKit\/(\d+)/); |
| 898 | + if ( match ) { |
| 899 | + var webKitVersion = parseInt( match[1] ); |
| 900 | + if ( webKitVersion < 523 ) { |
| 901 | + // CSS system highlight colors broken on old Safari |
| 902 | + // https://bugs.webkit.org/show_bug.cgi?id=6129 |
| 903 | + // Safari 3.0.4, 3.1 known ok |
| 904 | + return 'os-suggest-result-hl-webkit'; |
| 905 | + } |
| 906 | + } |
| 907 | + return 'os-suggest-result-hl'; |
| 908 | + }, |
| 909 | + 'os_updateSearchQuery': function( r, newText ) { |
| 910 | + document.getElementById( r.searchbox ).value = newText; |
| 911 | + r.query = newText; |
| 912 | + }, |
| 913 | + |
| 914 | + /* Mouse Event Functions */ |
| 915 | + |
| 916 | + /** |
| 917 | + * Mouse over the container |
| 918 | + */ |
| 919 | + 'os_eventMouseover': function( srcId, e ) { |
| 920 | + var targ = os_getTarget( e ); |
| 921 | + var r = os_map[srcId]; |
| 922 | + if( r == null || !os_mouse_moved ) { |
| 923 | + return; // not our event |
| 924 | + } |
| 925 | + var num = os_getNumberSuffix( targ.id ); |
| 926 | + if( num >= 0 ) { |
| 927 | + os_changeHighlight( r, r.selected, num, false ); |
| 928 | + } |
| 929 | + }, |
| 930 | + /** |
| 931 | + * Get row where the event occured (from its id) |
| 932 | + */ |
| 933 | + 'os_getNumberSuffix': function( id ) { |
| 934 | + var num = id.substring( id.length - 2 ); |
| 935 | + if( !( num.charAt( 0 ) >= '0' && num.charAt( 0 ) <= '9' ) ) { |
| 936 | + num = num.substring( 1 ); |
| 937 | + } |
| 938 | + if( os_isNumber( num ) ) { |
| 939 | + return parseInt( num ); |
| 940 | + } else { |
| 941 | + return -1; |
| 942 | + } |
| 943 | + }, |
| 944 | + /** |
| 945 | + * Save mouse move as last action |
| 946 | + */ |
| 947 | + 'os_eventMousemove': function( srcId, e ) { |
| 948 | + os_mouse_moved = true; |
| 949 | + }, |
| 950 | + /** |
| 951 | + * Mouse button held down, register possible click |
| 952 | + */ |
| 953 | + 'os_eventMousedown': function( srcId, e ) { |
| 954 | + var targ = os_getTarget( e ); |
| 955 | + var r = os_map[srcId]; |
| 956 | + if( r == null ) { |
| 957 | + return; // not our event |
| 958 | + } |
| 959 | + var num = os_getNumberSuffix( targ.id ); |
| 960 | + |
| 961 | + os_mouse_pressed = true; |
| 962 | + if( num >= 0 ) { |
| 963 | + os_mouse_num = num; |
| 964 | + // os_updateSearchQuery( r, r.results[num] ); |
| 965 | + } |
| 966 | + // keep the focus on the search field |
| 967 | + document.getElementById( r.searchbox ).focus(); |
| 968 | + |
| 969 | + return false; // prevents selection |
| 970 | + }, |
| 971 | + /** |
| 972 | + * Mouse button released, check for click on some row |
| 973 | + */ |
| 974 | + 'os_eventMouseup': function( srcId, e ) { |
| 975 | + var targ = os_getTarget( e ); |
| 976 | + var r = os_map[srcId]; |
| 977 | + if( r == null ) { |
| 978 | + return; // not our event |
| 979 | + } |
| 980 | + var num = os_getNumberSuffix( targ.id ); |
| 981 | + |
| 982 | + if( num >= 0 && os_mouse_num == num ) { |
| 983 | + os_updateSearchQuery( r, r.results[num] ); |
| 984 | + os_hideResults( r ); |
| 985 | + document.getElementById( r.searchform ).submit(); |
| 986 | + } |
| 987 | + os_mouse_pressed = false; |
| 988 | + // keep the focus on the search field |
| 989 | + document.getElementById( r.searchbox ).focus(); |
| 990 | + }, |
| 991 | + /** |
| 992 | + * Return the span element that contains the toggle link (dead code?) |
| 993 | + */ |
| 994 | + 'os_createToggle': function( r, className ) { |
| 995 | + var t = document.createElement( 'span' ); |
| 996 | + t.className = className; |
| 997 | + t.setAttribute( 'id', r.toggle ); |
| 998 | + var link = document.createElement( 'a' ); |
| 999 | + link.setAttribute( 'href', 'javascript:void(0);' ); |
| 1000 | + link.onclick = function() { os_toggle( r.searchbox, r.searchform ); }; |
| 1001 | + var msg = document.createTextNode( wgMWSuggestMessages[0] ); |
| 1002 | + link.appendChild( msg ); |
| 1003 | + t.appendChild( link ); |
| 1004 | + return t; |
| 1005 | + }, |
| 1006 | + /** |
| 1007 | + * Call when user clicks on some of the toggle links (dead code?) |
| 1008 | + */ |
| 1009 | + 'os_toggle': function( inputId, formName ) { |
| 1010 | + r = os_map[inputId]; |
| 1011 | + var msg = ''; |
| 1012 | + if( r == null ) { |
| 1013 | + os_enableSuggestionsOn( inputId, formName ); |
| 1014 | + r = os_map[inputId]; |
| 1015 | + msg = wgMWSuggestMessages[0]; |
| 1016 | + } else{ |
| 1017 | + os_disableSuggestionsOn( inputId, formName ); |
| 1018 | + msg = wgMWSuggestMessages[1]; |
| 1019 | + } |
| 1020 | + // change message |
| 1021 | + var link = document.getElementById( r.toggle ).firstChild; |
| 1022 | + link.replaceChild( document.createTextNode( msg ), link.firstChild ); |
| 1023 | + } |
| 1024 | +} ); |
| 1025 | + |
| 1026 | +/* Initialization */ |
| 1027 | + |
| 1028 | +$( document ).ready( function() { |
| 1029 | + mw.legacy.os_MWSuggestInit(); |
| 1030 | +} ); |
| 1031 | + |
| 1032 | +} )( jQuery, MediaWiki ); |
\ No newline at end of file |
Property changes on: branches/resourceloader/phase3/resources/mediawiki/legacy/mediawiki.legacy.mwsuggest.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 1033 | + native |
Index: branches/resourceloader/phase3/resources/mediawiki/legacy/mediawiki.legacy.htmlform.js |
— | — | @@ -0,0 +1,50 @@ |
| 2 | +/* |
| 3 | + * Legacy emulation for the now depricated skins/common/htmlform.js |
| 4 | + */ |
| 5 | + |
| 6 | +( function( $, mw ) { |
| 7 | + |
| 8 | +/* Extension */ |
| 9 | + |
| 10 | +$.extend( true, mw.legacy, { |
| 11 | + |
| 12 | + /* Global Variables */ |
| 13 | + |
| 14 | + 'htmlforms': { |
| 15 | + 'selectOrOtherSelectChanged': function( e ) { |
| 16 | + var select; |
| 17 | + if ( !e ) { |
| 18 | + e = window.event; |
| 19 | + } |
| 20 | + if ( e.target ) { |
| 21 | + select = e.target; |
| 22 | + } else if ( e.srcElement ) { |
| 23 | + select = e.srcElement; |
| 24 | + } |
| 25 | + // Defeat Safari bug |
| 26 | + if ( select.nodeType == 3 ) { |
| 27 | + select = select.parentNode; |
| 28 | + } |
| 29 | + var id = select.id; |
| 30 | + var textbox = document.getElementById( id + '-other' ); |
| 31 | + if ( select.value == 'other' ) { |
| 32 | + textbox.disabled = false; |
| 33 | + } else { |
| 34 | + textbox.disabled = true; |
| 35 | + } |
| 36 | + } |
| 37 | + } |
| 38 | +} ); |
| 39 | + |
| 40 | +/* Initialization */ |
| 41 | + |
| 42 | +$( document ).ready( function() { |
| 43 | + // Find select-or-other fields |
| 44 | + $( 'select .mw-htmlform-select-or-other' ).each( function() { |
| 45 | + $(this).change( function() { mw.legacy.htmlforms.selectOrOtherSelectChanged(); } ); |
| 46 | + // Use a fake event to update it. |
| 47 | + mw.legacy.htmlforms.selectOrOtherSelectChanged( { 'target': $(this).get( 0 ) } ); |
| 48 | + } ); |
| 49 | +} ); |
| 50 | + |
| 51 | +} )( jQuery, MediaWiki ); |
\ No newline at end of file |
Property changes on: branches/resourceloader/phase3/resources/mediawiki/legacy/mediawiki.legacy.htmlform.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 52 | + native |
Index: branches/resourceloader/phase3/resources/mediawiki/legacy/mediawiki.legacy.rightclickedit.js |
— | — | @@ -0,0 +1,65 @@ |
| 2 | +/* |
| 3 | + * Legacy emulation for the now depricated skins/common/rightclick.js |
| 4 | + */ |
| 5 | + |
| 6 | +( function( $, mw ) { |
| 7 | + |
| 8 | +/* Extension */ |
| 9 | + |
| 10 | +$.extend( true, mw.legacy, { |
| 11 | + |
| 12 | + /* Functions */ |
| 13 | + |
| 14 | + 'setupRightClickEdit': function() { |
| 15 | + if (document.getElementsByTagName) { |
| 16 | + var spans = document.getElementsByTagName('span'); |
| 17 | + for (var i = 0; i < spans.length; i++) { |
| 18 | + var el = spans[i]; |
| 19 | + if(el.className == 'editsection') { |
| 20 | + mw.legacy.addRightClickEditHandler(el); |
| 21 | + } |
| 22 | + } |
| 23 | + } |
| 24 | + } |
| 25 | + 'addRightClickEditHandler': function(el) { |
| 26 | + for (var i = 0; i < el.childNodes.length; i++) { |
| 27 | + var link = el.childNodes[i]; |
| 28 | + if (link.nodeType == 1 && link.nodeName.toLowerCase() == 'a') { |
| 29 | + var editHref = link.getAttribute('href'); |
| 30 | + // find the enclosing (parent) header |
| 31 | + var prev = el.parentNode; |
| 32 | + if (prev && prev.nodeType == 1 && |
| 33 | + prev.nodeName.match(/^[Hh][1-6]$/)) { |
| 34 | + prev.oncontextmenu = function(e) { |
| 35 | + if (!e) { e = window.event; } |
| 36 | + // e is now the event in all browsers |
| 37 | + var targ; |
| 38 | + if (e.target) { targ = e.target; } |
| 39 | + else if (e.srcElement) { targ = e.srcElement; } |
| 40 | + if (targ.nodeType == 3) { // defeat Safari bug |
| 41 | + targ = targ.parentNode; |
| 42 | + } |
| 43 | + // targ is now the target element |
| 44 | + // We don't want to deprive the noble reader of a context menu |
| 45 | + // for the section edit link, do we? (Might want to extend this |
| 46 | + // to all <a>'s?) |
| 47 | + if (targ.nodeName.toLowerCase() != 'a' |
| 48 | + || targ.parentNode.className != 'editsection') { |
| 49 | + document.location = editHref; |
| 50 | + return false; |
| 51 | + } |
| 52 | + return true; |
| 53 | + }; |
| 54 | + } |
| 55 | + } |
| 56 | + } |
| 57 | + } |
| 58 | +} ); |
| 59 | + |
| 60 | +/* Initialization */ |
| 61 | + |
| 62 | +$( document ).ready( function() { |
| 63 | + mw.legacy.setupRightClickEdit(); |
| 64 | +} ); |
| 65 | + |
| 66 | +} )( jQuery, MediaWiki ); |
\ No newline at end of file |
Property changes on: branches/resourceloader/phase3/resources/mediawiki/legacy/mediawiki.legacy.rightclickedit.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 67 | + native |
Index: branches/resourceloader/phase3/resources/mediawiki/legacy/mediawiki.legacy.block.js |
— | — | @@ -0,0 +1,44 @@ |
| 2 | +/* |
| 3 | + * Legacy emulation for the now depricated skins/common/block.js |
| 4 | + */ |
| 5 | + |
| 6 | +( function( $, mw ) { |
| 7 | + |
| 8 | +/* Extension */ |
| 9 | + |
| 10 | +$.extend( true, mw.legacy, { |
| 11 | + |
| 12 | + /* Functions */ |
| 13 | + |
| 14 | + 'considerChangingExpiryFocus': function() { |
| 15 | + var $expiry = $( '#wpBlockExpiry' ); |
| 16 | + var $other = $( '#wpBlockOther' ); |
| 17 | + if ( $expiry.length && $other.length ) { |
| 18 | + if ( $expiry.val() == 'other' ) { |
| 19 | + $other.css( 'display', '' ); |
| 20 | + } else { |
| 21 | + $other.css( 'display', 'none' ); |
| 22 | + } |
| 23 | + } |
| 24 | + }, |
| 25 | + 'updateBlockOptions': function() { |
| 26 | + var $target = $( '#mw-bi-target' ); |
| 27 | + if ( $target.length ) { |
| 28 | + var address = $target.val(); |
| 29 | + var isEmpty = address.match( /^\s*$/ ); |
| 30 | + var isIp = address.match( /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|:(:[0-9A-Fa-f]{1,4}){1,7}|[0-9A-Fa-f]{1,4}(:{1,2}[0-9A-Fa-f]{1,4}|::$){1,7})(\/\d+)?$/ ); |
| 31 | + var isIpRange = isIp && address.match( /\/\d+$/ ); |
| 32 | + $( '#wpAnonOnlyRow' ).css( 'display', !isIp && !isEmpty ? 'none' : '' ); |
| 33 | + $( '#wpEnableAutoblockRow,#wpEnableHideUser' ).css( 'display', isIp && !isEmpty ? 'none' : '' ); |
| 34 | + $( '#wpEnableWatchUser' ).css( 'display', isIpRange && !isEmpty ? 'none' : '' ); |
| 35 | + } |
| 36 | + } |
| 37 | +} ); |
| 38 | + |
| 39 | +/* Initialization */ |
| 40 | + |
| 41 | +$( document ).ready( function() { |
| 42 | + mw.legacy.considerChangingExpiryFocus(); |
| 43 | +} ); |
| 44 | + |
| 45 | +} )( jQuery, MediaWiki ); |
\ No newline at end of file |
Property changes on: branches/resourceloader/phase3/resources/mediawiki/legacy/mediawiki.legacy.block.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 46 | + native |
Index: branches/resourceloader/phase3/resources/mediawiki/legacy/mediawiki.legacy.IEFixes.js |
— | — | @@ -0,0 +1,141 @@ |
| 2 | +/* |
| 3 | + * Legacy emulation for the now depricated skins/common/IEFixes.js |
| 4 | + * |
| 5 | + * Internet Explorer JavaScript fixes |
| 6 | + */ |
| 7 | + |
| 8 | +( function( $, mw ) { |
| 9 | + |
| 10 | +/* Support */ |
| 11 | + |
| 12 | +/** |
| 13 | + * Expand links for printing |
| 14 | + */ |
| 15 | +String.prototype.hasClass = function( classWanted ) { |
| 16 | + var classArr = this.split(/\s/); |
| 17 | + for ( var i = 0; i < classArr.length; i++ ) { |
| 18 | + if ( classArr[i].toLowerCase() == classWanted.toLowerCase() ) { |
| 19 | + return true; |
| 20 | + } |
| 21 | + } |
| 22 | + return false; |
| 23 | +} |
| 24 | + |
| 25 | +/* Extension */ |
| 26 | + |
| 27 | +$.extend( true, mw.legacy, { |
| 28 | + |
| 29 | + /* Global Variables */ |
| 30 | + |
| 31 | + 'isMSIE55': ( window.showModalDialog && window.clipboardData && window.createPopup ), |
| 32 | + 'doneIETransform': null, |
| 33 | + 'doneIEAlphaFix': null, |
| 34 | + 'expandedURLs': null, |
| 35 | + |
| 36 | + /* Functions */ |
| 37 | + |
| 38 | + 'hookit': function() { |
| 39 | + if ( !doneIETransform && document.getElementById && document.getElementById( 'bodyContent' ) ) { |
| 40 | + doneIETransform = true; |
| 41 | + relativeforfloats(); |
| 42 | + fixalpha(); |
| 43 | + } |
| 44 | + }, |
| 45 | + /** |
| 46 | + * Fixes PNG alpha transparency |
| 47 | + */ |
| 48 | + function fixalpha( logoId ) { |
| 49 | + // bg |
| 50 | + if ( isMSIE55 && !doneIEAlphaFix ) { |
| 51 | + var plogo = document.getElementById( logoId || 'p-logo' ); |
| 52 | + if ( !plogo ) { |
| 53 | + return; |
| 54 | + } |
| 55 | + var logoa = plogo.getElementsByTagName('a')[0]; |
| 56 | + if ( !logoa ) { |
| 57 | + return; |
| 58 | + } |
| 59 | + var bg = logoa.currentStyle.backgroundImage; |
| 60 | + var imageUrl = bg.substring( 5, bg.length - 2 ); |
| 61 | + doneIEAlphaFix = true; |
| 62 | + if ( imageUrl.substr( imageUrl.length - 4 ).toLowerCase() == '.png' ) { |
| 63 | + var logospan = logoa.appendChild( document.createElement( 'span' ) ); |
| 64 | + logoa.style.backgroundImage = 'none'; |
| 65 | + logospan.style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src=' + imageUrl + ')'; |
| 66 | + logospan.style.height = '100%'; |
| 67 | + logospan.style.position = 'absolute'; |
| 68 | + logospan.style.width = logoa.currentStyle.width; |
| 69 | + logospan.style.cursor = 'hand'; |
| 70 | + // Center image with hack for IE5.5 |
| 71 | + if ( document.documentElement.dir == 'rtl' ) { |
| 72 | + logospan.style.right = '50%'; |
| 73 | + logospan.style.setExpression( 'marginRight', '"-" + (this.offsetWidth / 2) + "px"' ); |
| 74 | + } else { |
| 75 | + logospan.style.left = '50%'; |
| 76 | + logospan.style.setExpression( 'marginLeft', '"-" + (this.offsetWidth / 2) + "px"' ); |
| 77 | + } |
| 78 | + logospan.style.top = '50%'; |
| 79 | + logospan.style.setExpression( 'marginTop', '"-" + (this.offsetHeight / 2) + "px"' ); |
| 80 | + var linkFix = logoa.appendChild( logoa.cloneNode() ); |
| 81 | + linkFix.style.position = 'absolute'; |
| 82 | + linkFix.style.height = '100%'; |
| 83 | + linkFix.style.width = '100%'; |
| 84 | + } |
| 85 | + } |
| 86 | + }, |
| 87 | + /* |
| 88 | + * Fixes IE6 disappering float bug |
| 89 | + */ |
| 90 | + 'relativeforfloats': function() { |
| 91 | + var bc = document.getElementById( 'bodyContent' ); |
| 92 | + if ( bc ) { |
| 93 | + var tables = bc.getElementsByTagName( 'table' ); |
| 94 | + var divs = bc.getElementsByTagName( 'div' ); |
| 95 | + } |
| 96 | + setrelative( tables ); |
| 97 | + setrelative( divs ); |
| 98 | + }, |
| 99 | + 'setrelative': function ( nodes ) { |
| 100 | + var i = 0; |
| 101 | + while ( i < nodes.length ) { |
| 102 | + if( ( ( nodes[i].style.float && nodes[i].style.float != ( 'none' ) || |
| 103 | + ( nodes[i].align && nodes[i].align != ( 'none' ) ) ) && |
| 104 | + ( !nodes[i].style.position || nodes[i].style.position != 'relative' ) ) ) |
| 105 | + { |
| 106 | + nodes[i].style.position = 'relative'; |
| 107 | + } |
| 108 | + i++; |
| 109 | + } |
| 110 | + }, |
| 111 | + 'onbeforeprint': function() { |
| 112 | + expandedURLs = []; |
| 113 | + var contentEl = document.getElementById( 'content' ); |
| 114 | + if ( contentEl ) { |
| 115 | + var allLinks = contentEl.getElementsByTagName( 'a' ); |
| 116 | + for ( var i = 0; i < allLinks.length; i++ ) { |
| 117 | + if ( allLinks[i].className.hasClass( 'external' ) && !allLinks[i].className.hasClass( 'free' ) ) { |
| 118 | + var expandedLink = document.createElement( 'span' ); |
| 119 | + var expandedText = document.createTextNode( ' (' + allLinks[i].href + ')' ); |
| 120 | + expandedLink.appendChild( expandedText ); |
| 121 | + allLinks[i].parentNode.insertBefore( expandedLink, allLinks[i].nextSibling ); |
| 122 | + expandedURLs[i] = expandedLink; |
| 123 | + } |
| 124 | + } |
| 125 | + } |
| 126 | + }, |
| 127 | + 'onafterprint': function() { |
| 128 | + for ( var i = 0; i < expandedURLs.length; i++ ) { |
| 129 | + if ( expandedURLs[i] ) { |
| 130 | + expandedURLs[i].removeNode( true ); |
| 131 | + } |
| 132 | + } |
| 133 | + } |
| 134 | +} ); |
| 135 | + |
| 136 | +/* Initialization */ |
| 137 | + |
| 138 | +$( document ).ready( function() { |
| 139 | + mw.legacy.hookit(); |
| 140 | +} ); |
| 141 | + |
| 142 | +} )( jQuery, MediaWiki ); |
\ No newline at end of file |
Property changes on: branches/resourceloader/phase3/resources/mediawiki/legacy/mediawiki.legacy.IEFixes.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 143 | + native |
Index: branches/resourceloader/phase3/resources/mediawiki/legacy/mediawiki.legacy.enhancedchanges.js |
— | — | @@ -0,0 +1,46 @@ |
| 2 | +/* |
| 3 | + * Legacy emulation for the now depricated skins/common/enhancedchanges.js |
| 4 | + */ |
| 5 | + |
| 6 | +( function( $, mw ) { |
| 7 | + |
| 8 | +/* Extension */ |
| 9 | + |
| 10 | +$.extend( true, mw.legacy, { |
| 11 | + |
| 12 | + /* Functions */ |
| 13 | + |
| 14 | + /** |
| 15 | + * Switch an RC line between hidden/shown |
| 16 | + * |
| 17 | + * @param integer idNumber : the id number of the RC group |
| 18 | + */ |
| 19 | + 'toggleVisibility': function( idNumber ) { |
| 20 | + var elements = [ |
| 21 | + '#mw-rc-openarrow-' + idNumber, |
| 22 | + '#mw-rc-closearrow-' + idNumber, |
| 23 | + '#mw-rc-subentries-' + idNumber |
| 24 | + ]; |
| 25 | + $( elements.join( ',' ) ).toggleClass( 'mw-changeslist-hidden' ).toggleClass( 'mw-changeslist-expanded' ); |
| 26 | + } |
| 27 | +} ); |
| 28 | + |
| 29 | +/* Initialization */ |
| 30 | + |
| 31 | +$( document ).ready( function() { |
| 32 | + /* |
| 33 | + * Add the CSS to hide parts that should be collapsed |
| 34 | + * |
| 35 | + * We do this with JS so everything will be expanded by default |
| 36 | + * if JS is disabled |
| 37 | + */ |
| 38 | + $( 'head' ).append( |
| 39 | + '<style type="text/css">\ |
| 40 | + .mw-changeslist-hidden { display:none; }\ |
| 41 | + div.mw-changeslist-expanded { display:block; }\ |
| 42 | + span.mw-changeslist-expanded { display:inline !important; visibility:visible !important; }\ |
| 43 | + </style>' |
| 44 | + ); |
| 45 | +} ); |
| 46 | + |
| 47 | +} )( jQuery, MediaWiki ); |
\ No newline at end of file |
Property changes on: branches/resourceloader/phase3/resources/mediawiki/legacy/mediawiki.legacy.enhancedchanges.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 48 | + native |
Index: branches/resourceloader/phase3/resources/mediawiki/utilities/mediawiki.utilities.client.js |
— | — | @@ -0,0 +1,185 @@ |
| 2 | +/* |
| 3 | + * User-agent detection |
| 4 | + */ |
| 5 | + |
| 6 | +( function( $, mw ) { |
| 7 | + |
| 8 | +/* Extension */ |
| 9 | + |
| 10 | +$.extend( mw.utilities, { |
| 11 | + 'client': { |
| 12 | + /** |
| 13 | + * Returns an object containing information about the browser |
| 14 | + * |
| 15 | + * The resulting client object will be in the following format: |
| 16 | + * { |
| 17 | + * 'name': 'firefox', |
| 18 | + * 'layout': 'gecko', |
| 19 | + * 'os': 'linux' |
| 20 | + * 'version': '3.5.1', |
| 21 | + * 'versionBase': '3', |
| 22 | + * 'versionNumber': 3.5, |
| 23 | + * } |
| 24 | + */ |
| 25 | + this.profile = function() { |
| 26 | + // Use the cached version if possible |
| 27 | + if ( typeof this.profile === 'undefined' ) { |
| 28 | + |
| 29 | + /* Configuration */ |
| 30 | + |
| 31 | + // Name of browsers or layout engines we don't recognize |
| 32 | + var uk = 'unknown'; |
| 33 | + // Generic version digit |
| 34 | + var x = 'x'; |
| 35 | + // Strings found in user agent strings that need to be conformed |
| 36 | + var wildUserAgents = [ 'Opera', 'Navigator', 'Minefield', 'KHTML', 'Chrome', 'PLAYSTATION 3']; |
| 37 | + // Translations for conforming user agent strings |
| 38 | + var userAgentTranslations = [ |
| 39 | + // Tons of browsers lie about being something they are not |
| 40 | + [/(Firefox|MSIE|KHTML,\slike\sGecko|Konqueror)/, ''], |
| 41 | + // Chrome lives in the shadow of Safari still |
| 42 | + ['Chrome Safari', 'Chrome'], |
| 43 | + // KHTML is the layout engine not the browser - LIES! |
| 44 | + ['KHTML', 'Konqueror'], |
| 45 | + // Firefox nightly builds |
| 46 | + ['Minefield', 'Firefox'], |
| 47 | + // This helps keep differnt versions consistent |
| 48 | + ['Navigator', 'Netscape'], |
| 49 | + // This prevents version extraction issues, otherwise translation would happen later |
| 50 | + ['PLAYSTATION 3', 'PS3'], |
| 51 | + ]; |
| 52 | + // Strings which precede a version number in a user agent string - combined and used as match 1 in |
| 53 | + // version detectection |
| 54 | + var versionPrefixes = [ |
| 55 | + 'camino', 'chrome', 'firefox', 'netscape', 'netscape6', 'opera', 'version', 'konqueror', 'lynx', |
| 56 | + 'msie', 'safari', 'ps3' |
| 57 | + ]; |
| 58 | + // Used as matches 2, 3 and 4 in version extraction - 3 is used as actual version number |
| 59 | + var versionSuffix = '(\/|\;?\s|)([a-z0-9\.\+]*?)(\;|dev|rel|\\)|\s|$)'; |
| 60 | + // Names of known browsers |
| 61 | + var browserNames = [ |
| 62 | + 'camino', 'chrome', 'firefox', 'netscape', 'konqueror', 'lynx', 'msie', 'opera', 'safari', 'ipod', |
| 63 | + 'iphone', 'blackberry', 'ps3' |
| 64 | + ]; |
| 65 | + // Tanslations for conforming browser names |
| 66 | + var browserTranslations = []; |
| 67 | + // Names of known layout engines |
| 68 | + var layoutNames = ['gecko', 'konqueror', 'msie', 'opera', 'webkit']; |
| 69 | + // Translations for conforming layout names |
| 70 | + var layoutTranslations = [['konqueror', 'khtml'], ['msie', 'trident'], ['opera', 'presto']]; |
| 71 | + // Names of known operating systems |
| 72 | + var osNames = ['win', 'mac', 'linux', 'sunos', 'solaris', 'iphone']; |
| 73 | + // Translations for conforming operating system names |
| 74 | + var osTranslations = [['sunos', 'solaris']]; |
| 75 | + |
| 76 | + /* Methods */ |
| 77 | + |
| 78 | + // Performs multiple replacements on a string |
| 79 | + function translate( source, translations ) { |
| 80 | + for ( var i = 0; i < translations.length; i++ ) { |
| 81 | + source = source.replace( translations[i][0], translations[i][1] ); |
| 82 | + } |
| 83 | + return source; |
| 84 | + }; |
| 85 | + |
| 86 | + /* Pre-processing */ |
| 87 | + |
| 88 | + var userAgent = navigator.userAgent, match, browser = uk, layout = uk, os = uk, version = x; |
| 89 | + if ( match = new RegExp( '(' + wildUserAgents.join( '|' ) + ')' ).exec( userAgent ) ) { |
| 90 | + // Takes a userAgent string and translates given text into something we can more easily work with |
| 91 | + userAgent = translate( userAgent, userAgentTranslations ); |
| 92 | + } |
| 93 | + // Everything will be in lowercase from now on |
| 94 | + userAgent = userAgent.toLowerCase(); |
| 95 | + |
| 96 | + /* Extraction */ |
| 97 | + |
| 98 | + if ( match = new RegExp( '(' + browserNames.join( '|' ) + ')' ).exec( userAgent ) ) { |
| 99 | + browser = translate( match[1], browserTranslations ); |
| 100 | + } |
| 101 | + if ( match = new RegExp( '(' + layoutNames.join( '|' ) + ')' ).exec( userAgent ) ) { |
| 102 | + layout = translate( match[1], layoutTranslations ); |
| 103 | + } |
| 104 | + if ( match = new RegExp( '(' + osNames.join( '|' ) + ')' ).exec( navigator.platform.toLowerCase() ) ) { |
| 105 | + var os = translate( match[1], osTranslations ); |
| 106 | + } |
| 107 | + if ( match = new RegExp( '(' + versionPrefixes.join( '|' ) + ')' + versionSuffix ).exec( userAgent ) ) { |
| 108 | + version = match[3]; |
| 109 | + } |
| 110 | + |
| 111 | + /* Edge Cases -- did I mention about how user agent string lie? */ |
| 112 | + |
| 113 | + // Decode Safari's crazy 400+ version numbers |
| 114 | + if ( name.match( /safari/ ) && version > 400 ) { |
| 115 | + version = '2.0'; |
| 116 | + } |
| 117 | + // Expose Opera 10's lies about being Opera 9.8 |
| 118 | + if ( name === 'opera' && version >= 9.8) { |
| 119 | + version = userAgent.match( /version\/([0-9\.]*)/i )[1] || 10; |
| 120 | + } |
| 121 | + |
| 122 | + /* Caching */ |
| 123 | + |
| 124 | + this.profile = { |
| 125 | + 'browser': browser, |
| 126 | + 'layout': layout, |
| 127 | + 'os': os, |
| 128 | + 'version': version, |
| 129 | + 'versionBase': ( version !== x ? new String( version ).substr( 0, 1 ) : x ), |
| 130 | + 'versionNumber': ( parseFloat( version, 10 ) || 0.0 ) |
| 131 | + }; |
| 132 | + } |
| 133 | + return this.profile; |
| 134 | + }; |
| 135 | + /** |
| 136 | + * Checks the current browser against a support map object to determine if the browser has been black-listed or |
| 137 | + * not. If the browser was not configured specifically it is assumed to work. It is assumed that the body |
| 138 | + * element is classified as either "ltr" or "rtl". If neither is set, "ltr" is assumed. |
| 139 | + * |
| 140 | + * A browser map is in the following format: |
| 141 | + * { |
| 142 | + * 'ltr': { |
| 143 | + * // Multiple rules with configurable operators |
| 144 | + * 'msie': [['>=', 7], ['!=', 9]], |
| 145 | + * // Blocked entirely |
| 146 | + * 'iphone': false |
| 147 | + * }, |
| 148 | + * 'rtl': { |
| 149 | + * // Test against a string |
| 150 | + * 'msie': [['!==', '8.1.2.3']], |
| 151 | + * // RTL rules do not fall through to LTR rules, you must explicity set each of them |
| 152 | + * 'iphone': false |
| 153 | + * } |
| 154 | + * } |
| 155 | + * |
| 156 | + * @param map Object of browser support map |
| 157 | + * |
| 158 | + * @return Boolean true if browser known or assumed to be supported, false if blacklisted |
| 159 | + */ |
| 160 | + this.test = function( map ) { |
| 161 | + var client = this.client(); |
| 162 | + // Check over each browser condition to determine if we are running in a compatible client |
| 163 | + var browser = map[$( 'body' ).is( '.rtl' ) ? 'rtl' : 'ltr'][client.browser]; |
| 164 | + if ( typeof browser !== 'object' ) { |
| 165 | + // Unknown, so we assume it's working |
| 166 | + return true; |
| 167 | + } |
| 168 | + for ( var condition in browser ) { |
| 169 | + var op = browser[condition][0]; |
| 170 | + var val = browser[condition][1]; |
| 171 | + if ( val === false ) { |
| 172 | + return false; |
| 173 | + } else if ( typeof val == 'string' ) { |
| 174 | + if ( !( eval( 'client.version' + op + '"' + val + '"' ) ) ) { |
| 175 | + return false; |
| 176 | + } |
| 177 | + } else if ( typeof val == 'number' ) { |
| 178 | + if ( !( eval( 'client.versionNumber' + op + val ) ) ) { |
| 179 | + return false; |
| 180 | + } |
| 181 | + } |
| 182 | + } |
| 183 | + return true; |
| 184 | + }; |
| 185 | + } |
| 186 | +} ); |
\ No newline at end of file |
Property changes on: branches/resourceloader/phase3/resources/mediawiki/utilities/mediawiki.utilities.client.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 187 | + native |
Index: branches/resourceloader/phase3/resources/mediawiki/views/mediawiki.views.install.js |
— | — | @@ -0,0 +1,100 @@ |
| 2 | +/* |
| 3 | + * Web-installer progressive enhancement (ported from skins/common/config.js) |
| 4 | + * |
| 5 | + * Makes elements in the configuration form interactive and hides portions of the form when not in-use |
| 6 | + */ |
| 7 | + |
| 8 | +( function( $, mw ) { |
| 9 | + |
| 10 | +/* Initialization */ |
| 11 | + |
| 12 | +$( document ).ready( function() { |
| 13 | + // Show/hide code for help text |
| 14 | + $( '.config-show-help a' ).click( function() { |
| 15 | + $(this).parent().siblings( '.config-help-message' ).show( 'slow' ); |
| 16 | + $(this).parent().siblings( '.config-hide-help' ).show(); |
| 17 | + $(this).parent().hide(); |
| 18 | + return false; |
| 19 | + } ); |
| 20 | + $( '.config-hide-help a' ).click( function() { |
| 21 | + $(this).parent().siblings( '.config-help-message' ).hide( 'slow' ); |
| 22 | + $(this).parent().siblings( '.config-show-help' ).show(); |
| 23 | + $(this).parent().hide(); |
| 24 | + return false; |
| 25 | + } ); |
| 26 | + // Show/hide code for DB-specific options |
| 27 | + // FIXME: Do we want slow, fast, or even non-animated (instantaneous) showing/hiding here? |
| 28 | + $( '.dbRadio' ).each( function() { $( '#' + $(this).attr( 'rel' ) ).hide(); } ); |
| 29 | + $( '#' + $( '.dbRadio:checked' ).attr( 'rel' ) ).show(); |
| 30 | + $( '.dbRadio' ).click( function() { |
| 31 | + var $checked = $( '.dbRadio:checked' ); |
| 32 | + var $wrapper = $( '#' + $checked.attr( 'rel' ) ); |
| 33 | + if ( !$wrapper.is( ':visible' ) ) { |
| 34 | + $( '.dbWrapper' ).hide( 'slow' ); |
| 35 | + $wrapper.show( 'slow' ); |
| 36 | + } |
| 37 | + } ); |
| 38 | + // Scroll to the bottom of upgrade log |
| 39 | + $( "#config-update-log" ).each( function() { this.scrollTop = this.scrollHeight; } ); |
| 40 | + // Show/hide Creative Commons thingy |
| 41 | + $( '.licenseRadio' ).click( function() { |
| 42 | + var $wrapper = $( '#config-cc-wrapper' ); |
| 43 | + if ( $( '#config__LicenseCode_cc-choose' ).is( ':checked' ) ) { |
| 44 | + $wrapper.show( 'slow' ); |
| 45 | + } else { |
| 46 | + $wrapper.hide( 'slow' ); |
| 47 | + } |
| 48 | + } ); |
| 49 | + // Show/hide random stuff (email, upload) |
| 50 | + $( '.showHideRadio' ).click( function() { |
| 51 | + var $wrapper = $( '#' + $(this).attr( 'rel' ) ); |
| 52 | + if ( $(this).is( ':checked' ) ) { |
| 53 | + $wrapper.show( 'slow' ); |
| 54 | + } else { |
| 55 | + $wrapper.hide( 'slow' ); |
| 56 | + } |
| 57 | + } ); |
| 58 | + $( '.hideShowRadio' ).click( function() { |
| 59 | + var $wrapper = $( '#' + $(this).attr( 'rel' ) ); |
| 60 | + if ( $(this).is( ':checked' ) ) { |
| 61 | + $wrapper.hide( 'slow' ); |
| 62 | + } else { |
| 63 | + $wrapper.show( 'slow' ); |
| 64 | + } |
| 65 | + } ); |
| 66 | + // Enable/disable "other" textboxes |
| 67 | + $( '.enableForOther' ).click( function() { |
| 68 | + var $textbox = $( '#' + $(this).attr( 'rel' ) ); |
| 69 | + if ( $(this).val() == 'other' ) { // FIXME: Ugh, this is ugly |
| 70 | + $textbox.removeAttr( 'disabled' ); |
| 71 | + } else { |
| 72 | + $textbox.attr( 'disabled', 'disabled' ); |
| 73 | + } |
| 74 | + } ); |
| 75 | + // Synchronize radio button label for sitename with textbox |
| 76 | + $label = $( 'label[for=config__NamespaceType_site-name]' ); |
| 77 | + labelText = $label.text(); |
| 78 | + $label.text( labelText.replace( '$1', '' ) ); |
| 79 | + $( '#config_wgSitename' ).bind( 'keyup change', syncText ).each( syncText ); |
| 80 | + function syncText() { |
| 81 | + var value = $(this).val() |
| 82 | + .replace( /[\[\]\{\}|#<>%+? ]/g, '_' ) |
| 83 | + .replace( /&/, '&' ) |
| 84 | + .replace( /__+/g, '_' ) |
| 85 | + .replace( /^_+/, '' ) |
| 86 | + .replace( /_+$/, '' ); |
| 87 | + value = value.substr( 0, 1 ).toUpperCase() + value.substr( 1 ); |
| 88 | + $label.text( labelText.replace( '$1', value ) ); |
| 89 | + } |
| 90 | + // Show/Hide memcached servers when needed |
| 91 | + $( "input[name$='config_wgMainCacheType']" ).change( function() { |
| 92 | + var $memc = $( "#config-memcachewrapper" ); |
| 93 | + if ( $( "input[name$='config_wgMainCacheType']:checked" ).val() == 'memcached' ) { |
| 94 | + $memc.show( 'slow' ); |
| 95 | + } else { |
| 96 | + $memc.hide( 'slow' ); |
| 97 | + } |
| 98 | + } ); |
| 99 | +} ); |
| 100 | + |
| 101 | +} )( jQuery, MediaWiki ); |
\ No newline at end of file |
Property changes on: branches/resourceloader/phase3/resources/mediawiki/views/mediawiki.views.install.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 102 | + native |
Index: branches/resourceloader/phase3/resources/mediawiki/views/mediawiki.views.diff.js |
— | — | @@ -0,0 +1,30 @@ |
| 2 | +/* |
| 3 | + * Diff-view progressive enhancement (ported from skins/common/diff.js) |
| 4 | + * |
| 5 | + * Fixes an overflow bug in old versions of Firefox |
| 6 | + */ |
| 7 | + |
| 8 | +( function( $, mw ) { |
| 9 | + |
| 10 | +/* Initialization */ |
| 11 | + |
| 12 | +$( document ).ready( function() { |
| 13 | + /* |
| 14 | + * Workaround for overflow bug in Mozilla 1.1 and earlier, where scrolling <div>s in <td> cells collapse their |
| 15 | + * height to a single line. |
| 16 | + * |
| 17 | + * Known to be fixed in 1.2.1 (Gecko 20021130), but the CSS hacks I've tried with overflow-x disable the scrolling |
| 18 | + * all the way until Mozilla 1.8 / FF 1.5 and break Opera as well. |
| 19 | + * |
| 20 | + * So... we check for reaaaally old Gecko and hack in an alternate rule to let the wide cells spill instead of |
| 21 | + * scrolling them. Not ideal as it won't work if JS is disabled, of course. |
| 22 | + */ |
| 23 | + if ( window.navigator && window.navigator.product == 'Gecko' && window.navigator.productSub < '20021130' ) { |
| 24 | + document.styleSheets[document.styleSheets.length - 1].insertRule( |
| 25 | + 'table.diff td div { overflow: visible; }', |
| 26 | + document.styleSheets[document.styleSheets.length - 1].cssRules.length |
| 27 | + ); |
| 28 | + } |
| 29 | +} ); |
| 30 | + |
| 31 | +} )( jQuery, MediaWiki ); |
\ No newline at end of file |
Property changes on: branches/resourceloader/phase3/resources/mediawiki/views/mediawiki.views.diff.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 32 | + native |
Index: branches/resourceloader/phase3/resources/mediawiki/mediawiki.js |
— | — | @@ -0,0 +1,502 @@ |
| 2 | +/* |
| 3 | + * JavaScript Backwards Compatibility |
| 4 | + */ |
| 5 | + |
| 6 | +// Make calling .indexOf() on an array work on older browsers |
| 7 | +if ( typeof Array.prototype.indexOf === 'undefined' ) { |
| 8 | + Array.prototype.indexOf = function( needle ) { |
| 9 | + for ( var i = 0; i < this.length; i++ ) { |
| 10 | + if ( this[i] === needle ) { |
| 11 | + return i; |
| 12 | + } |
| 13 | + } |
| 14 | + return -1; |
| 15 | + }; |
| 16 | +} |
| 17 | + |
| 18 | +/* |
| 19 | + * Core MediaWiki JavaScript Library |
| 20 | + */ |
| 21 | +( function() { |
| 22 | + |
| 23 | + /* Constants */ |
| 24 | + |
| 25 | + // This will not change until we are 100% ready to turn off legacy globals |
| 26 | + const LEGACY_GLOBALS = true; |
| 27 | + |
| 28 | + /* Members */ |
| 29 | + |
| 30 | + this.legacy = LEGACY_GLOBALS ? window : {}; |
| 31 | + |
| 32 | + /* Methods */ |
| 33 | + |
| 34 | + /** |
| 35 | + * Log a string msg to the console |
| 36 | + * |
| 37 | + * All mw.log statements will be removed on minification so lots of mw.log calls will not impact performance in non-debug |
| 38 | + * mode. This is done using simple regular expressions, so the input of this function needs to not contain things like a |
| 39 | + * self-executing closure. In the case that the browser does not have a console available, one is created by appending a |
| 40 | + * <div> element to the bottom of the body and then appending a <div> element to that for each message. In the case that |
| 41 | + * the browser does have a console available |
| 42 | + * |
| 43 | + * @author Michael Dale <mdale@wikimedia.org>, Trevor Parscal <tparscal@wikimedia.org> |
| 44 | + * @param string string message to output to console |
| 45 | + */ |
| 46 | + this.log = function( string ) { |
| 47 | + // Allow log messages to use a configured prefix |
| 48 | + if ( mw.config.exists( 'mw.log.prefix' ) ) { |
| 49 | + string = mw.config.get( 'mw.log.prefix' ) + string; |
| 50 | + } |
| 51 | + // Try to use an existing console |
| 52 | + if ( typeof window.console !== 'undefined' && typeof window.console.log == 'function' ) { |
| 53 | + window.console.log( string ); |
| 54 | + } else { |
| 55 | + // Show a log box for console-less browsers |
| 56 | + var $log = $( '#mw_log_console' ); |
| 57 | + if ( !$log.length ) { |
| 58 | + $log = $( '<div id="mw_log_console"></div>' ) |
| 59 | + .css( { |
| 60 | + 'position': 'absolute', |
| 61 | + 'overflow': 'auto', |
| 62 | + 'z-index': 500, |
| 63 | + 'bottom': '0px', |
| 64 | + 'left': '0px', |
| 65 | + 'right': '0px', |
| 66 | + 'height': '150px', |
| 67 | + 'background-color': 'white', |
| 68 | + 'border-top': 'solid 1px #DDDDDD' |
| 69 | + } ) |
| 70 | + .appendTo( $( 'body' ) ); |
| 71 | + } |
| 72 | + if ( $log.length ) { |
| 73 | + $log.append( |
| 74 | + $( '<div>' + string + '</div>' ) |
| 75 | + .css( { |
| 76 | + 'border-bottom': 'solid 1px #DDDDDD', |
| 77 | + 'font-size': 'small', |
| 78 | + 'font-family': 'monospace', |
| 79 | + 'padding': '0.125em 0.25em' |
| 80 | + } ) |
| 81 | + ); |
| 82 | + } |
| 83 | + } |
| 84 | + }; |
| 85 | + /* |
| 86 | + * An object which allows single and multiple existence, setting and getting on a list of key / value pairs |
| 87 | + */ |
| 88 | + this.config = new ( function() { |
| 89 | + |
| 90 | + /* Private Members */ |
| 91 | + |
| 92 | + var that = this; |
| 93 | + // List of configuration values - in legacy mode these configurations were ALL in the global space |
| 94 | + var values = LEGACY_GLOBALS ? window : {}; |
| 95 | + |
| 96 | + /* Public Methods */ |
| 97 | + |
| 98 | + /** |
| 99 | + * Sets one or multiple configuration values using a key and a value or an object of keys and values |
| 100 | + */ |
| 101 | + this.set = function( keys, value ) { |
| 102 | + if ( typeof keys === 'object' ) { |
| 103 | + for ( var key in keys ) { |
| 104 | + values[key] = keys[key]; |
| 105 | + } |
| 106 | + } else if ( typeof keys === 'string' && typeof value !== 'undefined' ) { |
| 107 | + values[keys] = value; |
| 108 | + } |
| 109 | + }; |
| 110 | + /** |
| 111 | + * Gets one or multiple configuration values using a key and an optional fallback or an array of keys |
| 112 | + */ |
| 113 | + this.get = function( keys, fallback ) { |
| 114 | + if ( typeof keys === 'object' ) { |
| 115 | + var result = {}; |
| 116 | + for ( var k = 0; k < keys.length; k++ ) { |
| 117 | + if ( typeof values[keys[k]] !== 'undefined' ) { |
| 118 | + result[keys[k]] = values[keys[k]]; |
| 119 | + } |
| 120 | + } |
| 121 | + return result; |
| 122 | + } else if ( typeof values[keys] === 'undefined' ) { |
| 123 | + return typeof fallback !== 'undefined' ? fallback : null; |
| 124 | + } else { |
| 125 | + return values[keys]; |
| 126 | + } |
| 127 | + }; |
| 128 | + /** |
| 129 | + * Checks if one or multiple configuration fields exist |
| 130 | + */ |
| 131 | + this.exists = function( keys ) { |
| 132 | + if ( typeof keys === 'object' ) { |
| 133 | + for ( var k = 0; k < keys.length; k++ ) { |
| 134 | + if ( !( keys[k] in values ) ) { |
| 135 | + return false; |
| 136 | + } |
| 137 | + } |
| 138 | + return true; |
| 139 | + } else { |
| 140 | + return keys in values; |
| 141 | + } |
| 142 | + }; |
| 143 | + } )(); |
| 144 | + /* |
| 145 | + * Localization system |
| 146 | + */ |
| 147 | + this.msg = new ( function() { |
| 148 | + |
| 149 | + /* Private Members */ |
| 150 | + |
| 151 | + var that = this; |
| 152 | + // List of localized messages |
| 153 | + var messages = {}; |
| 154 | + |
| 155 | + /* Public Methods */ |
| 156 | + |
| 157 | + this.set = function( keys, value ) { |
| 158 | + if ( typeof keys === 'object' ) { |
| 159 | + for ( var key in keys ) { |
| 160 | + messages[key] = keys[key]; |
| 161 | + } |
| 162 | + } else if ( typeof keys === 'string' && typeof value !== 'undefined' ) { |
| 163 | + messages[keys] = value; |
| 164 | + } |
| 165 | + }; |
| 166 | + this.get = function( key, args ) { |
| 167 | + if ( !( key in messages ) ) { |
| 168 | + return '<' + key + '>'; |
| 169 | + } |
| 170 | + var msg = messages[key]; |
| 171 | + if ( typeof args == 'object' || typeof args == 'array' ) { |
| 172 | + for ( var argKey in args ) { |
| 173 | + msg = msg.replace( '\$' + ( parseInt( argKey ) + 1 ), args[argKey] ); |
| 174 | + } |
| 175 | + } else if ( typeof args == 'string' || typeof args == 'number' ) { |
| 176 | + msg = msg.replace( '$1', args ); |
| 177 | + } |
| 178 | + return msg; |
| 179 | + }; |
| 180 | + } )(); |
| 181 | + /* |
| 182 | + * Client-side module loader which integrates with the MediaWiki ResourceLoader |
| 183 | + */ |
| 184 | + this.loader = new ( function() { |
| 185 | + |
| 186 | + /* Private Members */ |
| 187 | + |
| 188 | + var that = this; |
| 189 | + var server = 'load.php'; |
| 190 | + /* |
| 191 | + * Mapping of registered modules |
| 192 | + * |
| 193 | + * Format: |
| 194 | + * { |
| 195 | + * 'moduleName': { |
| 196 | + * 'needs': ['required module', 'required module', ...], |
| 197 | + * 'state': 'registered, loading, loaded, or ready', |
| 198 | + * 'script': function() {}, |
| 199 | + * 'style': 'css code string', |
| 200 | + * 'localization': { 'key': 'value' } |
| 201 | + * } |
| 202 | + * } |
| 203 | + */ |
| 204 | + var registry = {}; |
| 205 | + // List of callbacks waiting on dependent modules to be loaded so they can be executed |
| 206 | + var queue = []; |
| 207 | + // Until document ready, load requests will be collected in a batch queue |
| 208 | + var batch = []; |
| 209 | + // True after document ready occurs |
| 210 | + var ready = false; |
| 211 | + |
| 212 | + /* Private Methods */ |
| 213 | + |
| 214 | + /** |
| 215 | + * Gets a list of modules names that a module needs in their proper dependency order |
| 216 | + * |
| 217 | + * @param string module name |
| 218 | + * @return |
| 219 | + * @throws Error if circular reference is detected |
| 220 | + */ |
| 221 | + function needs( module ) { |
| 222 | + if ( !( module in registry ) ) { |
| 223 | + // Undefined modules have no needs |
| 224 | + return []; |
| 225 | + } |
| 226 | + var resolved = []; |
| 227 | + var unresolved = []; |
| 228 | + if ( arguments.length === 3 ) { |
| 229 | + // Use arguemnts on inner call |
| 230 | + resolved = arguments[1]; |
| 231 | + unresolved = arguments[2]; |
| 232 | + } |
| 233 | + unresolved[unresolved.length] = module; |
| 234 | + for ( n in registry[module].needs ) { |
| 235 | + if ( resolved.indexOf( registry[module].needs[n] ) === -1 ) { |
| 236 | + if ( unresolved.indexOf( registry[module].needs[n] ) !== -1 ) { |
| 237 | + throw new Error( |
| 238 | + 'Circular reference detected: ' + module + ' -> ' + registry[module].needs[n] |
| 239 | + ); |
| 240 | + } |
| 241 | + needs( registry[module].needs[n], resolved, unresolved ); |
| 242 | + } |
| 243 | + } |
| 244 | + resolved[resolved.length] = module; |
| 245 | + unresolved.slice( unresolved.indexOf( module ), 1 ); |
| 246 | + if ( arguments.length === 1 ) { |
| 247 | + // Return resolved list on outer call |
| 248 | + return resolved; |
| 249 | + } |
| 250 | + }; |
| 251 | + /** |
| 252 | + * Narrows a list of module names down to those matching a specific state. Possible states are 'undefined', |
| 253 | + * 'registered', 'loading', 'loaded', or 'ready' |
| 254 | + * |
| 255 | + * @param mixed string or array of strings of module states to filter by |
| 256 | + * @param array list of module names to filter (optional, all modules will be used by default) |
| 257 | + * @return array list of filtered module names |
| 258 | + */ |
| 259 | + function filter( states, modules ) { |
| 260 | + var list = []; |
| 261 | + if ( typeof modules === 'undefined' ) { |
| 262 | + modules = []; |
| 263 | + for ( module in registry ) { |
| 264 | + modules[modules.length] = module; |
| 265 | + } |
| 266 | + } |
| 267 | + for ( var s in states ) { |
| 268 | + for ( var m in modules ) { |
| 269 | + if ( |
| 270 | + ( states[s] == 'undefined' && typeof registry[modules[m]] === 'undefined' ) || |
| 271 | + ( typeof registry[modules[m]] === 'object' && registry[modules[m]].state === states[s] ) |
| 272 | + ) { |
| 273 | + list[list.length] = modules[m]; |
| 274 | + } |
| 275 | + } |
| 276 | + } |
| 277 | + return list; |
| 278 | + } |
| 279 | + /** |
| 280 | + * Executes a loaded module, making it ready to use |
| 281 | + * |
| 282 | + * @param string module name to execute |
| 283 | + */ |
| 284 | + function execute( module ) { |
| 285 | + if ( typeof registry[module] === 'undefined' ) { |
| 286 | + throw new Error( 'module has not been registered: ' + module ); |
| 287 | + } |
| 288 | + switch ( registry[module].state ) { |
| 289 | + case 'registered': |
| 290 | + throw new Error( 'module has not completed loading: ' + module ); |
| 291 | + break; |
| 292 | + case 'loading': |
| 293 | + throw new Error( 'module has not completed loading: ' + module ); |
| 294 | + break; |
| 295 | + case 'ready': |
| 296 | + throw new Error( 'module has already been loaded: ' + module ); |
| 297 | + break; |
| 298 | + } |
| 299 | + // Add style sheet to document |
| 300 | + if ( typeof registry[module].style === 'string' && registry[module].style.length ) { |
| 301 | + $( 'head' ).append( '<style type="text/css">' + registry[module].style + '</style>' ); |
| 302 | + } |
| 303 | + // Add localizations to message system |
| 304 | + if ( typeof registry[module].localization === 'object' ) { |
| 305 | + mw.msg.set( registry[module].localization ); |
| 306 | + } |
| 307 | + // Execute script |
| 308 | + try { |
| 309 | + registry[module].script(); |
| 310 | + } catch( e ) { |
| 311 | + mw.log( 'Exception thrown by ' + module + ': ' + e.message ); |
| 312 | + } |
| 313 | + // Change state |
| 314 | + registry[module].state = 'ready'; |
| 315 | + |
| 316 | + // Execute all modules which were waiting for this to be ready |
| 317 | + for ( r in registry ) { |
| 318 | + if ( registry[r].state == 'loaded' ) { |
| 319 | + if ( filter( ['ready'], registry[r].needs ).length == registry[r].needs.length ) { |
| 320 | + execute( r ); |
| 321 | + } |
| 322 | + } |
| 323 | + } |
| 324 | + } |
| 325 | + /** |
| 326 | + * Adds a callback and it's needs to the queue |
| 327 | + * |
| 328 | + * @param array list of module names the callback needs to be ready before being executed |
| 329 | + * @param function callback to execute when needs are met |
| 330 | + */ |
| 331 | + function request( needs, callback ) { |
| 332 | + queue[queue.length] = { 'needs': filter( ['undefined', 'registered'], needs ), 'callback': callback }; |
| 333 | + } |
| 334 | + |
| 335 | + /* Public Methods */ |
| 336 | + |
| 337 | + /** |
| 338 | + * Processes the queue, loading and executing when things when ready. |
| 339 | + */ |
| 340 | + this.work = function() { |
| 341 | + // Appends a list of modules to the batch |
| 342 | + function append( modules ) { |
| 343 | + for ( m in modules ) { |
| 344 | + // Prevent requesting modules which are loading, loaded or ready |
| 345 | + if ( modules[m] in registry && registry[modules[m]].state == 'registered' ) { |
| 346 | + // Since the batch can live between calls to work until document ready, we need to make sure |
| 347 | + // we aren't making a duplicate entry |
| 348 | + if ( batch.indexOf( modules[m] ) == -1 ) { |
| 349 | + batch[batch.length] = modules[m]; |
| 350 | + registry[modules[m]].state = 'loading'; |
| 351 | + } |
| 352 | + } |
| 353 | + } |
| 354 | + } |
| 355 | + // Fill batch with modules that need to be loaded |
| 356 | + for ( var q in queue ) { |
| 357 | + append( queue[q].needs ); |
| 358 | + for ( n in queue[q].needs ) { |
| 359 | + append( needs( queue[q].needs[n] ) ); |
| 360 | + } |
| 361 | + } |
| 362 | + // After document ready, handle the batch |
| 363 | + if ( ready && batch.length ) { |
| 364 | + // Always order modules alphabetically to help reduce cache misses for otherwise identical content |
| 365 | + batch.sort(); |
| 366 | + |
| 367 | + var base = $.extend( {}, |
| 368 | + // Pass configuration values through the URL |
| 369 | + mw.config.get( [ 'user', 'skin', 'space', 'view', 'language' ] ), |
| 370 | + // Ensure request comes back in the proper mode (debug or not) |
| 371 | + { 'debug': typeof mw.debug !== 'undefined' ? '1' : '0' } |
| 372 | + ); |
| 373 | + var requests = []; |
| 374 | + if ( base.debug == '1' ) { |
| 375 | + for ( b in batch ) { |
| 376 | + requests[requests.length] = $.extend( { 'modules': batch[b] }, base ); |
| 377 | + } |
| 378 | + } else { |
| 379 | + requests[requests.length] = $.extend( { 'modules': batch.join( '|' ) }, base ); |
| 380 | + } |
| 381 | + // It may be more performant to do this with an Ajax call, but that's limited to same-domain, so we |
| 382 | + // can either auto-detect (if there really is any benefit) or just use this method, which is safe |
| 383 | + setTimeout( function() { |
| 384 | + // Clear the batch - this MUST happen before we append the script element to the body or it's |
| 385 | + // possible that the script will be locally cached, instantly load, and work the batch again, |
| 386 | + // all before we've cleared it causing each request to include modules which are already loaded |
| 387 | + batch = []; |
| 388 | + var html = ''; |
| 389 | + for ( r in requests ) { |
| 390 | + // Build out the HTML |
| 391 | + var src = mw.config.get( 'wgScriptPath' ) + '/load.php?' + jQuery.param( requests[r] ); |
| 392 | + html += '<script type="text/javascript" src="' + src + '"></script>'; |
| 393 | + } |
| 394 | + // Append script to head |
| 395 | + $( 'head' ).append( html ); |
| 396 | + }, 0 ) |
| 397 | + } |
| 398 | + }; |
| 399 | + /** |
| 400 | + * Registers a module, letting the system know about it and it's dependencies. loader.js files contain calls |
| 401 | + * to this function. |
| 402 | + */ |
| 403 | + this.register = function( name, needs ) { |
| 404 | + // Validate input |
| 405 | + if ( typeof name !== 'string' ) { |
| 406 | + throw new Error( 'name must be a string, not a ' + typeof name ); |
| 407 | + } |
| 408 | + if ( typeof registry[name] !== 'undefined' ) { |
| 409 | + throw new Error( 'module already implemeneted: ' + name ); |
| 410 | + } |
| 411 | + // List the module as registered |
| 412 | + registry[name] = { 'state': 'registered', 'needs': [] }; |
| 413 | + // Allow needs to be given as a function which returns a string or array |
| 414 | + if ( typeof needs === 'function' ) { |
| 415 | + needs = needs(); |
| 416 | + } |
| 417 | + if ( typeof needs === 'string' ) { |
| 418 | + // Allow needs to be given as a single module name |
| 419 | + registry[name].needs = [needs]; |
| 420 | + } else if ( typeof needs === 'object' ) { |
| 421 | + // Allow needs to be given as an array of module names |
| 422 | + registry[name].needs = needs; |
| 423 | + } |
| 424 | + }; |
| 425 | + /** |
| 426 | + * Implements a module, giving the system a course of action to take upon loading. Results of a request for |
| 427 | + * one or more modules contain calls to this function. |
| 428 | + */ |
| 429 | + this.implement = function( name, script, style, localization ) { |
| 430 | + // Automaically register module |
| 431 | + if ( typeof registry[name] === 'undefined' ) { |
| 432 | + that.register( name, needs ); |
| 433 | + } |
| 434 | + // Validate input |
| 435 | + if ( typeof script !== 'function' ) { |
| 436 | + throw new Error( 'script must be a function, not a ' + typeof script ); |
| 437 | + } |
| 438 | + if ( typeof style !== 'undefined' && typeof style !== 'string' ) { |
| 439 | + throw new Error( 'style must be a string, not a ' + typeof style ); |
| 440 | + } |
| 441 | + if ( typeof localization !== 'undefined' && typeof localization !== 'object' ) { |
| 442 | + throw new Error( 'localization must be an object, not a ' + typeof localization ); |
| 443 | + } |
| 444 | + if ( typeof registry[name] !== 'undefined' && typeof registry[name].script !== 'undefined' ) { |
| 445 | + throw new Error( 'module already implemeneted: ' + name ); |
| 446 | + } |
| 447 | + // Mark module as loaded |
| 448 | + registry[name].state = 'loaded'; |
| 449 | + // Attach components |
| 450 | + registry[name].script = script; |
| 451 | + if ( typeof style === 'string' ) { |
| 452 | + registry[name].style = style; |
| 453 | + } |
| 454 | + if ( typeof localization === 'object' ) { |
| 455 | + registry[name].localization = localization; |
| 456 | + } |
| 457 | + // Execute or queue callback |
| 458 | + if ( filter( ['ready'], registry[name].needs ).length == registry[name].needs.length ) { |
| 459 | + execute( name ); |
| 460 | + } else { |
| 461 | + request( registry[name].needs, function() { execute( name ); } ); |
| 462 | + } |
| 463 | + }; |
| 464 | + /** |
| 465 | + * Executes a function as soon as one or more required modules are ready |
| 466 | + * |
| 467 | + * @param mixed string or array of strings of modules names the callback needs to be ready before executing |
| 468 | + * @param function callback to execute when all needs are met |
| 469 | + */ |
| 470 | + this.using = function( needs, callback ) { |
| 471 | + // Validate input |
| 472 | + if ( typeof needs !== 'object' && typeof needs !== 'string' ) { |
| 473 | + throw new Error( 'needs must be a string or an array, not a ' + typeof needs ) |
| 474 | + } |
| 475 | + if ( typeof callback !== 'function' ) { |
| 476 | + throw new Error( 'callback must be a function, not a ' + typeof callback ) |
| 477 | + } |
| 478 | + if ( typeof needs === 'string' ) { |
| 479 | + needs = [needs]; |
| 480 | + } |
| 481 | + // Execute or queue callback |
| 482 | + if ( filter( ['ready'], needs ).length == needs.length ) { |
| 483 | + callback(); |
| 484 | + } else { |
| 485 | + request( needs, callback ); |
| 486 | + } |
| 487 | + }; |
| 488 | + |
| 489 | + /* Event Bindings */ |
| 490 | + |
| 491 | + $( document ).ready( function() { |
| 492 | + ready = true; |
| 493 | + that.work(); |
| 494 | + } ); |
| 495 | + } )(); |
| 496 | + |
| 497 | + /* Extension points */ |
| 498 | + |
| 499 | + this.utilities = {}; |
| 500 | + |
| 501 | + // Attach to window |
| 502 | + window.MediaWiki = window.mw = $.extend( 'mw' in window ? window.mw : {}, this ); |
| 503 | +} )(); |
\ No newline at end of file |
Property changes on: branches/resourceloader/phase3/resources/mediawiki/mediawiki.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 504 | + native |