jquery-ui-rails 2.0.2 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of jquery-ui-rails might be problematic. Click here for more details.

Files changed (131) hide show
  1. data/History.md +15 -0
  2. data/License.txt +2 -3
  3. data/README.md +17 -19
  4. data/Rakefile +79 -28
  5. data/lib/jquery/ui/rails/version.rb +1 -1
  6. data/vendor/assets/javascripts/jquery.ui.accordion.js +537 -417
  7. data/vendor/assets/javascripts/jquery.ui.all.js +17 -14
  8. data/vendor/assets/javascripts/jquery.ui.autocomplete.js +311 -339
  9. data/vendor/assets/javascripts/jquery.ui.button.js +63 -59
  10. data/vendor/assets/javascripts/jquery.ui.core.js +134 -112
  11. data/vendor/assets/javascripts/jquery.ui.datepicker-ar-DZ.js +2 -2
  12. data/vendor/assets/javascripts/jquery.ui.datepicker-ar.js +3 -3
  13. data/vendor/assets/javascripts/jquery.ui.datepicker-az.js +3 -3
  14. data/vendor/assets/javascripts/jquery.ui.datepicker-bg.js +16 -16
  15. data/vendor/assets/javascripts/jquery.ui.datepicker-bs.js +5 -5
  16. data/vendor/assets/javascripts/jquery.ui.datepicker-ca.js +13 -13
  17. data/vendor/assets/javascripts/jquery.ui.datepicker-cs.js +3 -3
  18. data/vendor/assets/javascripts/jquery.ui.datepicker-cy-GB.js +1 -1
  19. data/vendor/assets/javascripts/jquery.ui.datepicker-da.js +9 -9
  20. data/vendor/assets/javascripts/jquery.ui.datepicker-de.js +2 -2
  21. data/vendor/assets/javascripts/jquery.ui.datepicker-el.js +1 -1
  22. data/vendor/assets/javascripts/jquery.ui.datepicker-eo.js +2 -2
  23. data/vendor/assets/javascripts/jquery.ui.datepicker-es.js +6 -6
  24. data/vendor/assets/javascripts/jquery.ui.datepicker-et.js +1 -1
  25. data/vendor/assets/javascripts/jquery.ui.datepicker-eu.js +3 -3
  26. data/vendor/assets/javascripts/jquery.ui.datepicker-fa.js +3 -3
  27. data/vendor/assets/javascripts/jquery.ui.datepicker-fi.js +5 -5
  28. data/vendor/assets/javascripts/jquery.ui.datepicker-fo.js +3 -3
  29. data/vendor/assets/javascripts/jquery.ui.datepicker-fr-CH.js +3 -3
  30. data/vendor/assets/javascripts/jquery.ui.datepicker-fr.js +2 -2
  31. data/vendor/assets/javascripts/jquery.ui.datepicker-gl.js +6 -6
  32. data/vendor/assets/javascripts/jquery.ui.datepicker-he.js +2 -2
  33. data/vendor/assets/javascripts/jquery.ui.datepicker-hr.js +3 -3
  34. data/vendor/assets/javascripts/jquery.ui.datepicker-hy.js +3 -3
  35. data/vendor/assets/javascripts/jquery.ui.datepicker-id.js +3 -3
  36. data/vendor/assets/javascripts/jquery.ui.datepicker-is.js +11 -11
  37. data/vendor/assets/javascripts/jquery.ui.datepicker-it.js +3 -3
  38. data/vendor/assets/javascripts/jquery.ui.datepicker-ja.js +3 -3
  39. data/vendor/assets/javascripts/jquery.ui.datepicker-kk.js +2 -2
  40. data/vendor/assets/javascripts/jquery.ui.datepicker-ko.js +1 -1
  41. data/vendor/assets/javascripts/jquery.ui.datepicker-lt.js +3 -3
  42. data/vendor/assets/javascripts/jquery.ui.datepicker-lv.js +1 -1
  43. data/vendor/assets/javascripts/jquery.ui.datepicker-ml.js +1 -1
  44. data/vendor/assets/javascripts/jquery.ui.datepicker-ms.js +3 -3
  45. data/vendor/assets/javascripts/jquery.ui.datepicker-nl.js +1 -1
  46. data/vendor/assets/javascripts/jquery.ui.datepicker-no.js +18 -18
  47. data/vendor/assets/javascripts/jquery.ui.datepicker-pl.js +2 -2
  48. data/vendor/assets/javascripts/jquery.ui.datepicker-pt-BR.js +7 -7
  49. data/vendor/assets/javascripts/jquery.ui.datepicker-pt.js +6 -6
  50. data/vendor/assets/javascripts/jquery.ui.datepicker-rm.js +2 -2
  51. data/vendor/assets/javascripts/jquery.ui.datepicker-ro.js +2 -2
  52. data/vendor/assets/javascripts/jquery.ui.datepicker-ru.js +3 -3
  53. data/vendor/assets/javascripts/jquery.ui.datepicker-sk.js +2 -2
  54. data/vendor/assets/javascripts/jquery.ui.datepicker-sl.js +6 -6
  55. data/vendor/assets/javascripts/jquery.ui.datepicker-sq.js +2 -2
  56. data/vendor/assets/javascripts/jquery.ui.datepicker-sr-SR.js +2 -2
  57. data/vendor/assets/javascripts/jquery.ui.datepicker-sr.js +2 -2
  58. data/vendor/assets/javascripts/jquery.ui.datepicker-sv.js +9 -9
  59. data/vendor/assets/javascripts/jquery.ui.datepicker-th.js +3 -3
  60. data/vendor/assets/javascripts/jquery.ui.datepicker-tj.js +1 -1
  61. data/vendor/assets/javascripts/jquery.ui.datepicker-tr.js +2 -2
  62. data/vendor/assets/javascripts/jquery.ui.datepicker-uk.js +3 -3
  63. data/vendor/assets/javascripts/jquery.ui.datepicker-vi.js +2 -2
  64. data/vendor/assets/javascripts/jquery.ui.datepicker-zh-CN.js +2 -2
  65. data/vendor/assets/javascripts/jquery.ui.datepicker-zh-HK.js +2 -2
  66. data/vendor/assets/javascripts/jquery.ui.datepicker-zh-TW.js +2 -2
  67. data/vendor/assets/javascripts/jquery.ui.datepicker.js +51 -59
  68. data/vendor/assets/javascripts/jquery.ui.dialog.js +402 -409
  69. data/vendor/assets/javascripts/jquery.ui.draggable.js +79 -75
  70. data/vendor/assets/javascripts/jquery.ui.droppable.js +10 -17
  71. data/vendor/assets/javascripts/jquery.ui.effect-blind.js +84 -0
  72. data/vendor/assets/javascripts/jquery.ui.effect-bounce.js +115 -0
  73. data/vendor/assets/javascripts/jquery.ui.effect-clip.js +69 -0
  74. data/vendor/assets/javascripts/jquery.ui.effect-drop.js +67 -0
  75. data/vendor/assets/javascripts/jquery.ui.effect-explode.js +99 -0
  76. data/vendor/assets/javascripts/jquery.ui.effect-fade.js +32 -0
  77. data/vendor/assets/javascripts/jquery.ui.effect-fold.js +78 -0
  78. data/vendor/assets/javascripts/jquery.ui.effect-highlight.js +52 -0
  79. data/vendor/assets/javascripts/jquery.ui.effect-pulsate.js +65 -0
  80. data/vendor/assets/javascripts/jquery.ui.effect-scale.js +320 -0
  81. data/vendor/assets/javascripts/jquery.ui.effect-shake.js +76 -0
  82. data/vendor/assets/javascripts/jquery.ui.effect-slide.js +66 -0
  83. data/vendor/assets/javascripts/jquery.ui.effect-transfer.js +49 -0
  84. data/vendor/assets/javascripts/jquery.ui.effect.all.js +14 -0
  85. data/vendor/assets/javascripts/jquery.ui.effect.js +1276 -0
  86. data/vendor/assets/javascripts/jquery.ui.menu.js +614 -0
  87. data/vendor/assets/javascripts/jquery.ui.mouse.js +22 -20
  88. data/vendor/assets/javascripts/jquery.ui.position.js +367 -158
  89. data/vendor/assets/javascripts/jquery.ui.progressbar.js +10 -14
  90. data/vendor/assets/javascripts/jquery.ui.resizable.js +143 -149
  91. data/vendor/assets/javascripts/jquery.ui.selectable.js +22 -28
  92. data/vendor/assets/javascripts/jquery.ui.slider.js +88 -106
  93. data/vendor/assets/javascripts/jquery.ui.sortable.js +97 -95
  94. data/vendor/assets/javascripts/jquery.ui.spinner.js +482 -0
  95. data/vendor/assets/javascripts/jquery.ui.tabs.js +1189 -580
  96. data/vendor/assets/javascripts/jquery.ui.tooltip.js +402 -0
  97. data/vendor/assets/javascripts/jquery.ui.widget.js +373 -117
  98. data/vendor/assets/stylesheets/jquery.ui.accordion.css.erb +10 -13
  99. data/vendor/assets/stylesheets/jquery.ui.all.css.erb +4 -3
  100. data/vendor/assets/stylesheets/jquery.ui.autocomplete.css.erb +10 -44
  101. data/vendor/assets/stylesheets/jquery.ui.base.css.erb +7 -3
  102. data/vendor/assets/stylesheets/jquery.ui.button.css.erb +6 -4
  103. data/vendor/assets/stylesheets/jquery.ui.core.css.erb +5 -4
  104. data/vendor/assets/stylesheets/jquery.ui.datepicker.css.erb +4 -3
  105. data/vendor/assets/stylesheets/jquery.ui.dialog.css.erb +6 -5
  106. data/vendor/assets/stylesheets/jquery.ui.menu.css.erb +34 -0
  107. data/vendor/assets/stylesheets/jquery.ui.progressbar.css.erb +4 -3
  108. data/vendor/assets/stylesheets/jquery.ui.resizable.css.erb +4 -3
  109. data/vendor/assets/stylesheets/jquery.ui.selectable.css.erb +4 -3
  110. data/vendor/assets/stylesheets/jquery.ui.slider.css.erb +4 -3
  111. data/vendor/assets/stylesheets/jquery.ui.spinner.css.erb +27 -0
  112. data/vendor/assets/stylesheets/jquery.ui.tabs.css.erb +8 -8
  113. data/vendor/assets/stylesheets/jquery.ui.theme.css.erb +8 -7
  114. data/vendor/assets/stylesheets/jquery.ui.tooltip.css.erb +25 -0
  115. metadata +24 -19
  116. data/dependencies.js +0 -31
  117. data/vendor/assets/javascripts/jquery.effects.all.js +0 -14
  118. data/vendor/assets/javascripts/jquery.effects.blind.js +0 -51
  119. data/vendor/assets/javascripts/jquery.effects.bounce.js +0 -80
  120. data/vendor/assets/javascripts/jquery.effects.clip.js +0 -56
  121. data/vendor/assets/javascripts/jquery.effects.core.js +0 -612
  122. data/vendor/assets/javascripts/jquery.effects.drop.js +0 -52
  123. data/vendor/assets/javascripts/jquery.effects.explode.js +0 -81
  124. data/vendor/assets/javascripts/jquery.effects.fade.js +0 -34
  125. data/vendor/assets/javascripts/jquery.effects.fold.js +0 -58
  126. data/vendor/assets/javascripts/jquery.effects.highlight.js +0 -52
  127. data/vendor/assets/javascripts/jquery.effects.pulsate.js +0 -53
  128. data/vendor/assets/javascripts/jquery.effects.scale.js +0 -180
  129. data/vendor/assets/javascripts/jquery.effects.shake.js +0 -59
  130. data/vendor/assets/javascripts/jquery.effects.slide.js +0 -52
  131. data/vendor/assets/javascripts/jquery.effects.transfer.js +0 -47
