Eine aufbereitete Darstellung der Quelle

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

Benutzer

Quelle  box-model.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 boxModelReducer = require("resource://devtools/client/inspector/boxmodel/reducers/box-model.js");
const {
  updateGeometryEditorEnabled,
  updateLayout,
  updateOffsetParent,
} = require("resource://devtools/client/inspector/boxmodel/actions/box-model.js");

loader.lazyRequireGetter(
  this,
  "EditingSession",
  "resource://devtools/client/inspector/boxmodel/utils/editing-session.js"
);
loader.lazyRequireGetter(
  this,
  "InplaceEditor",
  "resource://devtools/client/shared/inplace-editor.js",
  true
);
loader.lazyRequireGetter(
  this,
  "RulePreviewTooltip",
  "resource://devtools/client/shared/widgets/tooltip/RulePreviewTooltip.js"
);

const NUMERIC = /^-?[\d\.]+$/;

/**
 * A singleton instance of the box model controllers.
 */

class BoxModel {
  /**
   * @param  {Inspector} inspector
   *         An instance of the Inspector currently loaded in the toolbox.
   * @param  {Window} window
   *         The document window of the toolbox.
   */

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

    this.store.injectReducer("boxModel", boxModelReducer);

    this.updateBoxModel = this.updateBoxModel.bind(this);

    this.onHideGeometryEditor = this.onHideGeometryEditor.bind(this);
    this.onMarkupViewLeave = this.onMarkupViewLeave.bind(this);
    this.onMarkupViewNodeHover = this.onMarkupViewNodeHover.bind(this);
    this.onNewSelection = this.onNewSelection.bind(this);
    this.onShowBoxModelEditor = this.onShowBoxModelEditor.bind(this);
    this.onShowRulePreviewTooltip = this.onShowRulePreviewTooltip.bind(this);
    this.onSidebarSelect = this.onSidebarSelect.bind(this);
    this.onToggleGeometryEditor = this.onToggleGeometryEditor.bind(this);

    this.inspector.selection.on("new-node-front", this.onNewSelection);
    this.inspector.sidebar.on("select", this.onSidebarSelect);

