Merge "mediawiki.special.preferences: Support Back/Forward navigation"
authorKrinkle <ttijhof@wikimedia.org>
Fri, 29 Jun 2012 02:37:37 +0000 (02:37 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Fri, 29 Jun 2012 02:37:37 +0000 (02:37 +0000)
resources/mediawiki.special/mediawiki.special.preferences.js

index e9df83c..0804825 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * JavaScript for Special:Preferences
  */
-jQuery( document ).ready( function( $ ) {
+jQuery( document ).ready( function ( $ ) {
 $( '#prefsubmit' ).attr( 'id', 'prefcontrol' );
 var $preftoc = $('<ul id="preftoc"></ul>');
 var $preferences = $( '#preferences' )
@@ -15,8 +15,35 @@ var $fieldsets = $preferences.children( 'fieldset' )
 var $legends = $fieldsets.children( 'legend' )
        .addClass( 'mainLegend' );
 
+/**
+ * It uses document.getElementById for security reasons (html injections in
+ * jQuery()).
+ *
+ * @param String name: the name of a tab without the prefix ("mw-prefsection-")
+ * @param String mode: [optional] 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' ) {
+               window.location.hash = '#mw-prefsection-' + name;
+       }
+       $( window ).scrollTop( scrollTop );
+
+       $preftoc.find( 'li' ).removeClass( 'selected' );
+       $tab = $( document.getElementById( 'preftab-' + name ) );
+       if ( $tab.length ) {
+               $tab.parent().addClass( 'selected' );
+               $preferences.children( 'fieldset' ).hide();
+               $( document.getElementById( 'mw-prefsection-' + name ) ).show();
+       }
+}
+
 // Populate the prefToc
-$legends.each( function( i, legend ) {
+$legends.each( function ( i, legend ) {
        var $legend = $(legend);
        if ( i === 0 ) {
                $legend.parent().show();
@@ -30,18 +57,6 @@ $legends.each( function( i, legend ) {
                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 );
@@ -50,11 +65,34 @@ $legends.each( function( i, legend ) {
 // If we've reloaded the page or followed an open-in-new-window,
 // make the selected tab visible.
 var hash = window.location.hash;
-if( hash.match( /^#mw-prefsection-[\w-]+/ ) ) {
-       var $tab = $( hash.replace( 'mw-prefsection', 'preftab' ) );
-       $tab.click();
+if ( hash.match( /^#mw-prefsection-[\w-]+/ ) ) {
+       switchPrefTab( hash.replace( '#mw-prefsection-' , '' ) );
 }
 
+// In browsers that support the onhashchange event we will not bind click
+// handlers and instead let the browser do the default behavior (clicking the
+// <a href="#.."> will naturally set the hash, handled by onhashchange.
+// But other things that change the hash will also be catched (e.g. using
+// the Back and Forward browser navigation).
+if ( 'onhashchange' in window ) {
+       $(window).on( 'hashchange' , function () {
+               var hash = window.location.hash;
+               if ( hash.match( /^#mw-prefsection-[\w-]+/ ) ) {
+                       switchPrefTab( hash.replace( '#mw-prefsection-', '' ) );
+               } else if ( hash === '' ) {
+                       switchPrefTab( 'personal', 'noHash' );
+               }
+       });
+// In older browsers we'll bind a click handler as fallback.
+// We must not have onhashchange *and* the click handlers, other wise
+// the click handler calls switchPrefTab() which sets the hash value,
+// which triggers onhashcange and calls switchPrefTab() again.
+} else {
+       $preftoc.on( 'click', 'li a', function ( e ) {
+               switchPrefTab( $( this ).attr( 'href' ).replace( '#mw-prefsection-', '' ) );
+               e.preventDefault();
+       });
+}
 
 /**
 * Timezone functions.
@@ -68,7 +106,7 @@ var $localtimeHolder = $( '#wpLocalTime' );
 var servertime = parseInt( $( 'input[name=wpServerTime]' ).val(), 10 );
 var minuteDiff = 0;
 
-var minutesToHours = function( min ) {
+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 +
@@ -76,7 +114,7 @@ var minutesToHours = function( min ) {
        return tzString;
 };
 
-var hoursToMinutes = function( hour ) {
+var hoursToMinutes = function ( hour ) {
        var arr = hour.split( ':' );
        arr[0] = parseInt( arr[0], 10 );
 
@@ -99,7 +137,7 @@ var hoursToMinutes = function( hour ) {
        }
 };
 
-var updateTimezoneSelection = function() {
+var updateTimezoneSelection = function () {
        var type = $tzSelect.val();
        if ( type == 'guess' ) {
                // Get browser timezone & fill it in
@@ -130,8 +168,8 @@ var updateTimezoneSelection = function() {
 };
 
 if ( $tzSelect.length && $tzTextbox.length ) {
-       $tzSelect.change( function() { updateTimezoneSelection(); } );
-       $tzTextbox.blur( function() { updateTimezoneSelection(); } );
+       $tzSelect.change( function () { updateTimezoneSelection(); } );
+       $tzTextbox.blur( function () { updateTimezoneSelection(); } );
        updateTimezoneSelection();
 }
 } );