diff options
Diffstat (limited to 'resources/mediawiki.special')
11 files changed, 872 insertions, 40 deletions
diff --git a/resources/mediawiki.special/mediawiki.special.block.js b/resources/mediawiki.special/mediawiki.special.block.js new file mode 100644 index 00000000..6f79929b --- /dev/null +++ b/resources/mediawiki.special/mediawiki.special.block.js @@ -0,0 +1,46 @@ +/* JavaScript for Special:Block */ + +jQuery( function( $ ) { + + var DO_INSTANT = true, + $blockTarget = $( '#mw-bi-target' ), + $anonOnlyRow = $( '#mw-input-wpHardBlock' ).closest( 'tr' ), + $enableAutoblockRow = $( '#mw-input-wpAutoBlock' ).closest( 'tr' ), + $hideUser = $( '#mw-input-wpHideUser' ).closest( 'tr' ), + $watchUser = $( '#mw-input-wpWatch' ).closest( 'tr' ); + + var updateBlockOptions = function( instant ) { + if ( !$blockTarget.length ) { + return; + } + + var blocktarget = $.trim( $blockTarget.val() ); + var isEmpty = ( blocktarget === '' ); + var isIp = mw.util.isIPv4Address( blocktarget, true ) || mw.util.isIPv6Address( blocktarget, true ); + var isIpRange = isIp && blocktarget.match( /\/\d+$/ ); + + if ( isIp && !isEmpty ) { + $enableAutoblockRow.goOut( instant ); + $hideUser.goOut( instant ); + } else { + $enableAutoblockRow.goIn( instant ); + $hideUser.goIn( instant ); + } + if ( !isIp && !isEmpty ) { + $anonOnlyRow.goOut( instant ); + } else { + $anonOnlyRow.goIn( instant ); + } + if ( isIpRange && !isEmpty ) { + $watchUser.goOut( instant ); + } else { + $watchUser.goIn( instant ); + } + }; + + // Bind functions so they're checked whenever stuff changes + $blockTarget.keyup( updateBlockOptions ); + + // Call them now to set initial state (ie. Special:Block/Foobar?wpBlockExpiry=2+hours) + updateBlockOptions( DO_INSTANT ); +}); diff --git a/resources/mediawiki.special/mediawiki.special.changeslist.css b/resources/mediawiki.special/mediawiki.special.changeslist.css new file mode 100644 index 00000000..cb4d2156 --- /dev/null +++ b/resources/mediawiki.special/mediawiki.special.changeslist.css @@ -0,0 +1,47 @@ +/** + * Styling for Special:Watchlist and Special:RecentChanges + */ + +table.mw-enhanced-rc { + border: 0; + border-spacing: 0; +} + +table.mw-enhanced-rc th, table.mw-enhanced-rc td { + padding: 0; + vertical-align: top; +} + +td.mw-enhanced-rc { + white-space: nowrap; + font-family: monospace; +} + +.mw-enhanced-rc-time { + font-family: monospace; +} + +table.mw-enhanced-rc td.mw-enhanced-rc-nested { + padding-left: 1em; +} + +/* Show/hide arrows in enhanced changeslist */ +.mw-enhanced-rc .collapsible-expander { + float: none; +} + +/* If JS is disabled, the arrow is still needed + for spacing, but ideally shouldn't be shown */ +.mw-enhanced-rc .mw-rc-openarrow { + visibility: hidden; +} + +.mw-enhanced-rc.mw-made-collapsible .mw-rc-openarrow, +.mw-enhanced-rc .mw-rc-closearrow { + visibility: visible; + display: none; +} +.mw-enhanced-rc.mw-made-collapsible .mw-collapsible-toggle-collapsed .mw-rc-openarrow, +.mw-enhanced-rc.mw-made-collapsible .mw-collapsible-toggle-expanded .mw-rc-closearrow { + display: inline; +} diff --git a/resources/mediawiki.special/mediawiki.special.css b/resources/mediawiki.special/mediawiki.special.css new file mode 100644 index 00000000..3cd97397 --- /dev/null +++ b/resources/mediawiki.special/mediawiki.special.css @@ -0,0 +1,274 @@ + +/**** Special:AllMessages ****/ +#mw-allmessagestable .allmessages-customised td.am_default { + background-color: #fcffc4; +} + +#mw-allmessagestable tr.allmessages-customised:hover td.am_default { + background-color: #faff90; +} + +#mw-allmessagestable td.am_actual { + background-color: #e2ffe2; +} + +#mw-allmessagestable tr.allmessages-customised:hover + tr.allmessages-customised td.am_actual { + background-color: #b1ffb1; +} + +/**** Special:Allpages ****/ +table.mw-allpages-table-form, table.mw-allpages-table-chunk { + width: 100%; +} +td.mw-allpages-alphaindexline { + text-align: right; +} +.mw-allpages-nav { + text-align: right; + margin-bottom: 1em; +} +table.mw-allpages-table-form tr { + vertical-align: top; +} + +/**** Special:Block ****/ +tr.mw-block-hideuser { + font-weight: bold; +} + +/**** Special:BlockList ****/ +table.mw-blocklist span.mw-usertoollinks, +span.mw-blocklist-actions{ + white-space: nowrap; + font-size: 90%; +} + +/**** Special:Contributions ****/ +.mw-uctop { + font-weight: bold; +} + +/**** Special:EmailUser ****/ +table.mw-emailuser-table { + width: 98%; +} +td#mw-emailuser-sender, +td#mw-emailuser-recipient { + font-weight: bold; +} + +/**** Special:ListGroupRights ****/ +table.mw-listgrouprights-table tr { + vertical-align: top; +} +.listgrouprights-revoked { + text-decoration: line-through; +} + +/**** Special:Prefixindex ****/ +table#mw-prefixindex-list-table, +table#mw-prefixindex-nav-table { + width: 98%; +} +td#mw-prefixindex-nav-form { + margin-bottom: 1em; + vertical-align: top; +} +.mw-prefixindex-nav { + text-align: right; +} + + +/**** Special:Search ****/ +.searchresults { +} + +.searchresults p { + margin-left: 0.4em; + margin-top: 1em; + margin-bottom: 1.2em; +} +div.searchresult { + font-size: 95%; + width: 38em; +} +.mw-search-results { + margin-left: 0.4em; +} +.mw-search-results li { + padding-bottom: 1em; + list-style: none; + list-style-image: none; +} +.mw-search-results li a { + font-size: 108%; +} +.mw-search-result-data { + color: green; + font-size: 97%; +} +.mw-search-formheader { + background-color: #f3f3f3; + margin-top: 1em; + border: 1px solid silver; +} +.mw-search-formheader div.search-types { + float: left; + padding-left: 0.25em; +} +.mw-search-formheader div.search-types ul { + margin: 0 !important; + padding: 0 !important; + list-style: none !important; +} +.mw-search-formheader div.search-types ul li { + float: left; + margin: 0; + padding: 0; +} +.mw-search-formheader div.search-types ul li a { + display: block; + padding: 0.5em; +} +.mw-search-formheader div.search-types ul li.current a { + color: #333333; + cursor: default; +} +.mw-search-formheader div.search-types ul li.current a:hover { + text-decoration: none; +} +.mw-search-formheader div.results-info { + float: right; + padding: 0.5em; + padding-right: 0.75em; +} +.mw-search-formheader div.results-info ul { + margin: 0 !important; + padding: 0 !important; + list-style: none !important; +} +.mw-search-formheader div.results-info ul li { + float: right; + margin: 0; + padding: 0; +} +fieldset#mw-searchoptions { + margin: 0; + padding: 0.5em 0.75em 0.75em 0.75em !important; + border: none; + background-color: #f9f9f9; + border: 1px solid silver !important; + border-top-width: 0 !important; +} +fieldset#mw-searchoptions legend { + display: none; +} +fieldset#mw-searchoptions h4 { + padding: 0; + margin: 0; + float: left; +} +fieldset#mw-searchoptions div#mw-search-togglebox { + float: right; +} +fieldset#mw-searchoptions div#mw-search-togglebox label { + margin-right: 0.25em; +} +fieldset#mw-searchoptions div#mw-search-togglebox input { + margin-left: 0.25em; +} +fieldset#mw-searchoptions table { + float: left; + margin-right: 3em; +} +fieldset#mw-searchoptions table td { + padding-right: 1em; +} +fieldset#mw-searchoptions div.divider { + clear: both; + border-bottom: 1px solid #DDDDDD; + padding-top: 0.5em; + margin-bottom: 0.5em; +} +td#mw-search-menu { + padding-left:6em; + font-size:85%; +} +div#mw-search-interwiki { + float: right; + width: 18em; + border: 1px solid #AAAAAA; + margin-top: 2ex; +} +div#mw-search-interwiki li { + font-size: 95%; +} +.mw-search-interwiki-more { + float: right; + font-size: 90%; +} +div#mw-search-interwiki-caption { + text-align: center; + font-weight: bold; + font-size: 95%; +} +.mw-search-interwiki-project { + font-size: 97%; + text-align: left; + padding: 0.15em 0.15em 0.2em 0.2em; + background-color: #ececec; + border-top: 1px solid #BBBBBB; +} +span.searchalttitle { + font-size: 95%; +} +div.searchdidyoumean { + font-size: 127%; + margin-top: 0.8em; + /* Note that this color won't affect the link, as desired. */ + color: #c00; +} +div.searchdidyoumean em { + font-weight: bold; +} +.searchmatch { + font-weight: bold; +} +/* Advanced PowerSearch box */ +td#mw-search-togglebox { + text-align: right; +} +table#mw-search-powertable { + width: 100%; +} +form#powersearch { + clear: both; +} + +/**** Special:Specialpages ****/ +.mw-specialpagerestricted { + font-weight: bold; +} + +.mw-specialpages-table { + margin-top: -1em; + margin-bottom: 1em; +} + +.mw-specialpages-table td { + vertical-align: top; +} + +/**** Special:Statistics ****/ +td.mw-statistics-numbers { + text-align: right; +} + +/**** Special:UserRights ****/ +.mw-userrights-disabled { + color: #888; +} +table.mw-userrights-groups * td, +table.mw-userrights-groups * th { + padding-right: 1.5em; +} diff --git a/resources/mediawiki.special/mediawiki.special.js b/resources/mediawiki.special/mediawiki.special.js new file mode 100644 index 00000000..3526cef4 --- /dev/null +++ b/resources/mediawiki.special/mediawiki.special.js @@ -0,0 +1 @@ +mw.special = {}; diff --git a/resources/mediawiki.special/mediawiki.special.movePage.js b/resources/mediawiki.special/mediawiki.special.movePage.js new file mode 100644 index 00000000..2f94cc06 --- /dev/null +++ b/resources/mediawiki.special/mediawiki.special.movePage.js @@ -0,0 +1,5 @@ +/* JavaScript for Special:MovePage */ + +jQuery( function( $ ) { + $( '#wpReason' ).byteLimit(); +}); diff --git a/resources/mediawiki.special/mediawiki.special.preferences.js b/resources/mediawiki.special/mediawiki.special.preferences.js index 1775bec4..2e07e7f1 100644 --- a/resources/mediawiki.special/mediawiki.special.preferences.js +++ b/resources/mediawiki.special/mediawiki.special.preferences.js @@ -2,44 +2,61 @@ * JavaScript for Special:Preferences */ ( function( $, mw ) { - $( '#prefsubmit' ).attr( 'id', 'prefcontrol' ); -$( '#preferences' ) +var $preftoc = $('<ul id="preftoc"></ul>'); +var $preferences = $( '#preferences' ) .addClass( 'jsprefs' ) - .before( $( '<ul id="preftoc"></ul>' ) ) - .children( 'fieldset' ) - .hide() - .addClass( 'prefsection' ) - .children( 'legend' ) - .addClass( 'mainLegend' ) - .each( function( i ) { - $(this).parent().attr( 'id', 'prefsection-' + i ); - if ( i === 0 ) { - $(this).parent().show(); - } - $( '#preftoc' ).append( - $( '<li></li>' ) - .addClass( i === 0 ? 'selected' : null ) - .append( - $( '<a></a>') - .text( $(this).text() ) - .attr( 'href', '#prefsection-' + i ) - .mousedown( function( e ) { - $(this).parent().parent().find( 'li' ).removeClass( 'selected' ); - $(this).parent().addClass( 'selected' ); - e.preventDefault(); - return false; - } ) - .click( function( e ) { - $( '#preferences > fieldset' ).hide(); - $( '#prefsection-' + i ).show(); - e.preventDefault(); - return false; - } ) - ) - ); - } - ); + .before( $preftoc ); + +var $fieldsets = $preferences.children( 'fieldset' ) + .hide() + .addClass( 'prefsection' ); + +var $legends = $fieldsets.children( 'legend' ) + .addClass( 'mainLegend' ); + +// Populate the prefToc +$legends.each( function( i, legend ) { + var $legend = $(legend); + if ( i === 0 ) { + $legend.parent().show(); + } + var ident = $legend.parent().attr( 'id' ); + + var $li = $( '<li/>', { + 'class' : ( i === 0 ) ? 'selected' : null + }); + var $a = $( '<a/>', { + text : $legend.text(), + id : ident.replace( 'mw-prefsection', 'preftab' ), + href : '#' + ident + }).click( function( e ) { + e.preventDefault(); + // Handle hash manually to prevent jumping + // Therefore save and restore scrollTop to prevent jumping + var scrollTop = $(window).scrollTop(); + window.location.hash = $(this).attr('href'); + $(window).scrollTop(scrollTop); + + $preftoc.find( 'li' ).removeClass( 'selected' ); + $(this).parent().addClass( 'selected' ); + $( '#preferences > fieldset' ).hide(); + $( '#' + ident ).show(); + }); + $li.append( $a ); + $preftoc.append( $li ); +} ); + +// If we've reloaded the page or followed an open-in-new-window, +// make the selected tab visible. +// On document ready: +$( function() { + var hash = window.location.hash; + if( hash.match( /^#mw-prefsection-[\w-]+/ ) ) { + var $tab = $( hash.replace( 'mw-prefsection', 'preftab' ) ); + $tab.click(); + } +} ); /** * Given an email validity status (true, false, null) update the label CSS class @@ -74,4 +91,85 @@ $( '#mw-input-wpemailaddress' ).one( 'blur', function() { updateMailValidityLabel( $(this).val() ); } ); } ); -} )( jQuery, mediaWiki );
\ No newline at end of file + + + +/** +* Timezone functions. +* Guesses Timezone from browser and updates fields onchange +*/ + +var $tzSelect = $( '#mw-input-wptimecorrection' ); +var $tzTextbox = $( '#mw-input-wptimecorrection-other' ); + +var $localtimeHolder = $( '#wpLocalTime' ); +var servertime = parseInt( $( 'input[name=wpServerTime]' ).val(), 10 ); +var minuteDiff = 0; + +var minutesToHours = function( min ) { + var tzHour = Math.floor( Math.abs( min ) / 60 ); + var tzMin = Math.abs( min ) % 60; + var tzString = ( ( min >= 0 ) ? '' : '-' ) + ( ( tzHour < 10 ) ? '0' : '' ) + tzHour + + ':' + ( ( tzMin < 10 ) ? '0' : '' ) + tzMin; + return tzString; +}; + +var hoursToMinutes = function( hour ) { + var arr = hour.split( ':' ); + arr[0] = parseInt( arr[0], 10 ); + + var minutes; + if ( arr.length == 1 ) { + // Specification is of the form [-]XX + minutes = arr[0] * 60; + } else { + // Specification is of the form [-]XX:XX + minutes = Math.abs( arr[0] ) * 60 + parseInt( arr[1], 10 ); + if ( arr[0] < 0 ) { + minutes *= -1; + } + } + // Gracefully handle non-numbers. + if ( isNaN( minutes ) ) { + return 0; + } else { + return minutes; + } +}; + +var updateTimezoneSelection = function() { + var type = $tzSelect.val(); + if ( type == 'guess' ) { + // Get browser timezone & fill it in + minuteDiff = -new Date().getTimezoneOffset(); + $tzTextbox.val( minutesToHours( minuteDiff ) ); + $tzSelect.val( 'other' ); + $tzTextbox.get( 0 ).disabled = false; + } else if ( type == 'other' ) { + // Grab data from the textbox, parse it. + minuteDiff = hoursToMinutes( $tzTextbox.val() ); + } else { + // Grab data from the $tzSelect value + minuteDiff = parseInt( type.split( '|' )[1], 10 ) || 0; + $tzTextbox.val( minutesToHours( minuteDiff ) ); + } + + // Determine local time from server time and minutes difference, for display. + var localTime = servertime + minuteDiff; + + // Bring time within the [0,1440) range. + while ( localTime < 0 ) { + localTime += 1440; + } + while ( localTime >= 1440 ) { + localTime -= 1440; + } + $localtimeHolder.text( minutesToHours( localTime ) ); +}; + +if ( $tzSelect.length && $tzTextbox.length ) { + $tzSelect.change( function() { updateTimezoneSelection(); } ); + $tzTextbox.blur( function() { updateTimezoneSelection(); } ); + updateTimezoneSelection(); +} +} )( jQuery, mediaWiki ); diff --git a/resources/mediawiki.special/mediawiki.special.recentchanges.js b/resources/mediawiki.special/mediawiki.special.recentchanges.js new file mode 100644 index 00000000..7e284fbd --- /dev/null +++ b/resources/mediawiki.special/mediawiki.special.recentchanges.js @@ -0,0 +1,39 @@ +/* JavaScript for Special:RecentChanges */ +( function( $ ) { + + var checkboxes = [ 'nsassociated', 'nsinvert' ]; + + /** + * @var select {jQuery} + */ + var $select = null; + + var rc = mw.special.recentchanges = { + + /** + * Handler to disable/enable the namespace selector checkboxes when the + * special 'all' namespace is selected/unselected respectively. + */ + updateCheckboxes: function() { + // The option element for the 'all' namespace has an empty value + var isAllNS = ('' === $select.find('option:selected').val() ); + + // Iterates over checkboxes and propagate the selected option + $.each( checkboxes, function( i, id ) { + $( '#' + id ).attr( 'disabled', isAllNS ); + }); + }, + + init: function() { + // Populate + $select = $( '#namespace' ); + + // Bind to change event, and trigger once to set the initial state of the checkboxes. + $select.change( rc.updateCheckboxes ).change(); + } + }; + + // Run when document is ready + $( rc.init ); + +})( jQuery ); diff --git a/resources/mediawiki.special/mediawiki.special.search.css b/resources/mediawiki.special/mediawiki.special.search.css new file mode 100644 index 00000000..89d55b0b --- /dev/null +++ b/resources/mediawiki.special/mediawiki.special.search.css @@ -0,0 +1,14 @@ +/** + * Fixes sister projects box moving down the extract + * of the first result (bug #16886). + * It only happens when the window is small and + * This changes slightly the layout for big screens + * where there was space for the extracts and the + * sister projects and thus it showed like in any + * other browser. + * + * This will only affect IE 7 and lower + */ +.searchresult { + display: inline !ie; +} diff --git a/resources/mediawiki.special/mediawiki.special.search.js b/resources/mediawiki.special/mediawiki.special.search.js index d4317188..bac27fc6 100644 --- a/resources/mediawiki.special/mediawiki.special.search.js +++ b/resources/mediawiki.special/mediawiki.special.search.js @@ -1,11 +1,37 @@ /* - * JavaScript for Specical:Search + * JavaScript for Special:Search */ -( function( $, mw ) { +jQuery( function( $ ) { // Emulate HTML5 autofocus behavior in non HTML5 compliant browsers if ( !( 'autofocus' in document.createElement( 'input' ) ) ) { $( 'input[autofocus]:first' ).focus(); } -} )( jQuery, mediaWiki );
\ No newline at end of file +// Bind check all/none button +var $checkboxes = $('#powersearch input[id^=mw-search-ns]'); +$('#mw-search-toggleall').click( function() { + $checkboxes.prop("checked", true); +} ); +$('#mw-search-togglenone').click( function() { + $checkboxes.prop("checked", false); +} ); + +// Change the header search links to what user entered +var headerLinks = $('.search-types a'); +$('#searchText, #powerSearchText').change(function() { + var searchterm = $(this).val(); + headerLinks.each( function() { + var parts = this.href.split( 'search=' ); + var lastpart = ''; + var prefix = 'search='; + if( parts.length > 1 && parts[1].indexOf('&') >= 0 ) { + lastpart = parts[1].substring( parts[1].indexOf('&') ); + } else { + prefix = '&search='; + } + this.href = parts[0] + prefix + encodeURIComponent( searchterm ) + lastpart; + }); +}).trigger('change'); + +} );
\ No newline at end of file diff --git a/resources/mediawiki.special/mediawiki.special.undelete.js b/resources/mediawiki.special/mediawiki.special.undelete.js new file mode 100644 index 00000000..33b80275 --- /dev/null +++ b/resources/mediawiki.special/mediawiki.special.undelete.js @@ -0,0 +1,10 @@ +/* + * JavaScript for Specical:Undelete + */ +jQuery( document ).ready( function( $ ) { + $( '#mw-undelete-invert' ).click( function( e ) { + e.preventDefault(); + $( '#undelete' ).find( 'input:checkbox' ) + .prop( 'checked', function( i, val ) { return !val; } ); + } ); +} ); diff --git a/resources/mediawiki.special/mediawiki.special.upload.js b/resources/mediawiki.special/mediawiki.special.upload.js new file mode 100644 index 00000000..4a8622f5 --- /dev/null +++ b/resources/mediawiki.special/mediawiki.special.upload.js @@ -0,0 +1,272 @@ +/* + * JavaScript for Special:Upload + * Note that additional code still lives in skins/common/upload.js + */ + +/** + * Add a preview to the upload form + */ +jQuery( function( $ ) { + /** + * Is the FileAPI available with sufficient functionality? + */ + function hasFileAPI(){ + return typeof window.FileReader !== 'undefined'; + } + + /** + * Check if this is a recognizable image type... + * Also excludes files over 10M to avoid going insane on memory usage. + * + * @todo is there a way we can ask the browser what's supported in <img>s? + * @todo put SVG back after working around Firefox 7 bug <https://bugzilla.wikimedia.org/show_bug.cgi?id=31643> + * + * @param {File} file + * @return boolean + */ + function fileIsPreviewable( file ) { + var known = ['image/png', 'image/gif', 'image/jpeg'], + tooHuge = 10 * 1024 * 1024; + return ( $.inArray( file.type, known ) !== -1 ) && file.size > 0 && file.size < tooHuge; + } + + /** + * Show a thumbnail preview of PNG, JPEG, GIF, and SVG files prior to upload + * in browsers supporting HTML5 FileAPI. + * + * As of this writing, known good: + * - Firefox 3.6+ + * - Chrome 7.something + * + * @todo check file size limits and warn of likely failures + * + * @param {File} file + */ + function showPreview( file ) { + var previewSize = 180, + thumb = $( '<div id="mw-upload-thumbnail" class="thumb tright">' + + '<div class="thumbinner">' + + '<canvas width="' + previewSize + '" height="' + previewSize + '" ></canvas>' + + '<div class="thumbcaption"><div class="filename"></div><div class="fileinfo"></div></div>' + + '</div>' + + '</div>' ); + thumb.find( '.filename' ).text( file.name ).end() + .find( '.fileinfo' ).text( prettySize( file.size ) ).end(); + + var ctx = thumb.find( 'canvas' )[0].getContext( '2d' ), + spinner = new Image(); + spinner.onload = function() { + ctx.drawImage( spinner, (previewSize - spinner.width) / 2, + (previewSize - spinner.height) / 2 ); + }; + spinner.src = mw.config.get( 'wgScriptPath' ) + '/skins/common/images/spinner.gif'; + $( '#mw-htmlform-source' ).parent().prepend( thumb ); + + var meta; + fetchPreview( file, function( dataURL ) { + var img = new Image(), + rotation = 0; + + if ( meta && meta.tiff && meta.tiff.Orientation ) { + rotation = (360 - function () { + // See includes/media/Bitmap.php + switch ( meta.tiff.Orientation.value ) { + case 8: + return 90; + case 3: + return 180; + case 6: + return 270; + default: + return 0; + } + }() ) % 360; + } + + img.onload = function() { + var width, height, x, y, dx, dy, logicalWidth, logicalHeight; + // Fit the image within the previewSizexpreviewSize box + if ( img.width > img.height ) { + width = previewSize; + height = img.height / img.width * previewSize; + } else { + height = previewSize; + width = img.width / img.height * previewSize; + } + // Determine the offset required to center the image + dx = (180 - width) / 2; + dy = (180 - height) / 2; + switch ( rotation ) { + // If a rotation is applied, the direction of the axis + // changes as well. You can derive the values below by + // drawing on paper an axis system, rotate it and see + // where the positive axis direction is + case 0: + x = dx; + y = dy; + logicalWidth = img.width; + logicalHeight = img.height; + break; + case 90: + + x = dx; + y = dy - previewSize; + logicalWidth = img.height; + logicalHeight = img.width; + break; + case 180: + x = dx - previewSize; + y = dy - previewSize; + logicalWidth = img.width; + logicalHeight = img.height; + break; + case 270: + x = dx - previewSize; + y = dy; + logicalWidth = img.height; + logicalHeight = img.width; + break; + } + + ctx.clearRect( 0, 0, 180, 180 ); + ctx.rotate( rotation / 180 * Math.PI ); + ctx.drawImage( img, x, y, width, height ); + + // Image size + var info = mw.msg( 'widthheight', logicalWidth, logicalHeight ) + + ', ' + prettySize( file.size ); + $( '#mw-upload-thumbnail .fileinfo' ).text( info ); + }; + img.src = dataURL; + }, mw.config.get( 'wgFileCanRotate' ) ? function ( data ) { + try { + meta = mw.libs.jpegmeta( data, file.fileName ); + meta._binary_data = null; + } catch ( e ) { + meta = null; + } + } : null ); + } + + /** + * Start loading a file into memory; when complete, pass it as a + * data URL to the callback function. If the callbackBinary is set it will + * first be read as binary and afterwards as data URL. Useful if you want + * to do preprocessing on the binary data first. + * + * @param {File} file + * @param {function} callback + * @param {function} callbackBinary + */ + function fetchPreview( file, callback, callbackBinary ) { + var reader = new FileReader(); + reader.onload = function() { + if ( callbackBinary ) { + callbackBinary( reader.result ); + reader.onload = function() { + callback( reader.result ); + }; + reader.readAsDataURL( file ); + } else { + callback( reader.result ); + } + }; + if ( callbackBinary ) { + reader.readAsBinaryString( file ); + } else { + reader.readAsDataURL( file ); + } + } + + /** + * Format a file size attractively. + * @todo match numeric formatting + * + * @param {number} s + * @return string + */ + function prettySize( s ) { + var sizes = ['size-bytes', 'size-kilobytes', 'size-megabytes', 'size-gigabytes']; + while ( s >= 1024 && sizes.length > 1 ) { + s /= 1024; + sizes = sizes.slice( 1 ); + } + return mw.msg( sizes[0], Math.round( s ) ); + } + + /** + * Clear the file upload preview area. + */ + function clearPreview() { + $( '#mw-upload-thumbnail' ).remove(); + } + + /** + * Check if the file does not exceed the maximum size + */ + function checkMaxUploadSize( file ) { + function getMaxUploadSize( type ) { + var sizes = mw.config.get( 'wgMaxUploadSize' ); + if ( sizes[type] !== undefined ) { + return sizes[type]; + } + return sizes['*']; + } + $( '.mw-upload-source-error' ).remove(); + + var maxSize = getMaxUploadSize( 'file' ); + if ( file.size > maxSize ) { + var error = $( '<p class="error mw-upload-source-error" id="wpSourceTypeFile-error">' + + mw.message( 'largefileserver', file.size, maxSize ).escaped() + '</p>' ); + $( '#wpUploadFile' ).after( error ); + return false; + } + return true; + } + + + /** + * Initialization + */ + if ( hasFileAPI() ) { + // Update thumbnail when the file selection control is updated. + $( '#wpUploadFile' ).change( function() { + clearPreview(); + if ( this.files && this.files.length ) { + // Note: would need to be updated to handle multiple files. + var file = this.files[0]; + + if ( !checkMaxUploadSize( file ) ) { + return; + } + + if ( fileIsPreviewable( file ) ) { + showPreview( file ); + } + } + } ); + } +} ); + +/** + * Disable all upload source fields except the selected one + */ +jQuery( function ( $ ) { + var rows = $( '.mw-htmlform-field-UploadSourceField' ); + for ( var i = rows.length; i; i-- ) { + var row = rows[i - 1]; + $( 'input[name="wpSourceType"]', row ).change( function () { + var currentRow = row; // Store current row in our own scope + return function () { + $( '.mw-upload-source-error' ).remove(); + if ( this.checked ) { + // Disable all inputs + $( 'input[name!="wpSourceType"]', rows ).attr( 'disabled', true ); + // Re-enable the current one + $( 'input', currentRow ).attr( 'disabled', false ); + } + }; + }() ); + } +} ); + |