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