@midscene/shared 0.12.4 → 0.12.5-beta-20250306111616.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/dist/es/{constants.js → constants/index.js} +4 -5
- package/dist/es/extractor/client-extractor.js +161 -0
- package/dist/es/extractor/constants.js +10 -0
- package/dist/es/extractor/debug.js +9 -0
- package/dist/es/extractor/dom-util.js +73 -0
- package/dist/es/extractor/index.js +14 -0
- package/dist/es/extractor/tree.js +126 -0
- package/dist/es/extractor/util.js +344 -0
- package/dist/es/extractor/web-extractor.js +317 -0
- package/dist/es/img/box-select.js +187 -0
- package/dist/es/img/draw-box.js +43 -0
- package/dist/es/img/get-jimp.js +11 -0
- package/dist/es/img/index.js +34 -0
- package/dist/es/img/info.js +50 -0
- package/dist/es/img/transform.js +128 -0
- package/dist/es/index.js +0 -1
- package/dist/es/{fs.js → node/fs.js} +22 -5
- package/dist/es/types/index.js +5 -0
- package/dist/es/us-keyboard-layout.js +10 -11
- package/dist/es/utils.js +10 -569
- package/dist/lib/constants/index.js +20 -0
- package/dist/lib/extractor/client-extractor.js +161 -0
- package/dist/lib/extractor/constants.js +10 -0
- package/dist/lib/extractor/debug.js +9 -0
- package/dist/lib/extractor/dom-util.js +73 -0
- package/dist/lib/extractor/index.js +14 -0
- package/dist/lib/extractor/tree.js +126 -0
- package/dist/lib/extractor/util.js +344 -0
- package/dist/lib/extractor/web-extractor.js +317 -0
- package/dist/lib/img/box-select.js +187 -0
- package/dist/lib/img/draw-box.js +43 -0
- package/dist/lib/img/get-jimp.js +11 -0
- package/dist/lib/img/index.js +34 -0
- package/dist/lib/img/info.js +50 -0
- package/dist/lib/img/transform.js +128 -0
- package/dist/lib/index.js +3 -25
- package/dist/lib/node/fs.js +62 -0
- package/dist/lib/types/index.js +5 -0
- package/dist/lib/us-keyboard-layout.js +12 -41
- package/dist/lib/utils.js +12 -578
- package/dist/types/constants/index.d.ts +12 -0
- package/dist/types/extractor/client-extractor.d.ts +2 -0
- package/dist/types/extractor/constants.d.ts +1 -0
- package/dist/types/extractor/debug.d.ts +1 -0
- package/dist/types/extractor/dom-util.d.ts +5 -0
- package/dist/types/extractor/index.d.ts +29 -0
- package/dist/types/extractor/tree.d.ts +9 -0
- package/dist/types/extractor/util.d.ts +40 -0
- package/dist/types/extractor/web-extractor.d.ts +14 -0
- package/dist/types/img/box-select.d.ts +31 -0
- package/dist/types/img/draw-box.d.ts +15 -0
- package/dist/types/img/get-jimp.d.ts +2 -0
- package/dist/types/img/index.d.ts +4 -0
- package/dist/types/img/info.d.ts +40 -0
- package/dist/types/img/transform.d.ts +68 -0
- package/dist/types/index.d.ts +1 -2
- package/dist/{lib → types/node}/fs.d.ts +4 -4
- package/dist/{lib/index-305e7a7e.d.ts → types/types/index.d.ts} +6 -9
- package/dist/types/us-keyboard-layout.d.ts +32 -0
- package/dist/types/utils.d.ts +11 -0
- package/package.json +39 -39
- package/src/img/box-select.ts +17 -11
- package/src/img/draw-box.ts +5 -5
- package/src/img/get-jimp.ts +3 -0
- package/src/{fs/index.ts → node/fs.ts} +20 -0
- package/src/utils.ts +13 -0
- package/dist/browser/constants.d.ts +0 -14
- package/dist/browser/constants.js +0 -42
- package/dist/browser/extractor-debug.d.ts +0 -2
- package/dist/browser/extractor-debug.js +0 -1269
- package/dist/browser/extractor.d.ts +0 -47
- package/dist/browser/extractor.js +0 -1557
- package/dist/browser/fs.d.ts +0 -14
- package/dist/browser/fs.js +0 -66
- package/dist/browser/img.d.ts +0 -152
- package/dist/browser/img.js +0 -483
- package/dist/browser/index-305e7a7e.d.ts +0 -32
- package/dist/browser/index.d.ts +0 -3
- package/dist/browser/index.js +0 -26
- package/dist/browser/us-keyboard-layout.d.ts +0 -34
- package/dist/browser/us-keyboard-layout.js +0 -456
- package/dist/browser/utils.d.ts +0 -5
- package/dist/browser/utils.js +0 -611
- package/dist/es/constants.d.ts +0 -14
- package/dist/es/extractor-debug.d.ts +0 -2
- package/dist/es/extractor-debug.js +0 -1267
- package/dist/es/extractor.d.ts +0 -47
- package/dist/es/extractor.js +0 -1546
- package/dist/es/fs.d.ts +0 -14
- package/dist/es/img.d.ts +0 -152
- package/dist/es/img.js +0 -451
- package/dist/es/index-305e7a7e.d.ts +0 -32
- package/dist/es/index.d.ts +0 -3
- package/dist/es/us-keyboard-layout.d.ts +0 -34
- package/dist/es/utils.d.ts +0 -5
- package/dist/lib/constants.d.ts +0 -14
- package/dist/lib/constants.js +0 -50
- package/dist/lib/extractor-debug.d.ts +0 -2
- package/dist/lib/extractor-debug.js +0 -1261
- package/dist/lib/extractor.d.ts +0 -47
- package/dist/lib/extractor.js +0 -1559
- package/dist/lib/fs.js +0 -71
- package/dist/lib/img.d.ts +0 -152
- package/dist/lib/img.js +0 -501
- package/dist/lib/index.d.ts +0 -3
- package/dist/lib/us-keyboard-layout.d.ts +0 -34
- package/dist/lib/utils.d.ts +0 -5
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import { generateHashId } from "../utils";
|
|
2
|
+
import { extractTextWithPosition } from "./web-extractor";
|
|
3
|
+
const MAX_VALUE_LENGTH = 300;
|
|
4
|
+
let debugMode = false;
|
|
5
|
+
function setDebugMode(mode) {
|
|
6
|
+
debugMode = mode;
|
|
7
|
+
}
|
|
8
|
+
function getDebugMode() {
|
|
9
|
+
return debugMode;
|
|
10
|
+
}
|
|
11
|
+
function logger(..._msg) {
|
|
12
|
+
if (!debugMode) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
console.log(..._msg);
|
|
16
|
+
}
|
|
17
|
+
const taskIdKey = "_midscene_retrieve_task_id";
|
|
18
|
+
function selectorForValue(val) {
|
|
19
|
+
return `[${taskIdKey}='${val}']`;
|
|
20
|
+
}
|
|
21
|
+
function setDataForNode(node, nodeHash, setToParentNode, currentWindow) {
|
|
22
|
+
const taskId = taskIdKey;
|
|
23
|
+
if (!(node instanceof currentWindow.HTMLElement)) {
|
|
24
|
+
return "";
|
|
25
|
+
}
|
|
26
|
+
if (!taskId) {
|
|
27
|
+
console.error("No task id found");
|
|
28
|
+
return "";
|
|
29
|
+
}
|
|
30
|
+
const selector = selectorForValue(nodeHash);
|
|
31
|
+
if (getDebugMode()) {
|
|
32
|
+
if (setToParentNode) {
|
|
33
|
+
if (node.parentNode instanceof currentWindow.HTMLElement) {
|
|
34
|
+
node.parentNode.setAttribute(taskIdKey, nodeHash.toString());
|
|
35
|
+
}
|
|
36
|
+
} else {
|
|
37
|
+
node.setAttribute(taskIdKey, nodeHash.toString());
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return selector;
|
|
41
|
+
}
|
|
42
|
+
function isElementPartiallyInViewport(rect, currentWindow, currentDocument) {
|
|
43
|
+
const elementHeight = rect.height;
|
|
44
|
+
const elementWidth = rect.width;
|
|
45
|
+
const viewportRect = {
|
|
46
|
+
left: 0,
|
|
47
|
+
top: 0,
|
|
48
|
+
width: currentWindow.innerWidth || currentDocument.documentElement.clientWidth,
|
|
49
|
+
height: currentWindow.innerHeight || currentDocument.documentElement.clientHeight,
|
|
50
|
+
right: currentWindow.innerWidth || currentDocument.documentElement.clientWidth,
|
|
51
|
+
bottom: currentWindow.innerHeight || currentDocument.documentElement.clientHeight,
|
|
52
|
+
x: 0,
|
|
53
|
+
y: 0,
|
|
54
|
+
zoom: 1
|
|
55
|
+
};
|
|
56
|
+
const overlapRect = overlappedRect(rect, viewportRect);
|
|
57
|
+
if (!overlapRect) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
const visibleArea = overlapRect.width * overlapRect.height;
|
|
61
|
+
const totalArea = elementHeight * elementWidth;
|
|
62
|
+
return visibleArea / totalArea >= 2 / 3;
|
|
63
|
+
}
|
|
64
|
+
function getPseudoElementContent(element, currentWindow) {
|
|
65
|
+
if (!(element instanceof currentWindow.HTMLElement)) {
|
|
66
|
+
return { before: "", after: "" };
|
|
67
|
+
}
|
|
68
|
+
const beforeContent = currentWindow.getComputedStyle(element, "::before").getPropertyValue("content");
|
|
69
|
+
const afterContent = currentWindow.getComputedStyle(element, "::after").getPropertyValue("content");
|
|
70
|
+
return {
|
|
71
|
+
before: beforeContent === "none" ? "" : beforeContent.replace(/"/g, ""),
|
|
72
|
+
after: afterContent === "none" ? "" : afterContent.replace(/"/g, "")
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function hasOverflowY(element, currentWindow) {
|
|
76
|
+
const style = currentWindow.getComputedStyle(element);
|
|
77
|
+
return style.overflowY === "scroll" || style.overflowY === "auto" || style.overflowY === "hidden";
|
|
78
|
+
}
|
|
79
|
+
function overlappedRect(rect1, rect2) {
|
|
80
|
+
const left = Math.max(rect1.left, rect2.left);
|
|
81
|
+
const top = Math.max(rect1.top, rect2.top);
|
|
82
|
+
const right = Math.min(rect1.right, rect2.right);
|
|
83
|
+
const bottom = Math.min(rect1.bottom, rect2.bottom);
|
|
84
|
+
if (left < right && top < bottom) {
|
|
85
|
+
return {
|
|
86
|
+
left,
|
|
87
|
+
top,
|
|
88
|
+
right,
|
|
89
|
+
bottom,
|
|
90
|
+
width: right - left,
|
|
91
|
+
height: bottom - top,
|
|
92
|
+
x: left,
|
|
93
|
+
y: top,
|
|
94
|
+
zoom: 1
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
function getRect(el, baseZoom, currentWindow) {
|
|
100
|
+
let originalRect;
|
|
101
|
+
let newZoom = 1;
|
|
102
|
+
if (!(el instanceof currentWindow.HTMLElement)) {
|
|
103
|
+
const range = currentWindow.document.createRange();
|
|
104
|
+
range.selectNodeContents(el);
|
|
105
|
+
originalRect = range.getBoundingClientRect();
|
|
106
|
+
} else {
|
|
107
|
+
originalRect = el.getBoundingClientRect();
|
|
108
|
+
if (!("currentCSSZoom" in el)) {
|
|
109
|
+
newZoom = Number.parseFloat(currentWindow.getComputedStyle(el).zoom) || 1;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const zoom = newZoom * baseZoom;
|
|
113
|
+
return {
|
|
114
|
+
width: originalRect.width * zoom,
|
|
115
|
+
height: originalRect.height * zoom,
|
|
116
|
+
left: originalRect.left * zoom,
|
|
117
|
+
top: originalRect.top * zoom,
|
|
118
|
+
right: originalRect.right * zoom,
|
|
119
|
+
bottom: originalRect.bottom * zoom,
|
|
120
|
+
x: originalRect.x * zoom,
|
|
121
|
+
y: originalRect.y * zoom,
|
|
122
|
+
zoom
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
const isElementCovered = (el, rect, currentWindow) => {
|
|
126
|
+
const x = rect.left + rect.width / 2;
|
|
127
|
+
const y = rect.top + rect.height / 2;
|
|
128
|
+
const topElement = currentWindow.document.elementFromPoint(x, y);
|
|
129
|
+
if (!topElement) {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
if (topElement === el) {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
if (el?.contains(topElement)) {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
if (topElement?.contains(el)) {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
const rectOfTopElement = getRect(topElement, 1, currentWindow);
|
|
142
|
+
const overlapRect = overlappedRect(rect, rectOfTopElement);
|
|
143
|
+
if (!overlapRect) {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
logger(el, "Element is covered by another element", {
|
|
147
|
+
topElement,
|
|
148
|
+
el,
|
|
149
|
+
rect,
|
|
150
|
+
x,
|
|
151
|
+
y
|
|
152
|
+
});
|
|
153
|
+
return true;
|
|
154
|
+
};
|
|
155
|
+
function visibleRect(el, currentWindow, currentDocument, baseZoom = 1) {
|
|
156
|
+
if (!el) {
|
|
157
|
+
logger(el, "Element is not in the DOM hierarchy");
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
if (!(el instanceof currentWindow.HTMLElement) && el.nodeType !== Node.TEXT_NODE && el.nodeName.toLowerCase() !== "svg") {
|
|
161
|
+
logger(el, "Element is not in the DOM hierarchy");
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
if (el instanceof currentWindow.HTMLElement) {
|
|
165
|
+
const style = currentWindow.getComputedStyle(el);
|
|
166
|
+
if (style.display === "none" || style.visibility === "hidden" || style.opacity === "0" && el.tagName !== "INPUT") {
|
|
167
|
+
logger(el, "Element is hidden");
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
const rect = getRect(el, baseZoom, currentWindow);
|
|
172
|
+
if (rect.width === 0 && rect.height === 0) {
|
|
173
|
+
logger(el, "Element has no size");
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
if (baseZoom === 1 && isElementCovered(el, rect, currentWindow)) {
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
const scrollLeft = currentWindow.pageXOffset || currentDocument.documentElement.scrollLeft;
|
|
180
|
+
const scrollTop = currentWindow.pageYOffset || currentDocument.documentElement.scrollTop;
|
|
181
|
+
const viewportWidth = currentWindow.innerWidth || currentDocument.documentElement.clientWidth;
|
|
182
|
+
const viewportHeight = currentWindow.innerHeight || currentDocument.documentElement.clientHeight;
|
|
183
|
+
const isPartiallyInViewport = isElementPartiallyInViewport(
|
|
184
|
+
rect,
|
|
185
|
+
currentWindow,
|
|
186
|
+
currentDocument
|
|
187
|
+
);
|
|
188
|
+
if (!isPartiallyInViewport) {
|
|
189
|
+
logger(el, "Element is completely outside the viewport", {
|
|
190
|
+
rect,
|
|
191
|
+
viewportHeight,
|
|
192
|
+
viewportWidth,
|
|
193
|
+
scrollTop,
|
|
194
|
+
scrollLeft
|
|
195
|
+
});
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
let parent = el;
|
|
199
|
+
const parentUntilNonStatic = (currentNode) => {
|
|
200
|
+
let parent2 = currentNode?.parentElement;
|
|
201
|
+
while (parent2) {
|
|
202
|
+
const style = currentWindow.getComputedStyle(parent2);
|
|
203
|
+
if (style.position !== "static") {
|
|
204
|
+
return parent2;
|
|
205
|
+
}
|
|
206
|
+
parent2 = parent2.parentElement;
|
|
207
|
+
}
|
|
208
|
+
return null;
|
|
209
|
+
};
|
|
210
|
+
while (parent && parent !== currentDocument.body) {
|
|
211
|
+
if (!(parent instanceof currentWindow.HTMLElement)) {
|
|
212
|
+
parent = parent.parentElement;
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
const parentStyle = currentWindow.getComputedStyle(parent);
|
|
216
|
+
if (parentStyle.overflow === "hidden") {
|
|
217
|
+
const parentRect = getRect(parent, 1, currentWindow);
|
|
218
|
+
const tolerance = 10;
|
|
219
|
+
if (rect.right < parentRect.left - tolerance || rect.left > parentRect.right + tolerance || rect.bottom < parentRect.top - tolerance || rect.top > parentRect.bottom + tolerance) {
|
|
220
|
+
logger(el, "element is partially or totally hidden by an ancestor", {
|
|
221
|
+
rect,
|
|
222
|
+
parentRect
|
|
223
|
+
});
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
if (parentStyle.position === "fixed" || parentStyle.position === "sticky") {
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
if (parentStyle.position === "absolute") {
|
|
231
|
+
parent = parentUntilNonStatic(parent);
|
|
232
|
+
} else {
|
|
233
|
+
parent = parent.parentElement;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return {
|
|
237
|
+
left: Math.round(rect.left),
|
|
238
|
+
top: Math.round(rect.top),
|
|
239
|
+
width: Math.round(rect.width),
|
|
240
|
+
height: Math.round(rect.height),
|
|
241
|
+
zoom: rect.zoom
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
function validTextNodeContent(node) {
|
|
245
|
+
if (!node) {
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
if (node.nodeType !== Node.ELEMENT_NODE && node.nodeType !== Node.TEXT_NODE && node.nodeName !== "#text") {
|
|
249
|
+
return false;
|
|
250
|
+
}
|
|
251
|
+
const content = node.textContent || node.innerText;
|
|
252
|
+
if (content && !/^\s*$/.test(content)) {
|
|
253
|
+
return content.trim();
|
|
254
|
+
}
|
|
255
|
+
return false;
|
|
256
|
+
}
|
|
257
|
+
function getNodeAttributes(node, currentWindow) {
|
|
258
|
+
if (!node || !(node instanceof currentWindow.HTMLElement) || !node.attributes) {
|
|
259
|
+
return {};
|
|
260
|
+
}
|
|
261
|
+
const attributesList = Array.from(node.attributes).map((attr) => {
|
|
262
|
+
if (attr.name === "class") {
|
|
263
|
+
return [attr.name, `.${attr.value.split(" ").join(".")}`];
|
|
264
|
+
}
|
|
265
|
+
if (!attr.value) {
|
|
266
|
+
return [];
|
|
267
|
+
}
|
|
268
|
+
let value = attr.value;
|
|
269
|
+
if (value.startsWith("data:image")) {
|
|
270
|
+
value = "image";
|
|
271
|
+
}
|
|
272
|
+
if (value.length > MAX_VALUE_LENGTH) {
|
|
273
|
+
value = `${value.slice(0, MAX_VALUE_LENGTH)}...`;
|
|
274
|
+
}
|
|
275
|
+
return [attr.name, value];
|
|
276
|
+
});
|
|
277
|
+
return Object.fromEntries(attributesList);
|
|
278
|
+
}
|
|
279
|
+
let nodeHashCacheList = [];
|
|
280
|
+
if (typeof window !== "undefined") {
|
|
281
|
+
window.midsceneNodeHashCacheList = window.midsceneNodeHashCacheList || [];
|
|
282
|
+
nodeHashCacheList = window.midsceneNodeHashCacheList;
|
|
283
|
+
}
|
|
284
|
+
const hashMap = {};
|
|
285
|
+
function resetNodeHashCacheList() {
|
|
286
|
+
if (typeof window !== "undefined") {
|
|
287
|
+
nodeHashCacheList = window.midsceneNodeHashCacheList || [];
|
|
288
|
+
window.midsceneNodeHashCacheList = [];
|
|
289
|
+
} else {
|
|
290
|
+
nodeHashCacheList = [];
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
function midsceneGenerateHash(node, content, rect) {
|
|
294
|
+
if (node && nodeHashCacheList.find((item) => item.node === node)) {
|
|
295
|
+
return nodeHashCacheList.find((item) => item.node === node)?.id || "";
|
|
296
|
+
}
|
|
297
|
+
const slicedHash = generateHashId(rect, content);
|
|
298
|
+
if (node && typeof window !== "undefined") {
|
|
299
|
+
window.midsceneNodeHashCacheList.push({ node, id: slicedHash });
|
|
300
|
+
}
|
|
301
|
+
return slicedHash;
|
|
302
|
+
}
|
|
303
|
+
function generateId(numberId) {
|
|
304
|
+
return `${numberId}`;
|
|
305
|
+
}
|
|
306
|
+
function setGenerateHashOnWindow() {
|
|
307
|
+
if (typeof window !== "undefined") {
|
|
308
|
+
window.midsceneGenerateHash = midsceneGenerateHash;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
function setMidsceneVisibleRectOnWindow() {
|
|
312
|
+
if (typeof window !== "undefined") {
|
|
313
|
+
window.midsceneVisibleRect = visibleRect;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
function setExtractTextWithPositionOnWindow() {
|
|
317
|
+
if (typeof window !== "undefined") {
|
|
318
|
+
window.extractTextWithPosition = extractTextWithPosition;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
function getTopDocument() {
|
|
322
|
+
const container = document.body || document;
|
|
323
|
+
return container;
|
|
324
|
+
}
|
|
325
|
+
export {
|
|
326
|
+
generateId,
|
|
327
|
+
getDebugMode,
|
|
328
|
+
getNodeAttributes,
|
|
329
|
+
getPseudoElementContent,
|
|
330
|
+
getRect,
|
|
331
|
+
getTopDocument,
|
|
332
|
+
hasOverflowY,
|
|
333
|
+
logger,
|
|
334
|
+
midsceneGenerateHash,
|
|
335
|
+
overlappedRect,
|
|
336
|
+
resetNodeHashCacheList,
|
|
337
|
+
setDataForNode,
|
|
338
|
+
setDebugMode,
|
|
339
|
+
setExtractTextWithPositionOnWindow,
|
|
340
|
+
setGenerateHashOnWindow,
|
|
341
|
+
setMidsceneVisibleRectOnWindow,
|
|
342
|
+
validTextNodeContent,
|
|
343
|
+
visibleRect
|
|
344
|
+
};
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CONTAINER_MINI_HEIGHT,
|
|
3
|
+
CONTAINER_MINI_WIDTH,
|
|
4
|
+
NodeType
|
|
5
|
+
} from "../constants";
|
|
6
|
+
import {
|
|
7
|
+
isButtonElement,
|
|
8
|
+
isContainerElement,
|
|
9
|
+
isFormElement,
|
|
10
|
+
isImgElement,
|
|
11
|
+
isTextElement
|
|
12
|
+
} from "./dom-util";
|
|
13
|
+
import { descriptionOfTree } from "./tree";
|
|
14
|
+
import {
|
|
15
|
+
getNodeAttributes,
|
|
16
|
+
getPseudoElementContent,
|
|
17
|
+
getRect,
|
|
18
|
+
getTopDocument,
|
|
19
|
+
logger,
|
|
20
|
+
midsceneGenerateHash,
|
|
21
|
+
resetNodeHashCacheList,
|
|
22
|
+
setDataForNode,
|
|
23
|
+
setDebugMode,
|
|
24
|
+
visibleRect
|
|
25
|
+
} from "./util";
|
|
26
|
+
let indexId = 0;
|
|
27
|
+
function tagNameOfNode(node) {
|
|
28
|
+
let tagName = "";
|
|
29
|
+
if (node instanceof HTMLElement) {
|
|
30
|
+
tagName = node.tagName.toLowerCase();
|
|
31
|
+
}
|
|
32
|
+
const parentElement = node.parentElement;
|
|
33
|
+
if (parentElement && parentElement instanceof HTMLElement) {
|
|
34
|
+
tagName = parentElement.tagName.toLowerCase();
|
|
35
|
+
}
|
|
36
|
+
return tagName ? `<${tagName}>` : "";
|
|
37
|
+
}
|
|
38
|
+
function collectElementInfo(node, currentWindow, currentDocument, baseZoom = 1, basePoint = { left: 0, top: 0 }) {
|
|
39
|
+
const rect = visibleRect(node, currentWindow, currentDocument, baseZoom);
|
|
40
|
+
if (!rect || rect.width < CONTAINER_MINI_WIDTH || rect.height < CONTAINER_MINI_HEIGHT) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
if (basePoint.left !== 0 || basePoint.top !== 0) {
|
|
44
|
+
rect.left += basePoint.left;
|
|
45
|
+
rect.top += basePoint.top;
|
|
46
|
+
}
|
|
47
|
+
if (rect.height >= window.innerHeight && rect.width >= window.innerWidth) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
if (isFormElement(node)) {
|
|
51
|
+
const attributes = getNodeAttributes(node, currentWindow);
|
|
52
|
+
let valueContent = attributes.value || attributes.placeholder || node.textContent || "";
|
|
53
|
+
const nodeHashId = midsceneGenerateHash(node, valueContent, rect);
|
|
54
|
+
const selector = setDataForNode(node, nodeHashId, false, currentWindow);
|
|
55
|
+
const tagName = node.tagName.toLowerCase();
|
|
56
|
+
if (node.tagName.toLowerCase() === "select") {
|
|
57
|
+
const selectedOption = node.options[node.selectedIndex];
|
|
58
|
+
valueContent = selectedOption.textContent || "";
|
|
59
|
+
}
|
|
60
|
+
if ((node.tagName.toLowerCase() === "input" || node.tagName.toLowerCase() === "textarea") && node.value) {
|
|
61
|
+
valueContent = node.value;
|
|
62
|
+
}
|
|
63
|
+
const elementInfo = {
|
|
64
|
+
id: nodeHashId,
|
|
65
|
+
nodeHashId,
|
|
66
|
+
locator: selector,
|
|
67
|
+
nodeType: NodeType.FORM_ITEM,
|
|
68
|
+
indexId: indexId++,
|
|
69
|
+
attributes: {
|
|
70
|
+
...attributes,
|
|
71
|
+
htmlTagName: `<${tagName}>`,
|
|
72
|
+
nodeType: NodeType.FORM_ITEM
|
|
73
|
+
},
|
|
74
|
+
content: valueContent.trim(),
|
|
75
|
+
rect,
|
|
76
|
+
center: [
|
|
77
|
+
Math.round(rect.left + rect.width / 2),
|
|
78
|
+
Math.round(rect.top + rect.height / 2)
|
|
79
|
+
],
|
|
80
|
+
zoom: rect.zoom,
|
|
81
|
+
screenWidth: currentWindow.innerWidth,
|
|
82
|
+
screenHeight: currentWindow.innerHeight
|
|
83
|
+
};
|
|
84
|
+
return elementInfo;
|
|
85
|
+
}
|
|
86
|
+
if (isButtonElement(node)) {
|
|
87
|
+
const attributes = getNodeAttributes(node, currentWindow);
|
|
88
|
+
const pseudo = getPseudoElementContent(node, currentWindow);
|
|
89
|
+
const content = node.innerText || pseudo.before || pseudo.after || "";
|
|
90
|
+
const nodeHashId = midsceneGenerateHash(node, content, rect);
|
|
91
|
+
const selector = setDataForNode(node, nodeHashId, false, currentWindow);
|
|
92
|
+
const elementInfo = {
|
|
93
|
+
id: nodeHashId,
|
|
94
|
+
indexId: indexId++,
|
|
95
|
+
nodeHashId,
|
|
96
|
+
nodeType: NodeType.BUTTON,
|
|
97
|
+
locator: selector,
|
|
98
|
+
attributes: {
|
|
99
|
+
...attributes,
|
|
100
|
+
htmlTagName: tagNameOfNode(node),
|
|
101
|
+
nodeType: NodeType.BUTTON
|
|
102
|
+
},
|
|
103
|
+
content,
|
|
104
|
+
rect,
|
|
105
|
+
center: [
|
|
106
|
+
Math.round(rect.left + rect.width / 2),
|
|
107
|
+
Math.round(rect.top + rect.height / 2)
|
|
108
|
+
],
|
|
109
|
+
zoom: rect.zoom,
|
|
110
|
+
screenWidth: currentWindow.innerWidth,
|
|
111
|
+
screenHeight: currentWindow.innerHeight
|
|
112
|
+
};
|
|
113
|
+
return elementInfo;
|
|
114
|
+
}
|
|
115
|
+
if (isImgElement(node)) {
|
|
116
|
+
const attributes = getNodeAttributes(node, currentWindow);
|
|
117
|
+
const nodeHashId = midsceneGenerateHash(node, "", rect);
|
|
118
|
+
const selector = setDataForNode(node, nodeHashId, false, currentWindow);
|
|
119
|
+
const elementInfo = {
|
|
120
|
+
id: nodeHashId,
|
|
121
|
+
indexId: indexId++,
|
|
122
|
+
nodeHashId,
|
|
123
|
+
locator: selector,
|
|
124
|
+
attributes: {
|
|
125
|
+
...attributes,
|
|
126
|
+
...node.nodeName.toLowerCase() === "svg" ? {
|
|
127
|
+
svgContent: "true"
|
|
128
|
+
} : {},
|
|
129
|
+
nodeType: NodeType.IMG,
|
|
130
|
+
htmlTagName: tagNameOfNode(node)
|
|
131
|
+
},
|
|
132
|
+
nodeType: NodeType.IMG,
|
|
133
|
+
content: "",
|
|
134
|
+
rect,
|
|
135
|
+
center: [
|
|
136
|
+
Math.round(rect.left + rect.width / 2),
|
|
137
|
+
Math.round(rect.top + rect.height / 2)
|
|
138
|
+
],
|
|
139
|
+
zoom: rect.zoom,
|
|
140
|
+
screenWidth: currentWindow.innerWidth,
|
|
141
|
+
screenHeight: currentWindow.innerHeight
|
|
142
|
+
};
|
|
143
|
+
return elementInfo;
|
|
144
|
+
}
|
|
145
|
+
if (isTextElement(node)) {
|
|
146
|
+
const text = node.textContent?.trim().replace(/\n+/g, " ");
|
|
147
|
+
if (!text) {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
const attributes = getNodeAttributes(node, currentWindow);
|
|
151
|
+
const attributeKeys = Object.keys(attributes);
|
|
152
|
+
if (!text.trim() && attributeKeys.length === 0) {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
const nodeHashId = midsceneGenerateHash(node, text, rect);
|
|
156
|
+
const selector = setDataForNode(node, nodeHashId, true, currentWindow);
|
|
157
|
+
const elementInfo = {
|
|
158
|
+
id: nodeHashId,
|
|
159
|
+
indexId: indexId++,
|
|
160
|
+
nodeHashId,
|
|
161
|
+
nodeType: NodeType.TEXT,
|
|
162
|
+
locator: selector,
|
|
163
|
+
attributes: {
|
|
164
|
+
...attributes,
|
|
165
|
+
nodeType: NodeType.TEXT,
|
|
166
|
+
htmlTagName: tagNameOfNode(node)
|
|
167
|
+
},
|
|
168
|
+
center: [
|
|
169
|
+
Math.round(rect.left + rect.width / 2),
|
|
170
|
+
Math.round(rect.top + rect.height / 2)
|
|
171
|
+
],
|
|
172
|
+
// attributes,
|
|
173
|
+
content: text,
|
|
174
|
+
rect,
|
|
175
|
+
zoom: rect.zoom,
|
|
176
|
+
screenWidth: currentWindow.innerWidth,
|
|
177
|
+
screenHeight: currentWindow.innerHeight
|
|
178
|
+
};
|
|
179
|
+
return elementInfo;
|
|
180
|
+
}
|
|
181
|
+
if (isContainerElement(node)) {
|
|
182
|
+
const attributes = getNodeAttributes(node, currentWindow);
|
|
183
|
+
const nodeHashId = midsceneGenerateHash(node, "", rect);
|
|
184
|
+
const selector = setDataForNode(node, nodeHashId, false, currentWindow);
|
|
185
|
+
const elementInfo = {
|
|
186
|
+
id: nodeHashId,
|
|
187
|
+
nodeHashId,
|
|
188
|
+
indexId: indexId++,
|
|
189
|
+
nodeType: NodeType.CONTAINER,
|
|
190
|
+
locator: selector,
|
|
191
|
+
attributes: {
|
|
192
|
+
...attributes,
|
|
193
|
+
nodeType: NodeType.CONTAINER,
|
|
194
|
+
htmlTagName: tagNameOfNode(node)
|
|
195
|
+
},
|
|
196
|
+
content: "",
|
|
197
|
+
rect,
|
|
198
|
+
center: [
|
|
199
|
+
Math.round(rect.left + rect.width / 2),
|
|
200
|
+
Math.round(rect.top + rect.height / 2)
|
|
201
|
+
],
|
|
202
|
+
zoom: rect.zoom,
|
|
203
|
+
screenWidth: currentWindow.innerWidth,
|
|
204
|
+
screenHeight: currentWindow.innerHeight
|
|
205
|
+
};
|
|
206
|
+
return elementInfo;
|
|
207
|
+
}
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
function extractTextWithPosition(initNode, debugMode = false) {
|
|
211
|
+
const elementNode = extractTreeNode(initNode, debugMode);
|
|
212
|
+
const elementInfoArray = [];
|
|
213
|
+
function dfsTopChildren(node) {
|
|
214
|
+
if (node.node) {
|
|
215
|
+
elementInfoArray.push(node.node);
|
|
216
|
+
}
|
|
217
|
+
for (let i = 0; i < node.children.length; i++) {
|
|
218
|
+
dfsTopChildren(node.children[i]);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
dfsTopChildren({ children: elementNode.children, node: elementNode.node });
|
|
222
|
+
return elementInfoArray;
|
|
223
|
+
}
|
|
224
|
+
function extractTreeNodeAsString(initNode, debugMode = false) {
|
|
225
|
+
const elementNode = extractTreeNode(initNode, debugMode);
|
|
226
|
+
return descriptionOfTree(elementNode);
|
|
227
|
+
}
|
|
228
|
+
function extractTreeNode(initNode, debugMode = false) {
|
|
229
|
+
setDebugMode(debugMode);
|
|
230
|
+
resetNodeHashCacheList();
|
|
231
|
+
indexId = 0;
|
|
232
|
+
const topDocument = getTopDocument();
|
|
233
|
+
const startNode = initNode || topDocument;
|
|
234
|
+
const topChildren = [];
|
|
235
|
+
function dfs(node, currentWindow, currentDocument, baseZoom = 1, basePoint = { left: 0, top: 0 }) {
|
|
236
|
+
if (!node) {
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
if (node.nodeType && node.nodeType === 10) {
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
const elementInfo = collectElementInfo(
|
|
243
|
+
node,
|
|
244
|
+
currentWindow,
|
|
245
|
+
currentDocument,
|
|
246
|
+
baseZoom,
|
|
247
|
+
basePoint
|
|
248
|
+
);
|
|
249
|
+
if (node instanceof currentWindow.HTMLIFrameElement) {
|
|
250
|
+
if (node.contentWindow && node.contentWindow) {
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
const nodeInfo = {
|
|
255
|
+
node: elementInfo,
|
|
256
|
+
children: []
|
|
257
|
+
};
|
|
258
|
+
if (elementInfo?.nodeType === NodeType.BUTTON || elementInfo?.nodeType === NodeType.IMG || elementInfo?.nodeType === NodeType.TEXT || elementInfo?.nodeType === NodeType.FORM_ITEM || elementInfo?.nodeType === NodeType.CONTAINER) {
|
|
259
|
+
return nodeInfo;
|
|
260
|
+
}
|
|
261
|
+
const rect = getRect(node, baseZoom, currentWindow);
|
|
262
|
+
for (let i = 0; i < node.childNodes.length; i++) {
|
|
263
|
+
logger("will dfs", node.childNodes[i]);
|
|
264
|
+
const childNodeInfo = dfs(
|
|
265
|
+
node.childNodes[i],
|
|
266
|
+
currentWindow,
|
|
267
|
+
currentDocument,
|
|
268
|
+
rect.zoom,
|
|
269
|
+
basePoint
|
|
270
|
+
);
|
|
271
|
+
if (childNodeInfo) {
|
|
272
|
+
nodeInfo.children.push(childNodeInfo);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return nodeInfo;
|
|
276
|
+
}
|
|
277
|
+
const rootNodeInfo = dfs(startNode, window, document, 1, {
|
|
278
|
+
left: 0,
|
|
279
|
+
top: 0
|
|
280
|
+
});
|
|
281
|
+
if (rootNodeInfo) {
|
|
282
|
+
topChildren.push(rootNodeInfo);
|
|
283
|
+
}
|
|
284
|
+
if (startNode === topDocument) {
|
|
285
|
+
const iframes = document.querySelectorAll("iframe");
|
|
286
|
+
for (let i = 0; i < iframes.length; i++) {
|
|
287
|
+
const iframe = iframes[i];
|
|
288
|
+
if (iframe.contentDocument && iframe.contentWindow) {
|
|
289
|
+
const iframeInfo = collectElementInfo(iframe, window, document, 1);
|
|
290
|
+
if (iframeInfo) {
|
|
291
|
+
const iframeChildren = dfs(
|
|
292
|
+
iframe.contentDocument.body,
|
|
293
|
+
iframe.contentWindow,
|
|
294
|
+
iframe.contentDocument,
|
|
295
|
+
1,
|
|
296
|
+
{
|
|
297
|
+
left: iframeInfo.rect.left,
|
|
298
|
+
top: iframeInfo.rect.top
|
|
299
|
+
}
|
|
300
|
+
);
|
|
301
|
+
if (iframeChildren) {
|
|
302
|
+
topChildren.push(iframeChildren);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return {
|
|
309
|
+
node: null,
|
|
310
|
+
children: topChildren
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
export {
|
|
314
|
+
extractTextWithPosition,
|
|
315
|
+
extractTreeNode,
|
|
316
|
+
extractTreeNodeAsString
|
|
317
|
+
};
|