@xiuchang-midscene/shared 2.0.2
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/baseDB.mjs.bak +109 -0
- package/dist/es/build/copy-static.mjs +31 -0
- package/dist/es/build/copy-static.mjs.bak +31 -0
- package/dist/es/build/rspack-config.mjs +4 -0
- package/dist/es/build/rspack-config.mjs.bak +4 -0
- package/dist/es/cli/cli-runner.mjs +140 -0
- package/dist/es/cli/cli-runner.mjs.bak +140 -0
- package/dist/es/cli/index.mjs +2 -0
- package/dist/es/cli/index.mjs.bak +2 -0
- package/dist/es/common.mjs +37 -0
- package/dist/es/common.mjs.bak +37 -0
- package/dist/es/constants/example-code.mjs +223 -0
- package/dist/es/constants/example-code.mjs.bak +223 -0
- package/dist/es/constants/index.mjs +23 -0
- package/dist/es/constants/index.mjs.bak +23 -0
- package/dist/es/env/basic.mjs +6 -0
- package/dist/es/env/basic.mjs.bak +6 -0
- package/dist/es/env/constants.mjs +70 -0
- package/dist/es/env/constants.mjs.bak +70 -0
- package/dist/es/env/global-config-manager.mjs +94 -0
- package/dist/es/env/global-config-manager.mjs.bak +94 -0
- package/dist/es/env/helper.mjs +43 -0
- package/dist/es/env/helper.mjs.bak +43 -0
- package/dist/es/env/index.mjs +5 -0
- package/dist/es/env/index.mjs.bak +5 -0
- package/dist/es/env/init-debug.mjs +18 -0
- package/dist/es/env/init-debug.mjs.bak +18 -0
- package/dist/es/env/model-config-manager.mjs +79 -0
- package/dist/es/env/model-config-manager.mjs.bak +79 -0
- package/dist/es/env/parse-model-config.mjs +132 -0
- package/dist/es/env/parse-model-config.mjs.bak +132 -0
- package/dist/es/env/types.mjs +220 -0
- package/dist/es/env/types.mjs.bak +220 -0
- package/dist/es/env/utils.mjs +26 -0
- package/dist/es/env/utils.mjs.bak +26 -0
- package/dist/es/extractor/constants.mjs +2 -0
- package/dist/es/extractor/constants.mjs.bak +2 -0
- package/dist/es/extractor/debug.mjs +6 -0
- package/dist/es/extractor/debug.mjs.bak +6 -0
- package/dist/es/extractor/dom-util.mjs +92 -0
- package/dist/es/extractor/dom-util.mjs.bak +92 -0
- package/dist/es/extractor/index.mjs +5 -0
- package/dist/es/extractor/index.mjs.bak +5 -0
- package/dist/es/extractor/locator.mjs +250 -0
- package/dist/es/extractor/locator.mjs.bak +250 -0
- package/dist/es/extractor/tree.mjs +78 -0
- package/dist/es/extractor/tree.mjs.bak +78 -0
- package/dist/es/extractor/util.mjs +245 -0
- package/dist/es/extractor/util.mjs.bak +245 -0
- package/dist/es/extractor/web-extractor.mjs +303 -0
- package/dist/es/extractor/web-extractor.mjs.bak +303 -0
- package/dist/es/img/box-select.mjs +824 -0
- package/dist/es/img/box-select.mjs.bak +824 -0
- package/dist/es/img/canvas-fallback.mjs +238 -0
- package/dist/es/img/canvas-fallback.mjs.bak +238 -0
- package/dist/es/img/get-photon.mjs +45 -0
- package/dist/es/img/get-photon.mjs.bak +45 -0
- package/dist/es/img/get-sharp.mjs +11 -0
- package/dist/es/img/get-sharp.mjs.bak +11 -0
- package/dist/es/img/index.mjs +4 -0
- package/dist/es/img/index.mjs.bak +4 -0
- package/dist/es/img/info.mjs +29 -0
- package/dist/es/img/info.mjs.bak +29 -0
- package/dist/es/img/transform.mjs +295 -0
- package/dist/es/img/transform.mjs.bak +295 -0
- package/dist/es/index.mjs +4 -0
- package/dist/es/index.mjs.bak +4 -0
- package/dist/es/logger.mjs +64 -0
- package/dist/es/logger.mjs.bak +64 -0
- package/dist/es/mcp/base-server.mjs +281 -0
- package/dist/es/mcp/base-server.mjs.bak +281 -0
- package/dist/es/mcp/base-tools.mjs +91 -0
- package/dist/es/mcp/base-tools.mjs.bak +91 -0
- package/dist/es/mcp/chrome-path.mjs +35 -0
- package/dist/es/mcp/chrome-path.mjs.bak +35 -0
- package/dist/es/mcp/index.mjs +7 -0
- package/dist/es/mcp/index.mjs.bak +7 -0
- package/dist/es/mcp/inject-report-html-plugin.mjs +53 -0
- package/dist/es/mcp/inject-report-html-plugin.mjs.bak +53 -0
- package/dist/es/mcp/launcher-helper.mjs +52 -0
- package/dist/es/mcp/launcher-helper.mjs.bak +52 -0
- package/dist/es/mcp/tool-generator.mjs +297 -0
- package/dist/es/mcp/tool-generator.mjs.bak +297 -0
- package/dist/es/mcp/types.mjs +3 -0
- package/dist/es/mcp/types.mjs.bak +3 -0
- package/dist/es/node/fs.mjs +44 -0
- package/dist/es/node/fs.mjs.bak +44 -0
- package/dist/es/node/index.mjs +2 -0
- package/dist/es/node/index.mjs.bak +2 -0
- package/dist/es/node/port.mjs +24 -0
- package/dist/es/node/port.mjs.bak +24 -0
- package/dist/es/oss/demo.mjs +30 -0
- package/dist/es/oss/demo.mjs.bak +30 -0
- package/dist/es/oss/index.mjs +90 -0
- package/dist/es/oss/index.mjs.bak +90 -0
- package/dist/es/polyfills/async-hooks.mjs +2 -0
- package/dist/es/polyfills/async-hooks.mjs.bak +2 -0
- package/dist/es/polyfills/index.mjs +1 -0
- package/dist/es/polyfills/index.mjs.bak +1 -0
- package/dist/es/types/index.mjs +3 -0
- package/dist/es/types/index.mjs.bak +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/us-keyboard-layout.mjs.bak +1414 -0
- package/dist/es/utils.mjs +72 -0
- package/dist/es/utils.mjs.bak +72 -0
- package/dist/es/zod-schema-utils.mjs +54 -0
- package/dist/es/zod-schema-utils.mjs.bak +54 -0
- package/dist/lib/baseDB.js +149 -0
- package/dist/lib/baseDB.js.bak +149 -0
- package/dist/lib/build/copy-static.js +79 -0
- package/dist/lib/build/copy-static.js.bak +79 -0
- package/dist/lib/build/rspack-config.js +38 -0
- package/dist/lib/build/rspack-config.js.bak +38 -0
- package/dist/lib/cli/cli-runner.js +196 -0
- package/dist/lib/cli/cli-runner.js.bak +196 -0
- package/dist/lib/cli/index.js +48 -0
- package/dist/lib/cli/index.js.bak +48 -0
- package/dist/lib/common.js +93 -0
- package/dist/lib/common.js.bak +93 -0
- package/dist/lib/constants/example-code.js +260 -0
- package/dist/lib/constants/example-code.js.bak +260 -0
- package/dist/lib/constants/index.js +96 -0
- package/dist/lib/constants/index.js.bak +96 -0
- package/dist/lib/env/basic.js +40 -0
- package/dist/lib/env/basic.js.bak +40 -0
- package/dist/lib/env/constants.js +113 -0
- package/dist/lib/env/constants.js.bak +113 -0
- package/dist/lib/env/global-config-manager.js +128 -0
- package/dist/lib/env/global-config-manager.js.bak +128 -0
- package/dist/lib/env/helper.js +80 -0
- package/dist/lib/env/helper.js.bak +80 -0
- package/dist/lib/env/index.js +90 -0
- package/dist/lib/env/index.js.bak +90 -0
- package/dist/lib/env/init-debug.js +52 -0
- package/dist/lib/env/init-debug.js.bak +52 -0
- package/dist/lib/env/model-config-manager.js +113 -0
- package/dist/lib/env/model-config-manager.js.bak +113 -0
- package/dist/lib/env/parse-model-config.js +178 -0
- package/dist/lib/env/parse-model-config.js.bak +178 -0
- package/dist/lib/env/types.js +554 -0
- package/dist/lib/env/types.js.bak +554 -0
- package/dist/lib/env/utils.js +72 -0
- package/dist/lib/env/utils.js.bak +72 -0
- package/dist/lib/extractor/constants.js +42 -0
- package/dist/lib/extractor/constants.js.bak +42 -0
- package/dist/lib/extractor/debug.js +12 -0
- package/dist/lib/extractor/debug.js.bak +12 -0
- package/dist/lib/extractor/dom-util.js +153 -0
- package/dist/lib/extractor/dom-util.js.bak +153 -0
- package/dist/lib/extractor/index.js +81 -0
- package/dist/lib/extractor/index.js.bak +81 -0
- package/dist/lib/extractor/locator.js +296 -0
- package/dist/lib/extractor/locator.js.bak +296 -0
- package/dist/lib/extractor/tree.js +124 -0
- package/dist/lib/extractor/tree.js.bak +124 -0
- package/dist/lib/extractor/util.js +336 -0
- package/dist/lib/extractor/util.js.bak +336 -0
- package/dist/lib/extractor/web-extractor.js +349 -0
- package/dist/lib/extractor/web-extractor.js.bak +349 -0
- package/dist/lib/img/box-select.js +875 -0
- package/dist/lib/img/box-select.js.bak +875 -0
- package/dist/lib/img/canvas-fallback.js +305 -0
- package/dist/lib/img/canvas-fallback.js.bak +305 -0
- package/dist/lib/img/get-photon.js +82 -0
- package/dist/lib/img/get-photon.js.bak +82 -0
- package/dist/lib/img/get-sharp.js +45 -0
- package/dist/lib/img/get-sharp.js.bak +45 -0
- package/dist/lib/img/index.js +95 -0
- package/dist/lib/img/index.js.bak +95 -0
- package/dist/lib/img/info.js +83 -0
- package/dist/lib/img/info.js.bak +83 -0
- package/dist/lib/img/transform.js +387 -0
- package/dist/lib/img/transform.js.bak +387 -0
- package/dist/lib/index.js +47 -0
- package/dist/lib/index.js.bak +47 -0
- package/dist/lib/logger.js +114 -0
- package/dist/lib/logger.js.bak +114 -0
- package/dist/lib/mcp/base-server.js +331 -0
- package/dist/lib/mcp/base-server.js.bak +331 -0
- package/dist/lib/mcp/base-tools.js +125 -0
- package/dist/lib/mcp/base-tools.js.bak +125 -0
- package/dist/lib/mcp/chrome-path.js +72 -0
- package/dist/lib/mcp/chrome-path.js.bak +72 -0
- package/dist/lib/mcp/index.js +100 -0
- package/dist/lib/mcp/index.js.bak +100 -0
- package/dist/lib/mcp/inject-report-html-plugin.js +98 -0
- package/dist/lib/mcp/inject-report-html-plugin.js.bak +98 -0
- package/dist/lib/mcp/launcher-helper.js +86 -0
- package/dist/lib/mcp/launcher-helper.js.bak +86 -0
- package/dist/lib/mcp/tool-generator.js +334 -0
- package/dist/lib/mcp/tool-generator.js.bak +334 -0
- package/dist/lib/mcp/types.js +40 -0
- package/dist/lib/mcp/types.js.bak +40 -0
- package/dist/lib/node/fs.js +97 -0
- package/dist/lib/node/fs.js.bak +97 -0
- package/dist/lib/node/index.js +65 -0
- package/dist/lib/node/index.js.bak +65 -0
- package/dist/lib/node/port.js +61 -0
- package/dist/lib/node/port.js.bak +61 -0
- package/dist/lib/oss/demo.js +36 -0
- package/dist/lib/oss/demo.js.bak +36 -0
- package/dist/lib/oss/index.js +138 -0
- package/dist/lib/oss/index.js.bak +138 -0
- package/dist/lib/polyfills/async-hooks.js +36 -0
- package/dist/lib/polyfills/async-hooks.js.bak +36 -0
- package/dist/lib/polyfills/index.js +58 -0
- package/dist/lib/polyfills/index.js.bak +58 -0
- package/dist/lib/types/index.js +37 -0
- package/dist/lib/types/index.js.bak +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/us-keyboard-layout.js.bak +1457 -0
- package/dist/lib/utils.js +148 -0
- package/dist/lib/utils.js.bak +148 -0
- package/dist/lib/zod-schema-utils.js +97 -0
- package/dist/lib/zod-schema-utils.js.bak +97 -0
- package/dist/types/baseDB.d.ts +25 -0
- package/dist/types/baseDB.d.ts.bak +25 -0
- package/dist/types/build/copy-static.d.ts +31 -0
- package/dist/types/build/copy-static.d.ts.bak +31 -0
- package/dist/types/build/rspack-config.d.ts +8 -0
- package/dist/types/build/rspack-config.d.ts.bak +8 -0
- package/dist/types/cli/cli-runner.d.ts +14 -0
- package/dist/types/cli/cli-runner.d.ts.bak +14 -0
- package/dist/types/cli/index.d.ts +2 -0
- package/dist/types/cli/index.d.ts.bak +2 -0
- package/dist/types/common.d.ts +12 -0
- package/dist/types/common.d.ts.bak +12 -0
- package/dist/types/constants/example-code.d.ts +2 -0
- package/dist/types/constants/example-code.d.ts.bak +2 -0
- package/dist/types/constants/index.d.ts +21 -0
- package/dist/types/constants/index.d.ts.bak +21 -0
- package/dist/types/env/basic.d.ts +6 -0
- package/dist/types/env/basic.d.ts.bak +6 -0
- package/dist/types/env/constants.d.ts +40 -0
- package/dist/types/env/constants.d.ts.bak +40 -0
- package/dist/types/env/global-config-manager.d.ts +32 -0
- package/dist/types/env/global-config-manager.d.ts.bak +32 -0
- package/dist/types/env/helper.d.ts +4 -0
- package/dist/types/env/helper.d.ts.bak +4 -0
- package/dist/types/env/index.d.ts +4 -0
- package/dist/types/env/index.d.ts.bak +4 -0
- package/dist/types/env/init-debug.d.ts +1 -0
- package/dist/types/env/init-debug.d.ts.bak +1 -0
- package/dist/types/env/model-config-manager.d.ts +25 -0
- package/dist/types/env/model-config-manager.d.ts.bak +25 -0
- package/dist/types/env/parse-model-config.d.ts +31 -0
- package/dist/types/env/parse-model-config.d.ts.bak +31 -0
- package/dist/types/env/types.d.ts +318 -0
- package/dist/types/env/types.d.ts.bak +318 -0
- package/dist/types/env/utils.d.ts +38 -0
- package/dist/types/env/utils.d.ts.bak +38 -0
- package/dist/types/extractor/constants.d.ts +1 -0
- package/dist/types/extractor/constants.d.ts.bak +1 -0
- package/dist/types/extractor/debug.d.ts +1 -0
- package/dist/types/extractor/debug.d.ts.bak +1 -0
- package/dist/types/extractor/dom-util.d.ts +56 -0
- package/dist/types/extractor/dom-util.d.ts.bak +56 -0
- package/dist/types/extractor/index.d.ts +32 -0
- package/dist/types/extractor/index.d.ts.bak +32 -0
- package/dist/types/extractor/locator.d.ts +9 -0
- package/dist/types/extractor/locator.d.ts.bak +9 -0
- package/dist/types/extractor/tree.d.ts +6 -0
- package/dist/types/extractor/tree.d.ts.bak +6 -0
- package/dist/types/extractor/util.d.ts +47 -0
- package/dist/types/extractor/util.d.ts.bak +47 -0
- package/dist/types/extractor/web-extractor.d.ts +19 -0
- package/dist/types/extractor/web-extractor.d.ts.bak +19 -0
- package/dist/types/img/box-select.d.ts +26 -0
- package/dist/types/img/box-select.d.ts.bak +26 -0
- package/dist/types/img/canvas-fallback.d.ts +105 -0
- package/dist/types/img/canvas-fallback.d.ts.bak +105 -0
- package/dist/types/img/get-photon.d.ts +19 -0
- package/dist/types/img/get-photon.d.ts.bak +19 -0
- package/dist/types/img/get-sharp.d.ts +3 -0
- package/dist/types/img/get-sharp.d.ts.bak +3 -0
- package/dist/types/img/index.d.ts +3 -0
- package/dist/types/img/index.d.ts.bak +3 -0
- package/dist/types/img/info.d.ts +29 -0
- package/dist/types/img/info.d.ts.bak +29 -0
- package/dist/types/img/transform.d.ts +107 -0
- package/dist/types/img/transform.d.ts.bak +107 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.d.ts.bak +4 -0
- package/dist/types/logger.d.ts +5 -0
- package/dist/types/logger.d.ts.bak +5 -0
- package/dist/types/mcp/base-server.d.ts +93 -0
- package/dist/types/mcp/base-server.d.ts.bak +93 -0
- package/dist/types/mcp/base-tools.d.ts +79 -0
- package/dist/types/mcp/base-tools.d.ts.bak +79 -0
- package/dist/types/mcp/chrome-path.d.ts +2 -0
- package/dist/types/mcp/chrome-path.d.ts.bak +2 -0
- package/dist/types/mcp/index.d.ts +7 -0
- package/dist/types/mcp/index.d.ts.bak +7 -0
- package/dist/types/mcp/inject-report-html-plugin.d.ts +18 -0
- package/dist/types/mcp/inject-report-html-plugin.d.ts.bak +18 -0
- package/dist/types/mcp/launcher-helper.d.ts +94 -0
- package/dist/types/mcp/launcher-helper.d.ts.bak +94 -0
- package/dist/types/mcp/tool-generator.d.ts +10 -0
- package/dist/types/mcp/tool-generator.d.ts.bak +10 -0
- package/dist/types/mcp/types.d.ts +103 -0
- package/dist/types/mcp/types.d.ts.bak +103 -0
- package/dist/types/node/fs.d.ts +15 -0
- package/dist/types/node/fs.d.ts.bak +15 -0
- package/dist/types/node/index.d.ts +2 -0
- package/dist/types/node/index.d.ts.bak +2 -0
- package/dist/types/node/port.d.ts +8 -0
- package/dist/types/node/port.d.ts.bak +8 -0
- package/dist/types/oss/demo.d.ts +1 -0
- package/dist/types/oss/demo.d.ts.bak +1 -0
- package/dist/types/oss/index.d.ts +34 -0
- package/dist/types/oss/index.d.ts.bak +34 -0
- package/dist/types/polyfills/async-hooks.d.ts +6 -0
- package/dist/types/polyfills/async-hooks.d.ts.bak +6 -0
- package/dist/types/polyfills/index.d.ts +4 -0
- package/dist/types/polyfills/index.d.ts.bak +4 -0
- package/dist/types/types/index.d.ts +34 -0
- package/dist/types/types/index.d.ts.bak +34 -0
- package/dist/types/us-keyboard-layout.d.ts +32 -0
- package/dist/types/us-keyboard-layout.d.ts.bak +32 -0
- package/dist/types/utils.d.ts +34 -0
- package/dist/types/utils.d.ts.bak +34 -0
- package/dist/types/zod-schema-utils.d.ts +23 -0
- package/dist/types/zod-schema-utils.d.ts.bak +23 -0
- package/package.json +132 -0
- package/src/baseDB.ts +158 -0
- package/src/build/copy-static.ts +68 -0
- package/src/build/rspack-config.ts +12 -0
- package/src/cli/cli-runner.ts +224 -0
- package/src/cli/index.ts +8 -0
- package/src/common.ts +67 -0
- package/src/constants/example-code.ts +223 -0
- package/src/constants/index.ts +29 -0
- package/src/env/basic.ts +12 -0
- package/src/env/constants.ts +234 -0
- package/src/env/global-config-manager.ts +191 -0
- package/src/env/helper.ts +58 -0
- package/src/env/index.ts +4 -0
- package/src/env/init-debug.ts +34 -0
- package/src/env/model-config-manager.ts +149 -0
- package/src/env/parse-model-config.ts +294 -0
- package/src/env/types.ts +547 -0
- package/src/env/utils.ts +89 -0
- package/src/extractor/constants.ts +5 -0
- package/src/extractor/debug.ts +10 -0
- package/src/extractor/dom-util.ts +226 -0
- package/src/extractor/index.ts +48 -0
- package/src/extractor/locator.ts +469 -0
- package/src/extractor/tree.ts +179 -0
- package/src/extractor/util.ts +482 -0
- package/src/extractor/web-extractor.ts +481 -0
- package/src/img/box-select.ts +588 -0
- package/src/img/canvas-fallback.ts +393 -0
- package/src/img/get-photon.ts +108 -0
- package/src/img/get-sharp.ts +18 -0
- package/src/img/index.ts +26 -0
- package/src/img/info.ts +75 -0
- package/src/img/transform.ts +594 -0
- package/src/index.ts +8 -0
- package/src/logger.ts +96 -0
- package/src/mcp/base-server.ts +502 -0
- package/src/mcp/base-tools.ts +185 -0
- package/src/mcp/chrome-path.ts +48 -0
- package/src/mcp/index.ts +7 -0
- package/src/mcp/inject-report-html-plugin.ts +119 -0
- package/src/mcp/launcher-helper.ts +200 -0
- package/src/mcp/tool-generator.ts +429 -0
- package/src/mcp/types.ts +112 -0
- package/src/node/fs.ts +84 -0
- package/src/node/index.ts +2 -0
- package/src/node/port.ts +37 -0
- package/src/oss/demo.ts +61 -0
- package/src/oss/index.ts +187 -0
- package/src/polyfills/async-hooks.ts +6 -0
- package/src/polyfills/index.ts +4 -0
- package/src/types/index.ts +52 -0
- package/src/us-keyboard-layout.ts +723 -0
- package/src/utils.ts +149 -0
- package/src/zod-schema-utils.ts +133 -0
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { isSvgElement } from "./dom-util.mjs";
|
|
2
|
+
import { getNodeFromCacheList, getRect, isElementPartiallyInViewport, logger } from "./util.mjs";
|
|
3
|
+
import { collectElementInfo } from "./web-extractor.mjs";
|
|
4
|
+
const SUB_XPATH_SEPARATOR = '|>>|';
|
|
5
|
+
function parseCSSZoom(style) {
|
|
6
|
+
return Number.parseFloat(style.zoom ?? '1') || 1;
|
|
7
|
+
}
|
|
8
|
+
function calculateIframeOffset(nodeOwnerDoc, rootDoc) {
|
|
9
|
+
let leftOffset = 0;
|
|
10
|
+
let topOffset = 0;
|
|
11
|
+
let iterDoc = nodeOwnerDoc;
|
|
12
|
+
while(iterDoc && iterDoc !== rootDoc)try {
|
|
13
|
+
const frameElement = iterDoc.defaultView?.frameElement;
|
|
14
|
+
if (!frameElement) break;
|
|
15
|
+
const rect = frameElement.getBoundingClientRect();
|
|
16
|
+
const parentWin = iterDoc.defaultView?.parent;
|
|
17
|
+
let borderLeft = 0;
|
|
18
|
+
let borderTop = 0;
|
|
19
|
+
let zoom = 1;
|
|
20
|
+
try {
|
|
21
|
+
if (parentWin) {
|
|
22
|
+
const style = parentWin.getComputedStyle(frameElement);
|
|
23
|
+
borderLeft = Number.parseFloat(style.borderLeftWidth) || 0;
|
|
24
|
+
borderTop = Number.parseFloat(style.borderTopWidth) || 0;
|
|
25
|
+
zoom = parseCSSZoom(style);
|
|
26
|
+
}
|
|
27
|
+
} catch {}
|
|
28
|
+
leftOffset = leftOffset / zoom + rect.left + borderLeft;
|
|
29
|
+
topOffset = topOffset / zoom + rect.top + borderTop;
|
|
30
|
+
iterDoc = frameElement.ownerDocument;
|
|
31
|
+
} catch {
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
left: leftOffset,
|
|
36
|
+
top: topOffset
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function translatePointToIframeCoordinates(point, iframeElement, parentWindow) {
|
|
40
|
+
const rect = iframeElement.getBoundingClientRect();
|
|
41
|
+
const style = parentWindow.getComputedStyle(iframeElement);
|
|
42
|
+
const clientLeft = iframeElement.clientLeft;
|
|
43
|
+
const clientTop = iframeElement.clientTop;
|
|
44
|
+
const paddingLeft = Number.parseFloat(style.paddingLeft) || 0;
|
|
45
|
+
const paddingTop = Number.parseFloat(style.paddingTop) || 0;
|
|
46
|
+
const zoom = parseCSSZoom(style);
|
|
47
|
+
return {
|
|
48
|
+
left: (point.left - rect.left - clientLeft - paddingLeft) / zoom,
|
|
49
|
+
top: (point.top - rect.top - clientTop - paddingTop) / zoom
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
const getElementXpathIndex = (element)=>{
|
|
53
|
+
let index = 1;
|
|
54
|
+
let prev = element.previousElementSibling;
|
|
55
|
+
while(prev){
|
|
56
|
+
if (prev.nodeName.toLowerCase() === element.nodeName.toLowerCase()) index++;
|
|
57
|
+
prev = prev.previousElementSibling;
|
|
58
|
+
}
|
|
59
|
+
return index;
|
|
60
|
+
};
|
|
61
|
+
const normalizeXpathText = (text)=>{
|
|
62
|
+
if ('string' != typeof text) return '';
|
|
63
|
+
return text.replace(/\s+/g, ' ').trim();
|
|
64
|
+
};
|
|
65
|
+
const buildCurrentElementXpath = (element, isOrderSensitive, isLeafElement, limitToCurrentDocument = false)=>{
|
|
66
|
+
const parentPath = element.parentNode ? getElementXpath(element.parentNode, isOrderSensitive, false, limitToCurrentDocument) : '';
|
|
67
|
+
const prefix = parentPath ? `${parentPath}/` : '/';
|
|
68
|
+
const tagName = element.nodeName.toLowerCase();
|
|
69
|
+
const textContent = element.textContent?.trim();
|
|
70
|
+
const isSVGNamespace = 'http://www.w3.org/2000/svg' === element.namespaceURI;
|
|
71
|
+
const tagSelector = isSVGNamespace ? `*[name()="${tagName}"]` : tagName;
|
|
72
|
+
if (isOrderSensitive) {
|
|
73
|
+
const index = getElementXpathIndex(element);
|
|
74
|
+
return `${prefix}${tagSelector}[${index}]`;
|
|
75
|
+
}
|
|
76
|
+
if (isLeafElement && textContent) return `${prefix}${tagSelector}[normalize-space()="${normalizeXpathText(textContent)}"]`;
|
|
77
|
+
const index = getElementXpathIndex(element);
|
|
78
|
+
return `${prefix}${tagSelector}[${index}]`;
|
|
79
|
+
};
|
|
80
|
+
const getElementXpath = (element, isOrderSensitive = false, isLeafElement = false, limitToCurrentDocument = false)=>{
|
|
81
|
+
if (element.nodeType === Node.TEXT_NODE) {
|
|
82
|
+
const parentNode = element.parentNode;
|
|
83
|
+
if (parentNode && parentNode.nodeType === Node.ELEMENT_NODE) {
|
|
84
|
+
const parentXPath = getElementXpath(parentNode, isOrderSensitive, true, limitToCurrentDocument);
|
|
85
|
+
const textContent = element.textContent?.trim();
|
|
86
|
+
if (textContent) return `${parentXPath}/text()[normalize-space()="${normalizeXpathText(textContent)}"]`;
|
|
87
|
+
return `${parentXPath}/text()`;
|
|
88
|
+
}
|
|
89
|
+
return '';
|
|
90
|
+
}
|
|
91
|
+
if (element.nodeType !== Node.ELEMENT_NODE) return '';
|
|
92
|
+
const el = element;
|
|
93
|
+
try {
|
|
94
|
+
const nodeName = el.nodeName.toLowerCase();
|
|
95
|
+
if (el === el.ownerDocument?.documentElement || 'html' === nodeName) {
|
|
96
|
+
if (!limitToCurrentDocument) {
|
|
97
|
+
const frameElement = el.ownerDocument?.defaultView?.frameElement;
|
|
98
|
+
if (frameElement) {
|
|
99
|
+
const framePath = getElementXpath(frameElement, isOrderSensitive, false, limitToCurrentDocument);
|
|
100
|
+
return `${framePath}${SUB_XPATH_SEPARATOR}/html`;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return '/html';
|
|
104
|
+
}
|
|
105
|
+
if (el === el.ownerDocument?.body || 'body' === nodeName) {
|
|
106
|
+
if (!limitToCurrentDocument) {
|
|
107
|
+
const frameElement = el.ownerDocument?.defaultView?.frameElement;
|
|
108
|
+
if (frameElement) {
|
|
109
|
+
const framePath = getElementXpath(frameElement, isOrderSensitive, false, limitToCurrentDocument);
|
|
110
|
+
return `${framePath}${SUB_XPATH_SEPARATOR}/html/body`;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return '/html/body';
|
|
114
|
+
}
|
|
115
|
+
} catch (error) {
|
|
116
|
+
logger('[midscene:locator] ownerDocument access failed:', error);
|
|
117
|
+
if ('html' === el.nodeName.toLowerCase()) return '/html';
|
|
118
|
+
if ('body' === el.nodeName.toLowerCase()) return '/html/body';
|
|
119
|
+
}
|
|
120
|
+
if (isSvgElement(el)) {
|
|
121
|
+
const tagName = el.nodeName.toLowerCase();
|
|
122
|
+
if ('svg' === tagName) return buildCurrentElementXpath(el, isOrderSensitive, isLeafElement, limitToCurrentDocument);
|
|
123
|
+
let parent = el.parentNode;
|
|
124
|
+
while(parent && parent.nodeType === Node.ELEMENT_NODE){
|
|
125
|
+
const parentEl = parent;
|
|
126
|
+
if (!isSvgElement(parentEl)) return getElementXpath(parentEl, isOrderSensitive, isLeafElement, limitToCurrentDocument);
|
|
127
|
+
const parentTag = parentEl.nodeName.toLowerCase();
|
|
128
|
+
if ('svg' === parentTag) return getElementXpath(parentEl, isOrderSensitive, isLeafElement, limitToCurrentDocument);
|
|
129
|
+
parent = parent.parentNode;
|
|
130
|
+
}
|
|
131
|
+
const fallbackParent = el.parentNode;
|
|
132
|
+
if (fallbackParent && fallbackParent.nodeType === Node.ELEMENT_NODE) return getElementXpath(fallbackParent, isOrderSensitive, isLeafElement, limitToCurrentDocument);
|
|
133
|
+
return '';
|
|
134
|
+
}
|
|
135
|
+
return buildCurrentElementXpath(el, isOrderSensitive, isLeafElement, limitToCurrentDocument);
|
|
136
|
+
};
|
|
137
|
+
function getXpathsById(id) {
|
|
138
|
+
const node = getNodeFromCacheList(id);
|
|
139
|
+
if (!node) return null;
|
|
140
|
+
const fullXPath = getElementXpath(node, false, true, true);
|
|
141
|
+
return [
|
|
142
|
+
fullXPath
|
|
143
|
+
];
|
|
144
|
+
}
|
|
145
|
+
function getXpathsByPoint(point, isOrderSensitive) {
|
|
146
|
+
let currentWindow = 'undefined' != typeof window ? window : void 0;
|
|
147
|
+
let currentDocument = 'undefined' != typeof document ? document : void 0;
|
|
148
|
+
let { left, top } = point;
|
|
149
|
+
let depth = 0;
|
|
150
|
+
const MAX_DEPTH = 10;
|
|
151
|
+
let xpathPrefix = '';
|
|
152
|
+
let lastFoundElement = null;
|
|
153
|
+
while(depth < MAX_DEPTH){
|
|
154
|
+
depth++;
|
|
155
|
+
const element = currentDocument.elementFromPoint(left, top);
|
|
156
|
+
if (!element) {
|
|
157
|
+
if (lastFoundElement) {
|
|
158
|
+
const fullXPath = getElementXpath(lastFoundElement, isOrderSensitive, true, true);
|
|
159
|
+
return [
|
|
160
|
+
xpathPrefix + fullXPath
|
|
161
|
+
];
|
|
162
|
+
}
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
lastFoundElement = element;
|
|
166
|
+
const tag = element.tagName.toLowerCase();
|
|
167
|
+
if ('iframe' === tag || 'frame' === tag) try {
|
|
168
|
+
const contentWindow = element.contentWindow;
|
|
169
|
+
const contentDocument = element.contentDocument;
|
|
170
|
+
if (contentWindow && contentDocument) {
|
|
171
|
+
const localPoint = translatePointToIframeCoordinates({
|
|
172
|
+
left,
|
|
173
|
+
top
|
|
174
|
+
}, element, currentWindow);
|
|
175
|
+
const currentIframeXpath = getElementXpath(element, isOrderSensitive, false, true);
|
|
176
|
+
xpathPrefix += currentIframeXpath + SUB_XPATH_SEPARATOR;
|
|
177
|
+
currentWindow = contentWindow;
|
|
178
|
+
currentDocument = contentDocument;
|
|
179
|
+
left = localPoint.left;
|
|
180
|
+
top = localPoint.top;
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
} catch (error) {
|
|
184
|
+
logger('[midscene:locator] iframe penetration failed (cross-origin?):', error);
|
|
185
|
+
}
|
|
186
|
+
const fullXPath = getElementXpath(element, isOrderSensitive, true, true);
|
|
187
|
+
return [
|
|
188
|
+
xpathPrefix + fullXPath
|
|
189
|
+
];
|
|
190
|
+
}
|
|
191
|
+
if (lastFoundElement) {
|
|
192
|
+
const fullXPath = getElementXpath(lastFoundElement, isOrderSensitive, true, true);
|
|
193
|
+
return [
|
|
194
|
+
xpathPrefix + fullXPath
|
|
195
|
+
];
|
|
196
|
+
}
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
function getNodeInfoByXpath(xpath) {
|
|
200
|
+
const parts = xpath.split(SUB_XPATH_SEPARATOR).map((p)=>p.trim()).filter(Boolean);
|
|
201
|
+
if (0 === parts.length) return null;
|
|
202
|
+
let currentDocument = 'undefined' != typeof document ? document : void 0;
|
|
203
|
+
let node = null;
|
|
204
|
+
for(let i = 0; i < parts.length; i++){
|
|
205
|
+
const currentXpath = parts[i];
|
|
206
|
+
const xpathResult = currentDocument.evaluate(currentXpath, currentDocument, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
|
|
207
|
+
if (1 !== xpathResult.snapshotLength) {
|
|
208
|
+
logger(`[midscene:locator] XPath "${currentXpath}" matched ${xpathResult.snapshotLength} elements (expected 1), discarding.`);
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
node = xpathResult.snapshotItem(0);
|
|
212
|
+
if (i < parts.length - 1) if (!node || node.nodeType !== Node.ELEMENT_NODE || 'iframe' !== node.tagName.toLowerCase()) return null;
|
|
213
|
+
else try {
|
|
214
|
+
const contentDocument = node.contentDocument;
|
|
215
|
+
if (contentDocument) currentDocument = contentDocument;
|
|
216
|
+
else {
|
|
217
|
+
logger('[midscene:locator] iframe contentDocument is null (cross-origin?)');
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
} catch (error) {
|
|
221
|
+
logger('[midscene:locator] iframe contentDocument access failed:', error);
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return node;
|
|
226
|
+
}
|
|
227
|
+
function getElementInfoByXpath(xpath) {
|
|
228
|
+
const node = getNodeInfoByXpath(xpath);
|
|
229
|
+
if (!node) return null;
|
|
230
|
+
let targetWindow = 'undefined' != typeof window ? window : void 0;
|
|
231
|
+
let targetDocument = 'undefined' != typeof document ? document : void 0;
|
|
232
|
+
if (node.ownerDocument?.defaultView) {
|
|
233
|
+
targetWindow = node.ownerDocument.defaultView;
|
|
234
|
+
targetDocument = node.ownerDocument;
|
|
235
|
+
}
|
|
236
|
+
const rootDoc = 'undefined' != typeof document ? document : null;
|
|
237
|
+
const iframeOffset = calculateIframeOffset(node.ownerDocument ?? null, rootDoc);
|
|
238
|
+
const targetWin = targetWindow;
|
|
239
|
+
const targetDoc = targetDocument;
|
|
240
|
+
if (node instanceof targetWin.HTMLElement) {
|
|
241
|
+
const rect = getRect(node, 1, targetWin);
|
|
242
|
+
const isVisible = isElementPartiallyInViewport(rect, targetWin, targetDoc, 1);
|
|
243
|
+
if (!isVisible) node.scrollIntoView({
|
|
244
|
+
behavior: 'instant',
|
|
245
|
+
block: 'center'
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
return collectElementInfo(node, targetWin, targetDoc, 1, iframeOffset, true);
|
|
249
|
+
}
|
|
250
|
+
export { getElementInfoByXpath, getElementXpath, getNodeInfoByXpath, getXpathsById, getXpathsByPoint };
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
function truncateText(text, maxLength = 150) {
|
|
2
|
+
if (void 0 === text) return '';
|
|
3
|
+
if ('object' == typeof text) text = JSON.stringify(text);
|
|
4
|
+
if ('number' == typeof text) return text.toString();
|
|
5
|
+
if ('string' == typeof text && text.length > maxLength) return `${text.slice(0, maxLength)}...`;
|
|
6
|
+
if ('string' == typeof text) return text.trim();
|
|
7
|
+
return '';
|
|
8
|
+
}
|
|
9
|
+
function trimAttributes(attributes, truncateTextLength) {
|
|
10
|
+
const tailorAttributes = Object.keys(attributes).reduce((res, currentKey)=>{
|
|
11
|
+
const attributeVal = attributes[currentKey];
|
|
12
|
+
if ('style' === currentKey || 'htmlTagName' === currentKey || 'nodeType' === currentKey) return res;
|
|
13
|
+
res[currentKey] = truncateText(attributeVal, truncateTextLength);
|
|
14
|
+
return res;
|
|
15
|
+
}, {});
|
|
16
|
+
return tailorAttributes;
|
|
17
|
+
}
|
|
18
|
+
const nodeSizeThreshold = 4;
|
|
19
|
+
function descriptionOfTree(tree, truncateTextLength, filterNonTextContent = false, visibleOnly = true) {
|
|
20
|
+
const attributesString = (kv)=>Object.entries(kv).map(([key, value])=>`${key}="${truncateText(value, truncateTextLength)}"`).join(' ');
|
|
21
|
+
function buildContentTree(node, indent = 0, visibleOnly = true) {
|
|
22
|
+
let before = '';
|
|
23
|
+
let contentWithIndent = '';
|
|
24
|
+
let after = '';
|
|
25
|
+
let emptyNode = true;
|
|
26
|
+
const indentStr = ' '.repeat(indent);
|
|
27
|
+
let children = '';
|
|
28
|
+
for(let i = 0; i < (node.children || []).length; i++){
|
|
29
|
+
const childContent = buildContentTree(node.children[i], indent + 1, visibleOnly);
|
|
30
|
+
if (childContent) children += `\n${childContent}`;
|
|
31
|
+
}
|
|
32
|
+
if (node.node && node.node.rect.width > nodeSizeThreshold && node.node.rect.height > nodeSizeThreshold && (!filterNonTextContent || filterNonTextContent && node.node.content) && (!visibleOnly || visibleOnly && node.node.isVisible)) {
|
|
33
|
+
emptyNode = false;
|
|
34
|
+
let nodeTypeString;
|
|
35
|
+
nodeTypeString = node.node.attributes?.htmlTagName ? node.node.attributes.htmlTagName.replace(/[<>]/g, '') : node.node.attributes.nodeType.replace(/\sNode$/, '').toLowerCase();
|
|
36
|
+
const rectAttribute = node.node.rect ? {
|
|
37
|
+
left: node.node.rect.left,
|
|
38
|
+
top: node.node.rect.top,
|
|
39
|
+
width: node.node.rect.width,
|
|
40
|
+
height: node.node.rect.height
|
|
41
|
+
} : {};
|
|
42
|
+
before = `<${nodeTypeString} id="${node.node.id}" ${attributesString(trimAttributes(node.node.attributes || {}, truncateTextLength))} ${attributesString(rectAttribute)}>`;
|
|
43
|
+
const content = truncateText(node.node.content, truncateTextLength);
|
|
44
|
+
contentWithIndent = content ? `\n${indentStr} ${content}` : '';
|
|
45
|
+
after = `</${nodeTypeString}>`;
|
|
46
|
+
} else if (!filterNonTextContent) {
|
|
47
|
+
if (!children.trim().startsWith('<>')) {
|
|
48
|
+
before = '<>';
|
|
49
|
+
contentWithIndent = '';
|
|
50
|
+
after = '</>';
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (emptyNode && !children.trim()) return '';
|
|
54
|
+
const result = `${indentStr}${before}${contentWithIndent}${children}\n${indentStr}${after}`;
|
|
55
|
+
if (result.trim()) return result;
|
|
56
|
+
return '';
|
|
57
|
+
}
|
|
58
|
+
const result = buildContentTree(tree, 0, visibleOnly);
|
|
59
|
+
return result.replace(/^\s*\n/gm, '');
|
|
60
|
+
}
|
|
61
|
+
function treeToList(tree) {
|
|
62
|
+
const result = [];
|
|
63
|
+
function dfs(node) {
|
|
64
|
+
if (node.node) result.push(node.node);
|
|
65
|
+
for (const child of node.children)dfs(child);
|
|
66
|
+
}
|
|
67
|
+
dfs(tree);
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
function traverseTree(tree, onNode) {
|
|
71
|
+
function dfs(node) {
|
|
72
|
+
if (node.node) node.node = onNode(node.node);
|
|
73
|
+
for (const child of node.children)dfs(child);
|
|
74
|
+
}
|
|
75
|
+
dfs(tree);
|
|
76
|
+
return tree;
|
|
77
|
+
}
|
|
78
|
+
export { descriptionOfTree, traverseTree, treeToList, trimAttributes, truncateText };
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
function truncateText(text, maxLength = 150) {
|
|
2
|
+
if (void 0 === text) return '';
|
|
3
|
+
if ('object' == typeof text) text = JSON.stringify(text);
|
|
4
|
+
if ('number' == typeof text) return text.toString();
|
|
5
|
+
if ('string' == typeof text && text.length > maxLength) return `${text.slice(0, maxLength)}...`;
|
|
6
|
+
if ('string' == typeof text) return text.trim();
|
|
7
|
+
return '';
|
|
8
|
+
}
|
|
9
|
+
function trimAttributes(attributes, truncateTextLength) {
|
|
10
|
+
const tailorAttributes = Object.keys(attributes).reduce((res, currentKey)=>{
|
|
11
|
+
const attributeVal = attributes[currentKey];
|
|
12
|
+
if ('style' === currentKey || 'htmlTagName' === currentKey || 'nodeType' === currentKey) return res;
|
|
13
|
+
res[currentKey] = truncateText(attributeVal, truncateTextLength);
|
|
14
|
+
return res;
|
|
15
|
+
}, {});
|
|
16
|
+
return tailorAttributes;
|
|
17
|
+
}
|
|
18
|
+
const nodeSizeThreshold = 4;
|
|
19
|
+
function descriptionOfTree(tree, truncateTextLength, filterNonTextContent = false, visibleOnly = true) {
|
|
20
|
+
const attributesString = (kv)=>Object.entries(kv).map(([key, value])=>`${key}="${truncateText(value, truncateTextLength)}"`).join(' ');
|
|
21
|
+
function buildContentTree(node, indent = 0, visibleOnly = true) {
|
|
22
|
+
let before = '';
|
|
23
|
+
let contentWithIndent = '';
|
|
24
|
+
let after = '';
|
|
25
|
+
let emptyNode = true;
|
|
26
|
+
const indentStr = ' '.repeat(indent);
|
|
27
|
+
let children = '';
|
|
28
|
+
for(let i = 0; i < (node.children || []).length; i++){
|
|
29
|
+
const childContent = buildContentTree(node.children[i], indent + 1, visibleOnly);
|
|
30
|
+
if (childContent) children += `\n${childContent}`;
|
|
31
|
+
}
|
|
32
|
+
if (node.node && node.node.rect.width > nodeSizeThreshold && node.node.rect.height > nodeSizeThreshold && (!filterNonTextContent || filterNonTextContent && node.node.content) && (!visibleOnly || visibleOnly && node.node.isVisible)) {
|
|
33
|
+
emptyNode = false;
|
|
34
|
+
let nodeTypeString;
|
|
35
|
+
nodeTypeString = node.node.attributes?.htmlTagName ? node.node.attributes.htmlTagName.replace(/[<>]/g, '') : node.node.attributes.nodeType.replace(/\sNode$/, '').toLowerCase();
|
|
36
|
+
const rectAttribute = node.node.rect ? {
|
|
37
|
+
left: node.node.rect.left,
|
|
38
|
+
top: node.node.rect.top,
|
|
39
|
+
width: node.node.rect.width,
|
|
40
|
+
height: node.node.rect.height
|
|
41
|
+
} : {};
|
|
42
|
+
before = `<${nodeTypeString} id="${node.node.id}" ${attributesString(trimAttributes(node.node.attributes || {}, truncateTextLength))} ${attributesString(rectAttribute)}>`;
|
|
43
|
+
const content = truncateText(node.node.content, truncateTextLength);
|
|
44
|
+
contentWithIndent = content ? `\n${indentStr} ${content}` : '';
|
|
45
|
+
after = `</${nodeTypeString}>`;
|
|
46
|
+
} else if (!filterNonTextContent) {
|
|
47
|
+
if (!children.trim().startsWith('<>')) {
|
|
48
|
+
before = '<>';
|
|
49
|
+
contentWithIndent = '';
|
|
50
|
+
after = '</>';
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (emptyNode && !children.trim()) return '';
|
|
54
|
+
const result = `${indentStr}${before}${contentWithIndent}${children}\n${indentStr}${after}`;
|
|
55
|
+
if (result.trim()) return result;
|
|
56
|
+
return '';
|
|
57
|
+
}
|
|
58
|
+
const result = buildContentTree(tree, 0, visibleOnly);
|
|
59
|
+
return result.replace(/^\s*\n/gm, '');
|
|
60
|
+
}
|
|
61
|
+
function treeToList(tree) {
|
|
62
|
+
const result = [];
|
|
63
|
+
function dfs(node) {
|
|
64
|
+
if (node.node) result.push(node.node);
|
|
65
|
+
for (const child of node.children)dfs(child);
|
|
66
|
+
}
|
|
67
|
+
dfs(tree);
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
function traverseTree(tree, onNode) {
|
|
71
|
+
function dfs(node) {
|
|
72
|
+
if (node.node) node.node = onNode(node.node);
|
|
73
|
+
for (const child of node.children)dfs(child);
|
|
74
|
+
}
|
|
75
|
+
dfs(tree);
|
|
76
|
+
return tree;
|
|
77
|
+
}
|
|
78
|
+
export { descriptionOfTree, traverseTree, treeToList, trimAttributes, truncateText };
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { generateHashId } from "../utils.mjs";
|
|
2
|
+
import { extractTextWithPosition } from "./web-extractor.mjs";
|
|
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) return;
|
|
13
|
+
console.log(..._msg);
|
|
14
|
+
}
|
|
15
|
+
function isElementPartiallyInViewport(rect, currentWindow, currentDocument, visibleAreaRatio = 2 / 3) {
|
|
16
|
+
const elementHeight = rect.height;
|
|
17
|
+
const elementWidth = rect.width;
|
|
18
|
+
const viewportRect = {
|
|
19
|
+
left: 0,
|
|
20
|
+
top: 0,
|
|
21
|
+
width: currentWindow.innerWidth || currentDocument.documentElement.clientWidth,
|
|
22
|
+
height: currentWindow.innerHeight || currentDocument.documentElement.clientHeight,
|
|
23
|
+
right: currentWindow.innerWidth || currentDocument.documentElement.clientWidth,
|
|
24
|
+
bottom: currentWindow.innerHeight || currentDocument.documentElement.clientHeight,
|
|
25
|
+
x: 0,
|
|
26
|
+
y: 0,
|
|
27
|
+
zoom: 1
|
|
28
|
+
};
|
|
29
|
+
const overlapRect = overlappedRect(rect, viewportRect);
|
|
30
|
+
if (!overlapRect) return false;
|
|
31
|
+
const visibleArea = overlapRect.width * overlapRect.height;
|
|
32
|
+
const totalArea = elementHeight * elementWidth;
|
|
33
|
+
return visibleArea / totalArea >= visibleAreaRatio;
|
|
34
|
+
}
|
|
35
|
+
function getPseudoElementContent(element, currentWindow) {
|
|
36
|
+
if (!(element instanceof currentWindow.HTMLElement)) return {
|
|
37
|
+
before: '',
|
|
38
|
+
after: ''
|
|
39
|
+
};
|
|
40
|
+
const beforeContent = currentWindow.getComputedStyle(element, '::before').getPropertyValue('content');
|
|
41
|
+
const afterContent = currentWindow.getComputedStyle(element, '::after').getPropertyValue('content');
|
|
42
|
+
return {
|
|
43
|
+
before: 'none' === beforeContent ? '' : beforeContent.replace(/"/g, ''),
|
|
44
|
+
after: 'none' === afterContent ? '' : afterContent.replace(/"/g, '')
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function hasOverflowY(element, currentWindow) {
|
|
48
|
+
const style = currentWindow.getComputedStyle(element);
|
|
49
|
+
return 'scroll' === style.overflowY || 'auto' === style.overflowY || 'hidden' === style.overflowY;
|
|
50
|
+
}
|
|
51
|
+
function overlappedRect(rect1, rect2) {
|
|
52
|
+
const left = Math.max(rect1.left, rect2.left);
|
|
53
|
+
const top = Math.max(rect1.top, rect2.top);
|
|
54
|
+
const right = Math.min(rect1.right, rect2.right);
|
|
55
|
+
const bottom = Math.min(rect1.bottom, rect2.bottom);
|
|
56
|
+
if (left < right && top < bottom) return {
|
|
57
|
+
left,
|
|
58
|
+
top,
|
|
59
|
+
right,
|
|
60
|
+
bottom,
|
|
61
|
+
width: right - left,
|
|
62
|
+
height: bottom - top,
|
|
63
|
+
x: left,
|
|
64
|
+
y: top,
|
|
65
|
+
zoom: 1
|
|
66
|
+
};
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
function getRect(el, baseZoom, currentWindow) {
|
|
70
|
+
let originalRect;
|
|
71
|
+
let newZoom = 1;
|
|
72
|
+
const hasGetBoundingClientRect = el instanceof Element;
|
|
73
|
+
if (hasGetBoundingClientRect) {
|
|
74
|
+
originalRect = el.getBoundingClientRect();
|
|
75
|
+
if (el instanceof currentWindow.HTMLElement && !('currentCSSZoom' in el)) newZoom = Number.parseFloat(currentWindow.getComputedStyle(el).zoom) || 1;
|
|
76
|
+
} else {
|
|
77
|
+
const range = currentWindow.document.createRange();
|
|
78
|
+
range.selectNodeContents(el);
|
|
79
|
+
originalRect = range.getBoundingClientRect();
|
|
80
|
+
}
|
|
81
|
+
const zoom = newZoom * baseZoom;
|
|
82
|
+
return {
|
|
83
|
+
width: originalRect.width * zoom,
|
|
84
|
+
height: originalRect.height * zoom,
|
|
85
|
+
left: originalRect.left * zoom,
|
|
86
|
+
top: originalRect.top * zoom,
|
|
87
|
+
right: originalRect.right * zoom,
|
|
88
|
+
bottom: originalRect.bottom * zoom,
|
|
89
|
+
x: originalRect.x * zoom,
|
|
90
|
+
y: originalRect.y * zoom,
|
|
91
|
+
zoom
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
const isElementCovered = (el, rect, currentWindow)=>{
|
|
95
|
+
const x = rect.left + rect.width / 2;
|
|
96
|
+
const y = rect.top + rect.height / 2;
|
|
97
|
+
const topElement = currentWindow.document.elementFromPoint(x, y);
|
|
98
|
+
if (!topElement) return false;
|
|
99
|
+
if (topElement === el) return false;
|
|
100
|
+
if (el?.contains(topElement)) return false;
|
|
101
|
+
if (topElement?.contains(el)) return false;
|
|
102
|
+
const rectOfTopElement = getRect(topElement, 1, currentWindow);
|
|
103
|
+
const overlapRect = overlappedRect(rect, rectOfTopElement);
|
|
104
|
+
if (!overlapRect) return false;
|
|
105
|
+
logger(el, 'Element is covered by another element', {
|
|
106
|
+
topElement,
|
|
107
|
+
el,
|
|
108
|
+
rect,
|
|
109
|
+
x,
|
|
110
|
+
y
|
|
111
|
+
});
|
|
112
|
+
return true;
|
|
113
|
+
};
|
|
114
|
+
function elementRect(el, currentWindow, currentDocument, baseZoom = 1) {
|
|
115
|
+
if (!el) {
|
|
116
|
+
logger(el, 'Element is not in the DOM hierarchy');
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
if (!(el instanceof currentWindow.HTMLElement) && el.nodeType !== Node.TEXT_NODE && 'svg' !== el.nodeName.toLowerCase()) {
|
|
120
|
+
logger(el, 'Element is not in the DOM hierarchy');
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
if (el instanceof currentWindow.HTMLElement) {
|
|
124
|
+
const style = currentWindow.getComputedStyle(el);
|
|
125
|
+
if ('none' === style.display || 'hidden' === style.visibility || '0' === style.opacity && 'INPUT' !== el.tagName) {
|
|
126
|
+
logger(el, 'Element is hidden');
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const rect = getRect(el, baseZoom, currentWindow);
|
|
131
|
+
if (0 === rect.width && 0 === rect.height) {
|
|
132
|
+
logger(el, 'Element has no size');
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
if (1 === baseZoom && isElementCovered(el, rect, currentWindow)) return false;
|
|
136
|
+
const isVisible = isElementPartiallyInViewport(rect, currentWindow, currentDocument);
|
|
137
|
+
let parent = el;
|
|
138
|
+
const parentUntilNonStatic = (currentNode)=>{
|
|
139
|
+
let parent = currentNode?.parentElement;
|
|
140
|
+
while(parent){
|
|
141
|
+
const style = currentWindow.getComputedStyle(parent);
|
|
142
|
+
if ('static' !== style.position) return parent;
|
|
143
|
+
parent = parent.parentElement;
|
|
144
|
+
}
|
|
145
|
+
return null;
|
|
146
|
+
};
|
|
147
|
+
while(parent && parent !== currentDocument.body){
|
|
148
|
+
if (!(parent instanceof currentWindow.HTMLElement)) {
|
|
149
|
+
parent = parent.parentElement;
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
const parentStyle = currentWindow.getComputedStyle(parent);
|
|
153
|
+
if ('hidden' === parentStyle.overflow) {
|
|
154
|
+
const parentRect = getRect(parent, 1, currentWindow);
|
|
155
|
+
const tolerance = 10;
|
|
156
|
+
if (rect.right < parentRect.left - tolerance || rect.left > parentRect.right + tolerance || rect.bottom < parentRect.top - tolerance || rect.top > parentRect.bottom + tolerance) {
|
|
157
|
+
logger(el, 'element is partially or totally hidden by an ancestor', {
|
|
158
|
+
rect,
|
|
159
|
+
parentRect
|
|
160
|
+
});
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if ('fixed' === parentStyle.position || 'sticky' === parentStyle.position) break;
|
|
165
|
+
parent = 'absolute' === parentStyle.position ? parentUntilNonStatic(parent) : parent.parentElement;
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
left: Math.round(rect.left),
|
|
169
|
+
top: Math.round(rect.top),
|
|
170
|
+
width: Math.round(rect.width),
|
|
171
|
+
height: Math.round(rect.height),
|
|
172
|
+
zoom: rect.zoom,
|
|
173
|
+
isVisible
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
function validTextNodeContent(node) {
|
|
177
|
+
if (!node) return false;
|
|
178
|
+
if (node.nodeType !== Node.ELEMENT_NODE && node.nodeType !== Node.TEXT_NODE && '#text' !== node.nodeName) return false;
|
|
179
|
+
const content = node.textContent || node.innerText;
|
|
180
|
+
if (content && !/^\s*$/.test(content)) return content.trim();
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
function getNodeAttributes(node, currentWindow) {
|
|
184
|
+
if (!node || !(node instanceof currentWindow.HTMLElement) || !node.attributes) return {};
|
|
185
|
+
const attributesList = Array.from(node.attributes).map((attr)=>{
|
|
186
|
+
if ('class' === attr.name) return [
|
|
187
|
+
attr.name,
|
|
188
|
+
`.${attr.value.split(' ').join('.')}`
|
|
189
|
+
];
|
|
190
|
+
let value = attr.value;
|
|
191
|
+
if (value.startsWith('data:image')) value = 'image';
|
|
192
|
+
if (value.length > MAX_VALUE_LENGTH) value = `${value.slice(0, MAX_VALUE_LENGTH)}...`;
|
|
193
|
+
return [
|
|
194
|
+
attr.name,
|
|
195
|
+
value
|
|
196
|
+
];
|
|
197
|
+
});
|
|
198
|
+
return Object.fromEntries(attributesList);
|
|
199
|
+
}
|
|
200
|
+
const NODE_CACHE_MAX_SIZE = 2000;
|
|
201
|
+
function setNodeHashCacheListOnWindow() {
|
|
202
|
+
if ('undefined' != typeof window) window.midsceneNodeHashCache = new Map();
|
|
203
|
+
}
|
|
204
|
+
function getNodeCacheMap() {
|
|
205
|
+
if ('undefined' == typeof window) return;
|
|
206
|
+
return window.midsceneNodeHashCache;
|
|
207
|
+
}
|
|
208
|
+
function setNodeToCacheList(node, id) {
|
|
209
|
+
const cache = getNodeCacheMap();
|
|
210
|
+
if (!cache) return;
|
|
211
|
+
if (cache.has(id)) return;
|
|
212
|
+
if (cache.size >= NODE_CACHE_MAX_SIZE) {
|
|
213
|
+
const firstKey = cache.keys().next().value;
|
|
214
|
+
if (void 0 !== firstKey) cache.delete(firstKey);
|
|
215
|
+
}
|
|
216
|
+
cache.set(id, node);
|
|
217
|
+
}
|
|
218
|
+
function getNodeFromCacheList(id) {
|
|
219
|
+
return getNodeCacheMap()?.get(id);
|
|
220
|
+
}
|
|
221
|
+
function midsceneGenerateHash(node, content, rect) {
|
|
222
|
+
const slicedHash = generateHashId(rect, content);
|
|
223
|
+
if (node) {
|
|
224
|
+
if ('undefined' != typeof window && !getNodeCacheMap()) setNodeHashCacheListOnWindow();
|
|
225
|
+
setNodeToCacheList(node, slicedHash);
|
|
226
|
+
}
|
|
227
|
+
return slicedHash;
|
|
228
|
+
}
|
|
229
|
+
function generateId(numberId) {
|
|
230
|
+
return `${numberId}`;
|
|
231
|
+
}
|
|
232
|
+
function setGenerateHashOnWindow() {
|
|
233
|
+
if ('undefined' != typeof window) window.midsceneGenerateHash = midsceneGenerateHash;
|
|
234
|
+
}
|
|
235
|
+
function setMidsceneVisibleRectOnWindow() {
|
|
236
|
+
if ('undefined' != typeof window) window.midsceneVisibleRect = elementRect;
|
|
237
|
+
}
|
|
238
|
+
function setExtractTextWithPositionOnWindow() {
|
|
239
|
+
if ('undefined' != typeof window) window.extractTextWithPosition = extractTextWithPosition;
|
|
240
|
+
}
|
|
241
|
+
function getTopDocument() {
|
|
242
|
+
const container = document.body || document;
|
|
243
|
+
return container;
|
|
244
|
+
}
|
|
245
|
+
export { elementRect, generateId, getDebugMode, getNodeAttributes, getNodeFromCacheList, getPseudoElementContent, getRect, getTopDocument, hasOverflowY, isElementPartiallyInViewport, logger, midsceneGenerateHash, overlappedRect, setDebugMode, setExtractTextWithPositionOnWindow, setGenerateHashOnWindow, setMidsceneVisibleRectOnWindow, setNodeHashCacheListOnWindow, setNodeToCacheList, validTextNodeContent };
|