]> source.dussan.org Git - jquery-ui.git/commitdiff
Tabs: split up _tabify, create refresh method. Fixes #7140 Tabs: Add refresh method
authorDavid Petersen <public@petersendidit.com>
Sun, 27 Mar 2011 00:37:26 +0000 (20:37 -0400)
committerDavid Petersen <public@petersendidit.com>
Sun, 27 Mar 2011 01:02:17 +0000 (21:02 -0400)
tests/unit/tabs/tabs_methods.js
ui/jquery.ui.tabs.js

index 44c91d4921f88ba4806015ea2b819cc7e4a99bf3..50b8abd27f6e2316ce1b97f5a47a02fdb5263b37 100644 (file)
@@ -159,6 +159,34 @@ test('select', function() {
        equals(el.tabs('option', 'selected'), 1, 'should select tab by id');
 });
 
+test('refresh', function() {
+       expect(5);
+
+       var el = $('<div id="tabs"><ul></ul></div>').tabs(),
+               ul = el.find('ul');
+
+       equals(el.tabs('option', 'selected'), -1, 'Initially empty, no selected tab');
+
+       ul.append('<li><a href="data/test.html">Test 1</a></li>');
+       el.tabs('refresh');
+       equals( el.find('.ui-tabs-panel').length, 1, 'Panel created after refresh');
+
+       ul.find('li').remove();
+       el.tabs('refresh');
+       equals( el.find('.ui-tabs-panel').length, 0, 'Panel removed after refresh');
+
+       ul.append('<li><a href="#test1">Test 1</a></li>');
+       $('<div id="test1">Test Panel 1</div>').insertAfter( ul );
+       el.tabs('refresh');
+       el.tabs('select', 0);
+       equals( el.tabs('option', 'selected'), 0, 'First tab added should be auto selected');
+
+       ul.append('<li><a href="#test2">Test 2</a></li>');
+       $('<div id="test2">Test Panel 2</div>').insertAfter( ul );
+       el.tabs('refresh');
+       equals( el.tabs('option', 'selected'), 0, 'Second tab added should not be auto selected');
+});
+
 test('load', function() {
        ok(false, "missing test - untested code is broken code.");
 });
