PageSourceSearch

https://www.drhvac.ca/wp-content/themes/drhvac/js/aria.accordion.js?ver=7.1.2

js drhvac.ca collected 2026-09-28 11:12:04 UTC 12,100 bytes, 407 lines download raw bytes

1;(function ( w, doc, undefined ) {
2	'use strict';
3
4	/**
5	 * Local object for method references
6	 * and define script meta-data
7	 */
8	var ARIAaccordion = {};
9	w.ARIAaccordion   = ARIAaccordion;
10
11	ARIAaccordion.NS      = 'ARIAaccordion';
12	ARIAaccordion.AUTHOR  = 'Scott O\'Hara';
13	ARIAaccordion.VERSION = '3.2.1';
14	ARIAaccordion.LICENSE = 'https://github.com/scottaohara/accessible_accordions/blob/master/LICENSE';
15
16	var widgetClass   = 'accordion';
17	var widgetTriggerClass = widgetClass + '__trigger';
18	var widgetHeadingClass = widgetClass + '__heading';
19	var widgetPanelClass = widgetClass + '__panel';
20
21	var widgetHeading = '[data-aria-accordion-heading]';
22	var widgetPanel   = '[data-aria-accordion-panel]'
23
24	var idCounter = 0;
25
26	/**
27	 * Global Create
28	 *
29	 * This function validates that the minimum required markup
30	 * is present to create the ARIA widget(s).
31	 * Any additional markup elements or attributes that
32	 * do not exist in the found required markup patterns
33	 * will be generated via this function.
34	 */
35	ARIAaccordion.create = function () {
36		var self;
37		var panels;
38		var defaultPanel = 'none';
39		var headings;
40		var triggers;
41		var constantPanel;
42		var multiPanel;
43		var i;
44
45		var widget = doc.querySelectorAll('[data-aria-accordion]');
46		console.log(widget);
47		idCounter += 1;
48
49		for ( i = 0; i < widget.length; i++ ) {
50			self = widget[i];
51			var t;
52
53			/**
54			 * Check for IDs and create arrays of necessary
55			 * panels & headings for further setup functions.
56			 */
57			if ( !self.hasAttribute('id') ) {
58				self.id = 'acc_' + idCounter + '-' + i;
59			}
60
61			/**
62			 * Setup accordion classes
63			 */
64			self.classList.add(widgetClass);
65
66
67			/**
68			 * Get all panels & headings of an accordion pattern based
69			 * on a specific ID > direct child selector (this will ensure
70			 * that nested accordions don't get properties meant for
71			 * the parent accordion, or vice-versa).
72			 *
73			 * If accordions are contained within an ol/ul, the selector
74			 * needs to be different.
75			 */
76			if ( doc.querySelectorAll('#' + self.id + '> li').length ) {
77				panels = doc.querySelectorAll('#' + self.id + ' li > ' + widgetPanel);
78				headings = doc.querySelectorAll('#' + self.id + ' li > ' + widgetHeading);
79			}
80			else {
81				panels = doc.querySelectorAll('#' + self.id + ' > ' + widgetPanel);
82				headings = doc.querySelectorAll('#' + self.id + ' > ' + widgetHeading);
83			}
84
85			/**
86			 * Check for options:
87			 * data-default - is there a default opened panel?
88			 * data-constant - should the accordion always have A panel open?
89			 */
90			if ( self.hasAttribute('data-default') ) {
91				defaultPanel = self.getAttribute('data-default');
92			}
93
94			/**
95			 * Accordions with a constantly open panel are not a default
96			 * but if a data-constant attribute is used, then we need this
97			 * to be true.
98			 */
99			constantPanel = self.hasAttribute('data-constant');
100
101			/**
102			 * Accordions can have multiple panels open at a time,
103			 * if they have a data-multi attribute.
104			 */
105			multiPanel = self.hasAttribute('data-multi');
106
107			/**
108			 * If accordion panels are meant to transition, apply this inline style.
109			 * This is to help mitigate a quick flash of CSS being applied to the
110			 * no-js styling, and having an unwanted transition on initial page load.
111			 */
112			if ( self.hasAttribute('data-transition') ) {
113				var thesePanels = self.querySelectorAll(widgetPanel);
114
115				for ( t = 0; t < thesePanels.length; t++ ) {
116					thesePanels[t].classList.add(widgetPanelClass + '--transition');
117				}
118			}
119
120			/**
121			 * Setup Panels, Headings & Buttons
122			 */
123			ARIAaccordion.setupPanels(self.id, panels, defaultPanel, constantPanel);
124			ARIAaccordion.setupHeadingButton(headings, constantPanel);
125
126			if ( doc.querySelectorAll('#' + self.id + '> li').length ) {
127				triggers = doc.querySelectorAll('#' + self.id + ' li > ' + widgetHeading + ' .' + widgetTriggerClass);
128			}
129			else {
130				triggers = doc.querySelectorAll('#' + self.id + ' > ' + widgetHeading + ' .' + widgetTriggerClass);
131			}
132
133			/**
134			 * Now that the headings/triggers and panels are setup
135			 * we can grab all the triggers and setup their functionality.
136			 */
137			for ( t = 0; t < triggers.length; t++ ) {
138				triggers[t].addEventListener('click', ARIAaccordion.actions);
139				triggers[t].addEventListener('keydown', ARIAaccordion.keytrolls);
140			}
141		} // for(widget.length)
142	}; // ARIAaccordion.create()
143
144
145	ARIAaccordion.setupPanels = function ( id, panels, defaultPanel, constantPanel ) {
146		var i;
147		var panel;
148		var panelID;
149		var setPanel;
150		var constant;
151
152		for ( i = 0; i < panels.length; i++ ) {
153			panel = panels[i];
154			panelID = id + '_panel_' + (i + 1);
155			setPanel = defaultPanel;
156			constant = constantPanel;
157
158			panel.setAttribute('id', panelID);
159			ariaHidden(panels[0], true);
160
161			panel.classList.add(widgetPanelClass);
162
163			/**
164			 * Set the accordion to have the appropriately
165			 * opened panel if a data-default value is set.
166			 * If no value set, then no panels are open.
167			 */
168			if ( setPanel !== 'none' && parseInt(setPanel) !== NaN ) {
169				// if value is 1 or less
170				if ( setPanel <= 1 ) {
171					ariaHidden(panels[0], false);
172				}
173				// if value is more than the number of panels, then open
174				// the last panel by default
175				else if ( (setPanel - 1) >= panels.length ) {
176					ariaHidden(panels[panels.length - 1], false);
177				}
178				// for any other value between 2 - the last panel #, open that one
179				else {
180					ariaHidden(panels[(setPanel - 1)], false);
181				}
182			}
183
184			/**
185			 * If an accordion is meant to have a consistently open panel,
186			 * and a default open panel was not set (or was not set correctly),
187			 * then run one more check.
188			 */
189			if ( constant && setPanel === 'none' || parseInt(setPanel) === NaN ) {
190				ariaHidden(panels[0], false);
191			}
192		}
193	}; // ARIAaccordion.setupPanels
194
195
196	ARIAaccordion.setupHeadingButton = function ( headings, constantPanel ) {
197		var heading;
198		var targetID;
199		var targetState;
200		var newButton;
201		var buttonText;
202		var i;
203
204		for ( i = 0; i < headings.length; i++ ) {
205			heading     = headings[i];
206			targetID    = heading.nextElementSibling.id;
207			targetState = doc.getElementById(targetID).getAttribute('aria-hidden');
208
209			// setup new heading buttons
210			newButton  = doc.createElement('button');
211			buttonText = heading.innerHTML;
212			// clear out the heading's content
213			heading.innerHTML = '';
214			// provide the heading with a class for styling
215			heading.classList.add(widgetHeadingClass);
216
217			newButton.setAttribute('type', 'button');
218			newButton.setAttribute('aria-controls', targetID);
219			newButton.setAttribute('id', targetID + '_trigger');
220			newButton.classList.add(widgetTriggerClass);
221
222			/**
223			 * Check the corresponding panel to see if it was set up
224			 * to be hidden or shown by default. Add an aria-expanded
225			 * attribute value that is appropriate.
226			 */
227			if ( targetState === 'false' ) {
228				ariaExpanded(newButton, true);
229				isCurrent(newButton, true);
230
231				/**
232				 * Check to see if this an accordion that needs a constantly
233				 * opened panel, and if the button's target is not hidden.
234				 */
235				if ( constantPanel ) {
236					newButton.setAttribute('aria-disabled', 'true');
237				}
238			}
239			else {
240				ariaExpanded(newButton, false);
241				isCurrent(newButton, false);
242			}
243
244			// Add the Button & previous heading text
245			heading.appendChild(newButton);
246			newButton.innerHTML = buttonText;
247		}
248	}; // ARIAaccordion.createButton
249
250
251	ARIAaccordion.actions = function ( e ) {
252		// Need to pass in if this is a multi accordion or not.
253		// Also need to pass in existing trigger arrays.
254		var thisAccordion = this.id.replace(/_panel.*$/g, '');
255		var thisTarget = doc.getElementById(this.getAttribute('aria-controls'));
256		var thisTriggers;
257
258		if ( doc.querySelectorAll('#' + thisAccordion + '> li').length ) {
259			thisTriggers = doc.querySelectorAll('#' + thisAccordion + ' li > ' + widgetHeading + ' .' + widgetTriggerClass);
260		}
261		else {
262			thisTriggers = doc.querySelectorAll('#' + thisAccordion + ' > ' + widgetHeading + ' .' + widgetTriggerClass);
263		}
264
265		e.preventDefault();
266
267		ARIAaccordion.togglePanel( e, thisAccordion, thisTarget, thisTriggers );
268	}; // ARIAaccordion.actions()
269
270
271	ARIAaccordion.togglePanel = function ( e, thisAccordion, targetPanel, triggers ) {
272		var getID;
273		var i;
274		var thisTrigger = e.target;
275
276		// check to see if a trigger is disabled
277		if ( thisTrigger.getAttribute('aria-disabled') !== 'true' ) {
278
279			getID = thisTrigger.getAttribute('aria-controls');
280
281			isCurrent(thisTrigger, 'true');
282
283			if ( thisTrigger.getAttribute('aria-expanded') === 'true' ) {
284				ariaExpanded(thisTrigger, 'false');
285				ariaHidden(targetPanel, 'true');
286			}
287			else {
288				ariaExpanded(thisTrigger, 'true');
289				ariaHidden(targetPanel, 'false');
290
291				if ( doc.getElementById(thisAccordion).hasAttribute('data-constant') ) {
292					ariaDisabled(thisTrigger, 'true');
293				}
294			}
295
296			if ( doc.getElementById(thisAccordion).hasAttribute('data-constant') ||
297					 !doc.getElementById(thisAccordion).hasAttribute('data-multi') ) {
298
299				for ( i = 0; i < triggers.length; i++ ) {
300					if ( thisTrigger !== triggers[i] ) {
301						isCurrent(triggers[i], 'false');
302						getID = triggers[i].getAttribute('aria-controls');
303						ariaDisabled(triggers[i], 'false');
304						ariaExpanded(triggers[i], 'false');
305						ariaHidden(doc.getElementById(getID), 'true');
306					}
307				}
308			}
309		}
310	};
311
312
313	ARIAaccordion.keytrolls = function ( e ) {
314		if ( e.target.classList.contains(widgetTriggerClass) ) {
315			var keyCode = e.keyCode || e.which;
316
317			// var keyUp = 38;
318			// var keyDown = 40;
319			var keyHome = 36;
320			var keyEnd = 35;
321
322			var thisAccordion = this.id.replace(/_panel.*$/g, '');
323			var thisTriggers;
324
325			if ( doc.querySelectorAll('#' + thisAccordion + '> li').length ) {
326				thisTriggers = doc.querySelectorAll('#' + thisAccordion + ' li > ' + widgetHeading + ' .' + widgetTriggerClass);
327			}
328			else {
329				thisTriggers = doc.querySelectorAll('#' + thisAccordion + ' > ' + widgetHeading + ' .' + widgetTriggerClass);
330			}
331
332
333			switch ( keyCode ) {
334				/**
335				 * keyUp & keyDown are optional controls
336				 * for accordion components.
337				 */
338				// case keyUp:
339				// 	if ( doc.getElementById(thisAccordion).hasAttribute('data-up-down') ) {
340				// 		e.preventDefault();
341				// 		// optional up arrow controls
342				// 	}
343				// 	break;
344
345				// case keyDown:
346				// 	if ( doc.getElementById(thisAccordion).hasAttribute('data-up-down') ) {
347				// 		e.preventDefault();
348				// 		// optional down arrow control
349				// 	}
350				// 	break;
351
352				/**
353				 * keyEnd/Home are optional functions that may not be inherently known
354				 * to most users and, in the case of END, conflict with expected
355				 * usage of that key with NVDA.
356				 */
357				case keyEnd:
358					e.preventDefault();
359					thisTriggers[thisTriggers.length - 1].focus();
360					break;
361
362				case keyHome:
363					e.preventDefault();
364					thisTriggers[0].focus();
365					break;
366
367				default:
368					break;
369			}
370		}
371	}; // ARIAaccordion.keytrolls()
372
373
374	/**
375	 * Initialize Accordion Functions
376	 * if expanding this script, place any other
377	 * initialize functions within here.
378	 */
379	ARIAaccordion.init = function () {
380		ARIAaccordion.create();
381	};
382
383
384	/**
385	 * Helper Functions
386	 * Just to cut down on the verboseness of some declarations
387	 */
388	var ariaHidden = function ( el, state ) {
389		el.setAttribute('aria-hidden', state);
390	};
391
392	var ariaExpanded = function ( el, state ) {
393		el.setAttribute('aria-expanded', state);
394	};
395
396	var ariaDisabled = function ( el, state ) {
397		el.setAttribute('aria-disabled', state);
398	};
399
400	var isCurrent = function ( el, state ) {
401		el.setAttribute('data-current', state);
402	};
403
404	// go go JavaScript
405	ARIAaccordion.init();
406
407})( 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.