Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/devtools/client/inspector/flexbox/   (Fast Lexical Analyzer Version 2.6©)  Datei vom 10.2.2025 mit Größe 17 kB image not shown  

Quelle  flexbox.js   Sprache: JAVA

 
/* 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/. */


"use strict";

const { throttle } = require("resource://devtools/shared/throttle.js");

const {
  clearFlexbox,
  updateFlexbox,
  updateFlexboxColor,
  updateFlexboxHighlighted,
} = require("resource://devtools/client/inspector/flexbox/actions/flexbox.js");
const flexboxReducer = require("resource://devtools/client/inspector/flexbox/reducers/flexbox.js");

loader.lazyRequireGetter(
  this,
  "parseURL",
  "resource://devtools/client/shared/source-utils.js",
  true
);
loader.lazyRequireGetter(
  this,
  "asyncStorage",
  "resource://devtools/shared/async-storage.js"
);

const FLEXBOX_COLOR = "#9400FF";

class FlexboxInspector {
  constructor(inspector, window) {
    this.document = window.document;
    this.inspector = inspector;
    this.selection = inspector.selection;
    this.store = inspector.store;

    this.store.injectReducer("flexbox", flexboxReducer);

    this.onHighlighterShown = this.onHighlighterShown.bind(this);
    this.onHighlighterHidden = this.onHighlighterHidden.bind(this);
    this.onNavigate = this.onNavigate.bind(this);
    this.onReflow = throttle(this.onReflow, 500, this);
    this.onSetFlexboxOverlayColor = this.onSetFlexboxOverlayColor.bind(this);
    this.onSidebarSelect = this.onSidebarSelect.bind(this);
    this.onUpdatePanel = this.onUpdatePanel.bind(this);

    this.init();
  }

  init() {
    if (!this.inspector) {
      return;
    }

    this.inspector.highlighters.on(
      "highlighter-shown",
      this.onHighlighterShown
    );
    this.inspector.highlighters.on(
      "highlighter-hidden",
      this.onHighlighterHidden
    );
    this.inspector.sidebar.on("select"this.onSidebarSelect);

    this.onSidebarSelect();
  }

  destroy() {
    this.selection.off("new-node-front"this.onUpdatePanel);
    this.inspector.off("new-root"this.onNavigate);
    this.inspector.off("reflow-in-selected-target"this.onReflow);
    this.inspector.highlighters.off(
      "highlighter-shown",
      this.onHighlighterShown
    );
    this.inspector.highlighters.off(
      "highlighter-hidden",
      this.onHighlighterHidden
    );
    this.inspector.sidebar.off("select"this.onSidebarSelect);

    this._customHostColors = null;
    this._overlayColor = null;
    this.document = null;
    this.inspector = null;
    this.selection = null;
    this.store = null;
  }

  getComponentProps() {
    return {
      onSetFlexboxOverlayColor: this.onSetFlexboxOverlayColor,
    };
  }

  /**
   * Returns an object containing the custom flexbox colors for different hosts.
   *
   * @return {Object} that maps a host name to a custom flexbox color for a given host.
   */

  async getCustomHostColors() {
    if (this._customHostColors) {
      return this._customHostColors;
    }

    // Cache the custom host colors to avoid refetching from async storage.
    this._customHostColors =
      (await asyncStorage.getItem("flexboxInspectorHostColors")) || {};
    return this._customHostColors;
  }

  /**
   * Returns the flex container properties for a given node. If the given node is a flex
   * item, it attempts to fetch the flex container of the parent node of the given node.
   *
   * @param  {NodeFront} nodeFront
   *         The NodeFront to fetch the flex container properties.
   * @param  {Boolean} onlyLookAtParents
   *         Whether or not to only consider the parent node of the given node.
   * @return {Object} consisting of the given node's flex container's properties.
   */

