Add deprecation notice
[web.git] / js / foundation / foundation.tab.js
1 ;
2 (function ($, window, document, undefined) {
3 'use strict';
4
5 Foundation.libs.tab = {
6 name: 'tab',
7
8 version: '5.5.0',
9
10 settings: {
11 active_class: 'active',
12 callback: function () {
13 },
14 deep_linking: false,
15 scroll_to_content: true,
16 is_hover: false
17 },
18
19 default_tab_hashes: [],
20
21 init: function (scope, method, options) {
22 var self = this,
23 S = this.S;
24
25 this.bindings(method, options);
26 this.handle_location_hash_change();
27
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);
33 });
34 },
35
36 events: function () {
37 var self = this,
38 S = this.S;
39
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) {
43 e.preventDefault();
44 e.stopPropagation();
45 self.toggle_active_tab(S(this).parent());
46 }
47 };
48
49 S(this.scope)
50 .off('.tab')
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());
58 });
59
60 // Location hash change event
61 S(window).on('hashchange.fndtn.tab', function (e) {
62 e.preventDefault();
63 self.handle_location_hash_change();
64 });
65 },
66
67 handle_location_hash_change: function () {
68
69 var self = this,
70 S = this.S;
71
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
76 var hash;
77 if (settings.scroll_to_content) {
78 hash = self.scope.location.hash;
79 } else {
80 // prefix the hash to prevent anchor scrolling
81 hash = self.scope.location.hash.replace('fndtn-', '');
82 }
83 if (hash != '') {
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')) {
88 // Tab content div
89 self.toggle_active_tab($('[' + self.attr_name() + '] > * > a[href=' + hash + ']').parent());
90 } else {
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);
96 }
97 }
98 } else {
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());
102 }
103 }
104 }
105 });
106 },
107
108 toggle_active_tab: function (tab, location_hash) {
109 var S = this.S,
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
119
120 // define current, previous and next (possible) tabs
121
122 var $original = $(this);
123 var $prev = $(this).parents('li').prev().children('[role="tab"]');
124 var $next = $(this).parents('li').next().children('[role="tab"]');
125 var $target;
126
127 // find the direction (prev or next)
128
129 switch (e.keyCode) {
130 case 37:
131 $target = $prev;
132 break;
133 case 39:
134 $target = $next;
135 break;
136 default:
137 $target = false
138 break;
139 }
140
141 if ($target.length) {
142 $original.attr({
143 'tabindex': '-1',
144 'aria-selected': null
145 });
146 $target.attr({
147 'tabindex': '0',
148 'aria-selected': true
149 }).focus();
150 }
151
152 // Hide panels
153
154 $('[role="tabpanel"]')
155 .attr('aria-hidden', 'true');
156
157 // Show panel which corresponds to target
158
159 $('#' + $(document.activeElement).attr('href').substring(1))
160 .attr('aria-hidden', null);
161
162 };
163
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);
168 }
169
170 if (settings.deep_linking) {
171
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();
177 } else {
178 S(target_hash)[0].scrollIntoView();
179 }
180 } else {
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('#', '');
184 } else {
185 window.location.hash = 'fndtn-' + target_hash.replace('#', '');
186 }
187 }
188 }
189
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]);
203
204 tab_link.off('keydown').on('keydown', interpret_keyup_action);
205 },
206
207 data_attr: function (str) {
208 if (this.namespace.length > 0) {
209 return this.namespace + '-' + str;
210 }
211
212 return str;
213 },
214
215 off: function () {
216 },
217
218 reflow: function () {
219 }
220 };
221 }(jQuery, window, window.document));