import * as Common from "../../core/common/common.js";
import * as i18n from "../../core/i18n/i18n.js";
import * as Platform from "../../core/platform/platform.js";
import * as SDK from "../../core/sdk/sdk.js";
import * as InlineEditor from "../../ui/legacy/components/inline_editor/inline_editor.js";
import * as Components from "../../ui/legacy/components/utils/utils.js";
import * as UI from "../../ui/legacy/legacy.js";
import * as ElementsComponents from "./components/components.js";
import computedStyleSidebarPaneStyles from "./computedStyleSidebarPane.css.js";
import computedStyleWidgetTreeStyles from "./computedStyleWidgetTree.css.js";
import { ComputedStyleModel, Events } from "./ComputedStyleModel.js";
import { ImagePreviewPopover } from "./ImagePreviewPopover.js";
import { PlatformFontsWidget } from "./PlatformFontsWidget.js";
import { categorizePropertyName, DefaultCategoryOrder } from "./PropertyNameCategories.js";
import { StylePropertiesSection } from "./StylePropertiesSection.js";
import { IdleCallbackManager, StylesSidebarPane, StylesSidebarPropertyRenderer } from "./StylesSidebarPane.js";
const UIStrings = {
  filter: "Filter",
  filterComputedStyles: "Filter Computed Styles",
  showAll: "Show all",
  group: "Group",
  noMatchingProperty: "No matching property",
  navigateToSelectorSource: "Navigate to selector source",
  navigateToStyle: "Navigate to style"
};
const str_ = i18n.i18n.registerUIStrings("panels/elements/ComputedStyleWidget.ts", UIStrings);
const i18nString = i18n.i18n.getLocalizedString.bind(void 0, str_);
const createPropertyElement = (node, propertyName, propertyValue) => {
  const propertyElement = new ElementsComponents.ComputedStyleProperty.ComputedStyleProperty();
  const renderer = new StylesSidebarPropertyRenderer(null, node, propertyName, propertyValue);
  renderer.setColorHandler(processColor.bind(null, false));
  const propertyNameElement = renderer.renderName();
  propertyNameElement.slot = "property-name";
  propertyElement.appendChild(propertyNameElement);
  const propertyValueElement = renderer.renderValue();
  propertyValueElement.slot = "property-value";
  propertyElement.appendChild(propertyValueElement);
  return propertyElement;
};
const createTraceElement = (node, property, isPropertyOverloaded, matchedStyles, linkifier) => {
  const trace = new ElementsComponents.ComputedStyleTrace.ComputedStyleTrace();
  const renderer = new StylesSidebarPropertyRenderer(null, node, property.name, property.value);
  renderer.setColorHandler(processColor.bind(null, true));
  const valueElement = renderer.renderValue();
  valueElement.slot = "trace-value";
  trace.appendChild(valueElement);
  const rule = property.ownerStyle.parentRule;
  if (rule) {
    const linkSpan = document.createElement("span");
    linkSpan.appendChild(StylePropertiesSection.createRuleOriginNode(matchedStyles, linkifier, rule));
    linkSpan.slot = "trace-link";
    trace.appendChild(linkSpan);
  }
  trace.data = {
    selector: rule ? rule.selectorText() : "element.style",
    active: !isPropertyOverloaded,
    onNavigateToSource: navigateToSource.bind(null, property)
  };
  return trace;
};
const processColor = (autoDetectFormat, text) => {
  const swatch = new InlineEditor.ColorSwatch.ColorSwatch();
  swatch.renderColor(text, autoDetectFormat || Common.Color.Format.RGB);
  const valueElement = document.createElement("span");
  valueElement.textContent = text;
  swatch.append(valueElement);
  swatch.addEventListener(InlineEditor.ColorSwatch.FormatChangedEvent.eventName, (event) => {
    const { data } = event;
    valueElement.textContent = data.text;
  });
  return swatch;
};
const navigateToSource = (cssProperty, event) => {
  void Common.Revealer.reveal(cssProperty);
  event.consume(true);
};
const propertySorter = (propA, propB) => {
  if (propA.startsWith("--") !== propB.startsWith("--")) {
    return propA.startsWith("--") ? 1 : -1;
  }
  if (propA.startsWith("-webkit") !== propB.startsWith("-webkit")) {
    return propA.startsWith("-webkit") ? 1 : -1;
  }
  const canonicalA = SDK.CSSMetadata.cssMetadata().canonicalPropertyName(propA);
  const canonicalB = SDK.CSSMetadata.cssMetadata().canonicalPropertyName(propB);
  return Platform.StringUtilities.compare(canonicalA, canonicalB);
};
export class ComputedStyleWidget extends UI.ThrottledWidget.ThrottledWidget {
  computedStyleModel;
  showInheritedComputedStylePropertiesSetting;
  groupComputedStylesSetting;
  input;
  filterRegex;
  noMatchesElement;
  propertiesOutline;
  propertyByTreeElement;
  categoryByTreeElement;
  expandedProperties;
  expandedGroups;
  linkifier;
  imagePreviewPopover;
  idleCallbackManager;
  constructor() {
    super(true);
    this.computedStyleModel = new ComputedStyleModel();
    this.computedStyleModel.addEventListener(Events.ComputedStyleChanged, this.update, this);
    this.showInheritedComputedStylePropertiesSetting = Common.Settings.Settings.instance().createSetting("showInheritedComputedStyleProperties", false);
    this.showInheritedComputedStylePropertiesSetting.addChangeListener(this.update.bind(this));
    this.groupComputedStylesSetting = Common.Settings.Settings.instance().createSetting("groupComputedStyles", false);
    this.groupComputedStylesSetting.addChangeListener(() => {
      this.update();
    });
    const hbox = this.contentElement.createChild("div", "hbox styles-sidebar-pane-toolbar");
    const filterContainerElement = hbox.createChild("div", "styles-sidebar-pane-filter-box");
    const filterInput = StylesSidebarPane.createPropertyFilterElement(i18nString(UIStrings.filter), hbox, this.filterComputedStyles.bind(this));
    UI.ARIAUtils.setAccessibleName(filterInput, i18nString(UIStrings.filterComputedStyles));
    filterContainerElement.appendChild(filterInput);
    this.input = filterInput;
    this.filterRegex = null;
    const toolbar = new UI.Toolbar.Toolbar("styles-pane-toolbar", hbox);
    toolbar.appendToolbarItem(new UI.Toolbar.ToolbarSettingCheckbox(this.showInheritedComputedStylePropertiesSetting, void 0, i18nString(UIStrings.showAll)));
    toolbar.appendToolbarItem(new UI.Toolbar.ToolbarSettingCheckbox(this.groupComputedStylesSetting, void 0, i18nString(UIStrings.group)));
    this.noMatchesElement = this.contentElement.createChild("div", "gray-info-message");
    this.noMatchesElement.textContent = i18nString(UIStrings.noMatchingProperty);
    this.propertiesOutline = new UI.TreeOutline.TreeOutlineInShadow();
    this.propertiesOutline.hideOverflow();
    this.propertiesOutline.setShowSelectionOnKeyboardFocus(true);
    this.propertiesOutline.setFocusable(true);
    this.propertiesOutline.element.classList.add("monospace", "computed-properties");
    this.propertiesOutline.addEventListener(UI.TreeOutline.Events.ElementExpanded, this.onTreeElementToggled, this);
    this.propertiesOutline.addEventListener(UI.TreeOutline.Events.ElementCollapsed, this.onTreeElementToggled, this);
    this.contentElement.appendChild(this.propertiesOutline.element);
    this.propertyByTreeElement = /* @__PURE__ */ new WeakMap();
    this.categoryByTreeElement = /* @__PURE__ */ new WeakMap();
    this.expandedProperties = /* @__PURE__ */ new Set();
    this.expandedGroups = new Set(DefaultCategoryOrder);
    this.linkifier = new Components.Linkifier.Linkifier(_maxLinkLength);
    this.imagePreviewPopover = new ImagePreviewPopover(this.contentElement, (event) => {
      const link = event.composedPath()[0];
      if (link instanceof Element) {
        return link;
      }
      return null;
    }, () => this.computedStyleModel.node());
    const fontsWidget = new PlatformFontsWidget(this.computedStyleModel);
    fontsWidget.show(this.contentElement);
    this.idleCallbackManager = new IdleCallbackManager();
  }
  onResize() {
    const isNarrow = this.contentElement.offsetWidth < 260;
    this.propertiesOutline.contentElement.classList.toggle("computed-narrow", isNarrow);
  }
  showInheritedComputedStyleChanged() {
    this.update();
  }
  update() {
    if (this.idleCallbackManager) {
      this.idleCallbackManager.discard();
    }
    this.idleCallbackManager = new IdleCallbackManager();
    super.update();
  }
  wasShown() {
    super.wasShown();
    this.registerCSSFiles([computedStyleSidebarPaneStyles]);
    this.propertiesOutline.registerCSSFiles([computedStyleWidgetTreeStyles]);
  }
  async doUpdate() {
    const [nodeStyles, matchedStyles] = await Promise.all([this.computedStyleModel.fetchComputedStyle(), this.fetchMatchedCascade()]);
    const shouldGroupComputedStyles = this.groupComputedStylesSetting.get();
    this.propertiesOutline.contentElement.classList.toggle("grouped-list", shouldGroupComputedStyles);
    this.propertiesOutline.contentElement.classList.toggle("alphabetical-list", !shouldGroupComputedStyles);
    if (shouldGroupComputedStyles) {
      await this.rebuildGroupedList(nodeStyles, matchedStyles);
    } else {
      await this.rebuildAlphabeticalList(nodeStyles, matchedStyles);
    }
  }
  async fetchMatchedCascade() {
    const node = this.computedStyleModel.node();
    if (!node || !this.computedStyleModel.cssModel()) {
      return null;
    }
    const cssModel = this.computedStyleModel.cssModel();
    if (!cssModel) {
      return null;
    }
    return cssModel.cachedMatchedCascadeForNode(node).then(validateStyles.bind(this));
    function validateStyles(matchedStyles) {
      return matchedStyles && matchedStyles.node() === this.computedStyleModel.node() ? matchedStyles : null;
    }
  }
  async rebuildAlphabeticalList(nodeStyle, matchedStyles) {
    const hadFocus = this.propertiesOutline.element.hasFocus();
    this.imagePreviewPopover.hide();
    this.propertiesOutline.removeChildren();
    this.linkifier.reset();
    const cssModel = this.computedStyleModel.cssModel();
    if (!nodeStyle || !matchedStyles || !cssModel) {
      this.noMatchesElement.classList.remove("hidden");
      return;
    }
    const uniqueProperties = [...nodeStyle.computedStyle.keys()];
    uniqueProperties.sort(propertySorter);
    const node = nodeStyle.node;
    const propertyTraces = this.computePropertyTraces(matchedStyles);
    const nonInheritedProperties = this.computeNonInheritedProperties(matchedStyles);
    const showInherited = this.showInheritedComputedStylePropertiesSetting.get();
    const computedStyleQueue = [];
    for (const propertyName of uniqueProperties) {
      const propertyValue = nodeStyle.computedStyle.get(propertyName) || "";
      const canonicalName = SDK.CSSMetadata.cssMetadata().canonicalPropertyName(propertyName);
      const isInherited = !nonInheritedProperties.has(canonicalName);
      if (!showInherited && isInherited && !_alwaysShownComputedProperties.has(propertyName)) {
        continue;
      }
      if (!showInherited && propertyName.startsWith("--")) {
        continue;
      }
      if (propertyName !== canonicalName && propertyValue === nodeStyle.computedStyle.get(canonicalName)) {
        continue;
      }
      computedStyleQueue.push({ propertyName, propertyValue, isInherited });
    }
    this.propertiesOutline.contentElement.classList.add("render-flash");
    const batchSize = 20;
    const timeoutInterval = 100;
    let timeout = 100;
    while (computedStyleQueue.length > 0) {
      const currentBatch = computedStyleQueue.splice(0, batchSize);
      this.idleCallbackManager.schedule(() => {
        for (const { propertyName, propertyValue, isInherited } of currentBatch) {
          const treeElement = this.buildPropertyTreeElement(propertyTraces, node, matchedStyles, propertyName, propertyValue, isInherited, hadFocus);
          this.propertiesOutline.appendChild(treeElement);
        }
        this.filterAlphabeticalList();
      }, timeout);
      timeout += timeoutInterval;
    }
    await this.idleCallbackManager.awaitDone();
    this.propertiesOutline.contentElement.classList.remove("render-flash");
  }
  async rebuildGroupedList(nodeStyle, matchedStyles) {
    const hadFocus = this.propertiesOutline.element.hasFocus();
    this.imagePreviewPopover.hide();
    this.propertiesOutline.removeChildren();
    this.linkifier.reset();
    const cssModel = this.computedStyleModel.cssModel();
    if (!nodeStyle || !matchedStyles || !cssModel) {
      this.noMatchesElement.classList.remove("hidden");
      return;
    }
    const node = nodeStyle.node;
    const propertyTraces = this.computePropertyTraces(matchedStyles);
    const nonInheritedProperties = this.computeNonInheritedProperties(matchedStyles);
    const showInherited = this.showInheritedComputedStylePropertiesSetting.get();
    const propertiesByCategory = /* @__PURE__ */ new Map();
    for (const [propertyName, propertyValue] of nodeStyle.computedStyle) {
      const canonicalName = SDK.CSSMetadata.cssMetadata().canonicalPropertyName(propertyName);
      const isInherited = !nonInheritedProperties.has(canonicalName);
      if (!showInherited && isInherited && !_alwaysShownComputedProperties.has(propertyName)) {
        continue;
      }
      if (!showInherited && propertyName.startsWith("--")) {
        continue;
      }
      if (propertyName !== canonicalName && propertyValue === nodeStyle.computedStyle.get(canonicalName)) {
        continue;
      }
      const categories = categorizePropertyName(propertyName);
      for (const category of categories) {
        const treeElement = this.buildPropertyTreeElement(propertyTraces, node, matchedStyles, propertyName, propertyValue, isInherited, hadFocus);
        if (!propertiesByCategory.has(category)) {
          propertiesByCategory.set(category, []);
        }
        propertiesByCategory.get(category).push(treeElement);
      }
    }
    for (const category of DefaultCategoryOrder) {
      const properties = propertiesByCategory.get(category);
      if (properties && properties.length > 0) {
        const title = document.createElement("h1");
        title.textContent = category;
        const group = new UI.TreeOutline.TreeElement(title);
        group.listItemElement.classList.add("group-title");
        group.toggleOnClick = true;
        for (const property of properties) {
          group.appendChild(property);
        }
        this.propertiesOutline.appendChild(group);
        if (this.expandedGroups.has(category)) {
          group.expand();
        }
        this.categoryByTreeElement.set(group, category);
      }
    }
    this.filterGroupLists();
  }
  onTreeElementToggled(event) {
    const treeElement = event.data;
    const property = this.propertyByTreeElement.get(treeElement);
    if (property) {
      treeElement.expanded ? this.expandedProperties.add(property.name) : this.expandedProperties.delete(property.name);
    } else {
      const category = this.categoryByTreeElement.get(treeElement);
      if (category) {
        treeElement.expanded ? this.expandedGroups.add(category) : this.expandedGroups.delete(category);
      }
    }
  }
  buildPropertyTreeElement(propertyTraces, node, matchedStyles, propertyName, propertyValue, isInherited, hadFocus) {
    const treeElement = new UI.TreeOutline.TreeElement();
    const trace = propertyTraces.get(propertyName);
    let navigate = () => {
    };
    if (trace) {
      const activeProperty = this.renderPropertyTrace(matchedStyles, node, treeElement, trace);
      treeElement.setExpandable(true);
      treeElement.listItemElement.addEventListener("click", (event) => {
        treeElement.expanded ? treeElement.collapse() : treeElement.expand();
        event.consume();
      }, false);
      navigate = navigateToSource.bind(this, activeProperty);
    }
    const propertyElement = createPropertyElement(node, propertyName, propertyValue);
    propertyElement.data = {
      traceable: propertyTraces.has(propertyName),
      inherited: isInherited,
      onNavigateToSource: navigate
    };
    treeElement.title = propertyElement;
    this.propertyByTreeElement.set(treeElement, { name: propertyName, value: propertyValue });
    if (!this.propertiesOutline.selectedTreeElement) {
      treeElement.select(!hadFocus);
    }
    if (this.expandedProperties.has(propertyName)) {
      treeElement.expand();
    }
    return treeElement;
  }
  renderPropertyTrace(matchedStyles, node, rootTreeElement, tracedProperties) {
    let activeProperty = null;
    for (const property of tracedProperties) {
      const isPropertyOverloaded = matchedStyles.propertyState(property) === SDK.CSSMatchedStyles.PropertyState.Overloaded;
      if (!isPropertyOverloaded) {
        activeProperty = property;
        rootTreeElement.listItemElement.addEventListener("contextmenu", this.handleContextMenuEvent.bind(this, matchedStyles, property));
      }
      const trace = createTraceElement(node, property, isPropertyOverloaded, matchedStyles, this.linkifier);
      const traceTreeElement = new UI.TreeOutline.TreeElement();
      traceTreeElement.title = trace;
      traceTreeElement.listItemElement.addEventListener("contextmenu", this.handleContextMenuEvent.bind(this, matchedStyles, property));
      rootTreeElement.appendChild(traceTreeElement);
    }
    return activeProperty;
  }
  handleContextMenuEvent(matchedStyles, property, event) {
    const contextMenu = new UI.ContextMenu.ContextMenu(event);
    const rule = property.ownerStyle.parentRule;
    if (rule) {
      const header = rule.styleSheetId ? matchedStyles.cssModel().styleSheetHeaderForId(rule.styleSheetId) : null;
      if (header && !header.isAnonymousInlineStyleSheet()) {
        contextMenu.defaultSection().appendItem(i18nString(UIStrings.navigateToSelectorSource), () => {
          StylePropertiesSection.tryNavigateToRuleLocation(matchedStyles, rule);
        });
      }
    }
    contextMenu.defaultSection().appendItem(i18nString(UIStrings.navigateToStyle), () => Common.Revealer.reveal(property));
    void contextMenu.show();
  }
  computePropertyTraces(matchedStyles) {
    const result = /* @__PURE__ */ new Map();
    for (const style of matchedStyles.nodeStyles()) {
      const allProperties = style.allProperties();
      for (const property of allProperties) {
        if (!property.activeInStyle() || !matchedStyles.propertyState(property)) {
          continue;
        }
        if (!result.has(property.name)) {
          result.set(property.name, []);
        }
        result.get(property.name).push(property);
      }
    }
    return result;
  }
  computeNonInheritedProperties(matchedStyles) {
    const result = /* @__PURE__ */ new Set();
    for (const style of matchedStyles.nodeStyles()) {
      for (const property of style.allProperties()) {
        if (!matchedStyles.propertyState(property)) {
          continue;
        }
        result.add(SDK.CSSMetadata.cssMetadata().canonicalPropertyName(property.name));
      }
    }
    return result;
  }
  filterComputedStyles(regex) {
    this.filterRegex = regex;
    if (this.groupComputedStylesSetting.get()) {
      this.filterGroupLists();
    } else {
      this.filterAlphabeticalList();
    }
  }
  filterAlphabeticalList() {
    const regex = this.filterRegex;
    const children = this.propertiesOutline.rootElement().children();
    let hasMatch = false;
    for (const child of children) {
      const property = this.propertyByTreeElement.get(child);
      if (!property) {
        continue;
      }
      const matched = !regex || regex.test(property.name) || regex.test(property.value);
      child.hidden = !matched;
      hasMatch = hasMatch || matched;
    }
    this.noMatchesElement.classList.toggle("hidden", Boolean(hasMatch));
  }
  filterGroupLists() {
    const regex = this.filterRegex;
    const groups = this.propertiesOutline.rootElement().children();
    let hasOverallMatch = false;
    let foundFirstGroup = false;
    for (const group of groups) {
      let hasGroupMatch = false;
      const properties = group.children();
      for (const propertyTreeElement of properties) {
        const property = this.propertyByTreeElement.get(propertyTreeElement);
        if (!property) {
          continue;
        }
        const matched = !regex || regex.test(property.name) || regex.test(property.value);
        propertyTreeElement.hidden = !matched;
        hasOverallMatch = hasOverallMatch || matched;
        hasGroupMatch = hasGroupMatch || matched;
      }
      group.hidden = !hasGroupMatch;
      group.listItemElement.classList.toggle("first-group", hasGroupMatch && !foundFirstGroup);
      foundFirstGroup = foundFirstGroup || hasGroupMatch;
    }
    this.noMatchesElement.classList.toggle("hidden", hasOverallMatch);
  }
}
const _maxLinkLength = 30;
const _alwaysShownComputedProperties = /* @__PURE__ */ new Set(["display", "height", "width"]);
//# sourceMappingURL=ComputedStyleWidget.js.map
