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


Generated : Wed Dec 25 08:20:01 2024 Cross-referenced by PHPXref