jquery.tabs.js 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642
  1. /**
  2. * Tabs - jQuery plugin for accessible, unobtrusive tabs
  3. * @requires jQuery v1.1.1
  4. *
  5. * http://stilbuero.de/tabs/
  6. *
  7. * Copyright (c) 2006 Klaus Hartl (stilbuero.de)
  8. * Dual licensed under the MIT and GPL licenses:
  9. * http://www.opensource.org/licenses/mit-license.php
  10. * http://www.gnu.org/licenses/gpl.html
  11. *
  12. * Version: 2.7.4
  13. */
  14. (function($) { // block scope
  15. $.extend({
  16. tabs: {
  17. remoteCount: 0 // TODO in Tabs 3 this is going to be more cleanly in one single namespace
  18. }
  19. });
  20. /**
  21. * Create an accessible, unobtrusive tab interface based on a particular HTML structure.
  22. *
  23. * The underlying HTML has to look like this:
  24. *
  25. * <div id="container">
  26. * <ul>
  27. * <li><a href="#fragment-1">Section 1</a></li>
  28. * <li><a href="#fragment-2">Section 2</a></li>
  29. * <li><a href="#fragment-3">Section 3</a></li>
  30. * </ul>
  31. * <div id="fragment-1">
  32. *
  33. * </div>
  34. * <div id="fragment-2">
  35. *
  36. * </div>
  37. * <div id="fragment-3">
  38. *
  39. * </div>
  40. * </div>
  41. *
  42. * Each anchor in the unordered list points directly to a section below represented by one of the
  43. * divs (the URI in the anchor's href attribute refers to the fragment with the corresponding id).
  44. * Because such HTML structure is fully functional on its own, e.g. without JavaScript, the tab
  45. * interface is accessible and unobtrusive.
  46. *
  47. * A tab is also bookmarkable via hash in the URL. Use the History/Remote plugin (Tabs will
  48. * auto-detect its presence) to fix the back (and forward) button.
  49. *
  50. * @example $('#container').tabs();
  51. * @desc Create a basic tab interface.
  52. * @example $('#container').tabs(2);
  53. * @desc Create a basic tab interface with the second tab initially activated.
  54. * @example $('#container').tabs({disabled: [3, 4]});
  55. * @desc Create a tab interface with the third and fourth tab being disabled.
  56. * @example $('#container').tabs({fxSlide: true});
  57. * @desc Create a tab interface that uses slide down/up animations for showing/hiding tab
  58. * content upon tab switching.
  59. *
  60. * @param Number initial An integer specifying the position of the tab (no zero-based index) that
  61. * gets activated at first (on page load). Two alternative ways to specify
  62. * the active tab will overrule this argument. First a li element
  63. * (representing one single tab) belonging to the selected tab class, e.g.
  64. * set the selected tab class (default: "tabs-selected", see option
  65. * selectedClass) for one of the unordered li elements in the HTML source.
  66. * In addition if a fragment identifier/hash in the URL of the page refers
  67. * to the id of a tab container of a tab interface the corresponding tab will
  68. * be activated and both the initial argument as well as an eventually
  69. * declared class attribute will be overruled. Defaults to 1 if omitted.
  70. * @param Object settings An object literal containing key/value pairs to provide optional settings.
  71. * @option Array<Number> disabled An array containing the position of the tabs (no zero-based index)
  72. * that should be disabled on initialization. Default value: null.
  73. * A tab can also be disabled by simply adding the disabling class
  74. * (default: "tabs-disabled", see option disabledClass) to the li
  75. * element representing that particular tab.
  76. * @option Boolean bookmarkable Boolean flag indicating if support for bookmarking and history (via
  77. * changing hash in the URL of the browser) is enabled. Default value:
  78. * false, unless the History/Remote plugin is included. In that case the
  79. * default value becomes true. @see $.ajaxHistory.initialize
  80. * @option Boolean remote Boolean flag indicating that tab content has to be loaded remotely from
  81. * the url given in the href attribute of the tab menu anchor elements.
  82. * @option String spinner The content of this string is shown in a tab while remote content is loading.
  83. * Insert plain text as well as an img here. To turn off this notification
  84. * pass an empty string or null. Default: "Loading&#8230;".
  85. * @option String hashPrefix A String that is used for constructing the hash the link's href attribute
  86. * of a remote tab gets altered to, such as "#remote-1".
  87. * Default value: "remote-tab-".
  88. * @option Boolean fxFade Boolean flag indicating whether fade in/out animations are used for tab
  89. * switching. Can be combined with fxSlide. Will overrule fxShow/fxHide.
  90. * Default value: false.
  91. * @option Boolean fxSlide Boolean flag indicating whether slide down/up animations are used for tab
  92. * switching. Can be combined with fxFade. Will overrule fxShow/fxHide.
  93. * Default value: false.
  94. * @option String|Number fxSpeed A string representing one of the three predefined speeds ("slow",
  95. * "normal", or "fast") or the number of milliseconds (e.g. 1000) to
  96. * run an animation. Default value: "normal".
  97. * @option Object fxShow An object literal of the form jQuery's animate function expects for making
  98. * your own, custom animation to reveal a tab upon tab switch. Unlike fxFade
  99. * or fxSlide this animation is independent from an optional hide animation.
  100. * Default value: null. @see animate
  101. * @option Object fxHide An object literal of the form jQuery's animate function expects for making
  102. * your own, custom animation to hide a tab upon tab switch. Unlike fxFade
  103. * or fxSlide this animation is independent from an optional show animation.
  104. * Default value: null. @see animate
  105. * @option String|Number fxShowSpeed A string representing one of the three predefined speeds
  106. * ("slow", "normal", or "fast") or the number of milliseconds
  107. * (e.g. 1000) to run the animation specified in fxShow.
  108. * Default value: fxSpeed.
  109. * @option String|Number fxHideSpeed A string representing one of the three predefined speeds
  110. * ("slow", "normal", or "fast") or the number of milliseconds
  111. * (e.g. 1000) to run the animation specified in fxHide.
  112. * Default value: fxSpeed.
  113. * @option Boolean fxAutoHeight Boolean flag that if set to true causes all tab heights
  114. * to be constant (being the height of the tallest tab).
  115. * Default value: false.
  116. * @option Function onClick A function to be invoked upon tab switch, immediatly after a tab has
  117. * been clicked, e.g. before the other's tab content gets hidden. The
  118. * function gets passed three arguments: the first one is the clicked
  119. * tab (e.g. an anchor element), the second one is the DOM element
  120. * containing the content of the clicked tab (e.g. the div), the third
  121. * argument is the one of the tab that gets hidden. If this callback
  122. * returns false, the tab switch is canceled (use to disallow tab
  123. * switching for the reason of a failed form validation for example).
  124. * Default value: null.
  125. * @option Function onHide A function to be invoked upon tab switch, immediatly after one tab's
  126. * content got hidden (with or without an animation) and right before the
  127. * next tab is revealed. The function gets passed three arguments: the
  128. * first one is the clicked tab (e.g. an anchor element), the second one
  129. * is the DOM element containing the content of the clicked tab, (e.g. the
  130. * div), the third argument is the one of the tab that gets hidden.
  131. * Default value: null.
  132. * @option Function onShow A function to be invoked upon tab switch. This function is invoked
  133. * after the new tab has been revealed, e.g. after the switch is completed.
  134. * The function gets passed three arguments: the first one is the clicked
  135. * tab (e.g. an anchor element), the second one is the DOM element
  136. * containing the content of the clicked tab, (e.g. the div), the third
  137. * argument is the one of the tab that gets hidden. Default value: null.
  138. * @option String navClass A CSS class that is used to identify the tabs unordered list by class if
  139. * the required HTML structure differs from the default one.
  140. * Default value: "tabs-nav".
  141. * @option String selectedClass The CSS class attached to the li element representing the
  142. * currently selected (active) tab. Default value: "tabs-selected".
  143. * @option String disabledClass The CSS class attached to the li element representing a disabled
  144. * tab. Default value: "tabs-disabled".
  145. * @option String containerClass A CSS class that is used to identify tab containers by class if
  146. * the required HTML structure differs from the default one.
  147. * Default value: "tabs-container".
  148. * @option String hideClass The CSS class used for hiding inactive tabs. A class is used instead
  149. * of "display: none" in the style attribute to maintain control over
  150. * visibility in other media types than screen, most notably print.
  151. * Default value: "tabs-hide".
  152. * @option String loadingClass The CSS class used for indicating that an Ajax tab is currently
  153. * loading, for example by showing a spinner.
  154. * Default value: "tabs-loading".
  155. * @option String tabStruct @deprecated A CSS selector or basic XPath expression reflecting a
  156. * nested HTML structure that is different from the default single div
  157. * structure (one div with an id inside the overall container holds one
  158. * tab's content). If for instance an additional div is required to wrap
  159. * up the several tab containers such a structure is expressed by "div>div".
  160. * Default value: "div".
  161. * @type jQuery
  162. *
  163. * @name tabs
  164. * @cat Plugins/Tabs
  165. * @author Klaus Hartl/klaus.hartl@stilbuero.de
  166. */
  167. $.fn.tabs = function(initial, settings) {
  168. // settings
  169. if (typeof initial == 'object') settings = initial; // no initial tab given but a settings object
  170. settings = $.extend({
  171. initial: (initial && typeof initial == 'number' && initial > 0) ? --initial : 0,
  172. disabled: null,
  173. bookmarkable: $.ajaxHistory ? true : false,
  174. remote: false,
  175. spinner: 'Loading&#8230;',
  176. hashPrefix: 'remote-tab-',
  177. fxFade: null,
  178. fxSlide: null,
  179. fxShow: null,
  180. fxHide: null,
  181. fxSpeed: 'normal',
  182. fxShowSpeed: null,
  183. fxHideSpeed: null,
  184. fxAutoHeight: false,
  185. onClick: null,
  186. onHide: null,
  187. onShow: null,
  188. navClass: 'tabs-nav',
  189. selectedClass: 'tabs-selected',
  190. disabledClass: 'tabs-disabled',
  191. containerClass: 'tabs-container',
  192. hideClass: 'tabs-hide',
  193. loadingClass: 'tabs-loading',
  194. tabStruct: 'div'
  195. }, settings || {});
  196. $.browser.msie6 = $.browser.msie && ($.browser.version && $.browser.version < 7 || /MSIE 6.0/.test(navigator.userAgent)); // do not check for 6.0 alone, userAgent in Windows Vista has "Windows NT 6.0"
  197. // helper to prevent scroll to fragment
  198. function unFocus() {
  199. scrollTo(0, 0);
  200. }
  201. // initialize tabs
  202. return this.each(function() {
  203. // remember wrapper for later
  204. var container = this;
  205. // setup nav
  206. var nav = $('ul.' + settings.navClass, container);
  207. nav = nav.size() && nav || $('>ul:eq(0)', container); // fallback to default structure
  208. var tabs = $('a', nav);
  209. // prepare remote tabs
  210. if (settings.remote) {
  211. tabs.each(function() {
  212. var id = settings.hashPrefix + (++$.tabs.remoteCount), hash = '#' + id, url = this.href;
  213. this.href = hash;
  214. $('<div id="' + id + '" class="' + settings.containerClass + '"></div>').appendTo(container);
  215. $(this).bind('loadRemoteTab', function(e, callback) {
  216. var $$ = $(this).addClass(settings.loadingClass), span = $('span', this)[0], tabTitle = span.innerHTML;
  217. if (settings.spinner) {
  218. // TODO if spinner is image
  219. span.innerHTML = '<em>' + settings.spinner + '</em>'; // WARNING: html(...) crashes Safari with jQuery 1.1.2
  220. }
  221. setTimeout(function() { // Timeout is again required in IE, "wait" for id being restored
  222. $(hash).load(url, function() {
  223. if (settings.spinner) {
  224. span.innerHTML = tabTitle; // WARNING: html(...) crashes Safari with jQuery 1.1.2
  225. }
  226. $$.removeClass(settings.loadingClass);
  227. callback && callback();
  228. });
  229. }, 0);
  230. });
  231. });
  232. }
  233. // set up containers
  234. var containers = $('div.' + settings.containerClass, container);
  235. containers = containers.size() && containers || $('>' + settings.tabStruct, container); // fallback to default structure
  236. // attach classes for styling if not present
  237. nav.is('.' + settings.navClass) || nav.addClass(settings.navClass);
  238. containers.each(function() {
  239. var $$ = $(this);
  240. $$.is('.' + settings.containerClass) || $$.addClass(settings.containerClass);
  241. });
  242. // try to retrieve active tab from class in HTML
  243. var hasSelectedClass = $('li', nav).index( $('li.' + settings.selectedClass, nav)[0] );
  244. if (hasSelectedClass >= 0) {
  245. settings.initial = hasSelectedClass;
  246. }
  247. // try to retrieve active tab from hash in url, will override class in HTML
  248. if (location.hash) {
  249. tabs.each(function(i) {
  250. if (this.hash == location.hash) {
  251. settings.initial = i;
  252. // prevent page scroll to fragment
  253. if (($.browser.msie || $.browser.opera) && !settings.remote) {
  254. var toShow = $(location.hash);
  255. var toShowId = toShow.attr('id');
  256. toShow.attr('id', '');
  257. setTimeout(function() {
  258. toShow.attr('id', toShowId); // restore id
  259. }, 500);
  260. }
  261. unFocus();
  262. return false; // break
  263. }
  264. });
  265. }
  266. if ($.browser.msie) {
  267. unFocus(); // fix IE focussing bottom of the page for some unknown reason
  268. }
  269. // highlight tab accordingly
  270. containers.filter(':eq(' + settings.initial + ')').show().end().not(':eq(' + settings.initial + ')').addClass(settings.hideClass);
  271. $('li', nav).removeClass(settings.selectedClass).eq(settings.initial).addClass(settings.selectedClass); // we need to remove classes eventually if hash takes precedence over class
  272. // trigger load of initial tab
  273. tabs.eq(settings.initial).trigger('loadRemoteTab').end();
  274. // setup auto height
  275. if (settings.fxAutoHeight) {
  276. // helper
  277. var _setAutoHeight = function(reset) {
  278. // get tab heights in top to bottom ordered array
  279. var heights = $.map(containers.get(), function(el) {
  280. var h, jq = $(el);
  281. if (reset) {
  282. if ($.browser.msie6) {
  283. el.style.removeExpression('behaviour');
  284. el.style.height = '';
  285. el.minHeight = null;
  286. }
  287. h = jq.css({'min-height': ''}).height(); // use jQuery's height() to get hidden element values
  288. } else {
  289. h = jq.height(); // use jQuery's height() to get hidden element values
  290. }
  291. return h;
  292. }).sort(function(a, b) {
  293. return b - a;
  294. });
  295. if ($.browser.msie6) {
  296. containers.each(function() {
  297. this.minHeight = heights[0] + 'px';
  298. this.style.setExpression('behaviour', 'this.style.height = this.minHeight ? this.minHeight : "1px"'); // using an expression to not make print styles useless
  299. });
  300. } else {
  301. containers.css({'min-height': heights[0] + 'px'});
  302. }
  303. };
  304. // call once for initialization
  305. _setAutoHeight();
  306. // trigger auto height adjustment if needed
  307. var cachedWidth = container.offsetWidth;
  308. var cachedHeight = container.offsetHeight;
  309. var watchFontSize = $('#tabs-watch-font-size').get(0) || $('<span id="tabs-watch-font-size">M</span>').css({display: 'block', position: 'absolute', visibility: 'hidden'}).appendTo(document.body).get(0);
  310. var cachedFontSize = watchFontSize.offsetHeight;
  311. setInterval(function() {
  312. var currentWidth = container.offsetWidth;
  313. var currentHeight = container.offsetHeight;
  314. var currentFontSize = watchFontSize.offsetHeight;
  315. if (currentHeight > cachedHeight || currentWidth != cachedWidth || currentFontSize != cachedFontSize) {
  316. _setAutoHeight((currentWidth > cachedWidth || currentFontSize < cachedFontSize)); // if heights gets smaller reset min-height
  317. cachedWidth = currentWidth;
  318. cachedHeight = currentHeight;
  319. cachedFontSize = currentFontSize;
  320. }
  321. }, 50);
  322. }
  323. // setup animations
  324. var showAnim = {}, hideAnim = {}, showSpeed = settings.fxShowSpeed || settings.fxSpeed, hideSpeed = settings.fxHideSpeed || settings.fxSpeed;
  325. if (settings.fxSlide || settings.fxFade) {
  326. if (settings.fxSlide) {
  327. showAnim['height'] = 'show';
  328. hideAnim['height'] = 'hide';
  329. }
  330. if (settings.fxFade) {
  331. showAnim['opacity'] = 'show';
  332. hideAnim['opacity'] = 'hide';
  333. }
  334. } else {
  335. if (settings.fxShow) {
  336. showAnim = settings.fxShow;
  337. } else { // use some kind of animation to prevent browser scrolling to the tab
  338. showAnim['min-width'] = 0; // avoid opacity, causes flicker in Firefox
  339. showSpeed = 1; // as little as 1 is sufficient
  340. }
  341. if (settings.fxHide) {
  342. hideAnim = settings.fxHide;
  343. } else { // use some kind of animation to prevent browser scrolling to the tab
  344. hideAnim['min-width'] = 0; // avoid opacity, causes flicker in Firefox
  345. hideSpeed = 1; // as little as 1 is sufficient
  346. }
  347. }
  348. // callbacks
  349. var onClick = settings.onClick, onHide = settings.onHide, onShow = settings.onShow;
  350. // attach activateTab event, required for activating a tab programmatically
  351. tabs.bind('triggerTab', function() {
  352. // if the tab is already selected or disabled or animation is still running stop here
  353. var li = $(this).parents('li:eq(0)');
  354. if (container.locked || li.is('.' + settings.selectedClass) || li.is('.' + settings.disabledClass)) {
  355. return false;
  356. }
  357. var hash = this.hash;
  358. if ($.browser.msie) {
  359. $(this).trigger('click');
  360. if (settings.bookmarkable) {
  361. $.ajaxHistory.update(hash);
  362. location.hash = hash.replace('#', '');
  363. }
  364. } else if ($.browser.safari) {
  365. // Simply setting location.hash puts Safari into the eternal load state... ugh! Submit a form instead.
  366. var tempForm = $('<form action="' + hash + '"><div><input type="submit" value="h" /></div></form>').get(0); // no need to append it to the body
  367. tempForm.submit(); // does not trigger the form's submit event...
  368. $(this).trigger('click'); // ...thus do stuff here
  369. if (settings.bookmarkable) {
  370. $.ajaxHistory.update(hash);
  371. }
  372. } else {
  373. if (settings.bookmarkable) {
  374. location.hash = hash.replace('#', '');
  375. } else {
  376. $(this).trigger('click');
  377. }
  378. }
  379. });
  380. // attach disable event, required for disabling a tab
  381. tabs.bind('disableTab', function() {
  382. var li = $(this).parents('li:eq(0)');
  383. if ($.browser.safari) { /* fix opacity of tab after disabling in Safari... */
  384. li.animate({ opacity: 0 }, 1, function() {
  385. li.css({opacity: ''});
  386. });
  387. }
  388. li.addClass(settings.disabledClass);
  389. });
  390. // disabled from settings
  391. if (settings.disabled && settings.disabled.length) {
  392. for (var i = 0, k = settings.disabled.length; i < k; i++) {
  393. tabs.eq(--settings.disabled[i]).trigger('disableTab').end();
  394. }
  395. };
  396. // attach enable event, required for reenabling a tab
  397. tabs.bind('enableTab', function() {
  398. var li = $(this).parents('li:eq(0)');
  399. li.removeClass(settings.disabledClass);
  400. if ($.browser.safari) { /* fix disappearing tab after enabling in Safari... */
  401. li.animate({ opacity: 1 }, 1, function() {
  402. li.css({opacity: ''});
  403. });
  404. }
  405. });
  406. // attach click event
  407. tabs.bind('click', function(e) {
  408. var trueClick = e.clientX; // add to history only if true click occured, not a triggered click
  409. var clicked = this, li = $(this).parents('li:eq(0)'), toShow = $(this.hash), toHide = containers.filter(':visible');
  410. // if animation is still running, tab is selected or disabled or onClick callback returns false stop here
  411. // check if onClick returns false last so that it is not executed for a disabled tab
  412. if (container['locked'] || li.is('.' + settings.selectedClass) || li.is('.' + settings.disabledClass) || typeof onClick == 'function' && onClick(this, toShow[0], toHide[0]) === false) {
  413. this.blur();
  414. return false;
  415. }
  416. container['locked'] = true;
  417. // show new tab
  418. if (toShow.size()) {
  419. // prevent scrollbar scrolling to 0 and than back in IE7, happens only if bookmarking/history is enabled
  420. if ($.browser.msie && settings.bookmarkable) {
  421. var toShowId = this.hash.replace('#', '');
  422. toShow.attr('id', '');
  423. setTimeout(function() {
  424. toShow.attr('id', toShowId); // restore id
  425. }, 0);
  426. }
  427. var resetCSS = { display: '', overflow: '', height: '' };
  428. if (!$.browser.msie) { // not in IE to prevent ClearType font issue
  429. resetCSS['opacity'] = '';
  430. }
  431. // switch tab, animation prevents browser scrolling to the fragment
  432. function switchTab() {
  433. if (settings.bookmarkable && trueClick) { // add to history only if true click occured, not a triggered click
  434. $.ajaxHistory.update(clicked.hash);
  435. }
  436. toHide.animate(hideAnim, hideSpeed, function() { //
  437. $(clicked).parents('li:eq(0)').addClass(settings.selectedClass).siblings().removeClass(settings.selectedClass);
  438. toHide.addClass(settings.hideClass).css(resetCSS); // maintain flexible height and accessibility in print etc.
  439. if (typeof onHide == 'function') {
  440. onHide(clicked, toShow[0], toHide[0]);
  441. }
  442. if (!(settings.fxSlide || settings.fxFade || settings.fxShow)) {
  443. toShow.css('display', 'block'); // prevent occasionally occuring flicker in Firefox cause by gap between showing and hiding the tab containers
  444. }
  445. toShow.animate(showAnim, showSpeed, function() {
  446. toShow.removeClass(settings.hideClass).css(resetCSS); // maintain flexible height and accessibility in print etc.
  447. if ($.browser.msie) {
  448. toHide[0].style.filter = '';
  449. toShow[0].style.filter = '';
  450. }
  451. if (typeof onShow == 'function') {
  452. onShow(clicked, toShow[0], toHide[0]);
  453. }
  454. container['locked'] = null;
  455. });
  456. });
  457. }
  458. if (!settings.remote) {
  459. switchTab();
  460. } else {
  461. $(clicked).trigger('loadRemoteTab', [switchTab]);
  462. }
  463. } else {
  464. alert('There is no such container.');
  465. }
  466. // Set scrollbar to saved position - need to use timeout with 0 to prevent browser scroll to target of hash
  467. var scrollX = window.pageXOffset || document.documentElement && document.documentElement.scrollLeft || document.body.scrollLeft || 0;
  468. var scrollY = window.pageYOffset || document.documentElement && document.documentElement.scrollTop || document.body.scrollTop || 0;
  469. setTimeout(function() {
  470. window.scrollTo(scrollX, scrollY);
  471. }, 0);
  472. this.blur(); // prevent IE from keeping other link focussed when using the back button
  473. return settings.bookmarkable && !!trueClick; // convert undefined to Boolean for IE
  474. });
  475. // enable history support if bookmarking and history is turned on
  476. if (settings.bookmarkable) {
  477. $.ajaxHistory.initialize(function() {
  478. tabs.eq(settings.initial).trigger('click').end();
  479. });
  480. }
  481. });
  482. };
  483. /**
  484. * Activate a tab programmatically with the given position (no zero-based index)
  485. * or its id, e.g. the URL's fragment identifier/hash representing a tab, as if the tab
  486. * itself were clicked.
  487. *
  488. * @example $('#container').triggerTab(2);
  489. * @desc Activate the second tab of the tab interface contained in <div id="container">.
  490. * @example $('#container').triggerTab(1);
  491. * @desc Activate the first tab of the tab interface contained in <div id="container">.
  492. * @example $('#container').triggerTab();
  493. * @desc Activate the first tab of the tab interface contained in <div id="container">.
  494. * @example $('#container').triggerTab('fragment-2');
  495. * @desc Activate a tab via its URL fragment identifier representation.
  496. *
  497. * @param String|Number tab Either a string that matches the id of the tab (the URL's
  498. * fragment identifier/hash representing a tab) or an integer
  499. * specifying the position of the tab (no zero-based index) to
  500. * be activated. If this parameter is omitted, the first tab
  501. * will be activated.
  502. * @type jQuery
  503. *
  504. * @name triggerTab
  505. * @cat Plugins/Tabs
  506. * @author Klaus Hartl/klaus.hartl@stilbuero.de
  507. */
  508. /**
  509. * Disable a tab, so that clicking it has no effect.
  510. *
  511. * @example $('#container').disableTab(2);
  512. * @desc Disable the second tab of the tab interface contained in <div id="container">.
  513. *
  514. * @param String|Number tab Either a string that matches the id of the tab (the URL's
  515. * fragment identifier/hash representing a tab) or an integer
  516. * specifying the position of the tab (no zero-based index) to
  517. * be disabled. If this parameter is omitted, the first tab
  518. * will be disabled.
  519. * @type jQuery
  520. *
  521. * @name disableTab
  522. * @cat Plugins/Tabs
  523. * @author Klaus Hartl/klaus.hartl@stilbuero.de
  524. */
  525. /**
  526. * Enable a tab that has been disabled.
  527. *
  528. * @example $('#container').enableTab(2);
  529. * @desc Enable the second tab of the tab interface contained in <div id="container">.
  530. *
  531. * @param String|Number tab Either a string that matches the id of the tab (the URL's
  532. * fragment identifier/hash representing a tab) or an integer
  533. * specifying the position of the tab (no zero-based index) to
  534. * be enabled. If this parameter is omitted, the first tab
  535. * will be enabled.
  536. * @type jQuery
  537. *
  538. * @name enableTab
  539. * @cat Plugins/Tabs
  540. * @author Klaus Hartl/klaus.hartl@stilbuero.de
  541. */
  542. var tabEvents = ['triggerTab', 'disableTab', 'enableTab'];
  543. for (var i = 0; i < tabEvents.length; i++) {
  544. $.fn[tabEvents[i]] = (function(tabEvent) {
  545. return function(tab) {
  546. return this.each(function() {
  547. var nav = $('ul.tabs-nav' , this);
  548. nav = nav.size() && nav || $('>ul:eq(0)', this); // fallback to default structure
  549. var a;
  550. if (!tab || typeof tab == 'number') {
  551. a = $('li a', nav).eq((tab && tab > 0 && tab - 1 || 0)); // fall back to 0
  552. } else if (typeof tab == 'string') {
  553. a = $('li a[@href$="#' + tab + '"]', nav);
  554. }
  555. a.trigger(tabEvent);
  556. });
  557. };
  558. })(tabEvents[i]);
  559. }
  560. /**
  561. * Get the position of the currently selected tab (no zero-based index).
  562. *
  563. * @example $('#container').activeTab();
  564. * @desc Get the position of the currently selected tab of an interface
  565. * contained in <div id="container">.
  566. *
  567. * @type Number
  568. *
  569. * @name activeTab
  570. * @cat Plugins/Tabs
  571. * @author Klaus Hartl/klaus.hartl@stilbuero.de
  572. */
  573. $.fn.activeTab = function() {
  574. var selectedTabs = [];
  575. this.each(function() {
  576. var nav = $('ul.tabs-nav' , this);
  577. nav = nav.size() && nav || $('>ul:eq(0)', this); //fallback to default structure
  578. var lis = $('li', nav);
  579. selectedTabs.push(lis.index( lis.filter('.tabs-selected')[0] ) + 1);
  580. });
  581. return selectedTabs[0];
  582. };
  583. })(jQuery);