Blame | Last modification | View Log | RSS feed
/** jQuery UI Tabs 1.8.5** Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)* Dual licensed under the MIT or GPL Version 2 licenses.* http://jquery.org/license** http://docs.jquery.com/UI/Tabs** Depends:* jquery.ui.core.js* jquery.ui.widget.js*/(function( $, undefined ) {var tabId = 0,listId = 0;function getNextTabId() {return ++tabId;}function getNextListId() {return ++listId;}$.widget( "ui.tabs", {options: {add: null,ajaxOptions: null,cache: false,cookie: null, // e.g. { expires: 7, path: '/', domain: 'jquery.com', secure: true }collapsible: false,disable: null,disabled: [],enable: null,event: "click",fx: null, // e.g. { height: 'toggle', opacity: 'toggle', duration: 200 }idPrefix: "ui-tabs-",load: null,panelTemplate: "<div></div>",remove: null,select: null,show: null,spinner: "<em>Loading…</em>",tabTemplate: "<li><a href='#{href}'><span>#{label}</span></a></li>"},_create: function() {this._tabify( true );},_setOption: function( key, value ) {if ( key == "selected" ) {if (this.options.collapsible && value == this.options.selected ) {return;}this.select( value );} else {this.options[ key ] = value;this._tabify();}},_tabId: function( a ) {return a.title && a.title.replace( /\s/g, "_" ).replace( /[^\w\u00c0-\uFFFF-]/g, "" ) ||this.options.idPrefix + getNextTabId();},_sanitizeSelector: function( hash ) {// we need this because an id may contain a ":"return hash.replace( /:/g, "\\:" );},_cookie: function() {var cookie = this.cookie ||( this.cookie = this.options.cookie.name || "ui-tabs-" + getNextListId() );return $.cookie.apply( null, [ cookie ].concat( $.makeArray( arguments ) ) );},_ui: function( tab, panel ) {return {tab: tab,panel: panel,index: this.anchors.index( tab )};},_cleanup: function() {// restore all former loading tabs labelsthis.lis.filter( ".ui-state-processing" ).removeClass( "ui-state-processing" ).find( "span:data(label.tabs)" ).each(function() {var el = $( this );el.html( el.data( "label.tabs" ) ).removeData( "label.tabs" );});},_tabify: function( init ) {var self = this,o = this.options,fragmentId = /^#.+/; // Safari 2 reports '#' for an empty hashthis.list = this.element.find( "ol,ul" ).eq( 0 );this.lis = $( " > li:has(a[href])", this.list );this.anchors = this.lis.map(function() {return $( "a", this )[ 0 ];});this.panels = $( [] );this.anchors.each(function( i, a ) {var href = $( a ).attr( "href" );// For dynamically created HTML that contains a hash as href IE < 8 expands// such href to the full page url with hash and then misinterprets tab as ajax.// Same consideration applies for an added tab with a fragment identifier// since a[href=#fragment-identifier] does unexpectedly not match.// Thus normalize href attribute...var hrefBase = href.split( "#" )[ 0 ],baseEl;if ( hrefBase && ( hrefBase === location.toString().split( "#" )[ 0 ] ||( baseEl = $( "base" )[ 0 ]) && hrefBase === baseEl.href ) ) {href = a.hash;a.href = href;}// inline tabif ( fragmentId.test( href ) ) {self.panels = self.panels.add( self._sanitizeSelector( href ) );// remote tab// prevent loading the page itself if href is just "#"} else if ( href && href !== "#" ) {// required for restore on destroy$.data( a, "href.tabs", href );// TODO until #3808 is fixed strip fragment identifier from url// (IE fails to load from such url)$.data( a, "load.tabs", href.replace( /#.*$/, "" ) );var id = self._tabId( a );a.href = "#" + id;var $panel = $( "#" + id );if ( !$panel.length ) {$panel = $( o.panelTemplate ).attr( "id", id ).addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" ).insertAfter( self.panels[ i - 1 ] || self.list );$panel.data( "destroy.tabs", true );}self.panels = self.panels.add( $panel );// invalid tab href} else {o.disabled.push( i );}});// initialization from scratchif ( init ) {// attach necessary classes for stylingthis.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 releaseo.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.// A selected tab cannot become disabled.o.disabled = $.unique( o.disabled.concat($.map( this.lis.filter( ".ui-state-disabled" ), function( n, i ) {return self.lis.index( n );})) ).sort();if ( $.inArray( o.selected, o.disabled ) != -1 ) {o.disabled.splice( $.inArray( o.selected, o.disabled ), 1 );}// highlight selected tabthis.panels.addClass( "ui-tabs-hide" );this.lis.removeClass( "ui-tabs-selected ui-state-active" );// check for length avoids error when initializing empty listif ( o.selected >= 0 && this.anchors.length ) {this.panels.eq( o.selected ).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 firedself.element.queue( "tabs", function() {self._trigger( "show", null,self._ui( self.anchors[ o.selected ], self.panels[ o.selected ] ) );});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" ) );}// update collapsible// TODO: use .toggleClass()this.element[ o.collapsible ? "addClass" : "removeClass" ]( "ui-tabs-collapsible" );// set or update cookie after init and add/remove respectivelyif ( o.cookie ) {this._cookie( o.selected, o.cookie );}// disable tabsfor ( var i = 0, li; ( li = this.lis[ i ] ); i++ ) {$( li )[ $.inArray( i, o.disabled ) != -1 &&// TODO: use .toggleClass()!$( li ).hasClass( "ui-tabs-selected" ) ? "addClass" : "removeClass" ]( "ui-state-disabled" );}// reset cache if switching from cached to not cachedif ( o.cache === false ) {this.anchors.removeData( "cache.tabs" );}// remove all handlers before, tabify may run on existing tabs after add or option changethis.lis.add( this.anchors ).unbind( ".tabs" );if ( o.event !== "mouseover" ) {var addState = function( state, el ) {if ( el.is( ":not(.ui-state-disabled)" ) ) {el.addClass( "ui-state-" + state );}};var removeState = function( state, el ) {el.removeClass( "ui-state-" + state );};this.lis.bind( "mouseover.tabs" , function() {addState( "hover", $( this ) );});this.lis.bind( "mouseout.tabs", function() {removeState( "hover", $( this ) );});this.anchors.bind( "focus.tabs", function() {addState( "focus", $( this ).closest( "li" ) );});this.anchors.bind( "blur.tabs", function() {removeState( "focus", $( this ).closest( "li" ) );});}// set up animationsvar hideFx, showFx;if ( o.fx ) {if ( $.isArray( o.fx ) ) {hideFx = o.fx[ 0 ];showFx = o.fx[ 1 ];} else {hideFx = showFx = o.fx;}}// Reset certain styles left over from animation// and prevent IE's ClearType bug...function resetStyle( $el, fx ) {$el.css( "display", "" );if ( !$.support.opacity && fx.opacity ) {$el[ 0 ].style.removeAttribute( "filter" );}}// Show a tab...var showTab = showFx? function( clicked, $show ) {$( clicked ).closest( "li" ).addClass( "ui-tabs-selected ui-state-active" );$show.hide().removeClass( "ui-tabs-hide" ) // avoid flicker that way.animate( showFx, showFx.duration || "normal", function() {resetStyle( $show, showFx );self._trigger( "show", null, self._ui( clicked, $show[ 0 ] ) );});}: function( clicked, $show ) {$( clicked ).closest( "li" ).addClass( "ui-tabs-selected ui-state-active" );$show.removeClass( "ui-tabs-hide" );self._trigger( "show", null, self._ui( clicked, $show[ 0 ] ) );};// Hide a tab, $show is optional...var hideTab = hideFx? function( clicked, $hide ) {$hide.animate( hideFx, hideFx.duration || "normal", function() {self.lis.removeClass( "ui-tabs-selected ui-state-active" );$hide.addClass( "ui-tabs-hide" );resetStyle( $hide, hideFx );self.element.dequeue( "tabs" );});}: function( clicked, $hide, $show ) {self.lis.removeClass( "ui-tabs-selected ui-state-active" );$hide.addClass( "ui-tabs-hide" );self.element.dequeue( "tabs" );};// attach tab event handler, unbind to avoid duplicates from former tabifying...this.anchors.bind( o.event + ".tabs", function() {var el = this,$li = $(el).closest( "li" ),$hide = self.panels.filter( ":not(.ui-tabs-hide)" ),$show = $( self._sanitizeSelector( el.hash ) );// If tab is already selected and not collapsible or tab disabled or// or is already loading or click callback returns false stop here.// Check if click handler returns false last so that it is not executed// for a disabled or loading tab!if ( ( $li.hasClass( "ui-tabs-selected" ) && !o.collapsible) ||$li.hasClass( "ui-state-disabled" ) ||$li.hasClass( "ui-state-processing" ) ||self.panels.filter( ":animated" ).length ||self._trigger( "select", null, self._ui( this, $show[ 0 ] ) ) === false ) {this.blur();return false;}o.selected = self.anchors.index( this );self.abort();// if tab may be closedif ( o.collapsible ) {if ( $li.hasClass( "ui-tabs-selected" ) ) {o.selected = -1;if ( o.cookie ) {self._cookie( o.selected, o.cookie );}self.element.queue( "tabs", function() {hideTab( el, $hide );}).dequeue( "tabs" );this.blur();return false;} else if ( !$hide.length ) {if ( o.cookie ) {self._cookie( o.selected, o.cookie );}self.element.queue( "tabs", function() {showTab( el, $show );});// TODO make passing in node possible, see also http://dev.jqueryui.com/ticket/3171self.load( self.anchors.index( this ) );this.blur();return false;}}if ( o.cookie ) {self._cookie( o.selected, o.cookie );}// show new tabif ( $show.length ) {if ( $hide.length ) {self.element.queue( "tabs", function() {hideTab( el, $hide );});}self.element.queue( "tabs", function() {showTab( el, $show );});self.load( self.anchors.index( this ) );} else {throw "jQuery UI Tabs: Mismatching fragment identifier.";}// Prevent IE from keeping other link focussed when using the back button// and remove dotted border from clicked link. This is controlled via CSS// in modern browsers; blur() removes focus from address bar in Firefox// which can become a usability and annoying problem with tabs('rotate').if ( $.browser.msie ) {this.blur();}});// disable click in any casethis.anchors.bind( "click.tabs", function(){return false;});},_getIndex: function( index ) {// meta-function to give users option to provide a href string instead of a numerical index.// also sanitizes numerical indexes to valid values.if ( typeof index == "string" ) {index = this.anchors.index( this.anchors.filter( "[href$=" + index + "]" ) );}return index;},destroy: function() {var o = this.options;this.abort();this.element.unbind( ".tabs" ).removeClass( "ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible" ).removeData( "tabs" );this.list.removeClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" );this.anchors.each(function() {var href = $.data( this, "href.tabs" );if ( href ) {this.href = href;}var $this = $( this ).unbind( ".tabs" );$.each( [ "href", "load", "cache" ], function( i, prefix ) {$this.removeData( prefix + ".tabs" );});});this.lis.unbind( ".tabs" ).add( this.panels ).each(function() {if ( $.data( this, "destroy.tabs" ) ) {$( this ).remove();} else {$( this ).removeClass(["ui-state-default","ui-corner-top","ui-tabs-selected","ui-state-active","ui-state-hover","ui-state-focus","ui-state-disabled","ui-tabs-panel","ui-widget-content","ui-corner-bottom","ui-tabs-hide"].join( " " ) );}});if ( o.cookie ) {this._cookie( null, o.cookie );}return this;},add: function( url, label, index ) {if ( index === undefined ) {index = this.anchors.length;}var self = this,o = this.options,$li = $( o.tabTemplate.replace( /#\{href\}/g, url ).replace( /#\{label\}/g, label ) ),id = !url.indexOf( "#" ) ? url.replace( "#", "" ) : this._tabId( $( "a", $li )[ 0 ] );$li.addClass( "ui-state-default ui-corner-top" ).data( "destroy.tabs", true );// try to find an existing element before creating a new onevar $panel = $( "#" + id );if ( !$panel.length ) {$panel = $( o.panelTemplate ).attr( "id", id ).data( "destroy.tabs", true );}$panel.addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide" );if ( index >= this.lis.length ) {$li.appendTo( this.list );$panel.appendTo( this.list[ 0 ].parentNode );} else {$li.insertBefore( this.lis[ index ] );$panel.insertBefore( this.panels[ index ] );}o.disabled = $.map( o.disabled, function( n, i ) {return n >= index ? ++n : n;});this._tabify();if ( this.anchors.length == 1 ) {o.selected = 0;$li.addClass( "ui-tabs-selected ui-state-active" );$panel.removeClass( "ui-tabs-hide" );this.element.queue( "tabs", function() {self._trigger( "show", null, self._ui( self.anchors[ 0 ], self.panels[ 0 ] ) );});this.load( 0 );}this._trigger( "add", null, this._ui( this.anchors[ index ], this.panels[ index ] ) );return this;},remove: function( index ) {index = this._getIndex( index );var o = this.options,$li = this.lis.eq( index ).remove(),$panel = this.panels.eq( index ).remove();// If selected tab was removed focus tab to the right or// in case the last tab was removed the tab to the left.if ( $li.hasClass( "ui-tabs-selected" ) && this.anchors.length > 1) {this.select( index + ( index + 1 < this.anchors.length ? 1 : -1 ) );}o.disabled = $.map($.grep( o.disabled, function(n, i) {return n != index;}),function( n, i ) {return n >= index ? --n : n;});this._tabify();this._trigger( "remove", null, this._ui( $li.find( "a" )[ 0 ], $panel[ 0 ] ) );return this;},enable: function( index ) {index = this._getIndex( index );var o = this.options;if ( $.inArray( index, o.disabled ) == -1 ) {return;}this.lis.eq( index ).removeClass( "ui-state-disabled" );o.disabled = $.grep( o.disabled, function( n, i ) {return n != index;});this._trigger( "enable", null, this._ui( this.anchors[ index ], this.panels[ index ] ) );return this;},disable: function( index ) {index = this._getIndex( index );var self = this, o = this.options;// cannot disable already selected tabif ( index != o.selected ) {this.lis.eq( index ).addClass( "ui-state-disabled" );o.disabled.push( index );o.disabled.sort();this._trigger( "disable", null, this._ui( this.anchors[ index ], this.panels[ index ] ) );}return this;},select: function( index ) {index = this._getIndex( index );if ( index == -1 ) {if ( this.options.collapsible && this.options.selected != -1 ) {index = this.options.selected;} else {return this;}}this.anchors.eq( index ).trigger( this.options.event + ".tabs" );return this;},load: function( index ) {index = this._getIndex( index );var self = this,o = this.options,a = this.anchors.eq( index )[ 0 ],url = $.data( a, "load.tabs" );this.abort();// not remote or from cacheif ( !url || this.element.queue( "tabs" ).length !== 0 && $.data( a, "cache.tabs" ) ) {this.element.dequeue( "tabs" );return;}// load remote from here onthis.lis.eq( index ).addClass( "ui-state-processing" );if ( o.spinner ) {var span = $( "span", a );span.data( "label.tabs", span.html() ).html( o.spinner );}this.xhr = $.ajax( $.extend( {}, o.ajaxOptions, {url: url,success: function( r, s ) {$( self._sanitizeSelector( a.hash ) ).html( r );// take care of tab labelsself._cleanup();if ( o.cache ) {$.data( a, "cache.tabs", true );}self._trigger( "load", null, self._ui( self.anchors[ index ], self.panels[ index ] ) );try {o.ajaxOptions.success( r, s );}catch ( e ) {}},error: function( xhr, s, e ) {// take care of tab labelsself._cleanup();self._trigger( "load", null, self._ui( self.anchors[ index ], self.panels[ index ] ) );try {// Passing index avoid a race condition when this method is// called after the user has selected another tab.// Pass the anchor that initiated this request allows// loadError to manipulate the tab content panel via $(a.hash)o.ajaxOptions.error( xhr, s, index, a );}catch ( e ) {}}} ) );// last, so that load event is fired before show...self.element.dequeue( "tabs" );return this;},abort: function() {// stop possibly running animationsthis.element.queue( [] );this.panels.stop( false, true );// "tabs" queue must not contain more than two elements,// which are the callbacks for the latest clicked tab...this.element.queue( "tabs", this.element.queue( "tabs" ).splice( -2, 2 ) );// terminate pending requests from other tabsif ( this.xhr ) {this.xhr.abort();delete this.xhr;}// take care of tab labelsthis._cleanup();return this;},url: function( index, url ) {this.anchors.eq( index ).removeData( "cache.tabs" ).data( "load.tabs", url );return this;},length: function() {return this.anchors.length;}});$.extend( $.ui.tabs, {version: "1.8.5"});/** Tabs Extensions*//** Rotate*/$.extend( $.ui.tabs.prototype, {rotation: null,rotate: function( ms, continuing ) {var self = this,o = this.options;var rotate = self._rotate || ( self._rotate = function( e ) {clearTimeout( self.rotation );self.rotation = setTimeout(function() {var t = o.selected;self.select( ++t < self.anchors.length ? t : 0 );}, ms );if ( e ) {e.stopPropagation();}});var stop = self._unrotate || ( self._unrotate = !continuing? function(e) {if (e.clientX) { // in case of a true clickself.rotate(null);}}: function( e ) {t = o.selected;rotate();});// start rotationif ( ms ) {this.element.bind( "tabsshow", rotate );this.anchors.bind( o.event + ".tabs", stop );rotate();// stop rotation} else {clearTimeout( self.rotation );this.element.unbind( "tabsshow", rotate );this.anchors.unbind( o.event + ".tabs", stop );delete this._rotate;delete this._unrotate;}return this;}});})( jQuery );