1/** 2 * Accordion module. 3 * @module foundation.accordion 4 * @requires foundation.util.keyboard 5 * @requires foundation.util.motion 6 */ 7!function($, Foundation) { 8 'use strict'; 9 10 /** 11 * Creates a new instance of an accordion. 12 * @class 13 * @fires Accordion#init 14 * @param {jQuery} element - jQuery object to make into an accordion. 15 */ 16 function Accordion(element, options){ 17 this.$element = element; 18 this.options = $.extend({}, Accordion.defaults, this.$element.data(), options); 19 20 this._init(); 21 22 Foundation.registerPlugin(this, 'Accordion'); 23 Foundation.Keyboard.register('Accordion', { 24 'ENTER': 'toggle', 25 'SPACE': 'toggle', 26 'ARROW_DOWN': 'next', 27 'ARROW_UP': 'previous' 28 }); 29 } 30 31 Accordion.defaults = { 32 /** 33 * Amount of time to animate the opening of an accordion pane. 34 * @option 35 * @example 250 36 */ 37 slideSpeed: 250, 38 /** 39 * Allow the accordion to have multiple open panes. 40 * @option 41 * @example false 42 */ 43 multiExpand: false, 44 /** 45 * Allow the accordion to close all panes. 46 * @option 47 * @example false 48 */ 49 allowAllClosed: false 50 }; 51 52 /** 53 * Initializes the accordion by animating the preset active pane(s). 54 * @private 55 */ 56 Accordion.prototype._init = function() { 57 if (!this.$element.hasClass('accordion')) { 58 this.$element.attr('role', 'tablist'); 59 } 60 this.$tabs = this.$element.children('li'); 61 if (this.$tabs.length == 0) { 62 this.$tabs = this.$element.children('[data-accordion-item]'); 63 } 64 if (this.$element.hasClass('accordion')) { 65 this.$tabs.each(function(idx, el){ 66 67 var $el = $(el), 68 $content = $el.find('[data-tab-content]'), 69 id = $content[0].id || Foundation.GetYoDigits(6, 'accordion'), 70 linkId = el.id || id + '-label'; 71 72 $el.find('a:first').attr({ 73 'aria-controls': id, 74 'role': 'button', 75 'id': linkId, 76 'aria-expanded': false 77 }); 78 if ($el.hasClass('is-active')) { 79 $el.find('a:first').attr({ 80 'aria-controls': id, 81 'role': 'button', 82 'id': linkId, 83 'aria-expanded': true 84 }); 85 } 86 $content.attr({'role': 'region', 'aria-labelledby': linkId, 'aria-hidden': true, 'id': id}); 87 }); 88 } else { 89 this.$tabs.each(function(idx, el){ 90 var $el = $(el), 91 $content = $el.find('[data-tab-content]'), 92 id = $content[0].id || Foundation.GetYoDigits(6, 'accordion'), 93 linkId = el.id || id + '-label'; 94 95 $el.find('a:first').attr({ 96 'aria-controls': id, 97 'role': 'tab', 98 'id': linkId, 99 'aria-expanded': false, 100 'aria-selected': false 101 }); 102 $content.attr({'role': 'tabpanel', 'aria-labelledby': linkId, 'aria-hidden': true, 'id': id}); 103 }); 104 } 105 var $initActive = this.$element.find('.is-active').children('[data-tab-content]'); 106 if($initActive.length){ 107 this.down($initActive, true); 108 } 109 this._events(); 110 }; 111 112 /** 113 * Adds event handlers for items within the accordion. 114 * @private 115 */ 116 Accordion.prototype._events = function() { 117 var _this = this; 118 119 this.$tabs.each(function(){ 120 var $elem = $(this); 121 var $tabContent = $elem.children('[data-tab-content]'); 122 if ($tabContent.length) { 123 $elem.children('a').off('click.zf.accordion keydown.zf.accordion') 124 .on('click.zf.accordion', function(e){ 125 // $(this).children('a').on('click.zf.accordion', function(e) { 126 e.preventDefault(); 127 if ($elem.hasClass('is-active')) { 128 if(_this.options.allowAllClosed || $elem.siblings().hasClass('is-active')){ 129 _this.up($tabContent); 130 } 131 } 132 else { 133 _this.down($tabContent); 134 } 135 }).on('keydown.zf.accordion', function(e){ 136 Foundation.Keyboard.handleKey(e, 'Accordion', { 137 toggle: function() { 138 _this.toggle($tabContent); 139 }, 140 next: function() { 141 $elem.next().find('a').focus().trigger('click.zf.accordion'); 142 }, 143 previous: function() { 144 $elem.prev().find('a').focus().trigger('click.zf.accordion'); 145 }, 146 handled: function() { 147 e.preventDefault(); 148 e.stopPropagation(); 149 } 150 }); 151 }); 152 } 153 });
154 }; 155 /** 156 * Toggles the selected content pane's open/close state. 157 * @param {jQuery} $target - jQuery object of the pane to toggle. 158 * @function 159 */ 160 Accordion.prototype.toggle = function($target){ 161 if($target.parent().hasClass('is-active')){ 162 if(this.options.allowAllClosed || $target.parent().siblings().hasClass('is-active')){ 163 this.up($target); 164 }else{ return; } 165 }else{ 166 this.down($target); 167 } 168 }; 169 /** 170 * Opens the accordion tab defined by `$target`. 171 * @param {jQuery} $target - Accordion pane to open. 172 * @param {Boolean} firstTime - flag to determine if reflow should happen. 173 * @fires Accordion#down 174 * @function 175 */ 176 Accordion.prototype.down = function($target, firstTime) { 177 var _this = this; 178 if(!this.options.multiExpand && !firstTime){ 179 var $currentActive = this.$element.find('.is-active').children('[data-tab-content]'); 180 if($currentActive.length){ 181 this.up($currentActive); 182 } 183 } 184 185 $target 186 .attr('aria-hidden', false) 187 .parent('[data-tab-content]') 188 .addBack() 189 .parent().addClass('is-active'); 190 191 // Foundation.Move(_this.options.slideSpeed, $target, function(){ 192 $target.slideDown(_this.options.slideSpeed); 193 // }); 194 195 // if(!firstTime){ 196 // Foundation._reflow(this.$element.attr('data-accordion')); 197 // } 198 $('#' + $target.attr('aria-labelledby')).attr({ 199 'aria-expanded': true, 200 'aria-selected': true 201 }); 202 /** 203 * Fires when the tab is done opening. 204 * @event Accordion#down 205 */ 206 this.$element.trigger('down.zf.accordion', [$target]); 207 }; 208 209 /** 210 * Closes the tab defined by `$target`. 211 * @param {jQuery} $target - Accordion tab to close. 212 * @fires Accordion#up 213 * @function 214 */ 215 Accordion.prototype.up = function($target) { 216 var $aunts = $target.parent().siblings(), 217 _this = this; 218 var canClose = this.options.multiExpand ? $aunts.hasClass('is-active') : $target.parent().hasClass('is-active'); 219 220 if(!this.options.allowAllClosed && !canClose){ 221 return; 222 } 223 224 // Foundation.Move(this.options.slideSpeed, $target, function(){ 225 $target.slideUp(_this.options.slideSpeed); 226 // }); 227 228 $target.attr('aria-hidden', true) 229 .parent().removeClass('is-active'); 230 231 $('#' + $target.attr('aria-labelledby')).attr({ 232 'aria-expanded': false, 233 'aria-selected': false 234 }); 235 236 /** 237 * Fires when the tab is done collapsing up. 238 * @event Accordion#up 239 */ 240 this.$element.trigger('up.zf.accordion', [$target]); 241 }; 242 243 /** 244 * Destroys an instance of an accordion. 245 * @fires Accordion#destroyed 246 * @function 247 */ 248 Accordion.prototype.destroy = function() { 249 this.$element.find('[data-tab-content]').slideUp(0).css('display', ''); 250 this.$element.find('a').off('.zf.accordion'); 251 252 Foundation.unregisterPlugin(this); 253 }; 254 255 Foundation.plugin(Accordion, 'Accordion'); 256}(jQuery, window.Foundation);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.