2 (function ($, window
, document
, undefined) {
5 Foundation
.libs
.tab
= {
11 active_class
: 'active',
12 callback: function () {
15 scroll_to_content
: true,
19 default_tab_hashes
: [],
21 init: function (scope
, method
, options
) {
25 this.bindings(method
, options
);
26 this.handle_location_hash_change();
28 // Store the default active tabs which will be referenced when the
29 // location hash is absent, as in the case of navigating the tabs and
30 // returning to the first viewing via the browser Back button.
31 S('[' + this.attr_name() + '] > .active > a', this.scope
).each(function () {
32 self
.default_tab_hashes
.push(this.hash
);
40 var usual_tab_behavior = function (e
) {
41 var settings
= S(this).closest('[' + self
.attr_name() + ']').data(self
.attr_name(true) + '-init');
42 if (!settings
.is_hover
|| Modernizr
.touch
) {
45 self
.toggle_active_tab(S(this).parent());
51 // Click event: tab title
52 .on('focus.fndtn.tab', '[' + this.attr_name() + '] > * > a', usual_tab_behavior
)
53 .on('click.fndtn.tab', '[' + this.attr_name() + '] > * > a', usual_tab_behavior
)
54 // Hover event: tab title
55 .on('mouseenter.fndtn.tab', '[' + this.attr_name() + '] > * > a', function (e
) {
56 var settings
= S(this).closest('[' + self
.attr_name() + ']').data(self
.attr_name(true) + '-init');
57 if (settings
.is_hover
) self
.toggle_active_tab(S(this).parent());
60 // Location hash change event
61 S(window
).on('hashchange.fndtn.tab', function (e
) {
63 self
.handle_location_hash_change();
67 handle_location_hash_change: function () {
72 S('[' + this.attr_name() + ']', this.scope
).each(function () {
73 var settings
= S(this).data(self
.attr_name(true) + '-init');
74 if (settings
.deep_linking
) {
75 // Match the location hash to a label
77 if (settings
.scroll_to_content
) {
78 hash
= self
.scope
.location
.hash
;
80 // prefix the hash to prevent anchor scrolling
81 hash
= self
.scope
.location
.hash
.replace('fndtn-', '');
84 // Check whether the location hash references a tab content div or
85 // another element on the page (inside or outside the tab content div)
86 var hash_element
= S(hash
);
87 if (hash_element
.hasClass('content') && hash_element
.parent().hasClass('tabs-content')) {
89 self
.toggle_active_tab($('[' + self
.attr_name() + '] > * > a[href=' + hash
+ ']').parent());
91 // Not the tab content div. If inside the tab content, find the
92 // containing tab and toggle it as active.
93 var hash_tab_container_id
= hash_element
.closest('.content').attr('id');
94 if (hash_tab_container_id
!= undefined) {
95 self
.toggle_active_tab($('[' + self
.attr_name() + '] > * > a[href=#' + hash_tab_container_id
+ ']').parent(), hash
);
99 // Reference the default tab hashes which were initialized in the init function
100 for (var ind
= 0; ind
< self
.default_tab_hashes
.length
; ind
++) {
101 self
.toggle_active_tab($('[' + self
.attr_name() + '] > * > a[href=' + self
.default_tab_hashes
[ind
] + ']').parent());
108 toggle_active_tab: function (tab
, location_hash
) {
110 tabs
= tab
.closest('[' + this.attr_name() + ']'),
111 tab_link
= tab
.find('a'),
112 anchor
= tab
.children('a').first(),
113 target_hash
= '#' + anchor
.attr('href').split('#')[1],
114 target
= S(target_hash
),
115 siblings
= tab
.siblings(),
116 settings
= tabs
.data(this.attr_name(true) + '-init'),
117 interpret_keyup_action = function (e
) {
118 // Light modification of Heydon Pickering's Practical ARIA Examples: http://heydonworks.com/practical_aria_examples/js/a11y.js
120 // define current, previous and next (possible) tabs
122 var $original
= $(this);
123 var $prev
= $(this).parents('li').prev().children('[role="tab"]');
124 var $next
= $(this).parents('li').next().children('[role="tab"]');
127 // find the direction (prev or next)
141 if ($target
.length
) {
144 'aria-selected': null
148 'aria-selected': true
154 $('[role="tabpanel"]')
155 .attr('aria-hidden', 'true');
157 // Show panel which corresponds to target
159 $('#' + $(document
.activeElement
).attr('href').substring(1))
160 .attr('aria-hidden', null);
164 // allow usage of data-tab-content attribute instead of href
165 if (S(this).data(this.data_attr('tab-content'))) {
166 target_hash
= '#' + S(this).data(this.data_attr('tab-content')).split('#')[1];
167 target
= S(target_hash
);
170 if (settings
.deep_linking
) {
172 if (settings
.scroll_to_content
) {
173 // retain current hash to scroll to content
174 window
.location
.hash
= location_hash
|| target_hash
;
175 if (location_hash
== undefined || location_hash
== target_hash
) {
176 tab
.parent()[0].scrollIntoView();
178 S(target_hash
)[0].scrollIntoView();
181 // prefix the hashes so that the browser doesn't scroll down
182 if (location_hash
!= undefined) {
183 window
.location
.hash
= 'fndtn-' + location_hash
.replace('#', '');
185 window
.location
.hash
= 'fndtn-' + target_hash
.replace('#', '');
190 // WARNING: The activation and deactivation of the tab content must
191 // occur after the deep linking in order to properly refresh the browser
192 // window (notably in Chrome).
193 // Clean up multiple attr instances to done once
194 tab
.addClass(settings
.active_class
).triggerHandler('opened');
195 tab_link
.attr({'aria-selected': 'true', tabindex
: 0});
196 siblings
.removeClass(settings
.active_class
)
197 siblings
.find('a').attr({'aria-selected': 'false', tabindex
: -1});
198 target
.siblings().removeClass(settings
.active_class
).attr({'aria-hidden': 'true', tabindex
: -1});
199 target
.addClass(settings
.active_class
).attr('aria-hidden', 'false').removeAttr('tabindex');
200 settings
.callback(tab
);
201 target
.triggerHandler('toggled', [tab
]);
202 tabs
.triggerHandler('toggled', [target
]);
204 tab_link
.off('keydown').on('keydown', interpret_keyup_action
);
207 data_attr: function (str
) {
208 if (this.namespace.length
> 0) {
209 return this.namespace + '-' + str
;
218 reflow: function () {
221 }(jQuery
, window
, window
.document
));