218 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			218 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| ;(function ($, window, document, undefined) {
 | |
|   'use strict';
 | |
| 
 | |
|   Foundation.libs.tab = {
 | |
|     name : 'tab',
 | |
| 
 | |
|     version : '5.4.7',
 | |
| 
 | |
|     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;
 | |
| 
 | |
|       this.bindings(method, options);
 | |
|       this.handle_location_hash_change();
 | |
| 
 | |
|       // 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);
 | |
|       });
 | |
|     },
 | |
| 
 | |
|     events : function () {
 | |
|       var self = this,
 | |
|           S = this.S;
 | |
| 
 | |
|       var usual_tab_behavior =  function (e) {
 | |
|           var settings = S(this).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(this).parent());
 | |
|           }
 | |
|         };
 | |
| 
 | |
|       S(this.scope)
 | |
|         .off('.tab')
 | |
|         // Click event: tab title
 | |
|         .on('focus.fndtn.tab', '[' + this.attr_name() + '] > * > a', usual_tab_behavior )
 | |
|         .on('click.fndtn.tab', '[' + this.attr_name() + '] > * > a', usual_tab_behavior )
 | |
|         // 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 S = this.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);
 | |
| 
 | |
|           };
 | |
| 
 | |
|       // allow usage of data-tab-content attribute instead of href
 | |
|       if (S(this).data(this.data_attr('tab-content'))) {
 | |
|         target_hash = '#' + S(this).data(this.data_attr('tab-content')).split('#')[1];
 | |
|         target = S(target_hash);
 | |
|       }
 | |
| 
 | |
|       if (settings.deep_linking) {
 | |
| 
 | |
|         if (settings.scroll_to_content) {
 | |
|           // retain current hash to scroll to content
 | |
|           window.location.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) {
 | |
|             window.location.hash = 'fndtn-' + location_hash.replace('#', '');
 | |
|           } else {
 | |
|             window.location.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', [tab]);
 | |
|       tabs.triggerHandler('toggled', [target]);
 | |
| 
 | |
|       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));
 |