index c026858ee37642c31a4a7ffa58c5a99dfbf637e4..14c74b110a90fe414ef8f2b52f003685d18d93cb 100755 (executable)
@@ -41,7 +41,81 @@ $.widget( "ui.tabs", {
        },
 
        _create: function() {
-               this._tabify( true );
+               var self = this,
+                       o = this.options;
+
+               this.element.addClass( "ui-tabs ui-widget ui-widget-content ui-corner-all" );
+
+               this._processTabs();
+
+               // Selected tab
+               // use "selected" option or try to retrieve:
+               // 1. from fragment identifier in url
+               // 2. from cookie
+               // 3. from selected class attribute on <li>
+               if ( o.selected === undefined ) {
+                       if ( location.hash ) {
+                               this.anchors.each(function( i, a ) {
+                                       if ( a.hash == location.hash ) {
+                                               o.selected = i;
+                                               return false;
+                                       }
+                               });
+                       }
+                       if ( typeof o.selected !== "number" && o.cookie ) {
+                               o.selected = parseInt( self._cookie(), 10 );
+                       }
+                       if ( typeof o.selected !== "number" && this.lis.filter( ".ui-tabs-selected" ).length ) {
+                               o.selected = this.lis.index( this.lis.filter( ".ui-tabs-selected" ) );
+                       }
+                       o.selected = o.selected || ( this.lis.length ? 0 : -1 );
+               } else if ( o.selected === null ) { // usage of null is deprecated, TODO remove in next release
+                       o.selected = -1;
+               }
+
+               // sanity check - default to first tab...
+               o.selected = ( ( o.selected >= 0 && this.anchors[ o.selected ] ) || o.selected < 0 )
+                       ? o.selected
+                       : 0;
+
+               // Take disabling tabs via class attribute from HTML
+               // into account and update option properly.
+               if ( $.isArray( o.disabled ) ) {
+                       o.disabled = $.unique( o.disabled.concat(
+                               $.map( this.lis.filter( ".ui-state-disabled" ), function( n, i ) {
+                                       return self.lis.index( n );
+                               })
+                       ) ).sort();
+               }
+
+               this._setupFx( o.fx );
+
+               this._refresh();
+
+               // highlight selected tab
+               this.panels.addClass( "ui-tabs-hide" );
+               this.lis.removeClass( "ui-tabs-selected ui-state-active" );
+               // check for length avoids error when initializing empty list
+               if ( o.selected >= 0 && this.anchors.length ) {
+                       var temp = self.element.find( self._sanitizeSelector( self.anchors[ o.selected ].hash ) )
+                               .removeClass( "ui-tabs-hide" );
+
+                       this.lis.eq( o.selected ).addClass( "ui-tabs-selected ui-state-active" );
+
+                       // seems to be expected behavior that the show callback is fired
+                       self.element.queue( "tabs", function() {
+                               self._trigger( "show", null,
+                                       self._ui( self.anchors[ o.selected ], self.element.find( self._sanitizeSelector( self.anchors[ o.selected ].hash ) )[ 0 ] ) );
+                       });
+
+                       this.load( o.selected );
+               }
+
+               // clean up to avoid memory leaks in certain versions of IE 6
+               $( window ).bind( "unload.tabs", function() {
+                       self.lis.add( self.anchors ).unbind( ".tabs" );
+                       self.lis = self.anchors = self.panels = null;
+               });
        },
 
        _setOption: function( key, value ) {
@@ -52,7 +126,7 @@ $.widget( "ui.tabs", {
                        this.select( value );
                } else {
                        this.options[ key ] = value;
-                       this._tabify();
+                       this.refresh();
                }
        },
 
@@ -80,9 +154,64 @@ $.widget( "ui.tabs", {
                };
        },
 
-       _tabify: function( init ) {
+       refresh: function() {
+               var self = this;
+
+               this._processTabs();
+
+               this._refresh();
+
+               // Remove panels that we created that are missing their tab
+               this.element.find(".ui-tabs-panel:data(destroy.tabs)").each( function( index, panel ) {
+                       var anchor = self.anchors.filter( "[href$='#" + panel.id + "']");
+                       if ( !anchor.length ) {
+                               $( panel ).remove();
+                       }
+               });
+       },
+
+       _refresh: function() {
+               var self = this,
+                       o = this.options;
+
+               this.element
+                       .toggleClass( "ui-tabs-collapsible", o.collapsible );
+
+               this.list
+                       .addClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" );
+
+               this.lis
+                       .addClass( "ui-state-default ui-corner-top" );
+
+               this.panels
+                       .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" );
+
+               if ( !o.disabled.length ) {
+                       o.disabled = false;
+               }
+
+               // set or update cookie after init and add/remove respectively
+               if ( o.cookie ) {
+                       this._cookie( o.selected, o.cookie );
+               }
+
+               // disable tabs
+               for ( var i = 0, li; ( li = this.lis[ i ] ); i++ ) {
+                       $( li ).toggleClass( "ui-state-disabled", $.inArray( i, o.disabled ) != -1 );
+               }
+
+               this._setupEvents( o.event );
+
+               // remove all handlers, may run on existing tabs
+               this.lis.unbind( ".tabs" );
+
+
+               this._focusable( this.lis );
+               this._hoverable( this.lis );
+       },
+
+       _processTabs: function() {
                var self = this,
-                       o = this.options,
                        fragmentId = /^#.+/; // Safari 2 reports '#' for an empty hash
 
                this.list = this.element.find( "ol,ul" ).eq( 0 );
@@ -124,7 +253,7 @@ $.widget( "ui.tabs", {
                                a.href = "#" + id;
                                var $panel = self.element.find( "#" + id );
                                if ( !$panel.length ) {
-                                       $panel = $( o.panelTemplate )
+                                       $panel = $( self.options.panelTemplate )
                                                .attr( "id", id )
                                                .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )
                                                .insertAfter( self.panels[ i - 1 ] || self.list );
@@ -133,125 +262,21 @@ $.widget( "ui.tabs", {
                                self.panels = self.panels.add( $panel );
                        // invalid tab href
                        } else {
-                               o.disabled.push( i );
+                               self.options.disabled.push( i );
                        }
                });
+       },
 
-               // initialization from scratch
-               if ( init ) {
-                       // attach necessary classes for styling
-                       this.element.addClass( "ui-tabs ui-widget ui-widget-content ui-corner-all" );
-                       this.list.addClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" );
-                       this.lis.addClass( "ui-state-default ui-corner-top" );
-                       this.panels.addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" );
-
-                       // Selected tab
-                       // use "selected" option or try to retrieve:
-                       // 1. from fragment identifier in url
-                       // 2. from cookie
-                       // 3. from selected class attribute on <li>
-                       if ( o.selected === undefined ) {
-                               if ( location.hash ) {
-                                       this.anchors.each(function( i, a ) {
-                                               if ( a.hash == location.hash ) {
-                                                       o.selected = i;
-                                                       return false;
-                                               }
-                                       });
-                               }
-                               if ( typeof o.selected !== "number" && o.cookie ) {
-                                       o.selected = parseInt( self._cookie(), 10 );
-                               }
-                               if ( typeof o.selected !== "number" && this.lis.filter( ".ui-tabs-selected" ).length ) {
-                                       o.selected = this.lis.index( this.lis.filter( ".ui-tabs-selected" ) );
-                               }
-                               o.selected = o.selected || ( this.lis.length ? 0 : -1 );
-                       } else if ( o.selected === null ) { // usage of null is deprecated, TODO remove in next release
-                               o.selected = -1;
-                       }
-
-                       // sanity check - default to first tab...
-                       o.selected = ( ( o.selected >= 0 && this.anchors[ o.selected ] ) || o.selected < 0 )
-                               ? o.selected
-                               : 0;
-
-                       // Take disabling tabs via class attribute from HTML
-                       // into account and update option properly.
-                       if ( $.isArray( o.disabled ) ) {
-                               o.disabled = $.unique( o.disabled.concat(
-                                       $.map( this.lis.filter( ".ui-state-disabled" ), function( n, i ) {
-                                               return self.lis.index( n );
-                                       })
-                               ) ).sort();
-                       }
-
-                       // highlight selected tab
-                       this.panels.addClass( "ui-tabs-hide" );
-                       this.lis.removeClass( "ui-tabs-selected ui-state-active" );
-                       // check for length avoids error when initializing empty list
-                       if ( o.selected >= 0 && this.anchors.length ) {
-                               self.element.find( self._sanitizeSelector( self.anchors[ o.selected ].hash ) ).removeClass( "ui-tabs-hide" );
-                               this.lis.eq( o.selected ).addClass( "ui-tabs-selected ui-state-active" );
-
-                               // seems to be expected behavior that the show callback is fired
-                               self.element.queue( "tabs", function() {
-                                       self._trigger( "show", null,
-                                               self._ui( self.anchors[ o.selected ], self.element.find( self._sanitizeSelector( self.anchors[ o.selected ].hash ) )[ 0 ] ) );
-                               });
-
-                               this.load( o.selected );
-                       }
-
-                       // clean up to avoid memory leaks in certain versions of IE 6
-                       // TODO: namespace this event
-                       $( window ).bind( "unload", function() {
-                               self.lis.add( self.anchors ).unbind( ".tabs" );
-                               self.lis = self.anchors = self.panels = null;
-                       });
-               // update selected after add/remove
-               } else {
-                       o.selected = this.lis.index( this.lis.filter( ".ui-tabs-selected" ) );
-               }
-
-               if ( !o.disabled.length ) {
-                       o.disabled = false;
-               }
-
-               this.element.toggleClass( "ui-tabs-collapsible", o.collapsible );
-
-               // set or update cookie after init and add/remove respectively
-               if ( o.cookie ) {
-                       this._cookie( o.selected, o.cookie );
-               }
-
-               // disable tabs
-               for ( var i = 0, li; ( li = this.lis[ i ] ); i++ ) {
-                       $( li ).toggleClass( "ui-state-disabled", $.inArray( i, o.disabled ) != -1 );
-               }
-
-               // remove all handlers before, tabify may run on existing tabs after add or option change
-               this.lis.add( this.anchors ).unbind( ".tabs" );
-
-               this._focusable( this.lis );
-               this._hoverable( this.lis );
-
+       _setupFx: function( fx ) {
                // set up animations
-               if ( o.fx ) {
-                       if ( $.isArray( o.fx ) ) {
-                               this.hideFx = o.fx[ 0 ];
-                               this.showFx = o.fx[ 1 ];
+               if ( fx ) {
+                       if ( $.isArray( fx ) ) {
+                               this.hideFx = fx[ 0 ];
+                               this.showFx = fx[ 1 ];
                        } else {
-                               this.hideFx = this.showFx = o.fx;
+                               this.hideFx = this.showFx = fx;
                        }
                }
-
-               // attach tab event handler, unbind to avoid duplicates from former tabifying...
-               this.anchors.bind( o.event + ".tabs", $.proxy( this, "_eventHandler" ));
-
-               // disable click in any case
-               this.anchors.bind( "click.tabs", function( event ){
-                       event.preventDefault();
-               });
        },
 
        // Reset certain styles left over from animation
@@ -297,6 +322,21 @@ $.widget( "ui.tabs", {
                }
        },
 
+       _setupEvents: function( event ) {
+               // attach tab event handler, unbind to avoid duplicates from former tabifying...
+               this.anchors.unbind( ".tabs" );
+
+               if ( event ) {
+                       this.anchors.bind( event.split( " " ).join( ".tabs " ) + ".tabs",
+                               $.proxy( this, "_eventHandler" ) );
+               }
+
+               // disable click in any case
+               this.anchors.bind( "click.tabs", function( event ){
+                       event.preventDefault();
+               });
+       },
+
        _eventHandler: function( event ) {
                event.preventDefault();
                var self = this,
@@ -764,7 +804,7 @@ if ( $.uiBackCompat !== false ) {
                                return n >= index ? ++n : n;
                        });
 
-                       this._tabify();
+                       this.refresh();
 
                        if ( this.anchors.length == 1 ) {
                                o.selected = 0;
@@ -801,7 +841,7 @@ if ( $.uiBackCompat !== false ) {
                                        return n >= index ? --n : n;
                                });
 
-                       this._tabify();
+                       this.refresh();
 
                        this._trigger( "remove", null, this._ui( $li.find( "a" )[ 0 ], $panel[ 0 ] ) );
                        return this;