Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.
 
 
 
 
 
 

926 rindas
23 KiB

  1. /*!
  2. * jQuery UI Tabs 1.13.1
  3. * http://jqueryui.com
  4. *
  5. * Copyright jQuery Foundation and other contributors
  6. * Released under the MIT license.
  7. * http://jquery.org/license
  8. */
  9. //>>label: Tabs
  10. //>>group: Widgets
  11. //>>description: Transforms a set of container elements into a tab structure.
  12. //>>docs: http://api.jqueryui.com/tabs/
  13. //>>demos: http://jqueryui.com/tabs/
  14. //>>css.structure: ../../themes/base/core.css
  15. //>>css.structure: ../../themes/base/tabs.css
  16. //>>css.theme: ../../themes/base/theme.css
  17. ( function( factory ) {
  18. "use strict";
  19. if ( typeof define === "function" && define.amd ) {
  20. // AMD. Register as an anonymous module.
  21. define( [
  22. "jquery",
  23. "../keycode",
  24. "../safe-active-element",
  25. "../unique-id",
  26. "../version",
  27. "../widget"
  28. ], factory );
  29. } else {
  30. // Browser globals
  31. factory( jQuery );
  32. }
  33. } )( function( $ ) {
  34. "use strict";
  35. $.widget( "ui.tabs", {
  36. version: "1.13.1",
  37. delay: 300,
  38. options: {
  39. active: null,
  40. classes: {
  41. "ui-tabs": "ui-corner-all",
  42. "ui-tabs-nav": "ui-corner-all",
  43. "ui-tabs-panel": "ui-corner-bottom",
  44. "ui-tabs-tab": "ui-corner-top"
  45. },
  46. collapsible: false,
  47. event: "click",
  48. heightStyle: "content",
  49. hide: null,
  50. show: null,
  51. // Callbacks
  52. activate: null,
  53. beforeActivate: null,
  54. beforeLoad: null,
  55. load: null
  56. },
  57. _isLocal: ( function() {
  58. var rhash = /#.*$/;
  59. return function( anchor ) {
  60. var anchorUrl, locationUrl;
  61. anchorUrl = anchor.href.replace( rhash, "" );
  62. locationUrl = location.href.replace( rhash, "" );
  63. // Decoding may throw an error if the URL isn't UTF-8 (#9518)
  64. try {
  65. anchorUrl = decodeURIComponent( anchorUrl );
  66. } catch ( error ) {}
  67. try {
  68. locationUrl = decodeURIComponent( locationUrl );
  69. } catch ( error ) {}
  70. return anchor.hash.length > 1 && anchorUrl === locationUrl;
  71. };
  72. } )(),
  73. _create: function() {
  74. var that = this,
  75. options = this.options;
  76. this.running = false;
  77. this._addClass( "ui-tabs", "ui-widget ui-widget-content" );
  78. this._toggleClass( "ui-tabs-collapsible", null, options.collapsible );
  79. this._processTabs();
  80. options.active = this._initialActive();
  81. // Take disabling tabs via class attribute from HTML
  82. // into account and update option properly.
  83. if ( Array.isArray( options.disabled ) ) {
  84. options.disabled = $.uniqueSort( options.disabled.concat(
  85. $.map( this.tabs.filter( ".ui-state-disabled" ), function( li ) {
  86. return that.tabs.index( li );
  87. } )
  88. ) ).sort();
  89. }
  90. // Check for length avoids error when initializing empty list
  91. if ( this.options.active !== false && this.anchors.length ) {
  92. this.active = this._findActive( options.active );
  93. } else {
  94. this.active = $();
  95. }
  96. this._refresh();
  97. if ( this.active.length ) {
  98. this.load( options.active );
  99. }
  100. },
  101. _initialActive: function() {
  102. var active = this.options.active,
  103. collapsible = this.options.collapsible,
  104. locationHash = location.hash.substring( 1 );
  105. if ( active === null ) {
  106. // check the fragment identifier in the URL
  107. if ( locationHash ) {
  108. this.tabs.each( function( i, tab ) {
  109. if ( $( tab ).attr( "aria-controls" ) === locationHash ) {
  110. active = i;
  111. return false;
  112. }
  113. } );
  114. }
  115. // Check for a tab marked active via a class
  116. if ( active === null ) {
  117. active = this.tabs.index( this.tabs.filter( ".ui-tabs-active" ) );
  118. }
  119. // No active tab, set to false
  120. if ( active === null || active === -1 ) {
  121. active = this.tabs.length ? 0 : false;
  122. }
  123. }
  124. // Handle numbers: negative, out of range
  125. if ( active !== false ) {
  126. active = this.tabs.index( this.tabs.eq( active ) );
  127. if ( active === -1 ) {
  128. active = collapsible ? false : 0;
  129. }
  130. }
  131. // Don't allow collapsible: false and active: false
  132. if ( !collapsible && active === false && this.anchors.length ) {
  133. active = 0;
  134. }
  135. return active;
  136. },
  137. _getCreateEventData: function() {
  138. return {
  139. tab: this.active,
  140. panel: !this.active.length ? $() : this._getPanelForTab( this.active )
  141. };
  142. },
  143. _tabKeydown: function( event ) {
  144. var focusedTab = $( $.ui.safeActiveElement( this.document[ 0 ] ) ).closest( "li" ),
  145. selectedIndex = this.tabs.index( focusedTab ),
  146. goingForward = true;
  147. if ( this._handlePageNav( event ) ) {
  148. return;
  149. }
  150. switch ( event.keyCode ) {
  151. case $.ui.keyCode.RIGHT:
  152. case $.ui.keyCode.DOWN:
  153. selectedIndex++;
  154. break;
  155. case $.ui.keyCode.UP:
  156. case $.ui.keyCode.LEFT:
  157. goingForward = false;
  158. selectedIndex--;
  159. break;
  160. case $.ui.keyCode.END:
  161. selectedIndex = this.anchors.length - 1;
  162. break;
  163. case $.ui.keyCode.HOME:
  164. selectedIndex = 0;
  165. break;
  166. case $.ui.keyCode.SPACE:
  167. // Activate only, no collapsing
  168. event.preventDefault();
  169. clearTimeout( this.activating );
  170. this._activate( selectedIndex );
  171. return;
  172. case $.ui.keyCode.ENTER:
  173. // Toggle (cancel delayed activation, allow collapsing)
  174. event.preventDefault();
  175. clearTimeout( this.activating );
  176. // Determine if we should collapse or activate
  177. this._activate( selectedIndex === this.options.active ? false : selectedIndex );
  178. return;
  179. default:
  180. return;
  181. }
  182. // Focus the appropriate tab, based on which key was pressed
  183. event.preventDefault();
  184. clearTimeout( this.activating );
  185. selectedIndex = this._focusNextTab( selectedIndex, goingForward );
  186. // Navigating with control/command key will prevent automatic activation
  187. if ( !event.ctrlKey && !event.metaKey ) {
  188. // Update aria-selected immediately so that AT think the tab is already selected.
  189. // Otherwise AT may confuse the user by stating that they need to activate the tab,
  190. // but the tab will already be activated by the time the announcement finishes.
  191. focusedTab.attr( "aria-selected", "false" );
  192. this.tabs.eq( selectedIndex ).attr( "aria-selected", "true" );
  193. this.activating = this._delay( function() {
  194. this.option( "active", selectedIndex );
  195. }, this.delay );
  196. }
  197. },
  198. _panelKeydown: function( event ) {
  199. if ( this._handlePageNav( event ) ) {
  200. return;
  201. }
  202. // Ctrl+up moves focus to the current tab
  203. if ( event.ctrlKey && event.keyCode === $.ui.keyCode.UP ) {
  204. event.preventDefault();
  205. this.active.trigger( "focus" );
  206. }
  207. },
  208. // Alt+page up/down moves focus to the previous/next tab (and activates)
  209. _handlePageNav: function( event ) {
  210. if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_UP ) {
  211. this._activate( this._focusNextTab( this.options.active - 1, false ) );
  212. return true;
  213. }
  214. if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_DOWN ) {
  215. this._activate( this._focusNextTab( this.options.active + 1, true ) );
  216. return true;
  217. }
  218. },
  219. _findNextTab: function( index, goingForward ) {
  220. var lastTabIndex = this.tabs.length - 1;
  221. function constrain() {
  222. if ( index > lastTabIndex ) {
  223. index = 0;
  224. }
  225. if ( index < 0 ) {
  226. index = lastTabIndex;
  227. }
  228. return index;
  229. }
  230. while ( $.inArray( constrain(), this.options.disabled ) !== -1 ) {
  231. index = goingForward ? index + 1 : index - 1;
  232. }
  233. return index;
  234. },
  235. _focusNextTab: function( index, goingForward ) {
  236. index = this._findNextTab( index, goingForward );
  237. this.tabs.eq( index ).trigger( "focus" );
  238. return index;
  239. },
  240. _setOption: function( key, value ) {
  241. if ( key === "active" ) {
  242. // _activate() will handle invalid values and update this.options
  243. this._activate( value );
  244. return;
  245. }
  246. this._super( key, value );
  247. if ( key === "collapsible" ) {
  248. this._toggleClass( "ui-tabs-collapsible", null, value );
  249. // Setting collapsible: false while collapsed; open first panel
  250. if ( !value && this.options.active === false ) {
  251. this._activate( 0 );
  252. }
  253. }
  254. if ( key === "event" ) {
  255. this._setupEvents( value );
  256. }
  257. if ( key === "heightStyle" ) {
  258. this._setupHeightStyle( value );
  259. }
  260. },
  261. _sanitizeSelector: function( hash ) {
  262. return hash ? hash.replace( /[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g, "\\$&" ) : "";
  263. },
  264. refresh: function() {
  265. var options = this.options,
  266. lis = this.tablist.children( ":has(a[href])" );
  267. // Get disabled tabs from class attribute from HTML
  268. // this will get converted to a boolean if needed in _refresh()
  269. options.disabled = $.map( lis.filter( ".ui-state-disabled" ), function( tab ) {
  270. return lis.index( tab );
  271. } );
  272. this._processTabs();
  273. // Was collapsed or no tabs
  274. if ( options.active === false || !this.anchors.length ) {
  275. options.active = false;
  276. this.active = $();
  277. // was active, but active tab is gone
  278. } else if ( this.active.length && !$.contains( this.tablist[ 0 ], this.active[ 0 ] ) ) {
  279. // all remaining tabs are disabled
  280. if ( this.tabs.length === options.disabled.length ) {
  281. options.active = false;
  282. this.active = $();
  283. // activate previous tab
  284. } else {
  285. this._activate( this._findNextTab( Math.max( 0, options.active - 1 ), false ) );
  286. }
  287. // was active, active tab still exists
  288. } else {
  289. // make sure active index is correct
  290. options.active = this.tabs.index( this.active );
  291. }
  292. this._refresh();
  293. },
  294. _refresh: function() {
  295. this._setOptionDisabled( this.options.disabled );
  296. this._setupEvents( this.options.event );
  297. this._setupHeightStyle( this.options.heightStyle );
  298. this.tabs.not( this.active ).attr( {
  299. "aria-selected": "false",
  300. "aria-expanded": "false",
  301. tabIndex: -1
  302. } );
  303. this.panels.not( this._getPanelForTab( this.active ) )
  304. .hide()
  305. .attr( {
  306. "aria-hidden": "true"
  307. } );
  308. // Make sure one tab is in the tab order
  309. if ( !this.active.length ) {
  310. this.tabs.eq( 0 ).attr( "tabIndex", 0 );
  311. } else {
  312. this.active
  313. .attr( {
  314. "aria-selected": "true",
  315. "aria-expanded": "true",
  316. tabIndex: 0
  317. } );
  318. this._addClass( this.active, "ui-tabs-active", "ui-state-active" );
  319. this._getPanelForTab( this.active )
  320. .show()
  321. .attr( {
  322. "aria-hidden": "false"
  323. } );
  324. }
  325. },
  326. _processTabs: function() {
  327. var that = this,
  328. prevTabs = this.tabs,
  329. prevAnchors = this.anchors,
  330. prevPanels = this.panels;
  331. this.tablist = this._getList().attr( "role", "tablist" );
  332. this._addClass( this.tablist, "ui-tabs-nav",
  333. "ui-helper-reset ui-helper-clearfix ui-widget-header" );
  334. // Prevent users from focusing disabled tabs via click
  335. this.tablist
  336. .on( "mousedown" + this.eventNamespace, "> li", function( event ) {
  337. if ( $( this ).is( ".ui-state-disabled" ) ) {
  338. event.preventDefault();
  339. }
  340. } )
  341. // Support: IE <9
  342. // Preventing the default action in mousedown doesn't prevent IE
  343. // from focusing the element, so if the anchor gets focused, blur.
  344. // We don't have to worry about focusing the previously focused
  345. // element since clicking on a non-focusable element should focus
  346. // the body anyway.
  347. .on( "focus" + this.eventNamespace, ".ui-tabs-anchor", function() {
  348. if ( $( this ).closest( "li" ).is( ".ui-state-disabled" ) ) {
  349. this.blur();
  350. }
  351. } );
  352. this.tabs = this.tablist.find( "> li:has(a[href])" )
  353. .attr( {
  354. role: "tab",
  355. tabIndex: -1
  356. } );
  357. this._addClass( this.tabs, "ui-tabs-tab", "ui-state-default" );
  358. this.anchors = this.tabs.map( function() {
  359. return $( "a", this )[ 0 ];
  360. } )
  361. .attr( {
  362. tabIndex: -1
  363. } );
  364. this._addClass( this.anchors, "ui-tabs-anchor" );
  365. this.panels = $();
  366. this.anchors.each( function( i, anchor ) {
  367. var selector, panel, panelId,
  368. anchorId = $( anchor ).uniqueId().attr( "id" ),
  369. tab = $( anchor ).closest( "li" ),
  370. originalAriaControls = tab.attr( "aria-controls" );
  371. // Inline tab
  372. if ( that._isLocal( anchor ) ) {
  373. selector = anchor.hash;
  374. panelId = selector.substring( 1 );
  375. panel = that.element.find( that._sanitizeSelector( selector ) );
  376. // remote tab
  377. } else {
  378. // If the tab doesn't already have aria-controls,
  379. // generate an id by using a throw-away element
  380. panelId = tab.attr( "aria-controls" ) || $( {} ).uniqueId()[ 0 ].id;
  381. selector = "#" + panelId;
  382. panel = that.element.find( selector );
  383. if ( !panel.length ) {
  384. panel = that._createPanel( panelId );
  385. panel.insertAfter( that.panels[ i - 1 ] || that.tablist );
  386. }
  387. panel.attr( "aria-live", "polite" );
  388. }
  389. if ( panel.length ) {
  390. that.panels = that.panels.add( panel );
  391. }
  392. if ( originalAriaControls ) {
  393. tab.data( "ui-tabs-aria-controls", originalAriaControls );
  394. }
  395. tab.attr( {
  396. "aria-controls": panelId,
  397. "aria-labelledby": anchorId
  398. } );
  399. panel.attr( "aria-labelledby", anchorId );
  400. } );
  401. this.panels.attr( "role", "tabpanel" );
  402. this._addClass( this.panels, "ui-tabs-panel", "ui-widget-content" );
  403. // Avoid memory leaks (#10056)
  404. if ( prevTabs ) {
  405. this._off( prevTabs.not( this.tabs ) );
  406. this._off( prevAnchors.not( this.anchors ) );
  407. this._off( prevPanels.not( this.panels ) );
  408. }
  409. },
  410. // Allow overriding how to find the list for rare usage scenarios (#7715)
  411. _getList: function() {
  412. return this.tablist || this.element.find( "ol, ul" ).eq( 0 );
  413. },
  414. _createPanel: function( id ) {
  415. return $( "<div>" )
  416. .attr( "id", id )
  417. .data( "ui-tabs-destroy", true );
  418. },
  419. _setOptionDisabled: function( disabled ) {
  420. var currentItem, li, i;
  421. if ( Array.isArray( disabled ) ) {
  422. if ( !disabled.length ) {
  423. disabled = false;
  424. } else if ( disabled.length === this.anchors.length ) {
  425. disabled = true;
  426. }
  427. }
  428. // Disable tabs
  429. for ( i = 0; ( li = this.tabs[ i ] ); i++ ) {
  430. currentItem = $( li );
  431. if ( disabled === true || $.inArray( i, disabled ) !== -1 ) {
  432. currentItem.attr( "aria-disabled", "true" );
  433. this._addClass( currentItem, null, "ui-state-disabled" );
  434. } else {
  435. currentItem.removeAttr( "aria-disabled" );
  436. this._removeClass( currentItem, null, "ui-state-disabled" );
  437. }
  438. }
  439. this.options.disabled = disabled;
  440. this._toggleClass( this.widget(), this.widgetFullName + "-disabled", null,
  441. disabled === true );
  442. },
  443. _setupEvents: function( event ) {
  444. var events = {};
  445. if ( event ) {
  446. $.each( event.split( " " ), function( index, eventName ) {
  447. events[ eventName ] = "_eventHandler";
  448. } );
  449. }
  450. this._off( this.anchors.add( this.tabs ).add( this.panels ) );
  451. // Always prevent the default action, even when disabled
  452. this._on( true, this.anchors, {
  453. click: function( event ) {
  454. event.preventDefault();
  455. }
  456. } );
  457. this._on( this.anchors, events );
  458. this._on( this.tabs, { keydown: "_tabKeydown" } );
  459. this._on( this.panels, { keydown: "_panelKeydown" } );
  460. this._focusable( this.tabs );
  461. this._hoverable( this.tabs );
  462. },
  463. _setupHeightStyle: function( heightStyle ) {
  464. var maxHeight,
  465. parent = this.element.parent();
  466. if ( heightStyle === "fill" ) {
  467. maxHeight = parent.height();
  468. maxHeight -= this.element.outerHeight() - this.element.height();
  469. this.element.siblings( ":visible" ).each( function() {
  470. var elem = $( this ),
  471. position = elem.css( "position" );
  472. if ( position === "absolute" || position === "fixed" ) {
  473. return;
  474. }
  475. maxHeight -= elem.outerHeight( true );
  476. } );
  477. this.element.children().not( this.panels ).each( function() {
  478. maxHeight -= $( this ).outerHeight( true );
  479. } );
  480. this.panels.each( function() {
  481. $( this ).height( Math.max( 0, maxHeight -
  482. $( this ).innerHeight() + $( this ).height() ) );
  483. } )
  484. .css( "overflow", "auto" );
  485. } else if ( heightStyle === "auto" ) {
  486. maxHeight = 0;
  487. this.panels.each( function() {
  488. maxHeight = Math.max( maxHeight, $( this ).height( "" ).height() );
  489. } ).height( maxHeight );
  490. }
  491. },
  492. _eventHandler: function( event ) {
  493. var options = this.options,
  494. active = this.active,
  495. anchor = $( event.currentTarget ),
  496. tab = anchor.closest( "li" ),
  497. clickedIsActive = tab[ 0 ] === active[ 0 ],
  498. collapsing = clickedIsActive && options.collapsible,
  499. toShow = collapsing ? $() : this._getPanelForTab( tab ),
  500. toHide = !active.length ? $() : this._getPanelForTab( active ),
  501. eventData = {
  502. oldTab: active,
  503. oldPanel: toHide,
  504. newTab: collapsing ? $() : tab,
  505. newPanel: toShow
  506. };
  507. event.preventDefault();
  508. if ( tab.hasClass( "ui-state-disabled" ) ||
  509. // tab is already loading
  510. tab.hasClass( "ui-tabs-loading" ) ||
  511. // can't switch durning an animation
  512. this.running ||
  513. // click on active header, but not collapsible
  514. ( clickedIsActive && !options.collapsible ) ||
  515. // allow canceling activation
  516. ( this._trigger( "beforeActivate", event, eventData ) === false ) ) {
  517. return;
  518. }
  519. options.active = collapsing ? false : this.tabs.index( tab );
  520. this.active = clickedIsActive ? $() : tab;
  521. if ( this.xhr ) {
  522. this.xhr.abort();
  523. }
  524. if ( !toHide.length && !toShow.length ) {
  525. $.error( "jQuery UI Tabs: Mismatching fragment identifier." );
  526. }
  527. if ( toShow.length ) {
  528. this.load( this.tabs.index( tab ), event );
  529. }
  530. this._toggle( event, eventData );
  531. },
  532. // Handles show/hide for selecting tabs
  533. _toggle: function( event, eventData ) {
  534. var that = this,
  535. toShow = eventData.newPanel,
  536. toHide = eventData.oldPanel;
  537. this.running = true;
  538. function complete() {
  539. that.running = false;
  540. that._trigger( "activate", event, eventData );
  541. }
  542. function show() {
  543. that._addClass( eventData.newTab.closest( "li" ), "ui-tabs-active", "ui-state-active" );
  544. if ( toShow.length && that.options.show ) {
  545. that._show( toShow, that.options.show, complete );
  546. } else {
  547. toShow.show();
  548. complete();
  549. }
  550. }
  551. // Start out by hiding, then showing, then completing
  552. if ( toHide.length && this.options.hide ) {
  553. this._hide( toHide, this.options.hide, function() {
  554. that._removeClass( eventData.oldTab.closest( "li" ),
  555. "ui-tabs-active", "ui-state-active" );
  556. show();
  557. } );
  558. } else {
  559. this._removeClass( eventData.oldTab.closest( "li" ),
  560. "ui-tabs-active", "ui-state-active" );
  561. toHide.hide();
  562. show();
  563. }
  564. toHide.attr( "aria-hidden", "true" );
  565. eventData.oldTab.attr( {
  566. "aria-selected": "false",
  567. "aria-expanded": "false"
  568. } );
  569. // If we're switching tabs, remove the old tab from the tab order.
  570. // If we're opening from collapsed state, remove the previous tab from the tab order.
  571. // If we're collapsing, then keep the collapsing tab in the tab order.
  572. if ( toShow.length && toHide.length ) {
  573. eventData.oldTab.attr( "tabIndex", -1 );
  574. } else if ( toShow.length ) {
  575. this.tabs.filter( function() {
  576. return $( this ).attr( "tabIndex" ) === 0;
  577. } )
  578. .attr( "tabIndex", -1 );
  579. }
  580. toShow.attr( "aria-hidden", "false" );
  581. eventData.newTab.attr( {
  582. "aria-selected": "true",
  583. "aria-expanded": "true",
  584. tabIndex: 0
  585. } );
  586. },
  587. _activate: function( index ) {
  588. var anchor,
  589. active = this._findActive( index );
  590. // Trying to activate the already active panel
  591. if ( active[ 0 ] === this.active[ 0 ] ) {
  592. return;
  593. }
  594. // Trying to collapse, simulate a click on the current active header
  595. if ( !active.length ) {
  596. active = this.active;
  597. }
  598. anchor = active.find( ".ui-tabs-anchor" )[ 0 ];
  599. this._eventHandler( {
  600. target: anchor,
  601. currentTarget: anchor,
  602. preventDefault: $.noop
  603. } );
  604. },
  605. _findActive: function( index ) {
  606. return index === false ? $() : this.tabs.eq( index );
  607. },
  608. _getIndex: function( index ) {
  609. // meta-function to give users option to provide a href string instead of a numerical index.
  610. if ( typeof index === "string" ) {
  611. index = this.anchors.index( this.anchors.filter( "[href$='" +
  612. $.escapeSelector( index ) + "']" ) );
  613. }
  614. return index;
  615. },
  616. _destroy: function() {
  617. if ( this.xhr ) {
  618. this.xhr.abort();
  619. }
  620. this.tablist
  621. .removeAttr( "role" )
  622. .off( this.eventNamespace );
  623. this.anchors
  624. .removeAttr( "role tabIndex" )
  625. .removeUniqueId();
  626. this.tabs.add( this.panels ).each( function() {
  627. if ( $.data( this, "ui-tabs-destroy" ) ) {
  628. $( this ).remove();
  629. } else {
  630. $( this ).removeAttr( "role tabIndex " +
  631. "aria-live aria-busy aria-selected aria-labelledby aria-hidden aria-expanded" );
  632. }
  633. } );
  634. this.tabs.each( function() {
  635. var li = $( this ),
  636. prev = li.data( "ui-tabs-aria-controls" );
  637. if ( prev ) {
  638. li
  639. .attr( "aria-controls", prev )
  640. .removeData( "ui-tabs-aria-controls" );
  641. } else {
  642. li.removeAttr( "aria-controls" );
  643. }
  644. } );
  645. this.panels.show();
  646. if ( this.options.heightStyle !== "content" ) {
  647. this.panels.css( "height", "" );
  648. }
  649. },
  650. enable: function( index ) {
  651. var disabled = this.options.disabled;
  652. if ( disabled === false ) {
  653. return;
  654. }
  655. if ( index === undefined ) {
  656. disabled = false;
  657. } else {
  658. index = this._getIndex( index );
  659. if ( Array.isArray( disabled ) ) {
  660. disabled = $.map( disabled, function( num ) {
  661. return num !== index ? num : null;
  662. } );
  663. } else {
  664. disabled = $.map( this.tabs, function( li, num ) {
  665. return num !== index ? num : null;
  666. } );
  667. }
  668. }
  669. this._setOptionDisabled( disabled );
  670. },
  671. disable: function( index ) {
  672. var disabled = this.options.disabled;
  673. if ( disabled === true ) {
  674. return;
  675. }
  676. if ( index === undefined ) {
  677. disabled = true;
  678. } else {
  679. index = this._getIndex( index );
  680. if ( $.inArray( index, disabled ) !== -1 ) {
  681. return;
  682. }
  683. if ( Array.isArray( disabled ) ) {
  684. disabled = $.merge( [ index ], disabled ).sort();
  685. } else {
  686. disabled = [ index ];
  687. }
  688. }
  689. this._setOptionDisabled( disabled );
  690. },
  691. load: function( index, event ) {
  692. index = this._getIndex( index );
  693. var that = this,
  694. tab = this.tabs.eq( index ),
  695. anchor = tab.find( ".ui-tabs-anchor" ),
  696. panel = this._getPanelForTab( tab ),
  697. eventData = {
  698. tab: tab,
  699. panel: panel
  700. },
  701. complete = function( jqXHR, status ) {
  702. if ( status === "abort" ) {
  703. that.panels.stop( false, true );
  704. }
  705. that._removeClass( tab, "ui-tabs-loading" );
  706. panel.removeAttr( "aria-busy" );
  707. if ( jqXHR === that.xhr ) {
  708. delete that.xhr;
  709. }
  710. };
  711. // Not remote
  712. if ( this._isLocal( anchor[ 0 ] ) ) {
  713. return;
  714. }
  715. this.xhr = $.ajax( this._ajaxSettings( anchor, event, eventData ) );
  716. // Support: jQuery <1.8
  717. // jQuery <1.8 returns false if the request is canceled in beforeSend,
  718. // but as of 1.8, $.ajax() always returns a jqXHR object.
  719. if ( this.xhr && this.xhr.statusText !== "canceled" ) {
  720. this._addClass( tab, "ui-tabs-loading" );
  721. panel.attr( "aria-busy", "true" );
  722. this.xhr
  723. .done( function( response, status, jqXHR ) {
  724. // support: jQuery <1.8
  725. // http://bugs.jquery.com/ticket/11778
  726. setTimeout( function() {
  727. panel.html( response );
  728. that._trigger( "load", event, eventData );
  729. complete( jqXHR, status );
  730. }, 1 );
  731. } )
  732. .fail( function( jqXHR, status ) {
  733. // support: jQuery <1.8
  734. // http://bugs.jquery.com/ticket/11778
  735. setTimeout( function() {
  736. complete( jqXHR, status );
  737. }, 1 );
  738. } );
  739. }
  740. },
  741. _ajaxSettings: function( anchor, event, eventData ) {
  742. var that = this;
  743. return {
  744. // Support: IE <11 only
  745. // Strip any hash that exists to prevent errors with the Ajax request
  746. url: anchor.attr( "href" ).replace( /#.*$/, "" ),
  747. beforeSend: function( jqXHR, settings ) {
  748. return that._trigger( "beforeLoad", event,
  749. $.extend( { jqXHR: jqXHR, ajaxSettings: settings }, eventData ) );
  750. }
  751. };
  752. },
  753. _getPanelForTab: function( tab ) {
  754. var id = $( tab ).attr( "aria-controls" );
  755. return this.element.find( this._sanitizeSelector( "#" + id ) );
  756. }
  757. } );
  758. // DEPRECATED
  759. // TODO: Switch return back to widget declaration at top of file when this is removed
  760. if ( $.uiBackCompat !== false ) {
  761. // Backcompat for ui-tab class (now ui-tabs-tab)
  762. $.widget( "ui.tabs", $.ui.tabs, {
  763. _processTabs: function() {
  764. this._superApply( arguments );
  765. this._addClass( this.tabs, "ui-tab" );
  766. }
  767. } );
  768. }
  769. return $.ui.tabs;
  770. } );