PageSourceSearch

https://www.cegema.de/_assets/edb4f517f670dc083919197ece387a78/Plugins/accordion/accordion.js?1780571688

js cegema.de collected 2026-10-07 04:15:15 UTC 28,914 bytes, 1,052 lines download raw bytes

vendor: 28,914 bytes, lines 1-1052
1(function(){
2	"use strict";
3
4	var touchEnabled = "ontouchstart" in document.documentElement;
5	var pressEvent   = touchEnabled ? "touchend" : "click";
6	var each         = [].forEach;
7
8
9	// Name of the onTransitionEnd event supported by this browser
10	var transitionEnd = (function(){
11		for(var names = "transitionend webkitTransitionEnd oTransitionEnd otransitionend".split(" "), i = 0; i < 4; ++i)
12			if("on"+names[i].toLowerCase() in window) return names[i];
13		return names[0];
14	}());
15	
16	
17	
18	/**
19	 * Conditionally add or remove a token from a token-list.
20	 *
21	 * @param {DOMTokenList} list
22	 * @param {String} token
23	 * @param {Boolean} enabled
24	 */
25	function setToken(list, token, enabled){
26		enabled ? list.add(token) : list.remove(token);
27	}
28
29
30
31	/**
32	 * Stop a function from firing too quickly.
33	 *
34	 * Returns a copy of the original function that runs only after the designated
35	 * number of milliseconds have elapsed. Useful for throttling onResize handlers.
36	 *
37	 * @param {Number} limit - Threshold to stall execution by, in milliseconds.
38	 * @param {Boolean} soon - If TRUE, will call the function *before* the threshold's elapsed, rather than after.
39	 * @return {Function}
40	 */
41	function debounce(fn, limit, soon){
42		var limit = limit < 0 ? 0 : limit,
43			started, context, args, timer,
44
45			delayed = function(){
46
47				// Get the time between now and when the function was first fired
48				var timeSince = Date.now() - started;
49
50				if(timeSince >= limit){
51					if(!soon) fn.apply(context, args);
52					if(timer) clearTimeout(timer);
53					timer = context = args = null;
54				}
55
56				else timer = setTimeout(delayed, limit - timeSince);
57			};
58
59
60		// Debounced copy of the original function
61		return function(){
62			context = this,
63			args    = arguments;
64
65			if(!limit)
66				return fn.apply(context, args);
67
68			started = Date.now();
69			if(!timer){
70				if(soon) fn.apply(context, args);
71				timer = setTimeout(delayed, limit);
72			}
73		};
74	};
75
76
77
78	var uniqueID = (function(){
79		var IDs     = {};
80		var indexes = {};
81		
82		
83		/**
84		 * Generate a unique ID for a DOM element.
85		 *
86		 * By default, minimalist IDs like "_1" or "_2" are generated using internally
87		 * tracked incrementation. Uglier, more collision-proof IDs can be generated by
88		 * passing a truthy value to the function's first argument.
89		 *
90		 * Irrespective of whether values are being generated simply or randomly, the
91		 * document tree is always consulted first to ensure a duplicate ID is never
92		 * returned.
93		 *
94		 * @param {String}  prefix - Prefix prepended to result. Default: "_"
95		 * @param {Boolean} random - Generate collision-proof IDs using random symbols
96		 * @param {Number}  length - Length of random passwords. Default: 6
97		 * @return {String}
98		 */
99		function uniqueID(prefix, complex, length){
100			length     = +(length || 6);
101			var result =  (prefix = prefix || "_");
102			
103			// Simple IDs
104			if(!complex){
105				
106				// Set this prefix's starting index if it's not been used yet
107				if(!indexes[prefix])
108					indexes[prefix] = 0;
109				
110				result += ++indexes[prefix];
111			}
112			
113			// Uglier/safer IDs
114			else{
115				var chars   = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
116				chars      += chars.toLowerCase();
117				result     += chars[ Math.round(Math.random() * (chars.length - 1)) ];
118				chars      += "0123456789";
119				
120				while(result.length < length)
121					result += chars[ Math.round(Math.random() * (chars.length - 1))];
122			}
123			
124			return IDs[result] || document.getElementById(result)
125				? uniqueID(prefix, complex)
126				: (IDs[result] = true, result);
127		}
128		
129		
130		return uniqueID;
131	}());
132
133
134	// Name of the CSSOM property used by this browser for CSS transforms
135	var cssTransform = (function(n){
136		s = document.documentElement.style;
137		if((prop = n.toLowerCase()) in s) return prop;
138		for(var prop, s, p = "Webkit Moz Ms O Khtml", p = (p.toLowerCase() + p).split(" "), i = 0; i < 10; ++i)
139			if((prop = p[i]+n) in s) return prop;
140		return "";
141	}("Transform"));
142
143
144	// Whether 3D transforms are supported by this browser
145	var css3DSupported = (function(propName){
146		var e = document.createElement("div"), s = e.style,
147		v = [["translateY(", ")"], ["translate3d(0,", ",0)"]]
148		try{ s[propName] = v[1].join("1px"); } catch(e){}
149		return v[+!!s[propName]] === v[1];
150	}(cssTransform));
151
152
153
154
155
156
157
158
159
160	var folds = [];
161
162
163	/**
164	 * Represents a single panel of togglable content inside an Accordion.
165	 *
166	 * @param {Accordion} accordion
167	 * @param {HTMLElement} el
168	 * @constructor
169	 */
170	var Fold = function(accordion, el){
171		var THIS            = this;
172		var heading         = el.firstElementChild;
173		var content         = el.lastElementChild;
174		var elClasses       = el.classList;
175		var openClass       = accordion.openClass;
176		var closeClass      = accordion.closeClass;
177		var keysEnabled     = !accordion.noKeys;
178		var useBorders      = accordion.useBorders;
179		var useTransforms   = !accordion.noTransforms && cssTransform;
180		var onToggle        = accordion.onToggle;
181		var _disabled       = false;
182		var _open, _y, _height, _ariaEnabled;
183		var scrollX, scrollY;
184		var onTouchStart;
185		var onKeyDown;
186		var onPress;
187		
188		
189		Object.defineProperties(THIS, {
190			fit: {value: fit},
191			
192			
193			// Add or remove relevant ARIA attributes from the fold's elements
194			ariaEnabled: {
195				get: function(){ return _ariaEnabled; },
196				set: function(input){
197					if((input = !!input) !== !!_ariaEnabled){
198						_ariaEnabled = input;
199						
200						// Enable ARIA-attribute management
201						if(input){
202							heading.setAttribute("role", "tab");
203							content.setAttribute("role", "tabpanel");
204							checkIDs();
205							
206							// Update the attributes that're controlled by .open's setter
207							heading.setAttribute("aria-selected", !!_open);
208							heading.setAttribute("aria-expanded", !!_open);
209							content.setAttribute("aria-hidden",   !_open);
210						}
211						
212						// Disabling; remove all relevant attributes
213						else{
214							heading.removeAttribute("role");
215							heading.removeAttribute("aria-controls");
216							heading.removeAttribute("aria-selected");
217							heading.removeAttribute("aria-expanded");
218							
219							content.removeAttribute("role");
220							content.removeAttribute("aria-labelledby");
221							content.removeAttribute("aria-hidden");
222						}
223					}
224				}
225			},
226
227			
228			
229			// Whether or not the fold's currently opened
230			open: {
231				
232				get: function(){
233					
234					// Derive the fold's opened state from the DOM if it's not been determined yet
235					if(undefined === _open){
236						_open = elClasses.contains(openClass);
237						setToken(elClasses, closeClass, !_open);
238					}
239					
240					return _open;
241				},
242				
243				
244				set: function(input){
245					if((input = !!input) !== _open){
246						
247						// If an onToggle callback was specified, run it. Avoid doing anything if it returns false.
248						if("function" === typeof onToggle && false === onToggle.call(null, THIS, input))
249							return;
250						
251						setToken(elClasses, openClass,   input);
252						setToken(elClasses, closeClass, !input);
253						_open = input;
254						
255						// Update ARIA attributes
256						if(_ariaEnabled){
257							heading.setAttribute("aria-selected",  input);
258							heading.setAttribute("aria-expanded",  input);
259							content.setAttribute("aria-hidden",   !input);
260						}
261						
262						// If this fold was closed when the screen resized, run a full update in case its contents were juggled around
263						if(THIS.needsRefresh){
264							delete THIS.needsRefresh;
265							accordion.refresh();
266						}
267						else accordion.update();
268						
269						// Close other folds if accordion is modal
270						if(accordion.modal && _open){
271							for(var fold, i = 0, l = accordion.folds.length; i < l; ++i){
272								if(THIS !== (fold = accordion.folds[i]))
273									fold.open = false;
274							}
275						}
276					}
277				}
278			},
279			
280			
281			// Whether the fold's been deactivated
282			disabled: {
283				get: function(){ return _disabled },
284				set: function(input){
285					if((input = !!input) !== _disabled){
286						var style = el.style;
287						
288						// Deactivated
289						if(_disabled = input){
290							style.height = null;
291							useTransforms
292								? (style[cssTransform] = null)
293								: (style.top = null);
294							
295							touchEnabled && heading.removeEventListener("touchstart", onTouchStart);
296							heading.removeEventListener(pressEvent, onPress);
297							elClasses.remove(openClass, closeClass);
298							if(onKeyDown){
299								heading.removeEventListener("keydown", onKeyDown);
300								heading.removeAttribute("tabindex");
301							}
302							
303							if(_ariaEnabled){
304								THIS.ariaEnabled = false;
305								_ariaEnabled     = true;
306							}
307						}
308						
309						// Reactivated
310						else{
311							style.height = _height + "px";
312							useTransforms
313								? style[cssTransform] =
314									css3DSupported
315										? ("translate3D(0," + _y + "px,0)")
316										: ("translateY("    + _y + "px)")
317								: (style.top = _y + "px");
318							
319							touchEnabled && heading.addEventListener("touchstart", onTouchStart);
320							heading.addEventListener(pressEvent, onPress);
321							
322							if(onKeyDown){
323								heading.addEventListener("keydown", onKeyDown);
324								heading.tabIndex = 0;
325							}
326						}
327					}
328				}
329			},
330			
331			
332			// Vertical position of the fold within an accordion's container
333			y: {
334				get: function(){
335					if(undefined === _y)
336						return (_y = parseInt(el.style.top) || 0);
337					return _y;
338				},
339				
340				set: function(input){
341					if((input = +input) !== _y){
342						_y = input;
343						useTransforms
344							? el.style[cssTransform] =
345								css3DSupported
346									? ("translate3D(0," + input + "px,0)")
347									: ("translateY("    + input + "px)")
348							: (el.style.top = input + "px");
349					}
350				}
351			},
352			
353			
354			// Height of the fold's outermost container
355			height: {
356				
357				get: function(){
358					if(undefined === _height){
359						_height = THIS.headingHeight + content.scrollHeight;
360						el.style.height = _height + "px";
361					}
362					return _height;
363				},
364				
365				set: function(input){
366					if(input && (input = +input) !== _height){
367						el.style.height = input + "px"
368						_height         = input;
369					}
370				}
371			},
372			
373
374			// Current height of the fold's heading
375			headingHeight: {
376				get: function(){
377					return heading.scrollHeight
378						+ THIS.heightOffset
379						+ (useBorders ? THIS.headingBorder : 0)
380				}
381			},
382			
383			// Total height consumed by the heading element's CSS borders, if any
384			headingBorder: {
385				get: function(){
386					return (heading.offsetHeight || 0) - (heading.clientHeight || 0);
387				}
388			},
389			
390			
391			
392			// Total height of the fold's container element
393			elHeight: {
394				get: function(){
395					return el.scrollHeight + (useBorders ? THIS.elBorder : 0);
396				}
397			},
398			
399			// Total height consumed by container element's CSS borders, if any
400			elBorder: {
401				get: function(){
402					return (el.offsetHeight || 0) - (el.clientHeight || 0);
403				}
404			},
405			
406			
407			// Whether the fold's container has been resized incorrectly
408			wrongSize: {
409				get: function(){
410					return THIS.headingHeight + content.scrollHeight !== el.scrollHeight;
411				}
412			}
413		});
414		
415		
416		
417		THIS.index        = folds.push(THIS) - 1;
418		THIS.accordion    = accordion;
419		THIS.el           = el;
420		THIS.heading      = heading;
421		THIS.content      = content;
422		THIS.ariaEnabled  = !accordion.noAria;
423		THIS.heightOffset = accordion.heightOffset;
424		el.accordionFold  = THIS.index;
425		useBorders        = "auto" === useBorders ? (0 !== THIS.elBorder + THIS.headingBorder) : useBorders;
426		
427		
428		
429		function checkIDs(){
430			var headingSuffix = "-heading";
431			var contentSuffix = "-content";
432			var elID            = el.id;
433			var id;
434			
435			// Neither of the fold's elements have an ID attribute
436			if(!heading.id && !content.id){
437				id             = elID || uniqueID("a");
438				heading.id     = id + headingSuffix;
439				content.id     = id + contentSuffix;
440			}
441			
442			// Either the heading or element lack an ID
443			else if(!content.id) content.id   = (elID || heading.id) + contentSuffix;
444			else if(!heading.id) heading.id   = (elID || content.id) + headingSuffix;
445			
446			// Finally, double-check each element's ID is really unique
447			var $ = function(s){return document.querySelectorAll("#"+s)};
448			while($(content.id).length > 1 || $(heading.id).length > 1){
449				id         = uniqueID("a");
450				content.id = id + contentSuffix;
451				heading.id = id + headingSuffix;
452			}
453			
454			// Update ARIA attributes
455			heading.setAttribute("aria-controls",    content.id);
456			content.setAttribute("aria-labelledby",  heading.id);
457		}
458		
459		
460		
461		// Keyboard navigation
462		if(keysEnabled){
463			heading.tabIndex = 0;
464			heading.addEventListener("keydown", onKeyDown = function(e){
465				var key = e.keyCode;
466				var fold;
467				
468				switch(key){
469
470					// Spacebar: Toggle
471					case 32:
472						e.preventDefault(); // Fall-through
473					
474					
475					// Enter: Toggle
476					case 13:
477						THIS.open = !THIS.open;
478						if("A" === e.target.tagName)
479							e.preventDefault();
480						break;
481					
482					
483					// Escape: Clear focus
484					case 27:
485						e.target.blur();
486						break;
487					
488					
489					// Up arrow: Previous section
490					case 38:{
491						
492						// Is there a previous sibling to navigate up to?
493						if(fold = THIS.previousFold){
494							var children = fold.childAccordions;
495							
496							// Is it open, and does it have nested accordions?
497							if(fold.open && children){
498								var lastAcc;
499								var lastFold;
500								
501								// Locate the deepest/nearest accordion that's currently exposed
502								while(children){
503									lastAcc  = children[children.length - 1];
504									lastFold = lastAcc.folds[lastAcc.folds.length - 1];
505									if(!lastFold.open) break;
506									children = lastFold.childAccordions;
507								}
508								
509								lastFold.heading.focus();
510							}
511							
512							// Nope
513							else fold.heading.focus();
514						}
515						
516						// Is there a higher level we can jump back up to?
517						else if(accordion.parent)
518							accordion.parentFold.heading.focus();
519						
520						// There's nothing to move back to, so just let the browser run its usual behaviour
521						else return true;
522						
523						e.preventDefault();
524						return false;
525					}
526					
527					
528					
529					// Down arrow: Next section
530					case 40:{
531						var children = THIS.childAccordions;
532						
533						// Is there a nested accordion to jump into?
534						if(THIS.open && children)
535							children[0].folds[0].heading.focus();
536						
537						// No, there isn't. Is there another sibling to move down to?
538						else if(fold = THIS.nextFold)
539							fold.heading.focus();
540						
541						// Is there a containing accordion we can navigate back up to?
542						else if(THIS.accordion.parent){
543							var parent = THIS;
544							while(parent = parent.accordion.parentFold)
545								if(fold = parent.nextFold){
546									fold.heading.focus();
547									break;
548								}
549							
550							// Nowhere left to go...
551							if(!parent) return true;
552						}
553						
554						// Nah. Just scroll the window normally, as per browser default
555						else return true;
556						
557						e.preventDefault();
558						return false;
559					}
560					
561					
562					// Left arrow
563					case 37:{
564						
565						// Close an opened section
566						if(THIS.open) THIS.open = false;
567						
568						// Switch focus back to parent
569						else if(accordion.parent)
570							accordion.parentFold.heading.focus();
571						
572						break;
573					}
574					
575					// Right arrow
576					case 39:{
577						var children = THIS.childAccordions;
578						
579						// Open a closed section
580						if(!THIS.open) THIS.open = true;
581						
582						// Switch focus to a nested accordion
583						else if(children)
584							children[0].folds[0].heading.focus();
585						
586						break;
587					}
588				}
589			});
590		}
591		
592		
593		// Listener to record the viewport's scroll offsets at the beginning of a touch
594		touchEnabled && heading.addEventListener("touchstart", onTouchStart = function(e){
595			scrollX = window.pageXOffset;
596			scrollY = window.pageYOffset;
597		}, {passive: true});
598		
599		
600		heading.addEventListener(pressEvent, onPress = function(e){
601			
602			// Pressed on something inside the header
603			if(e.target !== heading && heading.contains(e.target)){
604				
605				// Cancel fold-toggle if user clicked on an anchor-link
606				if("A" === e.target.tagName && e.target.href)
607					return true;
608			}
609			
610			if(e.type !== "touchend" || (e.cancelable && window.pageXOffset === scrollX && window.pageYOffset === scrollY)){
611				THIS.open = !THIS.open;
612				e.preventDefault();
613			}
614			return false;
615		});
616		
617		
618		
619		
620		/**
621		 * Adjust a fold's container to fit its content.
622		 */
623		function fit(){
624			var height = THIS.headingHeight;
625			if(THIS.open)   height += content.scrollHeight;
626			if(useBorders)  height += THIS.elBorder;
627			THIS.height = height;
628		}
629	}
630
631
632
633
634
635
636
637	var accordions       = [];
638	var activeAccordions = 0;
639	var lastResizeRate;
640
641
642	/**
643	 * Represents a column of collapsible content regions.
644	 *
645	 * @param {HTMLElement} el                    - Container wrapped around each immediate fold
646	 * @param {Object}      options               - Optional hash of settings
647	 * @param {String}      options.openClass     - CSS class controlling each fold's "open" state
648	 * @param {String}      options.closeClass    - CSS class used to mark a fold as closed
649	 * @param {String}      options.edgeClass     - CSS class toggled based on whether the bottom-edge is visible
650	 * @param {String}      options.snapClass     - CSS class for disabling transitions between window resizes
651	 * @param {String}      options.enabledClass  - CSS class marking an accordion as enabled
652	 * @param {String}      options.disabledClass - CSS class marking an accordion as disabled
653	 * @param {Boolean}     options.disabled      - Whether to disable the accordion on creation
654	 * @param {Boolean}     options.modal         - Whether to close the current fold when opening another
655	 * @param {Boolean}     options.noAria        - Disable the addition and management of ARIA attributes
656	 * @param {Boolean}     options.noKeys        - Disable keyboard navigation
657	 * @param {Boolean}     options.noTransforms  - Disable CSS transforms; positioning will be used instead
658	 * @param {Number}      options.heightOffset  - Distance to offset each fold by
659	 * @param {Boolean}     options.useBorders    - Consider borders when calculating fold heights
660	 * @param {Function}    options.onToggle      - Callback executed when opening or closing a fold
661	 * @constructor
662	 */
663	var Accordion = function(el, options){
664		var THIS          = this;
665		var elClasses     = el.classList;
666		var options       = options || {};
667		var edgeClass     = (undefined === options.edgeClass    ? "edge-visible" : options.edgeClass);
668		var snapClass     = (undefined === options.snapClass    ? "snap"         : options.snapClass);
669		var enabledClass  = (undefined === options.enabledClass ? "accordion"    : options.enabledClass);
670		var disabledClass = options.disabledClass;
671		var _height, _disabled, _parent, _parentFold, _modal;
672
673
674		Object.defineProperties(THIS, {
675			update:     {value: update},
676			updateFold: {value: updateFold},
677			refresh:    {value: refresh},
678			
679			// Whether the accordion's been deactivated
680			disabled: {
681				get: function(){ return _disabled; },
682				set: function(input){
683					if((input = !!input) !== _disabled){
684						var style   = el.style;
685						var folds   = THIS.folds;
686						
687						enabledClass  && setToken(elClasses, enabledClass,  !input);
688						disabledClass && setToken(elClasses, disabledClass,  input);
689						
690						
691						// Deactivating
692						if(_disabled = input){
693							style.height = null;
694							snapClass && elClasses.remove(snapClass);
695							if(edgeClass){
696								el.removeEventListener(transitionEnd, THIS.onTransitionEnd);
697								elClasses.remove(edgeClass);
698							}
699							
700							for(var i = 0, l = folds.length; i < l; ++i)
701								folds[i].disabled = true;
702							
703							THIS.noAria || el.removeAttribute("role");
704							--activeAccordions;
705						}
706						
707						
708						// Reactivating
709						else{
710							for(var i = 0, l = folds.length; i < l; ++i)
711								folds[i].disabled = false;
712							
713							THIS.noAria || el.setAttribute("role", "tablist");
714							++activeAccordions;
715							update();
716						}
717						
718
719						
720						// If there're no more active accordions, disable the onResize handler
721						if(activeAccordions <= 0){
722							activeAccordions = 0;
723							Accordion.setResizeRate(false);
724						}
725						
726						// Otherwise, reactivate the onResize handler, assuming it was previously active
727						else if(lastResizeRate)
728							Accordion.setResizeRate(lastResizeRate);
729					}
730				}
731			},
732			
733			// Get or set the accordion enclosing this one
734			parent: {
735				set: function(input){ _parent = input; },
736				get: function(){
737					var result = _parent;
738					if(!result) return null;
739					
740					// Search for the first ancestor that *isn't* disabled
741					while(result){
742						if(!result.disabled) return result;
743						result = result.parent;
744					}
745					return null;
746				}
747			},
748			
749			// Get or set the fold of the accordion enclosing this one
750			parentFold: {
751				set: function(input){ _parentFold = input; },
752				get: function(){
753					var fold = _parentFold;
754					if(!fold) return null;
755					
756					var accordion = fold.accordion;
757					
758					// Search for the first ancestor that *isn't* disabled
759					while(fold && accordion){
760						if(!accordion.disabled) return fold;
761						if(accordion = accordion.parent)
762							fold = accordion.parentFold;
763					}
764					return null;
765				}
766			},
767			
768			// Height of the accordion's container element
769			height: {
770				get: function(){ return _height; },
771				set: function(input){
772					if(input && (input = +input) !== _height){
773						el.style.height = input + "px";
774						_height         = input;
775					}
776				}
777			},
778			
779			// Whether one of the Accordion's folds has been resized incorrectly
780			wrongSize: {
781				get: function(){
782					var a = this.folds;
783					var l = a.length;
784					var i = 0;
785					for(; i < l; ++i) if(a[i].wrongSize) return true;
786					if(a = this.childAccordions)
787					for(; i < l; ++i) if(a[i].wrongSize) return true;
788					return false;
789				}
790			},
791			
792			// Top-level ancestor this accordion's nested inside
793			root: {
794				get: function(){
795					var result = this;
796					while(result){
797						if(!result.parent) return result;
798						result = result.parent;
799					}
800				}
801			}
802		});
803
804		
805		// Assign options as properties
806		THIS.openClass    = options.openClass  || "open";
807		THIS.closeClass   = options.closeClass || "closed";
808		THIS.modal        = !!options.modal;
809		THIS.noAria       = !!options.noAria;
810		THIS.noKeys       = !!options.noKeys;
811		THIS.noTransforms = !!options.noTransforms;
812		THIS.index        = accordions.push(THIS) - 1;
813		THIS.heightOffset = +options.heightOffset || 0;
814		THIS.useBorders   = undefined === options.useBorders ? "auto" : options.useBorders;
815		THIS.onToggle     = options.onToggle;
816		
817		
818		// Create a fold for each immediate descendant of the Accordion's container
819		var folds = [];
820		each.call(el.children, function(i){
821			var fold = new Fold(THIS, i);
822			folds.push(fold);
823			
824			// Connect the fold to its previous sibling, if it's not the first to be added
825			var prev = folds[folds.length - 2];
826			if(prev){
827				prev.nextFold     = fold;
828				fold.previousFold = prev;
829			}
830		});
831		
832		
833		el.accordion    = THIS.index;
834		THIS.noAria || el.setAttribute("role", "tablist");
835		THIS.el         = el;
836		THIS.folds      = folds;
837		
838		// Add .enabledClass early - it might affect the heights of each fold
839		if(!options.disabled && enabledClass)
840			elClasses.add(enabledClass);
841		
842		update();
843		
844		
845		// Find out if this accordion's nested inside another
846		var next = el;
847		while((next = next.parentNode) && 1 === next.nodeType){
848			var fold = Accordion.getFold(next);
849			if(fold){
850				var accordion   = fold.accordion;
851				THIS.parent     = accordion;
852				THIS.parentFold = fold;
853				edgeClass && elClasses.remove(edgeClass);
854				(accordion.childAccordions = accordion.childAccordions || []).push(THIS);
855				(fold.childAccordions      = fold.childAccordions      || []).push(THIS);
856
857				// Adjust the height of the containing fold's element
858				if(fold.open){
859					var scrollHeight = fold.el.scrollHeight;
860					var distance     = (fold.headingHeight + fold.content.scrollHeight) - scrollHeight || (scrollHeight - fold.el.clientHeight);
861					accordion.updateFold(fold, distance);
862				}
863				break;
864			}
865		}
866		
867		
868		edgeClass && el.addEventListener(transitionEnd, this.onTransitionEnd = function(e){
869			if(!THIS.parent && e.target === el && "height" === e.propertyName && el.getBoundingClientRect().bottom > window.innerHeight)
870				elClasses.remove(edgeClass);
871		});
872		
873		this.disabled = !!options.disabled;
874		
875		
876		
877		/**
878		 * Internal method to check if an accordion's bottom-edge is visible to the user (or about to be).
879		 *
880		 * @param {Number} offset
881		 * @private
882		 */
883		function edgeCheck(offset){
884			if(edgeClass){
885				var box         = el.getBoundingClientRect();
886				var windowEdge  = window.innerHeight;
887				
888				// If the bottom-edge is visible (or about to be), enable height animation
889				if(box.bottom + (offset || 0) < windowEdge)
890					elClasses.add(edgeClass)
891				
892				// If the bottom-edge isn't visible anyway, disable height animation immediately
893				else if(box.bottom > windowEdge)
894					elClasses.remove(edgeClass);
895			}
896		}
897		
898		
899		
900		/**
901		 * Update the vertical ordinate of each sibling for a particular fold.
902		 *
903		 * @param {Fold} fold
904		 * @param {Number} offset - Pixel distance to adjust by
905		 */
906		function updateFold(fold, offset){
907			var next = fold;
908			var parentFold = THIS.parentFold;
909			
910			while(next = next.nextFold)
911				next.y  += offset;
912			parentFold || edgeCheck(offset);
913			fold.height += offset;
914			THIS.height += offset;
915			
916			parentFold && parentFold.open && THIS.parent.updateFold(parentFold, offset);
917		}
918		
919		
920		/**
921		 * Update the height of each fold to fit its content.
922		 */
923		function update(){
924			var y      = 0;
925			var height = 0;
926			var i      = 0;
927			var l      = folds.length;
928			var parentFold = THIS.parentFold;
929			var fold, diff;
930			
931			for(; i < l; ++i){
932				fold   = folds[i];
933				fold.y = y;
934				fold.fit();
935				y      += fold.height;
936				height += fold.height;
937			}
938			
939			diff = height - _height;
940			parentFold
941				? (parentFold.open && THIS.parent.updateFold(parentFold, diff))
942				: edgeCheck(diff);
943			
944			THIS.height = height;
945		}
946		
947		
948		
949		/**
950		 * Recalculate the boundaries of an Accordion and its descendants.
951		 *
952		 * This method should only be called if the width of a container changes,
953		 * or a fold's contents have resized unexpectedly (such as when images load).
954		 *
955		 * @param {Boolean} allowSnap - Snap folds instantly into place without transitioning
956		 */
957		function refresh(allowSnap){
958			var snap = allowSnap ? snapClass : false;
959			snap && elClasses.add(snap);
960			
961			THIS.update();
962			THIS.childAccordions && THIS.childAccordions.forEach(function(a){
963				a.parentFold.open
964					? a.refresh(allowSnap)
965					: (a.parentFold.needsRefresh = true);
966			});
967			
968			snap && setTimeout(function(e){elClasses.remove(snap)}, 20);
969		}
970	}
971
972	// If IE8PP exists, it means the author wants/needs IE8 support. See also: tinyurl.com/fixIE8-9
973	if("function" === typeof IE8PP)
974		Accordion = IE8PP(Accordion),
975		Fold      = IE8PP(Fold);
976
977
978
979	/**
980	 * Alter the rate at which screen-resize events update accordion widths.
981	 *
982	 * @param {Number} delay - Rate expressed in milliseconds
983	 */
984	Accordion.setResizeRate = function(delay){
985		var fn = function(e){
986			for(var a, i = 0, l = accordions.length; i < l; ++i){
987				a = accordions[i];
988				a.parent || a.disabled || a.refresh(true);
989			}
990		};
991		
992		var THIS = Accordion;
993		THIS.onResize && window.removeEventListener("resize", THIS.onResize);
994		
995		// Make sure we weren't passed an explicit value of FALSE, or a negative value
996		if(false !== delay && (delay = +delay || 0) >= 0){
997			THIS.onResize = delay ? debounce(fn, delay) : fn;
998			window.addEventListener("resize", THIS.onResize);
999			if(delay) lastResizeRate = delay;
1000		}
1001	}
1002	
1003	
1004	
1005	/**
1006	 * Return the closest (most deeply-nested) accordion enclosing an element.
1007	 *
1008	 * @param {Node} node
1009	 * @return {Accordion}
1010	 */
1011	Accordion.getAccordion = function(node){
1012		while(node){
1013			if("accordion" in node)
1014				return accordions[node.accordion];
1015			
1016			node = node.parentNode;
1017			if(!node || node.nodeType !== 1) return null;
1018		}
1019	}
1020	
1021	
1022	/**
1023	 * Return the closest (most deeply-nested) fold enclosing an element.
1024	 *
1025	 * @param {Node} node
1026	 * @return {Fold}
1027	 */
1028	Accordion.getFold = function(node){
1029		while(node){
1030			if("accordionFold" in node)
1031				return folds[node.accordionFold];
1032			
1033			node = node.parentNode;
1034			if(!node || node.nodeType !== 1) return null;
1035		}
1036	}
1037	
1038
1039	
1040	Accordion.setResizeRate(25);
1041	
1042	
1043	// Browser export
1044	window.Accordion = Accordion;
1045	
1046	// CommonJS/Node.js
1047	if("object" === typeof module && "object" === typeof module.exports)
1048		module.exports.Accordion = Accordion;
1049	
1050	// AMD/UMD-like systems
1051	return Accordion;
1052}());

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.