Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  overlayHelpers.mjs   Sprache: unbekannt

 
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// An autoselection smaller than these will be ignored entirely:
const MIN_DETECT_ABSOLUTE_HEIGHT = 10;
const MIN_DETECT_ABSOLUTE_WIDTH = 30;
// An autoselection smaller than these will not be preferred:
const MIN_DETECT_HEIGHT = 30;
const MIN_DETECT_WIDTH = 100;
// An autoselection bigger than either of these will be ignored:
let MAX_DETECT_HEIGHT = 700;
let MAX_DETECT_WIDTH = 1000;

const doNotAutoselectTags = {
  H1: true,
  H2: true,
  H3: true,
  H4: true,
  H5: true,
  H6: true,
};

/**
 * Gets the rect for an element if getBoundingClientRect exists
 * @param ele The element to get the rect from
 * @returns The bounding client rect of the element or null
 */
function getBoundingClientRect(ele) {
  if (!ele.getBoundingClientRect) {
    return null;
  }

  return ele.getBoundingClientRect();
}

export function setMaxDetectHeight(maxHeight) {
  MAX_DETECT_HEIGHT = maxHeight;
}

export function setMaxDetectWidth(maxWidth) {
  MAX_DETECT_WIDTH = maxWidth;
}

/**
 * This function will try to get an element from a given point in the doc.
 * This function is recursive because when sending a message to the
 * ScreenshotsHelper, the ScreenshotsHelper will call into this function.
 * This only occurs when the element at the given point is an iframe.
 *
 * If the element is an iframe, we will send a message to the ScreenshotsHelper
 * actor in the correct context to get the element at the given point.
 * The message will return the "getBestRectForElement" for the element at the
 * given point.
 *
 * If the element is not an iframe, then we will just return the element.
 *
 * @param {Number} x The x coordinate
 * @param {Number} y The y coordinate
 * @param {Document} doc The document
 * @returns {Object}
 *    ele: The element for a given point (x, y)
 *    rect: The rect for the given point if ele is an iframe
 *          otherwise null
 */
export async function getElementFromPoint(x, y, doc) {
  let ele = null;
  let rect = null;
  try {
    ele = doc.elementFromPoint(x, y);
    // if the element is an iframe, we need to send a message to that browsing context
    // to get the coordinates of the element in the iframe
    if (doc.defaultView.HTMLIFrameElement.isInstance(ele)) {
      let actor =
        ele.browsingContext.parentWindowContext.windowGlobalChild.getActor(
          "ScreenshotsHelper"
        );
      rect = await actor.sendQuery(
        "ScreenshotsHelper:GetElementRectFromPoint",
        {
          x: x + ele.ownerGlobal.mozInnerScreenX,
          y: y + ele.ownerGlobal.mozInnerScreenY,
          bcId: ele.browsingContext.id,
        }
      );

      if (rect) {
        rect = {
          left: rect.left - ele.ownerGlobal.mozInnerScreenX,
          right: rect.right - ele.ownerGlobal.mozInnerScreenX,
          top: rect.top - ele.ownerGlobal.mozInnerScreenY,
          bottom: rect.bottom - ele.ownerGlobal.mozInnerScreenY,
        };
      }
    } else if (ele.openOrClosedShadowRoot) {
      while (ele.openOrClosedShadowRoot) {
        let shadowEle = ele.openOrClosedShadowRoot.elementFromPoint(x, y);
        if (shadowEle) {
          ele = shadowEle;
        } else {
          break;
        }
      }
    }
  } catch (e) {
    console.error(e);
  }

  return { ele, rect };
}

/**
 * This function takes an element and finds a suitable rect to draw the hover box on
 * @param {Element} ele The element to find a suitale rect of
 * @param {Document} doc The current document
 * @returns A suitable rect or null
 */