    const { promise, resolve } = Promise.withResolvers();
    this.initialized = promise;
    this.#initializedPromiseResolve = resolve;
  }

  #initializedPromiseResolve;

  /**
   * Destruction function called when the inspector is destroyed. Removes event listeners
   * and cleans up references.
   */

  destroy() {
    this.inspector.selection.off("new-node-front", this.onNewSelection);
    this.inspector.sidebar.off("select", this.onSidebarSelect);

    if (this._geometryEditorEventsAbortController) {
      this._geometryEditorEventsAbortController.abort();
      this._geometryEditorEventsAbortController = null;
    }

    if (this._tooltip) {
      this._tooltip.destroy();
    }

    this.untrackReflows();

    this.elementRules = null;
    this._highlighters = null;
    this._tooltip = null;
    this.document = null;
    this.inspector = null;
    this.initialized = null;
  }

  get highlighters() {
    if (!this._highlighters) {
      // highlighters is a lazy getter in the inspector.
      this._highlighters = this.inspector.highlighters;
    }

    return this._highlighters;
  }

  get rulePreviewTooltip() {
    if (!this._tooltip) {
      this._tooltip = new RulePreviewTooltip(this.inspector.toolbox.doc);
    }

    return this._tooltip;
  }

  /**
   * Returns an object containing the box model's handler functions used in the box
   * model's React component props.
   */

  getComponentProps() {
    return {
      onShowBoxModelEditor: this.onShowBoxModelEditor,
      onShowRulePreviewTooltip: this.onShowRulePreviewTooltip,
      onToggleGeometryEditor: this.onToggleGeometryEditor,
    };
  }

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

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

  /**
   * Returns true if the layout panel is visible and the current element is valid to
   * be displayed in the view.
   */

  isPanelVisibleAndNodeValid() {
    return (
      this.isPanelVisible() &&
      this.inspector.selection.isConnected() &&
      this.inspector.selection.isElementNode()
    );
  }

  /**
   * Starts listening to reflows in the current tab.
   */

  trackReflows() {
    this.inspector.on("reflow-in-selected-target", this.updateBoxModel);
  }

  /**
   * Stops listening to reflows in the current tab.
   */

  untrackReflows() {
    this.inspector.off("reflow-in-selected-target", this.updateBoxModel);
  }

  /**
   * Updates the box model panel by dispatching the new layout data.
   *
   * @param  {string} reason
   *         Optional string describing the reason why the boxmodel is updated.
   */

  updateBoxModel(reason) {
    this._updateReasons = this._updateReasons || [];
    if (reason) {
      this._updateReasons.push(reason);
    }

    const lastRequest = (async () => {
      if (
        !this.inspector ||
        !this.isPanelVisible() ||
        !this.inspector.selection.isConnected() ||
        !this.inspector.selection.isElementNode()
      ) {
        return null;
      }

      const { nodeFront } = this.inspector.selection;
      const inspectorFront = this.getCurrentInspectorFront();
      const { pageStyle } = inspectorFront;

      let layout = await pageStyle.getLayout(nodeFront, {
        autoMargins: true,
      });

      const styleEntries = await pageStyle.getApplied(nodeFront, {
        // We don't need styles applied to pseudo elements of the current node.
        skipPseudo: true,
      });
      this.elementRules = styleEntries.map(e => e.rule);

      // Update the layout properties with whether or not the element's position is
      // editable with the geometry editor.
      const isPositionEditable = await pageStyle.isPositionEditable(nodeFront);

      layout = Object.assign({}, layout, {
        isPositionEditable,
      });

      // Update the redux store with the latest offset parent DOM node
      const offsetParent =
        await inspectorFront.walker.getOffsetParent(nodeFront);
      this.store.dispatch(updateOffsetParent(offsetParent));

      // Update the redux store with the latest layout properties and update the box
      // model view.
      this.store.dispatch(updateLayout(layout));

      // If a subsequent request has been made, wait for that one instead.
      if (this._lastRequest != lastRequest) {
        return this._lastRequest;
      }

      this.inspector.emit("boxmodel-view-updated", this._updateReasons);
      this.#initializedPromiseResolve();

      this._lastRequest = null;
      this._updateReasons = [];

      return null;
    })().catch(error => {
      // If we failed because we were being destroyed while waiting for a request, ignore.
      if (this.document) {
        console.error(error);
      }
    });

    this._lastRequest = lastRequest;
  }

  /**
   * Hides the geometry editor and updates the box moodel store with the new
   * geometry editor enabled state.
   */

  onHideGeometryEditor() {
    this.highlighters.hideGeometryEditor();
    this.store.dispatch(updateGeometryEditorEnabled(false));

    if (this._geometryEditorEventsAbortController) {
      this._geometryEditorEventsAbortController.abort();
      this._geometryEditorEventsAbortController = null;
    }
  }

  /**
   * Handler function that re-shows the geometry editor for an element that already
   * had the geometry editor enabled. This handler function is called on a "leave" event
   * on the markup view.
   */

  onMarkupViewLeave() {
    const state = this.store.getState();
    const enabled = state.boxModel.geometryEditorEnabled;

    if (!enabled) {
      return;
    }

    const nodeFront = this.inspector.selection.nodeFront;
    this.highlighters.showGeometryEditor(nodeFront);
  }

  /**
   * Handler function that temporarily hides the geomery editor when the
   * markup view has a "node-hover" event.
   */

  onMarkupViewNodeHover() {
    this.highlighters.hideGeometryEditor();
  }

  /**
   * Selection 'new-node-front' event handler.
   */

  onNewSelection() {
    if (!this.isPanelVisibleAndNodeValid()) {
      return;
    }

    if (
      this.inspector.selection.isConnected() &&
      this.inspector.selection.isElementNode()
    ) {
      this.trackReflows();
    }

    this.updateBoxModel("new-selection");
  }

  /**
   * Shows the RulePreviewTooltip when a box model editable value is hovered on the
   * box model panel.
   *
   * @param  {Element} target
   *         The target element.
   * @param  {string} property
   *         The name of the property.
   */

  async onShowRulePreviewTooltip(target, property) {
    const { highlightProperty } = this.inspector.getPanel("ruleview").view;
    const isHighlighted = await highlightProperty(property);

    // Only show the tooltip if the property is not highlighted.
    // TODO: In the future, use an associated ruleId for toggling the tooltip instead of
    // the Boolean returned from highlightProperty.
    if (!isHighlighted) {
      this.rulePreviewTooltip.show(target);
    }
  }

  getPropertyValue(property) {
    const state = this.store.getState();
    const { layout } = state.boxModel;
    return layout[property];
  }

  getFloatValue(valueString) {
    return valueString ? parseFloat(valueString) : 0;
  }

  getNonContentHeight() {
    return (
      this.getFloatValue(this.getPropertyValue("border-bottom-width")) +
      this.getFloatValue(this.getPropertyValue("border-top-width")) +
      this.getFloatValue(this.getPropertyValue("padding-bottom")) +
      this.getFloatValue(this.getPropertyValue("padding-top"))
    );
  }

  getNonContentWidth() {
    return (
      this.getFloatValue(this.getPropertyValue("border-left-width")) +
      this.getFloatValue(this.getPropertyValue("border-right-width")) +
      this.getFloatValue(this.getPropertyValue("padding-left")) +
      this.getFloatValue(this.getPropertyValue("padding-right"))
    );
  }

  /**
   * Shows the inplace editor when a box model editable value is clicked on the
   * box model panel.
   *
   * @param  {DOMNode} element
   *         The element that was clicked.
   * @param  {Event} event
   *         The event object.
   * @param  {string} property
   *         The name of the property.
   */

  onShowBoxModelEditor(element, event, property) {
    const session = new EditingSession({
      inspector: this.inspector,
      doc: this.document,
      elementRules: this.elementRules,
    });
    let initialValue = session.getProperty(property);
    const isBorderBox = this.getPropertyValue("box-sizing") === "border-box";
    if (isBorderBox && (property === "width" || property === "height")) {
      initialValue = this.getFloatValue(this.getPropertyValue(property));
      if (property === "height") {
        initialValue -= this.getNonContentHeight();
      } else if (property === "width") {
        initialValue -= this.getNonContentWidth();
      }
    }

    const editor = new InplaceEditor(
      {
        element,
        initial: initialValue,
        contentType: InplaceEditor.CONTENT_TYPES.CSS_VALUE,
        property: {
          name: property,
        },
        start: self => {
          self.elt.parentNode.classList.add("boxmodel-editing");
        },
        change: value => {
          if (isBorderBox && property === "height") {
            const float = this.getFloatValue(value);
            if (!isNaN(float)) {
              value = float + this.getNonContentHeight();
            }
          }
          if (isBorderBox && property === "width") {
            const float = this.getFloatValue(value);
            if (!isNaN(float)) {
              value = float + this.getNonContentWidth();
            }
          }
          if (NUMERIC.test(value)) {
            value += "px";
          }

          const properties = [{ name: property, value }];

          if (property.substring(0, 7) == "border-") {
            const bprop = property.substring(0, property.length - 5) + "style";
            const style = session.getProperty(bprop);
            if (!style || style == "none" || style == "hidden") {
              properties.push({ name: bprop, value: "solid" });
            }
          }

          if (property.substring(0, 9) == "position-") {
            properties[0].name = property.substring(9);
          }

          session.setProperties(properties).catch(console.error);
        },
        done: (value, commit) => {
          editor.elt.parentNode.classList.remove("boxmodel-editing");
          if (!commit) {
            session.revert().then(() => {
              session.destroy();
            }, console.error);
            return;
          }

          this.updateBoxModel("editable-value-change");
        },
        cssProperties: this.inspector.cssProperties,
      },
      event
    );
  }

  /**
   * Handler for the inspector sidebar select event. Starts tracking reflows if the
   * layout panel is visible. Otherwise, stop tracking reflows. Finally, refresh the box
   * model view if it is visible.
   */

  onSidebarSelect() {
    if (!this.isPanelVisible()) {
      this.untrackReflows();
      return;
    }

    if (
      this.inspector.selection.isConnected() &&
      this.inspector.selection.isElementNode()
    ) {
      this.trackReflows();
    }

    this.updateBoxModel();
  }

  /**
   * Toggles on/off the geometry editor for the current element when the geometry editor
   * toggle button is clicked.
   */

  onToggleGeometryEditor() {
    const { markup, selection, toolbox } = this.inspector;
    const nodeFront = this.inspector.selection.nodeFront;
    const state = this.store.getState();
    const enabled = !state.boxModel.geometryEditorEnabled;

    this.highlighters.toggleGeometryHighlighter(nodeFront);
    this.store.dispatch(updateGeometryEditorEnabled(enabled));

    if (enabled) {
      this._geometryEditorEventsAbortController = new AbortController();
      const eventListenersConfig = {
        signal: this._geometryEditorEventsAbortController.signal,
      };
      // Hide completely the geometry editor if:
      // - the picker is clicked
      // - or if a new node is selected
      toolbox.nodePicker.on(
        "picker-started",
        this.onHideGeometryEditor,
        eventListenersConfig
      );
      selection.on(
        "new-node-front",
        this.onHideGeometryEditor,
        eventListenersConfig
      );
      // Temporarily hide the geometry editor
      markup.on("leave", this.onMarkupViewLeave, eventListenersConfig);
      markup.on("node-hover", this.onMarkupViewNodeHover, eventListenersConfig);
    } else if (this._geometryEditorEventsAbortController) {
      this._geometryEditorEventsAbortController.abort();
      this._geometryEditorEventsAbortController = null;
    }
  }

  getCurrentInspectorFront() {
    return this.inspector.selection.nodeFront.inspectorFront;
  }
}

module.exports = BoxModel;

Messung V0.5 in Prozent
C=93 H=100 G=96

¤ Dauer der Verarbeitung: 0.12 Sekunden  (vorverarbeitet am  2026-09-30) ¤

*© Formatika GbR, Deutschland






Versionsinformation zu Columbo

Bemerkung:

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Anfrage:

Dauer der Verarbeitung:

Sekunden

sprechenden Kalenders






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

letze Version des Elbe Quellennavigators


Jenseits des Üblichen ....
    

Besucher

Besucher

Statistik
#Sources=1126438
#Domains=1897691