diff -r 7b1b88e27a20 -r 48c4eec2b7e6 wp/wp-includes/js/dist/interactivity-router.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wp/wp-includes/js/dist/interactivity-router.js Fri Sep 05 18:40:08 2025 +0200 @@ -0,0 +1,337 @@ +import * as __WEBPACK_EXTERNAL_MODULE__wordpress_interactivity_8e89b257__ from "@wordpress/interactivity"; +/******/ // The require scope +/******/ var __webpack_require__ = {}; +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/define property getters */ +/******/ (() => { +/******/ // define getter functions for harmony exports +/******/ __webpack_require__.d = (exports, definition) => { +/******/ for(var key in definition) { +/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ (() => { +/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) +/******/ })(); +/******/ +/************************************************************************/ +var __webpack_exports__ = {}; + +// EXPORTS +__webpack_require__.d(__webpack_exports__, { + o: () => (/* binding */ actions), + w: () => (/* binding */ state) +}); + +;// CONCATENATED MODULE: external "@wordpress/interactivity" +var x = (y) => { + var x = {}; __webpack_require__.d(x, y); return x +} +var y = (x) => (() => (x)) +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) }); +;// CONCATENATED MODULE: ./node_modules/@wordpress/interactivity-router/build-module/index.js +var _getConfig$navigation; +/** + * WordPress dependencies + */ + + +/** + * Internal dependencies + */ + +const { + directivePrefix, + getRegionRootFragment, + initialVdom, + toVdom, + render, + parseInitialData, + populateInitialData, + batch +} = (0,interactivity_namespaceObject.privateApis)('I acknowledge that using private APIs means my theme or plugin will inevitably break in the next version of WordPress.'); +// Check if the navigation mode is full page or region based. +const navigationMode = (_getConfig$navigation = (0,interactivity_namespaceObject.getConfig)('core/router').navigationMode) !== null && _getConfig$navigation !== void 0 ? _getConfig$navigation : 'regionBased'; + +// The cache of visited and prefetched pages, stylesheets and scripts. +const pages = new Map(); +const headElements = new Map(); + +// Helper to remove domain and hash from the URL. We are only interesting in +// caching the path and the query. +const getPagePath = url => { + const u = new URL(url, window.location.href); + return u.pathname + u.search; +}; + +// Fetch a new page and convert it to a static virtual DOM. +const fetchPage = async (url, { + html +}) => { + try { + if (!html) { + const res = await window.fetch(url); + if (res.status !== 200) { + return false; + } + html = await res.text(); + } + const dom = new window.DOMParser().parseFromString(html, 'text/html'); + return regionsToVdom(dom); + } catch (e) { + return false; + } +}; + +// Return an object with VDOM trees of those HTML regions marked with a +// `router-region` directive. +const regionsToVdom = async (dom, { + vdom +} = {}) => { + const regions = { + body: undefined + }; + let head; + if (false) {} + if (navigationMode === 'regionBased') { + const attrName = `data-${directivePrefix}-router-region`; + dom.querySelectorAll(`[${attrName}]`).forEach(region => { + const id = region.getAttribute(attrName); + regions[id] = vdom?.has(region) ? vdom.get(region) : toVdom(region); + }); + } + const title = dom.querySelector('title')?.innerText; + const initialData = parseInitialData(dom); + return { + regions, + head, + title, + initialData + }; +}; + +// Render all interactive regions contained in the given page. +const renderRegions = page => { + batch(() => { + if (false) {} + if (navigationMode === 'regionBased') { + populateInitialData(page.initialData); + const attrName = `data-${directivePrefix}-router-region`; + document.querySelectorAll(`[${attrName}]`).forEach(region => { + const id = region.getAttribute(attrName); + const fragment = getRegionRootFragment(region); + render(page.regions[id], fragment); + }); + } + if (page.title) { + document.title = page.title; + } + }); +}; + +/** + * Load the given page forcing a full page reload. + * + * The function returns a promise that won't resolve, useful to prevent any + * potential feedback indicating that the navigation has finished while the new + * page is being loaded. + * + * @param href The page href. + * @return Promise that never resolves. + */ +const forcePageReload = href => { + window.location.assign(href); + return new Promise(() => {}); +}; + +// Listen to the back and forward buttons and restore the page if it's in the +// cache. +window.addEventListener('popstate', async () => { + const pagePath = getPagePath(window.location.href); // Remove hash. + const page = pages.has(pagePath) && (await pages.get(pagePath)); + if (page) { + renderRegions(page); + // Update the URL in the state. + state.url = window.location.href; + } else { + window.location.reload(); + } +}); + +// Initialize the router and cache the initial page using the initial vDOM. +// Once this code is tested and more mature, the head should be updated for +// region based navigation as well. +if (false) {} +pages.set(getPagePath(window.location.href), Promise.resolve(regionsToVdom(document, { + vdom: initialVdom +}))); + +// Check if the link is valid for client-side navigation. +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'); + +// Check if the event is valid for client-side navigation. +const isValidEvent = event => event && event.button === 0 && +// Left clicks only. +!event.metaKey && +// Open in new tab (Mac). +!event.ctrlKey && +// Open in new tab (Windows). +!event.altKey && +// Download. +!event.shiftKey && !event.defaultPrevented; + +// Variable to store the current navigation. +let navigatingTo = ''; +const { + state, + actions +} = (0,interactivity_namespaceObject.store)('core/router', { + state: { + url: window.location.href, + navigation: { + hasStarted: false, + hasFinished: false, + texts: { + loading: '', + loaded: '' + }, + message: '' + } + }, + actions: { + /** + * Navigates to the specified page. + * + * This function normalizes the passed href, fetchs the page HTML if + * needed, and updates any interactive regions whose contents have + * changed. It also creates a new entry in the browser session history. + * + * @param href The page href. + * @param [options] Options object. + * @param [options.force] If true, it forces re-fetching the URL. + * @param [options.html] HTML string to be used instead of fetching the requested URL. + * @param [options.replace] If true, it replaces the current entry in the browser session history. + * @param [options.timeout] Time until the navigation is aborted, in milliseconds. Default is 10000. + * @param [options.loadingAnimation] Whether an animation should be shown while navigating. Default to `true`. + * @param [options.screenReaderAnnouncement] Whether a message for screen readers should be announced while navigating. Default to `true`. + * + * @return Promise that resolves once the navigation is completed or aborted. + */ + *navigate(href, options = {}) { + const { + clientNavigationDisabled + } = (0,interactivity_namespaceObject.getConfig)(); + if (clientNavigationDisabled) { + yield forcePageReload(href); + } + const pagePath = getPagePath(href); + const { + navigation + } = state; + const { + loadingAnimation = true, + screenReaderAnnouncement = true, + timeout = 10000 + } = options; + navigatingTo = href; + actions.prefetch(pagePath, options); + + // Create a promise that resolves when the specified timeout ends. + // The timeout value is 10 seconds by default. + const timeoutPromise = new Promise(resolve => setTimeout(resolve, timeout)); + + // Don't update the navigation status immediately, wait 400 ms. + const loadingTimeout = setTimeout(() => { + if (navigatingTo !== href) { + return; + } + if (loadingAnimation) { + navigation.hasStarted = true; + navigation.hasFinished = false; + } + if (screenReaderAnnouncement) { + navigation.message = navigation.texts.loading; + } + }, 400); + const page = yield Promise.race([pages.get(pagePath), timeoutPromise]); + + // Dismiss loading message if it hasn't been added yet. + clearTimeout(loadingTimeout); + + // Once the page is fetched, the destination URL could have changed + // (e.g., by clicking another link in the meantime). If so, bail + // out, and let the newer execution to update the HTML. + if (navigatingTo !== href) { + return; + } + if (page && !page.initialData?.config?.['core/router']?.clientNavigationDisabled) { + yield renderRegions(page); + window.history[options.replace ? 'replaceState' : 'pushState']({}, '', href); + + // Update the URL in the state. + state.url = href; + + // Update the navigation status once the the new page rendering + // has been completed. + if (loadingAnimation) { + navigation.hasStarted = false; + navigation.hasFinished = true; + } + if (screenReaderAnnouncement) { + // Announce that the page has been loaded. If the message is the + // same, we use a no-break space similar to the @wordpress/a11y + // package: https://github.com/WordPress/gutenberg/blob/c395242b8e6ee20f8b06c199e4fc2920d7018af1/packages/a11y/src/filter-message.js#L20-L26 + navigation.message = navigation.texts.loaded + (navigation.message === navigation.texts.loaded ? '\u00A0' : ''); + } + + // Scroll to the anchor if exits in the link. + const { + hash + } = new URL(href, window.location.href); + if (hash) { + document.querySelector(hash)?.scrollIntoView(); + } + } else { + yield forcePageReload(href); + } + }, + /** + * Prefetchs the page with the passed URL. + * + * The function normalizes the URL and stores internally the fetch + * promise, to avoid triggering a second fetch for an ongoing request. + * + * @param url The page URL. + * @param [options] Options object. + * @param [options.force] Force fetching the URL again. + * @param [options.html] HTML string to be used instead of fetching the requested URL. + */ + prefetch(url, options = {}) { + const { + clientNavigationDisabled + } = (0,interactivity_namespaceObject.getConfig)(); + if (clientNavigationDisabled) { + return; + } + const pagePath = getPagePath(url); + if (options.force || !pages.has(pagePath)) { + pages.set(pagePath, fetchPage(pagePath, { + html: options.html + })); + } + } + } +}); + +// Add click and prefetch to all links. +if (false) {} + +var __webpack_exports__actions = __webpack_exports__.o; +var __webpack_exports__state = __webpack_exports__.w; +export { __webpack_exports__actions as actions, __webpack_exports__state as state };