UNPKG

chrome-devtools-frontend

Version:
92 lines (82 loc) 3.33 kB
// Copyright 2025 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. /* eslint-disable @devtools/enforce-custom-element-definitions-location */ import * as TextUtils from '../../../models/text_utils/text_utils.js'; import {HighlightManager} from './HighlightManager.js'; import {type HighlightChange, highlightRangesWithStyleClass, revertDomChanges} from './MarkupHighlight.js'; export class HighlightElement extends HTMLElement { static readonly observedAttributes = ['ranges', 'current-range', 'type']; #ranges: TextUtils.TextRange.SourceRange[] = []; #currentRange: TextUtils.TextRange.SourceRange|undefined; #type = 'css'; #markupChanges: HighlightChange[] = []; attributeChangedCallback(name: string, oldValue: string|null, newValue: string|null): void { if (oldValue === newValue) { return; } switch (name) { case 'ranges': this.#ranges = parseRanges(newValue); break; case 'current-range': this.#currentRange = parseRanges(newValue)[0]; break; case 'type': this.#type = newValue || 'css'; break; } queueMicrotask(() => { if (this.#type === 'css') { HighlightManager.instance().set(this, this.#ranges, this.#currentRange); } else { revertDomChanges(this.#markupChanges); highlightRangesWithStyleClass(this, this.#ranges, 'highlight', this.#markupChanges); } }); } } function parseRanges(value: string|null): TextUtils.TextRange.SourceRange[] { if (!value) { return []; } const ranges = value.split(' ') .filter(rangeString => { const parts = rangeString.split(','); // A valid range string must have exactly two parts. if (parts.length !== 2) { return false; } // Both parts must be convertible to valid numbers. const num1 = Number(parts[0]); const num2 = Number(parts[1]); return !isNaN(num1) && !isNaN(num2); }) .map(rangeString => { const parts = rangeString.split(',').map(part => Number(part)); return new TextUtils.TextRange.SourceRange(parts[0], parts[1]); }); return sortAndMergeRanges(ranges); } function sortAndMergeRanges(ranges: TextUtils.TextRange.SourceRange[]): TextUtils.TextRange.SourceRange[] { // Sort by start position. ranges.sort((a, b) => a.offset - b.offset); if (ranges.length === 0) { return []; } // Merge overlapping ranges. const merged = [ranges[0]]; for (let i = 1; i < ranges.length; i++) { const last = merged[merged.length - 1]; const current = ranges[i]; if (current.offset <= last.offset + last.length) { const newEnd = Math.max(last.offset + last.length, current.offset + current.length); const newLength = newEnd - last.offset; merged[merged.length - 1] = new TextUtils.TextRange.SourceRange(last.offset, newLength); } else { merged.push(current); } } return merged; } customElements.define('devtools-highlight', HighlightElement);