/* 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/. */
/** * 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) { returnthis._customHostColors;
}
// Cache the custom host colors to avoid refetching from async storage. this._customHostColors =
(await asyncStorage.getItem("flexboxInspectorHostColors")) || {}; returnthis._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) { returnnull;
}
// 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"]
);
}
/** * 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"]
);
}
/** * Returns the custom overlay color for the current host or the default flexbox color. * * @return {String} overlay color.
*/
async getOverlayColor() { if (this._overlayColor) { returnthis._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; returnthis._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();
/** * 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;
}
// 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;
}
/** * 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;
}
/** * 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;
}
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) { returnnull;
}
/** * 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))
);
}
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.