|
1 import * as __WEBPACK_EXTERNAL_MODULE__wordpress_interactivity_8e89b257__ from "@wordpress/interactivity"; |
|
2 /******/ var __webpack_modules__ = ({ |
|
3 |
|
4 /***/ 317: |
|
5 /***/ ((module) => { |
|
6 |
|
7 module.exports = import("@wordpress/a11y");; |
|
8 |
|
9 /***/ }) |
|
10 |
|
11 /******/ }); |
|
12 /************************************************************************/ |
|
13 /******/ // The module cache |
|
14 /******/ var __webpack_module_cache__ = {}; |
|
15 /******/ |
|
16 /******/ // The require function |
|
17 /******/ function __webpack_require__(moduleId) { |
|
18 /******/ // Check if module is in cache |
|
19 /******/ var cachedModule = __webpack_module_cache__[moduleId]; |
|
20 /******/ if (cachedModule !== undefined) { |
|
21 /******/ return cachedModule.exports; |
|
22 /******/ } |
|
23 /******/ // Create a new module (and put it into the cache) |
|
24 /******/ var module = __webpack_module_cache__[moduleId] = { |
|
25 /******/ // no module.id needed |
|
26 /******/ // no module.loaded needed |
|
27 /******/ exports: {} |
|
28 /******/ }; |
|
29 /******/ |
|
30 /******/ // Execute the module function |
|
31 /******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); |
|
32 /******/ |
|
33 /******/ // Return the exports of the module |
|
34 /******/ return module.exports; |
|
35 /******/ } |
|
36 /******/ |
|
37 /************************************************************************/ |
|
38 /******/ /* webpack/runtime/define property getters */ |
|
39 /******/ (() => { |
|
40 /******/ // define getter functions for harmony exports |
|
41 /******/ __webpack_require__.d = (exports, definition) => { |
|
42 /******/ for(var key in definition) { |
|
43 /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { |
|
44 /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); |
|
45 /******/ } |
|
46 /******/ } |
|
47 /******/ }; |
|
48 /******/ })(); |
|
49 /******/ |
|
50 /******/ /* webpack/runtime/hasOwnProperty shorthand */ |
|
51 /******/ (() => { |
|
52 /******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) |
|
53 /******/ })(); |
|
54 /******/ |
|
55 /************************************************************************/ |
|
56 var __webpack_exports__ = {}; |
|
57 |
|
58 // EXPORTS |
|
59 __webpack_require__.d(__webpack_exports__, { |
|
60 o: () => (/* binding */ actions), |
|
61 w: () => (/* binding */ state) |
|
62 }); |
|
63 |
|
64 ;// external "@wordpress/interactivity" |
|
65 var x = (y) => { |
|
66 var x = {}; __webpack_require__.d(x, y); return x |
|
67 } |
|
68 var y = (x) => (() => (x)) |
|
69 const interactivity_namespaceObject = x({ ["getConfig"]: () => (__WEBPACK_EXTERNAL_MODULE__wordpress_interactivity_8e89b257__.getConfig), ["privateApis"]: () => (__WEBPACK_EXTERNAL_MODULE__wordpress_interactivity_8e89b257__.privateApis), ["store"]: () => (__WEBPACK_EXTERNAL_MODULE__wordpress_interactivity_8e89b257__.store) }); |
|
70 ;// ./node_modules/@wordpress/interactivity-router/build-module/head.js |
|
71 /** |
|
72 * The cache of prefetched stylesheets and scripts. |
|
73 */ |
|
74 const headElements = new Map(); |
|
75 |
|
76 /** |
|
77 * Helper to update only the necessary tags in the head. |
|
78 * |
|
79 * @async |
|
80 * @param newHead The head elements of the new page. |
|
81 */ |
|
82 const updateHead = async newHead => { |
|
83 // Helper to get the tag id store in the cache. |
|
84 const getTagId = tag => tag.id || tag.outerHTML; |
|
85 |
|
86 // Map incoming head tags by their content. |
|
87 const newHeadMap = new Map(); |
|
88 for (const child of newHead) { |
|
89 newHeadMap.set(getTagId(child), child); |
|
90 } |
|
91 const toRemove = []; |
|
92 |
|
93 // Detect nodes that should be added or removed. |
|
94 for (const child of document.head.children) { |
|
95 const id = getTagId(child); |
|
96 // Always remove styles and links as they might change. |
|
97 if (child.nodeName === 'LINK' || child.nodeName === 'STYLE') { |
|
98 toRemove.push(child); |
|
99 } else if (newHeadMap.has(id)) { |
|
100 newHeadMap.delete(id); |
|
101 } else if (child.nodeName !== 'SCRIPT' && child.nodeName !== 'META') { |
|
102 toRemove.push(child); |
|
103 } |
|
104 } |
|
105 await Promise.all([...headElements.entries()].filter(([, { |
|
106 tag |
|
107 }]) => tag.nodeName === 'SCRIPT').map(async ([url]) => { |
|
108 await import(/* webpackIgnore: true */url); |
|
109 })); |
|
110 |
|
111 // Prepare new assets. |
|
112 const toAppend = [...newHeadMap.values()]; |
|
113 |
|
114 // Apply the changes. |
|
115 toRemove.forEach(n => n.remove()); |
|
116 document.head.append(...toAppend); |
|
117 }; |
|
118 |
|
119 /** |
|
120 * Fetches and processes head assets (stylesheets and scripts) from a specified document. |
|
121 * |
|
122 * @async |
|
123 * @param doc The document from which to fetch head assets. It should support standard DOM querying methods. |
|
124 * |
|
125 * @return Returns an array of HTML elements representing the head assets. |
|
126 */ |
|
127 const fetchHeadAssets = async doc => { |
|
128 const headTags = []; |
|
129 |
|
130 // We only want to fetch module scripts because regular scripts (without |
|
131 // `async` or `defer` attributes) can depend on the execution of other scripts. |
|
132 // Scripts found in the head are blocking and must be executed in order. |
|
133 const scripts = doc.querySelectorAll('script[type="module"][src]'); |
|
134 scripts.forEach(script => { |
|
135 const src = script.getAttribute('src'); |
|
136 if (!headElements.has(src)) { |
|
137 // add the <link> elements to prefetch the module scripts |
|
138 const link = doc.createElement('link'); |
|
139 link.rel = 'modulepreload'; |
|
140 link.href = src; |
|
141 document.head.append(link); |
|
142 headElements.set(src, { |
|
143 tag: script |
|
144 }); |
|
145 } |
|
146 }); |
|
147 const stylesheets = doc.querySelectorAll('link[rel=stylesheet]'); |
|
148 await Promise.all(Array.from(stylesheets).map(async tag => { |
|
149 const href = tag.getAttribute('href'); |
|
150 if (!href) { |
|
151 return; |
|
152 } |
|
153 if (!headElements.has(href)) { |
|
154 try { |
|
155 const response = await fetch(href); |
|
156 const text = await response.text(); |
|
157 headElements.set(href, { |
|
158 tag, |
|
159 text |
|
160 }); |
|
161 } catch (e) { |
|
162 // eslint-disable-next-line no-console |
|
163 console.error(e); |
|
164 } |
|
165 } |
|
166 const headElement = headElements.get(href); |
|
167 const styleElement = doc.createElement('style'); |
|
168 styleElement.textContent = headElement.text; |
|
169 headTags.push(styleElement); |
|
170 })); |
|
171 return [doc.querySelector('title'), ...doc.querySelectorAll('style'), ...headTags]; |
|
172 }; |
|
173 |
|
174 ;// ./node_modules/@wordpress/interactivity-router/build-module/index.js |
|
175 var _getConfig$navigation; |
|
176 /** |
|
177 * WordPress dependencies |
|
178 */ |
|
179 |
|
180 |
|
181 /** |
|
182 * Internal dependencies |
|
183 */ |
|
184 |
|
185 const { |
|
186 directivePrefix, |
|
187 getRegionRootFragment, |
|
188 initialVdom, |
|
189 toVdom, |
|
190 render, |
|
191 parseServerData, |
|
192 populateServerData, |
|
193 batch |
|
194 } = (0,interactivity_namespaceObject.privateApis)('I acknowledge that using private APIs means my theme or plugin will inevitably break in the next version of WordPress.'); |
|
195 // Check if the navigation mode is full page or region based. |
|
196 const navigationMode = (_getConfig$navigation = (0,interactivity_namespaceObject.getConfig)('core/router').navigationMode) !== null && _getConfig$navigation !== void 0 ? _getConfig$navigation : 'regionBased'; |
|
197 |
|
198 // The cache of visited and prefetched pages, stylesheets and scripts. |
|
199 const pages = new Map(); |
|
200 |
|
201 // Helper to remove domain and hash from the URL. We are only interesting in |
|
202 // caching the path and the query. |
|
203 const getPagePath = url => { |
|
204 const u = new URL(url, window.location.href); |
|
205 return u.pathname + u.search; |
|
206 }; |
|
207 |
|
208 // Fetch a new page and convert it to a static virtual DOM. |
|
209 const fetchPage = async (url, { |
|
210 html |
|
211 }) => { |
|
212 try { |
|
213 if (!html) { |
|
214 const res = await window.fetch(url); |
|
215 if (res.status !== 200) { |
|
216 return false; |
|
217 } |
|
218 html = await res.text(); |
|
219 } |
|
220 const dom = new window.DOMParser().parseFromString(html, 'text/html'); |
|
221 return regionsToVdom(dom); |
|
222 } catch (e) { |
|
223 return false; |
|
224 } |
|
225 }; |
|
226 |
|
227 // Return an object with VDOM trees of those HTML regions marked with a |
|
228 // `router-region` directive. |
|
229 const regionsToVdom = async (dom, { |
|
230 vdom |
|
231 } = {}) => { |
|
232 const regions = { |
|
233 body: undefined |
|
234 }; |
|
235 let head; |
|
236 if (false) {} |
|
237 if (navigationMode === 'regionBased') { |
|
238 const attrName = `data-${directivePrefix}-router-region`; |
|
239 dom.querySelectorAll(`[${attrName}]`).forEach(region => { |
|
240 const id = region.getAttribute(attrName); |
|
241 regions[id] = vdom?.has(region) ? vdom.get(region) : toVdom(region); |
|
242 }); |
|
243 } |
|
244 const title = dom.querySelector('title')?.innerText; |
|
245 const initialData = parseServerData(dom); |
|
246 return { |
|
247 regions, |
|
248 head, |
|
249 title, |
|
250 initialData |
|
251 }; |
|
252 }; |
|
253 |
|
254 // Render all interactive regions contained in the given page. |
|
255 const renderRegions = async page => { |
|
256 if (false) {} |
|
257 if (navigationMode === 'regionBased') { |
|
258 const attrName = `data-${directivePrefix}-router-region`; |
|
259 batch(() => { |
|
260 populateServerData(page.initialData); |
|
261 document.querySelectorAll(`[${attrName}]`).forEach(region => { |
|
262 const id = region.getAttribute(attrName); |
|
263 const fragment = getRegionRootFragment(region); |
|
264 render(page.regions[id], fragment); |
|
265 }); |
|
266 }); |
|
267 } |
|
268 if (page.title) { |
|
269 document.title = page.title; |
|
270 } |
|
271 }; |
|
272 |
|
273 /** |
|
274 * Load the given page forcing a full page reload. |
|
275 * |
|
276 * The function returns a promise that won't resolve, useful to prevent any |
|
277 * potential feedback indicating that the navigation has finished while the new |
|
278 * page is being loaded. |
|
279 * |
|
280 * @param href The page href. |
|
281 * @return Promise that never resolves. |
|
282 */ |
|
283 const forcePageReload = href => { |
|
284 window.location.assign(href); |
|
285 return new Promise(() => {}); |
|
286 }; |
|
287 |
|
288 // Listen to the back and forward buttons and restore the page if it's in the |
|
289 // cache. |
|
290 window.addEventListener('popstate', async () => { |
|
291 const pagePath = getPagePath(window.location.href); // Remove hash. |
|
292 const page = pages.has(pagePath) && (await pages.get(pagePath)); |
|
293 if (page) { |
|
294 await renderRegions(page); |
|
295 // Update the URL in the state. |
|
296 state.url = window.location.href; |
|
297 } else { |
|
298 window.location.reload(); |
|
299 } |
|
300 }); |
|
301 |
|
302 // Initialize the router and cache the initial page using the initial vDOM. |
|
303 // Once this code is tested and more mature, the head should be updated for |
|
304 // region based navigation as well. |
|
305 if (false) {} |
|
306 pages.set(getPagePath(window.location.href), Promise.resolve(regionsToVdom(document, { |
|
307 vdom: initialVdom |
|
308 }))); |
|
309 |
|
310 // Check if the link is valid for client-side navigation. |
|
311 const isValidLink = ref => ref && ref instanceof window.HTMLAnchorElement && ref.href && (!ref.target || ref.target === '_self') && ref.origin === window.location.origin && !ref.pathname.startsWith('/wp-admin') && !ref.pathname.startsWith('/wp-login.php') && !ref.getAttribute('href').startsWith('#') && !new URL(ref.href).searchParams.has('_wpnonce'); |
|
312 |
|
313 // Check if the event is valid for client-side navigation. |
|
314 const isValidEvent = event => event && event.button === 0 && |
|
315 // Left clicks only. |
|
316 !event.metaKey && |
|
317 // Open in new tab (Mac). |
|
318 !event.ctrlKey && |
|
319 // Open in new tab (Windows). |
|
320 !event.altKey && |
|
321 // Download. |
|
322 !event.shiftKey && !event.defaultPrevented; |
|
323 |
|
324 // Variable to store the current navigation. |
|
325 let navigatingTo = ''; |
|
326 let hasLoadedNavigationTextsData = false; |
|
327 const navigationTexts = { |
|
328 loading: 'Loading page, please wait.', |
|
329 loaded: 'Page Loaded.' |
|
330 }; |
|
331 const { |
|
332 state, |
|
333 actions |
|
334 } = (0,interactivity_namespaceObject.store)('core/router', { |
|
335 state: { |
|
336 url: window.location.href, |
|
337 navigation: { |
|
338 hasStarted: false, |
|
339 hasFinished: false |
|
340 } |
|
341 }, |
|
342 actions: { |
|
343 /** |
|
344 * Navigates to the specified page. |
|
345 * |
|
346 * This function normalizes the passed href, fetches the page HTML if |
|
347 * needed, and updates any interactive regions whose contents have |
|
348 * changed. It also creates a new entry in the browser session history. |
|
349 * |
|
350 * @param href The page href. |
|
351 * @param [options] Options object. |
|
352 * @param [options.force] If true, it forces re-fetching the URL. |
|
353 * @param [options.html] HTML string to be used instead of fetching the requested URL. |
|
354 * @param [options.replace] If true, it replaces the current entry in the browser session history. |
|
355 * @param [options.timeout] Time until the navigation is aborted, in milliseconds. Default is 10000. |
|
356 * @param [options.loadingAnimation] Whether an animation should be shown while navigating. Default to `true`. |
|
357 * @param [options.screenReaderAnnouncement] Whether a message for screen readers should be announced while navigating. Default to `true`. |
|
358 * |
|
359 * @return Promise that resolves once the navigation is completed or aborted. |
|
360 */ |
|
361 *navigate(href, options = {}) { |
|
362 const { |
|
363 clientNavigationDisabled |
|
364 } = (0,interactivity_namespaceObject.getConfig)(); |
|
365 if (clientNavigationDisabled) { |
|
366 yield forcePageReload(href); |
|
367 } |
|
368 const pagePath = getPagePath(href); |
|
369 const { |
|
370 navigation |
|
371 } = state; |
|
372 const { |
|
373 loadingAnimation = true, |
|
374 screenReaderAnnouncement = true, |
|
375 timeout = 10000 |
|
376 } = options; |
|
377 navigatingTo = href; |
|
378 actions.prefetch(pagePath, options); |
|
379 |
|
380 // Create a promise that resolves when the specified timeout ends. |
|
381 // The timeout value is 10 seconds by default. |
|
382 const timeoutPromise = new Promise(resolve => setTimeout(resolve, timeout)); |
|
383 |
|
384 // Don't update the navigation status immediately, wait 400 ms. |
|
385 const loadingTimeout = setTimeout(() => { |
|
386 if (navigatingTo !== href) { |
|
387 return; |
|
388 } |
|
389 if (loadingAnimation) { |
|
390 navigation.hasStarted = true; |
|
391 navigation.hasFinished = false; |
|
392 } |
|
393 if (screenReaderAnnouncement) { |
|
394 a11ySpeak('loading'); |
|
395 } |
|
396 }, 400); |
|
397 const page = yield Promise.race([pages.get(pagePath), timeoutPromise]); |
|
398 |
|
399 // Dismiss loading message if it hasn't been added yet. |
|
400 clearTimeout(loadingTimeout); |
|
401 |
|
402 // Once the page is fetched, the destination URL could have changed |
|
403 // (e.g., by clicking another link in the meantime). If so, bail |
|
404 // out, and let the newer execution to update the HTML. |
|
405 if (navigatingTo !== href) { |
|
406 return; |
|
407 } |
|
408 if (page && !page.initialData?.config?.['core/router']?.clientNavigationDisabled) { |
|
409 yield renderRegions(page); |
|
410 window.history[options.replace ? 'replaceState' : 'pushState']({}, '', href); |
|
411 |
|
412 // Update the URL in the state. |
|
413 state.url = href; |
|
414 |
|
415 // Update the navigation status once the the new page rendering |
|
416 // has been completed. |
|
417 if (loadingAnimation) { |
|
418 navigation.hasStarted = false; |
|
419 navigation.hasFinished = true; |
|
420 } |
|
421 if (screenReaderAnnouncement) { |
|
422 a11ySpeak('loaded'); |
|
423 } |
|
424 |
|
425 // Scroll to the anchor if exits in the link. |
|
426 const { |
|
427 hash |
|
428 } = new URL(href, window.location.href); |
|
429 if (hash) { |
|
430 document.querySelector(hash)?.scrollIntoView(); |
|
431 } |
|
432 } else { |
|
433 yield forcePageReload(href); |
|
434 } |
|
435 }, |
|
436 /** |
|
437 * Prefetches the page with the passed URL. |
|
438 * |
|
439 * The function normalizes the URL and stores internally the fetch |
|
440 * promise, to avoid triggering a second fetch for an ongoing request. |
|
441 * |
|
442 * @param url The page URL. |
|
443 * @param [options] Options object. |
|
444 * @param [options.force] Force fetching the URL again. |
|
445 * @param [options.html] HTML string to be used instead of fetching the requested URL. |
|
446 */ |
|
447 prefetch(url, options = {}) { |
|
448 const { |
|
449 clientNavigationDisabled |
|
450 } = (0,interactivity_namespaceObject.getConfig)(); |
|
451 if (clientNavigationDisabled) { |
|
452 return; |
|
453 } |
|
454 const pagePath = getPagePath(url); |
|
455 if (options.force || !pages.has(pagePath)) { |
|
456 pages.set(pagePath, fetchPage(pagePath, { |
|
457 html: options.html |
|
458 })); |
|
459 } |
|
460 } |
|
461 } |
|
462 }); |
|
463 |
|
464 /** |
|
465 * Announces a message to screen readers. |
|
466 * |
|
467 * This is a wrapper around the `@wordpress/a11y` package's `speak` function. It handles importing |
|
468 * the package on demand and should be used instead of calling `ally.speak` direacly. |
|
469 * |
|
470 * @param messageKey The message to be announced by assistive technologies. |
|
471 */ |
|
472 function a11ySpeak(messageKey) { |
|
473 if (!hasLoadedNavigationTextsData) { |
|
474 hasLoadedNavigationTextsData = true; |
|
475 const content = document.getElementById('wp-script-module-data-@wordpress/interactivity-router')?.textContent; |
|
476 if (content) { |
|
477 try { |
|
478 const parsed = JSON.parse(content); |
|
479 if (typeof parsed?.i18n?.loading === 'string') { |
|
480 navigationTexts.loading = parsed.i18n.loading; |
|
481 } |
|
482 if (typeof parsed?.i18n?.loaded === 'string') { |
|
483 navigationTexts.loaded = parsed.i18n.loaded; |
|
484 } |
|
485 } catch {} |
|
486 } else { |
|
487 // Fallback to localized strings from Interactivity API state. |
|
488 // @todo This block is for Core < 6.7.0. Remove when support is dropped. |
|
489 |
|
490 // @ts-expect-error |
|
491 if (state.navigation.texts?.loading) { |
|
492 // @ts-expect-error |
|
493 navigationTexts.loading = state.navigation.texts.loading; |
|
494 } |
|
495 // @ts-expect-error |
|
496 if (state.navigation.texts?.loaded) { |
|
497 // @ts-expect-error |
|
498 navigationTexts.loaded = state.navigation.texts.loaded; |
|
499 } |
|
500 } |
|
501 } |
|
502 const message = navigationTexts[messageKey]; |
|
503 Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, 317)).then(({ |
|
504 speak |
|
505 }) => speak(message), |
|
506 // Ignore failures to load the a11y module. |
|
507 () => {}); |
|
508 } |
|
509 |
|
510 // Add click and prefetch to all links. |
|
511 if (false) {} |
|
512 |
|
513 var __webpack_exports__actions = __webpack_exports__.o; |
|
514 var __webpack_exports__state = __webpack_exports__.w; |
|
515 export { __webpack_exports__actions as actions, __webpack_exports__state as state }; |