@chat-de-hp/site 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +0 -0
- package/dist/internal/annotation-integration.d.ts.map +1 -1
- package/dist/internal/annotation-integration.js +8 -1
- package/package.json +1 -14
- package/dist/annotation-middleware.d.ts +0 -4
- package/dist/annotation-middleware.d.ts.map +0 -1
- package/dist/annotation-middleware.js +0 -20
- package/dist/internal/annotation-context.d.ts +0 -3
- package/dist/internal/annotation-context.d.ts.map +0 -1
- package/dist/internal/annotation-context.js +0 -14
- package/dist/runtime/annotation-capture.d.ts +0 -2
- package/dist/runtime/annotation-capture.d.ts.map +0 -1
- package/dist/runtime/annotation-capture.js +0 -231
- package/dist/runtime/capture-contract.d.ts +0 -71
- package/dist/runtime/capture-contract.d.ts.map +0 -1
- package/dist/runtime/capture-contract.js +0 -126
- package/dist/runtime/capture-viewport.d.ts +0 -4
- package/dist/runtime/capture-viewport.d.ts.map +0 -1
- package/dist/runtime/capture-viewport.js +0 -24
- package/dist/runtime/element-extractor.d.ts +0 -21
- package/dist/runtime/element-extractor.d.ts.map +0 -1
- package/dist/runtime/element-extractor.js +0 -258
package/dist/cli.js
CHANGED
|
File without changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"annotation-integration.d.ts","sourceRoot":"","sources":["../../src/internal/annotation-integration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"annotation-integration.d.ts","sourceRoot":"","sources":["../../src/internal/annotation-integration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAC;AAY9C,wBAAgB,kCAAkC,IAAI,gBAAgB,CAcrE"}
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
const ANNOTATION_INTEGRATION_NAME = "@chat-de-hp/site/public-annotations";
|
|
2
|
+
const CAPTURE_CLIENT_PATH = "/__chat_de_hp/client/capture.js";
|
|
3
|
+
const CAPTURE_CLIENT_LOADER = `if (window.parent !== window) {
|
|
4
|
+
const script = document.createElement("script");
|
|
5
|
+
script.type = "module";
|
|
6
|
+
script.src = ${JSON.stringify(CAPTURE_CLIENT_PATH)};
|
|
7
|
+
document.head.append(script);
|
|
8
|
+
}`;
|
|
2
9
|
export function createPublicAnnotationsIntegration() {
|
|
3
10
|
return {
|
|
4
11
|
hooks: {
|
|
5
12
|
"astro:config:setup": ({ injectScript, updateConfig }) => {
|
|
6
|
-
injectScript("page",
|
|
13
|
+
injectScript("page", CAPTURE_CLIENT_LOADER);
|
|
7
14
|
updateConfig({
|
|
8
15
|
vite: {
|
|
9
16
|
plugins: [createEmDashAnnotationResolver()],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chat-de-hp/site",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Managed Astro site runtime and primitive composition for Chat de HP",
|
|
6
6
|
"license": "MIT",
|
|
@@ -34,18 +34,6 @@
|
|
|
34
34
|
"types": "./dist/runtime/contracts.d.ts",
|
|
35
35
|
"default": "./dist/runtime/contracts.js"
|
|
36
36
|
},
|
|
37
|
-
"./runtime/annotation-capture": {
|
|
38
|
-
"types": "./dist/runtime/annotation-capture.d.ts",
|
|
39
|
-
"default": "./dist/runtime/annotation-capture.js"
|
|
40
|
-
},
|
|
41
|
-
"./runtime/capture-contract": {
|
|
42
|
-
"types": "./dist/runtime/capture-contract.d.ts",
|
|
43
|
-
"default": "./dist/runtime/capture-contract.js"
|
|
44
|
-
},
|
|
45
|
-
"./runtime/element-extractor": {
|
|
46
|
-
"types": "./dist/runtime/element-extractor.d.ts",
|
|
47
|
-
"default": "./dist/runtime/element-extractor.js"
|
|
48
|
-
},
|
|
49
37
|
"./control-plane": {
|
|
50
38
|
"types": "./dist/control-plane.d.ts",
|
|
51
39
|
"default": "./dist/control-plane.js"
|
|
@@ -86,7 +74,6 @@
|
|
|
86
74
|
"@types/node": "20",
|
|
87
75
|
"astro": "7.0.4",
|
|
88
76
|
"emdash": "0.31.1",
|
|
89
|
-
"happy-dom": "20.11.1",
|
|
90
77
|
"react": "19.2.7",
|
|
91
78
|
"react-dom": "19.2.7",
|
|
92
79
|
"typescript": "5.8.3",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"annotation-middleware.d.ts","sourceRoot":"","sources":["../src/annotation-middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAK/C,eAAO,MAAM,SAAS,EAAE,iBAiBvB,CAAC;AAEF,eAAe,SAAS,CAAC"}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { runWithAnnotationMode } from "./internal/annotation-context.js";
|
|
2
|
-
import { CHAT_DE_HP_ANNOTATION_MODE_HEADER } from "./runtime/contracts.js";
|
|
3
|
-
export const onRequest = (context, next) => {
|
|
4
|
-
if (context.request.headers.get(CHAT_DE_HP_ANNOTATION_MODE_HEADER) !== "1") {
|
|
5
|
-
return next();
|
|
6
|
-
}
|
|
7
|
-
context.cache.set(false);
|
|
8
|
-
return runWithAnnotationMode(async () => {
|
|
9
|
-
const response = await next();
|
|
10
|
-
const headers = new Headers(response.headers);
|
|
11
|
-
headers.set("cache-control", "private, no-store");
|
|
12
|
-
headers.set("x-robots-tag", "noindex, nofollow");
|
|
13
|
-
return new Response(response.body, {
|
|
14
|
-
headers,
|
|
15
|
-
status: response.status,
|
|
16
|
-
statusText: response.statusText,
|
|
17
|
-
});
|
|
18
|
-
});
|
|
19
|
-
};
|
|
20
|
-
export default onRequest;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"annotation-context.d.ts","sourceRoot":"","sources":["../../src/internal/annotation-context.ts"],"names":[],"mappings":"AAcA,wBAAgB,gBAAgB,YAE/B;AAED,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,CAE7D"}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
-
const ANNOTATION_CONTEXT_KEY = Symbol.for("chat-de-hp:annotation-only-context");
|
|
3
|
-
const storage = globalThis[ANNOTATION_CONTEXT_KEY] ??
|
|
4
|
-
(() => {
|
|
5
|
-
const context = new AsyncLocalStorage();
|
|
6
|
-
globalThis[ANNOTATION_CONTEXT_KEY] = context;
|
|
7
|
-
return context;
|
|
8
|
-
})();
|
|
9
|
-
export function isAnnotationMode() {
|
|
10
|
-
return storage.getStore() === true;
|
|
11
|
-
}
|
|
12
|
-
export function runWithAnnotationMode(callback) {
|
|
13
|
-
return storage.run(true, callback);
|
|
14
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"annotation-capture.d.ts","sourceRoot":"","sources":["../../src/runtime/annotation-capture.ts"],"names":[],"mappings":"AAkBA,wBAAgB,sBAAsB,eAqHrC"}
|
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
import { isExpectedSiteCaptureParentMessage, SITE_CAPTURE_BOOT_PAGE_HASH_PARAM, SITE_CAPTURE_BOOT_PARENT_ORIGIN_PARAM, SITE_CAPTURE_MESSAGE_TYPE, SITE_CAPTURE_SESSION_STORAGE_KEY, } from "./capture-contract.js";
|
|
2
|
-
import { freezeCaptureViewport } from "./capture-viewport.js";
|
|
3
|
-
import { extractElements } from "./element-extractor.js";
|
|
4
|
-
let captureFrozen = false;
|
|
5
|
-
let resumeViewport = null;
|
|
6
|
-
export function startAnnotationCapture() {
|
|
7
|
-
if (window.parent === window) {
|
|
8
|
-
return () => { };
|
|
9
|
-
}
|
|
10
|
-
const session = readCaptureSession();
|
|
11
|
-
if (!session) {
|
|
12
|
-
return () => { };
|
|
13
|
-
}
|
|
14
|
-
const onMessage = (event) => {
|
|
15
|
-
if (!isExpectedSiteCaptureParentMessage(event, {
|
|
16
|
-
origin: session.parentOrigin,
|
|
17
|
-
source: window.parent,
|
|
18
|
-
})) {
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
if (event.data.type === SITE_CAPTURE_MESSAGE_TYPE.reload) {
|
|
22
|
-
window.location.reload();
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
if (event.data.type === SITE_CAPTURE_MESSAGE_TYPE.end) {
|
|
26
|
-
cleanup();
|
|
27
|
-
endAnnotationSession(session, true);
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
if (event.data.type === SITE_CAPTURE_MESSAGE_TYPE.probe) {
|
|
31
|
-
postReady(session);
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
if (event.data.type === SITE_CAPTURE_MESSAGE_TYPE.resume) {
|
|
35
|
-
resumeViewport?.();
|
|
36
|
-
resumeViewport = null;
|
|
37
|
-
captureFrozen = false;
|
|
38
|
-
postReady(session);
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
if (captureFrozen) {
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
captureCurrentViewport(session);
|
|
45
|
-
};
|
|
46
|
-
const onBeforeUnload = () => {
|
|
47
|
-
postToParent(session, {
|
|
48
|
-
type: SITE_CAPTURE_MESSAGE_TYPE.loading,
|
|
49
|
-
});
|
|
50
|
-
};
|
|
51
|
-
const onClick = (event) => {
|
|
52
|
-
if (event.defaultPrevented || event.button !== 0) {
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
const target = event.target;
|
|
56
|
-
if (!(target instanceof Element)) {
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
const anchor = target.closest("a[href]");
|
|
60
|
-
if (!anchor) {
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
try {
|
|
64
|
-
const destination = new URL(anchor.href, window.location.href);
|
|
65
|
-
if ((destination.protocol === "https:" ||
|
|
66
|
-
destination.protocol === "http:") &&
|
|
67
|
-
destination.origin !== window.location.origin) {
|
|
68
|
-
event.preventDefault();
|
|
69
|
-
postToParent(session, {
|
|
70
|
-
type: SITE_CAPTURE_MESSAGE_TYPE.externalLinkBlocked,
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
catch {
|
|
75
|
-
// Leave malformed and non-navigation href values to the browser.
|
|
76
|
-
}
|
|
77
|
-
};
|
|
78
|
-
const onSubmit = (event) => {
|
|
79
|
-
event.preventDefault();
|
|
80
|
-
event.stopImmediatePropagation();
|
|
81
|
-
postToParent(session, {
|
|
82
|
-
type: SITE_CAPTURE_MESSAGE_TYPE.formSubmissionBlocked,
|
|
83
|
-
});
|
|
84
|
-
};
|
|
85
|
-
let pendingScrollFrame = null;
|
|
86
|
-
const onScroll = () => {
|
|
87
|
-
if (pendingScrollFrame !== null) {
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
pendingScrollFrame = window.requestAnimationFrame(() => {
|
|
91
|
-
pendingScrollFrame = null;
|
|
92
|
-
postToParent(session, {
|
|
93
|
-
type: SITE_CAPTURE_MESSAGE_TYPE.scroll,
|
|
94
|
-
});
|
|
95
|
-
});
|
|
96
|
-
};
|
|
97
|
-
window.addEventListener("message", onMessage);
|
|
98
|
-
window.addEventListener("beforeunload", onBeforeUnload);
|
|
99
|
-
window.addEventListener("scroll", onScroll, { passive: true });
|
|
100
|
-
document.addEventListener("click", onClick, true);
|
|
101
|
-
document.addEventListener("submit", onSubmit, true);
|
|
102
|
-
const cleanup = () => {
|
|
103
|
-
window.removeEventListener("message", onMessage);
|
|
104
|
-
window.removeEventListener("beforeunload", onBeforeUnload);
|
|
105
|
-
window.removeEventListener("scroll", onScroll);
|
|
106
|
-
document.removeEventListener("click", onClick, true);
|
|
107
|
-
document.removeEventListener("submit", onSubmit, true);
|
|
108
|
-
if (pendingScrollFrame !== null) {
|
|
109
|
-
window.cancelAnimationFrame(pendingScrollFrame);
|
|
110
|
-
pendingScrollFrame = null;
|
|
111
|
-
}
|
|
112
|
-
};
|
|
113
|
-
postReady(session);
|
|
114
|
-
return cleanup;
|
|
115
|
-
}
|
|
116
|
-
function readCaptureSession() {
|
|
117
|
-
const bootstrap = readCaptureBootstrap();
|
|
118
|
-
if (bootstrap) {
|
|
119
|
-
try {
|
|
120
|
-
window.sessionStorage.setItem(SITE_CAPTURE_SESSION_STORAGE_KEY, JSON.stringify(bootstrap));
|
|
121
|
-
}
|
|
122
|
-
catch {
|
|
123
|
-
// The active page can still capture even if browser storage is blocked.
|
|
124
|
-
}
|
|
125
|
-
return bootstrap;
|
|
126
|
-
}
|
|
127
|
-
try {
|
|
128
|
-
return parseStoredSession(window.sessionStorage.getItem(SITE_CAPTURE_SESSION_STORAGE_KEY));
|
|
129
|
-
}
|
|
130
|
-
catch {
|
|
131
|
-
return null;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
function readCaptureBootstrap() {
|
|
135
|
-
const hash = new URLSearchParams(window.location.hash.slice(1));
|
|
136
|
-
const parentOrigin = normalizeOrigin(hash.get(SITE_CAPTURE_BOOT_PARENT_ORIGIN_PARAM));
|
|
137
|
-
if (!parentOrigin) {
|
|
138
|
-
return null;
|
|
139
|
-
}
|
|
140
|
-
const pageHash = hash.get(SITE_CAPTURE_BOOT_PAGE_HASH_PARAM);
|
|
141
|
-
const nextUrl = `${window.location.pathname}${window.location.search}${pageHash ? `#${pageHash}` : ""}`;
|
|
142
|
-
window.history.replaceState(window.history.state, "", nextUrl);
|
|
143
|
-
return { parentOrigin };
|
|
144
|
-
}
|
|
145
|
-
function parseStoredSession(value) {
|
|
146
|
-
if (!value) {
|
|
147
|
-
return null;
|
|
148
|
-
}
|
|
149
|
-
try {
|
|
150
|
-
const parsed = JSON.parse(value);
|
|
151
|
-
if (!parsed || typeof parsed !== "object") {
|
|
152
|
-
return null;
|
|
153
|
-
}
|
|
154
|
-
const parentOrigin = normalizeOrigin(Reflect.get(parsed, "parentOrigin"));
|
|
155
|
-
return parentOrigin ? { parentOrigin } : null;
|
|
156
|
-
}
|
|
157
|
-
catch {
|
|
158
|
-
return null;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
function normalizeOrigin(value) {
|
|
162
|
-
if (typeof value !== "string") {
|
|
163
|
-
return null;
|
|
164
|
-
}
|
|
165
|
-
try {
|
|
166
|
-
const url = new URL(value);
|
|
167
|
-
if ((url.protocol !== "https:" && url.protocol !== "http:") ||
|
|
168
|
-
url.origin !== value) {
|
|
169
|
-
return null;
|
|
170
|
-
}
|
|
171
|
-
return url.origin;
|
|
172
|
-
}
|
|
173
|
-
catch {
|
|
174
|
-
return null;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
function currentPagePath() {
|
|
178
|
-
return `${window.location.pathname}${window.location.search}${window.location.hash}`;
|
|
179
|
-
}
|
|
180
|
-
function captureCurrentViewport(session) {
|
|
181
|
-
try {
|
|
182
|
-
const root = document.body ?? document.documentElement;
|
|
183
|
-
const elements = extractElements(root);
|
|
184
|
-
resumeViewport = freezeCaptureViewport();
|
|
185
|
-
captureFrozen = true;
|
|
186
|
-
postToParent(session, {
|
|
187
|
-
elements,
|
|
188
|
-
pagePath: currentPagePath(),
|
|
189
|
-
type: SITE_CAPTURE_MESSAGE_TYPE.captured,
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
catch {
|
|
193
|
-
postToParent(session, {
|
|
194
|
-
message: "画面を読み取れませんでした。",
|
|
195
|
-
type: SITE_CAPTURE_MESSAGE_TYPE.error,
|
|
196
|
-
});
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
function endAnnotationSession(session, notifyParent) {
|
|
200
|
-
resumeViewport?.();
|
|
201
|
-
resumeViewport = null;
|
|
202
|
-
captureFrozen = false;
|
|
203
|
-
try {
|
|
204
|
-
window.sessionStorage.removeItem(SITE_CAPTURE_SESSION_STORAGE_KEY);
|
|
205
|
-
}
|
|
206
|
-
catch {
|
|
207
|
-
// The capture bridge is inactive after its message listeners are removed.
|
|
208
|
-
}
|
|
209
|
-
if (notifyParent) {
|
|
210
|
-
postToParent(session, {
|
|
211
|
-
type: SITE_CAPTURE_MESSAGE_TYPE.ended,
|
|
212
|
-
});
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
function postToParent(session, message) {
|
|
216
|
-
window.parent.postMessage(message, session.parentOrigin);
|
|
217
|
-
}
|
|
218
|
-
function postReady(session) {
|
|
219
|
-
postToParent(session, {
|
|
220
|
-
pagePath: currentPagePath(),
|
|
221
|
-
type: SITE_CAPTURE_MESSAGE_TYPE.ready,
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
if (document.readyState === "loading") {
|
|
225
|
-
document.addEventListener("DOMContentLoaded", startAnnotationCapture, {
|
|
226
|
-
once: true,
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
else {
|
|
230
|
-
startAnnotationCapture();
|
|
231
|
-
}
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import type { ExtractedElement } from "./element-extractor.js";
|
|
2
|
-
export declare const SITE_CAPTURE_BOOT_PARENT_ORIGIN_PARAM = "__chat_de_hp_capture_parent";
|
|
3
|
-
export declare const SITE_CAPTURE_BOOT_PAGE_HASH_PARAM = "__chat_de_hp_capture_page_hash";
|
|
4
|
-
export declare const SITE_CAPTURE_SESSION_STORAGE_KEY = "chat-de-hp:capture-session";
|
|
5
|
-
export declare const SITE_CAPTURE_MESSAGE_TYPE: {
|
|
6
|
-
readonly capture: "chat-de-hp:capture";
|
|
7
|
-
readonly captured: "chat-de-hp:captured";
|
|
8
|
-
readonly end: "chat-de-hp:end-capture";
|
|
9
|
-
readonly ended: "chat-de-hp:capture-ended";
|
|
10
|
-
readonly error: "chat-de-hp:capture-error";
|
|
11
|
-
readonly externalLinkBlocked: "chat-de-hp:external-link-blocked";
|
|
12
|
-
readonly formSubmissionBlocked: "chat-de-hp:form-submission-blocked";
|
|
13
|
-
readonly loading: "chat-de-hp:capture-loading";
|
|
14
|
-
readonly probe: "chat-de-hp:capture-probe";
|
|
15
|
-
readonly ready: "chat-de-hp:capture-ready";
|
|
16
|
-
readonly reload: "chat-de-hp:reload";
|
|
17
|
-
readonly resume: "chat-de-hp:resume-capture";
|
|
18
|
-
readonly scroll: "chat-de-hp:scroll";
|
|
19
|
-
};
|
|
20
|
-
export type SiteCaptureParentMessage = {
|
|
21
|
-
type: typeof SITE_CAPTURE_MESSAGE_TYPE.capture;
|
|
22
|
-
} | {
|
|
23
|
-
type: typeof SITE_CAPTURE_MESSAGE_TYPE.end;
|
|
24
|
-
} | {
|
|
25
|
-
type: typeof SITE_CAPTURE_MESSAGE_TYPE.probe;
|
|
26
|
-
} | {
|
|
27
|
-
type: typeof SITE_CAPTURE_MESSAGE_TYPE.reload;
|
|
28
|
-
} | {
|
|
29
|
-
type: typeof SITE_CAPTURE_MESSAGE_TYPE.resume;
|
|
30
|
-
};
|
|
31
|
-
export type SiteCaptureFrameMessage = {
|
|
32
|
-
pagePath: string;
|
|
33
|
-
type: typeof SITE_CAPTURE_MESSAGE_TYPE.ready;
|
|
34
|
-
} | {
|
|
35
|
-
elements: ExtractedElement[];
|
|
36
|
-
pagePath: string;
|
|
37
|
-
type: typeof SITE_CAPTURE_MESSAGE_TYPE.captured;
|
|
38
|
-
} | {
|
|
39
|
-
message: string;
|
|
40
|
-
type: typeof SITE_CAPTURE_MESSAGE_TYPE.error;
|
|
41
|
-
} | {
|
|
42
|
-
type: typeof SITE_CAPTURE_MESSAGE_TYPE.externalLinkBlocked;
|
|
43
|
-
} | {
|
|
44
|
-
type: typeof SITE_CAPTURE_MESSAGE_TYPE.formSubmissionBlocked;
|
|
45
|
-
} | {
|
|
46
|
-
type: typeof SITE_CAPTURE_MESSAGE_TYPE.loading;
|
|
47
|
-
} | {
|
|
48
|
-
type: typeof SITE_CAPTURE_MESSAGE_TYPE.ended;
|
|
49
|
-
} | {
|
|
50
|
-
type: typeof SITE_CAPTURE_MESSAGE_TYPE.scroll;
|
|
51
|
-
};
|
|
52
|
-
type CaptureMessageEvent = {
|
|
53
|
-
data: unknown;
|
|
54
|
-
origin: string;
|
|
55
|
-
source: unknown;
|
|
56
|
-
};
|
|
57
|
-
type CaptureMessageExpectation = {
|
|
58
|
-
origin: string;
|
|
59
|
-
source: unknown;
|
|
60
|
-
};
|
|
61
|
-
export declare function isExpectedSiteCaptureParentMessage(event: CaptureMessageEvent, expected: CaptureMessageExpectation): event is CaptureMessageEvent & {
|
|
62
|
-
data: SiteCaptureParentMessage;
|
|
63
|
-
};
|
|
64
|
-
export declare function isExpectedSiteCaptureFrameMessage(event: CaptureMessageEvent, expected: CaptureMessageExpectation): event is CaptureMessageEvent & {
|
|
65
|
-
data: SiteCaptureFrameMessage;
|
|
66
|
-
};
|
|
67
|
-
export declare function isSiteCaptureFrameProtocolMessage(value: unknown): boolean;
|
|
68
|
-
export declare function isSiteCaptureParentMessage(value: unknown): value is SiteCaptureParentMessage;
|
|
69
|
-
export declare function isSiteCaptureFrameMessage(value: unknown): value is SiteCaptureFrameMessage;
|
|
70
|
-
export {};
|
|
71
|
-
//# sourceMappingURL=capture-contract.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"capture-contract.d.ts","sourceRoot":"","sources":["../../src/runtime/capture-contract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE/D,eAAO,MAAM,qCAAqC,gCACnB,CAAC;AAChC,eAAO,MAAM,iCAAiC,mCACZ,CAAC;AACnC,eAAO,MAAM,gCAAgC,+BAA+B,CAAC;AAE7E,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;CAc5B,CAAC;AAEX,MAAM,MAAM,wBAAwB,GAChC;IACE,IAAI,EAAE,OAAO,yBAAyB,CAAC,OAAO,CAAC;CAChD,GACD;IACE,IAAI,EAAE,OAAO,yBAAyB,CAAC,GAAG,CAAC;CAC5C,GACD;IACE,IAAI,EAAE,OAAO,yBAAyB,CAAC,KAAK,CAAC;CAC9C,GACD;IACE,IAAI,EAAE,OAAO,yBAAyB,CAAC,MAAM,CAAC;CAC/C,GACD;IACE,IAAI,EAAE,OAAO,yBAAyB,CAAC,MAAM,CAAC;CAC/C,CAAC;AAEN,MAAM,MAAM,uBAAuB,GAC/B;IACE,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,OAAO,yBAAyB,CAAC,KAAK,CAAC;CAC9C,GACD;IACE,QAAQ,EAAE,gBAAgB,EAAE,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,OAAO,yBAAyB,CAAC,QAAQ,CAAC;CACjD,GACD;IACE,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,OAAO,yBAAyB,CAAC,KAAK,CAAC;CAC9C,GACD;IACE,IAAI,EAAE,OAAO,yBAAyB,CAAC,mBAAmB,CAAC;CAC5D,GACD;IACE,IAAI,EAAE,OAAO,yBAAyB,CAAC,qBAAqB,CAAC;CAC9D,GACD;IACE,IAAI,EAAE,OAAO,yBAAyB,CAAC,OAAO,CAAC;CAChD,GACD;IACE,IAAI,EAAE,OAAO,yBAAyB,CAAC,KAAK,CAAC;CAC9C,GACD;IACE,IAAI,EAAE,OAAO,yBAAyB,CAAC,MAAM,CAAC;CAC/C,CAAC;AAEN,KAAK,mBAAmB,GAAG;IACzB,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,KAAK,yBAAyB,GAAG;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,wBAAgB,kCAAkC,CAChD,KAAK,EAAE,mBAAmB,EAC1B,QAAQ,EAAE,yBAAyB,GAClC,KAAK,IAAI,mBAAmB,GAAG;IAAE,IAAI,EAAE,wBAAwB,CAAA;CAAE,CAMnE;AAED,wBAAgB,iCAAiC,CAC/C,KAAK,EAAE,mBAAmB,EAC1B,QAAQ,EAAE,yBAAyB,GAClC,KAAK,IAAI,mBAAmB,GAAG;IAAE,IAAI,EAAE,uBAAuB,CAAA;CAAE,CAMlE;AAED,wBAAgB,iCAAiC,CAAC,KAAK,EAAE,OAAO,WAe/D;AAED,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,wBAAwB,CAWnC;AAED,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,uBAAuB,CAyBlC"}
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
export const SITE_CAPTURE_BOOT_PARENT_ORIGIN_PARAM = "__chat_de_hp_capture_parent";
|
|
2
|
-
export const SITE_CAPTURE_BOOT_PAGE_HASH_PARAM = "__chat_de_hp_capture_page_hash";
|
|
3
|
-
export const SITE_CAPTURE_SESSION_STORAGE_KEY = "chat-de-hp:capture-session";
|
|
4
|
-
export const SITE_CAPTURE_MESSAGE_TYPE = {
|
|
5
|
-
capture: "chat-de-hp:capture",
|
|
6
|
-
captured: "chat-de-hp:captured",
|
|
7
|
-
end: "chat-de-hp:end-capture",
|
|
8
|
-
ended: "chat-de-hp:capture-ended",
|
|
9
|
-
error: "chat-de-hp:capture-error",
|
|
10
|
-
externalLinkBlocked: "chat-de-hp:external-link-blocked",
|
|
11
|
-
formSubmissionBlocked: "chat-de-hp:form-submission-blocked",
|
|
12
|
-
loading: "chat-de-hp:capture-loading",
|
|
13
|
-
probe: "chat-de-hp:capture-probe",
|
|
14
|
-
ready: "chat-de-hp:capture-ready",
|
|
15
|
-
reload: "chat-de-hp:reload",
|
|
16
|
-
resume: "chat-de-hp:resume-capture",
|
|
17
|
-
scroll: "chat-de-hp:scroll",
|
|
18
|
-
};
|
|
19
|
-
export function isExpectedSiteCaptureParentMessage(event, expected) {
|
|
20
|
-
return (event.origin === expected.origin &&
|
|
21
|
-
event.source === expected.source &&
|
|
22
|
-
isSiteCaptureParentMessage(event.data));
|
|
23
|
-
}
|
|
24
|
-
export function isExpectedSiteCaptureFrameMessage(event, expected) {
|
|
25
|
-
return (event.origin === expected.origin &&
|
|
26
|
-
event.source === expected.source &&
|
|
27
|
-
isSiteCaptureFrameMessage(event.data));
|
|
28
|
-
}
|
|
29
|
-
export function isSiteCaptureFrameProtocolMessage(value) {
|
|
30
|
-
if (!isRecord(value)) {
|
|
31
|
-
return false;
|
|
32
|
-
}
|
|
33
|
-
const { type } = value;
|
|
34
|
-
return (type === SITE_CAPTURE_MESSAGE_TYPE.captured ||
|
|
35
|
-
type === SITE_CAPTURE_MESSAGE_TYPE.ended ||
|
|
36
|
-
type === SITE_CAPTURE_MESSAGE_TYPE.error ||
|
|
37
|
-
type === SITE_CAPTURE_MESSAGE_TYPE.externalLinkBlocked ||
|
|
38
|
-
type === SITE_CAPTURE_MESSAGE_TYPE.formSubmissionBlocked ||
|
|
39
|
-
type === SITE_CAPTURE_MESSAGE_TYPE.loading ||
|
|
40
|
-
type === SITE_CAPTURE_MESSAGE_TYPE.ready ||
|
|
41
|
-
type === SITE_CAPTURE_MESSAGE_TYPE.scroll);
|
|
42
|
-
}
|
|
43
|
-
export function isSiteCaptureParentMessage(value) {
|
|
44
|
-
if (!isRecord(value)) {
|
|
45
|
-
return false;
|
|
46
|
-
}
|
|
47
|
-
return (value.type === SITE_CAPTURE_MESSAGE_TYPE.capture ||
|
|
48
|
-
value.type === SITE_CAPTURE_MESSAGE_TYPE.end ||
|
|
49
|
-
value.type === SITE_CAPTURE_MESSAGE_TYPE.probe ||
|
|
50
|
-
value.type === SITE_CAPTURE_MESSAGE_TYPE.reload ||
|
|
51
|
-
value.type === SITE_CAPTURE_MESSAGE_TYPE.resume);
|
|
52
|
-
}
|
|
53
|
-
export function isSiteCaptureFrameMessage(value) {
|
|
54
|
-
if (!isRecord(value)) {
|
|
55
|
-
return false;
|
|
56
|
-
}
|
|
57
|
-
if (value.type === SITE_CAPTURE_MESSAGE_TYPE.ready) {
|
|
58
|
-
return isPagePath(value.pagePath);
|
|
59
|
-
}
|
|
60
|
-
if (value.type === SITE_CAPTURE_MESSAGE_TYPE.captured) {
|
|
61
|
-
return (isPagePath(value.pagePath) &&
|
|
62
|
-
Array.isArray(value.elements) &&
|
|
63
|
-
value.elements.every(isExtractedElement));
|
|
64
|
-
}
|
|
65
|
-
if (value.type === SITE_CAPTURE_MESSAGE_TYPE.error) {
|
|
66
|
-
return isNonEmptyString(value.message);
|
|
67
|
-
}
|
|
68
|
-
return (value.type === SITE_CAPTURE_MESSAGE_TYPE.ended ||
|
|
69
|
-
value.type === SITE_CAPTURE_MESSAGE_TYPE.externalLinkBlocked ||
|
|
70
|
-
value.type === SITE_CAPTURE_MESSAGE_TYPE.formSubmissionBlocked ||
|
|
71
|
-
value.type === SITE_CAPTURE_MESSAGE_TYPE.loading ||
|
|
72
|
-
value.type === SITE_CAPTURE_MESSAGE_TYPE.scroll);
|
|
73
|
-
}
|
|
74
|
-
function isExtractedElement(value) {
|
|
75
|
-
if (!isRecord(value) ||
|
|
76
|
-
!isNonEmptyString(value.elementId) ||
|
|
77
|
-
!isNonEmptyString(value.role) ||
|
|
78
|
-
!isNormalizedRect(value.bounds)) {
|
|
79
|
-
return false;
|
|
80
|
-
}
|
|
81
|
-
if (value.text !== undefined && typeof value.text !== "string") {
|
|
82
|
-
return false;
|
|
83
|
-
}
|
|
84
|
-
if (value.section !== undefined && typeof value.section !== "string") {
|
|
85
|
-
return false;
|
|
86
|
-
}
|
|
87
|
-
if (value.cmsRef === undefined) {
|
|
88
|
-
return true;
|
|
89
|
-
}
|
|
90
|
-
return (isRecord(value.cmsRef) &&
|
|
91
|
-
isNonEmptyString(value.cmsRef.collection) &&
|
|
92
|
-
isNonEmptyString(value.cmsRef.id) &&
|
|
93
|
-
(value.cmsRef.field === undefined || isNonEmptyString(value.cmsRef.field)));
|
|
94
|
-
}
|
|
95
|
-
function isNormalizedRect(value) {
|
|
96
|
-
if (!isRecord(value)) {
|
|
97
|
-
return false;
|
|
98
|
-
}
|
|
99
|
-
const { height, width, x, y } = value;
|
|
100
|
-
if (!isUnitNumber(height) ||
|
|
101
|
-
!isUnitNumber(width) ||
|
|
102
|
-
!isUnitNumber(x) ||
|
|
103
|
-
!isUnitNumber(y)) {
|
|
104
|
-
return false;
|
|
105
|
-
}
|
|
106
|
-
const tolerance = Number.EPSILON * 4;
|
|
107
|
-
return x + width <= 1 + tolerance && y + height <= 1 + tolerance;
|
|
108
|
-
}
|
|
109
|
-
function isUnitNumber(value) {
|
|
110
|
-
return (typeof value === "number" &&
|
|
111
|
-
Number.isFinite(value) &&
|
|
112
|
-
value >= 0 &&
|
|
113
|
-
value <= 1);
|
|
114
|
-
}
|
|
115
|
-
function isPagePath(value) {
|
|
116
|
-
return (typeof value === "string" &&
|
|
117
|
-
value.startsWith("/") &&
|
|
118
|
-
!value.startsWith("//") &&
|
|
119
|
-
!value.includes("\\"));
|
|
120
|
-
}
|
|
121
|
-
function isNonEmptyString(value) {
|
|
122
|
-
return typeof value === "string" && value.trim().length > 0;
|
|
123
|
-
}
|
|
124
|
-
function isRecord(value) {
|
|
125
|
-
return typeof value === "object" && value !== null;
|
|
126
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"capture-viewport.d.ts","sourceRoot":"","sources":["../../src/runtime/capture-viewport.ts"],"names":[],"mappings":"AAAA,KAAK,qBAAqB,GAAG,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;AAEtD,wBAAgB,qBAAqB,CACnC,YAAY,GAAE,qBAA8B,cA0B7C"}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
export function freezeCaptureViewport(targetWindow = window) {
|
|
2
|
-
const { body, documentElement } = targetWindow.document;
|
|
3
|
-
const documentStyles = {
|
|
4
|
-
overflow: documentElement.style.overflow,
|
|
5
|
-
touchAction: documentElement.style.touchAction,
|
|
6
|
-
};
|
|
7
|
-
const bodyStyles = {
|
|
8
|
-
overflow: body.style.overflow,
|
|
9
|
-
pointerEvents: body.style.pointerEvents,
|
|
10
|
-
touchAction: body.style.touchAction,
|
|
11
|
-
};
|
|
12
|
-
documentElement.style.overflow = "hidden";
|
|
13
|
-
documentElement.style.touchAction = "none";
|
|
14
|
-
body.style.overflow = "hidden";
|
|
15
|
-
body.style.pointerEvents = "none";
|
|
16
|
-
body.style.touchAction = "none";
|
|
17
|
-
return () => {
|
|
18
|
-
documentElement.style.overflow = documentStyles.overflow;
|
|
19
|
-
documentElement.style.touchAction = documentStyles.touchAction;
|
|
20
|
-
body.style.overflow = bodyStyles.overflow;
|
|
21
|
-
body.style.pointerEvents = bodyStyles.pointerEvents;
|
|
22
|
-
body.style.touchAction = bodyStyles.touchAction;
|
|
23
|
-
};
|
|
24
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
export type NormalizedRect = {
|
|
2
|
-
x: number;
|
|
3
|
-
y: number;
|
|
4
|
-
width: number;
|
|
5
|
-
height: number;
|
|
6
|
-
};
|
|
7
|
-
export type CmsRef = {
|
|
8
|
-
collection: string;
|
|
9
|
-
id: string;
|
|
10
|
-
field?: string;
|
|
11
|
-
};
|
|
12
|
-
export type ExtractedElement = {
|
|
13
|
-
elementId: string;
|
|
14
|
-
text?: string;
|
|
15
|
-
role: string;
|
|
16
|
-
section?: string;
|
|
17
|
-
bounds: NormalizedRect;
|
|
18
|
-
cmsRef?: CmsRef;
|
|
19
|
-
};
|
|
20
|
-
export declare function extractElements(root: Element): ExtractedElement[];
|
|
21
|
-
//# sourceMappingURL=element-extractor.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"element-extractor.d.ts","sourceRoot":"","sources":["../../src/runtime/element-extractor.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,cAAc,GAAG;IAC3B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,MAAM,GAAG;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,cAAc,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAuEF,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,GAAG,gBAAgB,EAAE,CAqDjE"}
|
|
@@ -1,258 +0,0 @@
|
|
|
1
|
-
const BLOCK_ELEMENTS = new Set([
|
|
2
|
-
"ADDRESS",
|
|
3
|
-
"ARTICLE",
|
|
4
|
-
"ASIDE",
|
|
5
|
-
"BLOCKQUOTE",
|
|
6
|
-
"DD",
|
|
7
|
-
"DIV",
|
|
8
|
-
"DL",
|
|
9
|
-
"DT",
|
|
10
|
-
"FIGCAPTION",
|
|
11
|
-
"FIGURE",
|
|
12
|
-
"FOOTER",
|
|
13
|
-
"FORM",
|
|
14
|
-
"H1",
|
|
15
|
-
"H2",
|
|
16
|
-
"H3",
|
|
17
|
-
"H4",
|
|
18
|
-
"H5",
|
|
19
|
-
"H6",
|
|
20
|
-
"HEADER",
|
|
21
|
-
"LI",
|
|
22
|
-
"MAIN",
|
|
23
|
-
"NAV",
|
|
24
|
-
"OL",
|
|
25
|
-
"P",
|
|
26
|
-
"PRE",
|
|
27
|
-
"SECTION",
|
|
28
|
-
"TABLE",
|
|
29
|
-
"TBODY",
|
|
30
|
-
"TD",
|
|
31
|
-
"TFOOT",
|
|
32
|
-
"TH",
|
|
33
|
-
"THEAD",
|
|
34
|
-
"TR",
|
|
35
|
-
"UL",
|
|
36
|
-
]);
|
|
37
|
-
const NON_RENDERED_ELEMENTS = new Set([
|
|
38
|
-
"LINK",
|
|
39
|
-
"META",
|
|
40
|
-
"NOSCRIPT",
|
|
41
|
-
"SCRIPT",
|
|
42
|
-
"STYLE",
|
|
43
|
-
"TEMPLATE",
|
|
44
|
-
]);
|
|
45
|
-
const MAX_TEXT_LENGTH = 200;
|
|
46
|
-
const TEXTLESS_CONTENT_ELEMENTS = new Set([
|
|
47
|
-
"A",
|
|
48
|
-
"AUDIO",
|
|
49
|
-
"BUTTON",
|
|
50
|
-
"CANVAS",
|
|
51
|
-
"IFRAME",
|
|
52
|
-
"IMG",
|
|
53
|
-
"INPUT",
|
|
54
|
-
"METER",
|
|
55
|
-
"PROGRESS",
|
|
56
|
-
"SELECT",
|
|
57
|
-
"TEXTAREA",
|
|
58
|
-
"VIDEO",
|
|
59
|
-
]);
|
|
60
|
-
export function extractElements(root) {
|
|
61
|
-
const view = root.ownerDocument.defaultView;
|
|
62
|
-
if (!(view && view.innerWidth > 0 && view.innerHeight > 0)) {
|
|
63
|
-
return [];
|
|
64
|
-
}
|
|
65
|
-
const elements = [root, ...root.querySelectorAll("*")];
|
|
66
|
-
const candidates = [];
|
|
67
|
-
for (const [documentOrder, element] of elements.entries()) {
|
|
68
|
-
if (isHiddenOrDecorative(element, root)) {
|
|
69
|
-
continue;
|
|
70
|
-
}
|
|
71
|
-
const cmsRef = parseCmsRef(element.dataset.emdashRef);
|
|
72
|
-
if (!cmsRef && element.parentElement?.closest("[data-emdash-ref]")) {
|
|
73
|
-
continue;
|
|
74
|
-
}
|
|
75
|
-
const text = truncateText(cmsRef ? descendantText(element) : directText(element));
|
|
76
|
-
if (!(cmsRef || text || TEXTLESS_CONTENT_ELEMENTS.has(element.tagName))) {
|
|
77
|
-
continue;
|
|
78
|
-
}
|
|
79
|
-
const rect = element.getBoundingClientRect();
|
|
80
|
-
if (!intersectsViewport(rect, view)) {
|
|
81
|
-
continue;
|
|
82
|
-
}
|
|
83
|
-
candidates.push({
|
|
84
|
-
bounds: normalizeRect(rect, view),
|
|
85
|
-
...(cmsRef ? { cmsRef } : {}),
|
|
86
|
-
documentOrder,
|
|
87
|
-
left: rect.left,
|
|
88
|
-
role: element.getAttribute("role") ?? element.tagName.toLowerCase(),
|
|
89
|
-
section: findSection(element),
|
|
90
|
-
...(text ? { text } : {}),
|
|
91
|
-
top: rect.top,
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
return candidates
|
|
95
|
-
.sort((first, second) => first.top - second.top ||
|
|
96
|
-
first.left - second.left ||
|
|
97
|
-
first.documentOrder - second.documentOrder)
|
|
98
|
-
.map(({ documentOrder: _, left: __, top: ___, ...element }, index) => ({
|
|
99
|
-
...element,
|
|
100
|
-
elementId: `element-${index + 1}`,
|
|
101
|
-
}));
|
|
102
|
-
}
|
|
103
|
-
function findSection(element) {
|
|
104
|
-
return element.parentElement?.closest("[id]")?.id || undefined;
|
|
105
|
-
}
|
|
106
|
-
function intersectsViewport(rect, view) {
|
|
107
|
-
return (rect.width > 0 &&
|
|
108
|
-
rect.height > 0 &&
|
|
109
|
-
rect.bottom > 0 &&
|
|
110
|
-
rect.right > 0 &&
|
|
111
|
-
rect.top < view.innerHeight &&
|
|
112
|
-
rect.left < view.innerWidth);
|
|
113
|
-
}
|
|
114
|
-
function normalizeRect(rect, view) {
|
|
115
|
-
const left = Math.max(0, rect.left);
|
|
116
|
-
const top = Math.max(0, rect.top);
|
|
117
|
-
const right = Math.min(view.innerWidth, rect.right);
|
|
118
|
-
const bottom = Math.min(view.innerHeight, rect.bottom);
|
|
119
|
-
return {
|
|
120
|
-
height: (bottom - top) / view.innerHeight,
|
|
121
|
-
width: (right - left) / view.innerWidth,
|
|
122
|
-
x: left / view.innerWidth,
|
|
123
|
-
y: top / view.innerHeight,
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
function descendantText(element) {
|
|
127
|
-
const view = element.ownerDocument.defaultView;
|
|
128
|
-
if (!view) {
|
|
129
|
-
return "";
|
|
130
|
-
}
|
|
131
|
-
const segments = [];
|
|
132
|
-
visitTextNodes(element, (textNode) => {
|
|
133
|
-
const rawText = textNode.nodeValue ?? "";
|
|
134
|
-
const parent = textNode.parentElement;
|
|
135
|
-
if (!parent ||
|
|
136
|
-
isHiddenOrDecorative(parent, element) ||
|
|
137
|
-
!intersectsViewport(parent.getBoundingClientRect(), view)) {
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
const text = normalizeText(rawText);
|
|
141
|
-
if (!text) {
|
|
142
|
-
const previous = segments.at(-1);
|
|
143
|
-
if (previous && /\s/u.test(rawText)) {
|
|
144
|
-
previous.trailingSpace = true;
|
|
145
|
-
}
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
const boundary = nearestBlockBoundary(parent, element);
|
|
149
|
-
const previous = segments.at(-1);
|
|
150
|
-
if (previous?.boundary === boundary) {
|
|
151
|
-
const separator = previous.trailingSpace || /^\s/u.test(rawText) ? " " : "";
|
|
152
|
-
previous.text += `${separator}${text}`;
|
|
153
|
-
previous.trailingSpace = /\s$/u.test(rawText);
|
|
154
|
-
return;
|
|
155
|
-
}
|
|
156
|
-
segments.push({
|
|
157
|
-
boundary,
|
|
158
|
-
text,
|
|
159
|
-
trailingSpace: /\s$/u.test(rawText),
|
|
160
|
-
});
|
|
161
|
-
});
|
|
162
|
-
return segments.map(({ text }) => text).join(" / ");
|
|
163
|
-
}
|
|
164
|
-
function directText(element) {
|
|
165
|
-
return normalizeText([...element.childNodes]
|
|
166
|
-
.filter((node) => node.nodeType === 3)
|
|
167
|
-
.map((node) => node.nodeValue ?? "")
|
|
168
|
-
.join(" "));
|
|
169
|
-
}
|
|
170
|
-
function nearestBlockBoundary(element, root) {
|
|
171
|
-
let current = element;
|
|
172
|
-
while (current && current !== root) {
|
|
173
|
-
if (BLOCK_ELEMENTS.has(current.tagName)) {
|
|
174
|
-
return current;
|
|
175
|
-
}
|
|
176
|
-
current = current.parentElement;
|
|
177
|
-
}
|
|
178
|
-
return root;
|
|
179
|
-
}
|
|
180
|
-
function normalizeText(text) {
|
|
181
|
-
return text.replaceAll(/\s+/gu, " ").trim();
|
|
182
|
-
}
|
|
183
|
-
function truncateText(text) {
|
|
184
|
-
const characters = [...text];
|
|
185
|
-
if (characters.length <= MAX_TEXT_LENGTH) {
|
|
186
|
-
return text;
|
|
187
|
-
}
|
|
188
|
-
return `${characters
|
|
189
|
-
.slice(0, MAX_TEXT_LENGTH - 1)
|
|
190
|
-
.join("")
|
|
191
|
-
.trimEnd()}…`;
|
|
192
|
-
}
|
|
193
|
-
function isHiddenOrDecorative(element, root) {
|
|
194
|
-
const view = root.ownerDocument.defaultView;
|
|
195
|
-
let current = element;
|
|
196
|
-
while (current) {
|
|
197
|
-
const role = current.getAttribute("role");
|
|
198
|
-
if (NON_RENDERED_ELEMENTS.has(current.tagName) ||
|
|
199
|
-
current.hasAttribute("hidden") ||
|
|
200
|
-
current.getAttribute("aria-hidden") === "true" ||
|
|
201
|
-
role === "none" ||
|
|
202
|
-
role === "presentation") {
|
|
203
|
-
return true;
|
|
204
|
-
}
|
|
205
|
-
if (view) {
|
|
206
|
-
const style = view.getComputedStyle(current);
|
|
207
|
-
if (style.display === "none" ||
|
|
208
|
-
style.visibility === "hidden" ||
|
|
209
|
-
style.visibility === "collapse" ||
|
|
210
|
-
style.opacity === "0" ||
|
|
211
|
-
style.contentVisibility === "hidden") {
|
|
212
|
-
return true;
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
if (current === root) {
|
|
216
|
-
break;
|
|
217
|
-
}
|
|
218
|
-
current = current.parentElement;
|
|
219
|
-
}
|
|
220
|
-
return false;
|
|
221
|
-
}
|
|
222
|
-
function visitTextNodes(element, visit) {
|
|
223
|
-
for (const child of element.childNodes) {
|
|
224
|
-
if (child.nodeType === 3) {
|
|
225
|
-
visit(child);
|
|
226
|
-
}
|
|
227
|
-
else if (child.nodeType === 1) {
|
|
228
|
-
visitTextNodes(child, visit);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
function parseCmsRef(value) {
|
|
233
|
-
if (!value) {
|
|
234
|
-
return undefined;
|
|
235
|
-
}
|
|
236
|
-
try {
|
|
237
|
-
const parsed = JSON.parse(value);
|
|
238
|
-
if (!parsed ||
|
|
239
|
-
typeof parsed !== "object" ||
|
|
240
|
-
!("collection" in parsed) ||
|
|
241
|
-
typeof parsed.collection !== "string" ||
|
|
242
|
-
!("id" in parsed) ||
|
|
243
|
-
typeof parsed.id !== "string") {
|
|
244
|
-
return undefined;
|
|
245
|
-
}
|
|
246
|
-
const field = "field" in parsed && typeof parsed.field === "string"
|
|
247
|
-
? parsed.field
|
|
248
|
-
: undefined;
|
|
249
|
-
return {
|
|
250
|
-
collection: parsed.collection,
|
|
251
|
-
...(field ? { field } : {}),
|
|
252
|
-
id: parsed.id,
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
catch {
|
|
256
|
-
return undefined;
|
|
257
|
-
}
|
|
258
|
-
}
|