  async getFlexContainerProps(nodeFront, onlyLookAtParents = false) {
    const layoutFront = await nodeFront.walkerFront.getLayoutInspector();
    const flexboxFront = await layoutFront.getCurrentFlexbox(
      nodeFront,
      onlyLookAtParents
    );

    if (!flexboxFront) {
      return null;
    }

    // If the FlexboxFront doesn't yet have access to the NodeFront for its container,
    // then get it from the walker. This happens when the walker hasn't seen this
    // particular DOM Node in the tree yet or when we are connected to an older server.
    let containerNodeFront = flexboxFront.containerNodeFront;
    if (!containerNodeFront) {
      containerNodeFront = await flexboxFront.walkerFront.getNodeFromActor(
        flexboxFront.actorID,
        ["containerEl"]
      );
    }

    const flexItems = await this.getFlexItems(flexboxFront);

    // If the current selected node is a flex item, display its flex item sizing
    // properties.
    let flexItemShown = null;
    if (onlyLookAtParents) {
      flexItemShown = this.selection.nodeFront.actorID;
    } else {
      const selectedFlexItem = flexItems.find(
        item => item.nodeFront === this.selection.nodeFront
      );
      if (selectedFlexItem) {
        flexItemShown = selectedFlexItem.nodeFront.actorID;
      }
    }

    return {
      actorID: flexboxFront.actorID,
      flexItems,
      flexItemShown,
      isFlexItemContainer: onlyLookAtParents,
      nodeFront: containerNodeFront,
      properties: flexboxFront.properties,
    };
  }

  /**
   * Returns an array of flex items object for the given flex container front.
   *
   * @param  {FlexboxFront} flexboxFront
   *         A flex container FlexboxFront.
   * @return {Array} of objects containing the flex item front properties.
   */

  async getFlexItems(flexboxFront) {
    const flexItemFronts = await flexboxFront.getFlexItems();
    const flexItems = [];

    for (const flexItemFront of flexItemFronts) {
      // Fetch the NodeFront of the flex items.
      let itemNodeFront = flexItemFront.nodeFront;
      if (!itemNodeFront) {
        itemNodeFront = await flexItemFront.walkerFront.getNodeFromActor(
          flexItemFront.actorID,
          ["element"]
        );
      }

      flexItems.push({
        actorID: flexItemFront.actorID,
        computedStyle: flexItemFront.computedStyle,
        flexItemSizing: flexItemFront.flexItemSizing,
        nodeFront: itemNodeFront,
        properties: flexItemFront.properties,
      });
    }

    return flexItems;
  }

  /**
   * Returns the custom overlay color for the current host or the default flexbox color.
   *
   * @return {String} overlay color.
   */

  async getOverlayColor() {
    if (this._overlayColor) {
      return this._overlayColor;
    }

    // Cache the overlay color for the current host to avoid repeatably parsing the host
    // and fetching the custom color from async storage.
    const customColors = await this.getCustomHostColors();
    const currentUrl = this.inspector.currentTarget.url;
    // Get the hostname, if there is no hostname, fall back on protocol
    // ex: `data:` uri, and `about:` pages
    const hostname =
      parseURL(currentUrl).hostname || parseURL(currentUrl).protocol;
    this._overlayColor = customColors[hostname]
      ? customColors[hostname]
      : FLEXBOX_COLOR;
    return this._overlayColor;
  }

  /**
   * Returns true if the layout panel is visible, and false otherwise.
   */

  isPanelVisible() {
    return (
      this.inspector &&
      this.inspector.toolbox &&
      this.inspector.sidebar &&
      this.inspector.toolbox.currentToolId === "inspector" &&
      this.inspector.sidebar.getCurrentTabID() === "layoutview"
    );
  }

  /**
   * Handler for "highlighter-shown" events emitted by HighlightersOverlay.
   * If the event is dispatched on behalf of a flex highlighter, toggle the
   * corresponding flex container's highlighted state in the Redux store.
   *
   * @param {Object} data
   *        Object with data associated with the highlighter event.
   *        {NodeFront} data.nodeFront
   *        The NodeFront of the flex container element for which the flexbox
   *        highlighter is shown for.
   *        {String} data.type
   *        Highlighter type
   */

  onHighlighterShown(data) {
    if (data.type === this.inspector.highlighters.TYPES.FLEXBOX) {
      this.onHighlighterChange(true, data.nodeFront);
    }
  }

  /**
   * Handler for "highlighter-shown" events emitted by HighlightersOverlay.
   * If the event is dispatched on behalf of a flex highlighter, toggle the
   * corresponding flex container's highlighted state in the Redux store.
   *
   * @param {Object} data
   *        Object with data associated with the highlighter event.
   *        {NodeFront} data.nodeFront
   *        The NodeFront of the flex container element for which the flexbox
   *        highlighter was previously shown for.
   *        {String} data.type
   *        Highlighter type
   */

