From: Bartosz DziewoƄski Date: Fri, 15 Sep 2017 18:45:24 +0000 (+0200) Subject: Split mediawiki.special.preferences into separate file for each function X-Git-Tag: 1.31.0-rc.0~1982^2~1 X-Git-Url: https://git.cyclocoop.org/%7B%24admin_url%7Dmembres/fiche.php?a=commitdiff_plain;h=51a4c783e8900fdfabc353573991367ab63e11cf;p=lhc%2Fweb%2Fwiklou.git Split mediawiki.special.preferences into separate file for each function This JavaScript code does four things: * Convert a simple form into one with tab navigation * Convert a simple message box into pretty notifications * Prevent browser window from being closed with unsaved changes to preferences * Add some enhancements to the timezone field I will be making some large changes here for T117781 and having everything mixed in one file was making that difficult. I avoided making any cleanup changes to make it easier to review this change. Change-Id: I5e31a661177002e40708614011c1f7e08ec4f44d --- diff --git a/resources/Resources.php b/resources/Resources.php index 10786dacaa..52dccdbc4c 100644 --- a/resources/Resources.php +++ b/resources/Resources.php @@ -2092,7 +2092,12 @@ return [ 'styles' => 'resources/src/mediawiki.special/mediawiki.special.pagesWithProp.css', ], 'mediawiki.special.preferences' => [ - 'scripts' => 'resources/src/mediawiki.special/mediawiki.special.preferences.js', + 'scripts' => [ + 'resources/src/mediawiki.special/mediawiki.special.preferences.confirmClose.js', + 'resources/src/mediawiki.special/mediawiki.special.preferences.convertmessagebox.js', + 'resources/src/mediawiki.special/mediawiki.special.preferences.tabs.js', + 'resources/src/mediawiki.special/mediawiki.special.preferences.timezone.js', + ], 'messages' => [ 'prefs-tabs-navigation-hint', 'prefswarning-warning', diff --git a/resources/src/mediawiki.special/mediawiki.special.preferences.confirmClose.js b/resources/src/mediawiki.special/mediawiki.special.preferences.confirmClose.js new file mode 100644 index 0000000000..45df37ffd2 --- /dev/null +++ b/resources/src/mediawiki.special/mediawiki.special.preferences.confirmClose.js @@ -0,0 +1,63 @@ +/*! + * JavaScript for Special:Preferences: Enable save button and prevent the window being accidentally + * closed when any form field is changed. + */ +( function ( mw, $ ) { + $( function () { + var allowCloseWindow; + + // Check if all of the form values are unchanged + function isPrefsChanged() { + var inputs = $( '#mw-prefs-form :input[name]' ), + input, $input, inputType, + index, optIndex, + opt; + + for ( index = 0; index < inputs.length; index++ ) { + input = inputs[ index ]; + $input = $( input ); + + // Different types of inputs have different methods for accessing defaults + if ( $input.is( 'select' ) ) { + // has defaultValue or defaultChecked + inputType = input.type; + if ( inputType === 'radio' || inputType === 'checkbox' ) { + if ( input.checked !== input.defaultChecked ) { + return true; + } + } else if ( input.value !== input.defaultValue ) { + return true; + } + } + } + + return false; + } + + // Disable the button to save preferences unless preferences have changed + // Check if preferences have been changed before JS has finished loading + if ( !isPrefsChanged() ) { + $( '#prefcontrol' ).prop( 'disabled', true ); + $( '#preferences > fieldset' ).one( 'change keydown mousedown', function () { + $( '#prefcontrol' ).prop( 'disabled', false ); + } ); + } + + // Set up a message to notify users if they try to leave the page without + // saving. + allowCloseWindow = mw.confirmCloseWindow( { + test: isPrefsChanged, + message: mw.msg( 'prefswarning-warning', mw.msg( 'saveprefs' ) ), + namespace: 'prefswarning' + } ); + $( '#mw-prefs-form' ).submit( $.proxy( allowCloseWindow, 'release' ) ); + $( '#mw-prefs-restoreprefs' ).click( $.proxy( allowCloseWindow, 'release' ) ); + } ); +}( mediaWiki, jQuery ) ); diff --git a/resources/src/mediawiki.special/mediawiki.special.preferences.convertmessagebox.js b/resources/src/mediawiki.special/mediawiki.special.preferences.convertmessagebox.js new file mode 100644 index 0000000000..cee7382963 --- /dev/null +++ b/resources/src/mediawiki.special/mediawiki.special.preferences.convertmessagebox.js @@ -0,0 +1,9 @@ +/*! + * JavaScript for Special:Preferences: Check for successbox to replace with notifications. + */ +( function ( mw, $ ) { + $( function () { + var convertmessagebox = require( 'mediawiki.notification.convertmessagebox' ); + convertmessagebox(); + } ); +}( mediaWiki, jQuery ) ); diff --git a/resources/src/mediawiki.special/mediawiki.special.preferences.js b/resources/src/mediawiki.special/mediawiki.special.preferences.js deleted file mode 100644 index b86f21838c..0000000000 --- a/resources/src/mediawiki.special/mediawiki.special.preferences.js +++ /dev/null @@ -1,305 +0,0 @@ -/*! - * JavaScript for Special:Preferences - */ -( function ( mw, $ ) { - $( function () { - var $preftoc, $preferences, $fieldsets, labelFunc, previousTab, - $tzSelect, $tzTextbox, $localtimeHolder, servertime, allowCloseWindow, - convertmessagebox = require( 'mediawiki.notification.convertmessagebox' ); - - labelFunc = function () { - return this.id.replace( /^mw-prefsection/g, 'preftab' ); - }; - - $preftoc = $( '#preftoc' ); - $preferences = $( '#preferences' ); - - $fieldsets = $preferences.children( 'fieldset' ) - .attr( { - role: 'tabpanel', - 'aria-labelledby': labelFunc - } ); - $fieldsets.not( '#mw-prefsection-personal' ) - .hide() - .attr( 'aria-hidden', 'true' ); - - // T115692: The following is kept for backwards compatibility with older skins - $preferences.addClass( 'jsprefs' ); - $fieldsets.addClass( 'prefsection' ); - $fieldsets.children( 'legend' ).addClass( 'mainLegend' ); - - // Make sure the accessibility tip is selectable so that screen reader users take notice, - // but hide it per default to reduce interface clutter. Also make sure it becomes visible - // when selected. Similar to jquery.mw-jump - $( '
' ).addClass( 'mw-navigation-hint' ) - .text( mw.msg( 'prefs-tabs-navigation-hint' ) ) - .attr( 'tabIndex', 0 ) - .on( 'focus blur', function ( e ) { - if ( e.type === 'blur' || e.type === 'focusout' ) { - $( this ).css( 'height', '0' ); - } else { - $( this ).css( 'height', 'auto' ); - } - } ).insertBefore( $preftoc ); - - /** - * It uses document.getElementById for security reasons (HTML injections in $()). - * - * @ignore - * @param {string} name the name of a tab without the prefix ("mw-prefsection-") - * @param {string} [mode] A hash will be set according to the current - * open section. Set mode 'noHash' to surpress this. - */ - function switchPrefTab( name, mode ) { - var $tab, scrollTop; - // Handle hash manually to prevent jumping, - // therefore save and restore scrollTop to prevent jumping. - scrollTop = $( window ).scrollTop(); - if ( mode !== 'noHash' ) { - location.hash = '#mw-prefsection-' + name; - } - $( window ).scrollTop( scrollTop ); - - $preftoc.find( 'li' ).removeClass( 'selected' ) - .find( 'a' ).attr( { - tabIndex: -1, - 'aria-selected': 'false' - } ); - - $tab = $( document.getElementById( 'preftab-' + name ) ); - if ( $tab.length ) { - $tab.attr( { - tabIndex: 0, - 'aria-selected': 'true' - } ).focus() - .parent().addClass( 'selected' ); - - $preferences.children( 'fieldset' ).hide().attr( 'aria-hidden', 'true' ); - $( document.getElementById( 'mw-prefsection-' + name ) ).show().attr( 'aria-hidden', 'false' ); - } - } - - // Check for successbox to replace with notifications - convertmessagebox(); - - // Enable keyboard users to use left and right keys to switch tabs - $preftoc.on( 'keydown', function ( event ) { - var keyLeft = 37, - keyRight = 39, - $el; - - if ( event.keyCode === keyLeft ) { - $el = $( '#preftoc li.selected' ).prev().find( 'a' ); - } else if ( event.keyCode === keyRight ) { - $el = $( '#preftoc li.selected' ).next().find( 'a' ); - } else { - return; - } - if ( $el.length > 0 ) { - switchPrefTab( $el.attr( 'href' ).replace( '#mw-prefsection-', '' ) ); - } - } ); - - // Jump to correct section as indicated by the hash. - // This function is called onload and onhashchange. - function detectHash() { - var hash = location.hash, - matchedElement, parentSection; - if ( hash.match( /^#mw-prefsection-[\w]+$/ ) ) { - mw.storage.session.remove( 'mwpreferences-prevTab' ); - switchPrefTab( hash.replace( '#mw-prefsection-', '' ) ); - } else if ( hash.match( /^#mw-[\w-]+$/ ) ) { - matchedElement = document.getElementById( hash.slice( 1 ) ); - parentSection = $( matchedElement ).parent().closest( '[id^="mw-prefsection-"]' ); - if ( parentSection.length ) { - mw.storage.session.remove( 'mwpreferences-prevTab' ); - // Switch to proper tab and scroll to selected item. - switchPrefTab( parentSection.attr( 'id' ).replace( 'mw-prefsection-', '' ), 'noHash' ); - matchedElement.scrollIntoView(); - } - } - } - - // In browsers that support the onhashchange event we will not bind click - // handlers and instead let the browser do the default behavior (clicking the - // will naturally set the hash, handled by onhashchange. - // But other things that change the hash will also be caught (e.g. using - // the Back and Forward browser navigation). - // Note the special check for IE "compatibility" mode. - if ( 'onhashchange' in window && - ( document.documentMode === undefined || document.documentMode >= 8 ) - ) { - $( window ).on( 'hashchange', function () { - var hash = location.hash; - if ( hash.match( /^#mw-[\w-]+/ ) ) { - detectHash(); - } else if ( hash === '' ) { - switchPrefTab( 'personal', 'noHash' ); - } - } ) - // Run the function immediately to select the proper tab on startup. - .trigger( 'hashchange' ); - // In older browsers we'll bind a click handler as fallback. - // We must not have onhashchange *and* the click handlers, otherwise - // the click handler calls switchPrefTab() which sets the hash value, - // which triggers onhashchange and calls switchPrefTab() again. - } else { - $preftoc.on( 'click', 'li a', function ( e ) { - switchPrefTab( $( this ).attr( 'href' ).replace( '#mw-prefsection-', '' ) ); - e.preventDefault(); - } ); - // If we've reloaded the page or followed an open-in-new-window, - // make the selected tab visible. - detectHash(); - } - - // Timezone functions. - // Guesses Timezone from browser and updates fields onchange. - - $tzSelect = $( '#mw-input-wptimecorrection' ); - $tzTextbox = $( '#mw-input-wptimecorrection-other' ); - $localtimeHolder = $( '#wpLocalTime' ); - servertime = parseInt( $( 'input[name="wpServerTime"]' ).val(), 10 ); - - function minutesToHours( min ) { - var tzHour = Math.floor( Math.abs( min ) / 60 ), - tzMin = Math.abs( min ) % 60, - tzString = ( ( min >= 0 ) ? '' : '-' ) + ( ( tzHour < 10 ) ? '0' : '' ) + tzHour + - ':' + ( ( tzMin < 10 ) ? '0' : '' ) + tzMin; - return tzString; - } - - function hoursToMinutes( hour ) { - var minutes, - arr = hour.split( ':' ); - - arr[ 0 ] = parseInt( arr[ 0 ], 10 ); - - if ( arr.length === 1 ) { - // Specification is of the form [-]XX - minutes = arr[ 0 ] * 60; - } else { - // Specification is of the form [-]XX:XX - minutes = Math.abs( arr[ 0 ] ) * 60 + parseInt( arr[ 1 ], 10 ); - if ( arr[ 0 ] < 0 ) { - minutes *= -1; - } - } - // Gracefully handle non-numbers. - if ( isNaN( minutes ) ) { - return 0; - } else { - return minutes; - } - } - - function updateTimezoneSelection() { - var minuteDiff, localTime, - type = $tzSelect.val(); - - if ( type === 'other' ) { - // User specified time zone manually in - // Grab data from the textbox, parse it. - minuteDiff = hoursToMinutes( $tzTextbox.val() ); - } else { - // Time zone not manually specified by user - if ( type === 'guess' ) { - // Get browser timezone & fill it in - minuteDiff = -( new Date().getTimezoneOffset() ); - $tzTextbox.val( minutesToHours( minuteDiff ) ); - $tzSelect.val( 'other' ); - $tzTextbox.prop( 'disabled', false ); - } else { - // Grab data from the $tzSelect value - minuteDiff = parseInt( type.split( '|' )[ 1 ], 10 ) || 0; - $tzTextbox.val( minutesToHours( minuteDiff ) ); - } - - // Set defaultValue prop on the generated box so we don't trigger the - // unsaved preferences check - $tzTextbox.prop( 'defaultValue', $tzTextbox.val() ); - } - - // Determine local time from server time and minutes difference, for display. - localTime = servertime + minuteDiff; - - // Bring time within the [0,1440) range. - localTime = ( ( localTime % 1440 ) + 1440 ) % 1440; - - $localtimeHolder.text( mw.language.convertNumber( minutesToHours( localTime ) ) ); - } - - if ( $tzSelect.length && $tzTextbox.length ) { - $tzSelect.change( updateTimezoneSelection ); - $tzTextbox.blur( updateTimezoneSelection ); - updateTimezoneSelection(); - } - - // Restore the active tab after saving the preferences - previousTab = mw.storage.session.get( 'mwpreferences-prevTab' ); - if ( previousTab ) { - switchPrefTab( previousTab, 'noHash' ); - // Deleting the key, the tab states should be reset until we press Save - mw.storage.session.remove( 'mwpreferences-prevTab' ); - } - - $( '#mw-prefs-form' ).on( 'submit', function () { - var value = $( $preftoc ).find( 'li.selected a' ).attr( 'id' ).replace( 'preftab-', '' ); - mw.storage.session.set( 'mwpreferences-prevTab', value ); - } ); - - // Check if all of the form values are unchanged - function isPrefsChanged() { - var inputs = $( '#mw-prefs-form :input[name]' ), - input, $input, inputType, - index, optIndex, - opt; - - for ( index = 0; index < inputs.length; index++ ) { - input = inputs[ index ]; - $input = $( input ); - - // Different types of inputs have different methods for accessing defaults - if ( $input.is( 'select' ) ) { - // has defaultValue or defaultChecked - inputType = input.type; - if ( inputType === 'radio' || inputType === 'checkbox' ) { - if ( input.checked !== input.defaultChecked ) { - return true; - } - } else if ( input.value !== input.defaultValue ) { - return true; - } - } - } - - return false; - } - - // Disable the button to save preferences unless preferences have changed - // Check if preferences have been changed before JS has finished loading - if ( !isPrefsChanged() ) { - $( '#prefcontrol' ).prop( 'disabled', true ); - $( '#preferences > fieldset' ).one( 'change keydown mousedown', function () { - $( '#prefcontrol' ).prop( 'disabled', false ); - } ); - } - - // Set up a message to notify users if they try to leave the page without - // saving. - allowCloseWindow = mw.confirmCloseWindow( { - test: isPrefsChanged, - message: mw.msg( 'prefswarning-warning', mw.msg( 'saveprefs' ) ), - namespace: 'prefswarning' - } ); - $( '#mw-prefs-form' ).submit( $.proxy( allowCloseWindow, 'release' ) ); - $( '#mw-prefs-restoreprefs' ).click( $.proxy( allowCloseWindow, 'release' ) ); - } ); -}( mediaWiki, jQuery ) ); diff --git a/resources/src/mediawiki.special/mediawiki.special.preferences.tabs.js b/resources/src/mediawiki.special/mediawiki.special.preferences.tabs.js new file mode 100644 index 0000000000..dcfad271d1 --- /dev/null +++ b/resources/src/mediawiki.special/mediawiki.special.preferences.tabs.js @@ -0,0 +1,165 @@ +/*! + * JavaScript for Special:Preferences: Tab navigation. + */ +( function ( mw, $ ) { + $( function () { + var $preftoc, $preferences, $fieldsets, labelFunc, previousTab; + + labelFunc = function () { + return this.id.replace( /^mw-prefsection/g, 'preftab' ); + }; + + $preftoc = $( '#preftoc' ); + $preferences = $( '#preferences' ); + + $fieldsets = $preferences.children( 'fieldset' ) + .attr( { + role: 'tabpanel', + 'aria-labelledby': labelFunc + } ); + $fieldsets.not( '#mw-prefsection-personal' ) + .hide() + .attr( 'aria-hidden', 'true' ); + + // T115692: The following is kept for backwards compatibility with older skins + $preferences.addClass( 'jsprefs' ); + $fieldsets.addClass( 'prefsection' ); + $fieldsets.children( 'legend' ).addClass( 'mainLegend' ); + + // Make sure the accessibility tip is selectable so that screen reader users take notice, + // but hide it per default to reduce interface clutter. Also make sure it becomes visible + // when selected. Similar to jquery.mw-jump + $( '
' ).addClass( 'mw-navigation-hint' ) + .text( mw.msg( 'prefs-tabs-navigation-hint' ) ) + .attr( 'tabIndex', 0 ) + .on( 'focus blur', function ( e ) { + if ( e.type === 'blur' || e.type === 'focusout' ) { + $( this ).css( 'height', '0' ); + } else { + $( this ).css( 'height', 'auto' ); + } + } ).insertBefore( $preftoc ); + + /** + * It uses document.getElementById for security reasons (HTML injections in $()). + * + * @ignore + * @param {string} name the name of a tab without the prefix ("mw-prefsection-") + * @param {string} [mode] A hash will be set according to the current + * open section. Set mode 'noHash' to surpress this. + */ + function switchPrefTab( name, mode ) { + var $tab, scrollTop; + // Handle hash manually to prevent jumping, + // therefore save and restore scrollTop to prevent jumping. + scrollTop = $( window ).scrollTop(); + if ( mode !== 'noHash' ) { + location.hash = '#mw-prefsection-' + name; + } + $( window ).scrollTop( scrollTop ); + + $preftoc.find( 'li' ).removeClass( 'selected' ) + .find( 'a' ).attr( { + tabIndex: -1, + 'aria-selected': 'false' + } ); + + $tab = $( document.getElementById( 'preftab-' + name ) ); + if ( $tab.length ) { + $tab.attr( { + tabIndex: 0, + 'aria-selected': 'true' + } ).focus() + .parent().addClass( 'selected' ); + + $preferences.children( 'fieldset' ).hide().attr( 'aria-hidden', 'true' ); + $( document.getElementById( 'mw-prefsection-' + name ) ).show().attr( 'aria-hidden', 'false' ); + } + } + + // Enable keyboard users to use left and right keys to switch tabs + $preftoc.on( 'keydown', function ( event ) { + var keyLeft = 37, + keyRight = 39, + $el; + + if ( event.keyCode === keyLeft ) { + $el = $( '#preftoc li.selected' ).prev().find( 'a' ); + } else if ( event.keyCode === keyRight ) { + $el = $( '#preftoc li.selected' ).next().find( 'a' ); + } else { + return; + } + if ( $el.length > 0 ) { + switchPrefTab( $el.attr( 'href' ).replace( '#mw-prefsection-', '' ) ); + } + } ); + + // Jump to correct section as indicated by the hash. + // This function is called onload and onhashchange. + function detectHash() { + var hash = location.hash, + matchedElement, parentSection; + if ( hash.match( /^#mw-prefsection-[\w]+$/ ) ) { + mw.storage.session.remove( 'mwpreferences-prevTab' ); + switchPrefTab( hash.replace( '#mw-prefsection-', '' ) ); + } else if ( hash.match( /^#mw-[\w-]+$/ ) ) { + matchedElement = document.getElementById( hash.slice( 1 ) ); + parentSection = $( matchedElement ).parent().closest( '[id^="mw-prefsection-"]' ); + if ( parentSection.length ) { + mw.storage.session.remove( 'mwpreferences-prevTab' ); + // Switch to proper tab and scroll to selected item. + switchPrefTab( parentSection.attr( 'id' ).replace( 'mw-prefsection-', '' ), 'noHash' ); + matchedElement.scrollIntoView(); + } + } + } + + // In browsers that support the onhashchange event we will not bind click + // handlers and instead let the browser do the default behavior (clicking the + // will naturally set the hash, handled by onhashchange. + // But other things that change the hash will also be caught (e.g. using + // the Back and Forward browser navigation). + // Note the special check for IE "compatibility" mode. + if ( 'onhashchange' in window && + ( document.documentMode === undefined || document.documentMode >= 8 ) + ) { + $( window ).on( 'hashchange', function () { + var hash = location.hash; + if ( hash.match( /^#mw-[\w-]+/ ) ) { + detectHash(); + } else if ( hash === '' ) { + switchPrefTab( 'personal', 'noHash' ); + } + } ) + // Run the function immediately to select the proper tab on startup. + .trigger( 'hashchange' ); + // In older browsers we'll bind a click handler as fallback. + // We must not have onhashchange *and* the click handlers, otherwise + // the click handler calls switchPrefTab() which sets the hash value, + // which triggers onhashchange and calls switchPrefTab() again. + } else { + $preftoc.on( 'click', 'li a', function ( e ) { + switchPrefTab( $( this ).attr( 'href' ).replace( '#mw-prefsection-', '' ) ); + e.preventDefault(); + } ); + // If we've reloaded the page or followed an open-in-new-window, + // make the selected tab visible. + detectHash(); + } + + // Restore the active tab after saving the preferences + previousTab = mw.storage.session.get( 'mwpreferences-prevTab' ); + if ( previousTab ) { + switchPrefTab( previousTab, 'noHash' ); + // Deleting the key, the tab states should be reset until we press Save + mw.storage.session.remove( 'mwpreferences-prevTab' ); + } + + $( '#mw-prefs-form' ).on( 'submit', function () { + var value = $( $preftoc ).find( 'li.selected a' ).attr( 'id' ).replace( 'preftab-', '' ); + mw.storage.session.set( 'mwpreferences-prevTab', value ); + } ); + + } ); +}( mediaWiki, jQuery ) ); diff --git a/resources/src/mediawiki.special/mediawiki.special.preferences.timezone.js b/resources/src/mediawiki.special/mediawiki.special.preferences.timezone.js new file mode 100644 index 0000000000..58a5f84a9f --- /dev/null +++ b/resources/src/mediawiki.special/mediawiki.special.preferences.timezone.js @@ -0,0 +1,92 @@ +/*! + * JavaScript for Special:Preferences: Timezone field enhancements. + */ +( function ( mw, $ ) { + $( function () { + var + $tzSelect, $tzTextbox, $localtimeHolder, servertime; + + // Timezone functions. + // Guesses Timezone from browser and updates fields onchange. + + $tzSelect = $( '#mw-input-wptimecorrection' ); + $tzTextbox = $( '#mw-input-wptimecorrection-other' ); + $localtimeHolder = $( '#wpLocalTime' ); + servertime = parseInt( $( 'input[name="wpServerTime"]' ).val(), 10 ); + + function minutesToHours( min ) { + var tzHour = Math.floor( Math.abs( min ) / 60 ), + tzMin = Math.abs( min ) % 60, + tzString = ( ( min >= 0 ) ? '' : '-' ) + ( ( tzHour < 10 ) ? '0' : '' ) + tzHour + + ':' + ( ( tzMin < 10 ) ? '0' : '' ) + tzMin; + return tzString; + } + + function hoursToMinutes( hour ) { + var minutes, + arr = hour.split( ':' ); + + arr[ 0 ] = parseInt( arr[ 0 ], 10 ); + + if ( arr.length === 1 ) { + // Specification is of the form [-]XX + minutes = arr[ 0 ] * 60; + } else { + // Specification is of the form [-]XX:XX + minutes = Math.abs( arr[ 0 ] ) * 60 + parseInt( arr[ 1 ], 10 ); + if ( arr[ 0 ] < 0 ) { + minutes *= -1; + } + } + // Gracefully handle non-numbers. + if ( isNaN( minutes ) ) { + return 0; + } else { + return minutes; + } + } + + function updateTimezoneSelection() { + var minuteDiff, localTime, + type = $tzSelect.val(); + + if ( type === 'other' ) { + // User specified time zone manually in + // Grab data from the textbox, parse it. + minuteDiff = hoursToMinutes( $tzTextbox.val() ); + } else { + // Time zone not manually specified by user + if ( type === 'guess' ) { + // Get browser timezone & fill it in + minuteDiff = -( new Date().getTimezoneOffset() ); + $tzTextbox.val( minutesToHours( minuteDiff ) ); + $tzSelect.val( 'other' ); + $tzTextbox.prop( 'disabled', false ); + } else { + // Grab data from the $tzSelect value + minuteDiff = parseInt( type.split( '|' )[ 1 ], 10 ) || 0; + $tzTextbox.val( minutesToHours( minuteDiff ) ); + } + + // Set defaultValue prop on the generated box so we don't trigger the + // unsaved preferences check + $tzTextbox.prop( 'defaultValue', $tzTextbox.val() ); + } + + // Determine local time from server time and minutes difference, for display. + localTime = servertime + minuteDiff; + + // Bring time within the [0,1440) range. + localTime = ( ( localTime % 1440 ) + 1440 ) % 1440; + + $localtimeHolder.text( mw.language.convertNumber( minutesToHours( localTime ) ) ); + } + + if ( $tzSelect.length && $tzTextbox.length ) { + $tzSelect.change( updateTimezoneSelection ); + $tzTextbox.blur( updateTimezoneSelection ); + updateTimezoneSelection(); + } + + } ); +}( mediaWiki, jQuery ) );