@sqaitech/shared 0.5.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 +9 -0
- package/dist/es/baseDB.mjs +109 -0
- package/dist/es/build/copy-static.mjs +29 -0
- package/dist/es/common.mjs +37 -0
- package/dist/es/constants/example-code.mjs +202 -0
- package/dist/es/constants/index.mjs +24 -0
- package/dist/es/env/basic.mjs +6 -0
- package/dist/es/env/constants.mjs +97 -0
- package/dist/es/env/decide-model-config.mjs +172 -0
- package/dist/es/env/global-config-manager.mjs +91 -0
- package/dist/es/env/helper.mjs +48 -0
- package/dist/es/env/index.mjs +5 -0
- package/dist/es/env/init-debug.mjs +18 -0
- package/dist/es/env/model-config-manager.mjs +99 -0
- package/dist/es/env/parse.mjs +69 -0
- package/dist/es/env/types.mjs +253 -0
- package/dist/es/env/utils.mjs +18 -0
- package/dist/es/extractor/constants.mjs +2 -0
- package/dist/es/extractor/debug.mjs +6 -0
- package/dist/es/extractor/dom-util.mjs +92 -0
- package/dist/es/extractor/index.mjs +6 -0
- package/dist/es/extractor/locator.mjs +95 -0
- package/dist/es/extractor/tree.mjs +81 -0
- package/dist/es/extractor/util.mjs +244 -0
- package/dist/es/extractor/web-extractor.mjs +310 -0
- package/dist/es/img/box-select.mjs +184 -0
- package/dist/es/img/draw-box.mjs +42 -0
- package/dist/es/img/get-jimp.mjs +10 -0
- package/dist/es/img/get-photon.mjs +19 -0
- package/dist/es/img/get-sharp.mjs +11 -0
- package/dist/es/img/index.mjs +5 -0
- package/dist/es/img/info.mjs +32 -0
- package/dist/es/img/transform.mjs +192 -0
- package/dist/es/index.mjs +3 -0
- package/dist/es/logger.mjs +61 -0
- package/dist/es/node/fs.mjs +44 -0
- package/dist/es/node/index.mjs +1 -0
- package/dist/es/polyfills/async-hooks.mjs +2 -0
- package/dist/es/polyfills/index.mjs +1 -0
- package/dist/es/types/index.mjs +3 -0
- package/dist/es/us-keyboard-layout.mjs +1414 -0
- package/dist/es/us-keyboard-layout.mjs.LICENSE.txt +5 -0
- package/dist/es/utils.mjs +66 -0
- package/dist/lib/baseDB.js +149 -0
- package/dist/lib/build/copy-static.js +77 -0
- package/dist/lib/common.js +93 -0
- package/dist/lib/constants/example-code.js +239 -0
- package/dist/lib/constants/index.js +100 -0
- package/dist/lib/env/basic.js +40 -0
- package/dist/lib/env/constants.js +143 -0
- package/dist/lib/env/decide-model-config.js +212 -0
- package/dist/lib/env/global-config-manager.js +125 -0
- package/dist/lib/env/helper.js +89 -0
- package/dist/lib/env/index.js +94 -0
- package/dist/lib/env/init-debug.js +52 -0
- package/dist/lib/env/model-config-manager.js +133 -0
- package/dist/lib/env/parse.js +106 -0
- package/dist/lib/env/types.js +635 -0
- package/dist/lib/env/utils.js +61 -0
- package/dist/lib/extractor/constants.js +42 -0
- package/dist/lib/extractor/debug.js +12 -0
- package/dist/lib/extractor/dom-util.js +150 -0
- package/dist/lib/extractor/index.js +88 -0
- package/dist/lib/extractor/locator.js +141 -0
- package/dist/lib/extractor/tree.js +127 -0
- package/dist/lib/extractor/util.js +335 -0
- package/dist/lib/extractor/web-extractor.js +356 -0
- package/dist/lib/img/box-select.js +232 -0
- package/dist/lib/img/draw-box.js +89 -0
- package/dist/lib/img/get-jimp.js +72 -0
- package/dist/lib/img/get-photon.js +76 -0
- package/dist/lib/img/get-sharp.js +63 -0
- package/dist/lib/img/index.js +102 -0
- package/dist/lib/img/info.js +86 -0
- package/dist/lib/img/transform.js +279 -0
- package/dist/lib/index.js +43 -0
- package/dist/lib/logger.js +114 -0
- package/dist/lib/node/fs.js +97 -0
- package/dist/lib/node/index.js +60 -0
- package/dist/lib/polyfills/async-hooks.js +36 -0
- package/dist/lib/polyfills/index.js +60 -0
- package/dist/lib/types/index.js +37 -0
- package/dist/lib/us-keyboard-layout.js +1457 -0
- package/dist/lib/us-keyboard-layout.js.LICENSE.txt +5 -0
- package/dist/lib/utils.js +136 -0
- package/dist/types/baseDB.d.ts +25 -0
- package/dist/types/build/copy-static.d.ts +31 -0
- package/dist/types/common.d.ts +12 -0
- package/dist/types/constants/example-code.d.ts +2 -0
- package/dist/types/constants/index.d.ts +22 -0
- package/dist/types/env/basic.d.ts +6 -0
- package/dist/types/env/constants.d.ts +40 -0
- package/dist/types/env/decide-model-config.d.ts +14 -0
- package/dist/types/env/global-config-manager.d.ts +32 -0
- package/dist/types/env/helper.d.ts +6 -0
- package/dist/types/env/index.d.ts +4 -0
- package/dist/types/env/init-debug.d.ts +1 -0
- package/dist/types/env/model-config-manager.d.ts +24 -0
- package/dist/types/env/parse.d.ts +12 -0
- package/dist/types/env/types.d.ts +294 -0
- package/dist/types/env/utils.d.ts +7 -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 +26 -0
- package/dist/types/extractor/index.d.ts +33 -0
- package/dist/types/extractor/locator.d.ts +7 -0
- package/dist/types/extractor/tree.d.ts +9 -0
- package/dist/types/extractor/util.d.ts +43 -0
- package/dist/types/extractor/web-extractor.d.ts +19 -0
- package/dist/types/img/box-select.d.ts +25 -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/get-photon.d.ts +8 -0
- package/dist/types/img/get-sharp.d.ts +3 -0
- package/dist/types/img/index.d.ts +4 -0
- package/dist/types/img/info.d.ts +29 -0
- package/dist/types/img/transform.d.ts +88 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/logger.d.ts +4 -0
- package/dist/types/node/fs.d.ts +15 -0
- package/dist/types/node/index.d.ts +1 -0
- package/dist/types/polyfills/async-hooks.d.ts +6 -0
- package/dist/types/polyfills/index.d.ts +4 -0
- package/dist/types/types/index.d.ts +34 -0
- package/dist/types/us-keyboard-layout.d.ts +32 -0
- package/dist/types/utils.d.ts +22 -0
- package/package.json +106 -0
- package/src/baseDB.ts +158 -0
- package/src/build/copy-static.ts +62 -0
- package/src/common.ts +67 -0
- package/src/constants/example-code.ts +202 -0
- package/src/constants/index.ts +30 -0
- package/src/env/basic.ts +12 -0
- package/src/env/constants.ts +291 -0
- package/src/env/decide-model-config.ts +319 -0
- package/src/env/global-config-manager.ts +174 -0
- package/src/env/helper.ts +79 -0
- package/src/env/index.ts +4 -0
- package/src/env/init-debug.ts +29 -0
- package/src/env/model-config-manager.ts +145 -0
- package/src/env/parse.ts +131 -0
- package/src/env/types.ts +560 -0
- package/src/env/utils.ts +39 -0
- package/src/extractor/constants.ts +5 -0
- package/src/extractor/debug.ts +10 -0
- package/src/extractor/dom-util.ts +152 -0
- package/src/extractor/index.ts +50 -0
- package/src/extractor/locator.ts +179 -0
- package/src/extractor/tree.ts +179 -0
- package/src/extractor/util.ts +468 -0
- package/src/extractor/web-extractor.ts +481 -0
- package/src/img/box-select.ts +346 -0
- package/src/img/draw-box.ts +60 -0
- package/src/img/get-jimp.ts +12 -0
- package/src/img/get-photon.ts +48 -0
- package/src/img/get-sharp.ts +18 -0
- package/src/img/index.ts +24 -0
- package/src/img/info.ts +79 -0
- package/src/img/jimp.d.ts +4 -0
- package/src/img/transform.ts +396 -0
- package/src/index.ts +6 -0
- package/src/logger.ts +93 -0
- package/src/node/fs.ts +84 -0
- package/src/node/index.ts +1 -0
- package/src/polyfills/async-hooks.ts +6 -0
- package/src/polyfills/index.ts +4 -0
- package/src/types/index.ts +47 -0
- package/src/us-keyboard-layout.ts +723 -0
- package/src/utils.ts +127 -0
|
@@ -0,0 +1,481 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CONTAINER_MINI_HEIGHT,
|
|
3
|
+
CONTAINER_MINI_WIDTH,
|
|
4
|
+
NodeType,
|
|
5
|
+
} from '../constants/index';
|
|
6
|
+
import type { WebElementInfo } from '../types';
|
|
7
|
+
import type { Point } from '../types';
|
|
8
|
+
import {
|
|
9
|
+
isAElement,
|
|
10
|
+
isButtonElement,
|
|
11
|
+
isContainerElement,
|
|
12
|
+
isFormElement,
|
|
13
|
+
isImgElement,
|
|
14
|
+
isTextElement,
|
|
15
|
+
} from './dom-util';
|
|
16
|
+
import { descriptionOfTree } from './tree';
|
|
17
|
+
import {
|
|
18
|
+
elementRect,
|
|
19
|
+
getNodeAttributes,
|
|
20
|
+
getPseudoElementContent,
|
|
21
|
+
getRect,
|
|
22
|
+
getTopDocument,
|
|
23
|
+
logger,
|
|
24
|
+
midsceneGenerateHash,
|
|
25
|
+
setDebugMode,
|
|
26
|
+
} from './util';
|
|
27
|
+
|
|
28
|
+
let indexId = 0;
|
|
29
|
+
|
|
30
|
+
function tagNameOfNode(node: globalThis.Node): string {
|
|
31
|
+
let tagName = '';
|
|
32
|
+
if (node instanceof HTMLElement) {
|
|
33
|
+
tagName = node.tagName?.toLowerCase();
|
|
34
|
+
} else {
|
|
35
|
+
const parentElement = node.parentElement;
|
|
36
|
+
if (parentElement && parentElement instanceof HTMLElement) {
|
|
37
|
+
tagName = parentElement.tagName?.toLowerCase();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return tagName ? `<${tagName}>` : '';
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function collectElementInfo(
|
|
45
|
+
node: Node,
|
|
46
|
+
currentWindow: typeof window,
|
|
47
|
+
currentDocument: typeof document,
|
|
48
|
+
baseZoom = 1,
|
|
49
|
+
basePoint: Point = { left: 0, top: 0 },
|
|
50
|
+
isContainer = false, // if true, the element will be considered as a container
|
|
51
|
+
): WebElementInfo | null | any {
|
|
52
|
+
const rect = elementRect(node, currentWindow, currentDocument, baseZoom);
|
|
53
|
+
|
|
54
|
+
if (!rect) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (
|
|
59
|
+
rect.width < CONTAINER_MINI_WIDTH ||
|
|
60
|
+
rect.height < CONTAINER_MINI_HEIGHT
|
|
61
|
+
) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (basePoint.left !== 0 || basePoint.top !== 0) {
|
|
66
|
+
rect.left += basePoint.left;
|
|
67
|
+
rect.top += basePoint.top;
|
|
68
|
+
}
|
|
69
|
+
// Skip elements that cover the entire viewport, as they are likely background containers
|
|
70
|
+
// rather than meaningful interactive elements
|
|
71
|
+
if (rect.height >= window.innerHeight && rect.width >= window.innerWidth) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (isFormElement(node)) {
|
|
76
|
+
const attributes = getNodeAttributes(node, currentWindow);
|
|
77
|
+
let valueContent =
|
|
78
|
+
attributes.value || attributes.placeholder || node.textContent || '';
|
|
79
|
+
const nodeHashId = midsceneGenerateHash(node, valueContent, rect);
|
|
80
|
+
const tagName = (node as HTMLElement).tagName.toLowerCase();
|
|
81
|
+
if ((node as HTMLElement).tagName.toLowerCase() === 'select') {
|
|
82
|
+
// Get the selected option using the selectedIndex property
|
|
83
|
+
const selectedOption = (node as HTMLSelectElement).options[
|
|
84
|
+
(node as HTMLSelectElement).selectedIndex
|
|
85
|
+
];
|
|
86
|
+
|
|
87
|
+
// Retrieve the text content of the selected option
|
|
88
|
+
valueContent = selectedOption?.textContent || '';
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (
|
|
92
|
+
((node as HTMLElement).tagName.toLowerCase() === 'input' ||
|
|
93
|
+
(node as HTMLElement).tagName.toLowerCase() === 'textarea') &&
|
|
94
|
+
(node as HTMLInputElement).value
|
|
95
|
+
) {
|
|
96
|
+
valueContent = (node as HTMLInputElement).value;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const elementInfo: WebElementInfo = {
|
|
100
|
+
id: nodeHashId,
|
|
101
|
+
nodeHashId,
|
|
102
|
+
nodeType: NodeType.FORM_ITEM,
|
|
103
|
+
indexId: indexId++,
|
|
104
|
+
attributes: {
|
|
105
|
+
...attributes,
|
|
106
|
+
htmlTagName: `<${tagName}>`,
|
|
107
|
+
nodeType: NodeType.FORM_ITEM,
|
|
108
|
+
},
|
|
109
|
+
content: valueContent.trim(),
|
|
110
|
+
rect,
|
|
111
|
+
center: [
|
|
112
|
+
Math.round(rect.left + rect.width / 2),
|
|
113
|
+
Math.round(rect.top + rect.height / 2),
|
|
114
|
+
],
|
|
115
|
+
zoom: rect.zoom,
|
|
116
|
+
isVisible: rect.isVisible,
|
|
117
|
+
};
|
|
118
|
+
return elementInfo;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (isButtonElement(node)) {
|
|
122
|
+
const rect = mergeElementAndChildrenRects(
|
|
123
|
+
node,
|
|
124
|
+
currentWindow,
|
|
125
|
+
currentDocument,
|
|
126
|
+
baseZoom,
|
|
127
|
+
);
|
|
128
|
+
if (!rect) {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
const attributes = getNodeAttributes(node, currentWindow);
|
|
132
|
+
const pseudo = getPseudoElementContent(node, currentWindow);
|
|
133
|
+
const content = node.innerText || pseudo.before || pseudo.after || '';
|
|
134
|
+
const nodeHashId = midsceneGenerateHash(node, content, rect);
|
|
135
|
+
const elementInfo: WebElementInfo = {
|
|
136
|
+
id: nodeHashId,
|
|
137
|
+
indexId: indexId++,
|
|
138
|
+
nodeHashId,
|
|
139
|
+
nodeType: NodeType.BUTTON,
|
|
140
|
+
attributes: {
|
|
141
|
+
...attributes,
|
|
142
|
+
htmlTagName: tagNameOfNode(node),
|
|
143
|
+
nodeType: NodeType.BUTTON,
|
|
144
|
+
},
|
|
145
|
+
content,
|
|
146
|
+
rect,
|
|
147
|
+
center: [
|
|
148
|
+
Math.round(rect.left + rect.width / 2),
|
|
149
|
+
Math.round(rect.top + rect.height / 2),
|
|
150
|
+
],
|
|
151
|
+
zoom: rect.zoom,
|
|
152
|
+
isVisible: rect.isVisible,
|
|
153
|
+
};
|
|
154
|
+
return elementInfo;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (isImgElement(node)) {
|
|
158
|
+
const attributes = getNodeAttributes(node, currentWindow);
|
|
159
|
+
const nodeHashId = midsceneGenerateHash(node, '', rect);
|
|
160
|
+
const elementInfo: WebElementInfo = {
|
|
161
|
+
id: nodeHashId,
|
|
162
|
+
indexId: indexId++,
|
|
163
|
+
nodeHashId,
|
|
164
|
+
attributes: {
|
|
165
|
+
...attributes,
|
|
166
|
+
...(node.nodeName?.toLowerCase() === 'svg'
|
|
167
|
+
? {
|
|
168
|
+
svgContent: 'true',
|
|
169
|
+
}
|
|
170
|
+
: {}),
|
|
171
|
+
nodeType: NodeType.IMG,
|
|
172
|
+
htmlTagName: tagNameOfNode(node),
|
|
173
|
+
},
|
|
174
|
+
nodeType: NodeType.IMG,
|
|
175
|
+
content: '',
|
|
176
|
+
rect,
|
|
177
|
+
center: [
|
|
178
|
+
Math.round(rect.left + rect.width / 2),
|
|
179
|
+
Math.round(rect.top + rect.height / 2),
|
|
180
|
+
],
|
|
181
|
+
zoom: rect.zoom,
|
|
182
|
+
isVisible: rect.isVisible,
|
|
183
|
+
};
|
|
184
|
+
return elementInfo;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (isTextElement(node)) {
|
|
188
|
+
const text = node.textContent?.trim().replace(/\n+/g, ' ');
|
|
189
|
+
if (!text) {
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
const attributes = getNodeAttributes(node, currentWindow);
|
|
193
|
+
const attributeKeys = Object.keys(attributes);
|
|
194
|
+
if (!text.trim() && attributeKeys.length === 0) {
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
const nodeHashId = midsceneGenerateHash(node, text, rect);
|
|
198
|
+
const elementInfo: WebElementInfo = {
|
|
199
|
+
id: nodeHashId,
|
|
200
|
+
indexId: indexId++,
|
|
201
|
+
nodeHashId,
|
|
202
|
+
nodeType: NodeType.TEXT,
|
|
203
|
+
attributes: {
|
|
204
|
+
...attributes,
|
|
205
|
+
nodeType: NodeType.TEXT,
|
|
206
|
+
htmlTagName: tagNameOfNode(node),
|
|
207
|
+
},
|
|
208
|
+
center: [
|
|
209
|
+
Math.round(rect.left + rect.width / 2),
|
|
210
|
+
Math.round(rect.top + rect.height / 2),
|
|
211
|
+
],
|
|
212
|
+
content: text,
|
|
213
|
+
rect,
|
|
214
|
+
zoom: rect.zoom,
|
|
215
|
+
isVisible: rect.isVisible,
|
|
216
|
+
};
|
|
217
|
+
return elementInfo;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (isAElement(node)) {
|
|
221
|
+
const attributes = getNodeAttributes(node, currentWindow);
|
|
222
|
+
const pseudo = getPseudoElementContent(node, currentWindow);
|
|
223
|
+
const content = node.innerText || pseudo.before || pseudo.after || '';
|
|
224
|
+
const nodeHashId = midsceneGenerateHash(node, content, rect);
|
|
225
|
+
const elementInfo: WebElementInfo = {
|
|
226
|
+
id: nodeHashId,
|
|
227
|
+
indexId: indexId++,
|
|
228
|
+
nodeHashId,
|
|
229
|
+
nodeType: NodeType.A,
|
|
230
|
+
attributes: {
|
|
231
|
+
...attributes,
|
|
232
|
+
htmlTagName: tagNameOfNode(node),
|
|
233
|
+
nodeType: NodeType.A,
|
|
234
|
+
},
|
|
235
|
+
content,
|
|
236
|
+
rect,
|
|
237
|
+
center: [
|
|
238
|
+
Math.round(rect.left + rect.width / 2),
|
|
239
|
+
Math.round(rect.top + rect.height / 2),
|
|
240
|
+
],
|
|
241
|
+
zoom: rect.zoom,
|
|
242
|
+
isVisible: rect.isVisible,
|
|
243
|
+
};
|
|
244
|
+
return elementInfo;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// else, consider as a container
|
|
248
|
+
if (isContainerElement(node) || isContainer) {
|
|
249
|
+
const attributes = getNodeAttributes(node, currentWindow);
|
|
250
|
+
const nodeHashId = midsceneGenerateHash(node, '', rect);
|
|
251
|
+
const elementInfo: WebElementInfo = {
|
|
252
|
+
id: nodeHashId,
|
|
253
|
+
nodeHashId,
|
|
254
|
+
indexId: indexId++,
|
|
255
|
+
nodeType: NodeType.CONTAINER,
|
|
256
|
+
attributes: {
|
|
257
|
+
...attributes,
|
|
258
|
+
nodeType: NodeType.CONTAINER,
|
|
259
|
+
htmlTagName: tagNameOfNode(node),
|
|
260
|
+
},
|
|
261
|
+
content: '',
|
|
262
|
+
rect,
|
|
263
|
+
center: [
|
|
264
|
+
Math.round(rect.left + rect.width / 2),
|
|
265
|
+
Math.round(rect.top + rect.height / 2),
|
|
266
|
+
],
|
|
267
|
+
zoom: rect.zoom,
|
|
268
|
+
isVisible: rect.isVisible,
|
|
269
|
+
};
|
|
270
|
+
return elementInfo;
|
|
271
|
+
}
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
interface WebElementNode {
|
|
276
|
+
node: WebElementInfo | null;
|
|
277
|
+
children: WebElementNode[];
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// @deprecated
|
|
281
|
+
export function extractTextWithPosition(
|
|
282
|
+
initNode: globalThis.Node,
|
|
283
|
+
debugMode = false,
|
|
284
|
+
): WebElementInfo[] {
|
|
285
|
+
const elementNode = extractTreeNode(initNode, debugMode);
|
|
286
|
+
|
|
287
|
+
// dfs topChildren
|
|
288
|
+
const elementInfoArray: WebElementInfo[] = [];
|
|
289
|
+
function dfsTopChildren(node: WebElementNode) {
|
|
290
|
+
if (node.node) {
|
|
291
|
+
elementInfoArray.push(node.node);
|
|
292
|
+
}
|
|
293
|
+
for (let i = 0; i < node.children.length; i++) {
|
|
294
|
+
dfsTopChildren(node.children[i]);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
dfsTopChildren({ children: elementNode.children, node: elementNode.node });
|
|
298
|
+
return elementInfoArray;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
export function extractTreeNodeAsString(
|
|
302
|
+
initNode: globalThis.Node,
|
|
303
|
+
visibleOnly = false,
|
|
304
|
+
debugMode = false,
|
|
305
|
+
): string {
|
|
306
|
+
const elementNode = extractTreeNode(initNode, debugMode);
|
|
307
|
+
return descriptionOfTree(elementNode, undefined, false, visibleOnly);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
export function extractTreeNode(
|
|
311
|
+
initNode: globalThis.Node,
|
|
312
|
+
debugMode = false,
|
|
313
|
+
): WebElementNode {
|
|
314
|
+
setDebugMode(debugMode);
|
|
315
|
+
indexId = 0;
|
|
316
|
+
|
|
317
|
+
const topDocument = getTopDocument();
|
|
318
|
+
const startNode = initNode || topDocument;
|
|
319
|
+
const topChildren: WebElementNode[] = [];
|
|
320
|
+
|
|
321
|
+
function dfs(
|
|
322
|
+
node: globalThis.Node,
|
|
323
|
+
currentWindow: typeof globalThis.window,
|
|
324
|
+
currentDocument: typeof globalThis.document,
|
|
325
|
+
baseZoom = 1,
|
|
326
|
+
basePoint: Point = { left: 0, top: 0 },
|
|
327
|
+
): WebElementNode | WebElementNode[] | null {
|
|
328
|
+
if (!node) {
|
|
329
|
+
return null;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (node.nodeType && node.nodeType === 10) {
|
|
333
|
+
// Doctype node
|
|
334
|
+
return null;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const elementInfo = collectElementInfo(
|
|
338
|
+
node,
|
|
339
|
+
currentWindow,
|
|
340
|
+
currentDocument,
|
|
341
|
+
baseZoom,
|
|
342
|
+
basePoint,
|
|
343
|
+
);
|
|
344
|
+
|
|
345
|
+
if (node instanceof currentWindow.HTMLIFrameElement) {
|
|
346
|
+
if (
|
|
347
|
+
(node as HTMLIFrameElement).contentWindow &&
|
|
348
|
+
(node as HTMLIFrameElement).contentWindow
|
|
349
|
+
) {
|
|
350
|
+
return null;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const nodeInfo: WebElementNode = {
|
|
355
|
+
node: elementInfo,
|
|
356
|
+
children: [],
|
|
357
|
+
};
|
|
358
|
+
// stop collecting if the node is a Button/Image/Text/FormItem/Container
|
|
359
|
+
if (
|
|
360
|
+
elementInfo?.nodeType === NodeType.BUTTON ||
|
|
361
|
+
elementInfo?.nodeType === NodeType.IMG ||
|
|
362
|
+
elementInfo?.nodeType === NodeType.TEXT ||
|
|
363
|
+
elementInfo?.nodeType === NodeType.FORM_ITEM ||
|
|
364
|
+
elementInfo?.nodeType === NodeType.CONTAINER // TODO: need return the container node?
|
|
365
|
+
) {
|
|
366
|
+
return nodeInfo;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const rect = getRect(node, baseZoom, currentWindow);
|
|
370
|
+
for (let i = 0; i < node.childNodes.length; i++) {
|
|
371
|
+
logger('will dfs', node.childNodes[i]);
|
|
372
|
+
const childNodeInfo = dfs(
|
|
373
|
+
node.childNodes[i],
|
|
374
|
+
currentWindow,
|
|
375
|
+
currentDocument,
|
|
376
|
+
rect.zoom,
|
|
377
|
+
basePoint,
|
|
378
|
+
);
|
|
379
|
+
if (Array.isArray(childNodeInfo)) {
|
|
380
|
+
// if the recursive return is an array, expand and merge it into children
|
|
381
|
+
nodeInfo.children.push(...childNodeInfo);
|
|
382
|
+
} else if (childNodeInfo) {
|
|
383
|
+
nodeInfo.children.push(childNodeInfo);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// if nodeInfo.node is null
|
|
388
|
+
if (nodeInfo.node === null) {
|
|
389
|
+
if (nodeInfo.children.length === 0) {
|
|
390
|
+
return null;
|
|
391
|
+
}
|
|
392
|
+
// promote children to the upper layer
|
|
393
|
+
return nodeInfo.children;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
return nodeInfo;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
const rootNodeInfo = dfs(startNode, window, document, 1, {
|
|
400
|
+
left: 0,
|
|
401
|
+
top: 0,
|
|
402
|
+
});
|
|
403
|
+
if (Array.isArray(rootNodeInfo)) {
|
|
404
|
+
topChildren.push(...rootNodeInfo);
|
|
405
|
+
} else if (rootNodeInfo) {
|
|
406
|
+
topChildren.push(rootNodeInfo);
|
|
407
|
+
}
|
|
408
|
+
if (startNode === topDocument) {
|
|
409
|
+
// find all the same-origin iframes
|
|
410
|
+
const iframes = document.querySelectorAll('iframe');
|
|
411
|
+
for (let i = 0; i < iframes.length; i++) {
|
|
412
|
+
const iframe = iframes[i];
|
|
413
|
+
if (iframe.contentDocument && iframe.contentWindow) {
|
|
414
|
+
const iframeInfo = collectElementInfo(iframe, window, document, 1);
|
|
415
|
+
// when the iframe is in the viewport, we need to collect its children
|
|
416
|
+
if (iframeInfo) {
|
|
417
|
+
const iframeChildren = dfs(
|
|
418
|
+
iframe.contentDocument.body,
|
|
419
|
+
iframe.contentWindow as any,
|
|
420
|
+
iframe.contentDocument,
|
|
421
|
+
1,
|
|
422
|
+
{
|
|
423
|
+
left: iframeInfo.rect.left,
|
|
424
|
+
top: iframeInfo.rect.top,
|
|
425
|
+
},
|
|
426
|
+
);
|
|
427
|
+
if (Array.isArray(iframeChildren)) {
|
|
428
|
+
topChildren.push(...iframeChildren);
|
|
429
|
+
} else if (iframeChildren) {
|
|
430
|
+
topChildren.push(iframeChildren);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
return {
|
|
438
|
+
node: null,
|
|
439
|
+
children: topChildren,
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
export function mergeElementAndChildrenRects(
|
|
444
|
+
node: Node,
|
|
445
|
+
currentWindow: typeof window,
|
|
446
|
+
currentDocument: typeof document,
|
|
447
|
+
baseZoom = 1,
|
|
448
|
+
) {
|
|
449
|
+
const selfRect = elementRect(node, currentWindow, currentDocument, baseZoom);
|
|
450
|
+
if (!selfRect) return null;
|
|
451
|
+
|
|
452
|
+
let minLeft = selfRect.left;
|
|
453
|
+
let minTop = selfRect.top;
|
|
454
|
+
let maxRight = selfRect.left + selfRect.width;
|
|
455
|
+
let maxBottom = selfRect.top + selfRect.height;
|
|
456
|
+
|
|
457
|
+
function traverse(child: Node) {
|
|
458
|
+
for (let i = 0; i < child.childNodes.length; i++) {
|
|
459
|
+
const sub = child.childNodes[i];
|
|
460
|
+
if (sub.nodeType === 1) {
|
|
461
|
+
const rect = elementRect(sub, currentWindow, currentDocument, baseZoom);
|
|
462
|
+
if (rect) {
|
|
463
|
+
minLeft = Math.min(minLeft, rect.left);
|
|
464
|
+
minTop = Math.min(minTop, rect.top);
|
|
465
|
+
maxRight = Math.max(maxRight, rect.left + rect.width);
|
|
466
|
+
maxBottom = Math.max(maxBottom, rect.top + rect.height);
|
|
467
|
+
}
|
|
468
|
+
traverse(sub);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
traverse(node);
|
|
473
|
+
|
|
474
|
+
return {
|
|
475
|
+
...selfRect,
|
|
476
|
+
left: minLeft,
|
|
477
|
+
top: minTop,
|
|
478
|
+
width: maxRight - minLeft,
|
|
479
|
+
height: maxBottom - minTop,
|
|
480
|
+
};
|
|
481
|
+
}
|