  onHighlighterHidden(data) {
    if (data.type === this.inspector.highlighters.TYPES.FLEXBOX) {
      this.onHighlighterChange(false, data.nodeFront);
    }
  }

  /**
   * Updates the flex container highlighted state in the Redux store if the provided
   * NodeFront is the current selected flex container.
   *
   * @param  {Boolean} highlighted
   *         Whether the change is to highlight or hide the overlay.
   * @param  {NodeFront} nodeFront
   *         The NodeFront of the flex container element for which the flexbox
   *         highlighter is shown for.
   */

  onHighlighterChange(highlighted, nodeFront) {
    const { flexbox } = this.store.getState();

    if (
      flexbox.flexContainer.nodeFront === nodeFront &&
      flexbox.highlighted !== highlighted
    ) {
      this.store.dispatch(updateFlexboxHighlighted(highlighted));
    }
  }

  /**
   * Handler for the "new-root" event fired by the inspector. Clears the cached overlay
   * color for the flexbox highlighter and updates the panel.
   */

  onNavigate() {
    this._overlayColor = null;
    this.onUpdatePanel();
  }

  /**
   * Handler for reflow events fired by the inspector when a node is selected. On reflows,
   * update the flexbox panel because the shape of the flexbox on the page may have
   * changed.
   */

  async onReflow() {
    if (
      !this.isPanelVisible() ||
      !this.store ||
      !this.selection.nodeFront ||
      this._isUpdating
    ) {
      return;
    }

    try {
      const flexContainer = await this.getFlexContainerProps(
        this.selection.nodeFront
      );

      // Clear the flexbox panel if there is no flex container for the current node
      // selection.
      if (!flexContainer) {
        this.store.dispatch(clearFlexbox());
        return;
      }

      const { flexbox } = this.store.getState();

      // Compare the new flexbox state of the current selected nodeFront with the old
      // flexbox state to determine if we need to update.
      if (hasFlexContainerChanged(flexbox.flexContainer, flexContainer)) {
        this.update(flexContainer);
        return;
      }

      let flexItemContainer = null;
      // If the current selected node is also the flex container node, check if it is
      // a flex item of a parent flex container.
      if (flexContainer.nodeFront === this.selection.nodeFront) {
        flexItemContainer = await this.getFlexContainerProps(
          this.selection.nodeFront,
          true
        );
      }

      // Compare the new and old state of the parent flex container properties.
      if (
        hasFlexContainerChanged(flexbox.flexItemContainer, flexItemContainer)
      ) {
        this.update(flexContainer, flexItemContainer);
      }
    } catch (e) {
      // This call might fail if called asynchrously after the toolbox is finished
      // closing.
    }
  }

  /**
   * Handler for a change in the flexbox overlay color picker for a flex container.
   *
   * @param  {String} color
   *         A hex string representing the color to use.
   */

  async onSetFlexboxOverlayColor(color) {
    this.store.dispatch(updateFlexboxColor(color));

    const { flexbox } = this.store.getState();

    if (flexbox.highlighted) {
      this.inspector.highlighters.showFlexboxHighlighter(
        flexbox.flexContainer.nodeFront
      );
    }

    this._overlayColor = color;

    const currentUrl = this.inspector.currentTarget.url;
    // Get the hostname, if there is no hostname, fall back on protocol
    // ex: `data:` uri, and `about:` pages
    const hostname =
      parseURL(currentUrl).hostname || parseURL(currentUrl).protocol;
    const customColors = await this.getCustomHostColors();
    customColors[hostname] = color;
    this._customHostColors = customColors;
    await asyncStorage.setItem("flexboxInspectorHostColors", customColors);
  }

  /**
   * Handler for the inspector sidebar "select" event. Updates the flexbox panel if it
   * is visible.
   */

  onSidebarSelect() {
    if (!this.isPanelVisible()) {
      this.inspector.off("reflow-in-selected-target"this.onReflow);
      this.inspector.off("new-root"this.onNavigate);
      this.selection.off("new-node-front"this.onUpdatePanel);
      return;
    }

    this.inspector.on("reflow-in-selected-target"this.onReflow);
    this.inspector.on("new-root"this.onNavigate);
    this.selection.on("new-node-front"this.onUpdatePanel);

    this.update();
  }

