Skip to content
Permalink
Browse files

Tabs: split up _tabify, create refresh method. Fixes #7140 Tabs: Add …

…refresh method
  • Loading branch information...
petersendidit committed Mar 27, 2011
1 parent 03eb54b commit 8b89febbbb4d2f13c67bc8ec406b68ff29da3a5a
Showing with 187 additions and 119 deletions.
  1. +28 −0 tests/unit/tabs/tabs_methods.js
  2. +159 −119 ui/jquery.ui.tabs.js
@@ -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.");
});
@@ -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;

0 comments on commit 8b89feb

Please sign in to comment.
You can’t perform that action at this time.