export function getBestRectForElement(ele, doc) {
  let lastRect;
  let lastNode;
  let rect;
  let attemptExtend = false;
  let node = ele;
  while (node) {
    rect = getBoundingClientRect(node);
    if (!rect) {
      rect = lastRect;
      break;
    }
    if (rect.width < MIN_DETECT_WIDTH || rect.height < MIN_DETECT_HEIGHT) {
      // Avoid infinite loop for elements with zero or nearly zero height,
      // like non-clearfixed float parents with or without borders.
      break;
    }
    if (rect.width > MAX_DETECT_WIDTH || rect.height > MAX_DETECT_HEIGHT) {
      // Then the last rectangle is better
      rect = lastRect;
      attemptExtend = true;
      break;
    }
    if (rect.width >= MIN_DETECT_WIDTH && rect.height >= MIN_DETECT_HEIGHT) {
      if (!doNotAutoselectTags[node.tagName]) {
        break;
      }
    }
    lastRect = rect;
    lastNode = node;
    node = node.parentNode;
  }
  if (rect && node) {
    const evenBetter = evenBetterElement(node, doc);
    if (evenBetter) {
      node = lastNode = evenBetter;
      rect = getBoundingClientRect(evenBetter);
      attemptExtend = false;
    }
  }
  if (rect && attemptExtend) {
    let extendNode = lastNode.nextSibling;
    while (extendNode) {
      if (extendNode.nodeType === doc.ELEMENT_NODE) {
        break;
      }
      extendNode = extendNode.nextSibling;
      if (!extendNode) {
        const parentNode = lastNode.parentNode;
        for (let i = 0; i < parentNode.childNodes.length; i++) {
          if (parentNode.childNodes[i] === lastNode) {
            extendNode = parentNode.childNodes[i + 1];
          }
        }
      }
    }
    if (extendNode) {
      const extendRect = getBoundingClientRect(extendNode);
      let x = Math.min(rect.x, extendRect.x);
      let y = Math.min(rect.y, extendRect.y);
      let width = Math.max(rect.right, extendRect.right) - x;
      let height = Math.max(rect.bottom, extendRect.bottom) - y;
      const combinedRect = new DOMRect(x, y, width, height);
      if (
        combinedRect.width <= MAX_DETECT_WIDTH &&
        combinedRect.height <= MAX_DETECT_HEIGHT
      ) {
        rect = combinedRect;
      }
    }
  }

  if (
    rect &&
    (rect.width < MIN_DETECT_ABSOLUTE_WIDTH ||
      rect.height < MIN_DETECT_ABSOLUTE_HEIGHT)
  ) {
    rect = null;
  }

  return rect;
}

/**
 * This finds a better element by looking for elements with role article
 * @param {Element} node The currently hovered node
 * @param {Document} doc The current document
 * @returns A better node or null
 */
function evenBetterElement(node, doc) {
  let el = node.parentNode;
  const ELEMENT_NODE = doc.ELEMENT_NODE;
  while (el && el.nodeType === ELEMENT_NODE) {
    if (!el.getAttribute) {
      return null;
    }
    if (el.getAttribute("role") === "article") {
      const rect = getBoundingClientRect(el);
      if (!rect) {
        return null;
      }
      if (rect.width <= MAX_DETECT_WIDTH && rect.height <= MAX_DETECT_HEIGHT) {
        return el;
      }
      return null;
    }
    el = el.parentNode;
  }
  return null;
}

export class Region {
  #x1;
  #x2;
  #y1;
  #y2;
  #xOffset;
  #yOffset;
  #windowDimensions;

  constructor(windowDimensions) {
    this.resetDimensions();
    this.#windowDimensions = windowDimensions;
  }

  /**
   * Sets the dimensions if the given dimension is defined.
   * Otherwise will reset the dimensions
   * @param {Object} dims The new region dimensions
   *  {
   *    left: new left dimension value or undefined
   *    top: new top dimension value or undefined
   *    right: new right dimension value or undefined
   *    bottom: new bottom dimension value or undefined
   *   }
   */
  set dimensions(dims) {
    if (dims == null) {
      this.resetDimensions();
      return;
    }

    if (dims.left != null) {
      this.left = dims.left;
    }
    if (dims.top != null) {
      this.top = dims.top;
    }
    if (dims.right != null) {
      this.right = dims.right;
    }
    if (dims.bottom != null) {
      this.bottom = dims.bottom;
    }
  }

  get dimensions() {
    return {
      left: this.left,
      top: this.top,
      right: this.right,
      bottom: this.bottom,
      width: this.width,
      height: this.height,
    };
  }

  get isRegionValid() {
    return this.#x1 + this.#x2 + this.#y1 + this.#y2 > 0;
  }

  resetDimensions() {
    this.#x1 = 0;
    this.#x2 = 0;
    this.#y1 = 0;
    this.#y2 = 0;
    this.#xOffset = 0;
    this.#yOffset = 0;
  }

