@hereorcode/dom-inspector 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -0
- package/dist/index.cjs +764 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +103 -0
- package/dist/index.d.ts +103 -0
- package/dist/index.js +735 -0
- package/dist/index.js.map +1 -0
- package/package.json +44 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,764 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
21
|
+
|
|
22
|
+
// src/index.ts
|
|
23
|
+
var index_exports = {};
|
|
24
|
+
__export(index_exports, {
|
|
25
|
+
createDOMInspector: () => createDOMInspector,
|
|
26
|
+
getCssSelector: () => getCssSelector,
|
|
27
|
+
getElementPath: () => getElementPath,
|
|
28
|
+
getJSPath: () => getJSPath,
|
|
29
|
+
getXPath: () => getXPath
|
|
30
|
+
});
|
|
31
|
+
module.exports = __toCommonJS(index_exports);
|
|
32
|
+
|
|
33
|
+
// src/overlay.ts
|
|
34
|
+
var DEFAULT_BORDER_COLOR = "#7c3aed";
|
|
35
|
+
var DEFAULT_BACKGROUND_COLOR = "rgba(124, 58, 237, 0.12)";
|
|
36
|
+
var DEFAULT_Z_INDEX = 2147483647;
|
|
37
|
+
var ElementHighlighter = class {
|
|
38
|
+
constructor(document2, options = {}) {
|
|
39
|
+
__publicField(this, "document", document2);
|
|
40
|
+
__publicField(this, "element");
|
|
41
|
+
this.element = document2.createElement("dom-inspector-overlay");
|
|
42
|
+
this.element.setAttribute("data-dom-inspector-overlay", "true");
|
|
43
|
+
this.element.style.cssText = [
|
|
44
|
+
"position: fixed",
|
|
45
|
+
"top: 0",
|
|
46
|
+
"left: 0",
|
|
47
|
+
"display: none",
|
|
48
|
+
"pointer-events: none",
|
|
49
|
+
"box-sizing: border-box",
|
|
50
|
+
`border: 1px dashed ${options.borderColor ?? DEFAULT_BORDER_COLOR}`,
|
|
51
|
+
`background: ${options.backgroundColor ?? DEFAULT_BACKGROUND_COLOR}`,
|
|
52
|
+
`border-radius: ${options.borderRadius ?? 0}px`,
|
|
53
|
+
`z-index: ${options.zIndex ?? DEFAULT_Z_INDEX}`,
|
|
54
|
+
"transition: transform 80ms ease, width 80ms ease, height 80ms ease"
|
|
55
|
+
].join(";");
|
|
56
|
+
const parent = document2.body ?? document2.documentElement;
|
|
57
|
+
parent.appendChild(this.element);
|
|
58
|
+
}
|
|
59
|
+
show(target) {
|
|
60
|
+
this.showRect(target.getBoundingClientRect());
|
|
61
|
+
}
|
|
62
|
+
showRect(rect) {
|
|
63
|
+
this.element.style.display = "block";
|
|
64
|
+
this.element.style.transform = `translate(${rect.left}px, ${rect.top}px)`;
|
|
65
|
+
this.element.style.width = `${rect.width}px`;
|
|
66
|
+
this.element.style.height = `${rect.height}px`;
|
|
67
|
+
}
|
|
68
|
+
hide() {
|
|
69
|
+
this.element.style.display = "none";
|
|
70
|
+
}
|
|
71
|
+
destroy() {
|
|
72
|
+
this.element.remove();
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// src/path.ts
|
|
77
|
+
var ROOT_TAGS = /* @__PURE__ */ new Set(["html", "body"]);
|
|
78
|
+
function getCssSelector(element) {
|
|
79
|
+
const segments = [];
|
|
80
|
+
let current = element;
|
|
81
|
+
while (current && current.nodeType === 1) {
|
|
82
|
+
const tagName = getTagName(current);
|
|
83
|
+
segments.unshift(getCssSegment(current));
|
|
84
|
+
if (ROOT_TAGS.has(tagName)) {
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
current = current.parentElement;
|
|
88
|
+
}
|
|
89
|
+
return segments.join(" > ");
|
|
90
|
+
}
|
|
91
|
+
function getXPath(element, options = {}) {
|
|
92
|
+
const segments = [];
|
|
93
|
+
let current = element;
|
|
94
|
+
while (current && current.nodeType === 1) {
|
|
95
|
+
const tagName = getTagName(current);
|
|
96
|
+
const index = getSameTagIndex(current);
|
|
97
|
+
const shouldIncludeIndex = options.alwaysIncludeIndex || hasSameTagSibling(current);
|
|
98
|
+
segments.unshift(`${tagName}${shouldIncludeIndex ? `[${index}]` : ""}`);
|
|
99
|
+
if (tagName === "html") {
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
current = current.parentElement;
|
|
103
|
+
}
|
|
104
|
+
return `/${segments.join("/")}`;
|
|
105
|
+
}
|
|
106
|
+
function getJSPath(element) {
|
|
107
|
+
return `document.querySelector(${JSON.stringify(getCssSelector(element))})`;
|
|
108
|
+
}
|
|
109
|
+
function getElementPath(element, options = {}) {
|
|
110
|
+
const rootDocument = options.rootDocument ?? element.ownerDocument;
|
|
111
|
+
const selector = getCssSelector(element);
|
|
112
|
+
const xpath = getXPath(element);
|
|
113
|
+
const fullXPath = getXPath(element, { alwaysIncludeIndex: true });
|
|
114
|
+
const bridges = collectAccessBridges(element, rootDocument);
|
|
115
|
+
const orderedBridges = [...bridges].reverse();
|
|
116
|
+
const framePath = [];
|
|
117
|
+
const shadowPath = [];
|
|
118
|
+
let expression = "document";
|
|
119
|
+
for (const bridge of orderedBridges) {
|
|
120
|
+
if (bridge.kind === "iframe") {
|
|
121
|
+
const frameExpression = `${expression}.querySelector(${JSON.stringify(
|
|
122
|
+
bridge.selector
|
|
123
|
+
)})`;
|
|
124
|
+
framePath.push({
|
|
125
|
+
element: bridge.element,
|
|
126
|
+
selector: bridge.selector,
|
|
127
|
+
xpath: bridge.xpath,
|
|
128
|
+
fullXPath: bridge.fullXPath,
|
|
129
|
+
jsPath: frameExpression
|
|
130
|
+
});
|
|
131
|
+
expression = `${frameExpression}.contentDocument`;
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
const hostExpression = `${expression}.querySelector(${JSON.stringify(
|
|
135
|
+
bridge.selector
|
|
136
|
+
)})`;
|
|
137
|
+
shadowPath.push({
|
|
138
|
+
host: bridge.host,
|
|
139
|
+
hostSelector: bridge.selector,
|
|
140
|
+
hostXPath: bridge.xpath,
|
|
141
|
+
hostFullXPath: bridge.fullXPath,
|
|
142
|
+
jsPath: hostExpression
|
|
143
|
+
});
|
|
144
|
+
expression = `${hostExpression}.shadowRoot`;
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
rootKind: getRootKind(bridges),
|
|
148
|
+
selector,
|
|
149
|
+
xpath,
|
|
150
|
+
fullXPath,
|
|
151
|
+
jsPath: `${expression}.querySelector(${JSON.stringify(selector)})`,
|
|
152
|
+
framePath,
|
|
153
|
+
shadowPath
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
function getCssSegment(element) {
|
|
157
|
+
const tagName = getTagName(element);
|
|
158
|
+
if (ROOT_TAGS.has(tagName)) {
|
|
159
|
+
return tagName;
|
|
160
|
+
}
|
|
161
|
+
if (!hasSameTagSibling(element)) {
|
|
162
|
+
return tagName;
|
|
163
|
+
}
|
|
164
|
+
return `${tagName}:nth-child(${getElementIndex(element)})`;
|
|
165
|
+
}
|
|
166
|
+
function getTagName(element) {
|
|
167
|
+
return element.localName.toLowerCase();
|
|
168
|
+
}
|
|
169
|
+
function getElementIndex(element) {
|
|
170
|
+
const siblings = getElementSiblings(element);
|
|
171
|
+
if (!siblings) {
|
|
172
|
+
return 1;
|
|
173
|
+
}
|
|
174
|
+
return siblings.indexOf(element) + 1;
|
|
175
|
+
}
|
|
176
|
+
function getSameTagIndex(element) {
|
|
177
|
+
const siblings = getElementSiblings(element);
|
|
178
|
+
if (!siblings) {
|
|
179
|
+
return 1;
|
|
180
|
+
}
|
|
181
|
+
const tagName = getTagName(element);
|
|
182
|
+
let index = 0;
|
|
183
|
+
for (const child of siblings) {
|
|
184
|
+
if (getTagName(child) === tagName) {
|
|
185
|
+
index += 1;
|
|
186
|
+
}
|
|
187
|
+
if (child === element) {
|
|
188
|
+
return index;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return 1;
|
|
192
|
+
}
|
|
193
|
+
function hasSameTagSibling(element) {
|
|
194
|
+
const siblings = getElementSiblings(element);
|
|
195
|
+
if (!siblings) {
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
const tagName = getTagName(element);
|
|
199
|
+
let count = 0;
|
|
200
|
+
for (const child of siblings) {
|
|
201
|
+
if (getTagName(child) === tagName) {
|
|
202
|
+
count += 1;
|
|
203
|
+
}
|
|
204
|
+
if (count > 1) {
|
|
205
|
+
return true;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
function collectAccessBridges(element, rootDocument) {
|
|
211
|
+
const bridges = [];
|
|
212
|
+
let current = element;
|
|
213
|
+
let root = current.getRootNode();
|
|
214
|
+
while (true) {
|
|
215
|
+
if (isShadowRoot(root)) {
|
|
216
|
+
const host = root.host;
|
|
217
|
+
bridges.push({
|
|
218
|
+
kind: "shadow",
|
|
219
|
+
host,
|
|
220
|
+
selector: getCssSelector(host),
|
|
221
|
+
xpath: getXPath(host),
|
|
222
|
+
fullXPath: getXPath(host, { alwaysIncludeIndex: true })
|
|
223
|
+
});
|
|
224
|
+
current = host;
|
|
225
|
+
root = current.getRootNode();
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
if (isDocument(root) && root !== rootDocument) {
|
|
229
|
+
const frameElement = findFrameElementForDocument(root, rootDocument);
|
|
230
|
+
if (!frameElement) {
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
bridges.push({
|
|
234
|
+
kind: "iframe",
|
|
235
|
+
element: frameElement,
|
|
236
|
+
selector: getCssSelector(frameElement),
|
|
237
|
+
xpath: getXPath(frameElement),
|
|
238
|
+
fullXPath: getXPath(frameElement, { alwaysIncludeIndex: true })
|
|
239
|
+
});
|
|
240
|
+
current = frameElement;
|
|
241
|
+
root = current.getRootNode();
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
return bridges;
|
|
247
|
+
}
|
|
248
|
+
function getRootKind(bridges) {
|
|
249
|
+
const innermostBridge = bridges[0];
|
|
250
|
+
if (!innermostBridge) {
|
|
251
|
+
return "document";
|
|
252
|
+
}
|
|
253
|
+
return innermostBridge.kind === "shadow" ? "shadow" : "iframe";
|
|
254
|
+
}
|
|
255
|
+
function findFrameElementForDocument(targetDocument, searchDocument) {
|
|
256
|
+
const directFrame = targetDocument.defaultView?.frameElement;
|
|
257
|
+
if (directFrame && getTagName(directFrame) === "iframe") {
|
|
258
|
+
return directFrame;
|
|
259
|
+
}
|
|
260
|
+
for (const frame of Array.from(searchDocument.querySelectorAll("iframe"))) {
|
|
261
|
+
const childDocument = getAccessibleFrameDocument(frame);
|
|
262
|
+
if (!childDocument) {
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
if (childDocument === targetDocument) {
|
|
266
|
+
return frame;
|
|
267
|
+
}
|
|
268
|
+
const nestedFrame = findFrameElementForDocument(
|
|
269
|
+
targetDocument,
|
|
270
|
+
childDocument
|
|
271
|
+
);
|
|
272
|
+
if (nestedFrame) {
|
|
273
|
+
return nestedFrame;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
function getAccessibleFrameDocument(frame) {
|
|
279
|
+
try {
|
|
280
|
+
return frame.contentDocument?.documentElement ? frame.contentDocument : null;
|
|
281
|
+
} catch {
|
|
282
|
+
return null;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
function getElementSiblings(element) {
|
|
286
|
+
if (element.parentElement) {
|
|
287
|
+
return Array.from(element.parentElement.children);
|
|
288
|
+
}
|
|
289
|
+
const parentNode = element.parentNode;
|
|
290
|
+
if (!parentNode || parentNode.nodeType !== 11) {
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
return Array.from(parentNode.childNodes).filter(isElement);
|
|
294
|
+
}
|
|
295
|
+
function isDocument(root) {
|
|
296
|
+
return root.nodeType === 9;
|
|
297
|
+
}
|
|
298
|
+
function isElement(node) {
|
|
299
|
+
return node.nodeType === 1;
|
|
300
|
+
}
|
|
301
|
+
function isShadowRoot(root) {
|
|
302
|
+
return root.nodeType === 11 && "host" in root;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// src/pseudo.ts
|
|
306
|
+
var PSEUDO_ELEMENTS = ["::after", "::before"];
|
|
307
|
+
var HIT_SLOP = 4;
|
|
308
|
+
function getPseudoElementMatch(element, clientX, clientY) {
|
|
309
|
+
const pseudoElements = getPseudoElements(element);
|
|
310
|
+
for (const info of pseudoElements) {
|
|
311
|
+
if (!info.rect) {
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
if (containsPoint(info.rect, clientX, clientY, HIT_SLOP)) {
|
|
315
|
+
return {
|
|
316
|
+
hit: { pseudoElement: info.pseudoElement, rect: info.rect },
|
|
317
|
+
pseudoElements
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
return { hit: null, pseudoElements };
|
|
322
|
+
}
|
|
323
|
+
function getPseudoElements(element) {
|
|
324
|
+
const view = element.ownerDocument.defaultView;
|
|
325
|
+
if (!view) {
|
|
326
|
+
return [];
|
|
327
|
+
}
|
|
328
|
+
const pseudoElements = [];
|
|
329
|
+
for (const pseudoElement of PSEUDO_ELEMENTS) {
|
|
330
|
+
if (!hasVisiblePseudoElement(element, pseudoElement)) {
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
333
|
+
pseudoElements.push({
|
|
334
|
+
pseudoElement,
|
|
335
|
+
rect: getPseudoElementRect(element, pseudoElement)
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
return pseudoElements;
|
|
339
|
+
}
|
|
340
|
+
function getPseudoElementRect(element, pseudoElement) {
|
|
341
|
+
const view = element.ownerDocument.defaultView;
|
|
342
|
+
if (!view) {
|
|
343
|
+
return null;
|
|
344
|
+
}
|
|
345
|
+
const style = view.getComputedStyle(element, pseudoElement);
|
|
346
|
+
if (!isVisiblePseudoStyle(style)) {
|
|
347
|
+
return null;
|
|
348
|
+
}
|
|
349
|
+
const baseRect = element.getBoundingClientRect();
|
|
350
|
+
const leftInset = readLength(style.left, baseRect.width);
|
|
351
|
+
const rightInset = readLength(style.right, baseRect.width);
|
|
352
|
+
const topInset = readLength(style.top, baseRect.height);
|
|
353
|
+
const bottomInset = readLength(style.bottom, baseRect.height);
|
|
354
|
+
let width = readLength(style.width, baseRect.width);
|
|
355
|
+
let height = readLength(style.height, baseRect.height);
|
|
356
|
+
const hasGeometrySignal = style.position === "absolute" || style.position === "fixed" || width !== null || height !== null || leftInset !== null || rightInset !== null || topInset !== null || bottomInset !== null;
|
|
357
|
+
if (!hasGeometrySignal) {
|
|
358
|
+
return null;
|
|
359
|
+
}
|
|
360
|
+
if (width === null && leftInset !== null && rightInset !== null) {
|
|
361
|
+
width = Math.max(baseRect.width - leftInset - rightInset, 0);
|
|
362
|
+
}
|
|
363
|
+
if (height === null && topInset !== null && bottomInset !== null) {
|
|
364
|
+
height = Math.max(baseRect.height - topInset - bottomInset, 0);
|
|
365
|
+
}
|
|
366
|
+
if (width === null || height === null) {
|
|
367
|
+
return null;
|
|
368
|
+
}
|
|
369
|
+
const x = leftInset !== null ? baseRect.left + leftInset : rightInset !== null ? baseRect.right - rightInset - width : pseudoElement === "::before" ? baseRect.left : baseRect.right - width;
|
|
370
|
+
const y = topInset !== null ? baseRect.top + topInset : bottomInset !== null ? baseRect.bottom - bottomInset - height : pseudoElement === "::before" ? baseRect.top : baseRect.bottom - height;
|
|
371
|
+
return {
|
|
372
|
+
x,
|
|
373
|
+
y,
|
|
374
|
+
top: y,
|
|
375
|
+
left: x,
|
|
376
|
+
width,
|
|
377
|
+
height,
|
|
378
|
+
right: x + width,
|
|
379
|
+
bottom: y + height
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
function hasVisiblePseudoElement(element, pseudoElement) {
|
|
383
|
+
const view = element.ownerDocument.defaultView;
|
|
384
|
+
if (!view) {
|
|
385
|
+
return false;
|
|
386
|
+
}
|
|
387
|
+
return isVisiblePseudoStyle(view.getComputedStyle(element, pseudoElement));
|
|
388
|
+
}
|
|
389
|
+
function isVisiblePseudoStyle(style) {
|
|
390
|
+
return Boolean(
|
|
391
|
+
style && style.display !== "none" && style.visibility !== "hidden" && style.content !== "none" && style.content !== "normal"
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
function containsPoint(rect, clientX, clientY, slop) {
|
|
395
|
+
return clientX >= rect.left - slop && clientX <= rect.right + slop && clientY >= rect.top - slop && clientY <= rect.bottom + slop;
|
|
396
|
+
}
|
|
397
|
+
function readLength(value, basis) {
|
|
398
|
+
if (!value || value === "auto") {
|
|
399
|
+
return null;
|
|
400
|
+
}
|
|
401
|
+
if (value.endsWith("%")) {
|
|
402
|
+
const percentage = Number.parseFloat(value);
|
|
403
|
+
return Number.isFinite(percentage) ? basis * percentage / 100 : null;
|
|
404
|
+
}
|
|
405
|
+
const pixels = Number.parseFloat(value);
|
|
406
|
+
return Number.isFinite(pixels) ? pixels : null;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// src/inspector.ts
|
|
410
|
+
function createDOMInspector(options) {
|
|
411
|
+
return new DOMInspectorController(options);
|
|
412
|
+
}
|
|
413
|
+
var DOMInspectorController = class {
|
|
414
|
+
constructor(options) {
|
|
415
|
+
__publicField(this, "options", options);
|
|
416
|
+
__publicField(this, "document");
|
|
417
|
+
__publicField(this, "frameLoadHandlers", /* @__PURE__ */ new Map());
|
|
418
|
+
__publicField(this, "managedDocuments", /* @__PURE__ */ new Map());
|
|
419
|
+
__publicField(this, "selectionScope");
|
|
420
|
+
__publicField(this, "active", false);
|
|
421
|
+
__publicField(this, "hoveredElement", null);
|
|
422
|
+
__publicField(this, "hoveredPseudoElement", null);
|
|
423
|
+
__publicField(this, "hoveredPath", []);
|
|
424
|
+
__publicField(this, "hoveredPathIndex", 0);
|
|
425
|
+
__publicField(this, "handleMouseMove", (event) => {
|
|
426
|
+
const path = this.getInspectablePath(event);
|
|
427
|
+
const target = path[0];
|
|
428
|
+
if (!target) {
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
const nextIndex = this.hoveredPath[0] === target ? Math.min(this.hoveredPathIndex, path.length - 1) : 0;
|
|
432
|
+
this.hoveredPath = path;
|
|
433
|
+
this.hoveredPathIndex = nextIndex;
|
|
434
|
+
this.updateHoveredElement(path[nextIndex], event);
|
|
435
|
+
});
|
|
436
|
+
__publicField(this, "handleMouseDown", (event) => {
|
|
437
|
+
if (event.button !== 0) {
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
const target = this.getInspectablePath(event)[0];
|
|
441
|
+
if (!target) {
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
if (this.options.preventDefault !== false) {
|
|
445
|
+
event.preventDefault();
|
|
446
|
+
event.stopPropagation();
|
|
447
|
+
event.stopImmediatePropagation();
|
|
448
|
+
}
|
|
449
|
+
});
|
|
450
|
+
__publicField(this, "handleClick", (event) => {
|
|
451
|
+
const path = this.getInspectablePath(event);
|
|
452
|
+
const target = this.getClickTarget(path);
|
|
453
|
+
if (!target) {
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
const result = this.createResult(target, event);
|
|
457
|
+
if (this.options.preventDefault !== false) {
|
|
458
|
+
event.preventDefault();
|
|
459
|
+
event.stopPropagation();
|
|
460
|
+
event.stopImmediatePropagation();
|
|
461
|
+
}
|
|
462
|
+
this.showHighlight(result);
|
|
463
|
+
try {
|
|
464
|
+
this.options.onSelect?.(result);
|
|
465
|
+
} finally {
|
|
466
|
+
if (this.options.autoStop !== false) {
|
|
467
|
+
this.stop();
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
});
|
|
471
|
+
__publicField(this, "handleWheel", (event) => {
|
|
472
|
+
if (!this.selectionScope || !hasModifierKey(event, this.selectionScope.modifierKey)) {
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
const direction = getWheelDirection(event);
|
|
476
|
+
if (direction === 0) {
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
const path = this.getInspectablePath(event);
|
|
480
|
+
if (path.length > 0 && path[0] !== this.hoveredPath[0]) {
|
|
481
|
+
this.hoveredPath = path;
|
|
482
|
+
this.hoveredPathIndex = 0;
|
|
483
|
+
}
|
|
484
|
+
if (this.hoveredPath.length === 0) {
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
event.preventDefault();
|
|
488
|
+
event.stopPropagation();
|
|
489
|
+
event.stopImmediatePropagation();
|
|
490
|
+
const nextIndex = clamp(
|
|
491
|
+
this.hoveredPathIndex + direction,
|
|
492
|
+
0,
|
|
493
|
+
this.hoveredPath.length - 1
|
|
494
|
+
);
|
|
495
|
+
if (nextIndex === this.hoveredPathIndex) {
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
this.hoveredPathIndex = nextIndex;
|
|
499
|
+
this.updateHoveredElement(this.hoveredPath[nextIndex], event);
|
|
500
|
+
});
|
|
501
|
+
__publicField(this, "handleKeyDown", (event) => {
|
|
502
|
+
if (event.key !== "Escape") {
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
event.preventDefault();
|
|
506
|
+
this.stop();
|
|
507
|
+
this.options.onCancel?.();
|
|
508
|
+
});
|
|
509
|
+
__publicField(this, "handleViewportChange", () => {
|
|
510
|
+
if (!this.hoveredElement) {
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
const highlighter = this.getHighlighter(this.hoveredElement.ownerDocument);
|
|
514
|
+
if (highlighter) {
|
|
515
|
+
this.hideInactiveHighlighters(this.hoveredElement.ownerDocument);
|
|
516
|
+
highlighter.show(this.hoveredElement);
|
|
517
|
+
}
|
|
518
|
+
});
|
|
519
|
+
this.document = options.document ?? document;
|
|
520
|
+
this.selectionScope = getSelectionScope(options.selectionScope);
|
|
521
|
+
}
|
|
522
|
+
start() {
|
|
523
|
+
if (this.active) {
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
this.active = true;
|
|
527
|
+
this.attachDocument(this.document);
|
|
528
|
+
}
|
|
529
|
+
stop() {
|
|
530
|
+
if (!this.active) {
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
this.active = false;
|
|
534
|
+
this.hoveredElement = null;
|
|
535
|
+
this.hoveredPseudoElement = null;
|
|
536
|
+
this.hoveredPath = [];
|
|
537
|
+
this.hoveredPathIndex = 0;
|
|
538
|
+
for (const [frame, handler] of this.frameLoadHandlers) {
|
|
539
|
+
frame.removeEventListener("load", handler);
|
|
540
|
+
}
|
|
541
|
+
this.frameLoadHandlers.clear();
|
|
542
|
+
for (const state of this.managedDocuments.values()) {
|
|
543
|
+
state.document.documentElement.style.cursor = state.previousCursor;
|
|
544
|
+
state.document.removeEventListener("mousemove", this.handleMouseMove, true);
|
|
545
|
+
state.document.removeEventListener("mousedown", this.handleMouseDown, true);
|
|
546
|
+
state.document.removeEventListener("click", this.handleClick, true);
|
|
547
|
+
state.document.removeEventListener("keydown", this.handleKeyDown, true);
|
|
548
|
+
state.document.removeEventListener("wheel", this.handleWheel, true);
|
|
549
|
+
state.document.defaultView?.removeEventListener(
|
|
550
|
+
"scroll",
|
|
551
|
+
this.handleViewportChange,
|
|
552
|
+
true
|
|
553
|
+
);
|
|
554
|
+
state.document.defaultView?.removeEventListener(
|
|
555
|
+
"resize",
|
|
556
|
+
this.handleViewportChange,
|
|
557
|
+
true
|
|
558
|
+
);
|
|
559
|
+
state.observer?.disconnect();
|
|
560
|
+
state.highlighter?.destroy();
|
|
561
|
+
}
|
|
562
|
+
this.managedDocuments.clear();
|
|
563
|
+
}
|
|
564
|
+
destroy() {
|
|
565
|
+
this.stop();
|
|
566
|
+
}
|
|
567
|
+
isActive() {
|
|
568
|
+
return this.active;
|
|
569
|
+
}
|
|
570
|
+
getInspectablePath(event) {
|
|
571
|
+
const path = event.composedPath().filter(isElementLike);
|
|
572
|
+
if (path.some((entry) => this.options.exclude?.(entry))) {
|
|
573
|
+
return [];
|
|
574
|
+
}
|
|
575
|
+
const inspectablePath = [];
|
|
576
|
+
for (const entry of path) {
|
|
577
|
+
if (this.managedDocuments.has(entry.ownerDocument) && !entry.hasAttribute("data-dom-inspector-overlay")) {
|
|
578
|
+
inspectablePath.push(entry);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
if (inspectablePath.length > 0) {
|
|
582
|
+
return inspectablePath;
|
|
583
|
+
}
|
|
584
|
+
return isElementLike(event.target) && this.managedDocuments.has(event.target.ownerDocument) ? [event.target] : [];
|
|
585
|
+
}
|
|
586
|
+
getClickTarget(path) {
|
|
587
|
+
if (this.hoveredElement && path.includes(this.hoveredElement)) {
|
|
588
|
+
return this.hoveredElement;
|
|
589
|
+
}
|
|
590
|
+
return path[0] ?? null;
|
|
591
|
+
}
|
|
592
|
+
updateHoveredElement(element, event) {
|
|
593
|
+
const result = this.createResult(element, event);
|
|
594
|
+
if (element === this.hoveredElement && result.pseudoElement === this.hoveredPseudoElement) {
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
this.hoveredElement = element;
|
|
598
|
+
this.hoveredPseudoElement = result.pseudoElement;
|
|
599
|
+
this.showHighlight(result);
|
|
600
|
+
this.options.onHover?.(result);
|
|
601
|
+
}
|
|
602
|
+
createResult(element, event) {
|
|
603
|
+
const path = getElementPath(element, { rootDocument: this.document });
|
|
604
|
+
const pseudoMatch = getPseudoElementMatch(
|
|
605
|
+
element,
|
|
606
|
+
event.clientX,
|
|
607
|
+
event.clientY
|
|
608
|
+
);
|
|
609
|
+
const pseudoHit = pseudoMatch.hit;
|
|
610
|
+
return {
|
|
611
|
+
...path,
|
|
612
|
+
element,
|
|
613
|
+
ownerDocument: element.ownerDocument,
|
|
614
|
+
pseudoElement: pseudoHit?.pseudoElement ?? null,
|
|
615
|
+
pseudoElements: pseudoMatch.pseudoElements,
|
|
616
|
+
pseudoElementRect: pseudoHit?.rect ?? null,
|
|
617
|
+
rect: element.getBoundingClientRect(),
|
|
618
|
+
event,
|
|
619
|
+
selectorWithPseudo: pseudoHit ? `${path.selector}${pseudoHit.pseudoElement}` : path.selector
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
attachDocument(targetDocument) {
|
|
623
|
+
if (this.managedDocuments.has(targetDocument)) {
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
const previousCursor = targetDocument.documentElement.style.cursor;
|
|
627
|
+
const highlighter = this.options.highlight === false ? null : new ElementHighlighter(
|
|
628
|
+
targetDocument,
|
|
629
|
+
typeof this.options.highlight === "object" ? this.options.highlight : void 0
|
|
630
|
+
);
|
|
631
|
+
const Observer = targetDocument.defaultView?.MutationObserver;
|
|
632
|
+
const observer = Observer ? new Observer(() => this.syncChildFrames(targetDocument)) : null;
|
|
633
|
+
targetDocument.documentElement.style.cursor = "crosshair";
|
|
634
|
+
targetDocument.addEventListener("mousemove", this.handleMouseMove, true);
|
|
635
|
+
targetDocument.addEventListener("mousedown", this.handleMouseDown, true);
|
|
636
|
+
targetDocument.addEventListener("click", this.handleClick, true);
|
|
637
|
+
targetDocument.addEventListener("keydown", this.handleKeyDown, true);
|
|
638
|
+
targetDocument.addEventListener("wheel", this.handleWheel, {
|
|
639
|
+
capture: true,
|
|
640
|
+
passive: false
|
|
641
|
+
});
|
|
642
|
+
targetDocument.defaultView?.addEventListener(
|
|
643
|
+
"scroll",
|
|
644
|
+
this.handleViewportChange,
|
|
645
|
+
true
|
|
646
|
+
);
|
|
647
|
+
targetDocument.defaultView?.addEventListener(
|
|
648
|
+
"resize",
|
|
649
|
+
this.handleViewportChange,
|
|
650
|
+
true
|
|
651
|
+
);
|
|
652
|
+
observer?.observe(targetDocument.documentElement, {
|
|
653
|
+
childList: true,
|
|
654
|
+
subtree: true
|
|
655
|
+
});
|
|
656
|
+
this.managedDocuments.set(targetDocument, {
|
|
657
|
+
document: targetDocument,
|
|
658
|
+
highlighter,
|
|
659
|
+
observer,
|
|
660
|
+
previousCursor
|
|
661
|
+
});
|
|
662
|
+
this.syncChildFrames(targetDocument);
|
|
663
|
+
}
|
|
664
|
+
syncChildFrames(targetDocument) {
|
|
665
|
+
for (const frame of Array.from(
|
|
666
|
+
targetDocument.querySelectorAll("iframe")
|
|
667
|
+
)) {
|
|
668
|
+
this.observeFrameLoad(frame);
|
|
669
|
+
const frameDocument = getAccessibleFrameDocument2(frame);
|
|
670
|
+
if (frameDocument) {
|
|
671
|
+
this.attachDocument(frameDocument);
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
observeFrameLoad(frame) {
|
|
676
|
+
if (this.frameLoadHandlers.has(frame)) {
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
const handler = () => {
|
|
680
|
+
if (!this.active) {
|
|
681
|
+
return;
|
|
682
|
+
}
|
|
683
|
+
const frameDocument = getAccessibleFrameDocument2(frame);
|
|
684
|
+
if (frameDocument) {
|
|
685
|
+
this.attachDocument(frameDocument);
|
|
686
|
+
}
|
|
687
|
+
};
|
|
688
|
+
frame.addEventListener("load", handler);
|
|
689
|
+
this.frameLoadHandlers.set(frame, handler);
|
|
690
|
+
}
|
|
691
|
+
showHighlight(result) {
|
|
692
|
+
const highlighter = this.getHighlighter(result.element.ownerDocument);
|
|
693
|
+
if (!highlighter) {
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
this.hideInactiveHighlighters(result.element.ownerDocument);
|
|
697
|
+
if (result.pseudoElementRect) {
|
|
698
|
+
highlighter.showRect(result.pseudoElementRect);
|
|
699
|
+
return;
|
|
700
|
+
}
|
|
701
|
+
highlighter.show(result.element);
|
|
702
|
+
}
|
|
703
|
+
hideInactiveHighlighters(activeDocument) {
|
|
704
|
+
for (const state of this.managedDocuments.values()) {
|
|
705
|
+
if (state.document !== activeDocument) {
|
|
706
|
+
state.highlighter?.hide();
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
getHighlighter(targetDocument) {
|
|
711
|
+
return this.managedDocuments.get(targetDocument)?.highlighter ?? null;
|
|
712
|
+
}
|
|
713
|
+
};
|
|
714
|
+
function getAccessibleFrameDocument2(frame) {
|
|
715
|
+
try {
|
|
716
|
+
return frame.contentDocument?.documentElement ? frame.contentDocument : null;
|
|
717
|
+
} catch {
|
|
718
|
+
return null;
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
function getSelectionScope(options) {
|
|
722
|
+
if (options === false) {
|
|
723
|
+
return null;
|
|
724
|
+
}
|
|
725
|
+
return {
|
|
726
|
+
modifierKey: options?.modifierKey ?? "Alt"
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
function hasModifierKey(event, modifierKey) {
|
|
730
|
+
switch (modifierKey) {
|
|
731
|
+
case "Alt":
|
|
732
|
+
return event.altKey;
|
|
733
|
+
case "Control":
|
|
734
|
+
return event.ctrlKey;
|
|
735
|
+
case "Meta":
|
|
736
|
+
return event.metaKey;
|
|
737
|
+
case "Shift":
|
|
738
|
+
return event.shiftKey;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
function getWheelDirection(event) {
|
|
742
|
+
if (event.deltaY < 0) {
|
|
743
|
+
return 1;
|
|
744
|
+
}
|
|
745
|
+
if (event.deltaY > 0) {
|
|
746
|
+
return -1;
|
|
747
|
+
}
|
|
748
|
+
return 0;
|
|
749
|
+
}
|
|
750
|
+
function clamp(value, min, max) {
|
|
751
|
+
return Math.min(Math.max(value, min), max);
|
|
752
|
+
}
|
|
753
|
+
function isElementLike(value) {
|
|
754
|
+
return typeof value === "object" && value !== null && value.nodeType === 1 && typeof value.tagName === "string";
|
|
755
|
+
}
|
|
756
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
757
|
+
0 && (module.exports = {
|
|
758
|
+
createDOMInspector,
|
|
759
|
+
getCssSelector,
|
|
760
|
+
getElementPath,
|
|
761
|
+
getJSPath,
|
|
762
|
+
getXPath
|
|
763
|
+
});
|
|
764
|
+
//# sourceMappingURL=index.cjs.map
|