@@ -2,13 +2,14 @@
2
2
  //= require jquery.ui.widget
3
3
 
4
4
  /*!
5
- * jQuery UI Tabs 1.8.24
5
+ * jQuery UI Tabs 1.9.2
6
+ * http://jqueryui.com
6
7
  *
7
- * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
8
- * Dual licensed under the MIT or GPL Version 2 licenses.
8
+ * Copyright 2012 jQuery Foundation and other contributors
9
+ * Released under the MIT license.
9
10
  * http://jquery.org/license
10
11
  *
11
- * http://docs.jquery.com/UI/Tabs
12
+ * http://api.jqueryui.com/tabs/
12
13
  *
13
14
  * Depends:
14
15
  * jquery.ui.core.js
@@ -17,744 +18,1352 @@
17
18
  (function( $, undefined ) {
18
19
 
19
20
  var tabId = 0,
20
- listId = 0;
21
+ rhash = /#.*$/;
21
22
 
22
23
  function getNextTabId() {
23
24
  return ++tabId;
24
25
  }
25
26
 
26
- function getNextListId() {
27
- return ++listId;
27
+ function isLocal( anchor ) {
28
+ return anchor.hash.length > 1 &&
29
+ anchor.href.replace( rhash, "" ) ===
30
+ location.href.replace( rhash, "" )
31
+ // support: Safari 5.1
32
+ // Safari 5.1 doesn't encode spaces in window.location
33
+ // but it does encode spaces from anchors (#8777)
34
+ .replace( /\s/g, "%20" );
28
35
  }
29
36
 
30
37
  $.widget( "ui.tabs", {
38
+ version: "1.9.2",
39
+ delay: 300,
31
40
  options: {
32
- add: null,
33
- ajaxOptions: null,
34
- cache: false,
35
- cookie: null, // e.g. { expires: 7, path: '/', domain: 'jquery.com', secure: true }
41
+ active: null,
36
42
  collapsible: false,
37
- disable: null,
38
- disabled: [],
39
- enable: null,
40
43
  event: "click",
41
- fx: null, // e.g. { height: 'toggle', opacity: 'toggle', duration: 200 }
42
- idPrefix: "ui-tabs-",
43
- load: null,
44
- panelTemplate: "<div></div>",
45
- remove: null,
46
- select: null,
44
+ heightStyle: "content",
45
+ hide: null,
47
46
  show: null,
48
- spinner: "<em>Loading&#8230;</em>",
49
- tabTemplate: "<li><a href='#{href}'><span>#{label}</span></a></li>"
47
+
48
+ // callbacks
49
+ activate: null,
50
+ beforeActivate: null,
51
+ beforeLoad: null,
52
+ load: null
50
53
  },
51
54
 
52
55
  _create: function() {
53
- this._tabify( true );
54
- },
56
+ var that = this,
57
+ options = this.options,
58
+ active = options.active,
59
+ locationHash = location.hash.substring( 1 );
55
60
 
56
- _setOption: function( key, value ) {
57
- if ( key == "selected" ) {
58
- if (this.options.collapsible && value == this.options.selected ) {
59
- return;
61
+ this.running = false;
62
+
63
+ this.element
64
+ .addClass( "ui-tabs ui-widget ui-widget-content ui-corner-all" )
65
+ .toggleClass( "ui-tabs-collapsible", options.collapsible )
66
+ // Prevent users from focusing disabled tabs via click
67
+ .delegate( ".ui-tabs-nav > li", "mousedown" + this.eventNamespace, function( event ) {
68
+ if ( $( this ).is( ".ui-state-disabled" ) ) {
69
+ event.preventDefault();
70
+ }
71
+ })
72
+ // support: IE <9
73
+ // Preventing the default action in mousedown doesn't prevent IE
74
+ // from focusing the element, so if the anchor gets focused, blur.
75
+ // We don't have to worry about focusing the previously focused
76
+ // element since clicking on a non-focusable element should focus
77
+ // the body anyway.
78
+ .delegate( ".ui-tabs-anchor", "focus" + this.eventNamespace, function() {
79
+ if ( $( this ).closest( "li" ).is( ".ui-state-disabled" ) ) {
80
+ this.blur();
81
+ }
82
+ });
83
+
84
+ this._processTabs();
85
+
86
+ if ( active === null ) {
87
+ // check the fragment identifier in the URL
88
+ if ( locationHash ) {
89
+ this.tabs.each(function( i, tab ) {
90
+ if ( $( tab ).attr( "aria-controls" ) === locationHash ) {
91
+ active = i;
92
+ return false;
93
+ }
94
+ });
95
+ }
96
+
97
+ // check for a tab marked active via a class
98
+ if ( active === null ) {
99
+ active = this.tabs.index( this.tabs.filter( ".ui-tabs-active" ) );
100
+ }
101
+
102
+ // no active tab, set to false
103
+ if ( active === null || active === -1 ) {
104
+ active = this.tabs.length ? 0 : false;
60
105
  }
61
- this.select( value );
62
- } else {
63
- this.options[ key ] = value;
64
- this._tabify();
65
106
  }
66
- },
67
107
 
68
- _tabId: function( a ) {
69
- return a.title && a.title.replace( /\s/g, "_" ).replace( /[^\w\u00c0-\uFFFF-]/g, "" ) ||
70
- this.options.idPrefix + getNextTabId();
71
- },
108
+ // handle numbers: negative, out of range
109
+ if ( active !== false ) {
110
+ active = this.tabs.index( this.tabs.eq( active ) );
111
+ if ( active === -1 ) {
112
+ active = options.collapsible ? false : 0;
113
+ }
114
+ }
115
+ options.active = active;
72
116
 
73
- _sanitizeSelector: function( hash ) {
74
- // we need this because an id may contain a ":"
75
- return hash.replace( /:/g, "\\:" );
76
- },
117
+ // don't allow collapsible: false and active: false
118
+ if ( !options.collapsible && options.active === false && this.anchors.length ) {
119
+ options.active = 0;
120
+ }
121
+
122
+ // Take disabling tabs via class attribute from HTML
123
+ // into account and update option properly.
124
+ if ( $.isArray( options.disabled ) ) {
125
+ options.disabled = $.unique( options.disabled.concat(
126
+ $.map( this.tabs.filter( ".ui-state-disabled" ), function( li ) {
127
+ return that.tabs.index( li );
128
+ })
129
+ ) ).sort();
130
+ }
131
+
132
+ // check for length avoids error when initializing empty list
133
+ if ( this.options.active !== false && this.anchors.length ) {
134
+ this.active = this._findActive( this.options.active );
135
+ } else {
136
+ this.active = $();
137
+ }
138
+
139
+ this._refresh();
77
140
 
78
- _cookie: function() {
79
- var cookie = this.cookie ||
80
- ( this.cookie = this.options.cookie.name || "ui-tabs-" + getNextListId() );
81
- return $.cookie.apply( null, [ cookie ].concat( $.makeArray( arguments ) ) );
141
+ if ( this.active.length ) {
142
+ this.load( options.active );
143
+ }
82
144
  },
83
145
 
84
- _ui: function( tab, panel ) {
146
+ _getCreateEventData: function() {
85
147
  return {
86
- tab: tab,
87
- panel: panel,
88
- index: this.anchors.index( tab )
148
+ tab: this.active,
149
+ panel: !this.active.length ? $() : this._getPanelForTab( this.active )
89
150
  };
90
151
  },
91
152
 
92
- _cleanup: function() {
93
- // restore all former loading tabs labels
94
- this.lis.filter( ".ui-state-processing" )
95
- .removeClass( "ui-state-processing" )
96
- .find( "span:data(label.tabs)" )
97
- .each(function() {
98
- var el = $( this );
99
- el.html( el.data( "label.tabs" ) ).removeData( "label.tabs" );
100
- });
153
+ _tabKeydown: function( event ) {
154
+ var focusedTab = $( this.document[0].activeElement ).closest( "li" ),
155
+ selectedIndex = this.tabs.index( focusedTab ),
156
+ goingForward = true;
157
+
158
+ if ( this._handlePageNav( event ) ) {
159
+ return;
160
+ }
161
+
162
+ switch ( event.keyCode ) {
163
+ case $.ui.keyCode.RIGHT:
164
+ case $.ui.keyCode.DOWN:
165
+ selectedIndex++;
166
+ break;
167
+ case $.ui.keyCode.UP:
168
+ case $.ui.keyCode.LEFT:
169
+ goingForward = false;
170
+ selectedIndex--;
171
+ break;
172
+ case $.ui.keyCode.END:
173
+ selectedIndex = this.anchors.length - 1;
174
+ break;
175
+ case $.ui.keyCode.HOME:
176
+ selectedIndex = 0;
177
+ break;
178
+ case $.ui.keyCode.SPACE:
179
+ // Activate only, no collapsing
180
+ event.preventDefault();
181
+ clearTimeout( this.activating );
182
+ this._activate( selectedIndex );
183
+ return;
184
+ case $.ui.keyCode.ENTER:
185
+ // Toggle (cancel delayed activation, allow collapsing)
186
+ event.preventDefault();
187
+ clearTimeout( this.activating );
188
+ // Determine if we should collapse or activate
189
+ this._activate( selectedIndex === this.options.active ? false : selectedIndex );
190
+ return;
191
+ default:
192
+ return;
193
+ }
194
+
195
+ // Focus the appropriate tab, based on which key was pressed
196
+ event.preventDefault();
197
+ clearTimeout( this.activating );
198
+ selectedIndex = this._focusNextTab( selectedIndex, goingForward );
199
+
200
+ // Navigating with control key will prevent automatic activation
201
+ if ( !event.ctrlKey ) {
202
+ // Update aria-selected immediately so that AT think the tab is already selected.
203
+ // Otherwise AT may confuse the user by stating that they need to activate the tab,
204
+ // but the tab will already be activated by the time the announcement finishes.
205
+ focusedTab.attr( "aria-selected", "false" );
206
+ this.tabs.eq( selectedIndex ).attr( "aria-selected", "true" );
207
+
208
+ this.activating = this._delay(function() {
209
+ this.option( "active", selectedIndex );
210
+ }, this.delay );
211
+ }
101
212
  },
102
213
 
103
- _tabify: function( init ) {
104
- var self = this,
105
- o = this.options,
106
- fragmentId = /^#.+/; // Safari 2 reports '#' for an empty hash
214
+ _panelKeydown: function( event ) {
215
+ if ( this._handlePageNav( event ) ) {
216
+ return;
217
+ }
107
218
 
108
- this.list = this.element.find( "ol,ul" ).eq( 0 );
109
- this.lis = $( " > li:has(a[href])", this.list );
110
- this.anchors = this.lis.map(function() {
111
- return $( "a", this )[ 0 ];
112
- });
113
- this.panels = $( [] );
114
-
115
- this.anchors.each(function( i, a ) {
116
- var href = $( a ).attr( "href" );
117
- // For dynamically created HTML that contains a hash as href IE < 8 expands
118
- // such href to the full page url with hash and then misinterprets tab as ajax.
119
- // Same consideration applies for an added tab with a fragment identifier
120
- // since a[href=#fragment-identifier] does unexpectedly not match.
121
- // Thus normalize href attribute...
122
- var hrefBase = href.split( "#" )[ 0 ],
123
- baseEl;
124
- if ( hrefBase && ( hrefBase === location.toString().split( "#" )[ 0 ] ||
125
- ( baseEl = $( "base" )[ 0 ]) && hrefBase === baseEl.href ) ) {
126
- href = a.hash;
127
- a.href = href;
128
- }
219
+ // Ctrl+up moves focus to the current tab
220
+ if ( event.ctrlKey && event.keyCode === $.ui.keyCode.UP ) {
221
+ event.preventDefault();
222
+ this.active.focus();
223
+ }
224
+ },
129
225
 
130
- // inline tab
131
- if ( fragmentId.test( href ) ) {
132
- self.panels = self.panels.add( self.element.find( self._sanitizeSelector( href ) ) );
133
- // remote tab
134
- // prevent loading the page itself if href is just "#"
135
- } else if ( href && href !== "#" ) {
136
- // required for restore on destroy
137
- $.data( a, "href.tabs", href );
138
-
139
- // TODO until #3808 is fixed strip fragment identifier from url
140
- // (IE fails to load from such url)
141
- $.data( a, "load.tabs", href.replace( /#.*$/, "" ) );
142
-
143
- var id = self._tabId( a );
144
- a.href = "#" + id;
145
- var $panel = self.element.find( "#" + id );
146
- if ( !$panel.length ) {
147
- $panel = $( o.panelTemplate )
148
- .attr( "id", id )
149
- .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )
150
- .insertAfter( self.panels[ i - 1 ] || self.list );
151
- $panel.data( "destroy.tabs", true );
152
- }
153
- self.panels = self.panels.add( $panel );
154
- // invalid tab href
155
- } else {
156
- o.disabled.push( i );
157
- }
158
- });
226
+ // Alt+page up/down moves focus to the previous/next tab (and activates)
227
+ _handlePageNav: function( event ) {
228
+ if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_UP ) {
229
+ this._activate( this._focusNextTab( this.options.active - 1, false ) );
230
+ return true;
231
+ }
232
+ if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_DOWN ) {
233
+ this._activate( this._focusNextTab( this.options.active + 1, true ) );
234
+ return true;
235
+ }
236
+ },
159
237
 
160
- // initialization from scratch
161
- if ( init ) {
162
- // attach necessary classes for styling
163
- this.element.addClass( "ui-tabs ui-widget ui-widget-content ui-corner-all" );
164
- this.list.addClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" );
165
- this.lis.addClass( "ui-state-default ui-corner-top" );
166
- this.panels.addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" );
167
-
168
- // Selected tab
169
- // use "selected" option or try to retrieve:
170
- // 1. from fragment identifier in url
171
- // 2. from cookie
172
- // 3. from selected class attribute on <li>
173
- if ( o.selected === undefined ) {
174
- if ( location.hash ) {
175
- this.anchors.each(function( i, a ) {
176
- if ( a.hash == location.hash ) {
177
- o.selected = i;
178
- return false;
179
- }
180
- });
181
- }
182
- if ( typeof o.selected !== "number" && o.cookie ) {
183
- o.selected = parseInt( self._cookie(), 10 );
184
- }
185
- if ( typeof o.selected !== "number" && this.lis.filter( ".ui-tabs-selected" ).length ) {
186
- o.selected = this.lis.index( this.lis.filter( ".ui-tabs-selected" ) );
187
- }
188
- o.selected = o.selected || ( this.lis.length ? 0 : -1 );
189
- } else if ( o.selected === null ) { // usage of null is deprecated, TODO remove in next release
190
- o.selected = -1;
191
- }
192
-
193
- // sanity check - default to first tab...
194
- o.selected = ( ( o.selected >= 0 && this.anchors[ o.selected ] ) || o.selected < 0 )
195
- ? o.selected
196
- : 0;
197
-
198
- // Take disabling tabs via class attribute from HTML
199
- // into account and update option properly.
200
- // A selected tab cannot become disabled.
201
- o.disabled = $.unique( o.disabled.concat(
202
- $.map( this.lis.filter( ".ui-state-disabled" ), function( n, i ) {
203
- return self.lis.index( n );
204
- })
205
- ) ).sort();
238
+ _findNextTab: function( index, goingForward ) {
239
+ var lastTabIndex = this.tabs.length - 1;
206
240
 
207
- if ( $.inArray( o.selected, o.disabled ) != -1 ) {
208
- o.disabled.splice( $.inArray( o.selected, o.disabled ), 1 );
241
+ function constrain() {
242
+ if ( index > lastTabIndex ) {
243
+ index = 0;
209
244
  }
245
+ if ( index < 0 ) {
246
+ index = lastTabIndex;
247
+ }
248
+ return index;
249
+ }
210
250
 
211
- // highlight selected tab
212
- this.panels.addClass( "ui-tabs-hide" );
213
- this.lis.removeClass( "ui-tabs-selected ui-state-active" );
214
- // check for length avoids error when initializing empty list
215
- if ( o.selected >= 0 && this.anchors.length ) {
216
- self.element.find( self._sanitizeSelector( self.anchors[ o.selected ].hash ) ).removeClass( "ui-tabs-hide" );
217
- this.lis.eq( o.selected ).addClass( "ui-tabs-selected ui-state-active" );
251
+ while ( $.inArray( constrain(), this.options.disabled ) !== -1 ) {
252
+ index = goingForward ? index + 1 : index - 1;
253
+ }
218
254
 
219
- // seems to be expected behavior that the show callback is fired
220
- self.element.queue( "tabs", function() {
221
- self._trigger( "show", null,
222
- self._ui( self.anchors[ o.selected ], self.element.find( self._sanitizeSelector( self.anchors[ o.selected ].hash ) )[ 0 ] ) );
223
- });
255
+ return index;
256
+ },
224
257
 
225
- this.load( o.selected );
226
- }
258
+ _focusNextTab: function( index, goingForward ) {
259
+ index = this._findNextTab( index, goingForward );
260
+ this.tabs.eq( index ).focus();
261
+ return index;
262
+ },
227
263
 
228
- // clean up to avoid memory leaks in certain versions of IE 6
229
- // TODO: namespace this event
230
- $( window ).bind( "unload", function() {
231
- self.lis.add( self.anchors ).unbind( ".tabs" );
232
- self.lis = self.anchors = self.panels = null;
233
- });
234
- // update selected after add/remove
235
- } else {
236
- o.selected = this.lis.index( this.lis.filter( ".ui-tabs-selected" ) );
264
+ _setOption: function( key, value ) {
265
+ if ( key === "active" ) {
266
+ // _activate() will handle invalid values and update this.options
267
+ this._activate( value );
268
+ return;
237
269
  }
238
270
 
239
- // update collapsible
240
- // TODO: use .toggleClass()
241
- this.element[ o.collapsible ? "addClass" : "removeClass" ]( "ui-tabs-collapsible" );
271
+ if ( key === "disabled" ) {
272
+ // don't use the widget factory's disabled handling
273
+ this._setupDisabled( value );
274
+ return;
275
+ }
276
+
277
+ this._super( key, value);
242
278
 
243
- // set or update cookie after init and add/remove respectively
244
- if ( o.cookie ) {
245
- this._cookie( o.selected, o.cookie );
279
+ if ( key === "collapsible" ) {
280
+ this.element.toggleClass( "ui-tabs-collapsible", value );
281
+ // Setting collapsible: false while collapsed; open first panel
282
+ if ( !value && this.options.active === false ) {
283
+ this._activate( 0 );
284
+ }
246
285
  }
247
286
 
248
- // disable tabs
249
- for ( var i = 0, li; ( li = this.lis[ i ] ); i++ ) {
250
- $( li )[ $.inArray( i, o.disabled ) != -1 &&
251
- // TODO: use .toggleClass()
252
- !$( li ).hasClass( "ui-tabs-selected" ) ? "addClass" : "removeClass" ]( "ui-state-disabled" );
287
+ if ( key === "event" ) {
288
+ this._setupEvents( value );
253
289
  }
254
290
 
255
- // reset cache if switching from cached to not cached
256
- if ( o.cache === false ) {
257
- this.anchors.removeData( "cache.tabs" );
291
+ if ( key === "heightStyle" ) {
292
+ this._setupHeightStyle( value );
258
293
  }
294
+ },
259
295
 
260
- // remove all handlers before, tabify may run on existing tabs after add or option change
261
- this.lis.add( this.anchors ).unbind( ".tabs" );
296
+ _tabId: function( tab ) {
297
+ return tab.attr( "aria-controls" ) || "ui-tabs-" + getNextTabId();
298
+ },
262
299
 
263
- if ( o.event !== "mouseover" ) {
264
- var addState = function( state, el ) {
265
- if ( el.is( ":not(.ui-state-disabled)" ) ) {
266
- el.addClass( "ui-state-" + state );
267
- }
268
- };
269
- var removeState = function( state, el ) {
270
- el.removeClass( "ui-state-" + state );
271
- };
272
- this.lis.bind( "mouseover.tabs" , function() {
273
- addState( "hover", $( this ) );
300
+ _sanitizeSelector: function( hash ) {
301
+ return hash ? hash.replace( /[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g, "\\$&" ) : "";
302
+ },
303
+
304
+ refresh: function() {
305
+ var options = this.options,
306
+ lis = this.tablist.children( ":has(a[href])" );
307
+
308
+ // get disabled tabs from class attribute from HTML
309
+ // this will get converted to a boolean if needed in _refresh()
310
+ options.disabled = $.map( lis.filter( ".ui-state-disabled" ), function( tab ) {
311
+ return lis.index( tab );
312
+ });
313
+
314
+ this._processTabs();
315
+
316
+ // was collapsed or no tabs
317
+ if ( options.active === false || !this.anchors.length ) {
318
+ options.active = false;
319
+ this.active = $();
320
+ // was active, but active tab is gone
321
+ } else if ( this.active.length && !$.contains( this.tablist[ 0 ], this.active[ 0 ] ) ) {
322
+ // all remaining tabs are disabled
323
+ if ( this.tabs.length === options.disabled.length ) {
324
+ options.active = false;
325
+ this.active = $();
326
+ // activate previous tab
327
+ } else {
328
+ this._activate( this._findNextTab( Math.max( 0, options.active - 1 ), false ) );
329
+ }
330
+ // was active, active tab still exists
331
+ } else {
332
+ // make sure active index is correct
333
+ options.active = this.tabs.index( this.active );
334
+ }
335
+
336
+ this._refresh();
337
+ },
338
+
339
+ _refresh: function() {
340
+ this._setupDisabled( this.options.disabled );
341
+ this._setupEvents( this.options.event );
342
+ this._setupHeightStyle( this.options.heightStyle );
343
+
344
+ this.tabs.not( this.active ).attr({
345
+ "aria-selected": "false",
346
+ tabIndex: -1
347
+ });
348
+ this.panels.not( this._getPanelForTab( this.active ) )
349
+ .hide()
350
+ .attr({
351
+ "aria-expanded": "false",
352
+ "aria-hidden": "true"
274
353
  });
275
- this.lis.bind( "mouseout.tabs", function() {
276
- removeState( "hover", $( this ) );
354
+
355
+ // Make sure one tab is in the tab order
356
+ if ( !this.active.length ) {
357
+ this.tabs.eq( 0 ).attr( "tabIndex", 0 );
358
+ } else {
359
+ this.active
360
+ .addClass( "ui-tabs-active ui-state-active" )
361
+ .attr({
362
+ "aria-selected": "true",
363
+ tabIndex: 0
364
+ });
365
+ this._getPanelForTab( this.active )
366
+ .show()
367
+ .attr({
368
+ "aria-expanded": "true",
369
+ "aria-hidden": "false"
370
+ });
371
+ }
372
+ },
373
+
374
+ _processTabs: function() {
375
+ var that = this;
376
+
377
+ this.tablist = this._getList()
378
+ .addClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" )
379
+ .attr( "role", "tablist" );
380
+
381
+ this.tabs = this.tablist.find( "> li:has(a[href])" )
382
+ .addClass( "ui-state-default ui-corner-top" )
383
+ .attr({
384
+ role: "tab",
385
+ tabIndex: -1
277
386
  });
278
- this.anchors.bind( "focus.tabs", function() {
279
- addState( "focus", $( this ).closest( "li" ) );
387
+
388
+ this.anchors = this.tabs.map(function() {
389
+ return $( "a", this )[ 0 ];
390
+ })
391
+ .addClass( "ui-tabs-anchor" )
392
+ .attr({
393
+ role: "presentation",
394
+ tabIndex: -1
280
395
  });
281
- this.anchors.bind( "blur.tabs", function() {
282
- removeState( "focus", $( this ).closest( "li" ) );
396
+
397
+ this.panels = $();
398
+
399
+ this.anchors.each(function( i, anchor ) {
400
+ var selector, panel, panelId,
401
+ anchorId = $( anchor ).uniqueId().attr( "id" ),
402
+ tab = $( anchor ).closest( "li" ),
403
+ originalAriaControls = tab.attr( "aria-controls" );
404
+
405
+ // inline tab
406
+ if ( isLocal( anchor ) ) {
407
+ selector = anchor.hash;
408
+ panel = that.element.find( that._sanitizeSelector( selector ) );
409
+ // remote tab
410
+ } else {
411
+ panelId = that._tabId( tab );
412
+ selector = "#" + panelId;
413
+ panel = that.element.find( selector );
414
+ if ( !panel.length ) {
415
+ panel = that._createPanel( panelId );
416
+ panel.insertAfter( that.panels[ i - 1 ] || that.tablist );
417
+ }
418
+ panel.attr( "aria-live", "polite" );
419
+ }
420
+
421
+ if ( panel.length) {
422
+ that.panels = that.panels.add( panel );
423
+ }
424
+ if ( originalAriaControls ) {
425
+ tab.data( "ui-tabs-aria-controls", originalAriaControls );
426
+ }
427
+ tab.attr({
428
+ "aria-controls": selector.substring( 1 ),
429
+ "aria-labelledby": anchorId
283
430
  });
431
+ panel.attr( "aria-labelledby", anchorId );
432
+ });
433
+
434
+ this.panels
435
+ .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )
436
+ .attr( "role", "tabpanel" );
437
+ },
438
+
439
+ // allow overriding how to find the list for rare usage scenarios (#7715)
440
+ _getList: function() {
441
+ return this.element.find( "ol,ul" ).eq( 0 );
442
+ },
443
+
444
+ _createPanel: function( id ) {
445
+ return $( "<div>" )
446
+ .attr( "id", id )
447
+ .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )
448
+ .data( "ui-tabs-destroy", true );
449
+ },
450
+
451
+ _setupDisabled: function( disabled ) {
452
+ if ( $.isArray( disabled ) ) {
453
+ if ( !disabled.length ) {
454
+ disabled = false;
455
+ } else if ( disabled.length === this.anchors.length ) {
456
+ disabled = true;
457
+ }
284
458
  }
285
459
 
286
- // set up animations
287
- var hideFx, showFx;
288
- if ( o.fx ) {
289
- if ( $.isArray( o.fx ) ) {
290
- hideFx = o.fx[ 0 ];
291
- showFx = o.fx[ 1 ];
460
+ // disable tabs
461
+ for ( var i = 0, li; ( li = this.tabs[ i ] ); i++ ) {
462
+ if ( disabled === true || $.inArray( i, disabled ) !== -1 ) {
463
+ $( li )
464
+ .addClass( "ui-state-disabled" )
465
+ .attr( "aria-disabled", "true" );
292
466
  } else {
293
- hideFx = showFx = o.fx;
467
+ $( li )
468
+ .removeClass( "ui-state-disabled" )
469
+ .removeAttr( "aria-disabled" );
294
470
  }
295
471
  }
296
472
 
297
- // Reset certain styles left over from animation
298
- // and prevent IE's ClearType bug...
299
- function resetStyle( $el, fx ) {
300
- $el.css( "display", "" );
301
- if ( !$.support.opacity && fx.opacity ) {
302
- $el[ 0 ].style.removeAttribute( "filter" );
473
+ this.options.disabled = disabled;
474
+ },
475
+
476
+ _setupEvents: function( event ) {
477
+ var events = {
478
+ click: function( event ) {
479
+ event.preventDefault();
303
480
  }
481
+ };
482
+ if ( event ) {
483
+ $.each( event.split(" "), function( index, eventName ) {
484
+ events[ eventName ] = "_eventHandler";
485
+ });
304
486
  }
305
487
 
306
- // Show a tab...
307
- var showTab = showFx
308
- ? function( clicked, $show ) {
309
- $( clicked ).closest( "li" ).addClass( "ui-tabs-selected ui-state-active" );
310
- $show.hide().removeClass( "ui-tabs-hide" ) // avoid flicker that way
311
- .animate( showFx, showFx.duration || "normal", function() {
312
- resetStyle( $show, showFx );
313
- self._trigger( "show", null, self._ui( clicked, $show[ 0 ] ) );
314
- });
315
- }
316
- : function( clicked, $show ) {
317
- $( clicked ).closest( "li" ).addClass( "ui-tabs-selected ui-state-active" );
318
- $show.removeClass( "ui-tabs-hide" );
319
- self._trigger( "show", null, self._ui( clicked, $show[ 0 ] ) );
320
- };
488
+ this._off( this.anchors.add( this.tabs ).add( this.panels ) );
489
+ this._on( this.anchors, events );
490
+ this._on( this.tabs, { keydown: "_tabKeydown" } );
491
+ this._on( this.panels, { keydown: "_panelKeydown" } );
321
492
 
322
- // Hide a tab, $show is optional...
323
- var hideTab = hideFx
324
- ? function( clicked, $hide ) {
325
- $hide.animate( hideFx, hideFx.duration || "normal", function() {
326
- self.lis.removeClass( "ui-tabs-selected ui-state-active" );
327
- $hide.addClass( "ui-tabs-hide" );
328
- resetStyle( $hide, hideFx );
329
- self.element.dequeue( "tabs" );
330
- });
493
+ this._focusable( this.tabs );
494
+ this._hoverable( this.tabs );
495
+ },
496
+
497
+ _setupHeightStyle: function( heightStyle ) {
498
+ var maxHeight, overflow,
499
+ parent = this.element.parent();
500
+
501
+ if ( heightStyle === "fill" ) {
502
+ // IE 6 treats height like minHeight, so we need to turn off overflow
503
+ // in order to get a reliable height
504
+ // we use the minHeight support test because we assume that only
505
+ // browsers that don't support minHeight will treat height as minHeight
506
+ if ( !$.support.minHeight ) {
507
+ overflow = parent.css( "overflow" );
508
+ parent.css( "overflow", "hidden");
331
509
  }
332
- : function( clicked, $hide, $show ) {
333
- self.lis.removeClass( "ui-tabs-selected ui-state-active" );
334
- $hide.addClass( "ui-tabs-hide" );
335
- self.element.dequeue( "tabs" );
336
- };
510
+ maxHeight = parent.height();
511
+ this.element.siblings( ":visible" ).each(function() {
512
+ var elem = $( this ),
513
+ position = elem.css( "position" );
337
514
 
338
- // attach tab event handler, unbind to avoid duplicates from former tabifying...
339
- this.anchors.bind( o.event + ".tabs", function() {
340
- var el = this,
341
- $li = $(el).closest( "li" ),
342
- $hide = self.panels.filter( ":not(.ui-tabs-hide)" ),
343
- $show = self.element.find( self._sanitizeSelector( el.hash ) );
344
-
345
- // If tab is already selected and not collapsible or tab disabled or
346
- // or is already loading or click callback returns false stop here.
347
- // Check if click handler returns false last so that it is not executed
348
- // for a disabled or loading tab!
349
- if ( ( $li.hasClass( "ui-tabs-selected" ) && !o.collapsible) ||
350
- $li.hasClass( "ui-state-disabled" ) ||
351
- $li.hasClass( "ui-state-processing" ) ||
352
- self.panels.filter( ":animated" ).length ||
353
- self._trigger( "select", null, self._ui( this, $show[ 0 ] ) ) === false ) {
354
- this.blur();
355
- return false;
515
+ if ( position === "absolute" || position === "fixed" ) {
516
+ return;
517
+ }
518
+ maxHeight -= elem.outerHeight( true );
519
+ });
520
+ if ( overflow ) {
521
+ parent.css( "overflow", overflow );
356
522
  }
357
523
 
358
- o.selected = self.anchors.index( this );
524
+ this.element.children().not( this.panels ).each(function() {
525
+ maxHeight -= $( this ).outerHeight( true );
526
+ });
359
527
 
360
- self.abort();
528
+ this.panels.each(function() {
529
+ $( this ).height( Math.max( 0, maxHeight -
530
+ $( this ).innerHeight() + $( this ).height() ) );
531
+ })
532
+ .css( "overflow", "auto" );
533
+ } else if ( heightStyle === "auto" ) {
534
+ maxHeight = 0;
535
+ this.panels.each(function() {
536
+ maxHeight = Math.max( maxHeight, $( this ).height( "" ).height() );
537
+ }).height( maxHeight );
538
+ }
539
+ },
361
540
 
362
- // if tab may be closed
363
- if ( o.collapsible ) {
364
- if ( $li.hasClass( "ui-tabs-selected" ) ) {
365
- o.selected = -1;
541
+ _eventHandler: function( event ) {
542
+ var options = this.options,
543
+ active = this.active,
544
+ anchor = $( event.currentTarget ),
545
+ tab = anchor.closest( "li" ),
546
+ clickedIsActive = tab[ 0 ] === active[ 0 ],
547
+ collapsing = clickedIsActive && options.collapsible,
548
+ toShow = collapsing ? $() : this._getPanelForTab( tab ),
549
+ toHide = !active.length ? $() : this._getPanelForTab( active ),
550
+ eventData = {
551
+ oldTab: active,
552
+ oldPanel: toHide,
553
+ newTab: collapsing ? $() : tab,
554
+ newPanel: toShow
555
+ };
366
556
 
367
- if ( o.cookie ) {
368
- self._cookie( o.selected, o.cookie );
369
- }
557
+ event.preventDefault();
558
+
559
+ if ( tab.hasClass( "ui-state-disabled" ) ||
560
+ // tab is already loading
561
+ tab.hasClass( "ui-tabs-loading" ) ||
562
+ // can't switch durning an animation
563
+ this.running ||
564
+ // click on active header, but not collapsible
565
+ ( clickedIsActive && !options.collapsible ) ||
566
+ // allow canceling activation
567
+ ( this._trigger( "beforeActivate", event, eventData ) === false ) ) {
568
+ return;
569
+ }
370
570
 
371
- self.element.queue( "tabs", function() {
372
- hideTab( el, $hide );
373
- }).dequeue( "tabs" );
571
+ options.active = collapsing ? false : this.tabs.index( tab );
374
572
 
375
- this.blur();
376
- return false;
377
- } else if ( !$hide.length ) {
378
- if ( o.cookie ) {
379
- self._cookie( o.selected, o.cookie );
380
- }
573
+ this.active = clickedIsActive ? $() : tab;
574
+ if ( this.xhr ) {
575
+ this.xhr.abort();
576
+ }
381
577
 
382
- self.element.queue( "tabs", function() {
383
- showTab( el, $show );
384
- });
578
+ if ( !toHide.length && !toShow.length ) {
579
+ $.error( "jQuery UI Tabs: Mismatching fragment identifier." );
580
+ }
385
581
 
386
- // TODO make passing in node possible, see also http://dev.jqueryui.com/ticket/3171
387
- self.load( self.anchors.index( this ) );
582
+ if ( toShow.length ) {
583
+ this.load( this.tabs.index( tab ), event );
584
+ }
585
+ this._toggle( event, eventData );
586
+ },
388
587
 
389
- this.blur();
390
- return false;
391
- }
392
- }
588
+ // handles show/hide for selecting tabs
589
+ _toggle: function( event, eventData ) {
590
+ var that = this,
591
+ toShow = eventData.newPanel,
592
+ toHide = eventData.oldPanel;
393
593
 
394
- if ( o.cookie ) {
395
- self._cookie( o.selected, o.cookie );
396
- }
594
+ this.running = true;
397
595
 
398
- // show new tab
399
- if ( $show.length ) {
400
- if ( $hide.length ) {
401
- self.element.queue( "tabs", function() {
402
- hideTab( el, $hide );
403
- });
404
- }
405
- self.element.queue( "tabs", function() {
406
- showTab( el, $show );
407
- });
596
+ function complete() {
597
+ that.running = false;
598
+ that._trigger( "activate", event, eventData );
599
+ }
600
+
601
+ function show() {
602
+ eventData.newTab.closest( "li" ).addClass( "ui-tabs-active ui-state-active" );
408
603
 
409
- self.load( self.anchors.index( this ) );
604
+ if ( toShow.length && that.options.show ) {
605
+ that._show( toShow, that.options.show, complete );
410
606
  } else {
411
- throw "jQuery UI Tabs: Mismatching fragment identifier.";
607
+ toShow.show();
608
+ complete();
412
609
  }
610
+ }
413
611
 
414
- // Prevent IE from keeping other link focussed when using the back button
415
- // and remove dotted border from clicked link. This is controlled via CSS
416
- // in modern browsers; blur() removes focus from address bar in Firefox
417
- // which can become a usability and annoying problem with tabs('rotate').
418
- if ( $.browser.msie ) {
419
- this.blur();
420
- }
612
+ // start out by hiding, then showing, then completing
613
+ if ( toHide.length && this.options.hide ) {
614
+ this._hide( toHide, this.options.hide, function() {
615
+ eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );
616
+ show();
617
+ });
618
+ } else {
619
+ eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );
620
+ toHide.hide();
621
+ show();
622
+ }
623
+
624
+ toHide.attr({
625
+ "aria-expanded": "false",
626
+ "aria-hidden": "true"
421
627
  });
628
+ eventData.oldTab.attr( "aria-selected", "false" );
629
+ // If we're switching tabs, remove the old tab from the tab order.
630
+ // If we're opening from collapsed state, remove the previous tab from the tab order.
631
+ // If we're collapsing, then keep the collapsing tab in the tab order.
632
+ if ( toShow.length && toHide.length ) {
633
+ eventData.oldTab.attr( "tabIndex", -1 );
634
+ } else if ( toShow.length ) {
635
+ this.tabs.filter(function() {
636
+ return $( this ).attr( "tabIndex" ) === 0;
637
+ })
638
+ .attr( "tabIndex", -1 );
639
+ }
422
640
 
423
- // disable click in any case
424
- this.anchors.bind( "click.tabs", function(){
425
- return false;
641
+ toShow.attr({
642
+ "aria-expanded": "true",
643
+ "aria-hidden": "false"
644
+ });
645
+ eventData.newTab.attr({
646
+ "aria-selected": "true",
647
+ tabIndex: 0
426
648
  });
427
649
  },
428
650
 
429
- _getIndex: function( index ) {
651
+ _activate: function( index ) {
652
+ var anchor,
653
+ active = this._findActive( index );
654
+
655
+ // trying to activate the already active panel
656
+ if ( active[ 0 ] === this.active[ 0 ] ) {
657
+ return;
658
+ }
659
+
660
+ // trying to collapse, simulate a click on the current active header
661
+ if ( !active.length ) {
662
+ active = this.active;
663
+ }
664
+
665
+ anchor = active.find( ".ui-tabs-anchor" )[ 0 ];
666
+ this._eventHandler({
667
+ target: anchor,
668
+ currentTarget: anchor,
669
+ preventDefault: $.noop
670
+ });
671
+ },
672
+
673
+ _findActive: function( index ) {
674
+ return index === false ? $() : this.tabs.eq( index );
675
+ },
676
+
677
+ _getIndex: function( index ) {
430
678
  // meta-function to give users option to provide a href string instead of a numerical index.
431
- // also sanitizes numerical indexes to valid values.
432
- if ( typeof index == "string" ) {
679
+ if ( typeof index === "string" ) {
433
680
  index = this.anchors.index( this.anchors.filter( "[href$='" + index + "']" ) );
434
681
  }
435
682
 
436
683
  return index;
437
684
  },
438
685
 
439
- destroy: function() {
440
- var o = this.options;
686
+ _destroy: function() {
687
+ if ( this.xhr ) {
688
+ this.xhr.abort();
689
+ }
441
690
 
442
- this.abort();
691
+ this.element.removeClass( "ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible" );
443
692
 
444
- this.element
445
- .unbind( ".tabs" )
446
- .removeClass( "ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible" )
447
- .removeData( "tabs" );
693
+ this.tablist
694
+ .removeClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" )
695
+ .removeAttr( "role" );
448
696
 
449
- this.list.removeClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" );
697
+ this.anchors
698
+ .removeClass( "ui-tabs-anchor" )
699
+ .removeAttr( "role" )
700
+ .removeAttr( "tabIndex" )
701
+ .removeData( "href.tabs" )
702
+ .removeData( "load.tabs" )
703
+ .removeUniqueId();
450
704
 
451
- this.anchors.each(function() {
452
- var href = $.data( this, "href.tabs" );
453
- if ( href ) {
454
- this.href = href;
705
+ this.tabs.add( this.panels ).each(function() {
706
+ if ( $.data( this, "ui-tabs-destroy" ) ) {
707
+ $( this ).remove();
708
+ } else {
709
+ $( this )
710
+ .removeClass( "ui-state-default ui-state-active ui-state-disabled " +
711
+ "ui-corner-top ui-corner-bottom ui-widget-content ui-tabs-active ui-tabs-panel" )
712
+ .removeAttr( "tabIndex" )
713
+ .removeAttr( "aria-live" )
714
+ .removeAttr( "aria-busy" )
715
+ .removeAttr( "aria-selected" )
716
+ .removeAttr( "aria-labelledby" )
717
+ .removeAttr( "aria-hidden" )
718
+ .removeAttr( "aria-expanded" )
719
+ .removeAttr( "role" );
455
720
  }
456
- var $this = $( this ).unbind( ".tabs" );
457
- $.each( [ "href", "load", "cache" ], function( i, prefix ) {
458
- $this.removeData( prefix + ".tabs" );
459
- });
460
721
  });
461
722
 
462
- this.lis.unbind( ".tabs" ).add( this.panels ).each(function() {
463
- if ( $.data( this, "destroy.tabs" ) ) {
464
- $( this ).remove();
723
+ this.tabs.each(function() {
724
+ var li = $( this ),
725
+ prev = li.data( "ui-tabs-aria-controls" );
726
+ if ( prev ) {
727
+ li.attr( "aria-controls", prev );
465
728
  } else {
466
- $( this ).removeClass([
467
- "ui-state-default",
468
- "ui-corner-top",
469
- "ui-tabs-selected",
470
- "ui-state-active",
471
- "ui-state-hover",
472
- "ui-state-focus",
473
- "ui-state-disabled",
474
- "ui-tabs-panel",
475
- "ui-widget-content",
476
- "ui-corner-bottom",
477
- "ui-tabs-hide"
478
- ].join( " " ) );
729
+ li.removeAttr( "aria-controls" );
479
730
  }
480
731
  });
481
732
 
482
- if ( o.cookie ) {
483
- this._cookie( null, o.cookie );
484
- }
733
+ this.panels.show();
485
734
 
486
- return this;
735
+ if ( this.options.heightStyle !== "content" ) {
736
+ this.panels.css( "height", "" );
737
+ }
487
738
  },
488
739
 
489
- add: function( url, label, index ) {
490
- if ( index === undefined ) {
491
- index = this.anchors.length;
740
+ enable: function( index ) {
741
+ var disabled = this.options.disabled;
742
+ if ( disabled === false ) {
743
+ return;
492
744
  }
493
745
 
494
- var self = this,
495
- o = this.options,
496
- $li = $( o.tabTemplate.replace( /#\{href\}/g, url ).replace( /#\{label\}/g, label ) ),
497
- id = !url.indexOf( "#" ) ? url.replace( "#", "" ) : this._tabId( $( "a", $li )[ 0 ] );
498
-
499
- $li.addClass( "ui-state-default ui-corner-top" ).data( "destroy.tabs", true );
746
+ if ( index === undefined ) {
747
+ disabled = false;
748
+ } else {
749
+ index = this._getIndex( index );
750
+ if ( $.isArray( disabled ) ) {
751
+ disabled = $.map( disabled, function( num ) {
752
+ return num !== index ? num : null;
753
+ });
754
+ } else {
755
+ disabled = $.map( this.tabs, function( li, num ) {
756
+ return num !== index ? num : null;
757
+ });
758
+ }
759
+ }
760
+ this._setupDisabled( disabled );
761
+ },
500
762
 
501
- // try to find an existing element before creating a new one
502
- var $panel = self.element.find( "#" + id );
503
- if ( !$panel.length ) {
504
- $panel = $( o.panelTemplate )
505
- .attr( "id", id )
506
- .data( "destroy.tabs", true );
763
+ disable: function( index ) {
764
+ var disabled = this.options.disabled;
765
+ if ( disabled === true ) {
766
+ return;
507
767
  }
508
- $panel.addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide" );
509
768
 
510
- if ( index >= this.lis.length ) {
511
- $li.appendTo( this.list );
512
- $panel.appendTo( this.list[ 0 ].parentNode );
769
+ if ( index === undefined ) {
770
+ disabled = true;
513
771
  } else {
514
- $li.insertBefore( this.lis[ index ] );
515
- $panel.insertBefore( this.panels[ index ] );
772
+ index = this._getIndex( index );
773
+ if ( $.inArray( index, disabled ) !== -1 ) {
774
+ return;
775
+ }
776
+ if ( $.isArray( disabled ) ) {
777
+ disabled = $.merge( [ index ], disabled ).sort();
778
+ } else {
779
+ disabled = [ index ];
780
+ }
516
781
  }
782
+ this._setupDisabled( disabled );
783
+ },
517
784
 
518
- o.disabled = $.map( o.disabled, function( n, i ) {
519
- return n >= index ? ++n : n;
520
- });
785
+ load: function( index, event ) {
786
+ index = this._getIndex( index );
787
+ var that = this,
788
+ tab = this.tabs.eq( index ),
789
+ anchor = tab.find( ".ui-tabs-anchor" ),
790
+ panel = this._getPanelForTab( tab ),
791
+ eventData = {
792
+ tab: tab,
793
+ panel: panel
794
+ };
521
795
 
522
- this._tabify();
796
+ // not remote
797
+ if ( isLocal( anchor[ 0 ] ) ) {
798
+ return;
799
+ }
523
800
 
524
- if ( this.anchors.length == 1 ) {
525
- o.selected = 0;
526
- $li.addClass( "ui-tabs-selected ui-state-active" );
527
- $panel.removeClass( "ui-tabs-hide" );
528
- this.element.queue( "tabs", function() {
529
- self._trigger( "show", null, self._ui( self.anchors[ 0 ], self.panels[ 0 ] ) );
530
- });
801
+ this.xhr = $.ajax( this._ajaxSettings( anchor, event, eventData ) );
802
+
803
+ // support: jQuery <1.8
804
+ // jQuery <1.8 returns false if the request is canceled in beforeSend,
805
+ // but as of 1.8, $.ajax() always returns a jqXHR object.
806
+ if ( this.xhr && this.xhr.statusText !== "canceled" ) {
807
+ tab.addClass( "ui-tabs-loading" );
808
+ panel.attr( "aria-busy", "true" );
809
+
810
+ this.xhr
811
+ .success(function( response ) {
812
+ // support: jQuery <1.8
813
+ // http://bugs.jquery.com/ticket/11778
814
+ setTimeout(function() {
815
+ panel.html( response );
816
+ that._trigger( "load", event, eventData );
817
+ }, 1 );
818
+ })
819
+ .complete(function( jqXHR, status ) {
820
+ // support: jQuery <1.8
821
+ // http://bugs.jquery.com/ticket/11778
822
+ setTimeout(function() {
823
+ if ( status === "abort" ) {
824
+ that.panels.stop( false, true );
825
+ }
826
+
827
+ tab.removeClass( "ui-tabs-loading" );
828
+ panel.removeAttr( "aria-busy" );
531
829
 
532
- this.load( 0 );
830
+ if ( jqXHR === that.xhr ) {
831
+ delete that.xhr;
832
+ }
833
+ }, 1 );
834
+ });
533
835
  }
836
+ },
534
837
 
535
- this._trigger( "add", null, this._ui( this.anchors[ index ], this.panels[ index ] ) );
536
- return this;
838
+ // TODO: Remove this function in 1.10 when ajaxOptions is removed
839
+ _ajaxSettings: function( anchor, event, eventData ) {
840
+ var that = this;
841
+ return {
842
+ url: anchor.attr( "href" ),
843
+ beforeSend: function( jqXHR, settings ) {
844
+ return that._trigger( "beforeLoad", event,
845
+ $.extend( { jqXHR : jqXHR, ajaxSettings: settings }, eventData ) );
846
+ }
847
+ };
537
848
  },
538
849
 
539
- remove: function( index ) {
540
- index = this._getIndex( index );
541
- var o = this.options,
542
- $li = this.lis.eq( index ).remove(),
543
- $panel = this.panels.eq( index ).remove();
544
-
545
- // If selected tab was removed focus tab to the right or
546
- // in case the last tab was removed the tab to the left.
547
- if ( $li.hasClass( "ui-tabs-selected" ) && this.anchors.length > 1) {
548
- this.select( index + ( index + 1 < this.anchors.length ? 1 : -1 ) );
549
- }
550
-
551
- o.disabled = $.map(
552
- $.grep( o.disabled, function(n, i) {
553
- return n != index;
554
- }),
555
- function( n, i ) {
556
- return n >= index ? --n : n;
557
- });
850
+ _getPanelForTab: function( tab ) {
851
+ var id = $( tab ).attr( "aria-controls" );
852
+ return this.element.find( this._sanitizeSelector( "#" + id ) );
853
+ }
854
+ });
558
855
 
559
- this._tabify();
856
+ // DEPRECATED
857
+ if ( $.uiBackCompat !== false ) {
560
858
 
561
- this._trigger( "remove", null, this._ui( $li.find( "a" )[ 0 ], $panel[ 0 ] ) );
562
- return this;
563
- },
859
+ // helper method for a lot of the back compat extensions
860
+ $.ui.tabs.prototype._ui = function( tab, panel ) {
861
+ return {
862
+ tab: tab,
863
+ panel: panel,
864
+ index: this.anchors.index( tab )
865
+ };
866
+ };
564
867
 
565
- enable: function( index ) {
566
- index = this._getIndex( index );
567
- var o = this.options;
568
- if ( $.inArray( index, o.disabled ) == -1 ) {
569
- return;
868
+ // url method
869
+ $.widget( "ui.tabs", $.ui.tabs, {
870
+ url: function( index, url ) {
871
+ this.anchors.eq( index ).attr( "href", url );
570
872
  }
873
+ });
874
+
875
+ // TODO: Remove _ajaxSettings() method when removing this extension
876
+ // ajaxOptions and cache options
877
+ $.widget( "ui.tabs", $.ui.tabs, {
878
+ options: {
879
+ ajaxOptions: null,
880
+ cache: false
881
+ },
882
+
883
+ _create: function() {
884
+ this._super();
885
+
886
+ var that = this;
887
+
888
+ this._on({ tabsbeforeload: function( event, ui ) {
889
+ // tab is already cached
890
+ if ( $.data( ui.tab[ 0 ], "cache.tabs" ) ) {
891
+ event.preventDefault();
892
+ return;
893
+ }
571
894
 
572
- this.lis.eq( index ).removeClass( "ui-state-disabled" );
573
- o.disabled = $.grep( o.disabled, function( n, i ) {
574
- return n != index;
575
- });
576
-
577
- this._trigger( "enable", null, this._ui( this.anchors[ index ], this.panels[ index ] ) );
578
- return this;
579
- },
895
+ ui.jqXHR.success(function() {
896
+ if ( that.options.cache ) {
897
+ $.data( ui.tab[ 0 ], "cache.tabs", true );
898
+ }
899
+ });
900
+ }});
901
+ },
902
+
903
+ _ajaxSettings: function( anchor, event, ui ) {
904
+ var ajaxOptions = this.options.ajaxOptions;
905
+ return $.extend( {}, ajaxOptions, {
906
+ error: function( xhr, status ) {
907
+ try {
908
+ // Passing index avoid a race condition when this method is
909
+ // called after the user has selected another tab.
910
+ // Pass the anchor that initiated this request allows
911
+ // loadError to manipulate the tab content panel via $(a.hash)
912
+ ajaxOptions.error(
913
+ xhr, status, ui.tab.closest( "li" ).index(), ui.tab[ 0 ] );
914
+ }
915
+ catch ( error ) {}
916
+ }
917
+ }, this._superApply( arguments ) );
918
+ },
580
919
 
581
- disable: function( index ) {
582
- index = this._getIndex( index );
583
- var self = this, o = this.options;
584
- // cannot disable already selected tab
585
- if ( index != o.selected ) {
586
- this.lis.eq( index ).addClass( "ui-state-disabled" );
920
+ _setOption: function( key, value ) {
921
+ // reset cache if switching from cached to not cached
922
+ if ( key === "cache" && value === false ) {
923
+ this.anchors.removeData( "cache.tabs" );
924
+ }
925
+ this._super( key, value );
926
+ },
587
927
 
588
- o.disabled.push( index );
589
- o.disabled.sort();
928
+ _destroy: function() {
929
+ this.anchors.removeData( "cache.tabs" );
930
+ this._super();
931
+ },
590
932
 
591
- this._trigger( "disable", null, this._ui( this.anchors[ index ], this.panels[ index ] ) );
933
+ url: function( index ){
934
+ this.anchors.eq( index ).removeData( "cache.tabs" );
935
+ this._superApply( arguments );
592
936
  }
937
+ });
593
938
 
594
- return this;
595
- },
596
-
597
- select: function( index ) {
598
- index = this._getIndex( index );
599
- if ( index == -1 ) {
600
- if ( this.options.collapsible && this.options.selected != -1 ) {
601
- index = this.options.selected;
602
- } else {
603
- return this;
939
+ // abort method
940
+ $.widget( "ui.tabs", $.ui.tabs, {
941
+ abort: function() {
942
+ if ( this.xhr ) {
943
+ this.xhr.abort();
604
944
  }
605
945
  }
606
- this.anchors.eq( index ).trigger( this.options.event + ".tabs" );
607
- return this;
608
- },
946
+ });
947
+
948
+ // spinner
949
+ $.widget( "ui.tabs", $.ui.tabs, {
950
+ options: {
951
+ spinner: "<em>Loading&#8230;</em>"
952
+ },
953
+ _create: function() {
954
+ this._super();
955
+ this._on({
956
+ tabsbeforeload: function( event, ui ) {
957
+ // Don't react to nested tabs or tabs that don't use a spinner
958
+ if ( event.target !== this.element[ 0 ] ||
959
+ !this.options.spinner ) {
960
+ return;
961
+ }
609
962
 
610
- load: function( index ) {
611
- index = this._getIndex( index );
612
- var self = this,
613
- o = this.options,
614
- a = this.anchors.eq( index )[ 0 ],
615
- url = $.data( a, "load.tabs" );
963
+ var span = ui.tab.find( "span" ),
964
+ html = span.html();
965
+ span.html( this.options.spinner );
966
+ ui.jqXHR.complete(function() {
967
+ span.html( html );
968
+ });
969
+ }
970
+ });
971
+ }
972
+ });
973
+
974
+ // enable/disable events
975
+ $.widget( "ui.tabs", $.ui.tabs, {
976
+ options: {
977
+ enable: null,
978
+ disable: null
979
+ },
980
+
981
+ enable: function( index ) {
982
+ var options = this.options,
983
+ trigger;
984
+
985
+ if ( index && options.disabled === true ||
986
+ ( $.isArray( options.disabled ) && $.inArray( index, options.disabled ) !== -1 ) ) {
987
+ trigger = true;
988
+ }
616
989
 
617
- this.abort();
990
+ this._superApply( arguments );
618
991
 
619
- // not remote or from cache
620
- if ( !url || this.element.queue( "tabs" ).length !== 0 && $.data( a, "cache.tabs" ) ) {
621
- this.element.dequeue( "tabs" );
622
- return;
623
- }
992
+ if ( trigger ) {
993
+ this._trigger( "enable", null, this._ui( this.anchors[ index ], this.panels[ index ] ) );
994
+ }
995
+ },
624
996
 
625
- // load remote from here on
626
- this.lis.eq( index ).addClass( "ui-state-processing" );
997
+ disable: function( index ) {
998
+ var options = this.options,
999
+ trigger;
627
1000
 
628
- if ( o.spinner ) {
629
- var span = $( "span", a );
630
- span.data( "label.tabs", span.html() ).html( o.spinner );
631
- }
1001
+ if ( index && options.disabled === false ||
1002
+ ( $.isArray( options.disabled ) && $.inArray( index, options.disabled ) === -1 ) ) {
1003
+ trigger = true;
1004
+ }
632
1005
 
633
- this.xhr = $.ajax( $.extend( {}, o.ajaxOptions, {
634
- url: url,
635
- success: function( r, s ) {
636
- self.element.find( self._sanitizeSelector( a.hash ) ).html( r );
1006
+ this._superApply( arguments );
637
1007
 
638
- // take care of tab labels
639
- self._cleanup();
1008
+ if ( trigger ) {
1009
+ this._trigger( "disable", null, this._ui( this.anchors[ index ], this.panels[ index ] ) );
1010
+ }
1011
+ }
1012
+ });
1013
+
1014
+ // add/remove methods and events
1015
+ $.widget( "ui.tabs", $.ui.tabs, {
1016
+ options: {
1017
+ add: null,
1018
+ remove: null,
1019
+ tabTemplate: "<li><a href='#{href}'><span>#{label}</span></a></li>"
1020
+ },
1021
+
1022
+ add: function( url, label, index ) {
1023
+ if ( index === undefined ) {
1024
+ index = this.anchors.length;
1025
+ }
640
1026
 
641
- if ( o.cache ) {
642
- $.data( a, "cache.tabs", true );
1027
+ var doInsertAfter, panel,
1028
+ options = this.options,
1029
+ li = $( options.tabTemplate
1030
+ .replace( /#\{href\}/g, url )
1031
+ .replace( /#\{label\}/g, label ) ),
1032
+ id = !url.indexOf( "#" ) ?
1033
+ url.replace( "#", "" ) :
1034
+ this._tabId( li );
1035
+
1036
+ li.addClass( "ui-state-default ui-corner-top" ).data( "ui-tabs-destroy", true );
1037
+ li.attr( "aria-controls", id );
1038
+
1039
+ doInsertAfter = index >= this.tabs.length;
1040
+
1041
+ // try to find an existing element before creating a new one
1042
+ panel = this.element.find( "#" + id );
1043
+ if ( !panel.length ) {
1044
+ panel = this._createPanel( id );
1045
+ if ( doInsertAfter ) {
1046
+ if ( index > 0 ) {
1047
+ panel.insertAfter( this.panels.eq( -1 ) );
1048
+ } else {
1049
+ panel.appendTo( this.element );
1050
+ }
1051
+ } else {
1052
+ panel.insertBefore( this.panels[ index ] );
643
1053
  }
1054
+ }
1055
+ panel.addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" ).hide();
644
1056
 
645
- self._trigger( "load", null, self._ui( self.anchors[ index ], self.panels[ index ] ) );
646
- try {
647
- o.ajaxOptions.success( r, s );
648
- }
649
- catch ( e ) {}
650
- },
651
- error: function( xhr, s, e ) {
652
- // take care of tab labels
653
- self._cleanup();
654
-
655
- self._trigger( "load", null, self._ui( self.anchors[ index ], self.panels[ index ] ) );
656
- try {
657
- // Passing index avoid a race condition when this method is
658
- // called after the user has selected another tab.
659
- // Pass the anchor that initiated this request allows
660
- // loadError to manipulate the tab content panel via $(a.hash)
661
- o.ajaxOptions.error( xhr, s, index, a );
662
- }
663
- catch ( e ) {}
1057
+ if ( doInsertAfter ) {
1058
+ li.appendTo( this.tablist );
1059
+ } else {
1060
+ li.insertBefore( this.tabs[ index ] );
664
1061
  }
665
- } ) );
666
1062
 
667
- // last, so that load event is fired before show...
668
- self.element.dequeue( "tabs" );
1063
+ options.disabled = $.map( options.disabled, function( n ) {
1064
+ return n >= index ? ++n : n;
1065
+ });
669
1066
 
670
- return this;
671
- },
1067
+ this.refresh();
1068
+ if ( this.tabs.length === 1 && options.active === false ) {
1069
+ this.option( "active", 0 );
1070
+ }
672
1071
 
673
- abort: function() {
674
- // stop possibly running animations
675
- this.element.queue( [] );
676
- this.panels.stop( false, true );
1072
+ this._trigger( "add", null, this._ui( this.anchors[ index ], this.panels[ index ] ) );
1073
+ return this;
1074
+ },
1075
+
1076
+ remove: function( index ) {
1077
+ index = this._getIndex( index );
1078
+ var options = this.options,
1079
+ tab = this.tabs.eq( index ).remove(),
1080
+ panel = this._getPanelForTab( tab ).remove();
1081
+
1082
+ // If selected tab was removed focus tab to the right or
1083
+ // in case the last tab was removed the tab to the left.
1084
+ // We check for more than 2 tabs, because if there are only 2,
1085
+ // then when we remove this tab, there will only be one tab left
1086
+ // so we don't need to detect which tab to activate.
1087
+ if ( tab.hasClass( "ui-tabs-active" ) && this.anchors.length > 2 ) {
1088
+ this._activate( index + ( index + 1 < this.anchors.length ? 1 : -1 ) );
1089
+ }
677
1090
 
678
- // "tabs" queue must not contain more than two elements,
679
- // which are the callbacks for the latest clicked tab...
680
- this.element.queue( "tabs", this.element.queue( "tabs" ).splice( -2, 2 ) );
1091
+ options.disabled = $.map(
1092
+ $.grep( options.disabled, function( n ) {
1093
+ return n !== index;
1094
+ }),
1095
+ function( n ) {
1096
+ return n >= index ? --n : n;
1097
+ });
681
1098
 
682
- // terminate pending requests from other tabs
683
- if ( this.xhr ) {
684
- this.xhr.abort();
685
- delete this.xhr;
1099
+ this.refresh();
1100
+
1101
+ this._trigger( "remove", null, this._ui( tab.find( "a" )[ 0 ], panel[ 0 ] ) );
1102
+ return this;
686
1103
  }
1104
+ });
687
1105
 
688
- // take care of tab labels
689
- this._cleanup();
690
- return this;
691
- },
1106
+ // length method
1107
+ $.widget( "ui.tabs", $.ui.tabs, {
1108
+ length: function() {
1109
+ return this.anchors.length;
1110
+ }
1111
+ });
1112
+
1113
+ // panel ids (idPrefix option + title attribute)
1114
+ $.widget( "ui.tabs", $.ui.tabs, {
1115
+ options: {
1116
+ idPrefix: "ui-tabs-"
1117
+ },
1118
+
1119
+ _tabId: function( tab ) {
1120
+ var a = tab.is( "li" ) ? tab.find( "a[href]" ) : tab;
1121
+ a = a[0];
1122
+ return $( a ).closest( "li" ).attr( "aria-controls" ) ||
1123
+ a.title && a.title.replace( /\s/g, "_" ).replace( /[^\w\u00c0-\uFFFF\-]/g, "" ) ||
1124
+ this.options.idPrefix + getNextTabId();
1125
+ }
1126
+ });
692
1127
 
693
- url: function( index, url ) {
694
- this.anchors.eq( index ).removeData( "cache.tabs" ).data( "load.tabs", url );
695
- return this;
696
- },
1128
+ // _createPanel method
1129
+ $.widget( "ui.tabs", $.ui.tabs, {
1130
+ options: {
1131
+ panelTemplate: "<div></div>"
1132
+ },
697
1133
 
698
- length: function() {
699
- return this.anchors.length;
700
- }
701
- });
1134
+ _createPanel: function( id ) {
1135
+ return $( this.options.panelTemplate )
1136
+ .attr( "id", id )
1137
+ .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )
1138
+ .data( "ui-tabs-destroy", true );
1139
+ }
1140
+ });
1141
+
1142
+ // selected option
1143
+ $.widget( "ui.tabs", $.ui.tabs, {
1144
+ _create: function() {
1145
+ var options = this.options;
1146
+ if ( options.active === null && options.selected !== undefined ) {
1147
+ options.active = options.selected === -1 ? false : options.selected;
1148
+ }
1149
+ this._super();
1150
+ options.selected = options.active;
1151
+ if ( options.selected === false ) {
1152
+ options.selected = -1;
1153
+ }
1154
+ },
702
1155
 
703
- $.extend( $.ui.tabs, {
704
- version: "1.8.24"
705
- });
1156
+ _setOption: function( key, value ) {
1157
+ if ( key !== "selected" ) {
1158
+ return this._super( key, value );
1159
+ }
706
1160
 
707
- /*
708
- * Tabs Extensions
709
- */
1161
+ var options = this.options;
1162
+ this._super( "active", value === -1 ? false : value );
1163
+ options.selected = options.active;
1164
+ if ( options.selected === false ) {
1165
+ options.selected = -1;
1166
+ }
1167
+ },
710
1168
 