  /**
   * Sort the coordinates so x1 < x2 and y1 < y2
   */
  sortCoords() {
    if (this.#x1 > this.#x2) {
      [this.#x1, this.#x2] = [this.#x2, this.#x1];
    }
    if (this.#y1 > this.#y2) {
      [this.#y1, this.#y2] = [this.#y2, this.#y1];
    }
  }

  /**
   * The region should never appear outside the document so the region will
   * be shifted if the region is outside the page's width or height.
   */
  shift() {
    let didShift = false;
    let xDiff = this.right - this.#windowDimensions.scrollWidth;
    if (xDiff > 0) {
      this.left -= xDiff;
      this.right -= xDiff;

      didShift = true;
    }

    let yDiff = this.bottom - this.#windowDimensions.scrollHeight;
    if (yDiff > 0) {
      this.top -= yDiff;
      this.bottom -= yDiff;

      didShift = true;
    }

    return didShift;
  }

  /**
   * The diagonal distance of the region
   */
  get distance() {
    return Math.sqrt(Math.pow(this.width, 2) + Math.pow(this.height, 2));
  }

  get xOffset() {
    return this.#xOffset;
  }
  set xOffset(val) {
    this.#xOffset = val;
  }

  get yOffset() {
    return this.#yOffset;
  }
  set yOffset(val) {
    this.#yOffset = val;
  }

  get top() {
    return Math.min(this.#y1, this.#y2);
  }
  set top(val) {
    this.#y1 = Math.min(this.#windowDimensions.scrollHeight, Math.max(0, val));
  }

  get left() {
    return Math.min(this.#x1, this.#x2);
  }
  set left(val) {
    this.#x1 = Math.min(this.#windowDimensions.scrollWidth, Math.max(0, val));
  }

  get right() {
    return Math.max(this.#x1, this.#x2);
  }
  set right(val) {
    this.#x2 = Math.min(this.#windowDimensions.scrollWidth, Math.max(0, val));
  }

  get bottom() {
    return Math.max(this.#y1, this.#y2);
  }
  set bottom(val) {
    this.#y2 = Math.min(this.#windowDimensions.scrollHeight, Math.max(0, val));
  }

  get width() {
    return Math.abs(this.#x2 - this.#x1);
  }
  get height() {
    return Math.abs(this.#y2 - this.#y1);
  }

  get x1() {
    return this.#x1;
  }
  get x2() {
    return this.#x2;
  }
  get y1() {
    return this.#y1;
  }
  get y2() {
    return this.#y2;
  }
}

export class WindowDimensions {
  #clientHeight = null;
  #clientWidth = null;
  #scrollHeight = null;
  #scrollWidth = null;
  #scrollX = null;
  #scrollY = null;
  #scrollMinX = null;
  #scrollMinY = null;
  #scrollMaxX = null;
  #scrollMaxY = null;
  #devicePixelRatio = null;

  set dimensions(dimensions) {
    if (dimensions.clientHeight != null) {
      this.#clientHeight = dimensions.clientHeight;
    }
    if (dimensions.clientWidth != null) {
      this.#clientWidth = dimensions.clientWidth;
    }
    if (dimensions.scrollHeight != null) {
      this.#scrollHeight = dimensions.scrollHeight;
    }
    if (dimensions.scrollWidth != null) {
      this.#scrollWidth = dimensions.scrollWidth;
    }
    if (dimensions.scrollX != null) {
      this.#scrollX = dimensions.scrollX;
    }
    if (dimensions.scrollY != null) {
      this.#scrollY = dimensions.scrollY;
    }
    if (dimensions.scrollMinX != null) {
      this.#scrollMinX = dimensions.scrollMinX;
    }
    if (dimensions.scrollMinY != null) {
      this.#scrollMinY = dimensions.scrollMinY;
    }
    if (dimensions.scrollMaxX != null) {
      this.#scrollMaxX = dimensions.scrollMaxX;
    }
    if (dimensions.scrollMaxY != null) {
      this.#scrollMaxY = dimensions.scrollMaxY;
    }
    if (dimensions.devicePixelRatio != null) {
      this.#devicePixelRatio = dimensions.devicePixelRatio;
    }
  }

  get dimensions() {
    return {
      clientHeight: this.clientHeight,
      clientWidth: this.clientWidth,
      scrollHeight: this.scrollHeight,
      scrollWidth: this.scrollWidth,
      scrollX: this.scrollX,
      scrollY: this.scrollY,
      pageScrollX: this.pageScrollX,
      pageScrollY: this.pageScrollY,
      scrollMinX: this.scrollMinX,
      scrollMinY: this.scrollMinY,
      scrollMaxX: this.scrollMaxX,
      scrollMaxY: this.scrollMaxY,
      devicePixelRatio: this.devicePixelRatio,
    };
  }

  get clientWidth() {
    return this.#clientWidth;
  }

  get clientHeight() {
    return this.#clientHeight;
  }

  get scrollWidth() {
    return this.#scrollWidth;
  }

  get scrollHeight() {
    return this.#scrollHeight;
  }

  get scrollX() {
    return this.#scrollX - this.scrollMinX;
  }

  get pageScrollX() {
    return this.#scrollX;
  }

  get scrollY() {
    return this.#scrollY - this.scrollMinY;
  }

  get pageScrollY() {
    return this.#scrollY;
  }

  get scrollMinX() {
    return this.#scrollMinX;
  }

  get scrollMinY() {
    return this.#scrollMinY;
  }

  get scrollMaxX() {
    return this.#scrollMaxX;
  }

  get scrollMaxY() {
    return this.#scrollMaxY;
  }

  get devicePixelRatio() {
    return this.#devicePixelRatio;
  }

  isInViewport(rect) {
    // eslint-disable-next-line no-shadow
    let { left, top, right, bottom } = rect;

    if (
      left > this.scrollX + this.clientWidth ||
      right < this.scrollX ||
      top > this.scrollY + this.clientHeight ||
      bottom < this.scrollY
    ) {
      return false;
    }
    return true;
  }

  reset() {
    this.#clientHeight = 0;
    this.#clientWidth = 0;
    this.#scrollHeight = 0;
    this.#scrollWidth = 0;
    this.#scrollX = 0;
    this.#scrollY = 0;
    this.#scrollMinX = 0;
    this.#scrollMinY = 0;
    this.#scrollMaxX = 0;
    this.#scrollMaxY = 0;
  }
}

[ Dauer der Verarbeitung: 0.26 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge