Security update for Core, with self-updated composer
[yaffs-website] / web / core / misc / displace.es6.js
diff --git a/web/core/misc/displace.es6.js b/web/core/misc/displace.es6.js
new file mode 100644 (file)
index 0000000..0424d80
--- /dev/null
@@ -0,0 +1,218 @@
+/**
+ * @file
+ * Manages elements that can offset the size of the viewport.
+ *
+ * Measures and reports viewport offset dimensions from elements like the
+ * toolbar that can potentially displace the positioning of other elements.
+ */
+
+/**
+ * @typedef {object} Drupal~displaceOffset
+ *
+ * @prop {number} top
+ * @prop {number} left
+ * @prop {number} right
+ * @prop {number} bottom
+ */
+
+/**
+ * Triggers when layout of the page changes.
+ *
+ * This is used to position fixed element on the page during page resize and
+ * Toolbar toggling.
+ *
+ * @event drupalViewportOffsetChange
+ */
+
+(function ($, Drupal, debounce) {
+  /**
+   * @name Drupal.displace.offsets
+   *
+   * @type {Drupal~displaceOffset}
+   */
+  let offsets = {
+    top: 0,
+    right: 0,
+    bottom: 0,
+    left: 0,
+  };
+
+  /**
+   * 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
+   *
+   * @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 displace(broadcast) {
+    offsets = Drupal.displace.offsets = calculateOffsets();
+    if (typeof broadcast === 'undefined' || broadcast) {
+      $(document).trigger('drupalViewportOffsetChange', offsets);
+    }
+    return offsets;
+  }
+
+  /**
+   * 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'),
+    };
+  }
+
+  /**
+   * Gets a specific edge's offset.
+   *
+   * Any element with the attribute data-offset-{edge} e.g. data-offset-top will
+   * be considered in the viewport offset calculations. If the attribute has a
+   * numeric value, that value will be used. If no value is provided, one will
+   * be calculated using the element's dimensions and placement.
+   *
+   * @function Drupal.displace.calculateOffset
+   *
+   * @param {string} edge
+   *   The name of the edge to calculate. Can be 'top', 'right',
+   *   'bottom' or 'left'.
+   *
+   * @return {number}
+   *   The viewport displacement distance for the requested edge.
+   */
+  function calculateOffset(edge) {
+    let edgeOffset = 0;
+    const displacingElements = document.querySelectorAll(`[data-offset-${edge}]`);
+    const n = displacingElements.length;
+    for (let i = 0; i < n; i++) {
+      const el = displacingElements[i];
+      // If the element is not visible, do consider its dimensions.
+      if (el.style.display === 'none') {
+        continue;
+      }
+      // If the offset data attribute contains a displacing value, use it.
+      let displacement = parseInt(el.getAttribute(`data-offset-${edge}`), 10);
+      // If the element's offset data attribute exits
+      // but is not a valid number then get the displacement
+      // dimensions directly from the element.
+      if (isNaN(displacement)) {
+        displacement = getRawOffset(el, edge);
+      }
+      // If the displacement value is larger than the current value for this
+      // edge, use the displacement value.
+      edgeOffset = Math.max(edgeOffset, displacement);
+    }
+
+    return edgeOffset;
+  }
+
+  /**
+   * Calculates displacement for element based on its dimensions and placement.
+   *
+   * @param {HTMLElement} el
+   *   The jQuery element whose dimensions and placement will be measured.
+   *
+   * @param {string} edge
+   *   The name of the edge of the viewport that the element is associated
+   *   with.
+   *
+   * @return {number}
+   *   The viewport displacement distance for the requested edge.
+   */
+  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;
+    }
+    return displacement;
+  }
+
+  /**
+   * Assign the displace function to a property of the Drupal global object.
+   *
+   * @ignore
+   */
+  Drupal.displace = displace;
+  $.extend(Drupal.displace, {
+
+    /**
+     * Expose offsets to other scripts to avoid having to recalculate offsets.
+     *
+     * @ignore
+     */
+    offsets,
+
+    /**
+     * Expose method to compute a single edge offsets.
+     *
+     * @ignore
+     */
+    calculateOffset,
+  });
+}(jQuery, Drupal, Drupal.debounce));