1/*! 2 * jQuery Accordion 0.0.1 3 * (c) 2014 Victor Fernandez <[email protected]> 4 * MIT Licensed. 5 */ 6 7; 8(function ($, window, document, undefined) { 9 10 var pluginName = 'accordion', 11 defaults = { 12 transitionSpeed: 300, 13 transitionEasing: 'ease', 14 controlElement: '[data-control]', 15 contentElement: '[data-content]', 16 groupElement: '[data-accordion-group]', 17 singleOpen: true 18 }; 19 20 function Accordion(element, options) { 21 this.element = element; 22 this.options = $.extend({}, defaults, options); 23 this._defaults = defaults; 24 this._name = pluginName; 25 this.init(); 26 } 27 28 Accordion.prototype.init = function () { 29 var self = this, 30 opts = self.options; 31 32 var $accordion = $(self.element), 33 $controls = $accordion.find('> ' + opts.controlElement), 34 $content = $accordion.find('> ' + opts.contentElement); 35 36 var accordionParentsQty = $accordion.parents('[data-accordion]').length, 37 accordionHasParent = accordionParentsQty > 0; 38 39 var closedCSS = {'max-height': 0, 'overflow': 'hidden'}; 40 41 var CSStransitions = supportsTransitions(); 42 43 function debounce(func, threshold, execAsap) { 44 var timeout; 45 46 return function debounced() { 47 var obj = this, 48 args = arguments; 49 50 function delayed() { 51 if (!execAsap) 52 func.apply(obj, args); 53 timeout = null; 54 } 55 56 if (timeout) 57 clearTimeout(timeout); 58 else if (execAsap) 59 func.apply(obj, args); 60 61 timeout = setTimeout(delayed, threshold || 100); 62 }; 63 } 64 65 function supportsTransitions() { 66 var b = document.body || document.documentElement, 67 s = b.style, 68 p = 'transition'; 69 70 if (typeof s[p] == 'string') { 71 return true; 72 } 73 74 var v = ['Moz', 'webkit', 'Webkit', 'Khtml', 'O', 'ms']; 75 76 p = 'Transition'; 77 78 for (var i = 0; i < v.length; i++) { 79 if (typeof s[v[i] + p] == 'string') { 80 return true; 81 } 82 } 83 84 return false; 85 } 86 87 function requestAnimFrame(cb) { 88 if (window.requestAnimationFrame) { 89 requestAnimationFrame(cb); 90 } else if (window.webkitRequestAnimationFrame) { 91 webkitRequestAnimationFrame(cb); 92 } else if (window.mozRequestAnimationFrame) { 93 mozRequestAnimationFrame(cb); 94 } else { 95 setTimeout(cb, 1000 / 60); 96 } 97 } 98 99 function toggleTransition($el, remove) { 100 if (!remove) { 101 $content.css({ 102 '-webkit-transition': 'max-height ' + opts.transitionSpeed + 'ms ' + opts.transitionEasing, 103 'transition': 'max-height ' + opts.transitionSpeed + 'ms ' + opts.transitionEasing 104 }); 105 } else { 106 $content.css({ 107 '-webkit-transition': '', 108 'transition': '' 109 }); 110 } 111 } 112 113 function calculateHeight($el) { 114 var height = 0; 115 116 $el.children().each(function () { 117 height = height + $(this).outerHeight(true); 118 }); 119 120 $el.data('oHeight', height); 121 } 122 123 function updateParentHeight($parentAccordion, $currentAccordion, qty, operation) { 124 var $content = $parentAccordion.filter('.open').find('> [data-content]'), 125 $childs = $content.find('[data-accordion].open > [data-content]'), 126 $matched; 127 128 if (!opts.singleOpen) { 129 $childs = $childs.not($currentAccordion.siblings('[data-accordion].open').find('> [data-content]')); 130 } 131 132 $matched = $content.add($childs); 133 134 if ($parentAccordion.hasClass('open')) { 135 $matched.each(function () { 136 var currentHeight = $(this).data('oHeight'); 137 138 switch (operation) { 139 case '+': 140 $(this).data('oHeight', currentHeight + qty); 141 break; 142 case '-': 143 $(this).data('oHeight', currentHeight - qty); 144 break; 145 default: 146 throw 'updateParentHeight method needs an operation'; 147 } 148 149 $(this).css('max-height', $(this).data('oHeight')); 150 }); 151 } 152 } 153 154 function refreshHeight($accordion) { 155 if ($accordion.hasClass('open')) { 156 var $content = $accordion.find('> [data-content]'), 157 $childs = $content.find('[data-accordion].open > [data-content]'), 158 $matched = $content.add($childs); 159 160 calculateHeight($matched); 161 162 $matched.css('max-height', $matched.data('oHeight')); 163 } 164 } 165 166 function closeAccordion($accordion, $content) { 167 $accordion.trigger('accordion.close'); 168 169 if (CSStransitions) {
170 if (accordionHasParent) { 171 var $parentAccordions = $accordion.parents('[data-accordion]'); 172 173 updateParentHeight($parentAccordions, $accordion, $content.data('oHeight'), '-'); 174 } 175 176 $content.css(closedCSS); 177 178 $accordion.removeClass('open'); 179 } else { 180 $content.css('max-height', $content.data('oHeight')); 181 182 $content.animate(closedCSS, opts.transitionSpeed); 183 184 $accordion.removeClass('open'); 185 } 186 } 187 188 function openAccordion($accordion, $content) { 189 $accordion.trigger('accordion.open'); 190 if (CSStransitions) { 191 toggleTransition($content); 192 193 if (accordionHasParent) { 194 var $parentAccordions = $accordion.parents('[data-accordion]'); 195 196 updateParentHeight($parentAccordions, $accordion, $content.data('oHeight'), '+'); 197 } 198 199 requestAnimFrame(function () { 200 $content.css('max-height', $content.data('oHeight')); 201 }); 202 203 $accordion.addClass('open'); 204 } else { 205 $content.animate({ 206 'max-height': $content.data('oHeight') 207 }, opts.transitionSpeed, function () { 208 $content.css({'max-height': 'none'}); 209 }); 210 211 $accordion.addClass('open'); 212 } 213 } 214 215 function closeSiblingAccordions($accordion) { 216 var $accordionGroup = $accordion.closest(opts.groupElement); 217 218 var $siblings = $accordion.siblings('[data-accordion]').filter('.open'), 219 $siblingsChildren = $siblings.find('[data-accordion]').filter('.open'); 220 221 var $otherAccordions = $siblings.add($siblingsChildren); 222 223 $otherAccordions.each(function () { 224 var $accordion = $(this), 225 $content = $accordion.find(opts.contentElement); 226 227 closeAccordion($accordion, $content); 228 }); 229 230 $otherAccordions.removeClass('open'); 231 } 232 233 function toggleAccordion() { 234 var isAccordionGroup = (opts.singleOpen) ? $accordion.parents(opts.groupElement).length > 0 : false; 235 236 calculateHeight($content); 237 238 if (isAccordionGroup) { 239 closeSiblingAccordions($accordion); 240 } 241 242 if ($accordion.hasClass('open')) { 243 closeAccordion($accordion, $content); 244 } else { 245 openAccordion($accordion, $content); 246 } 247 } 248 249 function addEventListeners() { 250 $controls.on('click', toggleAccordion); 251 252 $controls.on('accordion.toggle', function () { 253 if (opts.singleOpen && $controls.length > 1) { 254 return false; 255 } 256 257 toggleAccordion(); 258 }); 259 260 $controls.on('accordion.refresh', function () { 261 refreshHeight($accordion); 262 }); 263 264 $(window).on('resize', debounce(function () { 265 refreshHeight($accordion); 266 })); 267 } 268 269 function setup() { 270 $content.each(function () { 271 var $curr = $(this); 272 273 if ($curr.css('max-height') != 0) { 274 if (!$curr.closest('[data-accordion]').hasClass('open')) { 275 $curr.css({'max-height': 0, 'overflow': 'hidden'}); 276 } else { 277 toggleTransition($curr); 278 calculateHeight($curr); 279 280 $curr.css('max-height', $curr.data('oHeight')); 281 } 282 } 283 }); 284 285 286 if (!$accordion.attr('data-accordion')) { 287 $accordion.attr('data-accordion', ''); 288 $accordion.find(opts.controlElement).attr('data-control', ''); 289 $accordion.find(opts.contentElement).attr('data-content', ''); 290 } 291 } 292 293 setup(); 294 addEventListeners(); 295 }; 296 297 $.fn[pluginName] = function (options) { 298 return this.each(function () { 299 if (!$.data(this, 'plugin_' + pluginName)) { 300 $.data(this, 'plugin_' + pluginName, 301 new Accordion(this, options)); 302 } 303 }); 304 } 305 306})(jQuery, window, document);
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.