client/player/development-bundle/ui/jquery.ui.tabs.js
changeset 57 3a3c15c462f8
parent 48 44d58d2e90b5
parent 56 da0957782d03
child 58 ec1d0e7dcb53
equal deleted inserted replaced
48:44d58d2e90b5 57:3a3c15c462f8
     1 /*
       
     2  * jQuery UI Tabs 1.8.1
       
     3  *
       
     4  * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
       
     5  * Dual licensed under the MIT (MIT-LICENSE.txt)
       
     6  * and GPL (GPL-LICENSE.txt) licenses.
       
     7  *
       
     8  * http://docs.jquery.com/UI/Tabs
       
     9  *
       
    10  * Depends:
       
    11  *	jquery.ui.core.js
       
    12  *	jquery.ui.widget.js
       
    13  */
       
    14 (function($) {
       
    15 
       
    16 var tabId = 0,
       
    17 	listId = 0;
       
    18 
       
    19 $.widget("ui.tabs", {
       
    20 	options: {
       
    21 		add: null,
       
    22 		ajaxOptions: null,
       
    23 		cache: false,
       
    24 		cookie: null, // e.g. { expires: 7, path: '/', domain: 'jquery.com', secure: true }
       
    25 		collapsible: false,
       
    26 		disable: null,
       
    27 		disabled: [],
       
    28 		enable: null,
       
    29 		event: 'click',
       
    30 		fx: null, // e.g. { height: 'toggle', opacity: 'toggle', duration: 200 }
       
    31 		idPrefix: 'ui-tabs-',
       
    32 		load: null,
       
    33 		panelTemplate: '<div></div>',
       
    34 		remove: null,
       
    35 		select: null,
       
    36 		show: null,
       
    37 		spinner: '<em>Loading&#8230;</em>',
       
    38 		tabTemplate: '<li><a href="#{href}"><span>#{label}</span></a></li>'
       
    39 	},
       
    40 	_create: function() {
       
    41 		this._tabify(true);
       
    42 	},
       
    43 
       
    44 	_setOption: function(key, value) {
       
    45 		if (key == 'selected') {
       
    46 			if (this.options.collapsible && value == this.options.selected) {
       
    47 				return;
       
    48 			}
       
    49 			this.select(value);
       
    50 		}
       
    51 		else {
       
    52 			this.options[key] = value;
       
    53 			this._tabify();
       
    54 		}
       
    55 	},
       
    56 
       
    57 	_tabId: function(a) {
       
    58 		return a.title && a.title.replace(/\s/g, '_').replace(/[^A-Za-z0-9\-_:\.]/g, '') ||
       
    59 			this.options.idPrefix + (++tabId);
       
    60 	},
       
    61 
       
    62 	_sanitizeSelector: function(hash) {
       
    63 		return hash.replace(/:/g, '\\:'); // we need this because an id may contain a ":"
       
    64 	},
       
    65 
       
    66 	_cookie: function() {
       
    67 		var cookie = this.cookie || (this.cookie = this.options.cookie.name || 'ui-tabs-' + (++listId));
       
    68 		return $.cookie.apply(null, [cookie].concat($.makeArray(arguments)));
       
    69 	},
       
    70 
       
    71 	_ui: function(tab, panel) {
       
    72 		return {
       
    73 			tab: tab,
       
    74 			panel: panel,
       
    75 			index: this.anchors.index(tab)
       
    76 		};
       
    77 	},
       
    78 
       
    79 	_cleanup: function() {
       
    80 		// restore all former loading tabs labels
       
    81 		this.lis.filter('.ui-state-processing').removeClass('ui-state-processing')
       
    82 				.find('span:data(label.tabs)')
       
    83 				.each(function() {
       
    84 					var el = $(this);
       
    85 					el.html(el.data('label.tabs')).removeData('label.tabs');
       
    86 				});
       
    87 	},
       
    88 
       
    89 	_tabify: function(init) {
       
    90 
       
    91 		this.list = this.element.find('ol,ul').eq(0);
       
    92 		this.lis = $('li:has(a[href])', this.list);
       
    93 		this.anchors = this.lis.map(function() { return $('a', this)[0]; });
       
    94 		this.panels = $([]);
       
    95 
       
    96 		var self = this, o = this.options;
       
    97 
       
    98 		var fragmentId = /^#.+/; // Safari 2 reports '#' for an empty hash
       
    99 		this.anchors.each(function(i, a) {
       
   100 			var href = $(a).attr('href');
       
   101 
       
   102 			// For dynamically created HTML that contains a hash as href IE < 8 expands
       
   103 			// such href to the full page url with hash and then misinterprets tab as ajax.
       
   104 			// Same consideration applies for an added tab with a fragment identifier
       
   105 			// since a[href=#fragment-identifier] does unexpectedly not match.
       
   106 			// Thus normalize href attribute...
       
   107 			var hrefBase = href.split('#')[0], baseEl;
       
   108 			if (hrefBase && (hrefBase === location.toString().split('#')[0] ||
       
   109 					(baseEl = $('base')[0]) && hrefBase === baseEl.href)) {
       
   110 				href = a.hash;
       
   111 				a.href = href;
       
   112 			}
       
   113 
       
   114 			// inline tab
       
   115 			if (fragmentId.test(href)) {
       
   116 				self.panels = self.panels.add(self._sanitizeSelector(href));
       
   117 			}
       
   118 
       
   119 			// remote tab
       
   120 			else if (href != '#') { // prevent loading the page itself if href is just "#"
       
   121 				$.data(a, 'href.tabs', href); // required for restore on destroy
       
   122 
       
   123 				// TODO until #3808 is fixed strip fragment identifier from url
       
   124 				// (IE fails to load from such url)
       
   125 				$.data(a, 'load.tabs', href.replace(/#.*$/, '')); // mutable data
       
   126 
       
   127 				var id = self._tabId(a);
       
   128 				a.href = '#' + id;
       
   129 				var $panel = $('#' + id);
       
   130 				if (!$panel.length) {
       
   131 					$panel = $(o.panelTemplate).attr('id', id).addClass('ui-tabs-panel ui-widget-content ui-corner-bottom')
       
   132 						.insertAfter(self.panels[i - 1] || self.list);
       
   133 					$panel.data('destroy.tabs', true);
       
   134 				}
       
   135 				self.panels = self.panels.add($panel);
       
   136 			}
       
   137 
       
   138 			// invalid tab href
       
   139 			else {
       
   140 				o.disabled.push(i);
       
   141 			}
       
   142 		});
       
   143 
       
   144 		// initialization from scratch
       
   145 		if (init) {
       
   146 
       
   147 			// attach necessary classes for styling
       
   148 			this.element.addClass('ui-tabs ui-widget ui-widget-content ui-corner-all');
       
   149 			this.list.addClass('ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all');
       
   150 			this.lis.addClass('ui-state-default ui-corner-top');
       
   151 			this.panels.addClass('ui-tabs-panel ui-widget-content ui-corner-bottom');
       
   152 
       
   153 			// Selected tab
       
   154 			// use "selected" option or try to retrieve:
       
   155 			// 1. from fragment identifier in url
       
   156 			// 2. from cookie
       
   157 			// 3. from selected class attribute on <li>
       
   158 			if (o.selected === undefined) {
       
   159 				if (location.hash) {
       
   160 					this.anchors.each(function(i, a) {
       
   161 						if (a.hash == location.hash) {
       
   162 							o.selected = i;
       
   163 							return false; // break
       
   164 						}
       
   165 					});
       
   166 				}
       
   167 				if (typeof o.selected != 'number' && o.cookie) {
       
   168 					o.selected = parseInt(self._cookie(), 10);
       
   169 				}
       
   170 				if (typeof o.selected != 'number' && this.lis.filter('.ui-tabs-selected').length) {
       
   171 					o.selected = this.lis.index(this.lis.filter('.ui-tabs-selected'));
       
   172 				}
       
   173 				o.selected = o.selected || (this.lis.length ? 0 : -1);
       
   174 			}
       
   175 			else if (o.selected === null) { // usage of null is deprecated, TODO remove in next release
       
   176 				o.selected = -1;
       
   177 			}
       
   178 
       
   179 			// sanity check - default to first tab...
       
   180 			o.selected = ((o.selected >= 0 && this.anchors[o.selected]) || o.selected < 0) ? o.selected : 0;
       
   181 
       
   182 			// Take disabling tabs via class attribute from HTML
       
   183 			// into account and update option properly.
       
   184 			// A selected tab cannot become disabled.
       
   185 			o.disabled = $.unique(o.disabled.concat(
       
   186 				$.map(this.lis.filter('.ui-state-disabled'),
       
   187 					function(n, i) { return self.lis.index(n); } )
       
   188 			)).sort();
       
   189 
       
   190 			if ($.inArray(o.selected, o.disabled) != -1) {
       
   191 				o.disabled.splice($.inArray(o.selected, o.disabled), 1);
       
   192 			}
       
   193 
       
   194 			// highlight selected tab
       
   195 			this.panels.addClass('ui-tabs-hide');
       
   196 			this.lis.removeClass('ui-tabs-selected ui-state-active');
       
   197 			if (o.selected >= 0 && this.anchors.length) { // check for length avoids error when initializing empty list
       
   198 				this.panels.eq(o.selected).removeClass('ui-tabs-hide');
       
   199 				this.lis.eq(o.selected).addClass('ui-tabs-selected ui-state-active');
       
   200 
       
   201 				// seems to be expected behavior that the show callback is fired
       
   202 				self.element.queue("tabs", function() {
       
   203 					self._trigger('show', null, self._ui(self.anchors[o.selected], self.panels[o.selected]));
       
   204 				});
       
   205 				
       
   206 				this.load(o.selected);
       
   207 			}
       
   208 
       
   209 			// clean up to avoid memory leaks in certain versions of IE 6
       
   210 			$(window).bind('unload', function() {
       
   211 				self.lis.add(self.anchors).unbind('.tabs');
       
   212 				self.lis = self.anchors = self.panels = null;
       
   213 			});
       
   214 
       
   215 		}
       
   216 		// update selected after add/remove
       
   217 		else {
       
   218 			o.selected = this.lis.index(this.lis.filter('.ui-tabs-selected'));
       
   219 		}
       
   220 
       
   221 		// update collapsible
       
   222 		this.element[o.collapsible ? 'addClass' : 'removeClass']('ui-tabs-collapsible');
       
   223 
       
   224 		// set or update cookie after init and add/remove respectively
       
   225 		if (o.cookie) {
       
   226 			this._cookie(o.selected, o.cookie);
       
   227 		}
       
   228 
       
   229 		// disable tabs
       
   230 		for (var i = 0, li; (li = this.lis[i]); i++) {
       
   231 			$(li)[$.inArray(i, o.disabled) != -1 &&
       
   232 				!$(li).hasClass('ui-tabs-selected') ? 'addClass' : 'removeClass']('ui-state-disabled');
       
   233 		}
       
   234 
       
   235 		// reset cache if switching from cached to not cached
       
   236 		if (o.cache === false) {
       
   237 			this.anchors.removeData('cache.tabs');
       
   238 		}
       
   239 
       
   240 		// remove all handlers before, tabify may run on existing tabs after add or option change
       
   241 		this.lis.add(this.anchors).unbind('.tabs');
       
   242 
       
   243 		if (o.event != 'mouseover') {
       
   244 			var addState = function(state, el) {
       
   245 				if (el.is(':not(.ui-state-disabled)')) {
       
   246 					el.addClass('ui-state-' + state);
       
   247 				}
       
   248 			};
       
   249 			var removeState = function(state, el) {
       
   250 				el.removeClass('ui-state-' + state);
       
   251 			};
       
   252 			this.lis.bind('mouseover.tabs', function() {
       
   253 				addState('hover', $(this));
       
   254 			});
       
   255 			this.lis.bind('mouseout.tabs', function() {
       
   256 				removeState('hover', $(this));
       
   257 			});
       
   258 			this.anchors.bind('focus.tabs', function() {
       
   259 				addState('focus', $(this).closest('li'));
       
   260 			});
       
   261 			this.anchors.bind('blur.tabs', function() {
       
   262 				removeState('focus', $(this).closest('li'));
       
   263 			});
       
   264 		}
       
   265 
       
   266 		// set up animations
       
   267 		var hideFx, showFx;
       
   268 		if (o.fx) {
       
   269 			if ($.isArray(o.fx)) {
       
   270 				hideFx = o.fx[0];
       
   271 				showFx = o.fx[1];
       
   272 			}
       
   273 			else {
       
   274 				hideFx = showFx = o.fx;
       
   275 			}
       
   276 		}
       
   277 
       
   278 		// Reset certain styles left over from animation
       
   279 		// and prevent IE's ClearType bug...
       
   280 		function resetStyle($el, fx) {
       
   281 			$el.css({ display: '' });
       
   282 			if (!$.support.opacity && fx.opacity) {
       
   283 				$el[0].style.removeAttribute('filter');
       
   284 			}
       
   285 		}
       
   286 
       
   287 		// Show a tab...
       
   288 		var showTab = showFx ?
       
   289 			function(clicked, $show) {
       
   290 				$(clicked).closest('li').addClass('ui-tabs-selected ui-state-active');
       
   291 				$show.hide().removeClass('ui-tabs-hide') // avoid flicker that way
       
   292 					.animate(showFx, showFx.duration || 'normal', function() {
       
   293 						resetStyle($show, showFx);
       
   294 						self._trigger('show', null, self._ui(clicked, $show[0]));
       
   295 					});
       
   296 			} :
       
   297 			function(clicked, $show) {
       
   298 				$(clicked).closest('li').addClass('ui-tabs-selected ui-state-active');
       
   299 				$show.removeClass('ui-tabs-hide');
       
   300 				self._trigger('show', null, self._ui(clicked, $show[0]));
       
   301 			};
       
   302 
       
   303 		// Hide a tab, $show is optional...
       
   304 		var hideTab = hideFx ?
       
   305 			function(clicked, $hide) {
       
   306 				$hide.animate(hideFx, hideFx.duration || 'normal', function() {
       
   307 					self.lis.removeClass('ui-tabs-selected ui-state-active');
       
   308 					$hide.addClass('ui-tabs-hide');
       
   309 					resetStyle($hide, hideFx);
       
   310 					self.element.dequeue("tabs");
       
   311 				});
       
   312 			} :
       
   313 			function(clicked, $hide, $show) {
       
   314 				self.lis.removeClass('ui-tabs-selected ui-state-active');
       
   315 				$hide.addClass('ui-tabs-hide');
       
   316 				self.element.dequeue("tabs");
       
   317 			};
       
   318 
       
   319 		// attach tab event handler, unbind to avoid duplicates from former tabifying...
       
   320 		this.anchors.bind(o.event + '.tabs', function() {
       
   321 			var el = this, $li = $(this).closest('li'), $hide = self.panels.filter(':not(.ui-tabs-hide)'),
       
   322 					$show = $(self._sanitizeSelector(this.hash));
       
   323 
       
   324 			// If tab is already selected and not collapsible or tab disabled or
       
   325 			// or is already loading or click callback returns false stop here.
       
   326 			// Check if click handler returns false last so that it is not executed
       
   327 			// for a disabled or loading tab!
       
   328 			if (($li.hasClass('ui-tabs-selected') && !o.collapsible) ||
       
   329 				$li.hasClass('ui-state-disabled') ||
       
   330 				$li.hasClass('ui-state-processing') ||
       
   331 				self._trigger('select', null, self._ui(this, $show[0])) === false) {
       
   332 				this.blur();
       
   333 				return false;
       
   334 			}
       
   335 
       
   336 			o.selected = self.anchors.index(this);
       
   337 
       
   338 			self.abort();
       
   339 
       
   340 			// if tab may be closed
       
   341 			if (o.collapsible) {
       
   342 				if ($li.hasClass('ui-tabs-selected')) {
       
   343 					o.selected = -1;
       
   344 
       
   345 					if (o.cookie) {
       
   346 						self._cookie(o.selected, o.cookie);
       
   347 					}
       
   348 
       
   349 					self.element.queue("tabs", function() {
       
   350 						hideTab(el, $hide);
       
   351 					}).dequeue("tabs");
       
   352 					
       
   353 					this.blur();
       
   354 					return false;
       
   355 				}
       
   356 				else if (!$hide.length) {
       
   357 					if (o.cookie) {
       
   358 						self._cookie(o.selected, o.cookie);
       
   359 					}
       
   360 					
       
   361 					self.element.queue("tabs", function() {
       
   362 						showTab(el, $show);
       
   363 					});
       
   364 
       
   365 					self.load(self.anchors.index(this)); // TODO make passing in node possible, see also http://dev.jqueryui.com/ticket/3171
       
   366 					
       
   367 					this.blur();
       
   368 					return false;
       
   369 				}
       
   370 			}
       
   371 
       
   372 			if (o.cookie) {
       
   373 				self._cookie(o.selected, o.cookie);
       
   374 			}
       
   375 
       
   376 			// show new tab
       
   377 			if ($show.length) {
       
   378 				if ($hide.length) {
       
   379 					self.element.queue("tabs", function() {
       
   380 						hideTab(el, $hide);
       
   381 					});
       
   382 				}
       
   383 				self.element.queue("tabs", function() {
       
   384 					showTab(el, $show);
       
   385 				});
       
   386 				
       
   387 				self.load(self.anchors.index(this));
       
   388 			}
       
   389 			else {
       
   390 				throw 'jQuery UI Tabs: Mismatching fragment identifier.';
       
   391 			}
       
   392 
       
   393 			// Prevent IE from keeping other link focussed when using the back button
       
   394 			// and remove dotted border from clicked link. This is controlled via CSS
       
   395 			// in modern browsers; blur() removes focus from address bar in Firefox
       
   396 			// which can become a usability and annoying problem with tabs('rotate').
       
   397 			if ($.browser.msie) {
       
   398 				this.blur();
       
   399 			}
       
   400 
       
   401 		});
       
   402 
       
   403 		// disable click in any case
       
   404 		this.anchors.bind('click.tabs', function(){return false;});
       
   405 
       
   406 	},
       
   407 
       
   408 	destroy: function() {
       
   409 		var o = this.options;
       
   410 
       
   411 		this.abort();
       
   412 		
       
   413 		this.element.unbind('.tabs')
       
   414 			.removeClass('ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible')
       
   415 			.removeData('tabs');
       
   416 
       
   417 		this.list.removeClass('ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all');
       
   418 
       
   419 		this.anchors.each(function() {
       
   420 			var href = $.data(this, 'href.tabs');
       
   421 			if (href) {
       
   422 				this.href = href;
       
   423 			}
       
   424 			var $this = $(this).unbind('.tabs');
       
   425 			$.each(['href', 'load', 'cache'], function(i, prefix) {
       
   426 				$this.removeData(prefix + '.tabs');
       
   427 			});
       
   428 		});
       
   429 
       
   430 		this.lis.unbind('.tabs').add(this.panels).each(function() {
       
   431 			if ($.data(this, 'destroy.tabs')) {
       
   432 				$(this).remove();
       
   433 			}
       
   434 			else {
       
   435 				$(this).removeClass([
       
   436 					'ui-state-default',
       
   437 					'ui-corner-top',
       
   438 					'ui-tabs-selected',
       
   439 					'ui-state-active',
       
   440 					'ui-state-hover',
       
   441 					'ui-state-focus',
       
   442 					'ui-state-disabled',
       
   443 					'ui-tabs-panel',
       
   444 					'ui-widget-content',
       
   445 					'ui-corner-bottom',
       
   446 					'ui-tabs-hide'
       
   447 				].join(' '));
       
   448 			}
       
   449 		});
       
   450 
       
   451 		if (o.cookie) {
       
   452 			this._cookie(null, o.cookie);
       
   453 		}
       
   454 
       
   455 		return this;
       
   456 	},
       
   457 
       
   458 	add: function(url, label, index) {
       
   459 		if (index === undefined) {
       
   460 			index = this.anchors.length; // append by default
       
   461 		}
       
   462 
       
   463 		var self = this, o = this.options,
       
   464 			$li = $(o.tabTemplate.replace(/#\{href\}/g, url).replace(/#\{label\}/g, label)),
       
   465 			id = !url.indexOf('#') ? url.replace('#', '') : this._tabId($('a', $li)[0]);
       
   466 
       
   467 		$li.addClass('ui-state-default ui-corner-top').data('destroy.tabs', true);
       
   468 
       
   469 		// try to find an existing element before creating a new one
       
   470 		var $panel = $('#' + id);
       
   471 		if (!$panel.length) {
       
   472 			$panel = $(o.panelTemplate).attr('id', id).data('destroy.tabs', true);
       
   473 		}
       
   474 		$panel.addClass('ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide');
       
   475 
       
   476 		if (index >= this.lis.length) {
       
   477 			$li.appendTo(this.list);
       
   478 			$panel.appendTo(this.list[0].parentNode);
       
   479 		}
       
   480 		else {
       
   481 			$li.insertBefore(this.lis[index]);
       
   482 			$panel.insertBefore(this.panels[index]);
       
   483 		}
       
   484 
       
   485 		o.disabled = $.map(o.disabled,
       
   486 			function(n, i) { return n >= index ? ++n : n; });
       
   487 
       
   488 		this._tabify();
       
   489 
       
   490 		if (this.anchors.length == 1) { // after tabify
       
   491 			o.selected = 0;
       
   492 			$li.addClass('ui-tabs-selected ui-state-active');
       
   493 			$panel.removeClass('ui-tabs-hide');
       
   494 			this.element.queue("tabs", function() {
       
   495 				self._trigger('show', null, self._ui(self.anchors[0], self.panels[0]));
       
   496 			});
       
   497 				
       
   498 			this.load(0);
       
   499 		}
       
   500 
       
   501 		// callback
       
   502 		this._trigger('add', null, this._ui(this.anchors[index], this.panels[index]));
       
   503 		return this;
       
   504 	},
       
   505 
       
   506 	remove: function(index) {
       
   507 		var o = this.options, $li = this.lis.eq(index).remove(),
       
   508 			$panel = this.panels.eq(index).remove();
       
   509 
       
   510 		// If selected tab was removed focus tab to the right or
       
   511 		// in case the last tab was removed the tab to the left.
       
   512 		if ($li.hasClass('ui-tabs-selected') && this.anchors.length > 1) {
       
   513 			this.select(index + (index + 1 < this.anchors.length ? 1 : -1));
       
   514 		}
       
   515 
       
   516 		o.disabled = $.map($.grep(o.disabled, function(n, i) { return n != index; }),
       
   517 			function(n, i) { return n >= index ? --n : n; });
       
   518 
       
   519 		this._tabify();
       
   520 
       
   521 		// callback
       
   522 		this._trigger('remove', null, this._ui($li.find('a')[0], $panel[0]));
       
   523 		return this;
       
   524 	},
       
   525 
       
   526 	enable: function(index) {
       
   527 		var o = this.options;
       
   528 		if ($.inArray(index, o.disabled) == -1) {
       
   529 			return;
       
   530 		}
       
   531 
       
   532 		this.lis.eq(index).removeClass('ui-state-disabled');
       
   533 		o.disabled = $.grep(o.disabled, function(n, i) { return n != index; });
       
   534 
       
   535 		// callback
       
   536 		this._trigger('enable', null, this._ui(this.anchors[index], this.panels[index]));
       
   537 		return this;
       
   538 	},
       
   539 
       
   540 	disable: function(index) {
       
   541 		var self = this, o = this.options;
       
   542 		if (index != o.selected) { // cannot disable already selected tab
       
   543 			this.lis.eq(index).addClass('ui-state-disabled');
       
   544 
       
   545 			o.disabled.push(index);
       
   546 			o.disabled.sort();
       
   547 
       
   548 			// callback
       
   549 			this._trigger('disable', null, this._ui(this.anchors[index], this.panels[index]));
       
   550 		}
       
   551 
       
   552 		return this;
       
   553 	},
       
   554 
       
   555 	select: function(index) {
       
   556 		if (typeof index == 'string') {
       
   557 			index = this.anchors.index(this.anchors.filter('[href$=' + index + ']'));
       
   558 		}
       
   559 		else if (index === null) { // usage of null is deprecated, TODO remove in next release
       
   560 			index = -1;
       
   561 		}
       
   562 		if (index == -1 && this.options.collapsible) {
       
   563 			index = this.options.selected;
       
   564 		}
       
   565 
       
   566 		this.anchors.eq(index).trigger(this.options.event + '.tabs');
       
   567 		return this;
       
   568 	},
       
   569 
       
   570 	load: function(index) {
       
   571 		var self = this, o = this.options, a = this.anchors.eq(index)[0], url = $.data(a, 'load.tabs');
       
   572 
       
   573 		this.abort();
       
   574 
       
   575 		// not remote or from cache
       
   576 		if (!url || this.element.queue("tabs").length !== 0 && $.data(a, 'cache.tabs')) {
       
   577 			this.element.dequeue("tabs");
       
   578 			return;
       
   579 		}
       
   580 
       
   581 		// load remote from here on
       
   582 		this.lis.eq(index).addClass('ui-state-processing');
       
   583 
       
   584 		if (o.spinner) {
       
   585 			var span = $('span', a);
       
   586 			span.data('label.tabs', span.html()).html(o.spinner);
       
   587 		}
       
   588 
       
   589 		this.xhr = $.ajax($.extend({}, o.ajaxOptions, {
       
   590 			url: url,
       
   591 			success: function(r, s) {
       
   592 				$(self._sanitizeSelector(a.hash)).html(r);
       
   593 
       
   594 				// take care of tab labels
       
   595 				self._cleanup();
       
   596 
       
   597 				if (o.cache) {
       
   598 					$.data(a, 'cache.tabs', true); // if loaded once do not load them again
       
   599 				}
       
   600 
       
   601 				// callbacks
       
   602 				self._trigger('load', null, self._ui(self.anchors[index], self.panels[index]));
       
   603 				try {
       
   604 					o.ajaxOptions.success(r, s);
       
   605 				}
       
   606 				catch (e) {}
       
   607 			},
       
   608 			error: function(xhr, s, e) {
       
   609 				// take care of tab labels
       
   610 				self._cleanup();
       
   611 
       
   612 				// callbacks
       
   613 				self._trigger('load', null, self._ui(self.anchors[index], self.panels[index]));
       
   614 				try {
       
   615 					// Passing index avoid a race condition when this method is
       
   616 					// called after the user has selected another tab.
       
   617 					// Pass the anchor that initiated this request allows
       
   618 					// loadError to manipulate the tab content panel via $(a.hash)
       
   619 					o.ajaxOptions.error(xhr, s, index, a);
       
   620 				}
       
   621 				catch (e) {}
       
   622 			}
       
   623 		}));
       
   624 
       
   625 		// last, so that load event is fired before show...
       
   626 		self.element.dequeue("tabs");
       
   627 
       
   628 		return this;
       
   629 	},
       
   630 
       
   631 	abort: function() {
       
   632 		// stop possibly running animations
       
   633 		this.element.queue([]);
       
   634 		this.panels.stop(false, true);
       
   635 
       
   636 		// "tabs" queue must not contain more than two elements,
       
   637 		// which are the callbacks for the latest clicked tab...
       
   638 		this.element.queue("tabs", this.element.queue("tabs").splice(-2, 2));
       
   639 
       
   640 		// terminate pending requests from other tabs
       
   641 		if (this.xhr) {
       
   642 			this.xhr.abort();
       
   643 			delete this.xhr;
       
   644 		}
       
   645 
       
   646 		// take care of tab labels
       
   647 		this._cleanup();
       
   648 		return this;
       
   649 	},
       
   650 
       
   651 	url: function(index, url) {
       
   652 		this.anchors.eq(index).removeData('cache.tabs').data('load.tabs', url);
       
   653 		return this;
       
   654 	},
       
   655 
       
   656 	length: function() {
       
   657 		return this.anchors.length;
       
   658 	}
       
   659 
       
   660 });
       
   661 
       
   662 $.extend($.ui.tabs, {
       
   663 	version: '1.8.1'
       
   664 });
       
   665 
       
   666 /*
       
   667  * Tabs Extensions
       
   668  */
       
   669 
       
   670 /*
       
   671  * Rotate
       
   672  */
       
   673 $.extend($.ui.tabs.prototype, {
       
   674 	rotation: null,
       
   675 	rotate: function(ms, continuing) {
       
   676 
       
   677 		var self = this, o = this.options;
       
   678 		
       
   679 		var rotate = self._rotate || (self._rotate = function(e) {
       
   680 			clearTimeout(self.rotation);
       
   681 			self.rotation = setTimeout(function() {
       
   682 				var t = o.selected;
       
   683 				self.select( ++t < self.anchors.length ? t : 0 );
       
   684 			}, ms);
       
   685 			
       
   686 			if (e) {
       
   687 				e.stopPropagation();
       
   688 			}
       
   689 		});
       
   690 		
       
   691 		var stop = self._unrotate || (self._unrotate = !continuing ?
       
   692 			function(e) {
       
   693 				if (e.clientX) { // in case of a true click
       
   694 					self.rotate(null);
       
   695 				}
       
   696 			} :
       
   697 			function(e) {
       
   698 				t = o.selected;
       
   699 				rotate();
       
   700 			});
       
   701 
       
   702 		// start rotation
       
   703 		if (ms) {
       
   704 			this.element.bind('tabsshow', rotate);
       
   705 			this.anchors.bind(o.event + '.tabs', stop);
       
   706 			rotate();
       
   707 		}
       
   708 		// stop rotation
       
   709 		else {
       
   710 			clearTimeout(self.rotation);
       
   711 			this.element.unbind('tabsshow', rotate);
       
   712 			this.anchors.unbind(o.event + '.tabs', stop);
       
   713 			delete this._rotate;
       
   714 			delete this._unrotate;
       
   715 		}
       
   716 
       
   717 		return this;
       
   718 	}
       
   719 });
       
   720 
       
   721 })(jQuery);