You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
249 lines
8.9 KiB
249 lines
8.9 KiB
;(function ($, window, document, undefined) {
|
|
'use strict';
|
|
|
|
Foundation.libs.tab = {
|
|
name : 'tab',
|
|
|
|
version : '5.5.2',
|
|
|
|
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);
|
|
});
|
|
|
|
// store the initial href, which is used to allow correct behaviour of the
|
|
// browser back button when deep linking is turned on.
|
|
self.entry_location = window.location.href;
|
|
|
|
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) {
|
|
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 el = this;
|
|
var keyCode = e.keyCode || e.which;
|
|
// if user pressed tab key
|
|
if (keyCode == 9) {
|
|
e.preventDefault();
|
|
// TODO: Change usual_tab_behavior into accessibility function?
|
|
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 is_entry_location = window.location.href === self.entry_location,
|
|
default_hash = settings.scroll_to_content ? self.default_tab_hashes[0] : is_entry_location ? window.location.hash :'fndtn-' + self.default_tab_hashes[0].replace('#', '')
|
|
|
|
if (!(is_entry_location && hash === default_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));
|
|
|