|
1 import * as __WEBPACK_EXTERNAL_MODULE__wordpress_interactivity_8e89b257__ from "@wordpress/interactivity"; |
|
2 /******/ // The require scope |
|
3 /******/ var __webpack_require__ = {}; |
|
4 /******/ |
|
5 /************************************************************************/ |
|
6 /******/ /* webpack/runtime/define property getters */ |
|
7 /******/ (() => { |
|
8 /******/ // define getter functions for harmony exports |
|
9 /******/ __webpack_require__.d = (exports, definition) => { |
|
10 /******/ for(var key in definition) { |
|
11 /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { |
|
12 /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); |
|
13 /******/ } |
|
14 /******/ } |
|
15 /******/ }; |
|
16 /******/ })(); |
|
17 /******/ |
|
18 /******/ /* webpack/runtime/hasOwnProperty shorthand */ |
|
19 /******/ (() => { |
|
20 /******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) |
|
21 /******/ })(); |
|
22 /******/ |
|
23 /************************************************************************/ |
|
24 var __webpack_exports__ = {}; |
|
25 |
|
26 // EXPORTS |
|
27 __webpack_require__.d(__webpack_exports__, { |
|
28 o: () => (/* binding */ actions), |
|
29 w: () => (/* binding */ state) |
|
30 }); |
|
31 |
|
32 ;// CONCATENATED MODULE: external "@wordpress/interactivity" |
|
33 var x = (y) => { |
|
34 var x = {}; __webpack_require__.d(x, y); return x |
|
35 } |
|
36 var y = (x) => (() => (x)) |
|
37 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) }); |
|
38 ;// CONCATENATED MODULE: ./node_modules/@wordpress/interactivity-router/build-module/index.js |
|
39 var _getConfig$navigation; |
|
40 /** |
|
41 * WordPress dependencies |
|
42 */ |
|
43 |
|
44 |
|
45 /** |
|
46 * Internal dependencies |
|
47 */ |
|
48 |
|
49 const { |
|
50 directivePrefix, |
|
51 getRegionRootFragment, |
|
52 initialVdom, |
|
53 toVdom, |
|
54 render, |
|
55 parseInitialData, |
|
56 populateInitialData, |
|
57 batch |
|
58 } = (0,interactivity_namespaceObject.privateApis)('I acknowledge that using private APIs means my theme or plugin will inevitably break in the next version of WordPress.'); |
|
59 // Check if the navigation mode is full page or region based. |
|
60 const navigationMode = (_getConfig$navigation = (0,interactivity_namespaceObject.getConfig)('core/router').navigationMode) !== null && _getConfig$navigation !== void 0 ? _getConfig$navigation : 'regionBased'; |
|
61 |
|
62 // The cache of visited and prefetched pages, stylesheets and scripts. |
|
63 const pages = new Map(); |
|
64 const headElements = new Map(); |
|
65 |
|
66 // Helper to remove domain and hash from the URL. We are only interesting in |
|
67 // caching the path and the query. |
|
68 const getPagePath = url => { |
|
69 const u = new URL(url, window.location.href); |
|
70 return u.pathname + u.search; |
|
71 }; |
|
72 |
|
73 // Fetch a new page and convert it to a static virtual DOM. |
|
74 const fetchPage = async (url, { |
|
75 html |
|
76 }) => { |
|
77 try { |
|
78 if (!html) { |
|
79 const res = await window.fetch(url); |
|
80 if (res.status !== 200) { |
|
81 return false; |
|
82 } |
|
83 html = await res.text(); |
|
84 } |
|
85 const dom = new window.DOMParser().parseFromString(html, 'text/html'); |
|
86 return regionsToVdom(dom); |
|
87 } catch (e) { |
|
88 return false; |
|
89 } |
|
90 }; |
|
91 |
|
92 // Return an object with VDOM trees of those HTML regions marked with a |
|
93 // `router-region` directive. |
|
94 const regionsToVdom = async (dom, { |
|
95 vdom |
|
96 } = {}) => { |
|
97 const regions = { |
|
98 body: undefined |
|
99 }; |
|
100 let head; |
|
101 if (false) {} |
|
102 if (navigationMode === 'regionBased') { |
|
103 const attrName = `data-${directivePrefix}-router-region`; |
|
104 dom.querySelectorAll(`[${attrName}]`).forEach(region => { |
|
105 const id = region.getAttribute(attrName); |
|
106 regions[id] = vdom?.has(region) ? vdom.get(region) : toVdom(region); |
|
107 }); |
|
108 } |
|
109 const title = dom.querySelector('title')?.innerText; |
|
110 const initialData = parseInitialData(dom); |
|
111 return { |
|
112 regions, |
|
113 head, |
|
114 title, |
|
115 initialData |
|
116 }; |
|
117 }; |
|
118 |
|
119 // Render all interactive regions contained in the given page. |
|
120 const renderRegions = page => { |
|
121 batch(() => { |
|
122 if (false) {} |
|
123 if (navigationMode === 'regionBased') { |
|
124 populateInitialData(page.initialData); |
|
125 const attrName = `data-${directivePrefix}-router-region`; |
|
126 document.querySelectorAll(`[${attrName}]`).forEach(region => { |
|
127 const id = region.getAttribute(attrName); |
|
128 const fragment = getRegionRootFragment(region); |
|
129 render(page.regions[id], fragment); |
|
130 }); |
|
131 } |
|
132 if (page.title) { |
|
133 document.title = page.title; |
|
134 } |
|
135 }); |
|
136 }; |
|
137 |
|
138 /** |
|
139 * Load the given page forcing a full page reload. |
|
140 * |
|
141 * The function returns a promise that won't resolve, useful to prevent any |
|
142 * potential feedback indicating that the navigation has finished while the new |
|
143 * page is being loaded. |
|
144 * |
|
145 * @param href The page href. |
|
146 * @return Promise that never resolves. |
|
147 */ |
|
148 const forcePageReload = href => { |
|
149 window.location.assign(href); |
|
150 return new Promise(() => {}); |
|
151 }; |
|
152 |
|
153 // Listen to the back and forward buttons and restore the page if it's in the |
|
154 // cache. |
|
155 window.addEventListener('popstate', async () => { |
|
156 const pagePath = getPagePath(window.location.href); // Remove hash. |
|
157 const page = pages.has(pagePath) && (await pages.get(pagePath)); |
|
158 if (page) { |
|
159 renderRegions(page); |
|
160 // Update the URL in the state. |
|
161 state.url = window.location.href; |
|
162 } else { |
|
163 window.location.reload(); |
|
164 } |
|
165 }); |
|
166 |
|
167 // Initialize the router and cache the initial page using the initial vDOM. |
|
168 // Once this code is tested and more mature, the head should be updated for |
|
169 // region based navigation as well. |
|
170 if (false) {} |
|
171 pages.set(getPagePath(window.location.href), Promise.resolve(regionsToVdom(document, { |
|
172 vdom: initialVdom |
|
173 }))); |
|
174 |
|
175 // Check if the link is valid for client-side navigation. |
|
176 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'); |
|
177 |
|
178 // Check if the event is valid for client-side navigation. |
|
179 const isValidEvent = event => event && event.button === 0 && |
|
180 // Left clicks only. |
|
181 !event.metaKey && |
|
182 // Open in new tab (Mac). |
|
183 !event.ctrlKey && |
|
184 // Open in new tab (Windows). |
|
185 !event.altKey && |
|
186 // Download. |
|
187 !event.shiftKey && !event.defaultPrevented; |
|
188 |
|
189 // Variable to store the current navigation. |
|
190 let navigatingTo = ''; |
|
191 const { |
|
192 state, |
|
193 actions |
|
194 } = (0,interactivity_namespaceObject.store)('core/router', { |
|
195 state: { |
|
196 url: window.location.href, |
|
197 navigation: { |
|
198 hasStarted: false, |
|
199 hasFinished: false, |
|
200 texts: { |
|
201 loading: '', |
|
202 loaded: '' |
|
203 }, |
|
204 message: '' |
|
205 } |
|
206 }, |
|
207 actions: { |
|
208 /** |
|
209 * Navigates to the specified page. |
|
210 * |
|
211 * This function normalizes the passed href, fetchs the page HTML if |
|
212 * needed, and updates any interactive regions whose contents have |
|
213 * changed. It also creates a new entry in the browser session history. |
|
214 * |
|
215 * @param href The page href. |
|
216 * @param [options] Options object. |
|
217 * @param [options.force] If true, it forces re-fetching the URL. |
|
218 * @param [options.html] HTML string to be used instead of fetching the requested URL. |
|
219 * @param [options.replace] If true, it replaces the current entry in the browser session history. |
|
220 * @param [options.timeout] Time until the navigation is aborted, in milliseconds. Default is 10000. |
|
221 * @param [options.loadingAnimation] Whether an animation should be shown while navigating. Default to `true`. |
|
222 * @param [options.screenReaderAnnouncement] Whether a message for screen readers should be announced while navigating. Default to `true`. |
|
223 * |
|
224 * @return Promise that resolves once the navigation is completed or aborted. |
|
225 */ |
|
226 *navigate(href, options = {}) { |
|
227 const { |
|
228 clientNavigationDisabled |
|
229 } = (0,interactivity_namespaceObject.getConfig)(); |
|
230 if (clientNavigationDisabled) { |
|
231 yield forcePageReload(href); |
|
232 } |
|
233 const pagePath = getPagePath(href); |
|
234 const { |
|
235 navigation |
|
236 } = state; |
|
237 const { |
|
238 loadingAnimation = true, |
|
239 screenReaderAnnouncement = true, |
|
240 timeout = 10000 |
|
241 } = options; |
|
242 navigatingTo = href; |
|
243 actions.prefetch(pagePath, options); |
|
244 |
|
245 // Create a promise that resolves when the specified timeout ends. |
|
246 // The timeout value is 10 seconds by default. |
|
247 const timeoutPromise = new Promise(resolve => setTimeout(resolve, timeout)); |
|
248 |
|
249 // Don't update the navigation status immediately, wait 400 ms. |
|
250 const loadingTimeout = setTimeout(() => { |
|
251 if (navigatingTo !== href) { |
|
252 return; |
|
253 } |
|
254 if (loadingAnimation) { |
|
255 navigation.hasStarted = true; |
|
256 navigation.hasFinished = false; |
|
257 } |
|
258 if (screenReaderAnnouncement) { |
|
259 navigation.message = navigation.texts.loading; |
|
260 } |
|
261 }, 400); |
|
262 const page = yield Promise.race([pages.get(pagePath), timeoutPromise]); |
|
263 |
|
264 // Dismiss loading message if it hasn't been added yet. |
|
265 clearTimeout(loadingTimeout); |
|
266 |
|
267 // Once the page is fetched, the destination URL could have changed |
|
268 // (e.g., by clicking another link in the meantime). If so, bail |
|
269 // out, and let the newer execution to update the HTML. |
|
270 if (navigatingTo !== href) { |
|
271 return; |
|
272 } |
|
273 if (page && !page.initialData?.config?.['core/router']?.clientNavigationDisabled) { |
|
274 yield renderRegions(page); |
|
275 window.history[options.replace ? 'replaceState' : 'pushState']({}, '', href); |
|
276 |
|
277 // Update the URL in the state. |
|
278 state.url = href; |
|
279 |
|
280 // Update the navigation status once the the new page rendering |
|
281 // has been completed. |
|
282 if (loadingAnimation) { |
|
283 navigation.hasStarted = false; |
|
284 navigation.hasFinished = true; |
|
285 } |
|
286 if (screenReaderAnnouncement) { |
|
287 // Announce that the page has been loaded. If the message is the |
|
288 // same, we use a no-break space similar to the @wordpress/a11y |
|
289 // package: https://github.com/WordPress/gutenberg/blob/c395242b8e6ee20f8b06c199e4fc2920d7018af1/packages/a11y/src/filter-message.js#L20-L26 |
|
290 navigation.message = navigation.texts.loaded + (navigation.message === navigation.texts.loaded ? '\u00A0' : ''); |
|
291 } |
|
292 |
|
293 // Scroll to the anchor if exits in the link. |
|
294 const { |
|
295 hash |
|
296 } = new URL(href, window.location.href); |
|
297 if (hash) { |
|
298 document.querySelector(hash)?.scrollIntoView(); |
|
299 } |
|
300 } else { |
|
301 yield forcePageReload(href); |
|
302 } |
|
303 }, |
|
304 /** |
|
305 * Prefetchs the page with the passed URL. |
|
306 * |
|
307 * The function normalizes the URL and stores internally the fetch |
|
308 * promise, to avoid triggering a second fetch for an ongoing request. |
|
309 * |
|
310 * @param url The page URL. |
|
311 * @param [options] Options object. |
|
312 * @param [options.force] Force fetching the URL again. |
|
313 * @param [options.html] HTML string to be used instead of fetching the requested URL. |
|
314 */ |
|
315 prefetch(url, options = {}) { |
|
316 const { |
|
317 clientNavigationDisabled |
|
318 } = (0,interactivity_namespaceObject.getConfig)(); |
|
319 if (clientNavigationDisabled) { |
|
320 return; |
|
321 } |
|
322 const pagePath = getPagePath(url); |
|
323 if (options.force || !pages.has(pagePath)) { |
|
324 pages.set(pagePath, fetchPage(pagePath, { |
|
325 html: options.html |
|
326 })); |
|
327 } |
|
328 } |
|
329 } |
|
330 }); |
|
331 |
|
332 // Add click and prefetch to all links. |
|
333 if (false) {} |
|
334 |
|
335 var __webpack_exports__actions = __webpack_exports__.o; |
|
336 var __webpack_exports__state = __webpack_exports__.w; |
|
337 export { __webpack_exports__actions as actions, __webpack_exports__state as state }; |