PageSourceSearch

https://www.desarrollandoamerica.org/wp-content/themes/twentyninet…/touch-keyboard-navigation.js?ver=20230621

js desarrollandoamerica.org collected 2026-09-24 10:38:51 UTC 9,503 bytes, 354 lines download raw bytes

1/**
2 * Touch & Keyboard navigation.
3 *
4 * Contains handlers for touch devices and keyboard navigation.
5 */
6
7(function() {
8
9	/**
10	 * Debounce.
11	 *
12	 * @param {Function} func
13	 * @param {number} wait
14	 * @param {boolean} immediate
15	 */
16	function debounce(func, wait, immediate) {
17		'use strict';
18
19		var timeout;
20		wait      = (typeof wait !== 'undefined') ? wait : 20;
21		immediate = (typeof immediate !== 'undefined') ? immediate : true;
22
23		return function() {
24
25			var context = this, args = arguments;
26			var later = function() {
27				timeout = null;
28
29				if (!immediate) {
30					func.apply(context, args);
31				}
32			};
33
34			var callNow = immediate && !timeout;
35
36			clearTimeout(timeout);
37			timeout = setTimeout(later, wait);
38
39			if (callNow) {
40				func.apply(context, args);
41			}
42		};
43	}
44
45	/**
46	 * Add class.
47	 *
48	 * @param {Object} el
49	 * @param {string} cls
50	 */
51	function addClass(el, cls) {
52		if ( ! el.className.match( '(?:^|\\s)' + cls + '(?!\\S)') ) {
53			el.className += ' ' + cls;
54		}
55	}
56
57	/**
58	 * Delete class.
59	 *
60	 * @param {Object} el
61	 * @param {string} cls
62	 */
63	function deleteClass(el, cls) {
64		el.className = el.className.replace( new RegExp( '(?:^|\\s)' + cls + '(?!\\S)' ),'' );
65	}
66
67	/**
68	 * Has class?
69	 *
70	 * @param {Object} el
71	 * @param {string} cls
72	 *
73	 * @returns {boolean} Has class
74	 */
75	function hasClass(el, cls) {
76
77		if ( el.className.match( '(?:^|\\s)' + cls + '(?!\\S)' ) ) {
78			return true;
79		}
80	}
81
82	/**
83	 * Toggle Aria Expanded state for screenreaders.
84	 *
85	 * @param {Object} ariaItem
86	 */
87	function toggleAriaExpandedState( ariaItem ) {
88		'use strict';
89
90		var ariaState = ariaItem.getAttribute('aria-expanded');
91
92		if ( ariaState === 'true' ) {
93			ariaState = 'false';
94		} else {
95			ariaState = 'true';
96		}
97
98		ariaItem.setAttribute('aria-expanded', ariaState);
99	}
100
101	/**
102	 * Open sub-menu.
103	 *
104	 * @param {Object} currentSubMenu
105	 */
106	function openSubMenu( currentSubMenu ) {
107		'use strict';
108
109		// Update classes.
110		// classList.add is not supported in IE11.
111		currentSubMenu.parentElement.className += ' off-canvas';
112		currentSubMenu.parentElement.lastElementChild.className += ' expanded-true';
113
114		// Update aria-expanded state.
115		toggleAriaExpandedState( currentSubMenu );
116	}
117
118	/**
119	 * Close sub-menu.
120	 *
121	 * @param {Object} currentSubMenu
122	 */
123	function closeSubMenu( currentSubMenu ) {
124		'use strict';
125
126		var menuItem     = getCurrentParent( currentSubMenu, '.menu-item' ); // this.parentNode
127		var menuItemAria = menuItem.querySelector('a[aria-expanded]');
128		var subMenu      = currentSubMenu.closest('.sub-menu');
129
130		// If this is in a sub-sub-menu, go back to parent sub-menu.
131		if ( getCurrentParent( currentSubMenu, 'ul' ).classList.contains( 'sub-menu' ) ) {
132
133			// Update classes.
134			// classList.remove is not supported in IE11.
135			menuItem.className = menuItem.className.replace( 'off-canvas', '' );
136			subMenu.className  = subMenu.className.replace( 'expanded-true', '' );
137
138			// Update aria-expanded and :focus states.
139			toggleAriaExpandedState( menuItemAria );
140
141		// Or else close all sub-menus.
142		} else {
143
144			// Update classes.
145			// classList.remove is not supported in IE11.
146			menuItem.className = menuItem.className.replace( 'off-canvas', '' );
147			menuItem.lastElementChild.className = menuItem.lastElementChild.className.replace( 'expanded-true', '' );
148
149			// Update aria-expanded and :focus states.
150			toggleAriaExpandedState( menuItemAria );
151		}
152	}
153
154	/**
155	 * Find first ancestor of an element by selector.
156	 *
157	 * @param {Object} child
158	 * @param {String} selector
159	 * @param {String} stopSelector
160	 */
161	function getCurrentParent( child, selector, stopSelector ) {
162
163		var currentParent = null;
164
165		while ( child ) {
166
167			if ( child.matches(selector) ) {
168
169				currentParent = child;
170				break;
171
172			} else if ( stopSelector && child.matches(stopSelector) ) {
173
174				break;
175			}
176
177			child = child.parentElement;
178		}
179
180		return currentParent;
181	}
182
183	/**
184	 * Remove all off-canvas states.
185	 */
186	function removeAllFocusStates() {
187		'use strict';
188
189		var siteBranding            = document.getElementsByClassName( 'site-branding' )[0];
190		var getFocusedElements      = siteBranding.querySelectorAll(':hover, :focus, :focus-within');
191		var getFocusedClassElements = siteBranding.querySelectorAll('.is-focused');
192		var i;
193		var o;
194
195		for ( i = 0; i < getFocusedElements.length; i++) {
196			getFocusedElements[i].blur();
197		}
198
199		for ( o = 0; o < getFocusedClassElements.length; o++) {
200			deleteClass( getFocusedClassElements[o], 'is-focused' );
201		}
202	}
203
204	/**
205	 * Matches polyfill for IE11.
206	 */
207	if (!Element.prototype.matches) {
208		Element.prototype.matches = Element.prototype.msMatchesSelector;
209	}
210
211	/**
212	 * Toggle `focus` class to allow sub-menu access on touch screens.
213	 */
214	function toggleSubmenuDisplay() {
215
216		document.addEventListener('touchstart', function(event) {
217
218			if ( event.target.matches('a') ) {
219
220				var url = event.target.getAttribute( 'href' ) ? event.target.getAttribute( 'href' ) : '';
221
222				// Open submenu if URL is #.
223				if ( '#' === url && event.target.nextSibling.matches('.submenu-expand') ) {
224					openSubMenu( event.target );
225				}
226			}
227
228			// Check if .submenu-expand is touched.
229			if ( event.target.matches('.submenu-expand') ) {
230				openSubMenu(event.target);
231
232			// Check if child of .submenu-expand is touched.
233			} else if ( null != getCurrentParent( event.target, '.submenu-expand' ) &&
234								getCurrentParent( event.target, '.submenu-expand' ).matches( '.submenu-expand' ) ) {
235				openSubMenu( getCurrentParent( event.target, '.submenu-expand' ) );
236
237			// Check if .menu-item-link-return is touched.
238			} else if ( event.target.matches('.menu-item-link-return') ) {
239				closeSubMenu( event.target );
240
241			// Check if child of .menu-item-link-return is touched.
242			} else if ( null != getCurrentParent( event.target, '.menu-item-link-return' ) && getCurrentParent( event.target, '.menu-item-link-return' ).matches( '.menu-item-link-return' ) ) {
243				closeSubMenu( event.target );
244			}
245
246			// Prevent default mouse/focus events.
247			removeAllFocusStates();
248
249		}, false);
250
251		document.addEventListener('touchend', function(event) {
252
253			var mainNav = getCurrentParent( event.target, '.main-navigation' );
254
255			if ( null != mainNav && hasClass( mainNav, '.main-navigation' ) ) {
256				// Prevent default mouse events.
257				event.preventDefault();
258
259			} else if (
260				event.target.matches('.submenu-expand') ||
261				null != getCurrentParent( event.target, '.submenu-expand' ) &&
262				getCurrentParent( event.target, '.submenu-expand' ).matches( '.submenu-expand' ) ||
263				event.target.matches('.menu-item-link-return') ||
264				null != getCurrentParent( event.target, '.menu-item-link-return' ) &&
265				getCurrentParent( event.target, '.menu-item-link-return' ).matches( '.menu-item-link-return' ) ) {
266					// Prevent default mouse events.
267					event.preventDefault();
268			}
269
270			// Prevent default mouse/focus events.
271			removeAllFocusStates();
272
273		}, false);
274
275		document.addEventListener('focus', function(event) {
276
277			if ( event.target !== window.document && event.target.matches( '.main-navigation > div > ul > li a' ) ) {
278
279				// Remove Focused elements in sibling div.
280				var currentDiv        = getCurrentParent( event.target, 'div', '.main-navigation' );
281				var currentDivSibling = currentDiv.previousElementSibling === null ? currentDiv.nextElementSibling : currentDiv.previousElementSibling;
282				var focusedElement    = currentDivSibling.querySelector( '.is-focused' );
283				var focusedClass      = 'is-focused';
284				var prevLi            = getCurrentParent( event.target, '.main-navigation > div > ul > li', '.main-navigation' ).previousElementSibling;
285				var nextLi            = getCurrentParent( event.target, '.main-navigation > div > ul > li', '.main-navigation' ).nextElementSibling;
286
287				if ( null !== focusedElement && null !== hasClass( focusedElement, focusedClass ) ) {
288					deleteClass( focusedElement, focusedClass );
289				}
290
291				// Add .is-focused class to top-level li.
292				if ( getCurrentParent( event.target, '.main-navigation > div > ul > li', '.main-navigation' ) ) {
293					addClass( getCurrentParent( event.target, '.main-navigation > div > ul > li', '.main-navigation' ), focusedClass );
294				}
295
296				// Check for previous li.
297				if ( prevLi && hasClass( prevLi, focusedClass ) ) {
298					deleteClass( prevLi, focusedClass );
299				}
300
301				// Check for next li.
302				if ( nextLi && hasClass( nextLi, focusedClass ) ) {
303					deleteClass( nextLi, focusedClass );
304				}
305			}
306
307		}, true);
308
309		document.addEventListener('click', function(event) {
310
311			// Remove all focused menu states when clicking outside site branding.
312			if ( event.target !== document.getElementsByClassName( 'site-branding' )[0] ) {
313				removeAllFocusStates();
314			} else {
315				// Nothing.
316			}
317
318		}, false);
319	}
320
321	/**
322	 * Run our sub-menu function as soon as the document is `ready`.
323	 */
324	document.addEventListener( 'DOMContentLoaded', function() {
325		toggleSubmenuDisplay();
326	});
327
328	/**
329	 * Run our sub-menu function on selective refresh in the customizer.
330	 */
331	document.addEventListener( 'customize-preview-menu-refreshed', function( e, params ) {
332		if ( 'menu-1' === params.wpNavMenuArgs.theme_location ) {
333			toggleSubmenuDisplay();
334		}
335	});
336
337	/**
338	 * Run our sub-menu function every time the window resizes.
339	 */
340	var isResizing = false;
341	window.addEventListener( 'resize', function() {
342		isResizing = true;
343		debounce( function() {
344			if ( isResizing ) {
345				return;
346			}
347
348			toggleSubmenuDisplay();
349			isResizing = false;
350
351		}, 150 );
352	} );
353
354})();

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.