;(function ($, window, document, undefined) { 'use strict'; Foundation.libs.tab = { name : 'tab', version : '{{VERSION}}', settings : { active_class : 'active', callback : function () {}, deep_linking : false, scroll_to_content : true, is_hover : false }, default_tab_hashes : [], init : function (scope, method, options) { var self = this, S = this.S; // Store the default active tabs which will be referenced when the // location hash is absent, as in the case of navigating the tabs and // returning to the first viewing via the browser Back button. S('[' + this.attr_name() + '] > .active > a', this.scope).each(function () { self.default_tab_hashes.push(this.hash); }); this.bindings(method, options); this.handle_location_hash_change(); }, events : function () { var self = this, S = this.S; var usual_tab_behavior = function (e, target) { var settings = S(target).closest('[' + self.attr_name() + ']').data(self.attr_name(true) + '-init'); if (!settings.is_hover || Modernizr.touch) { // if user did not pressed tab key, prevent default action var keyCode = e.keyCode || e.which; if (keyCode !== 9) { e.preventDefault(); e.stopPropagation(); } self.toggle_active_tab(S(target).parent()); } }; S(this.scope) .off('.tab') // Key event: focus/tab key .on('keydown.fndtn.tab', '[' + this.attr_name() + '] > * > a', function(e) { var keyCode = e.keyCode || e.which; // if user pressed tab key if (keyCode === 13 || keyCode === 32) { // enter or space var el = this; usual_tab_behavior(e, el); } }) // Click event: tab title .on('click.fndtn.tab', '[' + this.attr_name() + '] > * > a', function(e) { var el = this; usual_tab_behavior(e, el); }) // Hover event: tab title .on('mouseenter.fndtn.tab', '[' + this.attr_name() + '] > * > a', function (e) { var settings = S(this).closest('[' + self.attr_name() + ']').data(self.attr_name(true) + '-init'); if (settings.is_hover) { self.toggle_active_tab(S(this).parent()); } }); // Location hash change event S(window).on('hashchange.fndtn.tab', function (e) { e.preventDefault(); self.handle_location_hash_change(); }); }, handle_location_hash_change : function () { var self = this, S = this.S; S('[' + this.attr_name() + ']', this.scope).each(function () { var settings = S(this).data(self.attr_name(true) + '-init'); if (settings.deep_linking) { // Match the location hash to a label var hash; if (settings.scroll_to_content) { hash = self.scope.location.hash; } else { // prefix the hash to prevent anchor scrolling hash = self.scope.location.hash.replace('fndtn-', ''); } if (hash != '') { // Check whether the location hash references a tab content div or // another element on the page (inside or outside the tab content div) var hash_element = S(hash); if (hash_element.hasClass('content') && hash_element.parent().hasClass('tabs-content')) { // Tab content div self.toggle_active_tab($('[' + self.attr_name() + '] > * > a[href=\\' + hash + ']').parent()); } else { // Not the tab content div. If inside the tab content, find the // containing tab and toggle it as active. var hash_tab_container_id = hash_element.closest('.content').attr('id'); if (hash_tab_container_id != undefined) { self.toggle_active_tab($('[' + self.attr_name() + '] > * > a[href=\\#' + hash_tab_container_id + ']').parent(), hash); } } } else { // Reference the default tab hashes which were initialized in the init function for (var ind = 0; ind < self.default_tab_hashes.length; ind++) { self.toggle_active_tab($('[' + self.attr_name() + '] > * > a[href=\\' + self.default_tab_hashes[ind] + ']').parent()); } } } }); }, toggle_active_tab : function (tab, location_hash) { var self = this, S = self.S, tabs = tab.closest('[' + this.attr_name() + ']'), tab_link = tab.find('a'), anchor = tab.children('a').first(), target_hash = '#' + anchor.attr('href').split('#')[1], target = S(target_hash), siblings = tab.siblings(), settings = tabs.data(this.attr_name(true) + '-init'), interpret_keyup_action = function (e) { // Light modification of Heydon Pickering's Practical ARIA Examples: http://heydonworks.com/practical_aria_examples/js/a11y.js // define current, previous and next (possible) tabs var $original = $(this); var $prev = $(this).parents('li').prev().children('[role="tab"]'); var $next = $(this).parents('li').next().children('[role="tab"]'); var $target; // find the direction (prev or next) switch (e.keyCode) { case 37: $target = $prev; break; case 39: $target = $next; break; default: $target = false break; } if ($target.length) { $original.attr({ 'tabindex' : '-1', 'aria-selected' : null }); $target.attr({ 'tabindex' : '0', 'aria-selected' : true }).focus(); } // Hide panels $('[role="tabpanel"]') .attr('aria-hidden', 'true'); // Show panel which corresponds to target $('#' + $(document.activeElement).attr('href').substring(1)) .attr('aria-hidden', null); }, go_to_hash = function(hash) { // This function allows correct behaviour of the browser's back button when deep linking is enabled. Without it // the user would get continually redirected to the default hash. var default_hash = settings.scroll_to_content ? self.default_tab_hashes[0] : 'fndtn-' + self.default_tab_hashes[0].replace('#', ''); if (hash !== default_hash || window.location.hash) { window.location.hash = hash; } }; // allow usage of data-tab-content attribute instead of href if (anchor.data('tab-content')) { target_hash = '#' + anchor.data('tab-content').split('#')[1]; target = S(target_hash); } if (settings.deep_linking) { if (settings.scroll_to_content) { // retain current hash to scroll to content go_to_hash(location_hash || target_hash); if (location_hash == undefined || location_hash == target_hash) { tab.parent()[0].scrollIntoView(); } else { S(target_hash)[0].scrollIntoView(); } } else { // prefix the hashes so that the browser doesn't scroll down if (location_hash != undefined) { go_to_hash('fndtn-' + location_hash.replace('#', '')); } else { go_to_hash('fndtn-' + target_hash.replace('#', '')); } } } // WARNING: The activation and deactivation of the tab content must // occur after the deep linking in order to properly refresh the browser // window (notably in Chrome). // Clean up multiple attr instances to done once tab.addClass(settings.active_class).triggerHandler('opened'); tab_link.attr({'aria-selected' : 'true', tabindex : 0}); siblings.removeClass(settings.active_class) siblings.find('a').attr({'aria-selected' : 'false'/*, tabindex : -1*/}); target.siblings().removeClass(settings.active_class).attr({'aria-hidden' : 'true'/*, tabindex : -1*/}); target.addClass(settings.active_class).attr('aria-hidden', 'false').removeAttr('tabindex'); settings.callback(tab); target.triggerHandler('toggled', [target]); tabs.triggerHandler('toggled', [tab]); tab_link.off('keydown').on('keydown', interpret_keyup_action ); }, data_attr : function (str) { if (this.namespace.length > 0) { return this.namespace + '-' + str; } return str; }, off : function () {}, reflow : function () {} }; }(jQuery, window, window.document));