Changeset 7935 for trunk/wp-includes/js/jquery/ui.tabs.js
- Timestamp:
- 05/15/2008 03:45:42 AM (16 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/wp-includes/js/jquery/ui.tabs.js
r6588 r7935 1 1 /* 2 * Tabs 3 - New WaveTabs2 * jQuery UI Tabs 3 3 * 4 * Copyright (c) 2007 Klaus Hartl (stilbuero.de)4 * Copyright (c) 2007, 2008 Klaus Hartl (stilbuero.de) 5 5 * Dual licensed under the MIT (MIT-LICENSE.txt) 6 6 * and GPL (GPL-LICENSE.txt) licenses. 7 * 8 * http://docs.jquery.com/UI/Tabs 9 * 10 * Depends: 11 * ui.core.js 12 * 13 * Revision: $Id: ui.tabs.js 5547 2008-05-10 08:33:38Z klaus.hartl $ 7 14 */ 8 9 (function($) { 10 11 // if the UI scope is not availalable, add it 12 $.ui = $.ui || {}; 13 14 // tabs initialization 15 $.fn.tabs = function(initial, options) { 16 if (initial && initial.constructor == Object) { // shift arguments 17 options = initial; 18 initial = null; 19 } 20 options = options || {}; 21 22 initial = initial && initial.constructor == Number && --initial || 0; 23 24 return this.each(function() { 25 new $.ui.tabs(this, $.extend(options, { initial: initial })); 26 }); 27 }; 28 29 // other chainable tabs methods 30 $.each(['Add', 'Remove', 'Enable', 'Disable', 'Click', 'Load', 'Href'], function(i, method) { 31 $.fn['tabs' + method] = function() { 32 var args = arguments; 33 return this.each(function() { 34 var instance = $.ui.tabs.getInstance(this); 35 instance[method.toLowerCase()].apply(instance, args); 36 }); 37 }; 38 }); 39 $.fn.tabsSelected = function() { 40 var selected = -1; 41 if (this[0]) { 42 var instance = $.ui.tabs.getInstance(this[0]), 43 $lis = $('li', this); 44 selected = $lis.index( $lis.filter('.' + instance.options.selectedClass)[0] ); 45 } 46 return selected >= 0 ? ++selected : -1; 47 }; 48 49 // tabs class 50 $.ui.tabs = function(el, options) { 51 52 this.source = el; 53 54 this.options = $.extend({ 55 56 // basic setup 57 initial: 0, 58 event: 'click', 59 disabled: [], 60 cookie: null, // pass options object as expected by cookie plugin: { expires: 7, path: '/', domain: 'jquery.com', secure: true } 61 // TODO bookmarkable: $.ajaxHistory ? true : false, 62 unselected: false, 63 unselect: options.unselected ? true : false, 64 65 // Ajax 66 spinner: 'Loading…', 67 cache: false, 68 idPrefix: 'ui-tabs-', 69 ajaxOptions: {}, 70 71 // animations 72 /*fxFade: null, 73 fxSlide: null, 74 fxShow: null, 75 fxHide: null,*/ 76 fxSpeed: 'normal', 77 /*fxShowSpeed: null, 78 fxHideSpeed: null,*/ 79 80 // callbacks 81 add: function() {}, 82 remove: function() {}, 83 enable: function() {}, 84 disable: function() {}, 85 click: function() {}, 86 hide: function() {}, 87 show: function() {}, 88 load: function() {}, 89 90 // templates 91 tabTemplate: '<li><a href="#{href}"><span>#{text}</span></a></li>', 92 panelTemplate: '<div></div>', 93 94 // CSS classes 95 navClass: 'ui-tabs-nav', 96 selectedClass: 'ui-tabs-selected', 97 unselectClass: 'ui-tabs-unselect', 98 disabledClass: 'ui-tabs-disabled', 99 panelClass: 'ui-tabs-panel', 100 hideClass: 'ui-tabs-hide', 101 loadingClass: 'ui-tabs-loading' 102 103 }, options); 104 105 this.options.event += '.ui-tabs'; // namespace event 106 this.options.cookie = $.cookie && $.cookie.constructor == Function && this.options.cookie; 107 108 // save instance for later 109 $.data(el, $.ui.tabs.INSTANCE_KEY, this); 110 111 // create tabs 112 this.tabify(true); 113 }; 114 115 // static 116 $.ui.tabs.INSTANCE_KEY = 'ui_tabs_instance'; 117 $.ui.tabs.getInstance = function(el) { 118 return $.data(el, $.ui.tabs.INSTANCE_KEY); 119 }; 120 121 // instance methods 122 $.extend($.ui.tabs.prototype, { 123 tabId: function(a) { 124 return a.title ? a.title.replace(/\s/g, '_') 125 : this.options.idPrefix + $.data(a); 126 }, 127 tabify: function(init) { 128 129 this.$lis = $('li:has(a[href])', this.source); 130 this.$tabs = this.$lis.map(function() { return $('a', this)[0] }); 131 this.$panels = $([]); 132 133 var self = this, o = this.options; 134 135 this.$tabs.each(function(i, a) { 136 // inline tab 137 if (a.hash && a.hash.replace('#', '')) { // Safari 2 reports '#' for an empty hash 138 self.$panels = self.$panels.add(a.hash); 139 } 140 // remote tab 141 else if ($(a).attr('href') != '#') { // prevent loading the page itself if href is just "#" 142 $.data(a, 'href', a.href); 143 var id = self.tabId(a); 144 a.href = '#' + id; 145 self.$panels = self.$panels.add( 146 $('#' + id)[0] || $(o.panelTemplate).attr('id', id).addClass(o.panelClass) 147 .insertAfter( self.$panels[i - 1] || self.source ) 148 ); 149 } 150 // invalid tab href 151 else { 152 o.disabled.push(i + 1); 153 } 154 }); 155 156 if (init) { 157 158 // attach necessary classes for styling if not present 159 $(this.source).hasClass(o.navClass) || $(this.source).addClass(o.navClass); 160 this.$panels.each(function() { 161 var $this = $(this); 162 $this.hasClass(o.panelClass) || $this.addClass(o.panelClass); 163 }); 164 165 // disabled tabs 166 for (var i = 0, position; position = o.disabled[i]; i++) { 167 this.disable(position); 168 } 169 170 // Try to retrieve initial tab: 171 // 1. from fragment identifier in url if present 172 // 2. from cookie 173 // 3. from selected class attribute on <li> 174 // 4. otherwise use given initial argument 175 // 5. check if tab is disabled 176 this.$tabs.each(function(i, a) { 177 if (location.hash) { 178 if (a.hash == location.hash) { 179 o.initial = i; 180 // prevent page scroll to fragment 181 //if (($.browser.msie || $.browser.opera) && !o.remote) { 182 if ($.browser.msie || $.browser.opera) { 183 var $toShow = $(location.hash), toShowId = $toShow.attr('id'); 184 $toShow.attr('id', ''); 185 setTimeout(function() { 186 $toShow.attr('id', toShowId); // restore id 187 }, 500); 188 } 189 scrollTo(0, 0); 190 return false; // break 191 } 192 } else if (o.cookie) { 193 o.initial = parseInt($.cookie( $.ui.tabs.INSTANCE_KEY + $.data(self.source) )) || 0; 194 return false; // break 195 } else if ( self.$lis.eq(i).hasClass(o.selectedClass) ) { 196 o.initial = i; 197 return false; // break 198 } 199 }); 200 var n = this.$lis.length; 201 while (this.$lis.eq(o.initial).hasClass(o.disabledClass) && n) { 202 o.initial = ++o.initial < this.$lis.length ? o.initial : 0; 203 n--; 204 } 205 if (!n) { // all tabs disabled, set option unselected to true 206 o.unselected = o.unselect = true; 207 } 208 209 // highlight selected tab 210 this.$panels.addClass(o.hideClass); 211 this.$lis.removeClass(o.selectedClass); 212 if (!o.unselected) { 213 this.$panels.eq(o.initial).show().removeClass(o.hideClass); // use show and remove class to show in any case no matter how it has been hidden before 214 this.$lis.eq(o.initial).addClass(o.selectedClass); 215 } 216 217 // load if remote tab 218 var href = !o.unselected && $.data(this.$tabs[o.initial], 'href'); 219 if (href) { 220 this.load(o.initial + 1, href); 221 } 222 223 // disable click if event is configured to something else 224 if (!/^click/.test(o.event)) { 225 this.$tabs.bind('click', function(e) { e.preventDefault(); }); 226 } 227 228 } 229 230 // setup animations 231 var showAnim = {}, showSpeed = o.fxShowSpeed || o.fxSpeed, 232 hideAnim = {}, hideSpeed = o.fxHideSpeed || o.fxSpeed; 233 if (o.fxSlide || o.fxFade) { 234 if (o.fxSlide) { 235 showAnim['height'] = 'show'; 236 hideAnim['height'] = 'hide'; 237 } 238 if (o.fxFade) { 239 showAnim['opacity'] = 'show'; 240 hideAnim['opacity'] = 'hide'; 241 } 242 } else { 243 if (o.fxShow) { 244 showAnim = o.fxShow; 245 } else { // use some kind of animation to prevent browser scrolling to the tab 246 showAnim['min-width'] = 0; // avoid opacity, causes flicker in Firefox 247 showSpeed = 1; // as little as 1 is sufficient 248 } 249 if (o.fxHide) { 250 hideAnim = o.fxHide; 251 } else { // use some kind of animation to prevent browser scrolling to the tab 252 hideAnim['min-width'] = 0; // avoid opacity, causes flicker in Firefox 253 hideSpeed = 1; // as little as 1 is sufficient 254 } 255 } 256 257 // reset some styles to maintain print style sheets etc. 258 var resetCSS = { display: '', overflow: '', height: '' }; 259 if (!$.browser.msie) { // not in IE to prevent ClearType font issue 260 resetCSS['opacity'] = ''; 261 } 262 263 // Hide a tab, animation prevents browser scrolling to fragment, 264 // $show is optional. 265 function hideTab(clicked, $hide, $show) { 266 $hide.animate(hideAnim, hideSpeed, function() { // 267 $hide.addClass(o.hideClass).css(resetCSS); // maintain flexible height and accessibility in print etc. 268 if ($.browser.msie && hideAnim['opacity']) { 269 $hide[0].style.filter = ''; 270 } 271 o.hide(clicked, $hide[0], $show && $show[0] || null); 272 if ($show) { 273 showTab(clicked, $show, $hide); 274 } 275 }); 276 } 277 278 // Show a tab, animation prevents browser scrolling to fragment, 279 // $hide is optional 280 function showTab(clicked, $show, $hide) { 281 if (!(o.fxSlide || o.fxFade || o.fxShow)) { 282 $show.css('display', 'block'); // prevent occasionally occuring flicker in Firefox cause by gap between showing and hiding the tab panels 283 } 284 $show.animate(showAnim, showSpeed, function() { 285 $show.removeClass(o.hideClass).css(resetCSS); // maintain flexible height and accessibility in print etc. 286 if ($.browser.msie && showAnim['opacity']) { 287 $show[0].style.filter = ''; 288 } 289 o.show(clicked, $show[0], $hide && $hide[0] || null); 290 }); 291 } 292 293 // switch a tab 294 function switchTab(clicked, $li, $hide, $show) { 295 /*if (o.bookmarkable && trueClick) { // add to history only if true click occured, not a triggered click 296 $.ajaxHistory.update(clicked.hash); 297 }*/ 298 $li.addClass(o.selectedClass) 299 .siblings().removeClass(o.selectedClass); 300 hideTab(clicked, $hide, $show); 301 } 302 303 // attach tab event handler, unbind to avoid duplicates from former tabifying... 304 this.$tabs.unbind(o.event).bind(o.event, function() { 305 306 //var trueClick = e.clientX; // add to history only if true click occured, not a triggered click 307 var $li = $(this).parents('li:eq(0)'), 308 $hide = self.$panels.filter(':visible'), 309 $show = $(this.hash); 310 311 // If tab is already selected and not unselectable or tab disabled or click callback returns false stop here. 312 // Check if click handler returns false last so that it is not executed for a disabled tab! 313 if (($li.hasClass(o.selectedClass) && !o.unselect) || $li.hasClass(o.disabledClass) 314 || o.click(this, $show[0], $hide[0]) === false) { 315 this.blur(); 316 return false; 317 } 318 319 if (o.cookie) { 320 $.cookie($.ui.tabs.INSTANCE_KEY + $.data(self.source), self.$tabs.index(this), o.cookie); 321 } 322 323 // if tab may be closed 324 if (o.unselect) { 325 if ($li.hasClass(o.selectedClass)) { 326 $li.removeClass(o.selectedClass); 327 self.$panels.stop(); 328 hideTab(this, $hide); 329 this.blur(); 330 return false; 331 } else if (!$hide.length) { 332 self.$panels.stop(); 333 if ($.data(this, 'href')) { // remote tab 334 var a = this; 335 self.load(self.$tabs.index(this) + 1, $.data(this, 'href'), function() { 336 $li.addClass(o.selectedClass).addClass(o.unselectClass); 337 showTab(a, $show); 338 }); 339 } else { 340 $li.addClass(o.selectedClass).addClass(o.unselectClass); 341 showTab(this, $show); 342 } 343 this.blur(); 344 return false; 345 } 346 } 347 348 // stop possibly running animations 349 self.$panels.stop(); 350 351 // show new tab 352 if ($show.length) { 353 354 // prevent scrollbar scrolling to 0 and than back in IE7, happens only if bookmarking/history is enabled 355 /*if ($.browser.msie && o.bookmarkable) { 356 var showId = this.hash.replace('#', ''); 357 $show.attr('id', ''); 358 setTimeout(function() { 359 $show.attr('id', showId); // restore id 360 }, 0); 361 }*/ 362 363 if ($.data(this, 'href')) { // remote tab 364 var a = this; 365 self.load(self.$tabs.index(this) + 1, $.data(this, 'href'), function() { 366 switchTab(a, $li, $hide, $show); 367 }); 368 } else { 369 switchTab(this, $li, $hide, $show); 370 } 371 372 // Set scrollbar to saved position - need to use timeout with 0 to prevent browser scroll to target of hash 373 /*var scrollX = window.pageXOffset || document.documentElement && document.documentElement.scrollLeft || document.body.scrollLeft || 0; 374 var scrollY = window.pageYOffset || document.documentElement && document.documentElement.scrollTop || document.body.scrollTop || 0; 375 setTimeout(function() { 376 scrollTo(scrollX, scrollY); 377 }, 0);*/ 378 379 } else { 380 throw 'jQuery UI Tabs: Mismatching fragment identifier.'; 381 } 382 383 // Prevent IE from keeping other link focussed when using the back button 384 // and remove dotted border from clicked link. This is controlled in modern 385 // browsers via CSS, also blur removes focus from address bar in Firefox 386 // which can become a usability and annoying problem with tabsRotate. 387 if ($.browser.msie) { 388 this.blur(); 389 } 390 391 //return o.bookmarkable && !!trueClick; // convert trueClick == undefined to Boolean required in IE 392 return false; 393 394 }); 395 396 }, 397 add: function(url, text, position) { 398 if (url && text) { 399 position = position || this.$tabs.length; // append by default 400 401 var o = this.options, 402 $li = $(o.tabTemplate.replace(/#\{href\}/, url).replace(/#\{text\}/, text)); 403 404 var id = url.indexOf('#') == 0 ? url.replace('#', '') : this.tabId( $('a:first-child', $li)[0] ); 405 406 // try to find an existing element before creating a new one 407 var $panel = $('#' + id); 408 $panel = $panel.length && $panel 409 || $(o.panelTemplate).attr('id', id).addClass(o.panelClass).addClass(o.hideClass); 410 if (position >= this.$lis.length) { 411 $li.appendTo(this.source); 412 $panel.appendTo(this.source.parentNode); 413 } else { 414 $li.insertBefore(this.$lis[position - 1]); 415 $panel.insertBefore(this.$panels[position - 1]); 416 } 417 418 this.tabify(); 419 420 if (this.$tabs.length == 1) { 421 $li.addClass(o.selectedClass); 422 $panel.removeClass(o.hideClass); 423 var href = $.data(this.$tabs[0], 'href'); 424 if (href) { 425 this.load(position + 1, href); 426 } 427 } 428 o.add(this.$tabs[position], this.$panels[position]); // callback 429 } else { 430 throw 'jQuery UI Tabs: Not enough arguments to add tab.'; 431 } 432 }, 433 remove: function(position) { 434 if (position && position.constructor == Number) { 435 var o = this.options, $li = this.$lis.eq(position - 1).remove(), 436 $panel = this.$panels.eq(position - 1).remove(); 437 438 // If selected tab was removed focus tab to the right or 439 // tab to the left if last tab was removed. 440 if ($li.hasClass(o.selectedClass) && this.$tabs.length > 1) { 441 this.click(position + (position < this.$tabs.length ? 1 : -1)); 442 } 443 this.tabify(); 444 o.remove($li.end()[0], $panel[0]); // callback 445 } 446 }, 447 enable: function(position) { 448 var o = this.options, $li = this.$lis.eq(position - 1); 449 $li.removeClass(o.disabledClass); 450 if ($.browser.safari) { // fix disappearing tab (that used opacity indicating disabling) after enabling in Safari 2... 451 $li.css('display', 'inline-block'); 452 setTimeout(function() { 453 $li.css('display', 'block') 454 }, 0) 455 } 456 o.enable(this.$tabs[position - 1], this.$panels[position - 1]); // callback 457 }, 458 disable: function(position) { 459 var o = this.options; 460 this.$lis.eq(position - 1).addClass(o.disabledClass); 461 o.disable(this.$tabs[position - 1], this.$panels[position - 1]); // callback 462 }, 463 click: function(position) { 464 this.$tabs.eq(position - 1).trigger(this.options.event); 465 }, 466 load: function(position, url, callback) { 467 var self = this, o = this.options, 468 $a = this.$tabs.eq(position - 1), a = $a[0], $span = $('span', a); 469 470 // shift arguments 471 if (url && url.constructor == Function) { 472 callback = url; 473 url = null; 474 } 475 476 // set new URL or get existing 477 if (url) { 478 $.data(a, 'href', url); 479 } else { 480 url = $.data(a, 'href'); 481 } 482 483 // load 484 if (o.spinner) { 485 $.data(a, 'title', $span.html()); 486 $span.html('<em>' + o.spinner + '</em>'); 487 } 488 var finish = function() { 489 self.$tabs.filter('.' + o.loadingClass).each(function() { 490 $(this).removeClass(o.loadingClass); 491 if (o.spinner) { 492 $('span', this).html( $.data(this, 'title') ); 493 } 494 }); 495 self.xhr = null; 496 }; 497 var ajaxOptions = $.extend(o.ajaxOptions, { 498 url: url, 499 success: function(r) { 500 $(a.hash).html(r); 501 finish(); 502 // This callback is required because the switch has to take 503 // place after loading has completed. 504 if (callback && callback.constructor == Function) { 505 callback(); 506 } 507 if (o.cache) { 508 $.removeData(a, 'href'); // if loaded once do not load them again 509 } 510 o.load(self.$tabs[position - 1], self.$panels[position - 1]); // callback 511 } 512 }); 513 if (this.xhr) { 514 // terminate pending requests from other tabs and restore title 515 this.xhr.abort(); 516 finish(); 517 } 518 $a.addClass(o.loadingClass); 519 setTimeout(function() { // timeout is again required in IE, "wait" for id being restored 520 self.xhr = $.ajax(ajaxOptions); 521 }, 0); 522 523 }, 524 href: function(position, href) { 525 $.data(this.$tabs.eq(position - 1)[0], 'href', href); 526 } 527 }); 15 ;(function($) { 16 17 $.widget("ui.tabs", { 18 init: function() { 19 this.options.event += '.tabs'; // namespace event 20 21 // create tabs 22 this.tabify(true); 23 }, 24 setData: function(key, value) { 25 if ((/^selected/).test(key)) 26 this.select(value); 27 else { 28 this.options[key] = value; 29 this.tabify(); 30 } 31 }, 32 length: function() { 33 return this.$tabs.length; 34 }, 35 tabId: function(a) { 36 return a.title && a.title.replace(/\s/g, '_').replace(/[^A-Za-z0-9\-_:\.]/g, '') 37 || this.options.idPrefix + $.data(a); 38 }, 39 ui: function(tab, panel) { 40 return { 41 instance: this, 42 options: this.options, 43 tab: tab, 44 panel: panel 45 }; 46 }, 47 tabify: function(init) { 48 49 this.$lis = $('li:has(a[href])', this.element); 50 this.$tabs = this.$lis.map(function() { return $('a', this)[0]; }); 51 this.$panels = $([]); 52 53 var self = this, o = this.options; 54 55 this.$tabs.each(function(i, a) { 56 // inline tab 57 if (a.hash && a.hash.replace('#', '')) // Safari 2 reports '#' for an empty hash 58 self.$panels = self.$panels.add(a.hash); 59 // remote tab 60 else if ($(a).attr('href') != '#') { // prevent loading the page itself if href is just "#" 61 $.data(a, 'href.tabs', a.href); // required for restore on destroy 62 $.data(a, 'load.tabs', a.href); // mutable 63 var id = self.tabId(a); 64 a.href = '#' + id; 65 var $panel = $('#' + id); 66 if (!$panel.length) { 67 $panel = $(o.panelTemplate).attr('id', id).addClass(o.panelClass) 68 .insertAfter( self.$panels[i - 1] || self.element ); 69 $panel.data('destroy.tabs', true); 70 } 71 self.$panels = self.$panels.add( $panel ); 72 } 73 // invalid tab href 74 else 75 o.disabled.push(i + 1); 76 }); 77 78 if (init) { 79 80 // attach necessary classes for styling if not present 81 this.element.hasClass(o.navClass) || this.element.addClass(o.navClass); 82 this.$panels.each(function() { 83 var $this = $(this); 84 $this.hasClass(o.panelClass) || $this.addClass(o.panelClass); 85 }); 86 87 // Selected tab 88 // use "selected" option or try to retrieve: 89 // 1. from fragment identifier in url 90 // 2. from cookie 91 // 3. from selected class attribute on <li> 92 if (o.selected === undefined) { 93 if (location.hash) { 94 this.$tabs.each(function(i, a) { 95 if (a.hash == location.hash) { 96 o.selected = i; 97 // prevent page scroll to fragment 98 if ($.browser.msie || $.browser.opera) { // && !o.remote 99 var $toShow = $(location.hash), toShowId = $toShow.attr('id'); 100 $toShow.attr('id', ''); 101 setTimeout(function() { 102 $toShow.attr('id', toShowId); // restore id 103 }, 500); 104 } 105 scrollTo(0, 0); 106 return false; // break 107 } 108 }); 109 } 110 else if (o.cookie) { 111 var index = parseInt($.cookie('ui-tabs' + $.data(self.element)),10); 112 if (index && self.$tabs[index]) 113 o.selected = index; 114 } 115 else if (self.$lis.filter('.' + o.selectedClass).length) 116 o.selected = self.$lis.index( self.$lis.filter('.' + o.selectedClass)[0] ); 117 } 118 o.selected = o.selected === null || o.selected !== undefined ? o.selected : 0; // first tab selected by default 119 120 // Take disabling tabs via class attribute from HTML 121 // into account and update option properly. 122 // A selected tab cannot become disabled. 123 o.disabled = $.unique(o.disabled.concat( 124 $.map(this.$lis.filter('.' + o.disabledClass), 125 function(n, i) { return self.$lis.index(n); } ) 126 )).sort(); 127 if ($.inArray(o.selected, o.disabled) != -1) 128 o.disabled.splice($.inArray(o.selected, o.disabled), 1); 129 130 // highlight selected tab 131 this.$panels.addClass(o.hideClass); 132 this.$lis.removeClass(o.selectedClass); 133 if (o.selected !== null) { 134 this.$panels.eq(o.selected).show().removeClass(o.hideClass); // use show and remove class to show in any case no matter how it has been hidden before 135 this.$lis.eq(o.selected).addClass(o.selectedClass); 136 137 // seems to be expected behavior that the show callback is fired 138 var onShow = function() { 139 $(self.element).triggerHandler('tabsshow', 140 [self.ui(self.$tabs[o.selected], self.$panels[o.selected])], o.show); 141 }; 142 143 // load if remote tab 144 if ($.data(this.$tabs[o.selected], 'load.tabs')) 145 this.load(o.selected, onShow); 146 // just trigger show event 147 else 148 onShow(); 149 150 } 151 152 // clean up to avoid memory leaks in certain versions of IE 6 153 $(window).bind('unload', function() { 154 self.$tabs.unbind('.tabs'); 155 self.$lis = self.$tabs = self.$panels = null; 156 }); 157 158 } 159 160 // disable tabs 161 for (var i = 0, li; li = this.$lis[i]; i++) 162 $(li)[$.inArray(i, o.disabled) != -1 && !$(li).hasClass(o.selectedClass) ? 'addClass' : 'removeClass'](o.disabledClass); 163 164 // reset cache if switching from cached to not cached 165 if (o.cache === false) 166 this.$tabs.removeData('cache.tabs'); 167 168 // set up animations 169 var hideFx, showFx, baseFx = { 'min-width': 0, duration: 1 }, baseDuration = 'normal'; 170 if (o.fx && o.fx.constructor == Array) 171 hideFx = o.fx[0] || baseFx, showFx = o.fx[1] || baseFx; 172 else 173 hideFx = showFx = o.fx || baseFx; 174 175 // reset some styles to maintain print style sheets etc. 176 var resetCSS = { display: '', overflow: '', height: '' }; 177 if (!$.browser.msie) // not in IE to prevent ClearType font issue 178 resetCSS.opacity = ''; 179 180 // Hide a tab, animation prevents browser scrolling to fragment, 181 // $show is optional. 182 function hideTab(clicked, $hide, $show) { 183 $hide.animate(hideFx, hideFx.duration || baseDuration, function() { // 184 $hide.addClass(o.hideClass).css(resetCSS); // maintain flexible height and accessibility in print etc. 185 if ($.browser.msie && hideFx.opacity) 186 $hide[0].style.filter = ''; 187 if ($show) 188 showTab(clicked, $show, $hide); 189 }); 190 } 191 192 // Show a tab, animation prevents browser scrolling to fragment, 193 // $hide is optional. 194 function showTab(clicked, $show, $hide) { 195 if (showFx === baseFx) 196 $show.css('display', 'block'); // prevent occasionally occuring flicker in Firefox cause by gap between showing and hiding the tab panels 197 $show.animate(showFx, showFx.duration || baseDuration, function() { 198 $show.removeClass(o.hideClass).css(resetCSS); // maintain flexible height and accessibility in print etc. 199 if ($.browser.msie && showFx.opacity) 200 $show[0].style.filter = ''; 201 202 // callback 203 $(self.element).triggerHandler('tabsshow', 204 [self.ui(clicked, $show[0])], o.show); 205 206 }); 207 } 208 209 // switch a tab 210 function switchTab(clicked, $li, $hide, $show) { 211 /*if (o.bookmarkable && trueClick) { // add to history only if true click occured, not a triggered click 212 $.ajaxHistory.update(clicked.hash); 213 }*/ 214 $li.addClass(o.selectedClass) 215 .siblings().removeClass(o.selectedClass); 216 hideTab(clicked, $hide, $show); 217 } 218 219 // attach tab event handler, unbind to avoid duplicates from former tabifying... 220 this.$tabs.unbind('.tabs').bind(o.event, function() { 221 222 //var trueClick = e.clientX; // add to history only if true click occured, not a triggered click 223 var $li = $(this).parents('li:eq(0)'), 224 $hide = self.$panels.filter(':visible'), 225 $show = $(this.hash); 226 227 // If tab is already selected and not unselectable or tab disabled or 228 // or is already loading or click callback returns false stop here. 229 // Check if click handler returns false last so that it is not executed 230 // for a disabled or loading tab! 231 if (($li.hasClass(o.selectedClass) && !o.unselect) 232 || $li.hasClass(o.disabledClass) 233 || $(this).hasClass(o.loadingClass) 234 || $(self.element).triggerHandler('tabsselect', [self.ui(this, $show[0])], o.select) === false 235 ) { 236 this.blur(); 237 return false; 238 } 239 240 self.options.selected = self.$tabs.index(this); 241 242 // if tab may be closed 243 if (o.unselect) { 244 if ($li.hasClass(o.selectedClass)) { 245 self.options.selected = null; 246 $li.removeClass(o.selectedClass); 247 self.$panels.stop(); 248 hideTab(this, $hide); 249 this.blur(); 250 return false; 251 } else if (!$hide.length) { 252 self.$panels.stop(); 253 var a = this; 254 self.load(self.$tabs.index(this), function() { 255 $li.addClass(o.selectedClass).addClass(o.unselectClass); 256 showTab(a, $show); 257 }); 258 this.blur(); 259 return false; 260 } 261 } 262 263 if (o.cookie) 264 $.cookie('ui-tabs' + $.data(self.element), self.options.selected, o.cookie); 265 266 // stop possibly running animations 267 self.$panels.stop(); 268 269 // show new tab 270 if ($show.length) { 271 272 // prevent scrollbar scrolling to 0 and than back in IE7, happens only if bookmarking/history is enabled 273 /*if ($.browser.msie && o.bookmarkable) { 274 var showId = this.hash.replace('#', ''); 275 $show.attr('id', ''); 276 setTimeout(function() { 277 $show.attr('id', showId); // restore id 278 }, 0); 279 }*/ 280 281 var a = this; 282 self.load(self.$tabs.index(this), $hide.length ? 283 function() { 284 switchTab(a, $li, $hide, $show); 285 } : 286 function() { 287 $li.addClass(o.selectedClass); 288 showTab(a, $show); 289 } 290 ); 291 292 // Set scrollbar to saved position - need to use timeout with 0 to prevent browser scroll to target of hash 293 /*var scrollX = window.pageXOffset || document.documentElement && document.documentElement.scrollLeft || document.body.scrollLeft || 0; 294 var scrollY = window.pageYOffset || document.documentElement && document.documentElement.scrollTop || document.body.scrollTop || 0; 295 setTimeout(function() { 296 scrollTo(scrollX, scrollY); 297 }, 0);*/ 298 299 } else 300 throw 'jQuery UI Tabs: Mismatching fragment identifier.'; 301 302 // Prevent IE from keeping other link focussed when using the back button 303 // and remove dotted border from clicked link. This is controlled in modern 304 // browsers via CSS, also blur removes focus from address bar in Firefox 305 // which can become a usability and annoying problem with tabsRotate. 306 if ($.browser.msie) 307 this.blur(); 308 309 //return o.bookmarkable && !!trueClick; // convert trueClick == undefined to Boolean required in IE 310 return false; 311 312 }); 313 314 // disable click if event is configured to something else 315 if (!(/^click/).test(o.event)) 316 this.$tabs.bind('click.tabs', function() { return false; }); 317 318 }, 319 add: function(url, label, index) { 320 if (index == undefined) 321 index = this.$tabs.length; // append by default 322 323 var o = this.options; 324 var $li = $(o.tabTemplate.replace(/#\{href\}/, url).replace(/#\{label\}/, label)); 325 $li.data('destroy.tabs', true); 326 327 var id = url.indexOf('#') == 0 ? url.replace('#', '') : this.tabId( $('a:first-child', $li)[0] ); 328 329 // try to find an existing element before creating a new one 330 var $panel = $('#' + id); 331 if (!$panel.length) { 332 $panel = $(o.panelTemplate).attr('id', id) 333 .addClass(o.panelClass).addClass(o.hideClass); 334 $panel.data('destroy.tabs', true); 335 } 336 if (index >= this.$lis.length) { 337 $li.appendTo(this.element); 338 $panel.appendTo(this.element[0].parentNode); 339 } else { 340 $li.insertBefore(this.$lis[index]); 341 $panel.insertBefore(this.$panels[index]); 342 } 343 344 o.disabled = $.map(o.disabled, 345 function(n, i) { return n >= index ? ++n : n }); 346 347 this.tabify(); 348 349 if (this.$tabs.length == 1) { 350 $li.addClass(o.selectedClass); 351 $panel.removeClass(o.hideClass); 352 var href = $.data(this.$tabs[0], 'load.tabs'); 353 if (href) 354 this.load(index, href); 355 } 356 357 // callback 358 this.element.triggerHandler('tabsadd', 359 [this.ui(this.$tabs[index], this.$panels[index])], o.add 360 ); 361 }, 362 remove: function(index) { 363 var o = this.options, $li = this.$lis.eq(index).remove(), 364 $panel = this.$panels.eq(index).remove(); 365 366 // If selected tab was removed focus tab to the right or 367 // in case the last tab was removed the tab to the left. 368 if ($li.hasClass(o.selectedClass) && this.$tabs.length > 1) 369 this.select(index + (index + 1 < this.$tabs.length ? 1 : -1)); 370 371 o.disabled = $.map($.grep(o.disabled, function(n, i) { return n != index; }), 372 function(n, i) { return n >= index ? --n : n }); 373 374 this.tabify(); 375 376 // callback 377 this.element.triggerHandler('tabsremove', 378 [this.ui($li.find('a')[0], $panel[0])], o.remove 379 ); 380 }, 381 enable: function(index) { 382 var o = this.options; 383 if ($.inArray(index, o.disabled) == -1) 384 return; 385 386 var $li = this.$lis.eq(index).removeClass(o.disabledClass); 387 if ($.browser.safari) { // fix disappearing tab (that used opacity indicating disabling) after enabling in Safari 2... 388 $li.css('display', 'inline-block'); 389 setTimeout(function() { 390 $li.css('display', 'block'); 391 }, 0); 392 } 393 394 o.disabled = $.grep(o.disabled, function(n, i) { return n != index; }); 395 396 // callback 397 this.element.triggerHandler('tabsenable', 398 [this.ui(this.$tabs[index], this.$panels[index])], o.enable 399 ); 400 401 }, 402 disable: function(index) { 403 var self = this, o = this.options; 404 if (index != o.selected) { // cannot disable already selected tab 405 this.$lis.eq(index).addClass(o.disabledClass); 406 407 o.disabled.push(index); 408 o.disabled.sort(); 409 410 // callback 411 this.element.triggerHandler('tabsdisable', 412 [this.ui(this.$tabs[index], this.$panels[index])], o.disable 413 ); 414 } 415 }, 416 select: function(index) { 417 if (typeof index == 'string') 418 index = this.$tabs.index( this.$tabs.filter('[href$=' + index + ']')[0] ); 419 this.$tabs.eq(index).trigger(this.options.event); 420 }, 421 load: function(index, callback) { // callback is for internal usage only 422 423 var self = this, o = this.options, $a = this.$tabs.eq(index), a = $a[0], 424 bypassCache = callback == undefined || callback === false, url = $a.data('load.tabs'); 425 426 callback = callback || function() {}; 427 428 // no remote or from cache - just finish with callback 429 if (!url || !bypassCache && $.data(a, 'cache.tabs')) { 430 callback(); 431 return; 432 } 433 434 // load remote from here on 435 436 var inner = function(parent) { 437 var $parent = $(parent), $inner = $parent.find('*:last'); 438 return $inner.length && $inner || $parent; 439 }; 440 var cleanup = function() { 441 self.$tabs.filter('.' + o.loadingClass).removeClass(o.loadingClass) 442 .each(function() { 443 if (o.spinner) 444 inner(this).parent().html(inner(this).data('label.tabs')); 445 }); 446 self.xhr = null; 447 }; 448 449 if (o.spinner) { 450 var label = inner(a).html(); 451 inner(a).wrapInner('<em></em>') 452 .find('em').data('label.tabs', label).html(o.spinner); 453 } 454 455 var ajaxOptions = $.extend({}, o.ajaxOptions, { 456 url: url, 457 success: function(r, s) { 458 $(a.hash).html(r); 459 cleanup(); 460 461 if (o.cache) 462 $.data(a, 'cache.tabs', true); // if loaded once do not load them again 463 464 // callbacks 465 $(self.element).triggerHandler('tabsload', 466 [self.ui(self.$tabs[index], self.$panels[index])], o.load 467 ); 468 o.ajaxOptions.success && o.ajaxOptions.success(r, s); 469 470 // This callback is required because the switch has to take 471 // place after loading has completed. Call last in order to 472 // fire load before show callback... 473 callback(); 474 } 475 }); 476 if (this.xhr) { 477 // terminate pending requests from other tabs and restore tab label 478 this.xhr.abort(); 479 cleanup(); 480 } 481 $a.addClass(o.loadingClass); 482 setTimeout(function() { // timeout is again required in IE, "wait" for id being restored 483 self.xhr = $.ajax(ajaxOptions); 484 }, 0); 485 486 }, 487 url: function(index, url) { 488 this.$tabs.eq(index).removeData('cache.tabs').data('load.tabs', url); 489 }, 490 destroy: function() { 491 var o = this.options; 492 this.element.unbind('.tabs') 493 .removeClass(o.navClass).removeData('tabs'); 494 this.$tabs.each(function() { 495 var href = $.data(this, 'href.tabs'); 496 if (href) 497 this.href = href; 498 var $this = $(this).unbind('.tabs'); 499 $.each(['href', 'load', 'cache'], function(i, prefix) { 500 $this.removeData(prefix + '.tabs'); 501 }); 502 }); 503 this.$lis.add(this.$panels).each(function() { 504 if ($.data(this, 'destroy.tabs')) 505 $(this).remove(); 506 else 507 $(this).removeClass([o.selectedClass, o.unselectClass, 508 o.disabledClass, o.panelClass, o.hideClass].join(' ')); 509 }); 510 } 511 }); 512 513 $.ui.tabs.defaults = { 514 // basic setup 515 unselect: false, 516 event: 'click', 517 disabled: [], 518 cookie: null, // e.g. { expires: 7, path: '/', domain: 'jquery.com', secure: true } 519 // TODO history: false, 520 521 // Ajax 522 spinner: 'Loading…', 523 cache: false, 524 idPrefix: 'ui-tabs-', 525 ajaxOptions: {}, 526 527 // animations 528 fx: null, // e.g. { height: 'toggle', opacity: 'toggle', duration: 200 } 529 530 // templates 531 tabTemplate: '<li><a href="#{href}"><span>#{label}</span></a></li>', 532 panelTemplate: '<div></div>', 533 534 // CSS classes 535 navClass: 'ui-tabs-nav', 536 selectedClass: 'ui-tabs-selected', 537 unselectClass: 'ui-tabs-unselect', 538 disabledClass: 'ui-tabs-disabled', 539 panelClass: 'ui-tabs-panel', 540 hideClass: 'ui-tabs-hide', 541 loadingClass: 'ui-tabs-loading' 542 }; 543 544 $.ui.tabs.getter = "length"; 545 546 /* 547 * Tabs Extensions 548 */ 549 550 /* 551 * Rotate 552 */ 553 $.extend($.ui.tabs.prototype, { 554 rotation: null, 555 rotate: function(ms, continuing) { 556 557 continuing = continuing || false; 558 559 var self = this, t = this.options.selected; 560 561 function start() { 562 self.rotation = setInterval(function() { 563 t = ++t < self.$tabs.length ? t : 0; 564 self.select(t); 565 }, ms); 566 } 567 568 function stop(e) { 569 if (!e || e.clientX) { // only in case of a true click 570 clearInterval(self.rotation); 571 } 572 } 573 574 // start interval 575 if (ms) { 576 start(); 577 if (!continuing) 578 this.$tabs.bind(this.options.event, stop); 579 else 580 this.$tabs.bind(this.options.event, function() { 581 stop(); 582 t = self.options.selected; 583 start(); 584 }); 585 } 586 // stop interval 587 else { 588 stop(); 589 this.$tabs.unbind(this.options.event, stop); 590 } 591 } 592 }); 528 593 529 594 })(jQuery);
Note: See TracChangeset
for help on using the changeset viewer.