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