PageSourceSearch

https://www.mifcc.org/wp-content/themes/construction-light/inc/mobile-menu/navigation.js?ver=1

js mifcc.org collected 2026-10-04 20:22:15 UTC 23,244 bytes, 746 lines download raw bytes

1var sparklethemes = sparklethemes || {};
2
3// Set a default value for scrolled.
4sparklethemes.scrolled = 0;
5
6// event "polyfill"
7sparklethemes.createEvent = function( eventName ) {
8	var event;
9	if ( typeof window.Event === 'function' ) {
10		event = new Event( eventName );
11	} else {
12		event = document.createEvent( 'Event' );
13		event.initEvent( eventName, true, false );
14	}
15	return event;
16};
17
18/*****
19 * Cover Modals
20*/
21sparklethemes.coverModals = {
22
23	init: function() {
24		if ( document.querySelector( '.cover-modal' ) ) {
25			// Handle cover modals when they're toggled.
26			this.onToggle();
27
28			// When toggled, untoggle if visitor clicks on the wrapping element of the modal.
29			this.outsideUntoggle();
30
31			// Close on escape key press.
32			this.closeOnEscape();
33
34			// Hide and show modals before and after their animations have played out.
35			this.hideAndShowModals();
36		}
37	},
38	// Handle cover modals when they're toggled.
39	onToggle: function() {
40		document.querySelectorAll( '.cover-modal' ).forEach( function( element ) {
41			element.addEventListener( 'toggled', function( event ) {
42				var modal = event.target,
43					body = document.body;
44
45				if ( modal.classList.contains( 'active' ) ) {
46					body.classList.add( 'showing-modal' );
47				} else {
48					body.classList.remove( 'showing-modal' );
49					body.classList.add( 'hiding-modal' );
50
51					// Remove the hiding class after a delay, when animations have been run.
52					setTimeout( function() {
53						body.classList.remove( 'hiding-modal' );
54					}, 500 );
55				}
56			} );
57		} );
58	},
59	// Close modal on outside click.
60	outsideUntoggle: function() {
61		document.addEventListener( 'click', function( event ) {
62			var target = event.target;
63			var modal = document.querySelector( '.cover-modal.active' );
64
65			if ( target === modal ) {
66				this.untoggleModal( target );
67			}
68		}.bind( this ) );
69	},
70	// Close modal on escape key press.
71	closeOnEscape: function() {
72		document.addEventListener( 'keydown', function( event ) {
73			if ( event.keyCode === 27 ) {
74				event.preventDefault();
75				document.querySelectorAll( '.cover-modal.active' ).forEach( function( element ) {
76					this.untoggleModal( element );
77				}.bind( this ) );
78			}
79		}.bind( this ) );
80	},
81	// Hide and show modals before and after their animations have played out.
82	hideAndShowModals: function() {
83		var _doc = document,
84			_win = window,
85			modals = _doc.querySelectorAll( '.cover-modal' ),
86			htmlStyle = _doc.documentElement.style,
87			adminBar = _doc.querySelector( '#wpadminbar' );
88
89		function getAdminBarHeight( negativeValue ) {
90			var height,
91				currentScroll = _win.pageYOffset;
92
93			if ( adminBar ) {
94				height = currentScroll + adminBar.getBoundingClientRect().height;
95
96				return negativeValue ? -height : height;
97			}
98
99			return currentScroll === 0 ? 0 : -currentScroll;
100		}
101
102		function htmlStyles() {
103			var overflow = _win.innerHeight > _doc.documentElement.getBoundingClientRect().height;
104
105			return {
106				'overflow-y': overflow ? 'hidden' : 'scroll',
107				position: 'fixed',
108				width: '100%',
109				top: getAdminBarHeight( true ) + 'px',
110				left: 0
111			};
112		}
113
114		// Show the modal.
115		modals.forEach( function( modal ) {
116			modal.addEventListener( 'toggle-target-before-inactive', function( event ) {
117				var styles = htmlStyles(),
118					offsetY = _win.pageYOffset,
119					paddingTop = ( Math.abs( getAdminBarHeight() ) - offsetY ) + 'px',
120					mQuery = _win.matchMedia( '(max-width: 600px)' );
121
122				if ( event.target !== modal ) {
123					return;
124				}
125
126				Object.keys( styles ).forEach( function( styleKey ) {
127					htmlStyle.setProperty( styleKey, styles[ styleKey ] );
128				} );
129
130				_win.sparklethemes.scrolled = parseInt( styles.top, 10 );
131
132				if ( adminBar ) {
133					_doc.body.style.setProperty( 'padding-top', paddingTop );
134
135					if ( mQuery.matches ) {
136						if ( offsetY >= getAdminBarHeight() ) {
137							modal.style.setProperty( 'top', 0 );
138						} else {
139							modal.style.setProperty( 'top', ( getAdminBarHeight() - offsetY ) + 'px' );
140						}
141					}
142				}
143
144				modal.classList.add( 'show-modal' );
145			} );
146
147			// Hide the modal after a delay, so animations have time to play out.
148			modal.addEventListener( 'toggle-target-after-inactive', function( event ) {
149				if ( event.target !== modal ) {
150					return;
151				}
152
153				setTimeout( function() {
154					var clickedEl = sparklethemes.toggles.clickedEl;
155
156					modal.classList.remove( 'show-modal' );
157
158					Object.keys( htmlStyles() ).forEach( function( styleKey ) {
159						htmlStyle.removeProperty( styleKey );
160					} );
161
162					if ( adminBar ) {
163						_doc.body.style.removeProperty( 'padding-top' );
164						modal.style.removeProperty( 'top' );
165					}
166
167					if ( clickedEl !== false ) {
168						clickedEl.focus();
169						clickedEl = false;
170					}
171
172					_win.scrollTo( 0, Math.abs( _win.sparklethemes.scrolled + getAdminBarHeight() ) );
173
174					_win.sparklethemes.scrolled = 0;
175				}, 500 );
176			} );
177		} );
178	},
179
180	// Untoggle a modal.
181	untoggleModal: function( modal ) {
182		var modalTargetClass,
183			modalToggle = false;
184
185		// If the modal has specified the string (ID or class) used by toggles to target it, untoggle the toggles with that target string.
186		// The modal-target-string must match the string toggles use to target the modal.
187		if ( modal.dataset.modalTargetString ) {
188			modalTargetClass = modal.dataset.modalTargetString;
189
190			modalToggle = document.querySelector( '*[data-toggle-target="' + modalTargetClass + '"]' );
191		}
192
193		// If a modal toggle exists, trigger it so all of the toggle options are included.
194		if ( modalToggle ) {
195			modalToggle.click();
196
197			// If one doesn't exist, just hide the modal.
198		} else {
199			modal.classList.remove( 'active' );
200		}
201	}
202
203}; // sparklethemes.coverModals
204
205
206/*****
207 * Mobile Menu Cover Modals
208*/
209sparklethemes.modalMenu = {
210
211	init: function() {
212		// If the current menu item is in a sub level, expand all the levels higher up on load.
213		this.expandLevel();
214		this.keepFocusInModal();
215	},
216
217	expandLevel: function() {
218		var modalMenus = document.querySelectorAll( '.modal-menu' );
219
220		modalMenus.forEach( function( modalMenu ) {
221			var activeMenuItem = modalMenu.querySelector( '.current-menu-item' );
222
223			if ( activeMenuItem ) {
224				sparklethemesFindParents( activeMenuItem, 'li' ).forEach( function( element ) {
225					var subMenuToggle = element.querySelector( '.sub-menu-toggle' );
226					if ( subMenuToggle ) {
227						sparklethemes.toggles.performToggle( subMenuToggle, true );
228					}
229				} );
230			}
231		} );
232	},
233
234	keepFocusInModal: function() {
235		var _doc = document;
236
237		_doc.addEventListener( 'keydown', function( event ) {
238			var toggleTarget, modal, selectors, elements, menuType, bottomMenu, activeEl, lastEl, firstEl, tabKey, shift
238Key,
239				clickedEl = sparklethemes.toggles.clickedEl;
240
241			if ( clickedEl && _doc.body.classList.contains( 'showing-modal' ) ) {
242				toggleTarget = clickedEl.dataset.toggleTarget;
243				selectors = 'input, a, button';
244				modal = _doc.querySelector( toggleTarget );
245				if( !modal ) return;
246				elements = modal.querySelectorAll( selectors );
247				elements = Array.prototype.slice.call( elements );
248
249				if ( '.menu-modal' === toggleTarget ) {
250					menuType = window.matchMedia( '(min-width: 1000px)' ).matches;
251					menuType = menuType ? '.expanded-menu' : '.mobile-menu';
252
253					elements = elements.filter( function( element ) {
254						return null !== element.closest( menuType ) && null !== element.offsetParent;
255					} );
256
257					elements.unshift( _doc.querySelector( '.close-nav-toggle' ) );
258
259					bottomMenu = _doc.querySelector( '.menu-bottom > nav' );
260
261					if ( bottomMenu ) {
262						bottomMenu.querySelectorAll( selectors ).forEach( function( element ) {
263							elements.push( element );
264						} );
265					}
266				}
267
268				lastEl = elements[ elements.length - 1 ];
269				firstEl = elements[0];
270				activeEl = _doc.activeElement;
271				tabKey = event.keyCode === 9;
272				shiftKey = event.shiftKey;
273
274				if ( ! shiftKey && tabKey && lastEl === activeEl ) {
275					event.preventDefault();
276					firstEl.focus();
277				}
278
279				if ( shiftKey && tabKey && firstEl === activeEl ) {
280					event.preventDefault();
281					lastEl.focus();
282				}
283			}
284		} );
285	}
286
287}; // sparklethemes.modalMenu
288
289/*****
290 * Primary Menu Mobile Menu
291*/
292sparklethemes.primaryMenu = {
293
294	init: function() {
295		this.focusMenuWithChildren();
296	},
297
298	// The focusMenuWithChildren() function implements Keyboard Navigation in the Primary Menu
299	// by adding the '.focus' class to all 'li.menu-item-has-children' when the focus is on the 'a' element.
300	// The focusMenuWithChildren() function implements Keyboard Navigation in the Primary Menu
301    // by adding the '.focus' class to all 'li.menu-item-has-children' when the focus is on the 'a' element.
302    focusMenuWithChildren: function() {
303
304        // Get all the link elements within the primary menu.
305        var links, i, len,
306            menu = document.querySelector( '.box-header-nav' );
307
308        if ( ! menu ) {
309            return false;
310        }
311
312        links = menu.getElementsByTagName( 'a' );
313
314        // Each time a menu link is focused or blurred, toggle focus.
315        for ( i = 0, len = links.length; i < len; i++ ) {
316            links[i].addEventListener( 'focus', toggleFocus, true );
317            links[i].addEventListener( 'blur', toggleFocus, true );
318        }
319
320        //Sets or removes the .focus class on an element.
321        function toggleFocus() {
322        
323            var self = this;
324
325            // Move up through the ancestors of the current link until we hit .primary-menu.
326            while ( -1 === self.className.indexOf( 'main-menu' ) ) {
327                // On li elements toggle the class .focus.
328                if ( 'li' === self.tagName.toLowerCase() ) {
329                    if ( -1 !== self.className.indexOf( 'focus' ) ) {
330                        self.className = self.className.replace( ' focus', '' );
331                    } else {
332                        self.className += ' focus';
333                     }
334                 }
335                self = self.parentElement;
336             }
337        }
338    }
339}; // sparklethemes.primaryMenu
340
341
342/*****
343 * categoryMenu Main Menu
344*/
345sparklethemes.categoryMenu = {
346
347	init: function() {
348		this.focusMenuWithChildren();
349	},
350
351	// The focusMenuWithChildren() function implements Keyboard Navigation in the Primary Menu
352	// by adding the '.focus' class to all 'li.menu-item-has-children' when the focus is on the 'a' element.
353	// The focusMenuWithChildren() function implements Keyboard Navigation in the Primary Menu
354    // by adding the '.focus' class to all 'li.menu-item-has-children' when the focus is on the 'a' element.
355    focusMenuWithChildren: function() {
356
357        // Get all the link elements within the primary menu.
358        var links, i, len,
359            menu = document.querySelector( '.verticalmenu-content.menu-category' );
360
361        if ( ! menu ) {
362            return false;
363        }
364
365        links = menu.getElementsByTagName( 'a' );
366
367        // Each time a menu link is focused or blurred, toggle focus.
368        for ( i = 0, len = links.length; i < len; i++ ) {
369            links[i].addEventListener( 'focus', toggleFocus, true );
370            links[i].addEventListener( 'blur', toggleFocus, true );
371        }
372
373        //Sets or removes the .focus class on an element.
374        function toggleFocus() {
375        
376            var self = this;
377
378            // Move up through the ancestors of the current link until we hit .primary-menu.
379            while ( -1 === self.className.indexOf( 'vertical-menu' ) ) {
380                // On li elements toggle the class .focus.
381                if ( 'li' === self.tagName.toLowerCase() ) {
382                    if ( -1 !== self.className.indexOf( 'focus' ) ) {
383                        self.className = self.className.replace( ' focus', '' );
384                    } else {
385                        self.className += ' focus';
386                     }
387                 }
388                self = self.parentElement;
389             }
390        }
391    }
392}; // sparklethemes.categoryMenu
393
394/*****
395 * Mobile Menu Toggles
396*/
397sparklethemes.toggles = {
398
399	clickedEl: false,
400
401	init: function() {
402		// Do the toggle.
403		this.toggle();
404
405		// Check for toggle/untoggle on resize.
406		this.resizeCheck();
407
408		// Check for untoggle on escape key press.
409		this.untoggleOnEscapeKeyPress();
410	},
411
412	performToggle: function( element, instantly ) {
413		var target, timeOutTime, classToToggle,
414			self = this,
415			_doc = document,
416			// Get our targets.
417			toggle = element,
418			targetString = toggle.dataset.toggleTarget,
419			activeClass = 'active';
420
421		// Elements to focus after modals are closed.
422		if ( ! _doc.querySelectorAll( '.show-modal' ).length ) {
423			self.clickedEl = _doc.activeElement;
424		}
425
426		if ( targetString === 'next' ) {
427			target = toggle.nextSibling;
428		} else {
429			target = _doc.querySelector( targetString );
430		}
431
432		// Trigger events on the toggle targets before they are toggled.
433		if ( target.classList.contains( activeClass ) ) {
434			target.dispatchEvent( sparklethemes.createEvent( 'toggle-target-before-active' ) );
435		} else {
436			target.dispatchEvent( sparklethemes.createEvent( 'toggle-target-before-inactive' ) );
437		}
438
439		// Get the class to toggle, if specified.
440		classToToggle = toggle.dataset.classToToggle ? toggle.dataset.classToToggle : activeClass;
441
442		// For cover modals, set a short timeout duration so the class animations have time to play out.
443		timeOutTime = 0;
444
445		if ( target.classList.contains( 'cover-modal' ) ) {
446			timeOutTime = 10;
447		}
448
449		// if( target.dataset.modalTargetShow == 'relative'){
450		// 	target.style.setProperty( 'top', ( toggle.offsetTop ) + 'px' );
451		// }
452
453		setTimeout( function() {
454			var focusElement,
455				subMenued = target.classList.contains( 'sub-menu' ),
456				newTarget = subMenued ? toggle.closest( '.menu-item' ).querySelector( '.sub-menu' ) : target,
457				duration = toggle.dataset.toggleDuration;
458
459			// Toggle the target of the clicked toggle.
460			if ( toggle.dataset.toggleType === 'slidetoggle' && ! instantly && duration !== '0' ) {
461				sparklethemesMenuToggle( newTarget, duration );
462			} else {
463				newTarget.classList.toggle( classToToggle );
464			}
465
466			// If the toggle target is 'next', only give the clicked toggle the active class.
467			if ( targetString === 'next' ) {
468				toggle.classList.toggle( activeClass );
469			} else if ( target.classList.contains( 'sub-menu' ) ) {
470				toggle.classList.toggle( activeClass );
471			} else {
472				// If not, toggle all toggles with this toggle target.
473				_doc.querySelector( '*[data-toggle-target="' + targetString + '"]' ).classList.toggle( activeClass );
474			}
475
476			// Toggle aria-expanded on the toggle.
477			sparklethemesToggleAttribute( toggle, 'aria-expanded', 'true', 'false' );
478
479			if ( self.clickedEl && -1 !== toggle.getAttribute( 'class' ).indexOf( 'close-' ) ) {
480				sparklethemesToggleAttribute( self.clickedEl, 'aria-expanded', 'true', 'false' );
481			}
482
483			// Toggle body class.
484			if ( toggle.dataset.toggleBodyClass ) {
485				_doc.body.classList.toggle( toggle.dataset.toggleBodyClass );
486			}
487
488			// Check whether to set focus.
489			if ( toggle.dataset.setFocus ) {
490				focusElement = _doc.querySelector( toggle.dataset.setFocus );
491
492				if ( focusElement ) {
493					if ( target.classList.contains( activeClass ) ) {
494						focusElement.focus();
495					} else {
496						focusElement.blur();
497					}
498				}
499			}
500
501			// Trigger the toggled event on the toggle target.
502			target.dispatchEvent( sparklethemes.createEvent( 'toggled' ) );
503
504			// Trigger events on the toggle targets after they are toggled.
505			if ( target.classList.contains( activeClass ) ) {
506				target.dispatchEvent( sparklethemes.createEvent( 'toggle-target-after-active' ) );
507			} else {
508				target.dispatchEvent( sparklethemes.createEvent( 'toggle-target-after-inactive' ) );
509			}
510		}, timeOutTime );
511	},
512
513	// Do the toggle.
514	toggle: function() {
515		var self = this;
516
517		document.querySelectorAll( '*[data-toggle-target]' ).forEach( function( element ) {
518			element.addEventListener( 'click', function( event ) {
519				event.preventDefault();
520				self.performToggle( element );
521			} );
522		} );
523	},
524
525	// Check for toggle/untoggle on screen resize.
526	resizeCheck: function() {
527		if ( document.querySelectorAll( '*[data-untoggle-above], *[data-untoggle-below], *[data-toggle-above], *[data-toggle-below]' ).length ) {
528			window.addEventListener( 'resize', function() {
529				var winWidth = window.innerWidth,
530					toggles = document.querySelectorAll( '.toggle' );
531
532				toggles.forEach( function( toggle ) {
533					var unToggleAbove = toggle.dataset.untoggleAbove,
534						unToggleBelow = toggle.dataset.untoggleBelow,
535						toggleAbove = toggle.dataset.toggleAbove,
536						toggleBelow = toggle.dataset.toggleBelow;
537
538					// If no width comparison is set, continue.
539					if ( ! unToggleAbove && ! unToggleBelow && ! toggleAbove && ! toggleBelow ) {
540						return;
541					}
542
543					// If the toggle width comparison is true, toggle the toggle.
544					if (
545						( ( ( unToggleAbove && winWidth > unToggleAbove ) ||
546							( unToggleBelow && winWidth < unToggleBelow ) ) &&
547							toggle.classList.contains( 'active' ) ) ||
548						( ( ( toggleAbove && winWidth > toggleAbove ) ||
549							( toggleBelow && winWidth < toggleBelow ) ) &&
550							! toggle.classList.contains( 'active' ) )
551					) {
552						toggle.click();
553					}
554				} );
555			} );
556		}
557	},
558
559	// Close toggle on escape key press.
560	untoggleOnEscapeKeyPress: function() {
561		document.addEventListener( 'keyup', function( event ) {
562			if ( event.key === 'Escape' ) {
563				document.querySelectorAll( '*[data-untoggle-on-escape].active' ).forEach( function( element ) {
564					if ( element.classList.contains( 'active' ) ) {
565						element.click();
566					}
567				} );
568			}
569		} );
570	}
571
572}; // sparklethemes.toggles
573
574/**
575 * Is the DOM ready?
576 *
577 * This implementation is coming from https://gomakethings.com/a-native-javascript-equivalent-of-jquerys-ready-method/
578 *
579 * @param {Function} fn Callback function to run.
580 */
581function sparklethemesDomReady( fn ) {
582	if ( typeof fn !== 'function' ) {
583		return;
584	}
585
586	if ( document.readyState === 'interactive' || document.readyState === 'complete' ) {
587		return fn();
588	}
589
590	document.addEventListener( 'DOMContentLoaded', fn, false );
591}
592
593sparklethemesDomReady( function() {
594	sparklethemes.toggles.init();              // Handle toggles.
595	sparklethemes.coverModals.init();          // Handle cover modals.
596	sparklethemes.modalMenu.init();            // Modal Menu.
597	sparklethemes.primaryMenu.init();          // Primary Menu.
598	sparklethemes.categoryMenu.init();          // Vertical Category Menu.
599} );
600
601/*-----------------------------------------------------------------------------------------------
602	
603--------------------------------------------------------------------------------------------------- */
604/*****
605 * Helper functions ( Toggle an Attribute )
606*/
607function sparklethemesToggleAttribute( element, attribute, trueVal, falseVal ) {
608	if ( trueVal === undefined ) {
609		trueVal = true;
610	}
611	if ( falseVal === undefined ) {
612		falseVal = false;
613	}
614	if ( element.getAttribute( attribute ) !== trueVal ) {
615		element.setAttribute( attribute, trueVal );
616	} else {
617		element.setAttribute( attribute, falseVal );
618	}
619}
620
621/**
622 * Toggle a menu item on or off.
623 *
624 * @param {HTMLElement} target
625 * @param {number} duration
626 */
627function sparklethemesMenuToggle( target, duration ) {
628	var initialParentHeight, finalParentHeight, menu, menuItems, transitionListener,
629		initialPositions = [],
630		finalPositions = [];
631
632	if ( ! target ) {
633		return;
634	}
635
636	menu = target.closest( '.menu-wrapper' );
637
638	// Step 1: look at the initial positions of every menu item.
639	menuItems = menu.querySelectorAll( '.menu-item' );
640
641	menuItems.forEach( function( menuItem, index ) {
642		initialPositions[ index ] = { x: menuItem.offsetLeft, y: menuItem.offsetTop };
643	} );
644	initialParentHeight = target.parentElement.offsetHeight;
645
646	target.classList.add( 'toggling-target' );
647
648	// Step 2: toggle target menu item and look at the final positions of every menu item.
649	target.classList.toggle( 'active' );
650
651	menuItems.forEach( function( menuItem, index ) {
652		finalPositions[ index ] = { x: menuItem.offsetLeft, y: menuItem.offsetTop };
653	} );
654	finalParentHeight = target.parentElement.offsetHeight;
655
656	// Step 3: close target menu item again.
657	// The whole process happens without giving the browser a chance to render, so it's invisible.
658	target.classList.toggle( 'active' );
659
660	/*
661	 * Step 4: prepare animation.
662	 * Position all the items with absolute offsets, at the same starting position.
663	 * Shouldn't result in any visual changes if done right.
664	 */
665	menu.classList.add( 'is-toggling' );
666	target.classList.toggle( 'active' );
667	menuItems.forEach( function( menuItem, index ) {
668		var initialPosition = initialPositions[ index ];
669		if ( initialPosition.y === 0 && menuItem.parentElement === target ) {
670			initialPosition.y = initialParentHeight;
671		}
672		// menuItem.style.transform = 'translate(' + initialPosition.x + 'px, ' + initialPosition.y + 'px)';
673	} );
674
675	/*
676	 * The double rAF is unfortunately needed, since we're toggling CSS classes, and
677	 * the only way to ensure layout completion here across browsers is to wait twice.
678	 * This just delays the start of the animation by 2 frames and is thus not an issue.
679	 */
680	requestAnimationFrame( function() {
681		requestAnimationFrame( function() {
682			/*
683			 * Step 5: start animation by moving everything to final position.
684			 * All the layout work has already happened, while we were preparing for the animation.
685			 * The animation now runs entirely in CSS, using cheap CSS properties (opacity and transform)
686			 * that don't trigger the layout or paint stages.
687			 */
688			menu.classList.add( 'is-animating' );
689			menuItems.forEach( function( menuItem, index ) {
690				var finalPosition = finalPositions[ index ];
691				if ( finalPosition.y === 0 && menuItem.parentElement === target ) {
692					finalPosition.y = finalParentHeight;
693				}
694				if ( duration !== undefined ) {
695					menuItem.style.transitionDuration = duration + 'ms';
696				}
697				// menuItem.style.transform = 'translate(' + finalPosition.x + 'px, ' + finalPosition.y + 'px)';
698			} );
699			if ( duration !== undefined ) {
700				target.style.transitionDuration = duration + 'ms';
701			}
702		} );
703
704		// Step 6: finish toggling.
705		// Remove all transient classes when the animation ends.
706		transitionListener = function() {
707			menu.classList.remove( 'is-animating' );
708			menu.classList.remove( 'is-toggling' );
709			target.classList.remove( 'toggling-target' );
710			menuItems.forEach( function( menuItem ) {
711				menuItem.style.transform = '';
712				menuItem.style.transitionDuration = '';
713			} );
714			target.style.transitionDuration = '';
715			target.removeEventListener( 'transitionend', transitionListener );
716		};
717
718		target.addEventListener( 'transitionend', transitionListener );
719	} );
720}
721
722/**
723 * Traverses the DOM up to find elements matching the query.
724 *
725 * @param {HTMLElement} target
726 * @param {string} query
727 * @return {NodeList} parents matching query
728 */
729function sparklethemesFindParents( target, query ) {
730	var parents = [];
731
732	// Recursively go up the DOM adding matches to the parents array.
733	function traverse( item ) {
734		var parent = item.parentNode;
735		if ( parent instanceof HTMLElement ) {
736			if ( parent.matches( query ) ) {
737				parents.push( parent );
738			}
739			traverse( parent );
740		}
741	}
742
743	traverse( target );
744
745	return parents;
746}

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.