[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/js/dist/script-modules/interactivity-router/ -> index.js (source)

   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  // This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
  58  (() => {
  59  
  60  // EXPORTS
  61  __webpack_require__.d(__webpack_exports__, {
  62    o: () => (/* binding */ actions),
  63    w: () => (/* binding */ state)
  64  });
  65  
  66  ;// CONCATENATED MODULE: external "@wordpress/interactivity"
  67  var x = (y) => {
  68      var x = {}; __webpack_require__.d(x, y); return x
  69  } 
  70  var y = (x) => (() => (x))
  71  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) });
  72  ;// CONCATENATED MODULE: ./node_modules/@wordpress/interactivity-router/build-module/index.js
  73  var _getConfig$navigation;
  74  /**
  75   * WordPress dependencies
  76   */
  77  
  78  
  79  /**
  80   * Internal dependencies
  81   */
  82  
  83  const {
  84    directivePrefix,
  85    getRegionRootFragment,
  86    initialVdom,
  87    toVdom,
  88    render,
  89    parseServerData,
  90    populateServerData,
  91    batch
  92  } = (0,interactivity_namespaceObject.privateApis)('I acknowledge that using private APIs means my theme or plugin will inevitably break in the next version of WordPress.');
  93  // Check if the navigation mode is full page or region based.
  94  const navigationMode = (_getConfig$navigation = (0,interactivity_namespaceObject.getConfig)('core/router').navigationMode) !== null && _getConfig$navigation !== void 0 ? _getConfig$navigation : 'regionBased';
  95  
  96  // The cache of visited and prefetched pages, stylesheets and scripts.
  97  const pages = new Map();
  98  const headElements = new Map();
  99  
 100  // Helper to remove domain and hash from the URL. We are only interesting in
 101  // caching the path and the query.
 102  const getPagePath = url => {
 103    const u = new URL(url, window.location.href);
 104    return u.pathname + u.search;
 105  };
 106  
 107  // Fetch a new page and convert it to a static virtual DOM.
 108  const fetchPage = async (url, {
 109    html
 110  }) => {
 111    try {
 112      if (!html) {
 113        const res = await window.fetch(url);
 114        if (res.status !== 200) {
 115          return false;
 116        }
 117        html = await res.text();
 118      }
 119      const dom = new window.DOMParser().parseFromString(html, 'text/html');
 120      return regionsToVdom(dom);
 121    } catch (e) {
 122      return false;
 123    }
 124  };
 125  
 126  // Return an object with VDOM trees of those HTML regions marked with a
 127  // `router-region` directive.
 128  const regionsToVdom = async (dom, {
 129    vdom
 130  } = {}) => {
 131    const regions = {
 132      body: undefined
 133    };
 134    let head;
 135    if (false) {}
 136    if (navigationMode === 'regionBased') {
 137      const attrName = `data-$directivePrefix}-router-region`;
 138      dom.querySelectorAll(`[$attrName}]`).forEach(region => {
 139        const id = region.getAttribute(attrName);
 140        regions[id] = vdom?.has(region) ? vdom.get(region) : toVdom(region);
 141      });
 142    }
 143    const title = dom.querySelector('title')?.innerText;
 144    const initialData = parseServerData(dom);
 145    return {
 146      regions,
 147      head,
 148      title,
 149      initialData
 150    };
 151  };
 152  
 153  // Render all interactive regions contained in the given page.
 154  const renderRegions = page => {
 155    batch(() => {
 156      if (false) {}
 157      if (navigationMode === 'regionBased') {
 158        populateServerData(page.initialData);
 159        const attrName = `data-$directivePrefix}-router-region`;
 160        document.querySelectorAll(`[$attrName}]`).forEach(region => {
 161          const id = region.getAttribute(attrName);
 162          const fragment = getRegionRootFragment(region);
 163          render(page.regions[id], fragment);
 164        });
 165      }
 166      if (page.title) {
 167        document.title = page.title;
 168      }
 169    });
 170  };
 171  
 172  /**
 173   * Load the given page forcing a full page reload.
 174   *
 175   * The function returns a promise that won't resolve, useful to prevent any
 176   * potential feedback indicating that the navigation has finished while the new
 177   * page is being loaded.
 178   *
 179   * @param href The page href.
 180   * @return Promise that never resolves.
 181   */
 182  const forcePageReload = href => {
 183    window.location.assign(href);
 184    return new Promise(() => {});
 185  };
 186  
 187  // Listen to the back and forward buttons and restore the page if it's in the
 188  // cache.
 189  window.addEventListener('popstate', async () => {
 190    const pagePath = getPagePath(window.location.href); // Remove hash.
 191    const page = pages.has(pagePath) && (await pages.get(pagePath));
 192    if (page) {
 193      renderRegions(page);
 194      // Update the URL in the state.
 195      state.url = window.location.href;
 196    } else {
 197      window.location.reload();
 198    }
 199  });
 200  
 201  // Initialize the router and cache the initial page using the initial vDOM.
 202  // Once this code is tested and more mature, the head should be updated for
 203  // region based navigation as well.
 204  if (false) {}
 205  pages.set(getPagePath(window.location.href), Promise.resolve(regionsToVdom(document, {
 206    vdom: initialVdom
 207  })));
 208  
 209  // Check if the link is valid for client-side navigation.
 210  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');
 211  
 212  // Check if the event is valid for client-side navigation.
 213  const isValidEvent = event => event && event.button === 0 &&
 214  // Left clicks only.
 215  !event.metaKey &&
 216  // Open in new tab (Mac).
 217  !event.ctrlKey &&
 218  // Open in new tab (Windows).
 219  !event.altKey &&
 220  // Download.
 221  !event.shiftKey && !event.defaultPrevented;
 222  
 223  // Variable to store the current navigation.
 224  let navigatingTo = '';
 225  let hasLoadedNavigationTextsData = false;
 226  const navigationTexts = {
 227    loading: 'Loading page, please wait.',
 228    loaded: 'Page Loaded.'
 229  };
 230  const {
 231    state,
 232    actions
 233  } = (0,interactivity_namespaceObject.store)('core/router', {
 234    state: {
 235      url: window.location.href,
 236      navigation: {
 237        hasStarted: false,
 238        hasFinished: false
 239      }
 240    },
 241    actions: {
 242      /**
 243       * Navigates to the specified page.
 244       *
 245       * This function normalizes the passed href, fetchs the page HTML if
 246       * needed, and updates any interactive regions whose contents have
 247       * changed. It also creates a new entry in the browser session history.
 248       *
 249       * @param href                               The page href.
 250       * @param [options]                          Options object.
 251       * @param [options.force]                    If true, it forces re-fetching the URL.
 252       * @param [options.html]                     HTML string to be used instead of fetching the requested URL.
 253       * @param [options.replace]                  If true, it replaces the current entry in the browser session history.
 254       * @param [options.timeout]                  Time until the navigation is aborted, in milliseconds. Default is 10000.
 255       * @param [options.loadingAnimation]         Whether an animation should be shown while navigating. Default to `true`.
 256       * @param [options.screenReaderAnnouncement] Whether a message for screen readers should be announced while navigating. Default to `true`.
 257       *
 258       * @return  Promise that resolves once the navigation is completed or aborted.
 259       */
 260      *navigate(href, options = {}) {
 261        const {
 262          clientNavigationDisabled
 263        } = (0,interactivity_namespaceObject.getConfig)();
 264        if (clientNavigationDisabled) {
 265          yield forcePageReload(href);
 266        }
 267        const pagePath = getPagePath(href);
 268        const {
 269          navigation
 270        } = state;
 271        const {
 272          loadingAnimation = true,
 273          screenReaderAnnouncement = true,
 274          timeout = 10000
 275        } = options;
 276        navigatingTo = href;
 277        actions.prefetch(pagePath, options);
 278  
 279        // Create a promise that resolves when the specified timeout ends.
 280        // The timeout value is 10 seconds by default.
 281        const timeoutPromise = new Promise(resolve => setTimeout(resolve, timeout));
 282  
 283        // Don't update the navigation status immediately, wait 400 ms.
 284        const loadingTimeout = setTimeout(() => {
 285          if (navigatingTo !== href) {
 286            return;
 287          }
 288          if (loadingAnimation) {
 289            navigation.hasStarted = true;
 290            navigation.hasFinished = false;
 291          }
 292          if (screenReaderAnnouncement) {
 293            a11ySpeak('loading');
 294          }
 295        }, 400);
 296        const page = yield Promise.race([pages.get(pagePath), timeoutPromise]);
 297  
 298        // Dismiss loading message if it hasn't been added yet.
 299        clearTimeout(loadingTimeout);
 300  
 301        // Once the page is fetched, the destination URL could have changed
 302        // (e.g., by clicking another link in the meantime). If so, bail
 303        // out, and let the newer execution to update the HTML.
 304        if (navigatingTo !== href) {
 305          return;
 306        }
 307        if (page && !page.initialData?.config?.['core/router']?.clientNavigationDisabled) {
 308          yield renderRegions(page);
 309          window.history[options.replace ? 'replaceState' : 'pushState']({}, '', href);
 310  
 311          // Update the URL in the state.
 312          state.url = href;
 313  
 314          // Update the navigation status once the the new page rendering
 315          // has been completed.
 316          if (loadingAnimation) {
 317            navigation.hasStarted = false;
 318            navigation.hasFinished = true;
 319          }
 320          if (screenReaderAnnouncement) {
 321            a11ySpeak('loaded');
 322          }
 323  
 324          // Scroll to the anchor if exits in the link.
 325          const {
 326            hash
 327          } = new URL(href, window.location.href);
 328          if (hash) {
 329            document.querySelector(hash)?.scrollIntoView();
 330          }
 331        } else {
 332          yield forcePageReload(href);
 333        }
 334      },
 335      /**
 336       * Prefetchs the page with the passed URL.
 337       *
 338       * The function normalizes the URL and stores internally the fetch
 339       * promise, to avoid triggering a second fetch for an ongoing request.
 340       *
 341       * @param url             The page URL.
 342       * @param [options]       Options object.
 343       * @param [options.force] Force fetching the URL again.
 344       * @param [options.html]  HTML string to be used instead of fetching the requested URL.
 345       */
 346      prefetch(url, options = {}) {
 347        const {
 348          clientNavigationDisabled
 349        } = (0,interactivity_namespaceObject.getConfig)();
 350        if (clientNavigationDisabled) {
 351          return;
 352        }
 353        const pagePath = getPagePath(url);
 354        if (options.force || !pages.has(pagePath)) {
 355          pages.set(pagePath, fetchPage(pagePath, {
 356            html: options.html
 357          }));
 358        }
 359      }
 360    }
 361  });
 362  
 363  /**
 364   * Announces a message to screen readers.
 365   *
 366   * This is a wrapper around the `@wordpress/a11y` package's `speak` function. It handles importing
 367   * the package on demand and should be used instead of calling `ally.speak` direacly.
 368   *
 369   * @param messageKey The message to be announced by assistive technologies.
 370   */
 371  function a11ySpeak(messageKey) {
 372    if (!hasLoadedNavigationTextsData) {
 373      hasLoadedNavigationTextsData = true;
 374      const content = document.getElementById('wp-script-module-data-@wordpress/interactivity-router')?.textContent;
 375      if (content) {
 376        try {
 377          const parsed = JSON.parse(content);
 378          if (typeof parsed?.i18n?.loading === 'string') {
 379            navigationTexts.loading = parsed.i18n.loading;
 380          }
 381          if (typeof parsed?.i18n?.loaded === 'string') {
 382            navigationTexts.loaded = parsed.i18n.loaded;
 383          }
 384        } catch {}
 385      } else {
 386        // Fallback to localized strings from Interactivity API state.
 387        // @todo This block is for Core < 6.7.0. Remove when support is dropped.
 388  
 389        // @ts-expect-error
 390        if (state.navigation.texts?.loading) {
 391          // @ts-expect-error
 392          navigationTexts.loading = state.navigation.texts.loading;
 393        }
 394        // @ts-expect-error
 395        if (state.navigation.texts?.loaded) {
 396          // @ts-expect-error
 397          navigationTexts.loaded = state.navigation.texts.loaded;
 398        }
 399      }
 400    }
 401    const message = navigationTexts[messageKey];
 402    Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, 317)).then(({
 403      speak
 404    }) => speak(message),
 405    // Ignore failures to load the a11y module.
 406    () => {});
 407  }
 408  
 409  // Add click and prefetch to all links.
 410  if (false) {}
 411  
 412  })();
 413  
 414  var __webpack_exports__actions = __webpack_exports__.o;
 415  var __webpack_exports__state = __webpack_exports__.w;
 416  export { __webpack_exports__actions as actions, __webpack_exports__state as state };


Generated : Sat Nov 23 08:20:01 2024 Cross-referenced by PHPXref