annotate core/modules/big_pipe/js/big_pipe.es6.js @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 129ea1e6d783
children
rev   line source
Chris@0 1 /**
Chris@0 2 * @file
Chris@0 3 * Renders BigPipe placeholders using Drupal's Ajax system.
Chris@0 4 */
Chris@0 5
Chris@17 6 (function($, Drupal, drupalSettings) {
Chris@0 7 /**
Chris@14 8 * Maps textContent of <script type="application/vnd.drupal-ajax"> to an AJAX response.
Chris@14 9 *
Chris@14 10 * @param {string} content
Chris@14 11 * The text content of a <script type="application/vnd.drupal-ajax"> DOM node.
Chris@14 12 * @return {Array|boolean}
Chris@14 13 * The parsed Ajax response containing an array of Ajax commands, or false in
Chris@14 14 * case the DOM node hasn't fully arrived yet.
Chris@14 15 */
Chris@14 16 function mapTextContentToAjaxResponse(content) {
Chris@14 17 if (content === '') {
Chris@14 18 return false;
Chris@14 19 }
Chris@14 20
Chris@14 21 try {
Chris@14 22 return JSON.parse(content);
Chris@17 23 } catch (e) {
Chris@14 24 return false;
Chris@14 25 }
Chris@14 26 }
Chris@14 27
Chris@14 28 /**
Chris@17 29 * Executes Ajax commands in <script type="application/vnd.drupal-ajax"> tag.
Chris@17 30 *
Chris@17 31 * These Ajax commands replace placeholders with HTML and load missing CSS/JS.
Chris@17 32 *
Chris@17 33 * @param {number} index
Chris@17 34 * Current index.
Chris@17 35 * @param {HTMLScriptElement} placeholderReplacement
Chris@17 36 * Script tag created by BigPipe.
Chris@17 37 */
Chris@17 38 function bigPipeProcessPlaceholderReplacement(index, placeholderReplacement) {
Chris@17 39 const placeholderId = placeholderReplacement.getAttribute(
Chris@17 40 'data-big-pipe-replacement-for-placeholder-with-id',
Chris@17 41 );
Chris@17 42 const content = this.textContent.trim();
Chris@17 43 // Ignore any placeholders that are not in the known placeholder list. Used
Chris@17 44 // to avoid someone trying to XSS the site via the placeholdering mechanism.
Chris@17 45 if (
Chris@17 46 typeof drupalSettings.bigPipePlaceholderIds[placeholderId] !== 'undefined'
Chris@17 47 ) {
Chris@17 48 const response = mapTextContentToAjaxResponse(content);
Chris@17 49 // If we try to parse the content too early (when the JSON containing Ajax
Chris@17 50 // commands is still arriving), textContent will be empty or incomplete.
Chris@17 51 if (response === false) {
Chris@17 52 /**
Chris@17 53 * Mark as unprocessed so this will be retried later.
Chris@17 54 * @see bigPipeProcessDocument()
Chris@17 55 */
Chris@17 56 $(this).removeOnce('big-pipe');
Chris@17 57 } else {
Chris@17 58 // Create a Drupal.Ajax object without associating an element, a
Chris@17 59 // progress indicator or a URL.
Chris@17 60 const ajaxObject = Drupal.ajax({
Chris@17 61 url: '',
Chris@17 62 base: false,
Chris@17 63 element: false,
Chris@17 64 progress: false,
Chris@17 65 });
Chris@17 66 // Then, simulate an AJAX response having arrived, and let the Ajax
Chris@17 67 // system handle it.
Chris@17 68 ajaxObject.success(response, 'success');
Chris@17 69 }
Chris@17 70 }
Chris@17 71 }
Chris@17 72
Chris@17 73 // The frequency with which to check for newly arrived BigPipe placeholders.
Chris@17 74 // Hence 50 ms means we check 20 times per second. Setting this to 100 ms or
Chris@17 75 // more would cause the user to see content appear noticeably slower.
Chris@17 76 const interval = drupalSettings.bigPipeInterval || 50;
Chris@17 77
Chris@17 78 // The internal ID to contain the watcher service.
Chris@17 79 let timeoutID;
Chris@17 80
Chris@17 81 /**
Chris@0 82 * Processes a streamed HTML document receiving placeholder replacements.
Chris@0 83 *
Chris@0 84 * @param {HTMLDocument} context
Chris@0 85 * The HTML document containing <script type="application/vnd.drupal-ajax">
Chris@0 86 * tags generated by BigPipe.
Chris@0 87 *
Chris@0 88 * @return {bool}
Chris@0 89 * Returns true when processing has been finished and a stop signal has been
Chris@0 90 * found.
Chris@0 91 */
Chris@0 92 function bigPipeProcessDocument(context) {
Chris@0 93 // Make sure we have BigPipe-related scripts before processing further.
Chris@0 94 if (!context.querySelector('script[data-big-pipe-event="start"]')) {
Chris@0 95 return false;
Chris@0 96 }
Chris@0 97
Chris@17 98 $(context)
Chris@17 99 .find('script[data-big-pipe-replacement-for-placeholder-with-id]')
Chris@0 100 .once('big-pipe')
Chris@0 101 .each(bigPipeProcessPlaceholderReplacement);
Chris@0 102
Chris@0 103 // If we see the stop signal, clear the timeout: all placeholder
Chris@0 104 // replacements are guaranteed to be received and processed.
Chris@0 105 if (context.querySelector('script[data-big-pipe-event="stop"]')) {
Chris@0 106 if (timeoutID) {
Chris@0 107 clearTimeout(timeoutID);
Chris@0 108 }
Chris@0 109 return true;
Chris@0 110 }
Chris@0 111
Chris@0 112 return false;
Chris@0 113 }
Chris@0 114
Chris@0 115 function bigPipeProcess() {
Chris@0 116 timeoutID = setTimeout(() => {
Chris@0 117 if (!bigPipeProcessDocument(document)) {
Chris@0 118 bigPipeProcess();
Chris@0 119 }
Chris@0 120 }, interval);
Chris@0 121 }
Chris@0 122
Chris@0 123 bigPipeProcess();
Chris@0 124
Chris@0 125 // If something goes wrong, make sure everything is cleaned up and has had a
Chris@0 126 // chance to be processed with everything loaded.
Chris@0 127 $(window).on('load', () => {
Chris@0 128 if (timeoutID) {
Chris@0 129 clearTimeout(timeoutID);
Chris@0 130 }
Chris@0 131 bigPipeProcessDocument(document);
Chris@0 132 });
Chris@17 133 })(jQuery, Drupal, drupalSettings);