Initial Drupal 11 with DDEV setup
This commit is contained in:
		
							
								
								
									
										183
									
								
								web/core/modules/big_pipe/js/big_pipe.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										183
									
								
								web/core/modules/big_pipe/js/big_pipe.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,183 @@
 | 
			
		||||
/**
 | 
			
		||||
 * @file
 | 
			
		||||
 * Renders BigPipe placeholders using Drupal's Ajax system.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
((Drupal, drupalSettings) => {
 | 
			
		||||
  /**
 | 
			
		||||
   * CSS selector for script elements to process on page load.
 | 
			
		||||
   *
 | 
			
		||||
   * @type {string}
 | 
			
		||||
   */
 | 
			
		||||
  const replacementsSelector = `script[data-big-pipe-replacement-for-placeholder-with-id]`;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Ajax object that will process all the BigPipe responses.
 | 
			
		||||
   *
 | 
			
		||||
   * Create a Drupal.Ajax object without associating an element, a progress
 | 
			
		||||
   * indicator or a URL.
 | 
			
		||||
   *
 | 
			
		||||
   * @type {Drupal.Ajax}
 | 
			
		||||
   */
 | 
			
		||||
  const ajaxObject = Drupal.ajax({
 | 
			
		||||
    url: '',
 | 
			
		||||
    base: false,
 | 
			
		||||
    element: false,
 | 
			
		||||
    progress: false,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Maps textContent of <script type="application/vnd.drupal-ajax"> to an AJAX
 | 
			
		||||
   * response.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {string} content
 | 
			
		||||
   *   The text content of a <script type="application/vnd.drupal-ajax"> DOM
 | 
			
		||||
   *   node.
 | 
			
		||||
   * @return {Array|boolean}
 | 
			
		||||
   *   The parsed Ajax response containing an array of Ajax commands, or false
 | 
			
		||||
   *   in case the DOM node hasn't fully arrived yet.
 | 
			
		||||
   */
 | 
			
		||||
  function mapTextContentToAjaxResponse(content) {
 | 
			
		||||
    if (content === '') {
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      return JSON.parse(content);
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Executes Ajax commands in <script type="application/vnd.drupal-ajax"> tag.
 | 
			
		||||
   *
 | 
			
		||||
   * These Ajax commands replace placeholders with HTML and load missing CSS/JS.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {HTMLScriptElement} replacement
 | 
			
		||||
   *   Script tag created by BigPipe.
 | 
			
		||||
   */
 | 
			
		||||
  function processReplacement(replacement) {
 | 
			
		||||
    const id = replacement.dataset.bigPipeReplacementForPlaceholderWithId;
 | 
			
		||||
    // The content is not guaranteed to be complete at this point, but trimming
 | 
			
		||||
    // it will not make a big change, since json will not be valid if it was
 | 
			
		||||
    // not fully loaded anyway.
 | 
			
		||||
    const content = replacement.textContent.trim();
 | 
			
		||||
 | 
			
		||||
    // Ignore any placeholders that are not in the known placeholder list. Used
 | 
			
		||||
    // to avoid someone trying to XSS the site via the placeholdering mechanism.
 | 
			
		||||
    if (typeof drupalSettings.bigPipePlaceholderIds[id] === 'undefined') {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const response = mapTextContentToAjaxResponse(content);
 | 
			
		||||
 | 
			
		||||
    if (response === false) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Immediately remove the replacement to prevent it being processed twice.
 | 
			
		||||
    delete drupalSettings.bigPipePlaceholderIds[id];
 | 
			
		||||
 | 
			
		||||
    // Then, simulate an AJAX response having arrived, and let the Ajax system
 | 
			
		||||
    // handle it.
 | 
			
		||||
    ajaxObject.success(response, 'success');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Checks if node is valid big pipe replacement.
 | 
			
		||||
   */
 | 
			
		||||
  function checkMutation(node) {
 | 
			
		||||
    return Boolean(
 | 
			
		||||
      node.nodeType === Node.ELEMENT_NODE &&
 | 
			
		||||
        node.nodeName === 'SCRIPT' &&
 | 
			
		||||
        node.dataset?.bigPipeReplacementForPlaceholderWithId &&
 | 
			
		||||
        typeof drupalSettings.bigPipePlaceholderIds[
 | 
			
		||||
          node.dataset.bigPipeReplacementForPlaceholderWithId
 | 
			
		||||
        ] !== 'undefined',
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Check that the element is valid to process and process it.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {HTMLElement} node
 | 
			
		||||
   *  The node added to the body element.
 | 
			
		||||
   */
 | 
			
		||||
  function checkMutationAndProcess(node) {
 | 
			
		||||
    if (checkMutation(node)) {
 | 
			
		||||
      processReplacement(node);
 | 
			
		||||
    }
 | 
			
		||||
    // Checks if parent node of target node has not been processed, which can
 | 
			
		||||
    // occur if the script node was first observed with empty content and then
 | 
			
		||||
    // the child text node was added in full later.
 | 
			
		||||
    // @see `@ingroup large_chunk` for more information.
 | 
			
		||||
    // If an element is added and then immediately (faster than the next
 | 
			
		||||
    // setImmediate is triggered) removed to a watched element of a
 | 
			
		||||
    // MutationObserver, the observer will notice and add a mutation for both
 | 
			
		||||
    // the addedNode and the removedNode - but the referenced element will not
 | 
			
		||||
    // have a parent node.
 | 
			
		||||
    else if (node.parentNode !== null && checkMutation(node.parentNode)) {
 | 
			
		||||
      processReplacement(node.parentNode);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Handles the mutation callback.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {MutationRecord[]} mutations
 | 
			
		||||
   *  The list of mutations registered by the browser.
 | 
			
		||||
   */
 | 
			
		||||
  function processMutations(mutations) {
 | 
			
		||||
    mutations.forEach(({ addedNodes, type, target }) => {
 | 
			
		||||
      addedNodes.forEach(checkMutationAndProcess);
 | 
			
		||||
 | 
			
		||||
      // Checks if parent node of target node has not been processed.
 | 
			
		||||
      // @see `@ingroup large_chunk` for more information.
 | 
			
		||||
      if (
 | 
			
		||||
        type === 'characterData' &&
 | 
			
		||||
        checkMutation(target.parentNode) &&
 | 
			
		||||
        drupalSettings.bigPipePlaceholderIds[
 | 
			
		||||
          target.parentNode.dataset.bigPipeReplacementForPlaceholderWithId
 | 
			
		||||
        ] === true
 | 
			
		||||
      ) {
 | 
			
		||||
        processReplacement(target.parentNode);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const observer = new MutationObserver(processMutations);
 | 
			
		||||
 | 
			
		||||
  // Attach behaviors early, if possible.
 | 
			
		||||
  Drupal.attachBehaviors(document);
 | 
			
		||||
 | 
			
		||||
  // If loaded asynchronously there might already be replacement elements
 | 
			
		||||
  // in the DOM before the mutation observer is started.
 | 
			
		||||
  document.querySelectorAll(replacementsSelector).forEach(processReplacement);
 | 
			
		||||
 | 
			
		||||
  // Start observing the body element for new children and for new changes in
 | 
			
		||||
  // Text nodes of elements. We need to track Text nodes because content
 | 
			
		||||
  // of the node can be too large, browser will receive not fully loaded chunk
 | 
			
		||||
  // and render it as is. At this moment json inside script will be invalid and
 | 
			
		||||
  // we need to track new changes to that json (Text node), once it will be
 | 
			
		||||
  // fully loaded it will be processed.
 | 
			
		||||
  // @ingroup large_chunk
 | 
			
		||||
  observer.observe(document.body, {
 | 
			
		||||
    childList: true,
 | 
			
		||||
    // Without this options characterData will not be triggered inside child nodes.
 | 
			
		||||
    subtree: true,
 | 
			
		||||
    characterData: true,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  // As soon as the document is loaded, no more replacements will be added.
 | 
			
		||||
  // Immediately fetch and process all pending mutations and stop the observer.
 | 
			
		||||
  window.addEventListener('DOMContentLoaded', () => {
 | 
			
		||||
    const mutations = observer.takeRecords();
 | 
			
		||||
    observer.disconnect();
 | 
			
		||||
    if (mutations.length) {
 | 
			
		||||
      processMutations(mutations);
 | 
			
		||||
    }
 | 
			
		||||
    // No more mutations will be processed, remove the leftover Ajax object.
 | 
			
		||||
    Drupal.ajax.instances[ajaxObject.instanceIndex] = null;
 | 
			
		||||
  });
 | 
			
		||||
})(Drupal, drupalSettings);
 | 
			
		||||
		Reference in New Issue
	
	Block a user