  /**
   * Handler for "new-root" event fired by the inspector and "new-node-front" event fired
   * by the inspector selection. Updates the flexbox panel if it is visible.
   *
   * @param  {Object}
   *         This callback is sometimes executed on "new-node-front" events which means
   *         that a first param is passed here (the nodeFront), which we don't care about.
   * @param  {String} reason
   *         On "new-node-front" events, a reason is passed here, and we need it to detect
   *         if this update was caused by a node selection from the markup-view.
   */

  onUpdatePanel(_, reason) {
    if (!this.isPanelVisible()) {
      return;
    }

    this.update(nullnull, reason === "treepanel");
  }

  /**
   * Updates the flexbox panel by dispatching the new flexbox data. This is called when
   * the layout view becomes visible or a new node is selected and needs to be update
   * with new flexbox data.
   *
   * @param  {Object|null} flexContainer
   *         An object consisting of the current flex container's flex items and
   *         properties.
   * @param  {Object|null} flexItemContainer
   *         An object consisting of the parent flex container's flex items and
   *         properties.
   * @param  {Boolean} initiatedByMarkupViewSelection
   *         True if the update was due to a node selection in the markup-view.
   */

  async update(
    flexContainer,
    flexItemContainer,
    initiatedByMarkupViewSelection
  ) {
    this._isUpdating = true;

    // Stop refreshing if the inspector or store is already destroyed or no node is
    // selected.
    if (!this.inspector || !this.store || !this.selection.nodeFront) {
      this._isUpdating = false;
      return;
    }

    try {
      // Fetch the current flexbox if no flexbox front was passed into this update.
      if (!flexContainer) {
        flexContainer = await this.getFlexContainerProps(
          this.selection.nodeFront
        );
      }

      // Clear the flexbox panel if there is no flex container for the current node
      // selection.
      if (!flexContainer) {
        this.store.dispatch(clearFlexbox());
        this._isUpdating = false;
        return;
      }

      if (
        !flexItemContainer &&
        flexContainer.nodeFront === this.selection.nodeFront
      ) {
        flexItemContainer = await this.getFlexContainerProps(
          this.selection.nodeFront,
          true
        );
      }

      const highlighted =
        flexContainer.nodeFront ===
        this.inspector.highlighters.getNodeForActiveHighlighter(
          this.inspector.highlighters.TYPES.FLEXBOX
        );
      const color = await this.getOverlayColor();

      this.store.dispatch(
        updateFlexbox({
          color,
          flexContainer,
          flexItemContainer,
          highlighted,
          initiatedByMarkupViewSelection,
        })
      );
    } catch (e) {
      // This call might fail if called asynchrously after the toolbox is finished
      // closing.
    }

    this._isUpdating = false;
  }
}

/**
 * For a given flex container object, returns the flex container properties that can be
 * used to check if 2 flex container objects are the same.
 *
 * @param  {Object|null} flexContainer
 *         Object consisting of the flex container's properties.
 * @return {Object|null} consisting of the comparable flex container's properties.
 */

function getComparableFlexContainerProperties(flexContainer) {
  if (!flexContainer) {
    return null;
  }

  return {
    flexItems: getComparableFlexItemsProperties(flexContainer.flexItems),
    nodeFront: flexContainer.nodeFront.actorID,
    properties: flexContainer.properties,
  };
}

/**
 * Given an array of flex item objects, returns the relevant flex item properties that can
 * be compared to check if any changes has occurred.
 *
 * @param  {Array} flexItems
 *         Array of objects containing the flex item properties.
 * @return {Array} of objects consisting of the comparable flex item's properties.
 */

function getComparableFlexItemsProperties(flexItems) {
  return flexItems.map(item => {
    return {
      computedStyle: item.computedStyle,
      flexItemSizing: item.flexItemSizing,
      nodeFront: item.nodeFront.actorID,
      properties: item.properties,
    };
  });
}

/**
 * Compares the old and new flex container properties
 *
 * @param  {Object} oldFlexContainer
 *         Object consisting of the old flex container's properties.
 * @param  {Object} newFlexContainer
 *         Object consisting of the new flex container's properties.
 * @return {Boolean} true if the flex container properties are the same, false otherwise.
 */

function hasFlexContainerChanged(oldFlexContainer, newFlexContainer) {
  return (
    JSON.stringify(getComparableFlexContainerProperties(oldFlexContainer)) !==
    JSON.stringify(getComparableFlexContainerProperties(newFlexContainer))
  );
}

module.exports = FlexboxInspector;

97%


¤ Dauer der Verarbeitung: 0.6 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.