@rpascene/shared 0.30.8

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