711
- /*
712
- * Rotate
713
- */
714
- $.extend( $.ui.tabs.prototype, {
715
- rotation: null,
716
- rotate: function( ms, continuing ) {
717
- var self = this,
718
- o = this.options;
719
-
720
- var rotate = self._rotate || ( self._rotate = function( e ) {
721
- clearTimeout( self.rotation );
722
- self.rotation = setTimeout(function() {
723
- var t = o.selected;
724
- self.select( ++t < self.anchors.length ? t : 0 );
725
- }, ms );
726
-
727
- if ( e ) {
728
- e.stopPropagation();
1169
+ _eventHandler: function() {
1170
+ this._superApply( arguments );
1171
+ this.options.selected = this.options.active;
1172
+ if ( this.options.selected === false ) {
1173
+ this.options.selected = -1;
729
1174
  }
730
- });
1175
+ }
1176
+ });
1177
+
1178
+ // show and select event
1179
+ $.widget( "ui.tabs", $.ui.tabs, {
1180
+ options: {
1181
+ show: null,
1182
+ select: null
1183
+ },
1184
+ _create: function() {
1185
+ this._super();
1186
+ if ( this.options.active !== false ) {
1187
+ this._trigger( "show", null, this._ui(
1188
+ this.active.find( ".ui-tabs-anchor" )[ 0 ],
1189
+ this._getPanelForTab( this.active )[ 0 ] ) );
1190
+ }
1191
+ },
1192
+ _trigger: function( type, event, data ) {
1193
+ var tab, panel,
1194
+ ret = this._superApply( arguments );
731
1195
 
732
- var stop = self._unrotate || ( self._unrotate = !continuing
733
- ? function(e) {
734
- if (e.clientX) { // in case of a true click
735
- self.rotate(null);
1196
+ if ( !ret ) {
1197
+ return false;
1198
+ }
1199
+
1200
+ if ( type === "beforeActivate" ) {
1201
+ tab = data.newTab.length ? data.newTab : data.oldTab;
1202
+ panel = data.newPanel.length ? data.newPanel : data.oldPanel;
1203
+ ret = this._super( "select", event, {
1204
+ tab: tab.find( ".ui-tabs-anchor" )[ 0],
1205
+ panel: panel[ 0 ],
1206
+ index: tab.closest( "li" ).index()
1207
+ });
1208
+ } else if ( type === "activate" && data.newTab.length ) {
1209
+ ret = this._super( "show", event, {
1210
+ tab: data.newTab.find( ".ui-tabs-anchor" )[ 0 ],
1211
+ panel: data.newPanel[ 0 ],
1212
+ index: data.newTab.closest( "li" ).index()
1213
+ });
1214
+ }
1215
+ return ret;
1216
+ }
1217
+ });
1218
+
1219
+ // select method
1220
+ $.widget( "ui.tabs", $.ui.tabs, {
1221
+ select: function( index ) {
1222
+ index = this._getIndex( index );
1223
+ if ( index === -1 ) {
1224
+ if ( this.options.collapsible && this.options.selected !== -1 ) {
1225
+ index = this.options.selected;
1226
+ } else {
1227
+ return;
736
1228
  }
737
1229
  }
738
- : function( e ) {
739
- rotate();
740
- });
1230
+ this.anchors.eq( index ).trigger( this.options.event + this.eventNamespace );
1231
+ }
1232
+ });
1233
+
1234
+ // cookie option
1235
+ (function() {
1236
+
1237
+ var listId = 0;
1238
+
1239
+ $.widget( "ui.tabs", $.ui.tabs, {
1240
+ options: {
1241
+ cookie: null // e.g. { expires: 7, path: '/', domain: 'jquery.com', secure: true }
1242
+ },
1243
+ _create: function() {
1244
+ var options = this.options,
1245
+ active;
1246
+ if ( options.active == null && options.cookie ) {
1247
+ active = parseInt( this._cookie(), 10 );
1248
+ if ( active === -1 ) {
1249
+ active = false;
1250
+ }
1251
+ options.active = active;
1252
+ }
1253
+ this._super();
1254
+ },
1255
+ _cookie: function( active ) {
1256
+ var cookie = [ this.cookie ||
1257
+ ( this.cookie = this.options.cookie.name || "ui-tabs-" + (++listId) ) ];
1258
+ if ( arguments.length ) {
1259
+ cookie.push( active === false ? -1 : active );
1260
+ cookie.push( this.options.cookie );
1261
+ }
1262
+ return $.cookie.apply( null, cookie );
1263
+ },
1264
+ _refresh: function() {
1265
+ this._super();
1266
+ if ( this.options.cookie ) {
1267
+ this._cookie( this.options.active, this.options.cookie );
1268
+ }
1269
+ },
1270
+ _eventHandler: function() {
1271
+ this._superApply( arguments );
1272
+ if ( this.options.cookie ) {
1273
+ this._cookie( this.options.active, this.options.cookie );
1274
+ }
1275
+ },
1276
+ _destroy: function() {
1277
+ this._super();
1278
+ if ( this.options.cookie ) {
1279
+ this._cookie( null, this.options.cookie );
1280
+ }
1281
+ }
1282
+ });
741
1283
 
742
- // start rotation
743
- if ( ms ) {
744
- this.element.bind( "tabsshow", rotate );
745
- this.anchors.bind( o.event + ".tabs", stop );
746
- rotate();
747
- // stop rotation
748
- } else {
749
- clearTimeout( self.rotation );
750
- this.element.unbind( "tabsshow", rotate );
751
- this.anchors.unbind( o.event + ".tabs", stop );
752
- delete this._rotate;
753
- delete this._unrotate;
1284
+ })();
1285
+
1286
+ // load event
1287
+ $.widget( "ui.tabs", $.ui.tabs, {
1288
+ _trigger: function( type, event, data ) {
1289
+ var _data = $.extend( {}, data );
1290
+ if ( type === "load" ) {
1291
+ _data.panel = _data.panel[ 0 ];
1292
+ _data.tab = _data.tab.find( ".ui-tabs-anchor" )[ 0 ];
1293
+ }
1294
+ return this._super( type, event, _data );
754
1295
  }
1296
+ });
1297
+
1298
+ // fx option
1299
+ // The new animation options (show, hide) conflict with the old show callback.
1300
+ // The old fx option wins over show/hide anyway (always favor back-compat).
1301
+ // If a user wants to use the new animation API, they must give up the old API.
1302
+ $.widget( "ui.tabs", $.ui.tabs, {
1303
+ options: {
1304
+ fx: null // e.g. { height: "toggle", opacity: "toggle", duration: 200 }
1305
+ },
1306
+
1307
+ _getFx: function() {
1308
+ var hide, show,
1309
+ fx = this.options.fx;
1310
+
1311
+ if ( fx ) {
1312
+ if ( $.isArray( fx ) ) {
1313
+ hide = fx[ 0 ];
1314
+ show = fx[ 1 ];
1315
+ } else {
1316
+ hide = show = fx;
1317
+ }
1318
+ }
755
1319
 
756
- return this;
757
- }
758
- });
1320
+ return fx ? { show: show, hide: hide } : null;
1321
+ },
1322
+
1323
+ _toggle: function( event, eventData ) {
1324
+ var that = this,
1325
+ toShow = eventData.newPanel,
1326
+ toHide = eventData.oldPanel,
1327
+ fx = this._getFx();
1328
+
1329
+ if ( !fx ) {
1330
+ return this._super( event, eventData );
1331
+ }
1332
+
1333
+ that.running = true;
1334
+
1335
+ function complete() {
1336
+ that.running = false;
1337
+ that._trigger( "activate", event, eventData );
1338
+ }
1339
+
1340
+ function show() {
1341
+ eventData.newTab.closest( "li" ).addClass( "ui-tabs-active ui-state-active" );
1342
+
1343
+ if ( toShow.length && fx.show ) {
1344
+ toShow
1345
+ .animate( fx.show, fx.show.duration, function() {
1346
+ complete();
1347
+ });
1348
+ } else {
1349
+ toShow.show();
1350
+ complete();
1351
+ }
1352
+ }
1353
+
1354
+ // start out by hiding, then showing, then completing
1355
+ if ( toHide.length && fx.hide ) {
1356
+ toHide.animate( fx.hide, fx.hide.duration, function() {
1357
+ eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );
1358
+ show();
1359
+ });
1360
+ } else {
1361
+ eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );
1362
+ toHide.hide();
1363
+ show();
1364
+ }
1365
+ }
1366
+ });
1367
+ }
759
1368
 
760
1369
  })( jQuery );