PageSourceSearch

https://justjobs.com/download_files/agency-assets/index.js

js justjobs.com collected 2026-09-29 15:30:42 UTC 33,571 bytes, 1,019 lines download raw bytes

1/*	-----------------------------------------------------------------------------------------------
2	Namespace
3--------------------------------------------------------------------------------------------------- */
4
5var twentytwenty = twentytwenty || {};
6
7// Set a default value for scrolled.
8twentytwenty.scrolled = 0;
9
10// polyfill closest
11// https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill
12if (!Element.prototype.closest) {
13	Element.prototype.closest = function (s) {
14		var el = this;
15
16		do {
17			if (el.matches(s)) {
18				return el;
19			}
20
21			el = el.parentElement || el.parentNode;
22		} while (el !== null && el.nodeType === 1);
23
24		return null;
25	};
26}
27
28// polyfill forEach
29// https://developer.mozilla.org/en-US/docs/Web/API/NodeList/forEach#Polyfill
30if (window.NodeList && !NodeList.prototype.forEach) {
31	NodeList.prototype.forEach = function (callback, thisArg) {
32		var i;
33		var len = this.length;
34
35		thisArg = thisArg || window;
36
37		for (i = 0; i < len; i++) {
38			callback.call(thisArg, this[i], i, this);
39		}
40	};
41}
42
43// event "polyfill"
44twentytwenty.createEvent = function (eventName) {
45	var event;
46	if (typeof window.Event === 'function') {
47		event = new Event(eventName);
48	} else {
49		event = document.createEvent('Event');
50		event.initEvent(eventName, true, false);
51	}
52	return event;
53};
54
55// matches "polyfill"
56// https://developer.mozilla.org/es/docs/Web/API/Element/matches
57if (!Element.prototype.matches) {
58	Element.prototype.matches =
59		Element.prototype.matchesSelector ||
60		Element.prototype.mozMatchesSelector ||
61		Element.prototype.msMatchesSelector ||
62		Element.prototype.oMatchesSelector ||
63		Element.prototype.webkitMatchesSelector ||
64		function (s) {
65			var matches = (this.document || this.ownerDocument).querySelectorAll(s),
66				i = matches.length;
67			while (--i >= 0 && matches.item(i) !== this) {}
68			return i > -1;
69		};
70}
71
72// Add a class to the body for when touch is enabled for browsers that don't support media queries
73// for interaction media features. Adapted from <https://codepen.io/Ferie/pen/vQOMmO>
74(function () {
75	var matchMedia = function () {
76		// Include the 'heartz' as a way to have a non matching MQ to help terminate the join. See <https://git.io/vznFH>.
77		var prefixes = ['-webkit-', '-moz-', '-o-', '-ms-'];
78		var query = ['(', prefixes.join('touch-enabled),('), 'heartz', ')'].join('');
79		return window.matchMedia && window.matchMedia(query).matches;
80	};
81
82	if (('ontouchstart' in window) || (window.DocumentTouch && document instanceof window.DocumentTouch) || matchMedia()) {
83		document.body.classList.add('touch-enabled');
84	}
85}());
86
87/*	-----------------------------------------------------------------------------------------------
88	Cover Modals
89--------------------------------------------------------------------------------------------------- */
90
91twentytwenty.coverModals = {
92
93	init: function () {
94		if (document.querySelector('.cover-modal')) {
95			// Handle cover modals when they're toggled
96			this.onToggle();
97
98			// When toggled, untoggle if visitor clicks on the wrapping element of the modal
99			this.outsideUntoggle();
100
101			// Close on escape key press
102			this.closeOnEscape();
103
104			// Hide and show modals before and after their animations have played out
105			this.hideAndShowModals();
106		}
107	},
108
109	// Handle cover modals when they're toggled
110	onToggle: function () {
111		document.querySelectorAll('.cover-modal').forEach(function (element) {
112			element.addEventListener('toggled', function (event) {
113				var modal = event.target,
114					body = document.body;
115
116				if (modal.classList.contains('active')) {
117					body.classList.add('showing-modal');
118				} else {
119					body.classList.remove('showing-modal');
120					body.classList.add('hiding-modal');
121
122					// Remove the hiding class after a delay, when animations have been run
123					setTimeout(function () {
124						body.classList.remove('hiding-modal');
125					}, 500);
126				}
127			});
128		});
129	},
130
131	// Close modal on outside click
132	outsideUntoggle: function () {
133		document.addEventListener('click', function (event) {
134			var target = event.target;
135			var modal = document.querySelector('.cover-modal.active');
136
137			if (target === modal) {
138				this.untoggleModal(target);
139			}
140		}.bind(this));
141	},
142
143	// Close modal on escape key press
144	closeOnEscape: function () {
145		document.addEventListener('keydown', function (event) {
146			if (event.keyCode === 27) {
147				event.preventDefault();
148				document.querySelectorAll('.cover-modal.active').forEach(function (element) {
149					this.untoggleModal(element);
150				}.bind(this));
151			}
152		}.bind(this));
153	},
154
155	// Hide and show modals before and after their animations have played out
156	hideAndShowModals: function () {
157		var _doc = document,
158			_win = window,
159			modals = _doc.querySelectorAll('.cover-modal'),
160			htmlStyle = _doc.documentElement.style,
161			adminBar = _doc.querySelector('#wpadminbar');
162
163		function getAdminBarHeight(negativeValue) {
164			var height,
165				currentScroll = _win.pageYOffset;
166
167			if (adminBar) {
168				height = currentScroll + adminBar.getBoundingClientRect().height;
169
170				return negativeValue ? -height : height;
171			}
172
173			return currentScroll === 0 ? 0 : -currentScroll;
174		}
175
176		function htmlStyles() {
177			var overflow = _win.innerHeight > _doc.documentElement.getBoundingClientRect().height;
178
179			return {
180				'overflow-y': overflow ? 'hidden' : 'scroll',
181				position: 'fixed',
182				width: '100%',
183				top: getAdminBarHeight(true) + 'px',
184				left: 0
185			};
186		}
187
188		// Show the modal
189		modals.forEach(function (modal) {
190			modal.addEventListener('toggle-target-before-inactive', function (event) {
191				var styles = htmlStyles(),
192					offsetY = _win.pageYOffset,
193					paddingTop = (Math.abs(getAdminBarHeight()) - offsetY) + 'px',
194					mQuery = _win.matchMedia('(max-width: 600px)');
195
196				if (event.target !== modal) {
197					return;
198				}
199
200				Object.keys(styles).forEach(function (styleKey) {
201					htmlStyle.setProperty(styleKey, styles[styleKey]);
202				});
203
204				_win.twentytwenty.scrolled = parseInt(styles.top, 10);
205
206				if (adminBar) {
207					_doc.body.style.setProperty('padding-top', paddingTop);
208
209					if (mQuery.matches) {
210						if (offsetY >= getAdminBarHeight()) {
211							modal.style.setProperty('top', 0);
212						} else {
213							modal.style.setProperty('top', (getAdminBarHeight() - offsetY) + 'px');
214						}
215					}
216				}
217
218				modal.classList.add('show-modal');
219			});
220
221			// Hide the modal after a delay, so animations have time to play out
222			modal.addEventListener('toggle-target-after-inactive', function (event) {
223				if (event.target !== modal) {
224					return;
225				}
226
227				setTimeout(function () {
228					var clickedEl = twentytwenty.toggles.clickedEl;
229
230					modal.classList.remove('show-modal');
231
232					Object.keys(htmlStyles()).forEach(function (styleKey) {
233						htmlStyle.removeProperty(styleKey);
234					});
235
236					if (adminBar) {
237						_doc.body.style.removeProperty('padding-top');
238						modal.style.removeProperty('top');
239					}
240
241					if (clickedEl !== false) {
242						clickedEl.focus();
243						clickedEl = false;
244					}
245
246					_win.scrollTo(0, Math.abs(_win.twentytwenty.scrolled + getAdminBarHeight()));
247
248					_win.twentytwenty.scrolled = 0;
249				}, 500);
250			});
251		});
252	},
253
254	// Untoggle a modal
255	untoggleModal: function (modal) {
256		var modalTargetClass,
257			modalToggle = false;
258
259		// If the modal has specified the string (ID or class) used by toggles to target it, untoggle the toggles with that target string
260		// The modal-target-string must match the string toggles use to target the modal
261		if (modal.dataset.modalTargetString) {
262			modalTargetClass = modal.dataset.modalTargetString;
263
264			modalToggle = document.querySelector('*[data-toggle-target="' + modalTargetClass + '"]');
265		}
266
267		// If a modal toggle exists, trigger it so all of the toggle options are included
268		if (modalToggle) {
269			modalToggle.click();
270
271			// If one doesn't exist, just hide the modal
272		} else {
273			modal.classList.remove('active');
274		}
275	}
276
277}; // twentytwenty.coverModals
278
279/*	-----------------------------------------------------------------------------------------------
280	Intrinsic Ratio Embeds
281--------------------------------------------------------------------------------------------------- */
282
283twentytwenty.intrinsicRatioVideos = {
284
285	init: function () {
286		this.makeFit();
287
288		window.addEventListener('resize', function () {
289			this.makeFit();
290		}.bind(this));
291	},
292
293	makeFit: function () {
294		document.querySelectorAll('iframe, object, video').forEach(function (video) {
295			var ratio, iTargetWidth,
296				container = video.parentNode;
297
298			// Skip videos we want to ignore
299			if (video.classList.contains('intrinsic-ignore') || video.parentNode.classList.contains('intrinsic-ignore')) {
300				return true;
301			}
302
303			if (!video.dataset.origwidth) {
304				// Get the video element proportions
305				video.setAttribute('data-origwidth', video.width);
306				video.setAttribute('data-origheight', video.height);
307			}
308
309			iTargetWidth = container.offsetWidth;
310
311			// Get ratio from proportions
312			ratio = iTargetWidth / video.dataset.origwidth;
313
314			// Scale based on ratio, thus retaining proportions
315			video.style.width = iTargetWidth + 'px';
316			video.style.height = (video.dataset.origheight * ratio) + 'px';
317		});
318	}
319
320}; // twentytwenty.instrinsicRatioVideos
321
322/*	-----------------------------------------------------------------------------------------------
323	Smooth Scroll
324--------------------------------------------------------------------------------------------------- */
325
326twentytwenty.smoothScroll = {
327
328	init: function () {
329		// Scroll to anchor
330		this.scrollToAnchor();
331
332		// Scroll to element
333		this.scrollToElement();
334	},
335
336	// Scroll to anchor
337	scrollToAnchor: function () {
338		var anchorElements = document.querySelectorAll('a[href*="#"]');
339		var anchorElementsList = Array.prototype.slice.call(anchorElements);
340		anchorElementsList.filter(function (element) {
341			if (element.href === '#' || element.href === '#0' || element.i
341d === 'cancel-comment-reply-link' || element.classList.contains('do-not-scroll') || element.classList.contains('skip-link')) {
342				return false;
343			}
344			return true;
345		}).forEach(function (element) {
346			element.addEventListener('click', function (event) {
347				var target, scrollOffset, originalOffset, adminBar, scrollSpeed, additionalOffset;
348
349				// On-page links
350				if (window.location.hostname === event.target.hostname) {
351					// Figure out element to scroll to
352					target = window.location.hash !== '' && document.querySelector(window.location.hash);
353					target = target ? target : event.target.hash !== '' && document.querySelector(event.target.hash);
354
355					// Does a scroll target exist?
356					if (target) {
357						// Only prevent default if animation is actually gonna happen
358						event.preventDefault();
359
360						// Get options
361						additionalOffset = event.target.dataset.additionalOffset;
362						scrollSpeed = event.target.dataset.scrollSpeed ? event.target.dataset.scrollSpeed : 500;
363
364						// Determine offset
365
366						adminBar = document.querySelector('#wpadminbar');
367
368						originalOffset = target.getBoundingClientRect().top + window.pageYOffset;
369						scrollOffset = additionalOffset ? originalOffset + additionalOffset : originalOffset;
370
371						if (adminBar && event.target.className === 'to-the-top') {
372							scrollOffset = scrollOffset - adminBar.getBoundingClientRect().height;
373						}
374
375						twentytwentyScrollTo(scrollOffset, null, scrollSpeed);
376
377						window.location.hash = event.target.hash.slice(1);
378					}
379				}
380			});
381		});
382	},
383
384	// Scroll to element
385	scrollToElement: function () {
386		var scrollToElement = document.querySelector('*[data-scroll-to]');
387
388		if (scrollToElement) {
389			scrollToElement.addEventListener('click', function (event) {
390				var originalOffset, additionalOffset, scrollOffset, scrollSpeed,
391					// Figure out element to scroll to
392					target = event.target.dataset.twentytwentyScrollTo;
393
394				// Make sure said element exists
395				if (target) {
396					event.preventDefault();
397
398					// Get options
399					additionalOffset = event.target.dataset.additionalOffset;
400					scrollSpeed = event.target.dataset.scrollSpeed ? event.target.dataset.scrollSpeed : 500;
401
402					// Determine offset
403					originalOffset = target.getBoundingClientRect().top + window.pageYOffset;
404					scrollOffset = additionalOffset ? originalOffset + additionalOffset : originalOffset;
405
406					twentytwentyScrollTo(scrollOffset, null, scrollSpeed);
407				}
408			});
409		}
410	}
411
412}; // twentytwenty.smoothScroll
413
414/*	-----------------------------------------------------------------------------------------------
415	Modal Menu
416--------------------------------------------------------------------------------------------------- */
417twentytwenty.modalMenu = {
418
419	init: function () {
420		// If the current menu item is in a sub level, expand all the levels higher up on load
421		this.expandLevel();
422		this.keepFocusInModal();
423	},
424
425	expandLevel: function () {
426		var modalMenus = document.querySelectorAll('.modal-menu');
427
428		modalMenus.forEach(function (modalMenu) {
429			var activeMenuItem = modalMenu.querySelector('.current-menu-item');
430
431			if (activeMenuItem) {
432				twentytwentyFindParents(activeMenuItem, 'li').forEach(function (element) {
433					var subMenuToggle = element.querySelector('.sub-menu-toggle');
434					if (subMenuToggle) {
435						twentytwenty.toggles.performToggle(subMenuToggle, true);
436					}
437				});
438			}
439		});
440	},
441
442	keepFocusInModal: function () {
443		var _doc = document;
444
445		_doc.addEventListener('keydown', function (event) {
446			var toggleTarget, modal, selectors, elements, menuType, bottomMenu, activeEl, lastEl, firstEl, tabKey, shift
446Key,
447				clickedEl = twentytwenty.toggles.clickedEl;
448
449			if (clickedEl && _doc.body.classList.contains('showing-modal')) {
450				toggleTarget = clickedEl.dataset.toggleTarget;
451				selectors = 'input, a, button';
452				modal = _doc.querySelector(toggleTarget);
453
454				elements = modal.querySelectorAll(selectors);
455				elements = Array.prototype.slice.call(elements);
456
457				if ('.menu-modal' === toggleTarget) {
458					menuType = window.matchMedia('(min-width: 1000px)').matches;
459					menuType = menuType ? '.expanded-menu' : '.mobile-menu';
460
461					elements = elements.filter(function (element) {
462						return null !== element.closest(menuType) && null !== element.offsetParent;
463					});
464
465					elements.unshift(_doc.querySelector('.close-nav-toggle'));
466
467					bottomMenu = _doc.querySelector('.menu-bottom > nav');
468
469					if (bottomMenu) {
470						bottomMenu.querySelectorAll(selectors).forEach(function (element) {
471							elements.push(element);
472						});
473					}
474				}
475
476				lastEl = elements[elements.length - 1];
477				firstEl = elements[0];
478				activeEl = _doc.activeElement;
479				tabKey = event.keyCode === 9;
480				shiftKey = event.shiftKey;
481
482				if (!shiftKey && tabKey && lastEl === activeEl) {
483					event.preventDefault();
484					firstEl.focus();
485				}
486
487				if (shiftKey && tabKey && firstEl === activeEl) {
488					event.preventDefault();
489					lastEl.focus();
490				}
491			}
492		});
493	}
494}; // twentytwenty.modalMenu
495
496/*	-----------------------------------------------------------------------------------------------
497	Primary Menu
498--------------------------------------------------------------------------------------------------- */
499
500twentytwenty.primaryMenu = {
501
502	init: function () {
503		this.focusMenuWithChildren();
504	},
505
506	// The focusMenuWithChildren() function implements Keyboard Navigation in the Primary Menu
507	// by adding the '.focus' class to all 'li.menu-item-has-children' when the focus is on the 'a' element.
508	focusMenuWithChildren: function () {
509		// Get all the link elements within the primary menu.
510		var links, i, len,
511			menu = document.querySelector('.primary-menu-wrapper');
512
513		if (!menu) {
514			return false;
515		}
516
517		links = menu.getElementsByTagName('a');
518
519		// Each time a menu link is focused or blurred, toggle focus.
520		for (i = 0, len = links.length; i < len; i++) {
521			links[i].addEventListener('focus', toggleFocus, true);
522			links[i].addEventListener('blur', toggleFocus, true);
523		}
524
525		//Sets or removes the .focus class on an element.
526		function toggleFocus() {
527			var self = this;
528
529			// Move up through the ancestors of the current link until we hit .primary-menu.
530			while (-1 === self.className.indexOf('primary-menu')) {
531				// On li elements toggle the class .focus.
532				if ('li' === self.tagName.toLowerCase()) {
533					if (-1 !== self.className.indexOf('focus')) {
534						self.className = self.className.replace(' focus', '');
535					} else {
536						self.className += ' focus';
537					}
538				}
539				self = self.parentElement;
540			}
541		}
542	}
543}; // twentytwenty.primaryMenu
544
545/*	-----------------------------------------------------------------------------------------------
546	Toggles
547--------------------------------------------------------------------------------------------------- */
548
549twentytwenty.toggles = {
550
551	clickedEl: false,
552
553	init: function () {
554		// Do the toggle
555		this.toggle();
556
557		// Check for toggle/untoggle on resize
558		this.resizeCheck();
559
560		// Check for untoggle on escape key press
561		this.untoggleOnEscapeKeyPress();
562	},
563
564	performToggle: function (element, instantly) {
565		var target, timeOutTime, classToToggle,
566			self = this,
567			_doc = document,
568			// Get our targets
569			toggle = element,
570			targetString = toggle.dataset.toggleTarget,
571			activeClass = 'active';
572
573		// Elements to focus after modals are closed
574		if (!_doc.querySelectorAll('.show-modal').length) {
575			self.clickedEl = _doc.activeElement;
576		}
577
578		if (targetString === 'next') {
579			target = toggle.nextSibling;
580		} else {
581			target = _doc.querySelector(targetString);
582		}
583
584		// Trigger events on the toggle targets before they are toggled
585		if (target.classList.contains(activeClass)) {
586			target.dispatchEvent(twentytwenty.createEvent('toggle-target-before-active'));
587		} else {
588			target.dispatchEvent(twentytwenty.createEvent('toggle-target-before-inactive'));
589		}
590
591		// Get the class to toggle, if specified
592		classToToggle = toggle.dataset.classToToggle ? toggle.dataset.classToToggle : activeClass;
593
594		// For cover modals, set a short timeout duration so the class animations have time to play out
595		timeOutTime = 0;
596
597		if (target.classList.contains('cover-modal')) {
598			timeOutTime = 10;
599		}
600
601		setTimeout(function () {
602			var focusElement,
603				subMenued = target.classList.contains('sub-menu'),
604				newTarget = subMenued ? toggle.closest('.menu-item').querySelector('.sub-menu') : target,
605				duration = toggle.dataset.toggleDuration;
606
607			// Toggle the target of the clicked toggle
608			if (toggle.dataset.toggleType === 'slidetoggle' && !instantly && duration !== '0') {
609				twentytwentyMenuToggle(newTarget, duration);
610			} else {
611				newTarget.classList.toggle(classToToggle);
612			}
613
614			// If the toggle target is 'next', only give the clicked toggle the active class
615			if (targetString === 'next') {
616				toggle.classList.toggle(activeClass);
617			} else if (target.classList.contains('sub-menu')) {
618				toggle.classList.toggle(activeClass);
619			} else {
620				// If not, toggle all toggles with this toggle target
621				_doc.querySelector('*[data-toggle-target="' + targetString + '"]').classList.toggle(activeClass);
622			}
623
624			// Toggle aria-expanded on the toggle
625			twentytwentyToggleAttribute(toggle, 'aria-expanded', 'true', 'false');
626
627			if (self.clickedEl && -1 !== toggle.getAttribute('class').indexOf('close-')) {
628				twentytwentyToggleAttribute(self.clickedEl, 'aria-expanded', 'true', 'false');
629			}
630
631			// Toggle body class
632			if (toggle.dataset.toggleBodyClass) {
633				_doc.body.classList.toggle(toggle.dataset.toggleBodyClass);
634			}
635
636			// Check whether to set focus
637			if (toggle.dataset.setFocus) {
638				focusElement = _doc.querySelector(toggle.dataset.setFocus);
639
640				if (focusElement) {
641					if (target.classList.contains(activeClass)) {
642						focusElement.focus();
643					} else {
644						focusElement.blur();
645					}
646				}
647			}
648
649			// Trigger the toggled event on the toggle target
650			target.dispatchEvent(twentytwenty.createEvent('toggled'));
651
652			// Trigger events on the toggle targets after they are toggled
653			if (target.classList.contains(activeClass)) {
654				target.dispatchEvent(twentytwenty.createEvent('toggle-target-after-active'));
655			} else {
656				target.dispatchEvent(twentytwenty.createEvent('toggle-target-after-inactive'));
657			}
658		}, timeOutTime);
659	},
660
661	// Do the toggle
662	toggle: function () {
663		var self = this;
664
665		document.querySelectorAll('*[data-toggle-target]').forEach(function (element) {
666			element.addEventListener('click', function (event) {
667				event.preventDefault();
668				self.performToggle(element);
669			});
670		});
671	},
672
673	// Check for toggle/untoggle on screen resize
674	resizeCheck: function () {
675		if (document.querySelectorAll('*[data-untoggle-above], *[data-untoggle-below], *[data-toggle-above], *[data-toggle-below]').length) {
676			window.addEventListener('resize', function () {
677				var winWidth = window.innerWidth,
678					toggles = document.querySelectorAll('.toggle');
679
680				toggles.forEach(function (toggle) {
681					var unToggleAbove = toggle.dataset.untoggleAbove,
682						unToggleBelow = toggle.dataset.untoggleBelow,
683						toggleAbove = toggle.dataset.toggleAbove,
684						toggleBelow = toggle.dataset.toggleBelow;
685
686					// If no width comparison is set, continue
687					if (!unToggleAbove && !unToggleBelow && !toggleAbove && !toggleBelow) {
688						return;
689					}
690
691					// If the toggle width comparison is true, toggle the toggle
692					if (
693						(((unToggleAbove && winWidth > unToggleAbove) ||
694								(unToggleBelow && winWidth < unToggleBelow)) &&
695							toggle.classList.contains('active')) ||
696						(((toggleAbove && winWidth > toggleAbove) ||
697								(toggleBelow && winWidth < toggleBelow)) &&
698							!toggle.classList.contains('active'))
699					) {
700						toggle.click();
701					}
702				});
703			});
704		}
705	},
706
707	// Close toggle on escape key press
708	untoggleOnEscapeKeyPress: function () {
709		document.addEventListener('keyup', function (event) {
710			if (event.key === 'Escape') {
711				document.querySelectorAll('*[data-untoggle-on-escape].active').forEach(function (element) {
712					if (element.classList.contains('active')) {
713						element.click();
714					}
715				});
716			}
717		});
718	}
719
720}; // twentytwenty.toggles
721
722/**
723 * Is the DOM ready
724 *
725 * this implementation is coming from https://gomakethings.com/a-native-javascript-equivalent-of-jquerys-ready-method/
726 *
727 * @param {Function} fn Callback function to run.
728 */
729function twentytwentyDomReady(fn) {
730	if (typeof fn !== 'function') {
731		return;
732	}
733
734	if (document.readyState === 'interactive' || document.readyState === 'complete') {
735		return fn();
736	}
737
738	document.addEventListener('DOMContentLoaded', fn, false);
739}
740
741twentytwentyDomReady(function () {
742	twentytwenty.toggles.init(); // Handle toggles
743	twentytwenty.coverModals.init(); // Handle cover modals
744	twentytwenty.intrinsicRatioVideos.init(); // Retain aspect ratio of videos on window resize
745	twentytwenty.smoothScroll.init(); // Smooth scroll to anchor link or a specific element
746	twentytwenty.modalMenu.init(); // Modal Menu
747	twentytwenty.primaryMenu.init(); // Primary Menu
748});
749
750/*	-----------------------------------------------------------------------------------------------
751	Helper functions
752--------------------------------------------------------------------------------------------------- */
753
754/* Toggle an attribute ----------------------- */
755
756function twentytwentyToggleAttribute(element, attribute, trueVal, falseVal) {
757	if (trueVal === undefined) {
758		trueVal = true;
759	}
760	if (falseVal === undefined) {
761		falseVal = false;
762	}
763	if (element.getAttribute(attribute) !== trueVal) {
764		element.setAttribute(attribute, trueVal);
765	} else {
766		element.setAttribute(attribute, falseVal);
767	}
768}
769
770/**
771 * Toggle a menu item on or off.
772 *
773 * @param {HTMLElement} target
774 * @param {number} duration
775 */
776function twentytwentyMenuToggle(target, duration) {
777	var initialParentHeight, finalParentHeight, menu, menuItems, transitionListener,
778		initialPositions = [],
779		finalPositions = [];
780
781	if (!target) {
782		return;
783	}
784
785	menu = target.closest('.menu-wrapper');
786
787	// Step 1: look at the initial positions of every menu item.
788	menuItems = menu.querySelectorAll('.menu-item');
789
790	menuItems.forEach(function (menuItem, index) {
791		initialPositions[index] = {
792			x: menuItem.offsetLeft,
793			y: menuItem.offsetTop
794		};
795	});
796	initialParentHeight = target.parentElement.offsetHeight;
797
798	target.classList.add('toggling-target');
799
800	// Step 2: toggle target menu item and look at the final positions of every menu item.
801	target.classList.toggle('active');
802
803	menuItems.forEach(function (menuItem, index) {
804		finalPositions[index] = {
805			x: menuItem.offsetLeft,
806			y: menuItem.offsetTop
807		};
808	});
809	finalParentHeight = target.parentElement.offsetHeight;
810
811	// Step 3: close target menu item again.
812	// The whole process happens without giving the browser a chance to render, so it's invisible.
813	target.classList.toggle('active');
814
815	// Step 4: prepare animation.
816	// Position all the items with absolute offsets, at the same starting position.
817	// Shouldn't result in any visual changes if done right.
818	menu.classList.add('is-toggling');
819	target.classList.toggle('active');
820	menuItems.forEach(function (menuItem, index) {
821		var initialPosition = initialPositions[index];
822		if (initialPosition.y === 0 && menuItem.parentElement === target) {
823			initialPosition.y = initialParentHeight;
824		}
825		menuItem.style.transform = 'translate(' + initialPosition.x + 'px, ' + initialPosition.y + 'px)';
826	});
827
828	// The double rAF is unfortunately needed, since we're toggling CSS classes, and
829	// the only way to ensure layout completion here across browsers is to wait twice.
830	// This just delays the start of the animation by 2 frames and is thus not an issue.
831	requestAnimationFrame(function () {
832		requestAnimationFrame(function () {
833			// Step 5: start animation by moving everything to final position.
834			// All the layout work has already happened, while we were preparing for the animation.
835			// The animation now runs entirely in CSS, using cheap CSS properties (opacity and transform)
836			// that don't trigger the layout or paint stages.
837			menu.classList.add('is-animating');
838			menuItems.forEach(function (menuItem, index) {
839				var finalPosition = finalPositions[index];
840				if (finalPosition.y === 0 && menuItem.parentElement === target) {
841					finalPosition.y = finalParentHeight;
842				}
843				if (duration !== undefined) {
844					menuItem.style.transitionDuration = duration + 'ms';
845				}
846				menuItem.style.transform = 'translate(' + finalPosition.x + 'px, ' + finalPosition.y + 'px)';
847			});
848			if (duration !== undefined) {
849				target.style.transitionDuration = duration + 'ms';
850			}
851		});
852
853		// Step 6: finish toggling.
854		// Remove all transient classes when the animation ends.
855		transitionListener = function () {
856			menu.classList.remove('is-animating');
857			menu.classList.remove('is-toggling');
858			target.classList.remove('toggling-target');
859			menuItems.forEach(function (menuItem) {
860				menuItem.style.transform = '';
861				menuItem.style.transitionDuration = '';
862			});
863			target.style.transitionDuration = '';
864			target.removeEventListener('transitionend', transitionListener);
865		};
866
867		target.addEventListener('transitionend', transitionListener);
868	});
869}
870
871/**
872 * traverses the DOM up to find elements matching the query
873 *
874 * @param {HTMLElement} target
875 * @param {string} query
876 * @return {NodeList} parents matching query
877 */
878function twentytwentyFindParents(target, query) {
879	var parents = [];
880
881	// recursively go up the DOM adding matches to the parents array
882	function traverse(item) {
883		var parent = item.parentNode;
884		if (parent instanceof HTMLElement) {
885			if (parent.matches(query)) {
886				parents.push(parent);
887			}
888			traverse(parent);
889		}
890	}
891
892	traverse(target);
893
894	return parents;
895}
896
897// twentytwentyEaseInOutQuad functions http://goo.gl/5HLl8
898function twentytwentyEaseInOutQuad(t, b, c, d) {
899	t /= d / 2;
900	if (t < 1) {
901		return (((c / 2) * t) * t) + b;
902	}
903	t--;
904	return ((-c / 2) * ((t * (t - 2)) - 1)) + b;
905}
906
907function twentytwentyScrollTo(to, callback, duration) {
908	var start, change, increment, currentTime;
909
910	function move(amount) {
911		document.documentElement.scrollTop = amount;
912		document.body.parentNode.scrollTop = amount;
913		document.body.scrollTop = amount;
914	}
915
916	start = document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop;
917	change = to - start;
918	increment = 20;
919	currentTime = 0;
920
921	duration = (typeof (duration) === 'undefined') ? 500 : duration;
922
923	function animateScroll() {
924		var val;
925
926		// increment the time
927		currentTime += increment;
928		// find the value with the quadratic in-out twentytwentyEaseInOutQuad function
929		val = twentytwentyEaseInOutQuad(currentTime, start, change, duration);
930		// move the document.body
931		move(val);
932		// do the animation unless its over
933		if (currentTime < duration) {
934			window.requestAnimationFrame(animateScroll);
935		} else if (callback && typeof (callback) === 'function') {
936			// the animation is done so lets callback
937			callback();
938		}
939	}
940	animateScroll();
941}
942
943
944jQuery(document).ready(function ($) {
945	jQuery(document).on("click", ".right_side_links_wrapper .custom_menu .menu_item.menu-item-has-children", function () {
946		if (jQuery(this).find(".sub-menu").is(":hidden")) {
947			jQuery(this).find(".sub-menu").slideDown();
948		} else {
949			jQuery(this).find(".sub-menu").slideUp();
950		}
951	})
952
953	jQuery(document).on("click", ".close-nav-toggle", function () {
954		jQuery(".menu-item").removeClass("child_active sub_child_active");
955		jQuery(".movable_menu_items").removeClass("sub_menu_child_open sub_menu_open");
956	})
957
958	jQuery(".menu-modal .mobile-menu .menu-item.menu-item-has-children").each(function () {
959		var menu_name = jQuery(this).find(">.ancestor-wrapper > a").text();
960		if (jQuery(this).find(".menu_heading").length == 0) {
961			jQuery(this).find(">.sub-menu").prepend("<li class='menu_heading'>" + menu_name + "</li>");
962			jQuery(this).find(">.sub-menu").prepend("<li class='menu_back_to_parent'> <svg width='8' height='14' viewBox='0 0 8 14' fill='none' xmlns='http://www.w3.org/2000/svg'><path d='M7.70711 1.70711C8.09763 1.31658 8.09763 0.683418 7.70711 0.292893
962C7.31658 -0.0976311 6.68342 -0.0976311 6.29289 0.292893L7.70711 1.70711ZM1 7L0.292893 6.29289C-0.0976312 6.68342 -0.0976313 7.31658 0.292893 7.70711L1 7ZM6.29289 13.7071C6.68342 14.0976 7.31658 14.0976 7.70711 13.7071C8.09763 13.3166 8.09763 12.6834 7.70711 12.2929L6.29289 13.7071ZM6.29289 0.292893L0.292893 6.29289L1.70711 7.70711L7.70711 1.70711L6.29289 0.292893ZM0.292893 7.70711L6.29289 13.7071L7.70711 12.2929L1.70711 6.29289L0.292893 7.70711Z' fill='currentColor'/></svg> Back</li>");
963		}
964	})
965
966	jQuery(document).on("click", ".menu-modal .mobile-menu > .modal-menu > .menu-item.menu-item-has-children > .ancestor-wrapper > a", function (e) {
967		e.preventDefault();
968		jQuery(this).parent().parent().addClass("child_active");
969		jQuery(".movable_menu_items").addClass("sub_menu_open");
970	})
971
972	jQuery(document).on("click", ".menu-modal .mobile-menu > .modal-menu > .menu-item > .sub-menu .menu-item-has-children > .ancestor-wrapper > a", function (e) {
973		e.preventDefault();
974		jQuery(".movable_menu_items").addClass("sub_menu_child_open");
975		jQuery(this).parent().parent().addClass("sub_child_active");
976	})
977
978	jQuery(document).on("click", ".menu_back_to_parent", function () {
979		if (jQuery(".movable_menu_items").hasClass("sub_menu_child_open")) {
980			jQuery(".movable_menu_items").removeClass("sub_menu_child_open");
981			setTimeout(function () {
982				jQuery(".menu-modal .menu-modal-inner .menu-top .mobile-menu > .modal-menu > .menu-item > .sub-menu .menu-item-has-children").removeClass("sub_child_active");
983			}, 400);
984		} else {
985			jQuery(".movable_menu_items").removeClass("sub_menu_open");
986			setTimeout(function () {
987				jQuery(".menu-modal .menu-modal-inner .menu-top .mobile-menu > .modal-menu > .menu-item").removeClass("child_active");
988			}, 400);
989		}
990	})
991
992	if (jQuery(window).width() < 768) {
993		jQuery('.column_item.column_first .heading').addClass("active");
994		jQuery(".column_item.column_first .menu_section").css("display", "block");
995	}
996
997	jQuery(document).on("click", ".column_item .heading", function (e) {
998		if (jQuery(window).width() < 768) {
999			e.preventDefault();
1000			if (jQuery(this).next().is(":hidden")) {
1001				jQuery(".menu_section").slideUp(400);
1002				jQuery(".heading").removeClass("active");
1003				jQuery(this).addClass("active");
1004				jQuery(this).next().slideDown();
1005			} else {
1006				jQuery(this).removeClass("active");
1007				jQuery(this).next().slideUp();
1008			}
1009		}
1010	});
1011});
1012
1013jQuery(window).on("scroll", function () {
1014	if (jQuery(window).scrollTop() > 100) {
1015		jQuery("body").addClass("sticky_header");
1016	} else {
1017		jQuery("body").removeClass("sticky_header");
1018	}
1019})

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.