diff --git a/core/.eslintrc.passing.json b/core/.eslintrc.passing.json
index 3f42d326c6c0a5e92a528ee42a7817a170bb280d..13fcf79c77c3b4964d621d7812623176f6e4177c 100644
--- a/core/.eslintrc.passing.json
+++ b/core/.eslintrc.passing.json
@@ -1,7 +1,6 @@
 {
   "extends": "./.eslintrc.json",
   "rules": {
-    "no-use-before-define": "off",
     "no-shadow": "off",
     "no-new": "off",
     "no-continue": "off",
diff --git a/core/misc/autocomplete.es6.js b/core/misc/autocomplete.es6.js
index 9beaf02af3c1825187ab90d35c4e750c1adfb74a..79ed666ee115d3908168ee5197db4f8d143f8f85 100644
--- a/core/misc/autocomplete.es6.js
+++ b/core/misc/autocomplete.es6.js
@@ -122,6 +122,9 @@
       response(suggestions);
     }
 
+    // Get the desired term and construct the autocomplete URL for it.
+    const term = autocomplete.extractLastTerm(request.term);
+
     /**
      * Transforms the data object into an array and update autocomplete results.
      *
@@ -135,9 +138,6 @@
       showSuggestions(data);
     }
 
-    // Get the desired term and construct the autocomplete URL for it.
-    const term = autocomplete.extractLastTerm(request.term);
-
     // Check if the term is already cached.
     if (autocomplete.cache[elementId].hasOwnProperty(term)) {
       showSuggestions(autocomplete.cache[elementId][term]);
diff --git a/core/misc/autocomplete.js b/core/misc/autocomplete.js
index aa3311d08d3d37d185a204abf0a1499538a1f4e5..52ddfd2ab4825329cc5fbcc77e30d0c4df924bfb 100644
--- a/core/misc/autocomplete.js
+++ b/core/misc/autocomplete.js
@@ -73,14 +73,14 @@
       response(suggestions);
     }
 
+    var term = autocomplete.extractLastTerm(request.term);
+
     function sourceCallbackHandler(data) {
       autocomplete.cache[elementId][term] = data;
 
       showSuggestions(data);
     }
 
-    var term = autocomplete.extractLastTerm(request.term);
-
     if (autocomplete.cache[elementId].hasOwnProperty(term)) {
       showSuggestions(autocomplete.cache[elementId][term]);
     } else {
diff --git a/core/misc/dialog/dialog.es6.js b/core/misc/dialog/dialog.es6.js
index 545afc34ca79dd0f96ed32adc6c8c9ba7ddaa93d..26b9107bd988851e0737005b80cb5d67287c312c 100644
--- a/core/misc/dialog/dialog.es6.js
+++ b/core/misc/dialog/dialog.es6.js
@@ -65,13 +65,6 @@
     const dialog = {
       open: false,
       returnValue: undef,
-      show() {
-        openDialog({ modal: false });
-      },
-      showModal() {
-        openDialog({ modal: true });
-      },
-      close: closeDialog,
     };
 
     function openDialog(settings) {
@@ -91,6 +84,14 @@
       $(window).trigger('dialog:afterclose', [dialog, $element]);
     }
 
+    dialog.show = () => {
+      openDialog({ modal: false });
+    };
+    dialog.showModal = () => {
+      openDialog({ modal: true });
+    };
+    dialog.close = closeDialog;
+
     return dialog;
   };
 }(jQuery, Drupal, drupalSettings));
diff --git a/core/misc/dialog/dialog.js b/core/misc/dialog/dialog.js
index f25c0683e34e9e9ee0ceb943284a353687cd928a..9bd8b554e4e1e590fcf46623779be18f03fd0207 100644
--- a/core/misc/dialog/dialog.js
+++ b/core/misc/dialog/dialog.js
@@ -23,15 +23,7 @@
     var $element = $(element);
     var dialog = {
       open: false,
-      returnValue: undef,
-      show: function show() {
-        openDialog({ modal: false });
-      },
-      showModal: function showModal() {
-        openDialog({ modal: true });
-      },
-
-      close: closeDialog
+      returnValue: undef
     };
 
     function openDialog(settings) {
@@ -51,6 +43,14 @@
       $(window).trigger('dialog:afterclose', [dialog, $element]);
     }
 
+    dialog.show = function () {
+      openDialog({ modal: false });
+    };
+    dialog.showModal = function () {
+      openDialog({ modal: true });
+    };
+    dialog.close = closeDialog;
+
     return dialog;
   };
 })(jQuery, Drupal, drupalSettings);
\ No newline at end of file
diff --git a/core/misc/dialog/dialog.position.es6.js b/core/misc/dialog/dialog.position.es6.js
index d2d35a326339c96f24d6128b904c40132ad8b507..939fbd849bbe2627c0d184dbf1c46c36decb0d34 100644
--- a/core/misc/dialog/dialog.position.es6.js
+++ b/core/misc/dialog/dialog.position.es6.js
@@ -13,6 +13,31 @@
   // autoResize option will turn off resizable and draggable.
   drupalSettings.dialog = $.extend({ autoResize: true, maxHeight: '95%' }, drupalSettings.dialog);
 
+  /**
+   * Position the dialog's center at the center of displace.offsets boundaries.
+   *
+   * @function Drupal.dialog~resetPosition
+   *
+   * @param {object} options
+   *   Options object.
+   *
+   * @return {object}
+   *   Altered options object.
+   */
+  function resetPosition(options) {
+    const offsets = displace.offsets;
+    const left = offsets.left - offsets.right;
+    const top = offsets.top - offsets.bottom;
+
+    const leftString = `${(left > 0 ? '+' : '-') + Math.abs(Math.round(left / 2))}px`;
+    const topString = `${(top > 0 ? '+' : '-') + Math.abs(Math.round(top / 2))}px`;
+    options.position = {
+      my: `center${left !== 0 ? leftString : ''} center${top !== 0 ? topString : ''}`,
+      of: window,
+    };
+    return options;
+  }
+
   /**
    * Resets the current options for positioning.
    *
@@ -61,31 +86,6 @@
       .trigger('dialogContentResize');
   }
 
-  /**
-   * Position the dialog's center at the center of displace.offsets boundaries.
-   *
-   * @function Drupal.dialog~resetPosition
-   *
-   * @param {object} options
-   *   Options object.
-   *
-   * @return {object}
-   *   Altered options object.
-   */
-  function resetPosition(options) {
-    const offsets = displace.offsets;
-    const left = offsets.left - offsets.right;
-    const top = offsets.top - offsets.bottom;
-
-    const leftString = `${(left > 0 ? '+' : '-') + Math.abs(Math.round(left / 2))}px`;
-    const topString = `${(top > 0 ? '+' : '-') + Math.abs(Math.round(top / 2))}px`;
-    options.position = {
-      my: `center${left !== 0 ? leftString : ''} center${top !== 0 ? topString : ''}`,
-      of: window,
-    };
-    return options;
-  }
-
   $(window).on({
     'dialog:aftercreate': function (event, dialog, $element, settings) {
       const autoResize = debounce(resetSize, 20);
diff --git a/core/misc/dialog/dialog.position.js b/core/misc/dialog/dialog.position.js
index 7ff530f580e67947b8fa197a52560768e22bc608..843bf3b249ef95b78c5b720bdf881a874ae69787 100644
--- a/core/misc/dialog/dialog.position.js
+++ b/core/misc/dialog/dialog.position.js
@@ -8,6 +8,20 @@
 (function ($, Drupal, drupalSettings, debounce, displace) {
   drupalSettings.dialog = $.extend({ autoResize: true, maxHeight: '95%' }, drupalSettings.dialog);
 
+  function resetPosition(options) {
+    var offsets = displace.offsets;
+    var left = offsets.left - offsets.right;
+    var top = offsets.top - offsets.bottom;
+
+    var leftString = (left > 0 ? '+' : '-') + Math.abs(Math.round(left / 2)) + 'px';
+    var topString = (top > 0 ? '+' : '-') + Math.abs(Math.round(top / 2)) + 'px';
+    options.position = {
+      my: 'center' + (left !== 0 ? leftString : '') + ' center' + (top !== 0 ? topString : ''),
+      of: window
+    };
+    return options;
+  }
+
   function resetSize(event) {
     var positionOptions = ['width', 'height', 'minWidth', 'minHeight', 'maxHeight', 'maxWidth', 'position'];
     var adjustedOptions = {};
@@ -37,20 +51,6 @@
     event.data.$element.dialog('option', adjustedOptions).trigger('dialogContentResize');
   }
 
-  function resetPosition(options) {
-    var offsets = displace.offsets;
-    var left = offsets.left - offsets.right;
-    var top = offsets.top - offsets.bottom;
-
-    var leftString = (left > 0 ? '+' : '-') + Math.abs(Math.round(left / 2)) + 'px';
-    var topString = (top > 0 ? '+' : '-') + Math.abs(Math.round(top / 2)) + 'px';
-    options.position = {
-      my: 'center' + (left !== 0 ? leftString : '') + ' center' + (top !== 0 ? topString : ''),
-      of: window
-    };
-    return options;
-  }
-
   $(window).on({
     'dialog:aftercreate': function dialogAftercreate(event, dialog, $element, settings) {
       var autoResize = debounce(resetSize, 20);
diff --git a/core/misc/displace.es6.js b/core/misc/displace.es6.js
index 6ac09f4e6aae63778a7e40ef991f0d7fd4e21c4d..4a87edc3d12245746a8c3d75c01c5a164b781d3e 100644
--- a/core/misc/displace.es6.js
+++ b/core/misc/displace.es6.js
@@ -38,64 +38,56 @@
   };
 
   /**
-   * Registers a resize handler on the window.
-   *
-   * @type {Drupal~behavior}
-   */
-  Drupal.behaviors.drupalDisplace = {
-    attach() {
-      // Mark this behavior as processed on the first pass.
-      if (this.displaceProcessed) {
-        return;
-      }
-      this.displaceProcessed = true;
-
-      $(window).on('resize.drupalDisplace', debounce(displace, 200));
-    },
-  };
-
-  /**
-   * Informs listeners of the current offset dimensions.
-   *
-   * @function Drupal.displace
-   *
-   * @prop {Drupal~displaceOffset} offsets
+   * Calculates displacement for element based on its dimensions and placement.
    *
-   * @param {bool} [broadcast]
-   *   When true or undefined, causes the recalculated offsets values to be
-   *   broadcast to listeners.
+   * @param {HTMLElement} el
+   *   The jQuery element whose dimensions and placement will be measured.
    *
-   * @return {Drupal~displaceOffset}
-   *   An object whose keys are the for sides an element -- top, right, bottom
-   *   and left. The value of each key is the viewport displacement distance for
-   *   that edge.
+   * @param {string} edge
+   *   The name of the edge of the viewport that the element is associated
+   *   with.
    *
-   * @fires event:drupalViewportOffsetChange
+   * @return {number}
+   *   The viewport displacement distance for the requested edge.
    */
-  function displace(broadcast) {
-    offsets = calculateOffsets();
-    Drupal.displace.offsets = offsets;
-    if (typeof broadcast === 'undefined' || broadcast) {
-      $(document).trigger('drupalViewportOffsetChange', offsets);
-    }
-    return offsets;
-  }
+  function getRawOffset(el, edge) {
+    const $el = $(el);
+    const documentElement = document.documentElement;
+    let displacement = 0;
+    const horizontal = (edge === 'left' || edge === 'right');
+    // Get the offset of the element itself.
+    let placement = $el.offset()[horizontal ? 'left' : 'top'];
+    // Subtract scroll distance from placement to get the distance
+    // to the edge of the viewport.
+    placement -= window[`scroll${horizontal ? 'X' : 'Y'}`] || document.documentElement[`scroll${horizontal ? 'Left' : 'Top'}`] || 0;
+    // Find the displacement value according to the edge.
+    switch (edge) {
+      // Left and top elements displace as a sum of their own offset value
+      // plus their size.
+      case 'top':
+        // Total displacement is the sum of the elements placement and size.
+        displacement = placement + $el.outerHeight();
+        break;
 
-  /**
-   * Determines the viewport offsets.
-   *
-   * @return {Drupal~displaceOffset}
-   *   An object whose keys are the for sides an element -- top, right, bottom
-   *   and left. The value of each key is the viewport displacement distance for
-   *   that edge.
-   */
-  function calculateOffsets() {
-    return {
-      top: calculateOffset('top'),
-      right: calculateOffset('right'),
-      bottom: calculateOffset('bottom'),
-      left: calculateOffset('left'),
-    };
+      case 'left':
+        // Total displacement is the sum of the elements placement and size.
+        displacement = placement + $el.outerWidth();
+        break;
+
+      // Right and bottom elements displace according to their left and
+      // top offset. Their size isn't important.
+      case 'bottom':
+        displacement = documentElement.clientHeight - placement;
+        break;
+
+      case 'right':
+        displacement = documentElement.clientWidth - placement;
+        break;
+
+      default:
+        displacement = 0;
+    }
+    return displacement;
   }
 
   /**
@@ -142,58 +134,66 @@
   }
 
   /**
-   * Calculates displacement for element based on its dimensions and placement.
+   * Determines the viewport offsets.
    *
-   * @param {HTMLElement} el
-   *   The jQuery element whose dimensions and placement will be measured.
+   * @return {Drupal~displaceOffset}
+   *   An object whose keys are the for sides an element -- top, right, bottom
+   *   and left. The value of each key is the viewport displacement distance for
+   *   that edge.
+   */
+  function calculateOffsets() {
+    return {
+      top: calculateOffset('top'),
+      right: calculateOffset('right'),
+      bottom: calculateOffset('bottom'),
+      left: calculateOffset('left'),
+    };
+  }
+
+  /**
+   * Informs listeners of the current offset dimensions.
    *
-   * @param {string} edge
-   *   The name of the edge of the viewport that the element is associated
-   *   with.
+   * @function Drupal.displace
    *
-   * @return {number}
-   *   The viewport displacement distance for the requested edge.
+   * @prop {Drupal~displaceOffset} offsets
+   *
+   * @param {bool} [broadcast]
+   *   When true or undefined, causes the recalculated offsets values to be
+   *   broadcast to listeners.
+   *
+   * @return {Drupal~displaceOffset}
+   *   An object whose keys are the for sides an element -- top, right, bottom
+   *   and left. The value of each key is the viewport displacement distance for
+   *   that edge.
+   *
+   * @fires event:drupalViewportOffsetChange
    */
-  function getRawOffset(el, edge) {
-    const $el = $(el);
-    const documentElement = document.documentElement;
-    let displacement = 0;
-    const horizontal = (edge === 'left' || edge === 'right');
-    // Get the offset of the element itself.
-    let placement = $el.offset()[horizontal ? 'left' : 'top'];
-    // Subtract scroll distance from placement to get the distance
-    // to the edge of the viewport.
-    placement -= window[`scroll${horizontal ? 'X' : 'Y'}`] || document.documentElement[`scroll${horizontal ? 'Left' : 'Top'}`] || 0;
-    // Find the displacement value according to the edge.
-    switch (edge) {
-      // Left and top elements displace as a sum of their own offset value
-      // plus their size.
-      case 'top':
-        // Total displacement is the sum of the elements placement and size.
-        displacement = placement + $el.outerHeight();
-        break;
-
-      case 'left':
-        // Total displacement is the sum of the elements placement and size.
-        displacement = placement + $el.outerWidth();
-        break;
-
-      // Right and bottom elements displace according to their left and
-      // top offset. Their size isn't important.
-      case 'bottom':
-        displacement = documentElement.clientHeight - placement;
-        break;
-
-      case 'right':
-        displacement = documentElement.clientWidth - placement;
-        break;
-
-      default:
-        displacement = 0;
+  function displace(broadcast) {
+    offsets = calculateOffsets();
+    Drupal.displace.offsets = offsets;
+    if (typeof broadcast === 'undefined' || broadcast) {
+      $(document).trigger('drupalViewportOffsetChange', offsets);
     }
-    return displacement;
+    return offsets;
   }
 
+  /**
+   * Registers a resize handler on the window.
+   *
+   * @type {Drupal~behavior}
+   */
+  Drupal.behaviors.drupalDisplace = {
+    attach() {
+      // Mark this behavior as processed on the first pass.
+      if (this.displaceProcessed) {
+        return;
+      }
+      this.displaceProcessed = true;
+
+      $(window).on('resize.drupalDisplace', debounce(displace, 200));
+    },
+  };
+
   /**
    * Assign the displace function to a property of the Drupal global object.
    *
diff --git a/core/misc/displace.js b/core/misc/displace.js
index 777aee478d75a2a7e742bce579d575e183bf1b8c..9192fdb3a1ca46061e969d76c6ed1943d638771f 100644
--- a/core/misc/displace.js
+++ b/core/misc/displace.js
@@ -13,58 +13,6 @@
     left: 0
   };
 
-  Drupal.behaviors.drupalDisplace = {
-    attach: function attach() {
-      if (this.displaceProcessed) {
-        return;
-      }
-      this.displaceProcessed = true;
-
-      $(window).on('resize.drupalDisplace', debounce(displace, 200));
-    }
-  };
-
-  function displace(broadcast) {
-    offsets = calculateOffsets();
-    Drupal.displace.offsets = offsets;
-    if (typeof broadcast === 'undefined' || broadcast) {
-      $(document).trigger('drupalViewportOffsetChange', offsets);
-    }
-    return offsets;
-  }
-
-  function calculateOffsets() {
-    return {
-      top: calculateOffset('top'),
-      right: calculateOffset('right'),
-      bottom: calculateOffset('bottom'),
-      left: calculateOffset('left')
-    };
-  }
-
-  function calculateOffset(edge) {
-    var edgeOffset = 0;
-    var displacingElements = document.querySelectorAll('[data-offset-' + edge + ']');
-    var n = displacingElements.length;
-    for (var i = 0; i < n; i++) {
-      var el = displacingElements[i];
-
-      if (el.style.display === 'none') {
-        continue;
-      }
-
-      var displacement = parseInt(el.getAttribute('data-offset-' + edge), 10);
-
-      if (isNaN(displacement)) {
-        displacement = getRawOffset(el, edge);
-      }
-
-      edgeOffset = Math.max(edgeOffset, displacement);
-    }
-
-    return edgeOffset;
-  }
-
   function getRawOffset(el, edge) {
     var $el = $(el);
     var documentElement = document.documentElement;
@@ -98,6 +46,58 @@
     return displacement;
   }
 
+  function calculateOffset(edge) {
+    var edgeOffset = 0;
+    var displacingElements = document.querySelectorAll('[data-offset-' + edge + ']');
+    var n = displacingElements.length;
+    for (var i = 0; i < n; i++) {
+      var el = displacingElements[i];
+
+      if (el.style.display === 'none') {
+        continue;
+      }
+
+      var displacement = parseInt(el.getAttribute('data-offset-' + edge), 10);
+
+      if (isNaN(displacement)) {
+        displacement = getRawOffset(el, edge);
+      }
+
+      edgeOffset = Math.max(edgeOffset, displacement);
+    }
+
+    return edgeOffset;
+  }
+
+  function calculateOffsets() {
+    return {
+      top: calculateOffset('top'),
+      right: calculateOffset('right'),
+      bottom: calculateOffset('bottom'),
+      left: calculateOffset('left')
+    };
+  }
+
+  function displace(broadcast) {
+    offsets = calculateOffsets();
+    Drupal.displace.offsets = offsets;
+    if (typeof broadcast === 'undefined' || broadcast) {
+      $(document).trigger('drupalViewportOffsetChange', offsets);
+    }
+    return offsets;
+  }
+
+  Drupal.behaviors.drupalDisplace = {
+    attach: function attach() {
+      if (this.displaceProcessed) {
+        return;
+      }
+      this.displaceProcessed = true;
+
+      $(window).on('resize.drupalDisplace', debounce(displace, 200));
+    }
+  };
+
   Drupal.displace = displace;
   $.extend(Drupal.displace, {
     offsets: offsets,
diff --git a/core/misc/dropbutton/dropbutton.es6.js b/core/misc/dropbutton/dropbutton.es6.js
index 6fe2b67db7be68849fec587c0b814f9367baea6a..73ff363feb4eb3d1818269c4fab880fc692384d5 100644
--- a/core/misc/dropbutton/dropbutton.es6.js
+++ b/core/misc/dropbutton/dropbutton.es6.js
@@ -4,45 +4,6 @@
  */
 
 (function ($, Drupal) {
-  /**
-   * Process elements with the .dropbutton class on page load.
-   *
-   * @type {Drupal~behavior}
-   *
-   * @prop {Drupal~behaviorAttach} attach
-   *   Attaches dropButton behaviors.
-   */
-  Drupal.behaviors.dropButton = {
-    attach(context, settings) {
-      const $dropbuttons = $(context).find('.dropbutton-wrapper').once('dropbutton');
-      if ($dropbuttons.length) {
-        // Adds the delegated handler that will toggle dropdowns on click.
-        const $body = $('body').once('dropbutton-click');
-        if ($body.length) {
-          $body.on('click', '.dropbutton-toggle', dropbuttonClickHandler);
-        }
-        // Initialize all buttons.
-        const il = $dropbuttons.length;
-        for (let i = 0; i < il; i++) {
-          DropButton.dropbuttons.push(new DropButton($dropbuttons[i], settings.dropbutton));
-        }
-      }
-    },
-  };
-
-  /**
-   * Delegated callback for opening and closing dropbutton secondary actions.
-   *
-   * @function Drupal.DropButton~dropbuttonClickHandler
-   *
-   * @param {jQuery.Event} e
-   *   The event triggered.
-   */
-  function dropbuttonClickHandler(e) {
-    e.preventDefault();
-    $(e.target).closest('.dropbutton-wrapper').toggleClass('open');
-  }
-
   /**
    * A DropButton presents an HTML list as a button with a primary action.
    *
@@ -127,6 +88,45 @@
     }
   }
 
+  /**
+   * Delegated callback for opening and closing dropbutton secondary actions.
+   *
+   * @function Drupal.DropButton~dropbuttonClickHandler
+   *
+   * @param {jQuery.Event} e
+   *   The event triggered.
+   */
+  function dropbuttonClickHandler(e) {
+    e.preventDefault();
+    $(e.target).closest('.dropbutton-wrapper').toggleClass('open');
+  }
+
+  /**
+   * Process elements with the .dropbutton class on page load.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches dropButton behaviors.
+   */
+  Drupal.behaviors.dropButton = {
+    attach(context, settings) {
+      const $dropbuttons = $(context).find('.dropbutton-wrapper').once('dropbutton');
+      if ($dropbuttons.length) {
+        // Adds the delegated handler that will toggle dropdowns on click.
+        const $body = $('body').once('dropbutton-click');
+        if ($body.length) {
+          $body.on('click', '.dropbutton-toggle', dropbuttonClickHandler);
+        }
+        // Initialize all buttons.
+        const il = $dropbuttons.length;
+        for (let i = 0; i < il; i++) {
+          DropButton.dropbuttons.push(new DropButton($dropbuttons[i], settings.dropbutton));
+        }
+      }
+    },
+  };
+
   /**
    * Extend the DropButton constructor.
    */
diff --git a/core/misc/dropbutton/dropbutton.js b/core/misc/dropbutton/dropbutton.js
index 448484d336543486b7a0954366466c141b257e2a..166eab5845258c349aca0308457eef476681e3ef 100644
--- a/core/misc/dropbutton/dropbutton.js
+++ b/core/misc/dropbutton/dropbutton.js
@@ -6,28 +6,6 @@
 **/
 
 (function ($, Drupal) {
-  Drupal.behaviors.dropButton = {
-    attach: function attach(context, settings) {
-      var $dropbuttons = $(context).find('.dropbutton-wrapper').once('dropbutton');
-      if ($dropbuttons.length) {
-        var $body = $('body').once('dropbutton-click');
-        if ($body.length) {
-          $body.on('click', '.dropbutton-toggle', dropbuttonClickHandler);
-        }
-
-        var il = $dropbuttons.length;
-        for (var i = 0; i < il; i++) {
-          DropButton.dropbuttons.push(new DropButton($dropbuttons[i], settings.dropbutton));
-        }
-      }
-    }
-  };
-
-  function dropbuttonClickHandler(e) {
-    e.preventDefault();
-    $(e.target).closest('.dropbutton-wrapper').toggleClass('open');
-  }
-
   function DropButton(dropbutton, settings) {
     var options = $.extend({ title: Drupal.t('List additional actions') }, settings);
     var $dropbutton = $(dropbutton);
@@ -60,6 +38,28 @@
     }
   }
 
+  function dropbuttonClickHandler(e) {
+    e.preventDefault();
+    $(e.target).closest('.dropbutton-wrapper').toggleClass('open');
+  }
+
+  Drupal.behaviors.dropButton = {
+    attach: function attach(context, settings) {
+      var $dropbuttons = $(context).find('.dropbutton-wrapper').once('dropbutton');
+      if ($dropbuttons.length) {
+        var $body = $('body').once('dropbutton-click');
+        if ($body.length) {
+          $body.on('click', '.dropbutton-toggle', dropbuttonClickHandler);
+        }
+
+        var il = $dropbuttons.length;
+        for (var i = 0; i < il; i++) {
+          DropButton.dropbuttons.push(new DropButton($dropbuttons[i], settings.dropbutton));
+        }
+      }
+    }
+  };
+
   $.extend(DropButton, {
     dropbuttons: []
   });
diff --git a/core/misc/states.es6.js b/core/misc/states.es6.js
index 2274874f9050bcb3efb947de3c85924f6e5396e9..358277963321c879bf4fc15b197fd7b51410ce47 100644
--- a/core/misc/states.es6.js
+++ b/core/misc/states.es6.js
@@ -22,6 +22,44 @@
 
   Drupal.states = states;
 
+  /**
+   * Inverts a (if it's not undefined) when invertState is true.
+   *
+   * @function Drupal.states~invert
+   *
+   * @param {*} a
+   *   The value to maybe invert.
+   * @param {bool} invertState
+   *   Whether to invert state or not.
+   *
+   * @return {bool}
+   *   The result.
+   */
+  function invert(a, invertState) {
+    return (invertState && typeof a !== 'undefined') ? !a : a;
+  }
+
+  /**
+   * Compares two values while ignoring undefined values.
+   *
+   * @function Drupal.states~compare
+   *
+   * @param {*} a
+   *   Value a.
+   * @param {*} b
+   *   Value b.
+   *
+   * @return {bool}
+   *   The comparison result.
+   */
+  function compare(a, b) {
+    if (a === b) {
+      return typeof a === 'undefined' ? a : true;
+    }
+
+    return typeof a === 'undefined' || typeof b === 'undefined';
+  }
+
   /**
    * Attaches the states.
    *
@@ -642,47 +680,4 @@
       }
     }
   });
-
-  /**
-   * These are helper functions implementing addition "operators" and don't
-   * implement any logic that is particular to states.
-   */
-
-  /**
-   * Inverts a (if it's not undefined) when invertState is true.
-   *
-   * @function Drupal.states~invert
-   *
-   * @param {*} a
-   *   The value to maybe invert.
-   * @param {bool} invertState
-   *   Whether to invert state or not.
-   *
-   * @return {bool}
-   *   The result.
-   */
-  function invert(a, invertState) {
-    return (invertState && typeof a !== 'undefined') ? !a : a;
-  }
-
-  /**
-   * Compares two values while ignoring undefined values.
-   *
-   * @function Drupal.states~compare
-   *
-   * @param {*} a
-   *   Value a.
-   * @param {*} b
-   *   Value b.
-   *
-   * @return {bool}
-   *   The comparison result.
-   */
-  function compare(a, b) {
-    if (a === b) {
-      return typeof a === 'undefined' ? a : true;
-    }
-
-    return typeof a === 'undefined' || typeof b === 'undefined';
-  }
 }(jQuery, Drupal));
diff --git a/core/misc/states.js b/core/misc/states.js
index 2d1d84df314e398a27a9e93be5a85b3d9f751b56..a8c58021780aef4919bced1d30dcb970c255fda0 100644
--- a/core/misc/states.js
+++ b/core/misc/states.js
@@ -12,6 +12,18 @@
 
   Drupal.states = states;
 
+  function invert(a, invertState) {
+    return invertState && typeof a !== 'undefined' ? !a : a;
+  }
+
+  function _compare2(a, b) {
+    if (a === b) {
+      return typeof a === 'undefined' ? a : true;
+    }
+
+    return typeof a === 'undefined' || typeof b === 'undefined';
+  }
+
   Drupal.behaviors.states = {
     attach: function attach(context, settings) {
       var $states = $(context).find('[data-drupal-states]');
@@ -345,16 +357,4 @@
       }
     }
   });
-
-  function invert(a, invertState) {
-    return invertState && typeof a !== 'undefined' ? !a : a;
-  }
-
-  function _compare2(a, b) {
-    if (a === b) {
-      return typeof a === 'undefined' ? a : true;
-    }
-
-    return typeof a === 'undefined' || typeof b === 'undefined';
-  }
 })(jQuery, Drupal);
\ No newline at end of file
diff --git a/core/misc/tabbingmanager.es6.js b/core/misc/tabbingmanager.es6.js
index 075dda5177d0c911ec15cc442e83a06ffb97ff16..5183d8bca6c08fa42db5f5835fd716ec25d505f9 100644
--- a/core/misc/tabbingmanager.es6.js
+++ b/core/misc/tabbingmanager.es6.js
@@ -46,6 +46,61 @@
     this.stack = [];
   }
 
+  /**
+   * Stores a set of tabbable elements.
+   *
+   * This constraint can be removed with the release() method.
+   *
+   * @constructor Drupal~TabbingContext
+   *
+   * @param {object} options
+   *   A set of initiating values
+   * @param {number} options.level
+   *   The level in the TabbingManager's stack of this tabbingContext.
+   * @param {jQuery} options.$tabbableElements
+   *   The DOM elements that should be reachable via the tab key when this
+   *   tabbingContext is active.
+   * @param {jQuery} options.$disabledElements
+   *   The DOM elements that should not be reachable via the tab key when this
+   *   tabbingContext is active.
+   * @param {bool} options.released
+   *   A released tabbingContext can never be activated again. It will be
+   *   cleaned up when the TabbingManager unwinds its stack.
+   * @param {bool} options.active
+   *   When true, the tabbable elements of this tabbingContext will be reachable
+   *   via the tab key and the disabled elements will not. Only one
+   *   tabbingContext can be active at a time.
+   */
+  function TabbingContext(options) {
+    $.extend(this, /** @lends Drupal~TabbingContext# */{
+
+      /**
+       * @type {?number}
+       */
+      level: null,
+
+      /**
+       * @type {jQuery}
+       */
+      $tabbableElements: $(),
+
+      /**
+       * @type {jQuery}
+       */
+      $disabledElements: $(),
+
+      /**
+       * @type {bool}
+       */
+      released: false,
+
+      /**
+       * @type {bool}
+       */
+      active: false,
+    }, options);
+  }
+
   /**
    * Add public methods to the TabbingManager class.
    */
@@ -242,61 +297,6 @@
     },
   });
 
-  /**
-   * Stores a set of tabbable elements.
-   *
-   * This constraint can be removed with the release() method.
-   *
-   * @constructor Drupal~TabbingContext
-   *
-   * @param {object} options
-   *   A set of initiating values
-   * @param {number} options.level
-   *   The level in the TabbingManager's stack of this tabbingContext.
-   * @param {jQuery} options.$tabbableElements
-   *   The DOM elements that should be reachable via the tab key when this
-   *   tabbingContext is active.
-   * @param {jQuery} options.$disabledElements
-   *   The DOM elements that should not be reachable via the tab key when this
-   *   tabbingContext is active.
-   * @param {bool} options.released
-   *   A released tabbingContext can never be activated again. It will be
-   *   cleaned up when the TabbingManager unwinds its stack.
-   * @param {bool} options.active
-   *   When true, the tabbable elements of this tabbingContext will be reachable
-   *   via the tab key and the disabled elements will not. Only one
-   *   tabbingContext can be active at a time.
-   */
-  function TabbingContext(options) {
-    $.extend(this, /** @lends Drupal~TabbingContext# */{
-
-      /**
-       * @type {?number}
-       */
-      level: null,
-
-      /**
-       * @type {jQuery}
-       */
-      $tabbableElements: $(),
-
-      /**
-       * @type {jQuery}
-       */
-      $disabledElements: $(),
-
-      /**
-       * @type {bool}
-       */
-      released: false,
-
-      /**
-       * @type {bool}
-       */
-      active: false,
-    }, options);
-  }
-
   /**
    * Add public methods to the TabbingContext class.
    */
diff --git a/core/misc/tabbingmanager.js b/core/misc/tabbingmanager.js
index 2d0b92716d96656617b6bffe15a0ca70d6a78b48..c912c92684e8e92bf62d2672c9f872c70cc82cdd 100644
--- a/core/misc/tabbingmanager.js
+++ b/core/misc/tabbingmanager.js
@@ -10,6 +10,20 @@
     this.stack = [];
   }
 
+  function TabbingContext(options) {
+    $.extend(this, {
+      level: null,
+
+      $tabbableElements: $(),
+
+      $disabledElements: $(),
+
+      released: false,
+
+      active: false
+    }, options);
+  }
+
   $.extend(TabbingManager.prototype, {
     constrain: function constrain(elements) {
       var il = this.stack.length;
@@ -109,20 +123,6 @@
     }
   });
 
-  function TabbingContext(options) {
-    $.extend(this, {
-      level: null,
-
-      $tabbableElements: $(),
-
-      $disabledElements: $(),
-
-      released: false,
-
-      active: false
-    }, options);
-  }
-
   $.extend(TabbingContext.prototype, {
     release: function release() {
       if (!this.released) {
diff --git a/core/misc/tableheader.es6.js b/core/misc/tableheader.es6.js
index a5aa6635d34468c747f6e7ea9ef7c153e3f6cced..289e3fcba7b67ad1c6241d74e6ad8aecbadc2e6c 100644
--- a/core/misc/tableheader.es6.js
+++ b/core/misc/tableheader.es6.js
@@ -5,21 +5,66 @@
 
 (function ($, Drupal, displace) {
   /**
-   * Attaches sticky table headers.
+   * Constructor for the tableHeader object. Provides sticky table headers.
    *
-   * @type {Drupal~behavior}
+   * TableHeader will make the current table header stick to the top of the page
+   * if the table is very long.
    *
-   * @prop {Drupal~behaviorAttach} attach
-   *   Attaches the sticky table header behavior.
+   * @constructor Drupal.TableHeader
+   *
+   * @param {HTMLElement} table
+   *   DOM object for the table to add a sticky header to.
+   *
+   * @listens event:columnschange
    */
-  Drupal.behaviors.tableHeader = {
-    attach(context) {
-      $(window).one('scroll.TableHeaderInit', { context }, tableHeaderInitHandler);
-    },
-  };
+  function TableHeader(table) {
+    const $table = $(table);
 
-  function scrollValue(position) {
-    return document.documentElement[position] || document.body[position];
+    /**
+     * @name Drupal.TableHeader#$originalTable
+     *
+     * @type {HTMLElement}
+     */
+    this.$originalTable = $table;
+
+    /**
+     * @type {jQuery}
+     */
+    this.$originalHeader = $table.children('thead');
+
+    /**
+     * @type {jQuery}
+     */
+    this.$originalHeaderCells = this.$originalHeader.find('> tr > th');
+
+    /**
+     * @type {null|bool}
+     */
+    this.displayWeight = null;
+    this.$originalTable.addClass('sticky-table');
+    this.tableHeight = $table[0].clientHeight;
+    this.tableOffset = this.$originalTable.offset();
+
+    // React to columns change to avoid making checks in the scroll callback.
+    this.$originalTable.on('columnschange', { tableHeader: this }, (e, display) => {
+      const tableHeader = e.data.tableHeader;
+      if (tableHeader.displayWeight === null || tableHeader.displayWeight !== display) {
+        tableHeader.recalculateSticky();
+      }
+      tableHeader.displayWeight = display;
+    });
+
+    // Create and display sticky header.
+    this.createSticky();
+  }
+
+  // Helper method to loop through tables and execute a method.
+  function forTables(method, arg) {
+    const tables = TableHeader.tables;
+    const il = tables.length;
+    for (let i = 0; i < il; i++) {
+      tables[i][method](arg);
+    }
   }
 
   // Select and initialize sticky table headers.
@@ -32,13 +77,22 @@
     forTables('onScroll');
   }
 
-  // Helper method to loop through tables and execute a method.
-  function forTables(method, arg) {
-    const tables = TableHeader.tables;
-    const il = tables.length;
-    for (let i = 0; i < il; i++) {
-      tables[i][method](arg);
-    }
+  /**
+   * Attaches sticky table headers.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches the sticky table header behavior.
+   */
+  Drupal.behaviors.tableHeader = {
+    attach(context) {
+      $(window).one('scroll.TableHeaderInit', { context }, tableHeaderInitHandler);
+    },
+  };
+
+  function scrollValue(position) {
+    return document.documentElement[position] || document.body[position];
   }
 
   function tableHeaderResizeHandler(e) {
@@ -89,60 +143,6 @@
     'drupalViewportOffsetChange.TableHeader': tableHeaderOffsetChangeHandler,
   });
 
-  /**
-   * Constructor for the tableHeader object. Provides sticky table headers.
-   *
-   * TableHeader will make the current table header stick to the top of the page
-   * if the table is very long.
-   *
-   * @constructor Drupal.TableHeader
-   *
-   * @param {HTMLElement} table
-   *   DOM object for the table to add a sticky header to.
-   *
-   * @listens event:columnschange
-   */
-  function TableHeader(table) {
-    const $table = $(table);
-
-    /**
-     * @name Drupal.TableHeader#$originalTable
-     *
-     * @type {HTMLElement}
-     */
-    this.$originalTable = $table;
-
-    /**
-     * @type {jQuery}
-     */
-    this.$originalHeader = $table.children('thead');
-
-    /**
-     * @type {jQuery}
-     */
-    this.$originalHeaderCells = this.$originalHeader.find('> tr > th');
-
-    /**
-     * @type {null|bool}
-     */
-    this.displayWeight = null;
-    this.$originalTable.addClass('sticky-table');
-    this.tableHeight = $table[0].clientHeight;
-    this.tableOffset = this.$originalTable.offset();
-
-    // React to columns change to avoid making checks in the scroll callback.
-    this.$originalTable.on('columnschange', { tableHeader: this }, (e, display) => {
-      const tableHeader = e.data.tableHeader;
-      if (tableHeader.displayWeight === null || tableHeader.displayWeight !== display) {
-        tableHeader.recalculateSticky();
-      }
-      tableHeader.displayWeight = display;
-    });
-
-    // Create and display sticky header.
-    this.createSticky();
-  }
-
   /**
    * Store the state of TableHeader.
    */
diff --git a/core/misc/tableheader.js b/core/misc/tableheader.js
index bce54ec7d43d9cf9d6bbac087ba22a455b9fa088..1fd60086db89dc1a0516c7d2c3944f725c9d9926 100644
--- a/core/misc/tableheader.js
+++ b/core/misc/tableheader.js
@@ -6,14 +6,37 @@
 **/
 
 (function ($, Drupal, displace) {
-  Drupal.behaviors.tableHeader = {
-    attach: function attach(context) {
-      $(window).one('scroll.TableHeaderInit', { context: context }, tableHeaderInitHandler);
-    }
-  };
+  function TableHeader(table) {
+    var $table = $(table);
 
-  function scrollValue(position) {
-    return document.documentElement[position] || document.body[position];
+    this.$originalTable = $table;
+
+    this.$originalHeader = $table.children('thead');
+
+    this.$originalHeaderCells = this.$originalHeader.find('> tr > th');
+
+    this.displayWeight = null;
+    this.$originalTable.addClass('sticky-table');
+    this.tableHeight = $table[0].clientHeight;
+    this.tableOffset = this.$originalTable.offset();
+
+    this.$originalTable.on('columnschange', { tableHeader: this }, function (e, display) {
+      var tableHeader = e.data.tableHeader;
+      if (tableHeader.displayWeight === null || tableHeader.displayWeight !== display) {
+        tableHeader.recalculateSticky();
+      }
+      tableHeader.displayWeight = display;
+    });
+
+    this.createSticky();
+  }
+
+  function forTables(method, arg) {
+    var tables = TableHeader.tables;
+    var il = tables.length;
+    for (var i = 0; i < il; i++) {
+      tables[i][method](arg);
+    }
   }
 
   function tableHeaderInitHandler(e) {
@@ -25,12 +48,14 @@
     forTables('onScroll');
   }
 
-  function forTables(method, arg) {
-    var tables = TableHeader.tables;
-    var il = tables.length;
-    for (var i = 0; i < il; i++) {
-      tables[i][method](arg);
+  Drupal.behaviors.tableHeader = {
+    attach: function attach(context) {
+      $(window).one('scroll.TableHeaderInit', { context: context }, tableHeaderInitHandler);
     }
+  };
+
+  function scrollValue(position) {
+    return document.documentElement[position] || document.body[position];
   }
 
   function tableHeaderResizeHandler(e) {
@@ -57,31 +82,6 @@
     'drupalViewportOffsetChange.TableHeader': tableHeaderOffsetChangeHandler
   });
 
-  function TableHeader(table) {
-    var $table = $(table);
-
-    this.$originalTable = $table;
-
-    this.$originalHeader = $table.children('thead');
-
-    this.$originalHeaderCells = this.$originalHeader.find('> tr > th');
-
-    this.displayWeight = null;
-    this.$originalTable.addClass('sticky-table');
-    this.tableHeight = $table[0].clientHeight;
-    this.tableOffset = this.$originalTable.offset();
-
-    this.$originalTable.on('columnschange', { tableHeader: this }, function (e, display) {
-      var tableHeader = e.data.tableHeader;
-      if (tableHeader.displayWeight === null || tableHeader.displayWeight !== display) {
-        tableHeader.recalculateSticky();
-      }
-      tableHeader.displayWeight = display;
-    });
-
-    this.createSticky();
-  }
-
   $.extend(TableHeader, {
     tables: []
   });
diff --git a/core/misc/tableresponsive.es6.js b/core/misc/tableresponsive.es6.js
index 8d1ae0115e77f647a8a02592cce076e42eedee34..6030b6e37c06f5c0df95ca752bccc9fb6939f2d5 100644
--- a/core/misc/tableresponsive.es6.js
+++ b/core/misc/tableresponsive.es6.js
@@ -4,26 +4,6 @@
  */
 
 (function ($, Drupal, window) {
-  /**
-   * Attach the tableResponsive function to {@link Drupal.behaviors}.
-   *
-   * @type {Drupal~behavior}
-   *
-   * @prop {Drupal~behaviorAttach} attach
-   *   Attaches tableResponsive functionality.
-   */
-  Drupal.behaviors.tableResponsive = {
-    attach(context, settings) {
-      const $tables = $(context).find('table.responsive-enabled').once('tableresponsive');
-      if ($tables.length) {
-        const il = $tables.length;
-        for (let i = 0; i < il; i++) {
-          TableResponsive.tables.push(new TableResponsive($tables[i]));
-        }
-      }
-    },
-  };
-
   /**
    * The TableResponsive object optimizes table presentation for screen size.
    *
@@ -60,6 +40,26 @@
       .trigger('resize.tableresponsive');
   }
 
+  /**
+   * Attach the tableResponsive function to {@link Drupal.behaviors}.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches tableResponsive functionality.
+   */
+  Drupal.behaviors.tableResponsive = {
+    attach(context, settings) {
+      const $tables = $(context).find('table.responsive-enabled').once('tableresponsive');
+      if ($tables.length) {
+        const il = $tables.length;
+        for (let i = 0; i < il; i++) {
+          TableResponsive.tables.push(new TableResponsive($tables[i]));
+        }
+      }
+    },
+  };
+
   /**
    * Extend the TableResponsive function with a list of managed tables.
    */
diff --git a/core/misc/tableresponsive.js b/core/misc/tableresponsive.js
index d4fa6315cc11dfca2a2b0ccc45447f12b16e187d..0de5eee23cd66e3627d2fc0c4ea92eeb9229c41e 100644
--- a/core/misc/tableresponsive.js
+++ b/core/misc/tableresponsive.js
@@ -6,18 +6,6 @@
 **/
 
 (function ($, Drupal, window) {
-  Drupal.behaviors.tableResponsive = {
-    attach: function attach(context, settings) {
-      var $tables = $(context).find('table.responsive-enabled').once('tableresponsive');
-      if ($tables.length) {
-        var il = $tables.length;
-        for (var i = 0; i < il; i++) {
-          TableResponsive.tables.push(new TableResponsive($tables[i]));
-        }
-      }
-    }
-  };
-
   function TableResponsive(table) {
     this.table = table;
     this.$table = $(table);
@@ -33,6 +21,18 @@
     $(window).on('resize.tableresponsive', $.proxy(this, 'eventhandlerEvaluateColumnVisibility')).trigger('resize.tableresponsive');
   }
 
+  Drupal.behaviors.tableResponsive = {
+    attach: function attach(context, settings) {
+      var $tables = $(context).find('table.responsive-enabled').once('tableresponsive');
+      if ($tables.length) {
+        var il = $tables.length;
+        for (var i = 0; i < il; i++) {
+          TableResponsive.tables.push(new TableResponsive($tables[i]));
+        }
+      }
+    }
+  };
+
   $.extend(TableResponsive, {
     tables: []
   });
diff --git a/core/modules/big_pipe/js/big_pipe.es6.js b/core/modules/big_pipe/js/big_pipe.es6.js
index df72915a8ed674a6e73a505839d929421ecc2c01..afaa6aa67bb75a43d0218bd3132e974c2bb8f466 100644
--- a/core/modules/big_pipe/js/big_pipe.es6.js
+++ b/core/modules/big_pipe/js/big_pipe.es6.js
@@ -4,6 +4,28 @@
  */
 
 (function ($, Drupal, drupalSettings) {
+  /**
+   * 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.
    *
@@ -46,27 +68,13 @@
     }
   }
 
-  /**
-   * 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;
-    }
+  // The frequency with which to check for newly arrived BigPipe placeholders.
+  // Hence 50 ms means we check 20 times per second. Setting this to 100 ms or
+  // more would cause the user to see content appear noticeably slower.
+  const interval = drupalSettings.bigPipeInterval || 50;
 
-    try {
-      return JSON.parse(content);
-    }
-    catch (e) {
-      return false;
-    }
-  }
+  // The internal ID to contain the watcher service.
+  let timeoutID;
 
   /**
    * Processes a streamed HTML document receiving placeholder replacements.
@@ -109,13 +117,6 @@
     }, interval);
   }
 
-  // The frequency with which to check for newly arrived BigPipe placeholders.
-  // Hence 50 ms means we check 20 times per second. Setting this to 100 ms or
-  // more would cause the user to see content appear noticeably slower.
-  const interval = drupalSettings.bigPipeInterval || 50;
-  // The internal ID to contain the watcher service.
-  let timeoutID;
-
   bigPipeProcess();
 
   // If something goes wrong, make sure everything is cleaned up and has had a
diff --git a/core/modules/big_pipe/js/big_pipe.js b/core/modules/big_pipe/js/big_pipe.js
index b8a25f77cc8987832ec660ba87dea22329419b83..e386191ac1b99fce7e188ecc06eb885c455b0703 100644
--- a/core/modules/big_pipe/js/big_pipe.js
+++ b/core/modules/big_pipe/js/big_pipe.js
@@ -6,6 +6,18 @@
 **/
 
 (function ($, Drupal, drupalSettings) {
+  function mapTextContentToAjaxResponse(content) {
+    if (content === '') {
+      return false;
+    }
+
+    try {
+      return JSON.parse(content);
+    } catch (e) {
+      return false;
+    }
+  }
+
   function bigPipeProcessPlaceholderReplacement(index, placeholderReplacement) {
     var placeholderId = placeholderReplacement.getAttribute('data-big-pipe-replacement-for-placeholder-with-id');
     var content = this.textContent.trim();
@@ -28,17 +40,9 @@
     }
   }
 
-  function mapTextContentToAjaxResponse(content) {
-    if (content === '') {
-      return false;
-    }
+  var interval = drupalSettings.bigPipeInterval || 50;
 
-    try {
-      return JSON.parse(content);
-    } catch (e) {
-      return false;
-    }
-  }
+  var timeoutID = void 0;
 
   function bigPipeProcessDocument(context) {
     if (!context.querySelector('script[data-big-pipe-event="start"]')) {
@@ -65,10 +69,6 @@
     }, interval);
   }
 
-  var interval = drupalSettings.bigPipeInterval || 50;
-
-  var timeoutID = void 0;
-
   bigPipeProcess();
 
   $(window).on('load', function () {
diff --git a/core/modules/ckeditor/js/ckeditor.admin.es6.js b/core/modules/ckeditor/js/ckeditor.admin.es6.js
index 4cf6a63e44be1750eb3554fb4da850cd5e49bf57..687169f5764aa1275a2da43baf80493e3b98212d 100644
--- a/core/modules/ckeditor/js/ckeditor.admin.es6.js
+++ b/core/modules/ckeditor/js/ckeditor.admin.es6.js
@@ -215,6 +215,7 @@
          * Closes the dialog when the user cancels or supplies valid data.
          */
         function shutdown() {
+          // eslint-disable-next-line no-use-before-define
           dialog.close(action);
 
           // The processing marker can be deleted since the dialog has been
@@ -346,6 +347,7 @@
           $(event.target).remove();
         },
       });
+
       // A modal dialog is used because the user must provide a button group
       // name or cancel the button placement before taking any other action.
       dialog.showModal();
diff --git a/core/modules/ckeditor/js/plugins/drupalimage/plugin.es6.js b/core/modules/ckeditor/js/plugins/drupalimage/plugin.es6.js
index ac4fba44e4647482a3c4e408c4ef054006d5b977..94f92e6b904644c198a2014cd31ff5724c98ee60 100644
--- a/core/modules/ckeditor/js/plugins/drupalimage/plugin.es6.js
+++ b/core/modules/ckeditor/js/plugins/drupalimage/plugin.es6.js
@@ -14,6 +14,77 @@
  */
 
 (function ($, Drupal, CKEDITOR) {
+  /**
+   * Gets the focused widget, if of the type specific for this plugin.
+   *
+   * @param {CKEDITOR.editor} editor
+   *   A CKEditor instance.
+   *
+   * @return {?CKEDITOR.plugins.widget}
+   *   The focused image2 widget instance, or null.
+   */
+  function getFocusedWidget(editor) {
+    const widget = editor.widgets.focused;
+
+    if (widget && widget.name === 'image') {
+      return widget;
+    }
+
+    return null;
+  }
+
+  /**
+   * Integrates the drupalimage widget with the drupallink plugin.
+   *
+   * Makes images linkable.
+   *
+   * @param {CKEDITOR.editor} editor
+   *   A CKEditor instance.
+   */
+  function linkCommandIntegrator(editor) {
+    // Nothing to integrate with if the drupallink plugin is not loaded.
+    if (!editor.plugins.drupallink) {
+      return;
+    }
+
+    // Override default behaviour of 'drupalunlink' command.
+    editor.getCommand('drupalunlink').on('exec', function (evt) {
+      const widget = getFocusedWidget(editor);
+
+      // Override 'drupalunlink' only when link truly belongs to the widget. If
+      // wrapped inline widget in a link, let default unlink work.
+      // @see https://dev.ckeditor.com/ticket/11814
+      if (!widget || !widget.parts.link) {
+        return;
+      }
+
+      widget.setData('link', null);
+
+      // Selection (which is fake) may not change if unlinked image in focused
+      // widget, i.e. if captioned image. Let's refresh command state manually
+      // here.
+      this.refresh(editor, editor.elementPath());
+
+      evt.cancel();
+    });
+
+    // Override default refresh of 'drupalunlink' command.
+    editor.getCommand('drupalunlink').on('refresh', function (evt) {
+      const widget = getFocusedWidget(editor);
+
+      if (!widget) {
+        return;
+      }
+
+      // Note that widget may be wrapped in a link, which
+      // does not belong to that widget (#11814).
+      this.setState(widget.data.link || widget.wrapper.getAscendant('a') ?
+        CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED);
+
+      evt.cancel();
+    });
+  }
+
   CKEDITOR.plugins.add('drupalimage', {
     requires: 'image2',
     icons: 'drupalimage',
@@ -289,77 +360,6 @@
     return CKEDITOR.plugins.drupallink.getLinkAttributes;
   };
 
-  /**
-   * Integrates the drupalimage widget with the drupallink plugin.
-   *
-   * Makes images linkable.
-   *
-   * @param {CKEDITOR.editor} editor
-   *   A CKEditor instance.
-   */
-  function linkCommandIntegrator(editor) {
-    // Nothing to integrate with if the drupallink plugin is not loaded.
-    if (!editor.plugins.drupallink) {
-      return;
-    }
-
-    // Override default behaviour of 'drupalunlink' command.
-    editor.getCommand('drupalunlink').on('exec', function (evt) {
-      const widget = getFocusedWidget(editor);
-
-      // Override 'drupalunlink' only when link truly belongs to the widget. If
-      // wrapped inline widget in a link, let default unlink work.
-      // @see https://dev.ckeditor.com/ticket/11814
-      if (!widget || !widget.parts.link) {
-        return;
-      }
-
-      widget.setData('link', null);
-
-      // Selection (which is fake) may not change if unlinked image in focused
-      // widget, i.e. if captioned image. Let's refresh command state manually
-      // here.
-      this.refresh(editor, editor.elementPath());
-
-      evt.cancel();
-    });
-
-    // Override default refresh of 'drupalunlink' command.
-    editor.getCommand('drupalunlink').on('refresh', function (evt) {
-      const widget = getFocusedWidget(editor);
-
-      if (!widget) {
-        return;
-      }
-
-      // Note that widget may be wrapped in a link, which
-      // does not belong to that widget (#11814).
-      this.setState(widget.data.link || widget.wrapper.getAscendant('a') ?
-        CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED);
-
-      evt.cancel();
-    });
-  }
-
-  /**
-   * Gets the focused widget, if of the type specific for this plugin.
-   *
-   * @param {CKEDITOR.editor} editor
-   *   A CKEditor instance.
-   *
-   * @return {?CKEDITOR.plugins.widget}
-   *   The focused image2 widget instance, or null.
-   */
-  function getFocusedWidget(editor) {
-    const widget = editor.widgets.focused;
-
-    if (widget && widget.name === 'image') {
-      return widget;
-    }
-
-    return null;
-  }
-
   // Expose an API for other plugins to interact with drupalimage widgets.
   CKEDITOR.plugins.drupalimage = {
     getFocusedWidget,
diff --git a/core/modules/ckeditor/js/plugins/drupalimage/plugin.js b/core/modules/ckeditor/js/plugins/drupalimage/plugin.js
index b1751c28e0c55080d043afa3dcc62e7eee6850bf..8176a72a524e5ee856f5ffc5023b5fd767cd844b 100644
--- a/core/modules/ckeditor/js/plugins/drupalimage/plugin.js
+++ b/core/modules/ckeditor/js/plugins/drupalimage/plugin.js
@@ -6,6 +6,48 @@
 **/
 
 (function ($, Drupal, CKEDITOR) {
+  function getFocusedWidget(editor) {
+    var widget = editor.widgets.focused;
+
+    if (widget && widget.name === 'image') {
+      return widget;
+    }
+
+    return null;
+  }
+
+  function linkCommandIntegrator(editor) {
+    if (!editor.plugins.drupallink) {
+      return;
+    }
+
+    editor.getCommand('drupalunlink').on('exec', function (evt) {
+      var widget = getFocusedWidget(editor);
+
+      if (!widget || !widget.parts.link) {
+        return;
+      }
+
+      widget.setData('link', null);
+
+      this.refresh(editor, editor.elementPath());
+
+      evt.cancel();
+    });
+
+    editor.getCommand('drupalunlink').on('refresh', function (evt) {
+      var widget = getFocusedWidget(editor);
+
+      if (!widget) {
+        return;
+      }
+
+      this.setState(widget.data.link || widget.wrapper.getAscendant('a') ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED);
+
+      evt.cancel();
+    });
+  }
+
   CKEDITOR.plugins.add('drupalimage', {
     requires: 'image2',
     icons: 'drupalimage',
@@ -204,48 +246,6 @@
     return CKEDITOR.plugins.drupallink.getLinkAttributes;
   };
 
-  function linkCommandIntegrator(editor) {
-    if (!editor.plugins.drupallink) {
-      return;
-    }
-
-    editor.getCommand('drupalunlink').on('exec', function (evt) {
-      var widget = getFocusedWidget(editor);
-
-      if (!widget || !widget.parts.link) {
-        return;
-      }
-
-      widget.setData('link', null);
-
-      this.refresh(editor, editor.elementPath());
-
-      evt.cancel();
-    });
-
-    editor.getCommand('drupalunlink').on('refresh', function (evt) {
-      var widget = getFocusedWidget(editor);
-
-      if (!widget) {
-        return;
-      }
-
-      this.setState(widget.data.link || widget.wrapper.getAscendant('a') ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED);
-
-      evt.cancel();
-    });
-  }
-
-  function getFocusedWidget(editor) {
-    var widget = editor.widgets.focused;
-
-    if (widget && widget.name === 'image') {
-      return widget;
-    }
-
-    return null;
-  }
-
   CKEDITOR.plugins.drupalimage = {
     getFocusedWidget: getFocusedWidget
   };
diff --git a/core/modules/ckeditor/js/plugins/drupalimagecaption/plugin.es6.js b/core/modules/ckeditor/js/plugins/drupalimagecaption/plugin.es6.js
index 1d51bcb82d486240307929e1e0a0d7476458dd8f..140a18420703ea6450bc014809733526abfac371 100644
--- a/core/modules/ckeditor/js/plugins/drupalimagecaption/plugin.es6.js
+++ b/core/modules/ckeditor/js/plugins/drupalimagecaption/plugin.es6.js
@@ -11,6 +11,36 @@
  */
 
 (function (CKEDITOR) {
+  /**
+   * Finds an element by its name.
+   *
+   * Function will check first the passed element itself and then all its
+   * children in DFS order.
+   *
+   * @param {CKEDITOR.htmlParser.element} element
+   *   The element to search.
+   * @param {string} name
+   *   The element name to search for.
+   *
+   * @return {?CKEDITOR.htmlParser.element}
+   *   The found element, or null.
+   */
+  function findElementByName(element, name) {
+    if (element.name === name) {
+      return element;
+    }
+
+    let found = null;
+    element.forEach((el) => {
+      if (el.name === name) {
+        found = el;
+        // Stop here.
+        return false;
+      }
+    }, CKEDITOR.NODE_ELEMENT);
+    return found;
+  }
+
   CKEDITOR.plugins.add('drupalimagecaption', {
     requires: 'drupalimage',
 
@@ -263,34 +293,4 @@
       }
     },
   });
-
-  /**
-   * Finds an element by its name.
-   *
-   * Function will check first the passed element itself and then all its
-   * children in DFS order.
-   *
-   * @param {CKEDITOR.htmlParser.element} element
-   *   The element to search.
-   * @param {string} name
-   *   The element name to search for.
-   *
-   * @return {?CKEDITOR.htmlParser.element}
-   *   The found element, or null.
-   */
-  function findElementByName(element, name) {
-    if (element.name === name) {
-      return element;
-    }
-
-    let found = null;
-    element.forEach((el) => {
-      if (el.name === name) {
-        found = el;
-        // Stop here.
-        return false;
-      }
-    }, CKEDITOR.NODE_ELEMENT);
-    return found;
-  }
 }(CKEDITOR));
diff --git a/core/modules/ckeditor/js/plugins/drupalimagecaption/plugin.js b/core/modules/ckeditor/js/plugins/drupalimagecaption/plugin.js
index c9c9427327023947c4cff89f6310622b28a46372..d19f3b3adc5282f17b8939f82a6464673479580d 100644
--- a/core/modules/ckeditor/js/plugins/drupalimagecaption/plugin.js
+++ b/core/modules/ckeditor/js/plugins/drupalimagecaption/plugin.js
@@ -6,6 +6,22 @@
 **/
 
 (function (CKEDITOR) {
+  function findElementByName(element, name) {
+    if (element.name === name) {
+      return element;
+    }
+
+    var found = null;
+    element.forEach(function (el) {
+      if (el.name === name) {
+        found = el;
+
+        return false;
+      }
+    }, CKEDITOR.NODE_ELEMENT);
+    return found;
+  }
+
   CKEDITOR.plugins.add('drupalimagecaption', {
     requires: 'drupalimage',
 
@@ -192,20 +208,4 @@
       }
     }
   });
-
-  function findElementByName(element, name) {
-    if (element.name === name) {
-      return element;
-    }
-
-    var found = null;
-    element.forEach(function (el) {
-      if (el.name === name) {
-        found = el;
-
-        return false;
-      }
-    }, CKEDITOR.NODE_ELEMENT);
-    return found;
-  }
 })(CKEDITOR);
\ No newline at end of file
diff --git a/core/modules/ckeditor/js/plugins/drupallink/plugin.es6.js b/core/modules/ckeditor/js/plugins/drupallink/plugin.es6.js
index 8dddd2e1844082d5abb400cc0b1095442114141f..42b8565f81e072a4067d38fd0d8dfdc48755c90c 100644
--- a/core/modules/ckeditor/js/plugins/drupallink/plugin.es6.js
+++ b/core/modules/ckeditor/js/plugins/drupallink/plugin.es6.js
@@ -54,6 +54,42 @@
     };
   }
 
+  /**
+   * Get the surrounding link element of current selection.
+   *
+   * The following selection will all return the link element.
+   *
+   * @example
+   *  <a href="#">li^nk</a>
+   *  <a href="#">[link]</a>
+   *  text[<a href="#">link]</a>
+   *  <a href="#">li[nk</a>]
+   *  [<b><a href="#">li]nk</a></b>]
+   *  [<a href="#"><b>li]nk</b></a>
+   *
+   * @param {CKEDITOR.editor} editor
+   *   The CKEditor editor object
+   *
+   * @return {?HTMLElement}
+   *   The selected link element, or null.
+   *
+   */
+  function getSelectedLink(editor) {
+    const selection = editor.getSelection();
+    const selectedElement = selection.getSelectedElement();
+    if (selectedElement && selectedElement.is('a')) {
+      return selectedElement;
+    }
+
+    const range = selection.getRanges(true)[0];
+
+    if (range) {
+      range.shrink(CKEDITOR.SHRINK_TEXT);
+      return editor.elementPath(range.getCommonAncestor()).contains('a', 1);
+    }
+    return null;
+  }
+
   CKEDITOR.plugins.add('drupallink', {
     icons: 'drupallink,drupalunlink',
     hidpi: true,
@@ -248,42 +284,6 @@
     },
   });
 
-  /**
-   * Get the surrounding link element of current selection.
-   *
-   * The following selection will all return the link element.
-   *
-   * @example
-   *  <a href="#">li^nk</a>
-   *  <a href="#">[link]</a>
-   *  text[<a href="#">link]</a>
-   *  <a href="#">li[nk</a>]
-   *  [<b><a href="#">li]nk</a></b>]
-   *  [<a href="#"><b>li]nk</b></a>
-   *
-   * @param {CKEDITOR.editor} editor
-   *   The CKEditor editor object
-   *
-   * @return {?HTMLElement}
-   *   The selected link element, or null.
-   *
-   */
-  function getSelectedLink(editor) {
-    const selection = editor.getSelection();
-    const selectedElement = selection.getSelectedElement();
-    if (selectedElement && selectedElement.is('a')) {
-      return selectedElement;
-    }
-
-    const range = selection.getRanges(true)[0];
-
-    if (range) {
-      range.shrink(CKEDITOR.SHRINK_TEXT);
-      return editor.elementPath(range.getCommonAncestor()).contains('a', 1);
-    }
-    return null;
-  }
-
   // Expose an API for other plugins to interact with drupallink widgets.
   // (Compatible with the official CKEditor link plugin's API:
   // http://dev.ckeditor.com/ticket/13885.)
diff --git a/core/modules/ckeditor/js/plugins/drupallink/plugin.js b/core/modules/ckeditor/js/plugins/drupallink/plugin.js
index d53f49a345ec2a1670fe8c675b0552863a954947..3e5e0dbd1bef057f83e7589b0c0f4194d6bc35cb 100644
--- a/core/modules/ckeditor/js/plugins/drupallink/plugin.js
+++ b/core/modules/ckeditor/js/plugins/drupallink/plugin.js
@@ -49,6 +49,22 @@
     };
   }
 
+  function getSelectedLink(editor) {
+    var selection = editor.getSelection();
+    var selectedElement = selection.getSelectedElement();
+    if (selectedElement && selectedElement.is('a')) {
+      return selectedElement;
+    }
+
+    var range = selection.getRanges(true)[0];
+
+    if (range) {
+      range.shrink(CKEDITOR.SHRINK_TEXT);
+      return editor.elementPath(range.getCommonAncestor()).contains('a', 1);
+    }
+    return null;
+  }
+
   CKEDITOR.plugins.add('drupallink', {
     icons: 'drupallink,drupalunlink',
     hidpi: true,
@@ -216,22 +232,6 @@
     }
   });
 
-  function getSelectedLink(editor) {
-    var selection = editor.getSelection();
-    var selectedElement = selection.getSelectedElement();
-    if (selectedElement && selectedElement.is('a')) {
-      return selectedElement;
-    }
-
-    var range = selection.getRanges(true)[0];
-
-    if (range) {
-      range.shrink(CKEDITOR.SHRINK_TEXT);
-      return editor.elementPath(range.getCommonAncestor()).contains('a', 1);
-    }
-    return null;
-  }
-
   CKEDITOR.plugins.drupallink = {
     parseLinkAttributes: parseAttributes,
     getLinkAttributes: getAttributes
diff --git a/core/modules/color/color.es6.js b/core/modules/color/color.es6.js
index c4f923a02d4fbdf87dd9ead63878b09f79e39532..01939a1f65aadac774fc11f89b617c947f64b71f 100644
--- a/core/modules/color/color.es6.js
+++ b/core/modules/color/color.es6.js
@@ -40,37 +40,6 @@
       // Build a preview.
       const height = [];
       const width = [];
-      // Loop through all defined gradients.
-      Object.keys(settings.gradients || {}).forEach((i) => {
-        // Add element to display the gradient.
-        $('.color-preview').once('color').append(`<div id="gradient-${i}"></div>`);
-        const gradient = $(`.color-preview #gradient-${i}`);
-        // Add height of current gradient to the list (divided by 10).
-        height.push(parseInt(gradient.css('height'), 10) / 10);
-        // Add width of current gradient to the list (divided by 10).
-        width.push(parseInt(gradient.css('width'), 10) / 10);
-        // Add rows (or columns for horizontal gradients).
-        // Each gradient line should have a height (or width for horizontal
-        // gradients) of 10px (because we divided the height/width by 10
-        // above).
-        for (j = 0; j < (settings.gradients[i].direction === 'vertical' ? height[i] : width[i]); ++j) {
-          gradient.append('<div class="gradient-line"></div>');
-        }
-      });
-
-      // Set up colorScheme selector.
-      form.find('#edit-scheme').on('change', function () {
-        const schemes = settings.color.schemes;
-        const colorScheme = this.options[this.selectedIndex].value;
-        if (colorScheme !== '' && schemes[colorScheme]) {
-          // Get colors of active scheme.
-          colors = schemes[colorScheme];
-          Object.keys(colors || {}).forEach((fieldName) => {
-            callback($(`#edit-palette-${fieldName}`), colors[fieldName], false, true);
-          });
-          preview();
-        }
-      });
 
       /**
        * Renders the preview.
@@ -79,6 +48,15 @@
         Drupal.color.callback(context, settings, form, farb, height, width);
       }
 
+      /**
+       * Resets the color scheme selector.
+       */
+      function resetScheme() {
+        form.find('#edit-scheme').each(function () {
+          this.selectedIndex = this.options.length - 1;
+        });
+      }
+
       /**
        * Shifts a given color, using a reference pair (ref in HSL).
        *
@@ -193,14 +171,37 @@
         }
       }
 
-      /**
-       * Resets the color scheme selector.
-       */
-      function resetScheme() {
-        form.find('#edit-scheme').each(function () {
-          this.selectedIndex = this.options.length - 1;
-        });
-      }
+      // Loop through all defined gradients.
+      Object.keys(settings.gradients || {}).forEach((i) => {
+        // Add element to display the gradient.
+        $('.color-preview').once('color').append(`<div id="gradient-${i}"></div>`);
+        const gradient = $(`.color-preview #gradient-${i}`);
+        // Add height of current gradient to the list (divided by 10).
+        height.push(parseInt(gradient.css('height'), 10) / 10);
+        // Add width of current gradient to the list (divided by 10).
+        width.push(parseInt(gradient.css('width'), 10) / 10);
+        // Add rows (or columns for horizontal gradients).
+        // Each gradient line should have a height (or width for horizontal
+        // gradients) of 10px (because we divided the height/width by 10
+        // above).
+        for (j = 0; j < (settings.gradients[i].direction === 'vertical' ? height[i] : width[i]); ++j) {
+          gradient.append('<div class="gradient-line"></div>');
+        }
+      });
+
+      // Set up colorScheme selector.
+      form.find('#edit-scheme').on('change', function () {
+        const schemes = settings.color.schemes;
+        const colorScheme = this.options[this.selectedIndex].value;
+        if (colorScheme !== '' && schemes[colorScheme]) {
+          // Get colors of active scheme.
+          colors = schemes[colorScheme];
+          Object.keys(colors || {}).forEach((fieldName) => {
+            callback($(`#edit-palette-${fieldName}`), colors[fieldName], false, true);
+          });
+          preview();
+        }
+      });
 
       /**
        * Focuses Farbtastic on a particular field.
diff --git a/core/modules/color/color.js b/core/modules/color/color.js
index 5cc0b9be12d94d407f4ca737336601b70796e1ed..c92a0df36dcb78ac499b13b14b687e0aeab5f734 100644
--- a/core/modules/color/color.js
+++ b/core/modules/color/color.js
@@ -32,35 +32,16 @@
       var height = [];
       var width = [];
 
-      Object.keys(settings.gradients || {}).forEach(function (i) {
-        $('.color-preview').once('color').append('<div id="gradient-' + i + '"></div>');
-        var gradient = $('.color-preview #gradient-' + i);
-
-        height.push(parseInt(gradient.css('height'), 10) / 10);
-
-        width.push(parseInt(gradient.css('width'), 10) / 10);
-
-        for (j = 0; j < (settings.gradients[i].direction === 'vertical' ? height[i] : width[i]); ++j) {
-          gradient.append('<div class="gradient-line"></div>');
-        }
-      });
-
-      form.find('#edit-scheme').on('change', function () {
-        var schemes = settings.color.schemes;
-        var colorScheme = this.options[this.selectedIndex].value;
-        if (colorScheme !== '' && schemes[colorScheme]) {
-          colors = schemes[colorScheme];
-          Object.keys(colors || {}).forEach(function (fieldName) {
-            callback($('#edit-palette-' + fieldName), colors[fieldName], false, true);
-          });
-          preview();
-        }
-      });
-
       function preview() {
         Drupal.color.callback(context, settings, form, farb, height, width);
       }
 
+      function resetScheme() {
+        form.find('#edit-scheme').each(function () {
+          this.selectedIndex = this.options.length - 1;
+        });
+      }
+
       function shiftColor(given, ref1, ref2) {
         var d = void 0;
 
@@ -130,11 +111,30 @@
         }
       }
 
-      function resetScheme() {
-        form.find('#edit-scheme').each(function () {
-          this.selectedIndex = this.options.length - 1;
-        });
-      }
+      Object.keys(settings.gradients || {}).forEach(function (i) {
+        $('.color-preview').once('color').append('<div id="gradient-' + i + '"></div>');
+        var gradient = $('.color-preview #gradient-' + i);
+
+        height.push(parseInt(gradient.css('height'), 10) / 10);
+
+        width.push(parseInt(gradient.css('width'), 10) / 10);
+
+        for (j = 0; j < (settings.gradients[i].direction === 'vertical' ? height[i] : width[i]); ++j) {
+          gradient.append('<div class="gradient-line"></div>');
+        }
+      });
+
+      form.find('#edit-scheme').on('change', function () {
+        var schemes = settings.color.schemes;
+        var colorScheme = this.options[this.selectedIndex].value;
+        if (colorScheme !== '' && schemes[colorScheme]) {
+          colors = schemes[colorScheme];
+          Object.keys(colors || {}).forEach(function (fieldName) {
+            callback($('#edit-palette-' + fieldName), colors[fieldName], false, true);
+          });
+          preview();
+        }
+      });
 
       function focus(e) {
         var input = e.target;
diff --git a/core/modules/comment/js/comment-new-indicator.es6.js b/core/modules/comment/js/comment-new-indicator.es6.js
index dd1fadf3ad76765b5a6df2181bdee9daae3e239f..791853e6eed826b3a666070ac747e378fb44b683 100644
--- a/core/modules/comment/js/comment-new-indicator.es6.js
+++ b/core/modules/comment/js/comment-new-indicator.es6.js
@@ -7,46 +7,6 @@
  */
 
 (function ($, Drupal, window) {
-  /**
-   * Renders "new" comment indicators wherever necessary.
-   *
-   * @type {Drupal~behavior}
-   *
-   * @prop {Drupal~behaviorAttach} attach
-   *   Attaches "new" comment indicators behavior.
-   */
-  Drupal.behaviors.commentNewIndicator = {
-    attach(context) {
-      // Collect all "new" comment indicator placeholders (and their
-      // corresponding node IDs) newer than 30 days ago that have not already
-      // been read after their last comment timestamp.
-      const nodeIDs = [];
-      const $placeholders = $(context)
-        .find('[data-comment-timestamp]')
-        .once('history')
-        .filter(function () {
-          const $placeholder = $(this);
-          const commentTimestamp = parseInt($placeholder.attr('data-comment-timestamp'), 10);
-          const nodeID = $placeholder.closest('[data-history-node-id]').attr('data-history-node-id');
-          if (Drupal.history.needsServerCheck(nodeID, commentTimestamp)) {
-            nodeIDs.push(nodeID);
-            return true;
-          }
-
-          return false;
-        });
-
-      if ($placeholders.length === 0) {
-        return;
-      }
-
-      // Fetch the node read timestamps from the server.
-      Drupal.history.fetchTimestamps(nodeIDs, () => {
-        processCommentNewIndicators($placeholders);
-      });
-    },
-  };
-
   /**
    * Processes the markup for "new comment" indicators.
    *
@@ -88,4 +48,44 @@
       }
     });
   }
+
+  /**
+   * Renders "new" comment indicators wherever necessary.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches "new" comment indicators behavior.
+   */
+  Drupal.behaviors.commentNewIndicator = {
+    attach(context) {
+      // Collect all "new" comment indicator placeholders (and their
+      // corresponding node IDs) newer than 30 days ago that have not already
+      // been read after their last comment timestamp.
+      const nodeIDs = [];
+      const $placeholders = $(context)
+        .find('[data-comment-timestamp]')
+        .once('history')
+        .filter(function () {
+          const $placeholder = $(this);
+          const commentTimestamp = parseInt($placeholder.attr('data-comment-timestamp'), 10);
+          const nodeID = $placeholder.closest('[data-history-node-id]').attr('data-history-node-id');
+          if (Drupal.history.needsServerCheck(nodeID, commentTimestamp)) {
+            nodeIDs.push(nodeID);
+            return true;
+          }
+
+          return false;
+        });
+
+      if ($placeholders.length === 0) {
+        return;
+      }
+
+      // Fetch the node read timestamps from the server.
+      Drupal.history.fetchTimestamps(nodeIDs, () => {
+        processCommentNewIndicators($placeholders);
+      });
+    },
+  };
 }(jQuery, Drupal, window));
diff --git a/core/modules/comment/js/comment-new-indicator.js b/core/modules/comment/js/comment-new-indicator.js
index bc7f55ec37df19121f1a1b0489085c3e44b06c46..88587d5724c31c45b86a6232094f729ba777f550 100644
--- a/core/modules/comment/js/comment-new-indicator.js
+++ b/core/modules/comment/js/comment-new-indicator.js
@@ -6,31 +6,6 @@
 **/
 
 (function ($, Drupal, window) {
-  Drupal.behaviors.commentNewIndicator = {
-    attach: function attach(context) {
-      var nodeIDs = [];
-      var $placeholders = $(context).find('[data-comment-timestamp]').once('history').filter(function () {
-        var $placeholder = $(this);
-        var commentTimestamp = parseInt($placeholder.attr('data-comment-timestamp'), 10);
-        var nodeID = $placeholder.closest('[data-history-node-id]').attr('data-history-node-id');
-        if (Drupal.history.needsServerCheck(nodeID, commentTimestamp)) {
-          nodeIDs.push(nodeID);
-          return true;
-        }
-
-        return false;
-      });
-
-      if ($placeholders.length === 0) {
-        return;
-      }
-
-      Drupal.history.fetchTimestamps(nodeIDs, function () {
-        processCommentNewIndicators($placeholders);
-      });
-    }
-  };
-
   function processCommentNewIndicators($placeholders) {
     var isFirstNewComment = true;
     var newCommentString = Drupal.t('new');
@@ -57,4 +32,29 @@
       }
     });
   }
+
+  Drupal.behaviors.commentNewIndicator = {
+    attach: function attach(context) {
+      var nodeIDs = [];
+      var $placeholders = $(context).find('[data-comment-timestamp]').once('history').filter(function () {
+        var $placeholder = $(this);
+        var commentTimestamp = parseInt($placeholder.attr('data-comment-timestamp'), 10);
+        var nodeID = $placeholder.closest('[data-history-node-id]').attr('data-history-node-id');
+        if (Drupal.history.needsServerCheck(nodeID, commentTimestamp)) {
+          nodeIDs.push(nodeID);
+          return true;
+        }
+
+        return false;
+      });
+
+      if ($placeholders.length === 0) {
+        return;
+      }
+
+      Drupal.history.fetchTimestamps(nodeIDs, function () {
+        processCommentNewIndicators($placeholders);
+      });
+    }
+  };
 })(jQuery, Drupal, window);
\ No newline at end of file
diff --git a/core/modules/comment/js/node-new-comments-link.es6.js b/core/modules/comment/js/node-new-comments-link.es6.js
index a2bdc35b9d7272b4950f55f303a27b73eacf0486..0f084d4cb3db18ac4dfc1292794972de098f9d16 100644
--- a/core/modules/comment/js/node-new-comments-link.es6.js
+++ b/core/modules/comment/js/node-new-comments-link.es6.js
@@ -7,51 +7,6 @@
  */
 
 (function ($, Drupal, drupalSettings) {
-  /**
-   * Render "X new comments" links wherever necessary.
-   *
-   * @type {Drupal~behavior}
-   *
-   * @prop {Drupal~behaviorAttach} attach
-   *   Attaches new comment links behavior.
-   */
-  Drupal.behaviors.nodeNewCommentsLink = {
-    attach(context) {
-      // Collect all "X new comments" node link placeholders (and their
-      // corresponding node IDs) newer than 30 days ago that have not already
-      // been read after their last comment timestamp.
-      const nodeIDs = [];
-      const $placeholders = $(context)
-        .find('[data-history-node-last-comment-timestamp]')
-        .once('history')
-        .filter(function () {
-          const $placeholder = $(this);
-          const lastCommentTimestamp = parseInt($placeholder.attr('data-history-node-last-comment-timestamp'), 10);
-          const nodeID = $placeholder.closest('[data-history-node-id]').attr('data-history-node-id');
-          if (Drupal.history.needsServerCheck(nodeID, lastCommentTimestamp)) {
-            nodeIDs.push(nodeID);
-            // Hide this placeholder link until it is certain we'll need it.
-            hide($placeholder);
-            return true;
-          }
-
-            // Remove this placeholder link from the DOM because we won't need
-            // it.
-          remove($placeholder);
-          return false;
-        });
-
-      if ($placeholders.length === 0) {
-        return;
-      }
-
-      // Perform an AJAX request to retrieve node read timestamps.
-      Drupal.history.fetchTimestamps(nodeIDs, () => {
-        processNodeNewCommentLinks($placeholders);
-      });
-    },
-  };
-
   /**
    * Hides a "new comment" element.
    *
@@ -173,4 +128,49 @@
       });
     }
   }
+
+  /**
+   * Render "X new comments" links wherever necessary.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches new comment links behavior.
+   */
+  Drupal.behaviors.nodeNewCommentsLink = {
+    attach(context) {
+      // Collect all "X new comments" node link placeholders (and their
+      // corresponding node IDs) newer than 30 days ago that have not already
+      // been read after their last comment timestamp.
+      const nodeIDs = [];
+      const $placeholders = $(context)
+        .find('[data-history-node-last-comment-timestamp]')
+        .once('history')
+        .filter(function () {
+          const $placeholder = $(this);
+          const lastCommentTimestamp = parseInt($placeholder.attr('data-history-node-last-comment-timestamp'), 10);
+          const nodeID = $placeholder.closest('[data-history-node-id]').attr('data-history-node-id');
+          if (Drupal.history.needsServerCheck(nodeID, lastCommentTimestamp)) {
+            nodeIDs.push(nodeID);
+            // Hide this placeholder link until it is certain we'll need it.
+            hide($placeholder);
+            return true;
+          }
+
+            // Remove this placeholder link from the DOM because we won't need
+            // it.
+          remove($placeholder);
+          return false;
+        });
+
+      if ($placeholders.length === 0) {
+        return;
+      }
+
+      // Perform an AJAX request to retrieve node read timestamps.
+      Drupal.history.fetchTimestamps(nodeIDs, () => {
+        processNodeNewCommentLinks($placeholders);
+      });
+    },
+  };
 }(jQuery, Drupal, drupalSettings));
diff --git a/core/modules/comment/js/node-new-comments-link.js b/core/modules/comment/js/node-new-comments-link.js
index 1396018a561872b3a47ff5ac8993b0c3d03e9c39..b7439940f61ac81bc5739ca3ef73af8ea865bcdc 100644
--- a/core/modules/comment/js/node-new-comments-link.js
+++ b/core/modules/comment/js/node-new-comments-link.js
@@ -6,34 +6,6 @@
 **/
 
 (function ($, Drupal, drupalSettings) {
-  Drupal.behaviors.nodeNewCommentsLink = {
-    attach: function attach(context) {
-      var nodeIDs = [];
-      var $placeholders = $(context).find('[data-history-node-last-comment-timestamp]').once('history').filter(function () {
-        var $placeholder = $(this);
-        var lastCommentTimestamp = parseInt($placeholder.attr('data-history-node-last-comment-timestamp'), 10);
-        var nodeID = $placeholder.closest('[data-history-node-id]').attr('data-history-node-id');
-        if (Drupal.history.needsServerCheck(nodeID, lastCommentTimestamp)) {
-          nodeIDs.push(nodeID);
-
-          hide($placeholder);
-          return true;
-        }
-
-        remove($placeholder);
-        return false;
-      });
-
-      if ($placeholders.length === 0) {
-        return;
-      }
-
-      Drupal.history.fetchTimestamps(nodeIDs, function () {
-        processNodeNewCommentLinks($placeholders);
-      });
-    }
-  };
-
   function hide($placeholder) {
     return $placeholder.closest('.comment-new-comments').prev().addClass('last').end().hide();
   }
@@ -90,4 +62,32 @@
       });
     }
   }
+
+  Drupal.behaviors.nodeNewCommentsLink = {
+    attach: function attach(context) {
+      var nodeIDs = [];
+      var $placeholders = $(context).find('[data-history-node-last-comment-timestamp]').once('history').filter(function () {
+        var $placeholder = $(this);
+        var lastCommentTimestamp = parseInt($placeholder.attr('data-history-node-last-comment-timestamp'), 10);
+        var nodeID = $placeholder.closest('[data-history-node-id]').attr('data-history-node-id');
+        if (Drupal.history.needsServerCheck(nodeID, lastCommentTimestamp)) {
+          nodeIDs.push(nodeID);
+
+          hide($placeholder);
+          return true;
+        }
+
+        remove($placeholder);
+        return false;
+      });
+
+      if ($placeholders.length === 0) {
+        return;
+      }
+
+      Drupal.history.fetchTimestamps(nodeIDs, function () {
+        processNodeNewCommentLinks($placeholders);
+      });
+    }
+  };
 })(jQuery, Drupal, drupalSettings);
\ No newline at end of file
diff --git a/core/modules/contextual/js/contextual.es6.js b/core/modules/contextual/js/contextual.es6.js
index 701ed126cb0749e9a4a933447d7916e0a6aab82a..8e05f87ae3ac1a710dbe67c27587fd1e662753cc 100644
--- a/core/modules/contextual/js/contextual.es6.js
+++ b/core/modules/contextual/js/contextual.es6.js
@@ -29,6 +29,46 @@
     storage.setItem('Drupal.contextual.permissionsHash', permissionsHash);
   }
 
+  /**
+   * Determines if a contextual link is nested & overlapping, if so: adjusts it.
+   *
+   * This only deals with two levels of nesting; deeper levels are not touched.
+   *
+   * @param {jQuery} $contextual
+   *   A contextual links placeholder DOM element, containing the actual
+   *   contextual links as rendered by the server.
+   */
+  function adjustIfNestedAndOverlapping($contextual) {
+    const $contextuals = $contextual
+      // @todo confirm that .closest() is not sufficient
+      .parents('.contextual-region').eq(-1)
+      .find('.contextual');
+
+    // Early-return when there's no nesting.
+    if ($contextuals.length <= 1) {
+      return;
+    }
+
+    // If the two contextual links overlap, then we move the second one.
+    const firstTop = $contextuals.eq(0).offset().top;
+    const secondTop = $contextuals.eq(1).offset().top;
+    if (firstTop === secondTop) {
+      const $nestedContextual = $contextuals.eq(1);
+
+      // Retrieve height of nested contextual link.
+      let height = 0;
+      const $trigger = $nestedContextual.find('.trigger');
+      // Elements with the .visually-hidden class have no dimensions, so this
+      // class must be temporarily removed to the calculate the height.
+      $trigger.removeClass('visually-hidden');
+      height = $nestedContextual.height();
+      $trigger.addClass('visually-hidden');
+
+      // Adjust nested contextual link's position.
+      $nestedContextual.css({ top: $nestedContextual.position().top + height });
+    }
+  }
+
   /**
    * Initializes a contextual link: updates its DOM, sets up model and views.
    *
@@ -89,46 +129,6 @@
     adjustIfNestedAndOverlapping($contextual);
   }
 
-  /**
-   * Determines if a contextual link is nested & overlapping, if so: adjusts it.
-   *
-   * This only deals with two levels of nesting; deeper levels are not touched.
-   *
-   * @param {jQuery} $contextual
-   *   A contextual links placeholder DOM element, containing the actual
-   *   contextual links as rendered by the server.
-   */
-  function adjustIfNestedAndOverlapping($contextual) {
-    const $contextuals = $contextual
-      // @todo confirm that .closest() is not sufficient
-      .parents('.contextual-region').eq(-1)
-      .find('.contextual');
-
-    // Early-return when there's no nesting.
-    if ($contextuals.length <= 1) {
-      return;
-    }
-
-    // If the two contextual links overlap, then we move the second one.
-    const firstTop = $contextuals.eq(0).offset().top;
-    const secondTop = $contextuals.eq(1).offset().top;
-    if (firstTop === secondTop) {
-      const $nestedContextual = $contextuals.eq(1);
-
-      // Retrieve height of nested contextual link.
-      let height = 0;
-      const $trigger = $nestedContextual.find('.trigger');
-      // Elements with the .visually-hidden class have no dimensions, so this
-      // class must be temporarily removed to the calculate the height.
-      $trigger.removeClass('visually-hidden');
-      height = $nestedContextual.height();
-      $trigger.addClass('visually-hidden');
-
-      // Adjust nested contextual link's position.
-      $nestedContextual.css({ top: $nestedContextual.position().top + height });
-    }
-  }
-
   /**
    * Attaches outline behavior for regions associated with contextual links.
    *
diff --git a/core/modules/contextual/js/contextual.js b/core/modules/contextual/js/contextual.js
index ed210d070d57916a4f75614c1b0c395c8c864db9..7e6d000d407e25d67236355da810f518ae6ab15f 100644
--- a/core/modules/contextual/js/contextual.js
+++ b/core/modules/contextual/js/contextual.js
@@ -26,6 +26,29 @@
     storage.setItem('Drupal.contextual.permissionsHash', permissionsHash);
   }
 
+  function adjustIfNestedAndOverlapping($contextual) {
+    var $contextuals = $contextual.parents('.contextual-region').eq(-1).find('.contextual');
+
+    if ($contextuals.length <= 1) {
+      return;
+    }
+
+    var firstTop = $contextuals.eq(0).offset().top;
+    var secondTop = $contextuals.eq(1).offset().top;
+    if (firstTop === secondTop) {
+      var $nestedContextual = $contextuals.eq(1);
+
+      var height = 0;
+      var $trigger = $nestedContextual.find('.trigger');
+
+      $trigger.removeClass('visually-hidden');
+      height = $nestedContextual.height();
+      $trigger.addClass('visually-hidden');
+
+      $nestedContextual.css({ top: $nestedContextual.position().top + height });
+    }
+  }
+
   function initContextual($contextual, html) {
     var $region = $contextual.closest('.contextual-region');
     var contextual = Drupal.contextual;
@@ -61,29 +84,6 @@
     adjustIfNestedAndOverlapping($contextual);
   }
 
-  function adjustIfNestedAndOverlapping($contextual) {
-    var $contextuals = $contextual.parents('.contextual-region').eq(-1).find('.contextual');
-
-    if ($contextuals.length <= 1) {
-      return;
-    }
-
-    var firstTop = $contextuals.eq(0).offset().top;
-    var secondTop = $contextuals.eq(1).offset().top;
-    if (firstTop === secondTop) {
-      var $nestedContextual = $contextuals.eq(1);
-
-      var height = 0;
-      var $trigger = $nestedContextual.find('.trigger');
-
-      $trigger.removeClass('visually-hidden');
-      height = $nestedContextual.height();
-      $trigger.addClass('visually-hidden');
-
-      $nestedContextual.css({ top: $nestedContextual.position().top + height });
-    }
-  }
-
   Drupal.behaviors.contextual = {
     attach: function attach(context) {
       var $context = $(context);
diff --git a/core/modules/editor/js/editor.admin.es6.js b/core/modules/editor/js/editor.admin.es6.js
index 5b7eac78b2238e3a1bdd29b79f946523201b915a..7307bb0d31534f6a05aadc60d952c135505dd392 100644
--- a/core/modules/editor/js/editor.admin.es6.js
+++ b/core/modules/editor/js/editor.admin.es6.js
@@ -88,6 +88,20 @@
      *   Whether the given feature is allowed by the current filters.
      */
     featureIsAllowedByFilters(feature) {
+      /**
+       * Provided a section of a feature or filter rule, checks if no property
+       * values are defined for all properties: attributes, classes and styles.
+       *
+       * @param {object} section
+       *   The section to check.
+       *
+       * @return {bool}
+       *   Returns true if the section has empty properties, false otherwise.
+       */
+      function emptyProperties(section) {
+        return section.attributes.length === 0 && section.classes.length === 0 && section.styles.length === 0;
+      }
+
       /**
        * Generate the universe U of possible values that can result from the
        * feature's rules' requirements.
@@ -183,23 +197,10 @@
       }
 
       /**
-       * Provided a section of a feature or filter rule, checks if no property
-       * values are defined for all properties: attributes, classes and styles.
-       *
-       * @param {object} section
-       *   The section to check.
-       *
-       * @return {bool}
-       *   Returns true if the section has empty properties, false otherwise.
-       */
-      function emptyProperties(section) {
-        return section.attributes.length === 0 && section.classes.length === 0 && section.styles.length === 0;
-      }
-
-      /**
-       * Calls findPropertyValueOnTag on the given tag for every property value
-       * that is listed in the "propertyValues" parameter. Supports the wildcard
-       * tag.
+       * Finds out if a specific property value (potentially containing
+       * wildcards) exists on the given tag. When the "allowing" parameter
+       * equals true, the universe will be updated if that specific property
+       * value exists. Returns true if found, false otherwise.
        *
        * @param {object} universe
        *   The universe to check.
@@ -207,24 +208,50 @@
        *   The tag to look for.
        * @param {string} property
        *   The property to check.
-       * @param {Array} propertyValues
-       *   Values of the property to check.
+       * @param {string} propertyValue
+       *   The property value to check.
        * @param {bool} allowing
        *   Whether to update the universe or not.
        *
        * @return {bool}
        *   Returns true if found, false otherwise.
        */
-      function findPropertyValuesOnTag(universe, tag, property, propertyValues, allowing) {
-        // Detect the wildcard case.
-        if (tag === '*') {
-          return findPropertyValuesOnAllTags(universe, property, propertyValues, allowing);
+      function findPropertyValueOnTag(universe, tag, property, propertyValue, allowing) {
+        // If the tag does not exist in the universe, then it definitely can't
+        // have this specific property value.
+        if (!_.has(universe, tag)) {
+          return false;
         }
 
+        const key = `${property}:${propertyValue}`;
+
+        // Track whether a tag was touched by a filter rule that allows specific
+        // property values on this particular tag.
+        // @see generateUniverseFromFeatureRequirements
+        if (allowing) {
+          universe[tag].touchedByAllowedPropertyRule = true;
+        }
+
+        // The simple case: no wildcard in property value.
+        if (_.indexOf(propertyValue, '*') === -1) {
+          if (_.has(universe, tag) && _.has(universe[tag], key)) {
+            if (allowing) {
+              universe[tag][key] = true;
+            }
+            return true;
+          }
+          return false;
+        }
+        // The complex case: wildcard in property value.
+
         let atLeastOneFound = false;
-        _.each(propertyValues, (propertyValue) => {
-          if (findPropertyValueOnTag(universe, tag, property, propertyValue, allowing)) {
+        const regex = key.replace(/\*/g, '[^ ]*');
+        _.each(_.keys(universe[tag]), (key) => {
+          if (key.match(regex)) {
             atLeastOneFound = true;
+            if (allowing) {
+              universe[tag][key] = true;
+            }
           }
         });
         return atLeastOneFound;
@@ -248,6 +275,7 @@
       function findPropertyValuesOnAllTags(universe, property, propertyValues, allowing) {
         let atLeastOneFound = false;
         _.each(_.keys(universe), (tag) => {
+          // eslint-disable-next-line no-use-before-define
           if (findPropertyValuesOnTag(universe, tag, property, propertyValues, allowing)) {
             atLeastOneFound = true;
           }
@@ -256,10 +284,9 @@
       }
 
       /**
-       * Finds out if a specific property value (potentially containing
-       * wildcards) exists on the given tag. When the "allowing" parameter
-       * equals true, the universe will be updated if that specific property
-       * value exists. Returns true if found, false otherwise.
+       * Calls findPropertyValueOnTag on the given tag for every property value
+       * that is listed in the "propertyValues" parameter. Supports the wildcard
+       * tag.
        *
        * @param {object} universe
        *   The universe to check.
@@ -267,55 +294,49 @@
        *   The tag to look for.
        * @param {string} property
        *   The property to check.
-       * @param {string} propertyValue
-       *   The property value to check.
+       * @param {Array} propertyValues
+       *   Values of the property to check.
        * @param {bool} allowing
        *   Whether to update the universe or not.
        *
        * @return {bool}
        *   Returns true if found, false otherwise.
        */
-      function findPropertyValueOnTag(universe, tag, property, propertyValue, allowing) {
-        // If the tag does not exist in the universe, then it definitely can't
-        // have this specific property value.
-        if (!_.has(universe, tag)) {
-          return false;
-        }
-
-        const key = `${property}:${propertyValue}`;
-
-        // Track whether a tag was touched by a filter rule that allows specific
-        // property values on this particular tag.
-        // @see generateUniverseFromFeatureRequirements
-        if (allowing) {
-          universe[tag].touchedByAllowedPropertyRule = true;
-        }
-
-        // The simple case: no wildcard in property value.
-        if (_.indexOf(propertyValue, '*') === -1) {
-          if (_.has(universe, tag) && _.has(universe[tag], key)) {
-            if (allowing) {
-              universe[tag][key] = true;
-            }
-            return true;
-          }
-          return false;
+      function findPropertyValuesOnTag(universe, tag, property, propertyValues, allowing) {
+        // Detect the wildcard case.
+        if (tag === '*') {
+          return findPropertyValuesOnAllTags(universe, property, propertyValues, allowing);
         }
-        // The complex case: wildcard in property value.
 
         let atLeastOneFound = false;
-        const regex = key.replace(/\*/g, '[^ ]*');
-        _.each(_.keys(universe[tag]), (key) => {
-          if (key.match(regex)) {
+        _.each(propertyValues, (propertyValue) => {
+          if (findPropertyValueOnTag(universe, tag, property, propertyValue, allowing)) {
             atLeastOneFound = true;
-            if (allowing) {
-              universe[tag][key] = true;
-            }
           }
         });
         return atLeastOneFound;
       }
 
+      /**
+       * Calls deleteFromUniverseIfAllowed for all tags in the universe.
+       *
+       * @param {object} universe
+       *   The universe to delete from.
+       *
+       * @return {bool}
+       *   Whether something was deleted from the universe.
+       */
+      function deleteAllTagsFromUniverseIfAllowed(universe) {
+        let atLeastOneDeleted = false;
+        _.each(_.keys(universe), (tag) => {
+          // eslint-disable-next-line no-use-before-define
+          if (deleteFromUniverseIfAllowed(universe, tag)) {
+            atLeastOneDeleted = true;
+          }
+        });
+        return atLeastOneDeleted;
+      }
+
       /**
        * Deletes a tag from the universe if the tag itself and each of its
        * properties are marked as allowed.
@@ -340,25 +361,6 @@
         return false;
       }
 
-      /**
-       * Calls deleteFromUniverseIfAllowed for all tags in the universe.
-       *
-       * @param {object} universe
-       *   The universe to delete from.
-       *
-       * @return {bool}
-       *   Whether something was deleted from the universe.
-       */
-      function deleteAllTagsFromUniverseIfAllowed(universe) {
-        let atLeastOneDeleted = false;
-        _.each(_.keys(universe), (tag) => {
-          if (deleteFromUniverseIfAllowed(universe, tag)) {
-            atLeastOneDeleted = true;
-          }
-        });
-        return atLeastOneDeleted;
-      }
-
       /**
        * Checks if any filter rule forbids either a tag or a tag property value
        * that exists in the universe.
diff --git a/core/modules/editor/js/editor.admin.js b/core/modules/editor/js/editor.admin.js
index 4a1014096eed5321dd939219c86d3cd01fb81c37..3d88c936686ee9928af979bef90cf33adb932a17 100644
--- a/core/modules/editor/js/editor.admin.js
+++ b/core/modules/editor/js/editor.admin.js
@@ -17,6 +17,10 @@
       $(document).trigger('drupalEditorFeatureModified', feature);
     },
     featureIsAllowedByFilters: function featureIsAllowedByFilters(feature) {
+      function emptyProperties(section) {
+        return section.attributes.length === 0 && section.classes.length === 0 && section.styles.length === 0;
+      }
+
       function generateUniverseFromFeatureRequirements(feature) {
         var properties = ['attributes', 'styles', 'classes'];
         var universe = {};
@@ -51,34 +55,6 @@
         return universe;
       }
 
-      function emptyProperties(section) {
-        return section.attributes.length === 0 && section.classes.length === 0 && section.styles.length === 0;
-      }
-
-      function findPropertyValuesOnTag(universe, tag, property, propertyValues, allowing) {
-        if (tag === '*') {
-          return findPropertyValuesOnAllTags(universe, property, propertyValues, allowing);
-        }
-
-        var atLeastOneFound = false;
-        _.each(propertyValues, function (propertyValue) {
-          if (findPropertyValueOnTag(universe, tag, property, propertyValue, allowing)) {
-            atLeastOneFound = true;
-          }
-        });
-        return atLeastOneFound;
-      }
-
-      function findPropertyValuesOnAllTags(universe, property, propertyValues, allowing) {
-        var atLeastOneFound = false;
-        _.each(_.keys(universe), function (tag) {
-          if (findPropertyValuesOnTag(universe, tag, property, propertyValues, allowing)) {
-            atLeastOneFound = true;
-          }
-        });
-        return atLeastOneFound;
-      }
-
       function findPropertyValueOnTag(universe, tag, property, propertyValue, allowing) {
         if (!_.has(universe, tag)) {
           return false;
@@ -114,15 +90,28 @@
         return atLeastOneFound;
       }
 
-      function deleteFromUniverseIfAllowed(universe, tag) {
+      function findPropertyValuesOnAllTags(universe, property, propertyValues, allowing) {
+        var atLeastOneFound = false;
+        _.each(_.keys(universe), function (tag) {
+          if (findPropertyValuesOnTag(universe, tag, property, propertyValues, allowing)) {
+            atLeastOneFound = true;
+          }
+        });
+        return atLeastOneFound;
+      }
+
+      function findPropertyValuesOnTag(universe, tag, property, propertyValues, allowing) {
         if (tag === '*') {
-          return deleteAllTagsFromUniverseIfAllowed(universe);
-        }
-        if (_.has(universe, tag) && _.every(_.omit(universe[tag], 'touchedByAllowedPropertyRule'))) {
-          delete universe[tag];
-          return true;
+          return findPropertyValuesOnAllTags(universe, property, propertyValues, allowing);
         }
-        return false;
+
+        var atLeastOneFound = false;
+        _.each(propertyValues, function (propertyValue) {
+          if (findPropertyValueOnTag(universe, tag, property, propertyValue, allowing)) {
+            atLeastOneFound = true;
+          }
+        });
+        return atLeastOneFound;
       }
 
       function deleteAllTagsFromUniverseIfAllowed(universe) {
@@ -135,6 +124,17 @@
         return atLeastOneDeleted;
       }
 
+      function deleteFromUniverseIfAllowed(universe, tag) {
+        if (tag === '*') {
+          return deleteAllTagsFromUniverseIfAllowed(universe);
+        }
+        if (_.has(universe, tag) && _.every(_.omit(universe[tag], 'touchedByAllowedPropertyRule'))) {
+          delete universe[tag];
+          return true;
+        }
+        return false;
+      }
+
       function anyForbiddenFilterRuleMatches(universe, filterStatus) {
         var properties = ['attributes', 'styles', 'classes'];
 
diff --git a/core/modules/editor/js/editor.es6.js b/core/modules/editor/js/editor.es6.js
index b87e9141fb62f28193153398558e55fa44feceb4..f3fe38956870e7a28bbbfcfc89c78957567c78af 100644
--- a/core/modules/editor/js/editor.es6.js
+++ b/core/modules/editor/js/editor.es6.js
@@ -20,6 +20,46 @@
     return $(`#${fieldId}`).get(0);
   }
 
+  /**
+   * Filter away XSS attack vectors when switching text formats.
+   *
+   * @param {HTMLElement} field
+   *   The textarea DOM element.
+   * @param {object} format
+   *   The text format that's being activated, from
+   *   drupalSettings.editor.formats.
+   * @param {string} originalFormatID
+   *   The text format ID of the original text format.
+   * @param {function} callback
+   *   A callback to be called (with no parameters) after the field's value has
+   *   been XSS filtered.
+   */
+  function filterXssWhenSwitching(field, format, originalFormatID, callback) {
+    // A text editor that already is XSS-safe needs no additional measures.
+    if (format.editor.isXssSafe) {
+      callback(field, format);
+    }
+    // Otherwise, ensure XSS safety: let the server XSS filter this value.
+    else {
+      $.ajax({
+        url: Drupal.url(`editor/filter_xss/${format.format}`),
+        type: 'POST',
+        data: {
+          value: field.value,
+          original_format_id: originalFormatID,
+        },
+        dataType: 'json',
+        success(xssFilteredValue) {
+          // If the server returns false, then no XSS filtering is needed.
+          if (xssFilteredValue !== false) {
+            field.value = xssFilteredValue;
+          }
+          callback(field, format);
+        },
+      });
+    }
+  }
+
   /**
    * Changes the text editor on a text area.
    *
@@ -271,44 +311,4 @@
       }
     }
   };
-
-  /**
-   * Filter away XSS attack vectors when switching text formats.
-   *
-   * @param {HTMLElement} field
-   *   The textarea DOM element.
-   * @param {object} format
-   *   The text format that's being activated, from
-   *   drupalSettings.editor.formats.
-   * @param {string} originalFormatID
-   *   The text format ID of the original text format.
-   * @param {function} callback
-   *   A callback to be called (with no parameters) after the field's value has
-   *   been XSS filtered.
-   */
-  function filterXssWhenSwitching(field, format, originalFormatID, callback) {
-    // A text editor that already is XSS-safe needs no additional measures.
-    if (format.editor.isXssSafe) {
-      callback(field, format);
-    }
-    // Otherwise, ensure XSS safety: let the server XSS filter this value.
-    else {
-      $.ajax({
-        url: Drupal.url(`editor/filter_xss/${format.format}`),
-        type: 'POST',
-        data: {
-          value: field.value,
-          original_format_id: originalFormatID,
-        },
-        dataType: 'json',
-        success(xssFilteredValue) {
-          // If the server returns false, then no XSS filtering is needed.
-          if (xssFilteredValue !== false) {
-            field.value = xssFilteredValue;
-          }
-          callback(field, format);
-        },
-      });
-    }
-  }
 }(jQuery, Drupal, drupalSettings));
diff --git a/core/modules/editor/js/editor.js b/core/modules/editor/js/editor.js
index 30177497dddc09fb0f28357a60dacad73512967d..6448953f190acd372154c2ec9d194fb1c189984d 100644
--- a/core/modules/editor/js/editor.js
+++ b/core/modules/editor/js/editor.js
@@ -12,6 +12,28 @@
     return $('#' + fieldId).get(0);
   }
 
+  function filterXssWhenSwitching(field, format, originalFormatID, callback) {
+    if (format.editor.isXssSafe) {
+      callback(field, format);
+    } else {
+        $.ajax({
+          url: Drupal.url('editor/filter_xss/' + format.format),
+          type: 'POST',
+          data: {
+            value: field.value,
+            original_format_id: originalFormatID
+          },
+          dataType: 'json',
+          success: function success(xssFilteredValue) {
+            if (xssFilteredValue !== false) {
+              field.value = xssFilteredValue;
+            }
+            callback(field, format);
+          }
+        });
+      }
+  }
+
   function changeTextEditor(field, newFormatID) {
     var previousFormatID = field.getAttribute('data-editor-active-text-format');
 
@@ -168,26 +190,4 @@
       }
     }
   };
-
-  function filterXssWhenSwitching(field, format, originalFormatID, callback) {
-    if (format.editor.isXssSafe) {
-      callback(field, format);
-    } else {
-        $.ajax({
-          url: Drupal.url('editor/filter_xss/' + format.format),
-          type: 'POST',
-          data: {
-            value: field.value,
-            original_format_id: originalFormatID
-          },
-          dataType: 'json',
-          success: function success(xssFilteredValue) {
-            if (xssFilteredValue !== false) {
-              field.value = xssFilteredValue;
-            }
-            callback(field, format);
-          }
-        });
-      }
-  }
 })(jQuery, Drupal, drupalSettings);
\ No newline at end of file
diff --git a/core/modules/quickedit/js/editors/formEditor.es6.js b/core/modules/quickedit/js/editors/formEditor.es6.js
index c12308403d9c9581ad8a98100119ea1cdd46aa5a..f2eb3f03239509a451146720c66cef4e4287a5e7 100644
--- a/core/modules/quickedit/js/editors/formEditor.es6.js
+++ b/core/modules/quickedit/js/editors/formEditor.es6.js
@@ -189,17 +189,17 @@
       const editorModel = this.model;
       const fieldModel = this.fieldModel;
 
-      function cleanUpAjax() {
-        Drupal.quickedit.util.form.unajaxifySaving(formSaveAjax);
-        formSaveAjax = null;
-      }
-
       // Create an AJAX object for the form associated with the field.
       let formSaveAjax = Drupal.quickedit.util.form.ajaxifySaving({
         nocssjs: false,
         other_view_modes: fieldModel.findOtherViewModes(),
       }, $submit);
 
+      function cleanUpAjax() {
+        Drupal.quickedit.util.form.unajaxifySaving(formSaveAjax);
+        formSaveAjax = null;
+      }
+
       // Successfully saved.
       formSaveAjax.commands.quickeditFieldFormSaved = function (ajax, response, status) {
         cleanUpAjax();
diff --git a/core/modules/quickedit/js/editors/formEditor.js b/core/modules/quickedit/js/editors/formEditor.js
index 8a872edc57ef94b770f1083e7e2d932c4050dc1d..1ef1fa4077a541aa283b0d99d95a8b8e4ac857cc 100644
--- a/core/modules/quickedit/js/editors/formEditor.js
+++ b/core/modules/quickedit/js/editors/formEditor.js
@@ -122,16 +122,16 @@
       var editorModel = this.model;
       var fieldModel = this.fieldModel;
 
-      function cleanUpAjax() {
-        Drupal.quickedit.util.form.unajaxifySaving(formSaveAjax);
-        formSaveAjax = null;
-      }
-
       var formSaveAjax = Drupal.quickedit.util.form.ajaxifySaving({
         nocssjs: false,
         other_view_modes: fieldModel.findOtherViewModes()
       }, $submit);
 
+      function cleanUpAjax() {
+        Drupal.quickedit.util.form.unajaxifySaving(formSaveAjax);
+        formSaveAjax = null;
+      }
+
       formSaveAjax.commands.quickeditFieldFormSaved = function (ajax, response, status) {
         cleanUpAjax();
 
diff --git a/core/modules/quickedit/js/quickedit.es6.js b/core/modules/quickedit/js/quickedit.es6.js
index ba742033fcac818a83753b7ec07bed0a066c7ca4..eaacc3b6c51dc595c0e305d5f22355f54191055c 100644
--- a/core/modules/quickedit/js/quickedit.es6.js
+++ b/core/modules/quickedit/js/quickedit.es6.js
@@ -60,249 +60,6 @@
    */
   const entityInstancesTracker = {};
 
-  /**
-   *
-   * @type {Drupal~behavior}
-   */
-  Drupal.behaviors.quickedit = {
-    attach(context) {
-      // Initialize the Quick Edit app once per page load.
-      $('body').once('quickedit-init').each(initQuickEdit);
-
-      // Find all in-place editable fields, if any.
-      const $fields = $(context).find('[data-quickedit-field-id]').once('quickedit');
-      if ($fields.length === 0) {
-        return;
-      }
-
-      // Process each entity element: identical entities that appear multiple
-      // times will get a numeric identifier, starting at 0.
-      $(context).find('[data-quickedit-entity-id]').once('quickedit').each((index, entityElement) => {
-        processEntity(entityElement);
-      });
-
-      // Process each field element: queue to be used or to fetch metadata.
-      // When a field is being rerendered after editing, it will be processed
-      // immediately. New fields will be unable to be processed immediately,
-      // but will instead be queued to have their metadata fetched, which occurs
-      // below in fetchMissingMetaData().
-      $fields.each((index, fieldElement) => {
-        processField(fieldElement);
-      });
-
-      // Entities and fields on the page have been detected, try to set up the
-      // contextual links for those entities that already have the necessary
-      // meta- data in the client-side cache.
-      contextualLinksQueue = _.filter(contextualLinksQueue, contextualLink => !initializeEntityContextualLink(contextualLink));
-
-      // Fetch metadata for any fields that are queued to retrieve it.
-      fetchMissingMetadata((fieldElementsWithFreshMetadata) => {
-        // Metadata has been fetched, reprocess fields whose metadata was
-        // missing.
-        _.each(fieldElementsWithFreshMetadata, processField);
-
-        // Metadata has been fetched, try to set up more contextual links now.
-        contextualLinksQueue = _.filter(contextualLinksQueue, contextualLink => !initializeEntityContextualLink(contextualLink));
-      });
-    },
-    detach(context, settings, trigger) {
-      if (trigger === 'unload') {
-        deleteContainedModelsAndQueues($(context));
-      }
-    },
-  };
-
-  /**
-   *
-   * @namespace
-   */
-  Drupal.quickedit = {
-
-    /**
-     * A {@link Drupal.quickedit.AppView} instance.
-     */
-    app: null,
-
-    /**
-     * @type {object}
-     *
-     * @prop {Array.<Drupal.quickedit.EntityModel>} entities
-     * @prop {Array.<Drupal.quickedit.FieldModel>} fields
-     */
-    collections: {
-      // All in-place editable entities (Drupal.quickedit.EntityModel) on the
-      // page.
-      entities: null,
-      // All in-place editable fields (Drupal.quickedit.FieldModel) on the page.
-      fields: null,
-    },
-
-    /**
-     * In-place editors will register themselves in this object.
-     *
-     * @namespace
-     */
-    editors: {},
-
-    /**
-     * Per-field metadata that indicates whether in-place editing is allowed,
-     * which in-place editor should be used, etc.
-     *
-     * @namespace
-     */
-    metadata: {
-
-      /**
-       * Check if a field exists in storage.
-       *
-       * @param {string} fieldID
-       *   The field id to check.
-       *
-       * @return {bool}
-       *   Whether it was found or not.
-       */
-      has(fieldID) {
-        return storage.getItem(this._prefixFieldID(fieldID)) !== null;
-      },
-
-      /**
-       * Add metadata to a field id.
-       *
-       * @param {string} fieldID
-       *   The field ID to add data to.
-       * @param {object} metadata
-       *   Metadata to add.
-       */
-      add(fieldID, metadata) {
-        storage.setItem(this._prefixFieldID(fieldID), JSON.stringify(metadata));
-      },
-
-      /**
-       * Get a key from a field id.
-       *
-       * @param {string} fieldID
-       *   The field ID to check.
-       * @param {string} [key]
-       *   The key to check. If empty, will return all metadata.
-       *
-       * @return {object|*}
-       *   The value for the key, if defined. Otherwise will return all metadata
-       *   for the specified field id.
-       *
-       */
-      get(fieldID, key) {
-        const metadata = JSON.parse(storage.getItem(this._prefixFieldID(fieldID)));
-        return (typeof key === 'undefined') ? metadata : metadata[key];
-      },
-
-      /**
-       * Prefix the field id.
-       *
-       * @param {string} fieldID
-       *   The field id to prefix.
-       *
-       * @return {string}
-       *   A prefixed field id.
-       */
-      _prefixFieldID(fieldID) {
-        return `Drupal.quickedit.metadata.${fieldID}`;
-      },
-
-      /**
-       * Unprefix the field id.
-       *
-       * @param {string} fieldID
-       *   The field id to unprefix.
-       *
-       * @return {string}
-       *   An unprefixed field id.
-       */
-      _unprefixFieldID(fieldID) {
-        // Strip "Drupal.quickedit.metadata.", which is 26 characters long.
-        return fieldID.substring(26);
-      },
-
-      /**
-       * Intersection calculation.
-       *
-       * @param {Array} fieldIDs
-       *   An array of field ids to compare to prefix field id.
-       *
-       * @return {Array}
-       *   The intersection found.
-       */
-      intersection(fieldIDs) {
-        const prefixedFieldIDs = _.map(fieldIDs, this._prefixFieldID);
-        const intersection = _.intersection(prefixedFieldIDs, _.keys(sessionStorage));
-        return _.map(intersection, this._unprefixFieldID);
-      },
-    },
-  };
-
-  // Clear the Quick Edit metadata cache whenever the current user's set of
-  // permissions changes.
-  const permissionsHashKey = Drupal.quickedit.metadata._prefixFieldID('permissionsHash');
-  const permissionsHashValue = storage.getItem(permissionsHashKey);
-  const permissionsHash = drupalSettings.user.permissionsHash;
-  if (permissionsHashValue !== permissionsHash) {
-    if (typeof permissionsHash === 'string') {
-      _.chain(storage).keys().each((key) => {
-        if (key.substring(0, 26) === 'Drupal.quickedit.metadata.') {
-          storage.removeItem(key);
-        }
-      });
-    }
-    storage.setItem(permissionsHashKey, permissionsHash);
-  }
-
-  /**
-   * Detect contextual links on entities annotated by quickedit.
-   *
-   * Queue contextual links to be processed.
-   *
-   * @param {jQuery.Event} event
-   *   The `drupalContextualLinkAdded` event.
-   * @param {object} data
-   *   An object containing the data relevant to the event.
-   *
-   * @listens event:drupalContextualLinkAdded
-   */
-  $(document).on('drupalContextualLinkAdded', (event, data) => {
-    if (data.$region.is('[data-quickedit-entity-id]')) {
-      // If the contextual link is cached on the client side, an entity instance
-      // will not yet have been assigned. So assign one.
-      if (!data.$region.is('[data-quickedit-entity-instance-id]')) {
-        data.$region.once('quickedit');
-        processEntity(data.$region.get(0));
-      }
-      const contextualLink = {
-        entityID: data.$region.attr('data-quickedit-entity-id'),
-        entityInstanceID: data.$region.attr('data-quickedit-entity-instance-id'),
-        el: data.$el[0],
-        region: data.$region[0],
-      };
-      // Set up contextual links for this, otherwise queue it to be set up
-      // later.
-      if (!initializeEntityContextualLink(contextualLink)) {
-        contextualLinksQueue.push(contextualLink);
-      }
-    }
-  });
-
-  /**
-   * Extracts the entity ID from a field ID.
-   *
-   * @param {string} fieldID
-   *   A field ID: a string of the format
-   *   `<entity type>/<id>/<field name>/<language>/<view mode>`.
-   *
-   * @return {string}
-   *   An entity ID: a string of the format `<entity type>/<id>`.
-   */
-  function extractEntityID(fieldID) {
-    return fieldID.split('/').slice(0, 2).join('/');
-  }
-
   /**
    * Initialize the Quick Edit app.
    *
@@ -345,79 +102,16 @@
   }
 
   /**
-   * Fetch the field's metadata; queue or initialize it (if EntityModel exists).
+   * Initialize a field; create FieldModel.
    *
    * @param {HTMLElement} fieldElement
-   *   A Drupal Field API field's DOM element with a data-quickedit-field-id
-   *   attribute.
-   */
-  function processField(fieldElement) {
-    const metadata = Drupal.quickedit.metadata;
-    const fieldID = fieldElement.getAttribute('data-quickedit-field-id');
-    const entityID = extractEntityID(fieldID);
-    // Figure out the instance ID by looking at the ancestor
-    // [data-quickedit-entity-id] element's data-quickedit-entity-instance-id
-    // attribute.
-    const entityElementSelector = `[data-quickedit-entity-id="${entityID}"]`;
-    const $entityElement = $(entityElementSelector);
-
-    // If there are no elements returned from `entityElementSelector`
-    // throw an error. Check the browser console for this message.
-    if (!$entityElement.length) {
-      throw new Error(`Quick Edit could not associate the rendered entity field markup (with [data-quickedit-field-id="${fieldID}"]) with the corresponding rendered entity markup: no parent DOM node found with [data-quickedit-entity-id="${entityID}"]. This is typically caused by the theme's template for this entity type forgetting to print the attributes.`);
-    }
-    let entityElement = $(fieldElement).closest($entityElement);
-
-    // In the case of a full entity view page, the entity title is rendered
-    // outside of "the entity DOM node": it's rendered as the page title. So in
-    // this case, we find the lowest common parent element (deepest in the tree)
-    // and consider that the entity element.
-    if (entityElement.length === 0) {
-      const $lowestCommonParent = $entityElement.parents().has(fieldElement).first();
-      entityElement = $lowestCommonParent.find($entityElement);
-    }
-    const entityInstanceID = entityElement
-      .get(0)
-      .getAttribute('data-quickedit-entity-instance-id');
-
-    // Early-return if metadata for this field is missing.
-    if (!metadata.has(fieldID)) {
-      fieldsMetadataQueue.push({
-        el: fieldElement,
-        fieldID,
-        entityID,
-        entityInstanceID,
-      });
-      return;
-    }
-    // Early-return if the user is not allowed to in-place edit this field.
-    if (metadata.get(fieldID, 'access') !== true) {
-      return;
-    }
-
-    // If an EntityModel for this field already exists (and hence also a "Quick
-    // edit" contextual link), then initialize it immediately.
-    if (Drupal.quickedit.collections.entities.findWhere({ entityID, entityInstanceID })) {
-      initializeField(fieldElement, fieldID, entityID, entityInstanceID);
-    }
-    // Otherwise: queue the field. It is now available to be set up when its
-    // corresponding entity becomes in-place editable.
-    else {
-      fieldsAvailableQueue.push({ el: fieldElement, fieldID, entityID, entityInstanceID });
-    }
-  }
-
-  /**
-   * Initialize a field; create FieldModel.
-   *
-   * @param {HTMLElement} fieldElement
-   *   The field's DOM element.
-   * @param {string} fieldID
-   *   The field's ID.
-   * @param {string} entityID
-   *   The field's entity's ID.
-   * @param {string} entityInstanceID
-   *   The field's entity's instance ID.
+   *   The field's DOM element.
+   * @param {string} fieldID
+   *   The field's ID.
+   * @param {string} entityID
+   *   The field's entity's ID.
+   * @param {string} entityInstanceID
+   *   The field's entity's instance ID.
    */
   function initializeField(fieldElement, fieldID, entityID, entityInstanceID) {
     const entity = Drupal.quickedit.collections.entities.findWhere({
@@ -441,44 +135,6 @@
     Drupal.quickedit.collections.fields.add(field);
   }
 
-  /**
-   * Fetches metadata for fields whose metadata is missing.
-   *
-   * Fields whose metadata is missing are tracked at fieldsMetadataQueue.
-   *
-   * @param {function} callback
-   *   A callback function that receives field elements whose metadata will just
-   *   have been fetched.
-   */
-  function fetchMissingMetadata(callback) {
-    if (fieldsMetadataQueue.length) {
-      const fieldIDs = _.pluck(fieldsMetadataQueue, 'fieldID');
-      const fieldElementsWithoutMetadata = _.pluck(fieldsMetadataQueue, 'el');
-      let entityIDs = _.uniq(_.pluck(fieldsMetadataQueue, 'entityID'), true);
-      // Ensure we only request entityIDs for which we don't have metadata yet.
-      entityIDs = _.difference(entityIDs, Drupal.quickedit.metadata.intersection(entityIDs));
-      fieldsMetadataQueue = [];
-
-      $.ajax({
-        url: Drupal.url('quickedit/metadata'),
-        type: 'POST',
-        data: {
-          'fields[]': fieldIDs,
-          'entities[]': entityIDs,
-        },
-        dataType: 'json',
-        success(results) {
-          // Store the metadata.
-          _.each(results, (fieldMetadata, fieldID) => {
-            Drupal.quickedit.metadata.add(fieldID, fieldMetadata);
-          });
-
-          callback(fieldElementsWithoutMetadata);
-        },
-      });
-    }
-  }
-
   /**
    * Loads missing in-place editor's attachments (JavaScript and CSS files).
    *
@@ -629,6 +285,83 @@
     return false;
   }
 
+  /**
+   * Extracts the entity ID from a field ID.
+   *
+   * @param {string} fieldID
+   *   A field ID: a string of the format
+   *   `<entity type>/<id>/<field name>/<language>/<view mode>`.
+   *
+   * @return {string}
+   *   An entity ID: a string of the format `<entity type>/<id>`.
+   */
+  function extractEntityID(fieldID) {
+    return fieldID.split('/').slice(0, 2).join('/');
+  }
+
+  /**
+   * Fetch the field's metadata; queue or initialize it (if EntityModel exists).
+   *
+   * @param {HTMLElement} fieldElement
+   *   A Drupal Field API field's DOM element with a data-quickedit-field-id
+   *   attribute.
+   */
+  function processField(fieldElement) {
+    const metadata = Drupal.quickedit.metadata;
+    const fieldID = fieldElement.getAttribute('data-quickedit-field-id');
+    const entityID = extractEntityID(fieldID);
+    // Figure out the instance ID by looking at the ancestor
+    // [data-quickedit-entity-id] element's data-quickedit-entity-instance-id
+    // attribute.
+    const entityElementSelector = `[data-quickedit-entity-id="${entityID}"]`;
+    const $entityElement = $(entityElementSelector);
+
+    // If there are no elements returned from `entityElementSelector`
+    // throw an error. Check the browser console for this message.
+    if (!$entityElement.length) {
+      throw new Error(`Quick Edit could not associate the rendered entity field markup (with [data-quickedit-field-id="${fieldID}"]) with the corresponding rendered entity markup: no parent DOM node found with [data-quickedit-entity-id="${entityID}"]. This is typically caused by the theme's template for this entity type forgetting to print the attributes.`);
+    }
+    let entityElement = $(fieldElement).closest($entityElement);
+
+    // In the case of a full entity view page, the entity title is rendered
+    // outside of "the entity DOM node": it's rendered as the page title. So in
+    // this case, we find the lowest common parent element (deepest in the tree)
+    // and consider that the entity element.
+    if (entityElement.length === 0) {
+      const $lowestCommonParent = $entityElement.parents().has(fieldElement).first();
+      entityElement = $lowestCommonParent.find($entityElement);
+    }
+    const entityInstanceID = entityElement
+      .get(0)
+      .getAttribute('data-quickedit-entity-instance-id');
+
+    // Early-return if metadata for this field is missing.
+    if (!metadata.has(fieldID)) {
+      fieldsMetadataQueue.push({
+        el: fieldElement,
+        fieldID,
+        entityID,
+        entityInstanceID,
+      });
+      return;
+    }
+    // Early-return if the user is not allowed to in-place edit this field.
+    if (metadata.get(fieldID, 'access') !== true) {
+      return;
+    }
+
+    // If an EntityModel for this field already exists (and hence also a "Quick
+    // edit" contextual link), then initialize it immediately.
+    if (Drupal.quickedit.collections.entities.findWhere({ entityID, entityInstanceID })) {
+      initializeField(fieldElement, fieldID, entityID, entityInstanceID);
+    }
+    // Otherwise: queue the field. It is now available to be set up when its
+    // corresponding entity becomes in-place editable.
+    else {
+      fieldsAvailableQueue.push({ el: fieldElement, fieldID, entityID, entityInstanceID });
+    }
+  }
+
   /**
    * Delete models and queue items that are contained within a given context.
    *
@@ -683,4 +416,271 @@
       fieldsAvailableQueue = _.filter(fieldsAvailableQueue, hasOtherFieldElement);
     });
   }
+
+  /**
+   * Fetches metadata for fields whose metadata is missing.
+   *
+   * Fields whose metadata is missing are tracked at fieldsMetadataQueue.
+   *
+   * @param {function} callback
+   *   A callback function that receives field elements whose metadata will just
+   *   have been fetched.
+   */
+  function fetchMissingMetadata(callback) {
+    if (fieldsMetadataQueue.length) {
+      const fieldIDs = _.pluck(fieldsMetadataQueue, 'fieldID');
+      const fieldElementsWithoutMetadata = _.pluck(fieldsMetadataQueue, 'el');
+      let entityIDs = _.uniq(_.pluck(fieldsMetadataQueue, 'entityID'), true);
+      // Ensure we only request entityIDs for which we don't have metadata yet.
+      entityIDs = _.difference(entityIDs, Drupal.quickedit.metadata.intersection(entityIDs));
+      fieldsMetadataQueue = [];
+
+      $.ajax({
+        url: Drupal.url('quickedit/metadata'),
+        type: 'POST',
+        data: {
+          'fields[]': fieldIDs,
+          'entities[]': entityIDs,
+        },
+        dataType: 'json',
+        success(results) {
+          // Store the metadata.
+          _.each(results, (fieldMetadata, fieldID) => {
+            Drupal.quickedit.metadata.add(fieldID, fieldMetadata);
+          });
+
+          callback(fieldElementsWithoutMetadata);
+        },
+      });
+    }
+  }
+
+  /**
+   *
+   * @type {Drupal~behavior}
+   */
+  Drupal.behaviors.quickedit = {
+    attach(context) {
+      // Initialize the Quick Edit app once per page load.
+      $('body').once('quickedit-init').each(initQuickEdit);
+
+      // Find all in-place editable fields, if any.
+      const $fields = $(context).find('[data-quickedit-field-id]').once('quickedit');
+      if ($fields.length === 0) {
+        return;
+      }
+
+      // Process each entity element: identical entities that appear multiple
+      // times will get a numeric identifier, starting at 0.
+      $(context).find('[data-quickedit-entity-id]').once('quickedit').each((index, entityElement) => {
+        processEntity(entityElement);
+      });
+
+      // Process each field element: queue to be used or to fetch metadata.
+      // When a field is being rerendered after editing, it will be processed
+      // immediately. New fields will be unable to be processed immediately,
+      // but will instead be queued to have their metadata fetched, which occurs
+      // below in fetchMissingMetaData().
+      $fields.each((index, fieldElement) => {
+        processField(fieldElement);
+      });
+
+      // Entities and fields on the page have been detected, try to set up the
+      // contextual links for those entities that already have the necessary
+      // meta- data in the client-side cache.
+      contextualLinksQueue = _.filter(contextualLinksQueue, contextualLink => !initializeEntityContextualLink(contextualLink));
+
+      // Fetch metadata for any fields that are queued to retrieve it.
+      fetchMissingMetadata((fieldElementsWithFreshMetadata) => {
+        // Metadata has been fetched, reprocess fields whose metadata was
+        // missing.
+        _.each(fieldElementsWithFreshMetadata, processField);
+
+        // Metadata has been fetched, try to set up more contextual links now.
+        contextualLinksQueue = _.filter(contextualLinksQueue, contextualLink => !initializeEntityContextualLink(contextualLink));
+      });
+    },
+    detach(context, settings, trigger) {
+      if (trigger === 'unload') {
+        deleteContainedModelsAndQueues($(context));
+      }
+    },
+  };
+
+  /**
+   *
+   * @namespace
+   */
+  Drupal.quickedit = {
+
+    /**
+     * A {@link Drupal.quickedit.AppView} instance.
+     */
+    app: null,
+
+    /**
+     * @type {object}
+     *
+     * @prop {Array.<Drupal.quickedit.EntityModel>} entities
+     * @prop {Array.<Drupal.quickedit.FieldModel>} fields
+     */
+    collections: {
+      // All in-place editable entities (Drupal.quickedit.EntityModel) on the
+      // page.
+      entities: null,
+      // All in-place editable fields (Drupal.quickedit.FieldModel) on the page.
+      fields: null,
+    },
+
+    /**
+     * In-place editors will register themselves in this object.
+     *
+     * @namespace
+     */
+    editors: {},
+
+    /**
+     * Per-field metadata that indicates whether in-place editing is allowed,
+     * which in-place editor should be used, etc.
+     *
+     * @namespace
+     */
+    metadata: {
+
+      /**
+       * Check if a field exists in storage.
+       *
+       * @param {string} fieldID
+       *   The field id to check.
+       *
+       * @return {bool}
+       *   Whether it was found or not.
+       */
+      has(fieldID) {
+        return storage.getItem(this._prefixFieldID(fieldID)) !== null;
+      },
+
+      /**
+       * Add metadata to a field id.
+       *
+       * @param {string} fieldID
+       *   The field ID to add data to.
+       * @param {object} metadata
+       *   Metadata to add.
+       */
+      add(fieldID, metadata) {
+        storage.setItem(this._prefixFieldID(fieldID), JSON.stringify(metadata));
+      },
+
+      /**
+       * Get a key from a field id.
+       *
+       * @param {string} fieldID
+       *   The field ID to check.
+       * @param {string} [key]
+       *   The key to check. If empty, will return all metadata.
+       *
+       * @return {object|*}
+       *   The value for the key, if defined. Otherwise will return all metadata
+       *   for the specified field id.
+       *
+       */
+      get(fieldID, key) {
+        const metadata = JSON.parse(storage.getItem(this._prefixFieldID(fieldID)));
+        return (typeof key === 'undefined') ? metadata : metadata[key];
+      },
+
+      /**
+       * Prefix the field id.
+       *
+       * @param {string} fieldID
+       *   The field id to prefix.
+       *
+       * @return {string}
+       *   A prefixed field id.
+       */
+      _prefixFieldID(fieldID) {
+        return `Drupal.quickedit.metadata.${fieldID}`;
+      },
+
+      /**
+       * Unprefix the field id.
+       *
+       * @param {string} fieldID
+       *   The field id to unprefix.
+       *
+       * @return {string}
+       *   An unprefixed field id.
+       */
+      _unprefixFieldID(fieldID) {
+        // Strip "Drupal.quickedit.metadata.", which is 26 characters long.
+        return fieldID.substring(26);
+      },
+
+      /**
+       * Intersection calculation.
+       *
+       * @param {Array} fieldIDs
+       *   An array of field ids to compare to prefix field id.
+       *
+       * @return {Array}
+       *   The intersection found.
+       */
+      intersection(fieldIDs) {
+        const prefixedFieldIDs = _.map(fieldIDs, this._prefixFieldID);
+        const intersection = _.intersection(prefixedFieldIDs, _.keys(sessionStorage));
+        return _.map(intersection, this._unprefixFieldID);
+      },
+    },
+  };
+
+  // Clear the Quick Edit metadata cache whenever the current user's set of
+  // permissions changes.
+  const permissionsHashKey = Drupal.quickedit.metadata._prefixFieldID('permissionsHash');
+  const permissionsHashValue = storage.getItem(permissionsHashKey);
+  const permissionsHash = drupalSettings.user.permissionsHash;
+  if (permissionsHashValue !== permissionsHash) {
+    if (typeof permissionsHash === 'string') {
+      _.chain(storage).keys().each((key) => {
+        if (key.substring(0, 26) === 'Drupal.quickedit.metadata.') {
+          storage.removeItem(key);
+        }
+      });
+    }
+    storage.setItem(permissionsHashKey, permissionsHash);
+  }
+
+  /**
+   * Detect contextual links on entities annotated by quickedit.
+   *
+   * Queue contextual links to be processed.
+   *
+   * @param {jQuery.Event} event
+   *   The `drupalContextualLinkAdded` event.
+   * @param {object} data
+   *   An object containing the data relevant to the event.
+   *
+   * @listens event:drupalContextualLinkAdded
+   */
+  $(document).on('drupalContextualLinkAdded', (event, data) => {
+    if (data.$region.is('[data-quickedit-entity-id]')) {
+      // If the contextual link is cached on the client side, an entity instance
+      // will not yet have been assigned. So assign one.
+      if (!data.$region.is('[data-quickedit-entity-instance-id]')) {
+        data.$region.once('quickedit');
+        processEntity(data.$region.get(0));
+      }
+      const contextualLink = {
+        entityID: data.$region.attr('data-quickedit-entity-id'),
+        entityInstanceID: data.$region.attr('data-quickedit-entity-instance-id'),
+        el: data.$el[0],
+        region: data.$region[0],
+      };
+      // Set up contextual links for this, otherwise queue it to be set up
+      // later.
+      if (!initializeEntityContextualLink(contextualLink)) {
+        contextualLinksQueue.push(contextualLink);
+      }
+    }
+  });
 }(jQuery, _, Backbone, Drupal, drupalSettings, window.JSON, window.sessionStorage));
diff --git a/core/modules/quickedit/js/quickedit.js b/core/modules/quickedit/js/quickedit.js
index 4548c013a33f2b8d7001dbb4738441e5d05cdce2..1c677aa1e904b2d4c2af3405a428a86633824d34 100644
--- a/core/modules/quickedit/js/quickedit.js
+++ b/core/modules/quickedit/js/quickedit.js
@@ -20,115 +20,6 @@
 
   var entityInstancesTracker = {};
 
-  Drupal.behaviors.quickedit = {
-    attach: function attach(context) {
-      $('body').once('quickedit-init').each(initQuickEdit);
-
-      var $fields = $(context).find('[data-quickedit-field-id]').once('quickedit');
-      if ($fields.length === 0) {
-        return;
-      }
-
-      $(context).find('[data-quickedit-entity-id]').once('quickedit').each(function (index, entityElement) {
-        processEntity(entityElement);
-      });
-
-      $fields.each(function (index, fieldElement) {
-        processField(fieldElement);
-      });
-
-      contextualLinksQueue = _.filter(contextualLinksQueue, function (contextualLink) {
-        return !initializeEntityContextualLink(contextualLink);
-      });
-
-      fetchMissingMetadata(function (fieldElementsWithFreshMetadata) {
-        _.each(fieldElementsWithFreshMetadata, processField);
-
-        contextualLinksQueue = _.filter(contextualLinksQueue, function (contextualLink) {
-          return !initializeEntityContextualLink(contextualLink);
-        });
-      });
-    },
-    detach: function detach(context, settings, trigger) {
-      if (trigger === 'unload') {
-        deleteContainedModelsAndQueues($(context));
-      }
-    }
-  };
-
-  Drupal.quickedit = {
-    app: null,
-
-    collections: {
-      entities: null,
-
-      fields: null
-    },
-
-    editors: {},
-
-    metadata: {
-      has: function has(fieldID) {
-        return storage.getItem(this._prefixFieldID(fieldID)) !== null;
-      },
-      add: function add(fieldID, metadata) {
-        storage.setItem(this._prefixFieldID(fieldID), JSON.stringify(metadata));
-      },
-      get: function get(fieldID, key) {
-        var metadata = JSON.parse(storage.getItem(this._prefixFieldID(fieldID)));
-        return typeof key === 'undefined' ? metadata : metadata[key];
-      },
-      _prefixFieldID: function _prefixFieldID(fieldID) {
-        return 'Drupal.quickedit.metadata.' + fieldID;
-      },
-      _unprefixFieldID: function _unprefixFieldID(fieldID) {
-        return fieldID.substring(26);
-      },
-      intersection: function intersection(fieldIDs) {
-        var prefixedFieldIDs = _.map(fieldIDs, this._prefixFieldID);
-        var intersection = _.intersection(prefixedFieldIDs, _.keys(sessionStorage));
-        return _.map(intersection, this._unprefixFieldID);
-      }
-    }
-  };
-
-  var permissionsHashKey = Drupal.quickedit.metadata._prefixFieldID('permissionsHash');
-  var permissionsHashValue = storage.getItem(permissionsHashKey);
-  var permissionsHash = drupalSettings.user.permissionsHash;
-  if (permissionsHashValue !== permissionsHash) {
-    if (typeof permissionsHash === 'string') {
-      _.chain(storage).keys().each(function (key) {
-        if (key.substring(0, 26) === 'Drupal.quickedit.metadata.') {
-          storage.removeItem(key);
-        }
-      });
-    }
-    storage.setItem(permissionsHashKey, permissionsHash);
-  }
-
-  $(document).on('drupalContextualLinkAdded', function (event, data) {
-    if (data.$region.is('[data-quickedit-entity-id]')) {
-      if (!data.$region.is('[data-quickedit-entity-instance-id]')) {
-        data.$region.once('quickedit');
-        processEntity(data.$region.get(0));
-      }
-      var contextualLink = {
-        entityID: data.$region.attr('data-quickedit-entity-id'),
-        entityInstanceID: data.$region.attr('data-quickedit-entity-instance-id'),
-        el: data.$el[0],
-        region: data.$region[0]
-      };
-
-      if (!initializeEntityContextualLink(contextualLink)) {
-        contextualLinksQueue.push(contextualLink);
-      }
-    }
-  });
-
-  function extractEntityID(fieldID) {
-    return fieldID.split('/').slice(0, 2).join('/');
-  }
-
   function initQuickEdit(bodyElement) {
     Drupal.quickedit.collections.entities = new Drupal.quickedit.EntityCollection();
     Drupal.quickedit.collections.fields = new Drupal.quickedit.FieldCollection();
@@ -153,46 +44,6 @@
     entityElement.setAttribute('data-quickedit-entity-instance-id', entityInstanceID);
   }
 
-  function processField(fieldElement) {
-    var metadata = Drupal.quickedit.metadata;
-    var fieldID = fieldElement.getAttribute('data-quickedit-field-id');
-    var entityID = extractEntityID(fieldID);
-
-    var entityElementSelector = '[data-quickedit-entity-id="' + entityID + '"]';
-    var $entityElement = $(entityElementSelector);
-
-    if (!$entityElement.length) {
-      throw new Error('Quick Edit could not associate the rendered entity field markup (with [data-quickedit-field-id="' + fieldID + '"]) with the corresponding rendered entity markup: no parent DOM node found with [data-quickedit-entity-id="' + entityID + '"]. This is typically caused by the theme\'s template for this entity type forgetting to print the attributes.');
-    }
-    var entityElement = $(fieldElement).closest($entityElement);
-
-    if (entityElement.length === 0) {
-      var $lowestCommonParent = $entityElement.parents().has(fieldElement).first();
-      entityElement = $lowestCommonParent.find($entityElement);
-    }
-    var entityInstanceID = entityElement.get(0).getAttribute('data-quickedit-entity-instance-id');
-
-    if (!metadata.has(fieldID)) {
-      fieldsMetadataQueue.push({
-        el: fieldElement,
-        fieldID: fieldID,
-        entityID: entityID,
-        entityInstanceID: entityInstanceID
-      });
-      return;
-    }
-
-    if (metadata.get(fieldID, 'access') !== true) {
-      return;
-    }
-
-    if (Drupal.quickedit.collections.entities.findWhere({ entityID: entityID, entityInstanceID: entityInstanceID })) {
-      initializeField(fieldElement, fieldID, entityID, entityInstanceID);
-    } else {
-        fieldsAvailableQueue.push({ el: fieldElement, fieldID: fieldID, entityID: entityID, entityInstanceID: entityInstanceID });
-      }
-  }
-
   function initializeField(fieldElement, fieldID, entityID, entityInstanceID) {
     var entity = Drupal.quickedit.collections.entities.findWhere({
       entityID: entityID,
@@ -213,34 +64,6 @@
     Drupal.quickedit.collections.fields.add(field);
   }
 
-  function fetchMissingMetadata(callback) {
-    if (fieldsMetadataQueue.length) {
-      var fieldIDs = _.pluck(fieldsMetadataQueue, 'fieldID');
-      var fieldElementsWithoutMetadata = _.pluck(fieldsMetadataQueue, 'el');
-      var entityIDs = _.uniq(_.pluck(fieldsMetadataQueue, 'entityID'), true);
-
-      entityIDs = _.difference(entityIDs, Drupal.quickedit.metadata.intersection(entityIDs));
-      fieldsMetadataQueue = [];
-
-      $.ajax({
-        url: Drupal.url('quickedit/metadata'),
-        type: 'POST',
-        data: {
-          'fields[]': fieldIDs,
-          'entities[]': entityIDs
-        },
-        dataType: 'json',
-        success: function success(results) {
-          _.each(results, function (fieldMetadata, fieldID) {
-            Drupal.quickedit.metadata.add(fieldID, fieldMetadata);
-          });
-
-          callback(fieldElementsWithoutMetadata);
-        }
-      });
-    }
-  }
-
   function loadMissingEditors(callback) {
     var loadedEditors = _.keys(Drupal.quickedit.editors);
     var missingEditors = [];
@@ -338,6 +161,50 @@
     return false;
   }
 
+  function extractEntityID(fieldID) {
+    return fieldID.split('/').slice(0, 2).join('/');
+  }
+
+  function processField(fieldElement) {
+    var metadata = Drupal.quickedit.metadata;
+    var fieldID = fieldElement.getAttribute('data-quickedit-field-id');
+    var entityID = extractEntityID(fieldID);
+
+    var entityElementSelector = '[data-quickedit-entity-id="' + entityID + '"]';
+    var $entityElement = $(entityElementSelector);
+
+    if (!$entityElement.length) {
+      throw new Error('Quick Edit could not associate the rendered entity field markup (with [data-quickedit-field-id="' + fieldID + '"]) with the corresponding rendered entity markup: no parent DOM node found with [data-quickedit-entity-id="' + entityID + '"]. This is typically caused by the theme\'s template for this entity type forgetting to print the attributes.');
+    }
+    var entityElement = $(fieldElement).closest($entityElement);
+
+    if (entityElement.length === 0) {
+      var $lowestCommonParent = $entityElement.parents().has(fieldElement).first();
+      entityElement = $lowestCommonParent.find($entityElement);
+    }
+    var entityInstanceID = entityElement.get(0).getAttribute('data-quickedit-entity-instance-id');
+
+    if (!metadata.has(fieldID)) {
+      fieldsMetadataQueue.push({
+        el: fieldElement,
+        fieldID: fieldID,
+        entityID: entityID,
+        entityInstanceID: entityInstanceID
+      });
+      return;
+    }
+
+    if (metadata.get(fieldID, 'access') !== true) {
+      return;
+    }
+
+    if (Drupal.quickedit.collections.entities.findWhere({ entityID: entityID, entityInstanceID: entityInstanceID })) {
+      initializeField(fieldElement, fieldID, entityID, entityInstanceID);
+    } else {
+        fieldsAvailableQueue.push({ el: fieldElement, fieldID: fieldID, entityID: entityID, entityInstanceID: entityInstanceID });
+      }
+  }
+
   function deleteContainedModelsAndQueues($context) {
     $context.find('[data-quickedit-entity-id]').addBack('[data-quickedit-entity-id]').each(function (index, entityElement) {
       var entityModel = Drupal.quickedit.collections.entities.findWhere({ el: entityElement });
@@ -371,4 +238,137 @@
       fieldsAvailableQueue = _.filter(fieldsAvailableQueue, hasOtherFieldElement);
     });
   }
+
+  function fetchMissingMetadata(callback) {
+    if (fieldsMetadataQueue.length) {
+      var fieldIDs = _.pluck(fieldsMetadataQueue, 'fieldID');
+      var fieldElementsWithoutMetadata = _.pluck(fieldsMetadataQueue, 'el');
+      var entityIDs = _.uniq(_.pluck(fieldsMetadataQueue, 'entityID'), true);
+
+      entityIDs = _.difference(entityIDs, Drupal.quickedit.metadata.intersection(entityIDs));
+      fieldsMetadataQueue = [];
+
+      $.ajax({
+        url: Drupal.url('quickedit/metadata'),
+        type: 'POST',
+        data: {
+          'fields[]': fieldIDs,
+          'entities[]': entityIDs
+        },
+        dataType: 'json',
+        success: function success(results) {
+          _.each(results, function (fieldMetadata, fieldID) {
+            Drupal.quickedit.metadata.add(fieldID, fieldMetadata);
+          });
+
+          callback(fieldElementsWithoutMetadata);
+        }
+      });
+    }
+  }
+
+  Drupal.behaviors.quickedit = {
+    attach: function attach(context) {
+      $('body').once('quickedit-init').each(initQuickEdit);
+
+      var $fields = $(context).find('[data-quickedit-field-id]').once('quickedit');
+      if ($fields.length === 0) {
+        return;
+      }
+
+      $(context).find('[data-quickedit-entity-id]').once('quickedit').each(function (index, entityElement) {
+        processEntity(entityElement);
+      });
+
+      $fields.each(function (index, fieldElement) {
+        processField(fieldElement);
+      });
+
+      contextualLinksQueue = _.filter(contextualLinksQueue, function (contextualLink) {
+        return !initializeEntityContextualLink(contextualLink);
+      });
+
+      fetchMissingMetadata(function (fieldElementsWithFreshMetadata) {
+        _.each(fieldElementsWithFreshMetadata, processField);
+
+        contextualLinksQueue = _.filter(contextualLinksQueue, function (contextualLink) {
+          return !initializeEntityContextualLink(contextualLink);
+        });
+      });
+    },
+    detach: function detach(context, settings, trigger) {
+      if (trigger === 'unload') {
+        deleteContainedModelsAndQueues($(context));
+      }
+    }
+  };
+
+  Drupal.quickedit = {
+    app: null,
+
+    collections: {
+      entities: null,
+
+      fields: null
+    },
+
+    editors: {},
+
+    metadata: {
+      has: function has(fieldID) {
+        return storage.getItem(this._prefixFieldID(fieldID)) !== null;
+      },
+      add: function add(fieldID, metadata) {
+        storage.setItem(this._prefixFieldID(fieldID), JSON.stringify(metadata));
+      },
+      get: function get(fieldID, key) {
+        var metadata = JSON.parse(storage.getItem(this._prefixFieldID(fieldID)));
+        return typeof key === 'undefined' ? metadata : metadata[key];
+      },
+      _prefixFieldID: function _prefixFieldID(fieldID) {
+        return 'Drupal.quickedit.metadata.' + fieldID;
+      },
+      _unprefixFieldID: function _unprefixFieldID(fieldID) {
+        return fieldID.substring(26);
+      },
+      intersection: function intersection(fieldIDs) {
+        var prefixedFieldIDs = _.map(fieldIDs, this._prefixFieldID);
+        var intersection = _.intersection(prefixedFieldIDs, _.keys(sessionStorage));
+        return _.map(intersection, this._unprefixFieldID);
+      }
+    }
+  };
+
+  var permissionsHashKey = Drupal.quickedit.metadata._prefixFieldID('permissionsHash');
+  var permissionsHashValue = storage.getItem(permissionsHashKey);
+  var permissionsHash = drupalSettings.user.permissionsHash;
+  if (permissionsHashValue !== permissionsHash) {
+    if (typeof permissionsHash === 'string') {
+      _.chain(storage).keys().each(function (key) {
+        if (key.substring(0, 26) === 'Drupal.quickedit.metadata.') {
+          storage.removeItem(key);
+        }
+      });
+    }
+    storage.setItem(permissionsHashKey, permissionsHash);
+  }
+
+  $(document).on('drupalContextualLinkAdded', function (event, data) {
+    if (data.$region.is('[data-quickedit-entity-id]')) {
+      if (!data.$region.is('[data-quickedit-entity-instance-id]')) {
+        data.$region.once('quickedit');
+        processEntity(data.$region.get(0));
+      }
+      var contextualLink = {
+        entityID: data.$region.attr('data-quickedit-entity-id'),
+        entityInstanceID: data.$region.attr('data-quickedit-entity-instance-id'),
+        el: data.$el[0],
+        region: data.$region[0]
+      };
+
+      if (!initializeEntityContextualLink(contextualLink)) {
+        contextualLinksQueue.push(contextualLink);
+      }
+    }
+  });
 })(jQuery, _, Backbone, Drupal, drupalSettings, window.JSON, window.sessionStorage);
\ No newline at end of file
diff --git a/core/modules/toolbar/js/toolbar.menu.es6.js b/core/modules/toolbar/js/toolbar.menu.es6.js
index 03fd41f371dff0e8c61d48fa195f8217852dd6cd..1c06d8deff05343f6ca58335be1803a462f1d5a0 100644
--- a/core/modules/toolbar/js/toolbar.menu.es6.js
+++ b/core/modules/toolbar/js/toolbar.menu.es6.js
@@ -20,6 +20,30 @@
       handleClose: Drupal.t('Collapse'),
     };
 
+    /**
+     * Toggle the open/close state of a list is a menu.
+     *
+     * @param {jQuery} $item
+     *   The li item to be toggled.
+     *
+     * @param {Boolean} switcher
+     *   A flag that forces toggleClass to add or a remove a class, rather than
+     *   simply toggling its presence.
+     */
+    function toggleList($item, switcher) {
+      const $toggle = $item.children('.toolbar-box').children('.toolbar-handle');
+      switcher = (typeof switcher !== 'undefined') ? switcher : !$item.hasClass('open');
+      // Toggle the item open state.
+      $item.toggleClass('open', switcher);
+      // Twist the toggle.
+      $toggle.toggleClass('open', switcher);
+      // Adjust the toggle text.
+      $toggle
+        .find('.action')
+        // Expand Structure, Collapse Structure.
+        .text((switcher) ? ui.handleClose : ui.handleOpen);
+    }
+
     /**
      * Handle clicks from the disclosure button on an item with sub-items.
      *
@@ -56,30 +80,6 @@
       event.stopPropagation();
     }
 
-    /**
-     * Toggle the open/close state of a list is a menu.
-     *
-     * @param {jQuery} $item
-     *   The li item to be toggled.
-     *
-     * @param {Boolean} switcher
-     *   A flag that forces toggleClass to add or a remove a class, rather than
-     *   simply toggling its presence.
-     */
-    function toggleList($item, switcher) {
-      const $toggle = $item.children('.toolbar-box').children('.toolbar-handle');
-      switcher = (typeof switcher !== 'undefined') ? switcher : !$item.hasClass('open');
-      // Toggle the item open state.
-      $item.toggleClass('open', switcher);
-      // Twist the toggle.
-      $toggle.toggleClass('open', switcher);
-      // Adjust the toggle text.
-      $toggle
-        .find('.action')
-        // Expand Structure, Collapse Structure.
-        .text((switcher) ? ui.handleClose : ui.handleOpen);
-    }
-
     /**
      * Add markup to the menu elements.
      *
diff --git a/core/modules/toolbar/js/toolbar.menu.js b/core/modules/toolbar/js/toolbar.menu.js
index 45cfafbca87c36e0cdc3bf14f53a87d6044b093a..23f1076ce1ee5f4cceef01ffcb9e1ebfa78ecd0e 100644
--- a/core/modules/toolbar/js/toolbar.menu.js
+++ b/core/modules/toolbar/js/toolbar.menu.js
@@ -14,6 +14,17 @@
       handleClose: Drupal.t('Collapse')
     };
 
+    function toggleList($item, switcher) {
+      var $toggle = $item.children('.toolbar-box').children('.toolbar-handle');
+      switcher = typeof switcher !== 'undefined' ? switcher : !$item.hasClass('open');
+
+      $item.toggleClass('open', switcher);
+
+      $toggle.toggleClass('open', switcher);
+
+      $toggle.find('.action').text(switcher ? ui.handleClose : ui.handleOpen);
+    }
+
     function toggleClickHandler(event) {
       var $toggle = $(event.target);
       var $item = $toggle.closest('li');
@@ -32,17 +43,6 @@
       event.stopPropagation();
     }
 
-    function toggleList($item, switcher) {
-      var $toggle = $item.children('.toolbar-box').children('.toolbar-handle');
-      switcher = typeof switcher !== 'undefined' ? switcher : !$item.hasClass('open');
-
-      $item.toggleClass('open', switcher);
-
-      $toggle.toggleClass('open', switcher);
-
-      $toggle.find('.action').text(switcher ? ui.handleClose : ui.handleOpen);
-    }
-
     function initItems($menu) {
       var options = {
         class: 'toolbar-icon toolbar-handle',
diff --git a/core/modules/tracker/js/tracker-history.es6.js b/core/modules/tracker/js/tracker-history.es6.js
index 13090008d65e663550408c120c488e0c8db3971c..a80fa1d714be64ffb762fc3a3f0691371320b33f 100644
--- a/core/modules/tracker/js/tracker-history.es6.js
+++ b/core/modules/tracker/js/tracker-history.es6.js
@@ -4,6 +4,59 @@
  * May only be loaded for authenticated users, with the History module enabled.
  */
 (function ($, Drupal, window) {
+  function processNodeNewIndicators($placeholders) {
+    const newNodeString = Drupal.t('new');
+    const updatedNodeString = Drupal.t('updated');
+
+    $placeholders.each((index, placeholder) => {
+      const timestamp = parseInt(placeholder.getAttribute('data-history-node-timestamp'), 10);
+      const nodeID = placeholder.getAttribute('data-history-node-id');
+      const lastViewTimestamp = Drupal.history.getLastRead(nodeID);
+
+      if (timestamp > lastViewTimestamp) {
+        const message = (lastViewTimestamp === 0) ? newNodeString : updatedNodeString;
+        $(placeholder).append(`<span class="marker">${message}</span>`);
+      }
+    });
+  }
+
+  function processNewRepliesIndicators($placeholders) {
+    // Figure out which placeholders need the "x new" replies links.
+    const placeholdersToUpdate = {};
+    $placeholders.each((index, placeholder) => {
+      const timestamp = parseInt(placeholder.getAttribute('data-history-node-last-comment-timestamp'), 10);
+      const nodeID = placeholder.previousSibling.previousSibling.getAttribute('data-history-node-id');
+      const lastViewTimestamp = Drupal.history.getLastRead(nodeID);
+
+      // Queue this placeholder's "X new" replies link to be downloaded from the
+      // server.
+      if (timestamp > lastViewTimestamp) {
+        placeholdersToUpdate[nodeID] = placeholder;
+      }
+    });
+
+    // Perform an AJAX request to retrieve node view timestamps.
+    const nodeIDs = Object.keys(placeholdersToUpdate);
+    if (nodeIDs.length === 0) {
+      return;
+    }
+    $.ajax({
+      url: Drupal.url('comments/render_new_comments_node_links'),
+      type: 'POST',
+      data: { 'node_ids[]': nodeIDs },
+      dataType: 'json',
+      success(results) {
+        Object.keys(results || {}).forEach((nodeID) => {
+          if (placeholdersToUpdate.hasOwnProperty(nodeID)) {
+            const url = results[nodeID].first_new_comment_link;
+            const text = Drupal.formatPlural(results[nodeID].new_comment_count, '1 new', '@count new');
+            $(placeholdersToUpdate[nodeID]).append(`<br /><a href="${url}">${text}</a>`);
+          }
+        });
+      },
+    });
+  }
+
   /**
    * Render "new" and "updated" node indicators, as well as "X new" replies links.
    */
@@ -60,57 +113,4 @@
       });
     },
   };
-
-  function processNodeNewIndicators($placeholders) {
-    const newNodeString = Drupal.t('new');
-    const updatedNodeString = Drupal.t('updated');
-
-    $placeholders.each((index, placeholder) => {
-      const timestamp = parseInt(placeholder.getAttribute('data-history-node-timestamp'), 10);
-      const nodeID = placeholder.getAttribute('data-history-node-id');
-      const lastViewTimestamp = Drupal.history.getLastRead(nodeID);
-
-      if (timestamp > lastViewTimestamp) {
-        const message = (lastViewTimestamp === 0) ? newNodeString : updatedNodeString;
-        $(placeholder).append(`<span class="marker">${message}</span>`);
-      }
-    });
-  }
-
-  function processNewRepliesIndicators($placeholders) {
-    // Figure out which placeholders need the "x new" replies links.
-    const placeholdersToUpdate = {};
-    $placeholders.each((index, placeholder) => {
-      const timestamp = parseInt(placeholder.getAttribute('data-history-node-last-comment-timestamp'), 10);
-      const nodeID = placeholder.previousSibling.previousSibling.getAttribute('data-history-node-id');
-      const lastViewTimestamp = Drupal.history.getLastRead(nodeID);
-
-      // Queue this placeholder's "X new" replies link to be downloaded from the
-      // server.
-      if (timestamp > lastViewTimestamp) {
-        placeholdersToUpdate[nodeID] = placeholder;
-      }
-    });
-
-    // Perform an AJAX request to retrieve node view timestamps.
-    const nodeIDs = Object.keys(placeholdersToUpdate);
-    if (nodeIDs.length === 0) {
-      return;
-    }
-    $.ajax({
-      url: Drupal.url('comments/render_new_comments_node_links'),
-      type: 'POST',
-      data: { 'node_ids[]': nodeIDs },
-      dataType: 'json',
-      success(results) {
-        Object.keys(results || {}).forEach((nodeID) => {
-          if (placeholdersToUpdate.hasOwnProperty(nodeID)) {
-            const url = results[nodeID].first_new_comment_link;
-            const text = Drupal.formatPlural(results[nodeID].new_comment_count, '1 new', '@count new');
-            $(placeholdersToUpdate[nodeID]).append(`<br /><a href="${url}">${text}</a>`);
-          }
-        });
-      },
-    });
-  }
 }(jQuery, Drupal, window));
diff --git a/core/modules/tracker/js/tracker-history.js b/core/modules/tracker/js/tracker-history.js
index a147eb8a63641fbce5bd37c85da221a9108a37c4..30a500fe708f154295fb4b1795f6d5bf8d54885c 100644
--- a/core/modules/tracker/js/tracker-history.js
+++ b/core/modules/tracker/js/tracker-history.js
@@ -6,49 +6,6 @@
 **/
 
 (function ($, Drupal, window) {
-  Drupal.behaviors.trackerHistory = {
-    attach: function attach(context) {
-      var nodeIDs = [];
-      var $nodeNewPlaceholders = $(context).find('[data-history-node-timestamp]').once('history').filter(function () {
-        var nodeTimestamp = parseInt(this.getAttribute('data-history-node-timestamp'), 10);
-        var nodeID = this.getAttribute('data-history-node-id');
-        if (Drupal.history.needsServerCheck(nodeID, nodeTimestamp)) {
-          nodeIDs.push(nodeID);
-          return true;
-        }
-
-        return false;
-      });
-
-      var $newRepliesPlaceholders = $(context).find('[data-history-node-last-comment-timestamp]').once('history').filter(function () {
-        var lastCommentTimestamp = parseInt(this.getAttribute('data-history-node-last-comment-timestamp'), 10);
-        var nodeTimestamp = parseInt(this.previousSibling.previousSibling.getAttribute('data-history-node-timestamp'), 10);
-
-        if (lastCommentTimestamp === nodeTimestamp) {
-          return false;
-        }
-        var nodeID = this.previousSibling.previousSibling.getAttribute('data-history-node-id');
-        if (Drupal.history.needsServerCheck(nodeID, lastCommentTimestamp)) {
-          if (nodeIDs.indexOf(nodeID) === -1) {
-            nodeIDs.push(nodeID);
-          }
-          return true;
-        }
-
-        return false;
-      });
-
-      if ($nodeNewPlaceholders.length === 0 && $newRepliesPlaceholders.length === 0) {
-        return;
-      }
-
-      Drupal.history.fetchTimestamps(nodeIDs, function () {
-        processNodeNewIndicators($nodeNewPlaceholders);
-        processNewRepliesIndicators($newRepliesPlaceholders);
-      });
-    }
-  };
-
   function processNodeNewIndicators($placeholders) {
     var newNodeString = Drupal.t('new');
     var updatedNodeString = Drupal.t('updated');
@@ -97,4 +54,47 @@
       }
     });
   }
+
+  Drupal.behaviors.trackerHistory = {
+    attach: function attach(context) {
+      var nodeIDs = [];
+      var $nodeNewPlaceholders = $(context).find('[data-history-node-timestamp]').once('history').filter(function () {
+        var nodeTimestamp = parseInt(this.getAttribute('data-history-node-timestamp'), 10);
+        var nodeID = this.getAttribute('data-history-node-id');
+        if (Drupal.history.needsServerCheck(nodeID, nodeTimestamp)) {
+          nodeIDs.push(nodeID);
+          return true;
+        }
+
+        return false;
+      });
+
+      var $newRepliesPlaceholders = $(context).find('[data-history-node-last-comment-timestamp]').once('history').filter(function () {
+        var lastCommentTimestamp = parseInt(this.getAttribute('data-history-node-last-comment-timestamp'), 10);
+        var nodeTimestamp = parseInt(this.previousSibling.previousSibling.getAttribute('data-history-node-timestamp'), 10);
+
+        if (lastCommentTimestamp === nodeTimestamp) {
+          return false;
+        }
+        var nodeID = this.previousSibling.previousSibling.getAttribute('data-history-node-id');
+        if (Drupal.history.needsServerCheck(nodeID, lastCommentTimestamp)) {
+          if (nodeIDs.indexOf(nodeID) === -1) {
+            nodeIDs.push(nodeID);
+          }
+          return true;
+        }
+
+        return false;
+      });
+
+      if ($nodeNewPlaceholders.length === 0 && $newRepliesPlaceholders.length === 0) {
+        return;
+      }
+
+      Drupal.history.fetchTimestamps(nodeIDs, function () {
+        processNodeNewIndicators($nodeNewPlaceholders);
+        processNewRepliesIndicators($newRepliesPlaceholders);
+      });
+    }
+  };
 })(jQuery, Drupal, window);
\ No newline at end of file
diff --git a/core/themes/seven/js/responsive-details.es6.js b/core/themes/seven/js/responsive-details.es6.js
index 8d5c46565a28fe79d7ec27635a289a54c60146e1..78530d8ea63f37bc1611804520ff4ecc2074ac41 100644
--- a/core/themes/seven/js/responsive-details.es6.js
+++ b/core/themes/seven/js/responsive-details.es6.js
@@ -20,6 +20,8 @@
         return;
       }
 
+      const $summaries = $details.find('> summary');
+
       function detailsToggle(matches) {
         if (matches) {
           $details.attr('open', true);
@@ -43,7 +45,6 @@
         detailsToggle(event.matches);
       }
 
-      const $summaries = $details.find('> summary');
       const mql = window.matchMedia('(min-width:48em)');
       mql.addListener(handleDetailsMQ);
       detailsToggle(mql.matches);
diff --git a/core/themes/seven/js/responsive-details.js b/core/themes/seven/js/responsive-details.js
index 62ad4b2b35fc6a006b70a60923d4e1b675c8d159..3c62e3cef6af47dfb1d0190b9aa1af256d99fe86 100644
--- a/core/themes/seven/js/responsive-details.js
+++ b/core/themes/seven/js/responsive-details.js
@@ -14,6 +14,8 @@
         return;
       }
 
+      var $summaries = $details.find('> summary');
+
       function detailsToggle(matches) {
         if (matches) {
           $details.attr('open', true);
@@ -31,7 +33,6 @@
         detailsToggle(event.matches);
       }
 
-      var $summaries = $details.find('> summary');
       var mql = window.matchMedia('(min-width:48em)');
       mql.addListener(handleDetailsMQ);
       detailsToggle(mql.matches);