@parhelia/editor-bridge-head 0.1.12875
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 +22 -0
- package/dist/host/v1/parhelia-bridge.js +2699 -0
- package/dist/host/v1/parhelia-stub.js +185 -0
- package/dist/index.d.ts +510 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +189 -0
- package/package.json +40 -0
|
@@ -0,0 +1,2699 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
(() => {
|
|
3
|
+
// <define:__PARHELIA_BRIDGE_BUILD__>
|
|
4
|
+
var define_PARHELIA_BRIDGE_BUILD_default = { packageVersion: "#{VersionNumber}#", commit: "52e1923b", builtAt: "2026-06-06T14:31:46.468Z" };
|
|
5
|
+
|
|
6
|
+
// src/index.ts
|
|
7
|
+
var PARHELIA_EDITING_BRIDGE_FAMILY = "parhelia/editing-bridge";
|
|
8
|
+
var PARHELIA_EDITING_BRIDGE_VERSION = {
|
|
9
|
+
major: 1,
|
|
10
|
+
minor: 0
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// src/host/origin.ts
|
|
14
|
+
var PARENT_ORIGIN_STORAGE_KEY = "parhelia:parentOrigin";
|
|
15
|
+
function normalizeOrigin(origin) {
|
|
16
|
+
try {
|
|
17
|
+
return new URL(origin).origin;
|
|
18
|
+
} catch (error) {
|
|
19
|
+
return void 0;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function normalizeOrigins(origins) {
|
|
23
|
+
return origins.map(function(origin) {
|
|
24
|
+
return normalizeOrigin(origin);
|
|
25
|
+
}).filter(Boolean);
|
|
26
|
+
}
|
|
27
|
+
function rememberParentOrigin(origin) {
|
|
28
|
+
if (!origin) return;
|
|
29
|
+
try {
|
|
30
|
+
sessionStorage.setItem(PARENT_ORIGIN_STORAGE_KEY, origin);
|
|
31
|
+
} catch (error) {
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function readRememberedParentOrigin() {
|
|
35
|
+
try {
|
|
36
|
+
return normalizeOrigin(sessionStorage.getItem(PARENT_ORIGIN_STORAGE_KEY));
|
|
37
|
+
} catch (error) {
|
|
38
|
+
return void 0;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function resolveParentOrigin(config) {
|
|
42
|
+
config = config || {};
|
|
43
|
+
if (config.parentOrigin || config.editorOrigin) {
|
|
44
|
+
var configuredOrigin = normalizeOrigin(
|
|
45
|
+
config.parentOrigin || config.editorOrigin
|
|
46
|
+
);
|
|
47
|
+
rememberParentOrigin(configuredOrigin);
|
|
48
|
+
return configuredOrigin;
|
|
49
|
+
}
|
|
50
|
+
var referrerOrigin = document.referrer ? normalizeOrigin(document.referrer) : void 0;
|
|
51
|
+
if (referrerOrigin && referrerOrigin !== location.origin) {
|
|
52
|
+
rememberParentOrigin(referrerOrigin);
|
|
53
|
+
return referrerOrigin;
|
|
54
|
+
}
|
|
55
|
+
var rememberedOrigin = readRememberedParentOrigin();
|
|
56
|
+
if (rememberedOrigin) return rememberedOrigin;
|
|
57
|
+
if (referrerOrigin) {
|
|
58
|
+
rememberParentOrigin(referrerOrigin);
|
|
59
|
+
return referrerOrigin;
|
|
60
|
+
}
|
|
61
|
+
return void 0;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// src/host/runtime.ts
|
|
65
|
+
function isBridgeFamilyScriptSrc(src) {
|
|
66
|
+
if (!src) return false;
|
|
67
|
+
return src.indexOf("/parhelia-bridge.js") >= 0 || src.indexOf("/parhelia-stub.js") >= 0;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// src/host/parhelia-bridge.ts
|
|
71
|
+
(function() {
|
|
72
|
+
"use strict";
|
|
73
|
+
var family = PARHELIA_EDITING_BRIDGE_FAMILY;
|
|
74
|
+
var version = PARHELIA_EDITING_BRIDGE_VERSION;
|
|
75
|
+
var buildInfo = typeof define_PARHELIA_BRIDGE_BUILD_default !== "undefined" ? define_PARHELIA_BRIDGE_BUILD_default : {};
|
|
76
|
+
var operationalStarted = false;
|
|
77
|
+
var initFallbackTimer = 0;
|
|
78
|
+
var INIT_FALLBACK_MS = 2e3;
|
|
79
|
+
var acknowledgedCapabilities = null;
|
|
80
|
+
var config = window.__PARHELIA_EDITING_BRIDGE__ || {};
|
|
81
|
+
var parentWindow = window.parent;
|
|
82
|
+
var sequence = 0;
|
|
83
|
+
var elementSequence = 0;
|
|
84
|
+
var hoverTimer = 0;
|
|
85
|
+
var structureTimer = 0;
|
|
86
|
+
var geometryTimer = 0;
|
|
87
|
+
var domTimer = 0;
|
|
88
|
+
var pendingClickTimer = 0;
|
|
89
|
+
var zoomAnimationFrame = 0;
|
|
90
|
+
var zoomGeometryTimer = 0;
|
|
91
|
+
var lastGeometryOptions = null;
|
|
92
|
+
var ZOOM_TRANSITION_MS = 300;
|
|
93
|
+
var lastPostedScrollX;
|
|
94
|
+
var lastPostedScrollY;
|
|
95
|
+
var savedTextSelection = null;
|
|
96
|
+
var knownSelectedComponents = [];
|
|
97
|
+
var currentMode = config.mode || getQueryValue("sc_mode") || "edit";
|
|
98
|
+
if (!parentWindow || parentWindow === window) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
var parentOrigin = resolveParentOrigin(config);
|
|
102
|
+
var allowedParentOrigins = normalizeOrigins(
|
|
103
|
+
config.allowedParentOrigins || config.parentOrigins || [parentOrigin]
|
|
104
|
+
);
|
|
105
|
+
if (!parentOrigin || allowedParentOrigins.indexOf(parentOrigin) < 0) {
|
|
106
|
+
console.warn("[Parhelia bridge] Missing or rejected parent origin.");
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
var capabilities = [
|
|
110
|
+
"structure",
|
|
111
|
+
"geometry",
|
|
112
|
+
"selection",
|
|
113
|
+
"inlineEditing",
|
|
114
|
+
"richTextCommands",
|
|
115
|
+
"captureDom",
|
|
116
|
+
"refresh",
|
|
117
|
+
"zoom"
|
|
118
|
+
];
|
|
119
|
+
var runtime = {
|
|
120
|
+
source: config.source,
|
|
121
|
+
packageVersion: buildInfo.packageVersion,
|
|
122
|
+
commit: buildInfo.commit,
|
|
123
|
+
builtAt: buildInfo.builtAt
|
|
124
|
+
};
|
|
125
|
+
window.__PARHELIA_EDITING_BRIDGE_RUNTIME__ = {
|
|
126
|
+
family,
|
|
127
|
+
version,
|
|
128
|
+
capabilities,
|
|
129
|
+
runtime,
|
|
130
|
+
emitStructure,
|
|
131
|
+
emitGeometry,
|
|
132
|
+
emitDomSnapshot
|
|
133
|
+
};
|
|
134
|
+
if (typeof window.requestRefresh !== "function" && shouldInstallIncrementalRefresh()) {
|
|
135
|
+
window.requestRefresh = requestIncrementalRefresh;
|
|
136
|
+
}
|
|
137
|
+
addEventListener("message", handleMessage);
|
|
138
|
+
addEventListener("pagehide", rememberHostScroll);
|
|
139
|
+
addEventListener(
|
|
140
|
+
"scroll",
|
|
141
|
+
function() {
|
|
142
|
+
if (scheduleScrollChanged()) {
|
|
143
|
+
scheduleGeometry();
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
true
|
|
147
|
+
);
|
|
148
|
+
addEventListener("resize", function() {
|
|
149
|
+
postEvent("resizeChanged", {
|
|
150
|
+
viewport: getViewport(),
|
|
151
|
+
devicePixelRatio: window.devicePixelRatio || 1
|
|
152
|
+
});
|
|
153
|
+
scheduleGeometry();
|
|
154
|
+
scheduleDomSnapshot();
|
|
155
|
+
});
|
|
156
|
+
document.addEventListener("selectionchange", emitSelection);
|
|
157
|
+
document.addEventListener("click", handleInteraction, true);
|
|
158
|
+
document.addEventListener("dblclick", handleInteraction, true);
|
|
159
|
+
document.addEventListener("contextmenu", handleInteraction, true);
|
|
160
|
+
document.addEventListener("keydown", handleInteraction, true);
|
|
161
|
+
document.addEventListener("wheel", handleWheel, {
|
|
162
|
+
capture: true,
|
|
163
|
+
passive: false
|
|
164
|
+
});
|
|
165
|
+
document.addEventListener("pointermove", handleHover, true);
|
|
166
|
+
document.addEventListener("input", handleInput, true);
|
|
167
|
+
document.addEventListener("focusout", handleFocusOut, true);
|
|
168
|
+
var observer = new MutationObserver(function() {
|
|
169
|
+
scheduleStructure();
|
|
170
|
+
scheduleGeometry();
|
|
171
|
+
scheduleDomSnapshot();
|
|
172
|
+
});
|
|
173
|
+
if (document.documentElement) {
|
|
174
|
+
observer.observe(document.documentElement, {
|
|
175
|
+
childList: true,
|
|
176
|
+
subtree: true,
|
|
177
|
+
characterData: true,
|
|
178
|
+
attributes: true,
|
|
179
|
+
attributeFilter: [
|
|
180
|
+
"data-component-start",
|
|
181
|
+
"data-component-end",
|
|
182
|
+
"data-component-id",
|
|
183
|
+
"data-component",
|
|
184
|
+
"data-placeholder-start",
|
|
185
|
+
"data-placeholder-key",
|
|
186
|
+
"data-placeholderkey",
|
|
187
|
+
"data-fieldid",
|
|
188
|
+
"data-itemid",
|
|
189
|
+
"data-language",
|
|
190
|
+
"data-version",
|
|
191
|
+
"contenteditable",
|
|
192
|
+
"hidden",
|
|
193
|
+
"style",
|
|
194
|
+
"class"
|
|
195
|
+
]
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
postReady();
|
|
199
|
+
applyRememberedHostZoom();
|
|
200
|
+
applyRememberedHostScroll();
|
|
201
|
+
initFallbackTimer = setTimeout(startOperationalEmission, INIT_FALLBACK_MS);
|
|
202
|
+
function postReady() {
|
|
203
|
+
postEvent("ready", {
|
|
204
|
+
version,
|
|
205
|
+
hostUrl: location.href,
|
|
206
|
+
pageId: config.pageId || getQueryValue("sc_itemid") || void 0,
|
|
207
|
+
mode: config.mode || getQueryValue("sc_mode") || void 0,
|
|
208
|
+
viewport: getViewport(),
|
|
209
|
+
scroll: getScroll(),
|
|
210
|
+
devicePixelRatio: window.devicePixelRatio || 1,
|
|
211
|
+
capabilities,
|
|
212
|
+
runtime
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
function handleMessage(event) {
|
|
216
|
+
if (allowedParentOrigins.indexOf(event.origin) < 0) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
var message = event.data;
|
|
220
|
+
if (!isCommand(message)) {
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
logBridgeMessage("parent -> host", message);
|
|
224
|
+
try {
|
|
225
|
+
handleCommand(message);
|
|
226
|
+
} catch (error) {
|
|
227
|
+
postEvent("renderError", {
|
|
228
|
+
message: error && error.message ? error.message : String(error),
|
|
229
|
+
code: "command_failed",
|
|
230
|
+
details: { command: message.name, requestId: message.requestId }
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
function handleCommand(message) {
|
|
235
|
+
var payload = message.payload || {};
|
|
236
|
+
if (message.name === "hello") {
|
|
237
|
+
postReady();
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
if (message.name === "init") {
|
|
241
|
+
knownSelectedComponents = payload.selectedComponentIds || [];
|
|
242
|
+
acknowledgedCapabilities = payload.acknowledgedCapabilities || capabilities;
|
|
243
|
+
applyEditorMode(payload.mode || currentMode);
|
|
244
|
+
applySelectionClasses();
|
|
245
|
+
startOperationalEmission();
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
if (message.name === "refresh") {
|
|
249
|
+
postEvent("refreshStarted", { revision: payload.revision });
|
|
250
|
+
if (typeof window.requestRefresh === "function") {
|
|
251
|
+
var accepted;
|
|
252
|
+
try {
|
|
253
|
+
accepted = window.requestRefresh(payload.url);
|
|
254
|
+
} catch (error) {
|
|
255
|
+
postEvent("renderError", {
|
|
256
|
+
message: error && error.message ? error.message : String(error),
|
|
257
|
+
code: "refresh_failed",
|
|
258
|
+
details: {
|
|
259
|
+
requestId: message.requestId,
|
|
260
|
+
revision: payload.revision
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
runRefreshFallback(payload);
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
if (accepted === false) {
|
|
267
|
+
runRefreshFallback(payload);
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
if (accepted && typeof accepted.then === "function") {
|
|
271
|
+
accepted.then(
|
|
272
|
+
function() {
|
|
273
|
+
completeRefresh(payload);
|
|
274
|
+
},
|
|
275
|
+
function(error) {
|
|
276
|
+
postEvent("renderError", {
|
|
277
|
+
message: error && error.message ? error.message : String(error),
|
|
278
|
+
code: "refresh_failed",
|
|
279
|
+
details: {
|
|
280
|
+
requestId: message.requestId,
|
|
281
|
+
revision: payload.revision
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
runRefreshFallback(payload);
|
|
285
|
+
}
|
|
286
|
+
);
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
completeRefresh(payload);
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
runRefreshFallback(payload);
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
if (message.name === "setSelection") {
|
|
296
|
+
knownSelectedComponents = payload.componentIds || [];
|
|
297
|
+
applySelectionClasses();
|
|
298
|
+
emitGeometry();
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
if (message.name === "scrollIntoView") {
|
|
302
|
+
var scrollTarget = findTargetElement(payload);
|
|
303
|
+
if (scrollTarget) {
|
|
304
|
+
scrollTarget.scrollIntoView({
|
|
305
|
+
block: payload.block || "center",
|
|
306
|
+
inline: "nearest",
|
|
307
|
+
behavior: "smooth"
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
if (message.name === "scrollBy") {
|
|
313
|
+
var scrollX = Number(payload.x) || 0;
|
|
314
|
+
var scrollY = Number(payload.y) || 0;
|
|
315
|
+
var scrollTarget = document.scrollingElement || document.documentElement || document.body;
|
|
316
|
+
var currentX = window.scrollX || scrollTarget && scrollTarget.scrollLeft || 0;
|
|
317
|
+
var currentY = window.scrollY || scrollTarget && scrollTarget.scrollTop || 0;
|
|
318
|
+
var scrollOptions = {
|
|
319
|
+
left: currentX + scrollX,
|
|
320
|
+
top: currentY + scrollY,
|
|
321
|
+
behavior: payload.behavior === "smooth" ? "smooth" : "auto"
|
|
322
|
+
};
|
|
323
|
+
try {
|
|
324
|
+
if (scrollTarget && typeof scrollTarget.scrollTo === "function") {
|
|
325
|
+
scrollTarget.scrollTo(scrollOptions);
|
|
326
|
+
} else {
|
|
327
|
+
window.scrollTo(scrollOptions);
|
|
328
|
+
}
|
|
329
|
+
} catch (error) {
|
|
330
|
+
window.scrollBy(scrollX, scrollY);
|
|
331
|
+
if (scrollTarget) {
|
|
332
|
+
scrollTarget.scrollLeft = currentX + scrollX;
|
|
333
|
+
scrollTarget.scrollTop = currentY + scrollY;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
if (message.name === "queryGeometry") {
|
|
339
|
+
rememberGeometryOptions(payload);
|
|
340
|
+
emitGeometry(message.requestId, payload);
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
if (message.name === "beginInlineEdit") {
|
|
344
|
+
var editable = getInlineEditableElement(payload);
|
|
345
|
+
if (editable) {
|
|
346
|
+
beginInlineEdit(editable, payload);
|
|
347
|
+
}
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
if (message.name === "applyFieldPatch") {
|
|
351
|
+
var patchTarget = getInlineEditableElement(payload);
|
|
352
|
+
if (patchTarget) {
|
|
353
|
+
if (payload.isRichText) {
|
|
354
|
+
patchTarget.innerHTML = payload.value || "";
|
|
355
|
+
} else {
|
|
356
|
+
patchTarget.textContent = payload.value || "";
|
|
357
|
+
}
|
|
358
|
+
scheduleStructure();
|
|
359
|
+
scheduleGeometry();
|
|
360
|
+
scheduleDomSnapshot();
|
|
361
|
+
}
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
if (message.name === "applyRichTextCommand") {
|
|
365
|
+
var richTextTarget = getInlineEditableElement(payload);
|
|
366
|
+
if (richTextTarget) {
|
|
367
|
+
var shouldRestoreEditableState = !richTextTarget.isContentEditable;
|
|
368
|
+
if (shouldRestoreEditableState) {
|
|
369
|
+
rememberInlineEditableState(richTextTarget);
|
|
370
|
+
richTextTarget.setAttribute("contenteditable", "true");
|
|
371
|
+
}
|
|
372
|
+
if (!restoreSavedTextSelection(richTextTarget, payload)) {
|
|
373
|
+
richTextTarget.focus();
|
|
374
|
+
}
|
|
375
|
+
applyRichTextCommand(payload);
|
|
376
|
+
emitFieldValueChanged(richTextTarget);
|
|
377
|
+
emitSelection();
|
|
378
|
+
if (shouldRestoreEditableState) {
|
|
379
|
+
restoreInlineEditableState(richTextTarget);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
if (message.name === "commitInlineEdit") {
|
|
385
|
+
var commitTarget = getInlineEditableElement(payload);
|
|
386
|
+
if (commitTarget) {
|
|
387
|
+
emitFieldValueChanged(commitTarget);
|
|
388
|
+
emitInlineEditEnded(commitTarget, "commit");
|
|
389
|
+
restoreInlineEditableState(commitTarget);
|
|
390
|
+
}
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
if (message.name === "cancelInlineEdit") {
|
|
394
|
+
var cancelTarget = getInlineEditableElement(payload);
|
|
395
|
+
if (cancelTarget) {
|
|
396
|
+
emitInlineEditEnded(cancelTarget, "cancel");
|
|
397
|
+
restoreInlineEditableState(cancelTarget);
|
|
398
|
+
cancelTarget.blur();
|
|
399
|
+
}
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
if (message.name === "setPreviewMode") {
|
|
403
|
+
applyEditorMode(payload.enabled ? "preview" : "edit");
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
if (message.name === "setEditorMode") {
|
|
407
|
+
applyEditorMode(payload.mode || "edit");
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
if (message.name === "setChromeVisibility") {
|
|
411
|
+
document.documentElement.toggleAttribute(
|
|
412
|
+
"data-parhelia-chrome-hidden",
|
|
413
|
+
!payload.visible
|
|
414
|
+
);
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
if (message.name === "setZoom") {
|
|
418
|
+
rememberHostZoom(payload.zoom);
|
|
419
|
+
applyHostZoom(payload.zoom);
|
|
420
|
+
emitGeometry();
|
|
421
|
+
scheduleGeometry();
|
|
422
|
+
clearTimeout(zoomGeometryTimer);
|
|
423
|
+
zoomGeometryTimer = setTimeout(function() {
|
|
424
|
+
zoomGeometryTimer = 0;
|
|
425
|
+
emitGeometry();
|
|
426
|
+
scheduleScrollChanged();
|
|
427
|
+
scheduleDomSnapshot();
|
|
428
|
+
}, ZOOM_TRANSITION_MS + 50);
|
|
429
|
+
scheduleScrollChanged();
|
|
430
|
+
scheduleDomSnapshot();
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
if (message.name === "captureDom") {
|
|
434
|
+
var captureTarget = payload.selector ? document.querySelector(payload.selector) : document.documentElement;
|
|
435
|
+
postEvent("captureDomResult", {
|
|
436
|
+
requestId: payload.requestId || message.requestId || message.id,
|
|
437
|
+
html: payload.selector ? captureTarget ? serializeElementForCapture(captureTarget) : "" : serializeDocumentForCapture(),
|
|
438
|
+
text: payload.includeText && captureTarget ? captureTarget.textContent || "" : void 0
|
|
439
|
+
});
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
function applyEditorMode(mode) {
|
|
444
|
+
currentMode = mode || currentMode || "edit";
|
|
445
|
+
setEditingEnabled(currentMode !== "preview");
|
|
446
|
+
}
|
|
447
|
+
function buildStructure() {
|
|
448
|
+
var componentMap = {};
|
|
449
|
+
var placeholderMap = {};
|
|
450
|
+
var fields = [];
|
|
451
|
+
var components = [];
|
|
452
|
+
var placeholders = [];
|
|
453
|
+
var fieldNodes = Array.prototype.slice.call(
|
|
454
|
+
document.querySelectorAll("[data-fieldid]")
|
|
455
|
+
);
|
|
456
|
+
var componentNodes = Array.prototype.slice.call(
|
|
457
|
+
document.querySelectorAll(
|
|
458
|
+
"[data-component-start], [data-component-id], [data-renderingid], [data-rendering-id]"
|
|
459
|
+
)
|
|
460
|
+
);
|
|
461
|
+
var placeholderNodes = Array.prototype.slice.call(
|
|
462
|
+
document.querySelectorAll(
|
|
463
|
+
"[data-placeholder-start], [data-placeholder-key], [data-placeholderkey]"
|
|
464
|
+
)
|
|
465
|
+
);
|
|
466
|
+
componentNodes.forEach(function(element) {
|
|
467
|
+
var id = readAttribute(element, [
|
|
468
|
+
"data-component-start",
|
|
469
|
+
"data-component-id",
|
|
470
|
+
"data-renderingid",
|
|
471
|
+
"data-rendering-id",
|
|
472
|
+
"id"
|
|
473
|
+
]) || ensureElementKey(element);
|
|
474
|
+
if (componentMap[id]) return;
|
|
475
|
+
var measurement = measureComponentMarker(element, id);
|
|
476
|
+
var component = {
|
|
477
|
+
id,
|
|
478
|
+
elementKey: ensureElementKey(
|
|
479
|
+
measurement ? measurement.element : element
|
|
480
|
+
),
|
|
481
|
+
endElementKey: readEndElementKey(id),
|
|
482
|
+
name: readAttribute(element, ["data-component", "data-component-name"]),
|
|
483
|
+
type: readAttribute(element, ["data-component", "data-component-name"]),
|
|
484
|
+
typeId: readAttribute(element, [
|
|
485
|
+
"data-renderingid",
|
|
486
|
+
"data-rendering-id"
|
|
487
|
+
]),
|
|
488
|
+
placeholderKey: readAttribute(element, [
|
|
489
|
+
"data-placeholder-key",
|
|
490
|
+
"data-placeholderkey"
|
|
491
|
+
]),
|
|
492
|
+
items: [],
|
|
493
|
+
placeholders: [],
|
|
494
|
+
renderedDictionaryKeys: [],
|
|
495
|
+
editable: readEditable(element),
|
|
496
|
+
visible: !isElementHidden(element)
|
|
497
|
+
};
|
|
498
|
+
componentMap[id] = component;
|
|
499
|
+
components.push(component);
|
|
500
|
+
});
|
|
501
|
+
placeholderNodes.forEach(function(element) {
|
|
502
|
+
var key = readAttribute(element, [
|
|
503
|
+
"data-placeholder-start",
|
|
504
|
+
"data-placeholder-key",
|
|
505
|
+
"data-placeholderkey",
|
|
506
|
+
"name",
|
|
507
|
+
"id"
|
|
508
|
+
]) || ensureElementKey(element);
|
|
509
|
+
if (placeholderMap[key]) return;
|
|
510
|
+
var placeholder = {
|
|
511
|
+
key,
|
|
512
|
+
name: key,
|
|
513
|
+
elementKey: ensureElementKey(element),
|
|
514
|
+
componentIds: [],
|
|
515
|
+
editable: readEditable(element)
|
|
516
|
+
};
|
|
517
|
+
placeholderMap[key] = placeholder;
|
|
518
|
+
placeholders.push(placeholder);
|
|
519
|
+
});
|
|
520
|
+
fieldNodes.forEach(function(element) {
|
|
521
|
+
var fieldId = element.getAttribute("data-fieldid");
|
|
522
|
+
if (!fieldId) return;
|
|
523
|
+
var componentElement = findNearestComponentElement(element);
|
|
524
|
+
var componentId = componentElement ? readAttribute(componentElement, [
|
|
525
|
+
"data-component-start",
|
|
526
|
+
"data-component-id",
|
|
527
|
+
"data-renderingid",
|
|
528
|
+
"data-rendering-id",
|
|
529
|
+
"data-itemid",
|
|
530
|
+
"id"
|
|
531
|
+
]) : void 0;
|
|
532
|
+
fields.push({
|
|
533
|
+
elementKey: ensureElementKey(element),
|
|
534
|
+
fieldId,
|
|
535
|
+
componentId,
|
|
536
|
+
item: readItemDescriptor(element),
|
|
537
|
+
isRichText: element.getAttribute("data-is-richtext") === "true" || element.getAttribute("data-is-rich-text") === "true",
|
|
538
|
+
editable: readEditable(element)
|
|
539
|
+
});
|
|
540
|
+
if (componentId && componentMap[componentId]) {
|
|
541
|
+
componentMap[componentId].items = mergeRenderedField(
|
|
542
|
+
componentMap[componentId].items,
|
|
543
|
+
readItemDescriptor(element),
|
|
544
|
+
fieldId
|
|
545
|
+
);
|
|
546
|
+
}
|
|
547
|
+
});
|
|
548
|
+
return {
|
|
549
|
+
editRevision: getQueryValue("edit_rev") || void 0,
|
|
550
|
+
pageId: config.pageId || getQueryValue("sc_itemid") || void 0,
|
|
551
|
+
url: location.href,
|
|
552
|
+
components,
|
|
553
|
+
placeholders,
|
|
554
|
+
fields
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
function buildPageSkeleton() {
|
|
558
|
+
var pageItem = readPageItemDescriptor();
|
|
559
|
+
var root = {
|
|
560
|
+
id: "page",
|
|
561
|
+
name: "Page",
|
|
562
|
+
type: "Page",
|
|
563
|
+
typeId: "",
|
|
564
|
+
items: [pageItem],
|
|
565
|
+
placeholders: [],
|
|
566
|
+
datasourceItem: pageItem,
|
|
567
|
+
renderedDictionaryKeys: [],
|
|
568
|
+
editorFields: {},
|
|
569
|
+
editable: true
|
|
570
|
+
};
|
|
571
|
+
var currentComponent = root;
|
|
572
|
+
var currentPlaceholder;
|
|
573
|
+
var componentStack = [];
|
|
574
|
+
function isComponentContainerElement(element) {
|
|
575
|
+
return element.hasAttribute("data-component-id") || element.hasAttribute("data-renderingid") || element.hasAttribute("data-rendering-id") || element.hasAttribute("data-component-start") && !isElementHidden(element);
|
|
576
|
+
}
|
|
577
|
+
function findOpenComponent(componentId) {
|
|
578
|
+
if (currentComponent && currentComponent.id === componentId) {
|
|
579
|
+
return currentComponent;
|
|
580
|
+
}
|
|
581
|
+
for (var index = componentStack.length - 1; index >= 0; index--) {
|
|
582
|
+
if (componentStack[index].component.id === componentId) {
|
|
583
|
+
return componentStack[index].component;
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
return void 0;
|
|
587
|
+
}
|
|
588
|
+
function trackComponentElement(component, element) {
|
|
589
|
+
if (!isComponentContainerElement(element)) return;
|
|
590
|
+
for (var index = componentStack.length - 1; index >= 0; index--) {
|
|
591
|
+
if (componentStack[index].component.id === component.id) {
|
|
592
|
+
componentStack[index].element = element;
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
componentStack.push({ component, element });
|
|
597
|
+
}
|
|
598
|
+
function forgetOpenComponent(componentId) {
|
|
599
|
+
for (var index = componentStack.length - 1; index >= 0; index--) {
|
|
600
|
+
if (componentStack[index].component.id === componentId) {
|
|
601
|
+
componentStack.splice(index, 1);
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
function closeOpenComponentsBefore(element) {
|
|
607
|
+
while (componentStack.length > 0) {
|
|
608
|
+
var entry = componentStack[componentStack.length - 1];
|
|
609
|
+
if (entry.element === element || entry.element.contains(element)) {
|
|
610
|
+
return;
|
|
611
|
+
}
|
|
612
|
+
componentStack.pop();
|
|
613
|
+
currentComponent = entry.component;
|
|
614
|
+
endComponent(entry.component.id);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
function ensurePlaceholder(parentComponent, key, element) {
|
|
618
|
+
var existing = parentComponent.placeholders.filter(
|
|
619
|
+
function(placeholder2) {
|
|
620
|
+
return placeholder2.key === key;
|
|
621
|
+
}
|
|
622
|
+
)[0];
|
|
623
|
+
if (existing) return existing;
|
|
624
|
+
var placeholder = {
|
|
625
|
+
key,
|
|
626
|
+
name: key.indexOf(parentComponent.id + "_") === 0 ? key.substring(parentComponent.id.length + 1) : key,
|
|
627
|
+
description: element.getAttribute("data-description") || "",
|
|
628
|
+
components: [],
|
|
629
|
+
editable: readEditable(element),
|
|
630
|
+
__parentComponent: parentComponent
|
|
631
|
+
};
|
|
632
|
+
parentComponent.placeholders.push(placeholder);
|
|
633
|
+
return placeholder;
|
|
634
|
+
}
|
|
635
|
+
function findPlaceholderByKey(component, key) {
|
|
636
|
+
for (var index = 0; index < component.placeholders.length; index++) {
|
|
637
|
+
var placeholder = component.placeholders[index];
|
|
638
|
+
if (placeholder.key === key) return placeholder;
|
|
639
|
+
for (var componentIndex = 0; componentIndex < placeholder.components.length; componentIndex++) {
|
|
640
|
+
var nested = findPlaceholderByKey(
|
|
641
|
+
placeholder.components[componentIndex],
|
|
642
|
+
key
|
|
643
|
+
);
|
|
644
|
+
if (nested) return nested;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
return void 0;
|
|
648
|
+
}
|
|
649
|
+
function startComponent(componentId, element) {
|
|
650
|
+
var openComponent = findOpenComponent(componentId);
|
|
651
|
+
if (openComponent) {
|
|
652
|
+
trackComponentElement(openComponent, element);
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
if (!currentPlaceholder) {
|
|
656
|
+
var parentComponent = currentComponent || root;
|
|
657
|
+
var implicitKey = "implicit_" + (parentComponent.datasourceItem && parentComponent.datasourceItem.id || parentComponent.id);
|
|
658
|
+
currentPlaceholder = ensurePlaceholder(
|
|
659
|
+
parentComponent,
|
|
660
|
+
implicitKey,
|
|
661
|
+
element
|
|
662
|
+
);
|
|
663
|
+
}
|
|
664
|
+
var datasourceItem = readRenderedItemDescriptor(element, pageItem);
|
|
665
|
+
var componentType = readAttribute(element, [
|
|
666
|
+
"data-type",
|
|
667
|
+
"data-component",
|
|
668
|
+
"data-component-name"
|
|
669
|
+
]) || "";
|
|
670
|
+
var component = {
|
|
671
|
+
id: componentId,
|
|
672
|
+
name: componentType,
|
|
673
|
+
type: componentType,
|
|
674
|
+
typeId: readAttribute(element, [
|
|
675
|
+
"data-typeid",
|
|
676
|
+
"data-renderingid",
|
|
677
|
+
"data-rendering-id"
|
|
678
|
+
]) || "",
|
|
679
|
+
parametersTemplateId: element.getAttribute("data-parameters-template-id") || void 0,
|
|
680
|
+
parameters: element.getAttribute("data-parameters") || void 0,
|
|
681
|
+
items: datasourceItem ? [datasourceItem] : [],
|
|
682
|
+
placeholders: [],
|
|
683
|
+
datasourceItem,
|
|
684
|
+
renderedDictionaryKeys: [],
|
|
685
|
+
isShared: element.getAttribute("data-shared-datasource") === "true",
|
|
686
|
+
hasPersonalization: element.getAttribute("data-has-personalization") === "true",
|
|
687
|
+
layoutId: element.getAttribute("data-layoutid") || void 0,
|
|
688
|
+
isSharedLayout: element.getAttribute("data-shared-rendering") === "true",
|
|
689
|
+
editorFields: {},
|
|
690
|
+
editable: readEditable(element),
|
|
691
|
+
visible: !isElementHidden(element),
|
|
692
|
+
__parentPlaceholder: currentPlaceholder
|
|
693
|
+
};
|
|
694
|
+
currentPlaceholder.components.push(component);
|
|
695
|
+
currentComponent = component;
|
|
696
|
+
currentPlaceholder = void 0;
|
|
697
|
+
trackComponentElement(component, element);
|
|
698
|
+
}
|
|
699
|
+
function endComponent(componentId) {
|
|
700
|
+
if (!currentComponent || currentComponent.id !== componentId) {
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
var parentPlaceholder = currentComponent.__parentPlaceholder;
|
|
704
|
+
currentPlaceholder = parentPlaceholder;
|
|
705
|
+
currentComponent = parentPlaceholder && parentPlaceholder.key.indexOf("implicit_") === 0 ? parentPlaceholder.__parentComponent : void 0;
|
|
706
|
+
}
|
|
707
|
+
function closePlaceholder(placeholderKey) {
|
|
708
|
+
var closingPlaceholder = currentPlaceholder && currentPlaceholder.key === placeholderKey ? currentPlaceholder : findPlaceholderByKey(root, placeholderKey);
|
|
709
|
+
if (!closingPlaceholder) return;
|
|
710
|
+
currentComponent = closingPlaceholder.__parentComponent;
|
|
711
|
+
if (currentPlaceholder === closingPlaceholder) {
|
|
712
|
+
currentPlaceholder = void 0;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
function addEditorFields(element) {
|
|
716
|
+
if (!currentComponent) return;
|
|
717
|
+
var groupName = element.getAttribute("data-editor-group");
|
|
718
|
+
if (!groupName) return;
|
|
719
|
+
var group = currentComponent.editorFields[groupName];
|
|
720
|
+
if (!group) {
|
|
721
|
+
group = { addFields: [], removeFields: [] };
|
|
722
|
+
currentComponent.editorFields[groupName] = group;
|
|
723
|
+
}
|
|
724
|
+
group.addFields = group.addFields.concat(
|
|
725
|
+
splitCsv(element.getAttribute("data-add-fields"))
|
|
726
|
+
);
|
|
727
|
+
group.removeFields = group.removeFields.concat(
|
|
728
|
+
splitCsv(element.getAttribute("data-remove-fields"))
|
|
729
|
+
);
|
|
730
|
+
}
|
|
731
|
+
function addRenderedField(element) {
|
|
732
|
+
if (!currentComponent || !element.hasAttribute("data-fieldid")) return;
|
|
733
|
+
var renderedItem = readRenderedItemDescriptor(element, pageItem) || currentComponent.datasourceItem || pageItem;
|
|
734
|
+
var fieldId = element.getAttribute("data-fieldid");
|
|
735
|
+
var existingItem = currentComponent.items.filter(function(item) {
|
|
736
|
+
return item.id === renderedItem.id && item.language === renderedItem.language && item.version === renderedItem.version;
|
|
737
|
+
})[0];
|
|
738
|
+
if (!existingItem) {
|
|
739
|
+
existingItem = {
|
|
740
|
+
id: renderedItem.id,
|
|
741
|
+
language: renderedItem.language,
|
|
742
|
+
version: renderedItem.version,
|
|
743
|
+
database: renderedItem.database,
|
|
744
|
+
renderedFieldIds: []
|
|
745
|
+
};
|
|
746
|
+
currentComponent.items.push(existingItem);
|
|
747
|
+
}
|
|
748
|
+
if (existingItem.renderedFieldIds.indexOf(fieldId) < 0) {
|
|
749
|
+
existingItem.renderedFieldIds.push(fieldId);
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
Array.prototype.slice.call(document.documentElement.querySelectorAll("*")).forEach(function(element) {
|
|
753
|
+
closeOpenComponentsBefore(element);
|
|
754
|
+
addEditorFields(element);
|
|
755
|
+
addRenderedField(element);
|
|
756
|
+
var placeholderStartId = readAttribute(element, [
|
|
757
|
+
"data-placeholder-start",
|
|
758
|
+
"data-placeholder-key",
|
|
759
|
+
"data-placeholderkey"
|
|
760
|
+
]);
|
|
761
|
+
var placeholderEndId = element.getAttribute("data-placeholder-end");
|
|
762
|
+
var componentStartId = readAttribute(element, [
|
|
763
|
+
"data-component-start",
|
|
764
|
+
"data-component-id",
|
|
765
|
+
"data-renderingid",
|
|
766
|
+
"data-rendering-id"
|
|
767
|
+
]);
|
|
768
|
+
var componentEndId = element.getAttribute("data-component-end");
|
|
769
|
+
var dictionaryKeyStart = element.getAttribute(
|
|
770
|
+
"data-dictionary-key-start"
|
|
771
|
+
);
|
|
772
|
+
if (dictionaryKeyStart && currentComponent) {
|
|
773
|
+
currentComponent.renderedDictionaryKeys.push(dictionaryKeyStart);
|
|
774
|
+
}
|
|
775
|
+
if (placeholderStartId) {
|
|
776
|
+
var parentComponent = currentComponent || root;
|
|
777
|
+
currentPlaceholder = ensurePlaceholder(
|
|
778
|
+
parentComponent,
|
|
779
|
+
placeholderStartId,
|
|
780
|
+
element
|
|
781
|
+
);
|
|
782
|
+
currentComponent = void 0;
|
|
783
|
+
}
|
|
784
|
+
if (componentStartId) {
|
|
785
|
+
startComponent(componentStartId, element);
|
|
786
|
+
}
|
|
787
|
+
if (componentEndId) {
|
|
788
|
+
endComponent(componentEndId);
|
|
789
|
+
forgetOpenComponent(componentEndId);
|
|
790
|
+
}
|
|
791
|
+
if (placeholderEndId) {
|
|
792
|
+
closePlaceholder(placeholderEndId);
|
|
793
|
+
}
|
|
794
|
+
});
|
|
795
|
+
return {
|
|
796
|
+
editRevision: getQueryValue("edit_rev") || "",
|
|
797
|
+
item: pageItem,
|
|
798
|
+
rootComponent: stripSkeletonComponent(root)
|
|
799
|
+
};
|
|
800
|
+
}
|
|
801
|
+
function getHostZoom() {
|
|
802
|
+
var documentElement = document.documentElement;
|
|
803
|
+
if (!documentElement) return 1;
|
|
804
|
+
var inlineZoom = Number.parseFloat(documentElement.style.zoom);
|
|
805
|
+
if (Number.isFinite(inlineZoom) && inlineZoom > 0) return inlineZoom;
|
|
806
|
+
var computedZoom = Number.parseFloat(
|
|
807
|
+
window.getComputedStyle(documentElement).zoom
|
|
808
|
+
);
|
|
809
|
+
if (Number.isFinite(computedZoom) && computedZoom > 0) return computedZoom;
|
|
810
|
+
return 1;
|
|
811
|
+
}
|
|
812
|
+
function getBridgeGeometryRectScale() {
|
|
813
|
+
var hostZoom = getHostZoom();
|
|
814
|
+
if (!Number.isFinite(hostZoom) || hostZoom <= 0) return 1;
|
|
815
|
+
if (Math.abs(hostZoom - 1) < 1e-3) return 1;
|
|
816
|
+
if (!document.body) return hostZoom;
|
|
817
|
+
var probe = document.createElement("div");
|
|
818
|
+
probe.style.cssText = "position:absolute;left:-10000px;top:-10000px;width:100px;height:1px;visibility:hidden;pointer-events:none;";
|
|
819
|
+
document.body.appendChild(probe);
|
|
820
|
+
var measuredWidth = probe.getBoundingClientRect().width;
|
|
821
|
+
if (probe.parentNode) {
|
|
822
|
+
probe.parentNode.removeChild(probe);
|
|
823
|
+
}
|
|
824
|
+
if (!Number.isFinite(measuredWidth) || measuredWidth <= 0) return hostZoom;
|
|
825
|
+
var unscaledDelta = Math.abs(measuredWidth - 100);
|
|
826
|
+
var scaledDelta = Math.abs(measuredWidth - 100 * hostZoom);
|
|
827
|
+
return unscaledDelta <= scaledDelta ? hostZoom : 1;
|
|
828
|
+
}
|
|
829
|
+
function buildGeometry(options) {
|
|
830
|
+
options = options || {};
|
|
831
|
+
var targets = [];
|
|
832
|
+
Array.prototype.slice.call(
|
|
833
|
+
document.querySelectorAll(
|
|
834
|
+
"[data-component-start], [data-component-id], [data-renderingid], [data-rendering-id]"
|
|
835
|
+
)
|
|
836
|
+
).forEach(function(element) {
|
|
837
|
+
var componentId = readAttribute(element, [
|
|
838
|
+
"data-component-start",
|
|
839
|
+
"data-component-id",
|
|
840
|
+
"data-renderingid",
|
|
841
|
+
"data-rendering-id",
|
|
842
|
+
"id"
|
|
843
|
+
]) || ensureElementKey(element);
|
|
844
|
+
var measurement = measureComponentMarker(element, componentId);
|
|
845
|
+
if (!measurement) return;
|
|
846
|
+
targets.push({
|
|
847
|
+
kind: "component",
|
|
848
|
+
key: componentId,
|
|
849
|
+
elementKey: ensureElementKey(measurement.element),
|
|
850
|
+
componentId,
|
|
851
|
+
rect: rectToJson(measurement.rect)
|
|
852
|
+
});
|
|
853
|
+
});
|
|
854
|
+
Array.prototype.slice.call(
|
|
855
|
+
document.querySelectorAll(
|
|
856
|
+
"[data-placeholder-start], [data-placeholder-key], [data-placeholderkey]"
|
|
857
|
+
)
|
|
858
|
+
).forEach(function(element) {
|
|
859
|
+
var placeholderKey = readAttribute(element, [
|
|
860
|
+
"data-placeholder-start",
|
|
861
|
+
"data-placeholder-key",
|
|
862
|
+
"data-placeholderkey",
|
|
863
|
+
"name",
|
|
864
|
+
"id"
|
|
865
|
+
]) || ensureElementKey(element);
|
|
866
|
+
var measurement = measurePlaceholderMarker(element);
|
|
867
|
+
targets.push({
|
|
868
|
+
kind: "placeholder",
|
|
869
|
+
key: placeholderKey,
|
|
870
|
+
elementKey: ensureElementKey(measurement.element || element),
|
|
871
|
+
placeholderKey,
|
|
872
|
+
orientation: detectPlaceholderOrientation(element),
|
|
873
|
+
description: element.getAttribute("data-description") || void 0,
|
|
874
|
+
rect: rectToJson(measurement.rect)
|
|
875
|
+
});
|
|
876
|
+
});
|
|
877
|
+
Array.prototype.slice.call(document.querySelectorAll("[data-fieldid]")).forEach(function(element) {
|
|
878
|
+
var fieldId = element.getAttribute("data-fieldid");
|
|
879
|
+
var elementKey = ensureElementKey(element);
|
|
880
|
+
targets.push({
|
|
881
|
+
kind: "field",
|
|
882
|
+
key: elementKey,
|
|
883
|
+
elementKey,
|
|
884
|
+
fieldId: fieldId || void 0,
|
|
885
|
+
item: readItemDescriptor(element),
|
|
886
|
+
rect: rectToJson(element.getBoundingClientRect())
|
|
887
|
+
});
|
|
888
|
+
});
|
|
889
|
+
(options.textRanges || []).forEach(function(query) {
|
|
890
|
+
var fieldElement = findFieldElementForGeometryQuery(query);
|
|
891
|
+
if (!fieldElement) return;
|
|
892
|
+
var rects = measureTextRangeClientRects(
|
|
893
|
+
fieldElement,
|
|
894
|
+
query.start,
|
|
895
|
+
query.end
|
|
896
|
+
);
|
|
897
|
+
rects.forEach(function(rect, index) {
|
|
898
|
+
targets.push({
|
|
899
|
+
kind: "textRange",
|
|
900
|
+
key: query.key,
|
|
901
|
+
elementKey: ensureElementKey(fieldElement),
|
|
902
|
+
fieldId: fieldElement.getAttribute("data-fieldid") || void 0,
|
|
903
|
+
rect: rectToJson(rect),
|
|
904
|
+
description: String(index)
|
|
905
|
+
});
|
|
906
|
+
});
|
|
907
|
+
});
|
|
908
|
+
return {
|
|
909
|
+
viewport: getViewport(),
|
|
910
|
+
scroll: getScroll(),
|
|
911
|
+
devicePixelRatio: window.devicePixelRatio || 1,
|
|
912
|
+
rectScale: getBridgeGeometryRectScale(),
|
|
913
|
+
scrollScale: getHostZoom(),
|
|
914
|
+
targets
|
|
915
|
+
};
|
|
916
|
+
}
|
|
917
|
+
function startOperationalEmission() {
|
|
918
|
+
if (operationalStarted) return;
|
|
919
|
+
operationalStarted = true;
|
|
920
|
+
if (initFallbackTimer) {
|
|
921
|
+
clearTimeout(initFallbackTimer);
|
|
922
|
+
initFallbackTimer = 0;
|
|
923
|
+
}
|
|
924
|
+
emitStructure();
|
|
925
|
+
emitGeometry();
|
|
926
|
+
scheduleDomSnapshot();
|
|
927
|
+
}
|
|
928
|
+
function emitStructure() {
|
|
929
|
+
if (!operationalStarted) return;
|
|
930
|
+
postEvent("structureUpdated", { structure: buildStructure() });
|
|
931
|
+
postEvent("pageSkeletonUpdated", { pageSkeleton: buildPageSkeleton() });
|
|
932
|
+
}
|
|
933
|
+
function rememberGeometryOptions(options) {
|
|
934
|
+
if (!options || !Array.isArray(options.textRanges)) return;
|
|
935
|
+
lastGeometryOptions = {
|
|
936
|
+
textRanges: options.textRanges.slice()
|
|
937
|
+
};
|
|
938
|
+
}
|
|
939
|
+
function getGeometryOptions(options) {
|
|
940
|
+
if (options && Array.isArray(options.textRanges)) return options;
|
|
941
|
+
return lastGeometryOptions || options || void 0;
|
|
942
|
+
}
|
|
943
|
+
function emitGeometry(requestId, options) {
|
|
944
|
+
if (!operationalStarted && !requestId) return;
|
|
945
|
+
postEvent(
|
|
946
|
+
"geometryUpdated",
|
|
947
|
+
{ geometry: buildGeometry(getGeometryOptions(options)) },
|
|
948
|
+
requestId
|
|
949
|
+
);
|
|
950
|
+
}
|
|
951
|
+
function completeRefresh(payload) {
|
|
952
|
+
setTimeout(function() {
|
|
953
|
+
emitStructure();
|
|
954
|
+
emitGeometry();
|
|
955
|
+
emitDomSnapshot();
|
|
956
|
+
postEvent("refreshCompleted", {
|
|
957
|
+
revision: payload.revision,
|
|
958
|
+
structure: buildStructure(),
|
|
959
|
+
geometry: buildGeometry()
|
|
960
|
+
});
|
|
961
|
+
}, 0);
|
|
962
|
+
}
|
|
963
|
+
function runRefreshFallback(payload) {
|
|
964
|
+
rememberHostScroll();
|
|
965
|
+
if (payload.url) {
|
|
966
|
+
location.href = payload.url;
|
|
967
|
+
return;
|
|
968
|
+
}
|
|
969
|
+
location.reload();
|
|
970
|
+
}
|
|
971
|
+
function shouldInstallIncrementalRefresh() {
|
|
972
|
+
if (config.refresh === false || config.refreshStrategy === "none" || config.hostRefresh === "none") {
|
|
973
|
+
return false;
|
|
974
|
+
}
|
|
975
|
+
if (config.refresh === "incremental" || config.refreshStrategy === "incremental-dom" || config.hostRefresh === "incremental-dom") {
|
|
976
|
+
return true;
|
|
977
|
+
}
|
|
978
|
+
return !(config.bridgeUrl || config.parentOrigin || config.editorOrigin || config.allowedParentOrigins || config.parentOrigins);
|
|
979
|
+
}
|
|
980
|
+
function requestIncrementalRefresh(url) {
|
|
981
|
+
var target = normalizeRefreshTarget(url);
|
|
982
|
+
if (!target || target.origin !== location.origin) {
|
|
983
|
+
return false;
|
|
984
|
+
}
|
|
985
|
+
return fetch(target.href, {
|
|
986
|
+
credentials: "same-origin",
|
|
987
|
+
cache: "no-store",
|
|
988
|
+
headers: {
|
|
989
|
+
"X-Parhelia-Bridge-Refresh": "1"
|
|
990
|
+
}
|
|
991
|
+
}).then(function(response) {
|
|
992
|
+
if (isLoginRedirect(response)) {
|
|
993
|
+
location.href = response.url;
|
|
994
|
+
return;
|
|
995
|
+
}
|
|
996
|
+
if (!response.ok) {
|
|
997
|
+
throw new Error(
|
|
998
|
+
"Refresh request failed with HTTP " + response.status
|
|
999
|
+
);
|
|
1000
|
+
}
|
|
1001
|
+
var contentType = response.headers && response.headers.get ? response.headers.get("content-type") || "" : "";
|
|
1002
|
+
if (contentType && contentType.toLowerCase().indexOf("text/html") === -1) {
|
|
1003
|
+
throw new Error("Refresh response was not HTML.");
|
|
1004
|
+
}
|
|
1005
|
+
return response.text().then(function(html) {
|
|
1006
|
+
applyIncrementalRefreshHtml(html, response.url || target.href);
|
|
1007
|
+
});
|
|
1008
|
+
});
|
|
1009
|
+
}
|
|
1010
|
+
function normalizeRefreshTarget(url) {
|
|
1011
|
+
try {
|
|
1012
|
+
var target = new URL(url || location.href, location.href);
|
|
1013
|
+
if (target.protocol !== "http:" && target.protocol !== "https:") {
|
|
1014
|
+
return null;
|
|
1015
|
+
}
|
|
1016
|
+
return target;
|
|
1017
|
+
} catch (error) {
|
|
1018
|
+
return null;
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
function isLoginRedirect(response) {
|
|
1022
|
+
if (!response || !response.redirected || !response.url) return false;
|
|
1023
|
+
var redirectUrl = response.url.toLowerCase();
|
|
1024
|
+
return redirectUrl.indexOf("/identity/login") >= 0 || redirectUrl.indexOf("/sitecore/login") >= 0 || redirectUrl.indexOf("returnurl=") >= 0;
|
|
1025
|
+
}
|
|
1026
|
+
function applyIncrementalRefreshHtml(html, url) {
|
|
1027
|
+
var parser = new DOMParser();
|
|
1028
|
+
var nextDocument = parser.parseFromString(html, "text/html");
|
|
1029
|
+
if (!nextDocument || !nextDocument.documentElement) {
|
|
1030
|
+
throw new Error("Refresh response could not be parsed as HTML.");
|
|
1031
|
+
}
|
|
1032
|
+
var scrollX = window.scrollX || 0;
|
|
1033
|
+
var scrollY = window.scrollY || 0;
|
|
1034
|
+
var currentZoom = getHostZoom();
|
|
1035
|
+
if (nextDocument.title !== document.title) {
|
|
1036
|
+
document.title = nextDocument.title;
|
|
1037
|
+
}
|
|
1038
|
+
syncElementAttributes(
|
|
1039
|
+
document.documentElement,
|
|
1040
|
+
nextDocument.documentElement,
|
|
1041
|
+
{
|
|
1042
|
+
preserveStyle: true
|
|
1043
|
+
}
|
|
1044
|
+
);
|
|
1045
|
+
if (document.head && nextDocument.head) {
|
|
1046
|
+
patchElement(document.head, nextDocument.head, {
|
|
1047
|
+
preserveBridgeRuntime: true
|
|
1048
|
+
});
|
|
1049
|
+
}
|
|
1050
|
+
if (document.body && nextDocument.body) {
|
|
1051
|
+
patchElement(document.body, nextDocument.body, {
|
|
1052
|
+
preserveBridgeRuntime: true
|
|
1053
|
+
});
|
|
1054
|
+
} else {
|
|
1055
|
+
patchElement(document.documentElement, nextDocument.documentElement, {
|
|
1056
|
+
preserveBridgeRuntime: true
|
|
1057
|
+
});
|
|
1058
|
+
}
|
|
1059
|
+
updateRefreshUrl(url);
|
|
1060
|
+
applyEditorMode(currentMode);
|
|
1061
|
+
applySelectionClasses();
|
|
1062
|
+
reinitializeHostAfterRefresh();
|
|
1063
|
+
restoreRefreshScroll(scrollX, scrollY);
|
|
1064
|
+
if (Math.abs(currentZoom - 1) >= 1e-3) {
|
|
1065
|
+
if (typeof applyHostZoomInstant === "function") {
|
|
1066
|
+
applyHostZoomInstant(currentZoom);
|
|
1067
|
+
} else {
|
|
1068
|
+
applyHostZoom(currentZoom);
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
function patchElement(currentElement, nextElement, options) {
|
|
1073
|
+
if (!currentElement || !nextElement) return;
|
|
1074
|
+
syncElementAttributes(currentElement, nextElement, options);
|
|
1075
|
+
patchChildren(currentElement, nextElement, options);
|
|
1076
|
+
}
|
|
1077
|
+
function patchChildren(currentParent, nextParent, options) {
|
|
1078
|
+
var currentChild = currentParent.firstChild;
|
|
1079
|
+
var nextChild = nextParent.firstChild;
|
|
1080
|
+
while (nextChild) {
|
|
1081
|
+
var nextSibling = nextChild.nextSibling;
|
|
1082
|
+
var currentMatch = currentChild;
|
|
1083
|
+
if (!currentMatch || !canPatchNode(currentMatch, nextChild)) {
|
|
1084
|
+
currentMatch = findPatchMatch(currentParent, nextChild, currentChild);
|
|
1085
|
+
}
|
|
1086
|
+
if (!currentMatch) {
|
|
1087
|
+
currentParent.insertBefore(
|
|
1088
|
+
importRefreshNode(nextChild),
|
|
1089
|
+
currentChild
|
|
1090
|
+
);
|
|
1091
|
+
} else {
|
|
1092
|
+
if (currentMatch !== currentChild) {
|
|
1093
|
+
currentParent.insertBefore(currentMatch, currentChild);
|
|
1094
|
+
}
|
|
1095
|
+
patchNode(currentMatch, nextChild, options);
|
|
1096
|
+
currentChild = currentMatch.nextSibling;
|
|
1097
|
+
}
|
|
1098
|
+
nextChild = nextSibling;
|
|
1099
|
+
}
|
|
1100
|
+
while (currentChild) {
|
|
1101
|
+
var removeNode = currentChild;
|
|
1102
|
+
currentChild = currentChild.nextSibling;
|
|
1103
|
+
if (options && options.preserveBridgeRuntime && isCurrentBridgeScript(removeNode)) {
|
|
1104
|
+
continue;
|
|
1105
|
+
}
|
|
1106
|
+
currentParent.removeChild(removeNode);
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
function patchNode(currentNode, nextNode, options) {
|
|
1110
|
+
if (!canPatchNode(currentNode, nextNode)) {
|
|
1111
|
+
currentNode.parentNode.replaceChild(importRefreshNode(nextNode), currentNode);
|
|
1112
|
+
return;
|
|
1113
|
+
}
|
|
1114
|
+
if (currentNode.nodeType === Node.TEXT_NODE || currentNode.nodeType === Node.COMMENT_NODE) {
|
|
1115
|
+
if (currentNode.nodeValue !== nextNode.nodeValue) {
|
|
1116
|
+
currentNode.nodeValue = nextNode.nodeValue;
|
|
1117
|
+
}
|
|
1118
|
+
return;
|
|
1119
|
+
}
|
|
1120
|
+
if (currentNode.nodeType === Node.ELEMENT_NODE) {
|
|
1121
|
+
patchElement(currentNode, nextNode, options);
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
function canPatchNode(currentNode, nextNode) {
|
|
1125
|
+
if (!currentNode || !nextNode) return false;
|
|
1126
|
+
if (currentNode.nodeType !== nextNode.nodeType) return false;
|
|
1127
|
+
if (currentNode.nodeType !== Node.ELEMENT_NODE) {
|
|
1128
|
+
return true;
|
|
1129
|
+
}
|
|
1130
|
+
if (currentNode.tagName !== nextNode.tagName) {
|
|
1131
|
+
return false;
|
|
1132
|
+
}
|
|
1133
|
+
var currentKey = getRefreshPatchKey(currentNode);
|
|
1134
|
+
var nextKey = getRefreshPatchKey(nextNode);
|
|
1135
|
+
return !currentKey || !nextKey || currentKey === nextKey;
|
|
1136
|
+
}
|
|
1137
|
+
function findPatchMatch(parent, nextNode, startNode) {
|
|
1138
|
+
var key = getRefreshPatchKey(nextNode);
|
|
1139
|
+
if (!key) return null;
|
|
1140
|
+
var child = startNode;
|
|
1141
|
+
while (child) {
|
|
1142
|
+
if (getRefreshPatchKey(child) === key && canPatchNode(child, nextNode)) {
|
|
1143
|
+
return child;
|
|
1144
|
+
}
|
|
1145
|
+
child = child.nextSibling;
|
|
1146
|
+
}
|
|
1147
|
+
return null;
|
|
1148
|
+
}
|
|
1149
|
+
function getRefreshPatchKey(node) {
|
|
1150
|
+
if (!node || node.nodeType !== Node.ELEMENT_NODE) return "";
|
|
1151
|
+
var element = node;
|
|
1152
|
+
var componentKey = readAttribute(element, [
|
|
1153
|
+
"data-component-start",
|
|
1154
|
+
"data-component-end",
|
|
1155
|
+
"data-component-id",
|
|
1156
|
+
"data-renderingid",
|
|
1157
|
+
"data-rendering-id"
|
|
1158
|
+
]);
|
|
1159
|
+
if (componentKey) return element.tagName + ":component:" + componentKey;
|
|
1160
|
+
var placeholderKey = readAttribute(element, [
|
|
1161
|
+
"data-placeholder-start",
|
|
1162
|
+
"data-placeholder-end",
|
|
1163
|
+
"data-placeholder-key",
|
|
1164
|
+
"data-placeholderkey"
|
|
1165
|
+
]);
|
|
1166
|
+
if (placeholderKey) return element.tagName + ":placeholder:" + placeholderKey;
|
|
1167
|
+
var fieldId = element.getAttribute("data-fieldid");
|
|
1168
|
+
if (fieldId) {
|
|
1169
|
+
return [
|
|
1170
|
+
element.tagName,
|
|
1171
|
+
"field",
|
|
1172
|
+
fieldId,
|
|
1173
|
+
element.getAttribute("data-itemid") || "",
|
|
1174
|
+
element.getAttribute("data-language") || "",
|
|
1175
|
+
element.getAttribute("data-version") || ""
|
|
1176
|
+
].join(":");
|
|
1177
|
+
}
|
|
1178
|
+
if (element.id) return element.tagName + ":id:" + element.id;
|
|
1179
|
+
var scriptSrc = element.tagName === "SCRIPT" ? element.getAttribute("src") : "";
|
|
1180
|
+
if (scriptSrc) return "SCRIPT:src:" + scriptSrc;
|
|
1181
|
+
return "";
|
|
1182
|
+
}
|
|
1183
|
+
function syncElementAttributes(currentElement, nextElement, options) {
|
|
1184
|
+
if (!currentElement || !nextElement) return;
|
|
1185
|
+
var currentAttributes = Array.prototype.slice.call(
|
|
1186
|
+
currentElement.attributes || []
|
|
1187
|
+
);
|
|
1188
|
+
currentAttributes.forEach(function(attribute) {
|
|
1189
|
+
if (options && options.preserveStyle && currentElement === document.documentElement && attribute.name === "style") {
|
|
1190
|
+
return;
|
|
1191
|
+
}
|
|
1192
|
+
if (attribute.name === "data-parhelia-element-key" && !nextElement.hasAttribute(attribute.name)) {
|
|
1193
|
+
return;
|
|
1194
|
+
}
|
|
1195
|
+
if (!nextElement.hasAttribute(attribute.name)) {
|
|
1196
|
+
currentElement.removeAttribute(attribute.name);
|
|
1197
|
+
}
|
|
1198
|
+
});
|
|
1199
|
+
Array.prototype.slice.call(nextElement.attributes || []).forEach(
|
|
1200
|
+
function(attribute) {
|
|
1201
|
+
if (options && options.preserveStyle && currentElement === document.documentElement && attribute.name === "style") {
|
|
1202
|
+
return;
|
|
1203
|
+
}
|
|
1204
|
+
if (currentElement.getAttribute(attribute.name) !== attribute.value) {
|
|
1205
|
+
currentElement.setAttribute(attribute.name, attribute.value);
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
);
|
|
1209
|
+
}
|
|
1210
|
+
function importRefreshNode(node) {
|
|
1211
|
+
return document.importNode(node, true);
|
|
1212
|
+
}
|
|
1213
|
+
function isCurrentBridgeScript(node) {
|
|
1214
|
+
if (!node || node.nodeType !== Node.ELEMENT_NODE) return false;
|
|
1215
|
+
if (node.tagName !== "SCRIPT") return false;
|
|
1216
|
+
return isBridgeFamilyScriptSrc(node.getAttribute("src") || "");
|
|
1217
|
+
}
|
|
1218
|
+
function updateRefreshUrl(url) {
|
|
1219
|
+
if (!url || !history || !history.replaceState) return;
|
|
1220
|
+
try {
|
|
1221
|
+
var nextUrl = new URL(url, location.href);
|
|
1222
|
+
if (nextUrl.origin === location.origin) {
|
|
1223
|
+
history.replaceState(history.state, document.title, nextUrl.href);
|
|
1224
|
+
}
|
|
1225
|
+
} catch (error) {
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
function reinitializeHostAfterRefresh() {
|
|
1229
|
+
try {
|
|
1230
|
+
if (window.XA && typeof window.XA.init === "function") {
|
|
1231
|
+
window.XA.init();
|
|
1232
|
+
}
|
|
1233
|
+
} catch (error) {
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
function restoreRefreshScroll(x, y) {
|
|
1237
|
+
var targetX = x || 0;
|
|
1238
|
+
var targetY = y || 0;
|
|
1239
|
+
var attempts = 0;
|
|
1240
|
+
function apply() {
|
|
1241
|
+
window.scrollTo(targetX, targetY);
|
|
1242
|
+
attempts += 1;
|
|
1243
|
+
var settled = Math.abs((window.scrollY || 0) - targetY) <= 1 && Math.abs((window.scrollX || 0) - targetX) <= 1;
|
|
1244
|
+
if (!settled && attempts < 10) {
|
|
1245
|
+
requestAnimationFrame(apply);
|
|
1246
|
+
return;
|
|
1247
|
+
}
|
|
1248
|
+
scheduleScrollChanged();
|
|
1249
|
+
}
|
|
1250
|
+
requestAnimationFrame(apply);
|
|
1251
|
+
}
|
|
1252
|
+
function emitDomSnapshot() {
|
|
1253
|
+
if (!operationalStarted) return;
|
|
1254
|
+
postEvent("domUpdated", buildDomSnapshot());
|
|
1255
|
+
}
|
|
1256
|
+
function buildDomSnapshot() {
|
|
1257
|
+
var size = getDocumentSize();
|
|
1258
|
+
return {
|
|
1259
|
+
html: serializeDocumentForMinimap(),
|
|
1260
|
+
url: location.href,
|
|
1261
|
+
viewport: getViewport(),
|
|
1262
|
+
scroll: getScroll(),
|
|
1263
|
+
scrollWidth: size.width,
|
|
1264
|
+
scrollHeight: size.height
|
|
1265
|
+
};
|
|
1266
|
+
}
|
|
1267
|
+
function serializeDocumentForMinimap() {
|
|
1268
|
+
if (!document.documentElement) return "";
|
|
1269
|
+
var clone = document.documentElement.cloneNode(true);
|
|
1270
|
+
bakeMinimapLayoutDimensions(document.documentElement, clone);
|
|
1271
|
+
stripMinimapUnsafeNodes(clone);
|
|
1272
|
+
ensureMinimapBase(clone);
|
|
1273
|
+
injectMinimapConstraints(clone);
|
|
1274
|
+
return "<!doctype html>\n" + clone.outerHTML;
|
|
1275
|
+
}
|
|
1276
|
+
function serializeDocumentForCapture() {
|
|
1277
|
+
if (!document.documentElement) return "";
|
|
1278
|
+
var clone = document.documentElement.cloneNode(true);
|
|
1279
|
+
stripMinimapUnsafeNodes(clone);
|
|
1280
|
+
ensureMinimapBase(clone);
|
|
1281
|
+
return "<!doctype html>\n" + clone.outerHTML;
|
|
1282
|
+
}
|
|
1283
|
+
function serializeElementForCapture(element) {
|
|
1284
|
+
if (!element) return "";
|
|
1285
|
+
var clone = element.cloneNode(true);
|
|
1286
|
+
stripMinimapUnsafeNodes(clone);
|
|
1287
|
+
return clone.outerHTML || "";
|
|
1288
|
+
}
|
|
1289
|
+
function bakeMinimapLayoutDimensions(source, target, insideTextFlow) {
|
|
1290
|
+
if (!source || !target) return;
|
|
1291
|
+
var isTextFlow = isMinimapTextFlowElement(source);
|
|
1292
|
+
var shouldBakeSelf = !insideTextFlow && !isTextFlow;
|
|
1293
|
+
if (shouldBakeSelf && target.style && typeof source.getBoundingClientRect === "function") {
|
|
1294
|
+
var rect = source.getBoundingClientRect();
|
|
1295
|
+
if (rect.width > 0) {
|
|
1296
|
+
target.style.setProperty("width", rect.width + "px", "important");
|
|
1297
|
+
}
|
|
1298
|
+
if (rect.height > 0) {
|
|
1299
|
+
target.style.setProperty("height", rect.height + "px", "important");
|
|
1300
|
+
}
|
|
1301
|
+
var computed = window.getComputedStyle ? window.getComputedStyle(source) : null;
|
|
1302
|
+
if (computed && rect.width > 0 && computed.minWidth && computed.minWidth !== "0px" && computed.minWidth !== "auto") {
|
|
1303
|
+
target.style.setProperty("min-width", rect.width + "px", "important");
|
|
1304
|
+
}
|
|
1305
|
+
if (computed && rect.height > 0 && computed.minHeight && computed.minHeight !== "0px" && computed.minHeight !== "auto") {
|
|
1306
|
+
target.style.setProperty("min-height", rect.height + "px", "important");
|
|
1307
|
+
}
|
|
1308
|
+
if (computed && (computed.position === "absolute" || computed.position === "fixed")) {
|
|
1309
|
+
var parentRect = source.offsetParent && source.offsetParent.getBoundingClientRect ? source.offsetParent.getBoundingClientRect() : { left: 0, top: 0 };
|
|
1310
|
+
target.style.setProperty(
|
|
1311
|
+
"left",
|
|
1312
|
+
rect.left - parentRect.left + "px",
|
|
1313
|
+
"important"
|
|
1314
|
+
);
|
|
1315
|
+
target.style.setProperty(
|
|
1316
|
+
"top",
|
|
1317
|
+
rect.top - parentRect.top + "px",
|
|
1318
|
+
"important"
|
|
1319
|
+
);
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
var sourceChildren = Array.prototype.slice.call(source.children || []);
|
|
1323
|
+
var targetChildren = Array.prototype.slice.call(target.children || []);
|
|
1324
|
+
var childInsideTextFlow = insideTextFlow || isTextFlow;
|
|
1325
|
+
for (var index = 0; index < sourceChildren.length && index < targetChildren.length; index += 1) {
|
|
1326
|
+
bakeMinimapLayoutDimensions(
|
|
1327
|
+
sourceChildren[index],
|
|
1328
|
+
targetChildren[index],
|
|
1329
|
+
childInsideTextFlow
|
|
1330
|
+
);
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
function isMinimapTextFlowElement(element) {
|
|
1334
|
+
if (!element || typeof element.matches !== "function") return false;
|
|
1335
|
+
return element.matches(
|
|
1336
|
+
"[data-fieldid], [contenteditable], input, textarea"
|
|
1337
|
+
);
|
|
1338
|
+
}
|
|
1339
|
+
function stripMinimapUnsafeNodes(root) {
|
|
1340
|
+
Array.prototype.slice.call(root.querySelectorAll("script")).forEach(function(node) {
|
|
1341
|
+
if (node.parentNode) node.parentNode.removeChild(node);
|
|
1342
|
+
});
|
|
1343
|
+
Array.prototype.slice.call(root.querySelectorAll("[contenteditable]")).forEach(function(node) {
|
|
1344
|
+
node.removeAttribute("contenteditable");
|
|
1345
|
+
node.removeAttribute("data-parhelia-inline-editing");
|
|
1346
|
+
});
|
|
1347
|
+
if (root.hasAttribute && root.hasAttribute("contenteditable")) {
|
|
1348
|
+
root.removeAttribute("contenteditable");
|
|
1349
|
+
root.removeAttribute("data-parhelia-inline-editing");
|
|
1350
|
+
}
|
|
1351
|
+
[root].concat(Array.prototype.slice.call(root.querySelectorAll("*"))).forEach(function(node) {
|
|
1352
|
+
Array.prototype.slice.call(node.attributes || []).forEach(function(attribute) {
|
|
1353
|
+
var name = attribute.name.toLowerCase();
|
|
1354
|
+
var value = String(attribute.value || "");
|
|
1355
|
+
var isScriptAttribute = name.indexOf("on") === 0 || name === "srcdoc" || (name === "href" || name === "src" || name === "xlink:href" || name === "formaction") && /^\s*javascript:/i.test(value);
|
|
1356
|
+
if (isScriptAttribute) {
|
|
1357
|
+
node.removeAttribute(attribute.name);
|
|
1358
|
+
}
|
|
1359
|
+
});
|
|
1360
|
+
});
|
|
1361
|
+
}
|
|
1362
|
+
function ensureMinimapBase(root) {
|
|
1363
|
+
var head = root.querySelector("head");
|
|
1364
|
+
if (!head) return;
|
|
1365
|
+
Array.prototype.slice.call(head.querySelectorAll("base")).forEach(function(node) {
|
|
1366
|
+
node.parentNode.removeChild(node);
|
|
1367
|
+
});
|
|
1368
|
+
var base = document.createElement("base");
|
|
1369
|
+
base.setAttribute("href", location.href);
|
|
1370
|
+
head.insertBefore(base, head.firstChild);
|
|
1371
|
+
}
|
|
1372
|
+
function injectMinimapConstraints(root) {
|
|
1373
|
+
var head = root.querySelector("head");
|
|
1374
|
+
if (!head) return;
|
|
1375
|
+
var style = document.createElement("style");
|
|
1376
|
+
style.setAttribute("data-parhelia-minimap-constraints", "true");
|
|
1377
|
+
style.textContent = [
|
|
1378
|
+
"html, body { overflow: hidden !important; }",
|
|
1379
|
+
"body { pointer-events: none !important; }",
|
|
1380
|
+
"script { display: none !important; }",
|
|
1381
|
+
"video, img, picture, iframe { max-width: 100% !important; height: auto !important; }"
|
|
1382
|
+
].join("\n");
|
|
1383
|
+
head.appendChild(style);
|
|
1384
|
+
}
|
|
1385
|
+
function emitSelection() {
|
|
1386
|
+
var selection = document.getSelection();
|
|
1387
|
+
if (!selection || selection.rangeCount === 0) {
|
|
1388
|
+
postEvent("selectionChanged", {
|
|
1389
|
+
selection: { collapsed: true }
|
|
1390
|
+
});
|
|
1391
|
+
return;
|
|
1392
|
+
}
|
|
1393
|
+
if (!selection.isCollapsed && selection.toString().trim()) {
|
|
1394
|
+
clearPendingClickInteraction();
|
|
1395
|
+
}
|
|
1396
|
+
var range = selection.getRangeAt(0);
|
|
1397
|
+
var fieldElement = closestFieldElement(
|
|
1398
|
+
range.startContainer.nodeType === Node.ELEMENT_NODE ? range.startContainer : range.startContainer.parentElement
|
|
1399
|
+
);
|
|
1400
|
+
var startOffset = fieldElement ? getTextOffset(fieldElement, range.startContainer, range.startOffset) : range.startOffset;
|
|
1401
|
+
var endOffset = fieldElement ? getTextOffset(fieldElement, range.endContainer, range.endOffset) : range.endOffset;
|
|
1402
|
+
var context = fieldElement ? getSelectionContext(fieldElement, startOffset, endOffset) : void 0;
|
|
1403
|
+
var selectionRect = getSelectionRect(range);
|
|
1404
|
+
var activeField = fieldElement ? {
|
|
1405
|
+
elementKey: ensureElementKey(fieldElement),
|
|
1406
|
+
fieldId: fieldElement.getAttribute("data-fieldid") || "",
|
|
1407
|
+
item: readItemDescriptor(fieldElement),
|
|
1408
|
+
isRichText: fieldElement.getAttribute("data-is-richtext") === "true" || fieldElement.getAttribute("data-is-rich-text") === "true"
|
|
1409
|
+
} : void 0;
|
|
1410
|
+
if (activeField && !selection.isCollapsed) {
|
|
1411
|
+
try {
|
|
1412
|
+
savedTextSelection = {
|
|
1413
|
+
elementKey: activeField.elementKey,
|
|
1414
|
+
fieldId: activeField.fieldId,
|
|
1415
|
+
range: range.cloneRange()
|
|
1416
|
+
};
|
|
1417
|
+
} catch (error) {
|
|
1418
|
+
savedTextSelection = null;
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
postEvent("selectionChanged", {
|
|
1422
|
+
selection: {
|
|
1423
|
+
activeField,
|
|
1424
|
+
collapsed: selection.isCollapsed,
|
|
1425
|
+
rect: selectionRect ? rectToJson(selectionRect) : void 0,
|
|
1426
|
+
startOffset,
|
|
1427
|
+
endOffset,
|
|
1428
|
+
text: selection.toString(),
|
|
1429
|
+
metadata: context
|
|
1430
|
+
}
|
|
1431
|
+
});
|
|
1432
|
+
}
|
|
1433
|
+
function clearPendingClickInteraction() {
|
|
1434
|
+
if (!pendingClickTimer) return;
|
|
1435
|
+
clearTimeout(pendingClickTimer);
|
|
1436
|
+
pendingClickTimer = 0;
|
|
1437
|
+
}
|
|
1438
|
+
function hasActiveTextSelection(fieldElement) {
|
|
1439
|
+
var selection = document.getSelection();
|
|
1440
|
+
if (!selection || selection.rangeCount === 0 || selection.isCollapsed || !selection.toString().trim()) {
|
|
1441
|
+
return false;
|
|
1442
|
+
}
|
|
1443
|
+
if (!fieldElement) return true;
|
|
1444
|
+
try {
|
|
1445
|
+
var range = selection.getRangeAt(0);
|
|
1446
|
+
var container = range.commonAncestorContainer;
|
|
1447
|
+
var element = container.nodeType === Node.ELEMENT_NODE ? container : container.parentElement;
|
|
1448
|
+
return !!element && fieldElement.contains(element);
|
|
1449
|
+
} catch (error) {
|
|
1450
|
+
return true;
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
function handleInteraction(event) {
|
|
1454
|
+
var target = event.target && event.target.nodeType === Node.ELEMENT_NODE ? event.target : event.target && event.target.parentElement;
|
|
1455
|
+
if (!target) return;
|
|
1456
|
+
var fieldElement = closestFieldElement(target);
|
|
1457
|
+
var componentElement = findNearestComponentElement(target);
|
|
1458
|
+
var placeholderElement = closestPlaceholderElement(target);
|
|
1459
|
+
var kind = event.type === "dblclick" ? "doubleClick" : event.type === "contextmenu" ? "contextMenu" : event.type === "keydown" ? "keydown" : "click";
|
|
1460
|
+
var anchor = target.tagName && target.tagName.toLowerCase() === "a" ? target : target.closest ? target.closest("a") : null;
|
|
1461
|
+
if (kind === "click" && anchor && currentMode !== "preview" && currentMode !== "suggestions") {
|
|
1462
|
+
event.preventDefault();
|
|
1463
|
+
}
|
|
1464
|
+
if (kind === "contextMenu" && !event.ctrlKey) {
|
|
1465
|
+
event.preventDefault();
|
|
1466
|
+
}
|
|
1467
|
+
if (kind === "doubleClick") {
|
|
1468
|
+
clearPendingClickInteraction();
|
|
1469
|
+
emitSelection();
|
|
1470
|
+
return;
|
|
1471
|
+
}
|
|
1472
|
+
if (kind === "click" && fieldElement && hasActiveTextSelection(fieldElement)) {
|
|
1473
|
+
clearPendingClickInteraction();
|
|
1474
|
+
emitSelection();
|
|
1475
|
+
return;
|
|
1476
|
+
}
|
|
1477
|
+
var payload = {
|
|
1478
|
+
kind,
|
|
1479
|
+
elementKey: fieldElement ? ensureElementKey(fieldElement) : ensureElementKey(target),
|
|
1480
|
+
componentId: componentElement ? readAttribute(componentElement, [
|
|
1481
|
+
"data-component-start",
|
|
1482
|
+
"data-component-id",
|
|
1483
|
+
"data-renderingid",
|
|
1484
|
+
"data-rendering-id",
|
|
1485
|
+
"id"
|
|
1486
|
+
]) : void 0,
|
|
1487
|
+
fieldId: fieldElement ? fieldElement.getAttribute("data-fieldid") : void 0,
|
|
1488
|
+
item: fieldElement ? readItemDescriptor(fieldElement) : void 0,
|
|
1489
|
+
placeholderKey: placeholderElement ? readAttribute(placeholderElement, [
|
|
1490
|
+
"data-placeholder-start",
|
|
1491
|
+
"data-placeholder-key",
|
|
1492
|
+
"data-placeholderkey",
|
|
1493
|
+
"name",
|
|
1494
|
+
"id"
|
|
1495
|
+
]) : void 0,
|
|
1496
|
+
clientX: event.clientX,
|
|
1497
|
+
clientY: event.clientY,
|
|
1498
|
+
key: event.key,
|
|
1499
|
+
code: event.code,
|
|
1500
|
+
button: event.button,
|
|
1501
|
+
ctrlKey: !!event.ctrlKey,
|
|
1502
|
+
shiftKey: !!event.shiftKey,
|
|
1503
|
+
altKey: !!event.altKey,
|
|
1504
|
+
metaKey: !!event.metaKey
|
|
1505
|
+
};
|
|
1506
|
+
if (kind === "click" && fieldElement) {
|
|
1507
|
+
if (componentElement || event.ctrlKey || event.shiftKey || event.metaKey) {
|
|
1508
|
+
postEvent("interaction", payload);
|
|
1509
|
+
return;
|
|
1510
|
+
}
|
|
1511
|
+
clearPendingClickInteraction();
|
|
1512
|
+
pendingClickTimer = setTimeout(function() {
|
|
1513
|
+
pendingClickTimer = 0;
|
|
1514
|
+
if (hasActiveTextSelection(fieldElement)) {
|
|
1515
|
+
emitSelection();
|
|
1516
|
+
return;
|
|
1517
|
+
}
|
|
1518
|
+
postEvent("interaction", payload);
|
|
1519
|
+
}, 220);
|
|
1520
|
+
return;
|
|
1521
|
+
}
|
|
1522
|
+
postEvent("interaction", payload);
|
|
1523
|
+
}
|
|
1524
|
+
function handleWheel(event) {
|
|
1525
|
+
if (!event.ctrlKey && !event.metaKey) return;
|
|
1526
|
+
if (event.deltaY === 0) return;
|
|
1527
|
+
event.preventDefault();
|
|
1528
|
+
event.stopPropagation();
|
|
1529
|
+
var target = event.target && event.target.nodeType === Node.ELEMENT_NODE ? event.target : event.target && event.target.parentElement;
|
|
1530
|
+
if (!target) return;
|
|
1531
|
+
var fieldElement = closestFieldElement(target);
|
|
1532
|
+
var componentElement = findNearestComponentElement(target);
|
|
1533
|
+
var placeholderElement = closestPlaceholderElement(target);
|
|
1534
|
+
postEvent("interaction", {
|
|
1535
|
+
kind: "wheel",
|
|
1536
|
+
elementKey: fieldElement ? ensureElementKey(fieldElement) : ensureElementKey(target),
|
|
1537
|
+
componentId: componentElement ? readAttribute(componentElement, [
|
|
1538
|
+
"data-component-start",
|
|
1539
|
+
"data-component-id",
|
|
1540
|
+
"data-renderingid",
|
|
1541
|
+
"data-rendering-id",
|
|
1542
|
+
"id"
|
|
1543
|
+
]) : void 0,
|
|
1544
|
+
fieldId: fieldElement ? fieldElement.getAttribute("data-fieldid") : void 0,
|
|
1545
|
+
item: fieldElement ? readItemDescriptor(fieldElement) : void 0,
|
|
1546
|
+
placeholderKey: placeholderElement ? readAttribute(placeholderElement, [
|
|
1547
|
+
"data-placeholder-start",
|
|
1548
|
+
"data-placeholder-key",
|
|
1549
|
+
"data-placeholderkey",
|
|
1550
|
+
"name",
|
|
1551
|
+
"id"
|
|
1552
|
+
]) : void 0,
|
|
1553
|
+
clientX: event.clientX,
|
|
1554
|
+
clientY: event.clientY,
|
|
1555
|
+
deltaX: event.deltaX,
|
|
1556
|
+
deltaY: event.deltaY,
|
|
1557
|
+
ctrlKey: !!event.ctrlKey,
|
|
1558
|
+
shiftKey: !!event.shiftKey,
|
|
1559
|
+
altKey: !!event.altKey,
|
|
1560
|
+
metaKey: !!event.metaKey
|
|
1561
|
+
});
|
|
1562
|
+
}
|
|
1563
|
+
function handleHover(event) {
|
|
1564
|
+
clearTimeout(hoverTimer);
|
|
1565
|
+
hoverTimer = setTimeout(function() {
|
|
1566
|
+
var target = event.target && event.target.nodeType === Node.ELEMENT_NODE ? event.target : event.target && event.target.parentElement;
|
|
1567
|
+
if (!target) return;
|
|
1568
|
+
var mediaElement = closestMediaElement(target);
|
|
1569
|
+
var fieldElement = closestFieldElement(target) || (mediaElement ? closestFieldElement(mediaElement) : null);
|
|
1570
|
+
var componentElement = findNearestComponentElement(target);
|
|
1571
|
+
var placeholderElement = closestPlaceholderElement(target);
|
|
1572
|
+
var currentMediaSource = mediaElement ? getCurrentPictureSource(mediaElement) : null;
|
|
1573
|
+
postEvent("interaction", {
|
|
1574
|
+
kind: "hover",
|
|
1575
|
+
elementKey: ensureElementKey(fieldElement || mediaElement || target),
|
|
1576
|
+
componentId: componentElement ? readAttribute(componentElement, [
|
|
1577
|
+
"data-component-start",
|
|
1578
|
+
"data-component-id",
|
|
1579
|
+
"data-renderingid",
|
|
1580
|
+
"data-rendering-id",
|
|
1581
|
+
"id"
|
|
1582
|
+
]) : void 0,
|
|
1583
|
+
fieldId: fieldElement ? fieldElement.getAttribute("data-fieldid") : void 0,
|
|
1584
|
+
item: fieldElement ? readItemDescriptor(fieldElement) : void 0,
|
|
1585
|
+
media: mediaElement ? {
|
|
1586
|
+
elementKey: ensureElementKey(mediaElement),
|
|
1587
|
+
tagName: mediaElement.tagName ? mediaElement.tagName.toLowerCase() : "",
|
|
1588
|
+
variantName: currentMediaSource && currentMediaSource.getAttribute("data-variant") || mediaElement.getAttribute("data-variant") || void 0
|
|
1589
|
+
} : void 0,
|
|
1590
|
+
placeholderKey: placeholderElement ? readAttribute(placeholderElement, [
|
|
1591
|
+
"data-placeholder-start",
|
|
1592
|
+
"data-placeholder-key",
|
|
1593
|
+
"data-placeholderkey",
|
|
1594
|
+
"name",
|
|
1595
|
+
"id"
|
|
1596
|
+
]) : void 0,
|
|
1597
|
+
clientX: event.clientX,
|
|
1598
|
+
clientY: event.clientY
|
|
1599
|
+
});
|
|
1600
|
+
}, 100);
|
|
1601
|
+
}
|
|
1602
|
+
function handleInput(event) {
|
|
1603
|
+
var target = event.target && event.target.nodeType === Node.ELEMENT_NODE ? event.target : void 0;
|
|
1604
|
+
if (!target) return;
|
|
1605
|
+
var fieldElement = closestFieldElement(target);
|
|
1606
|
+
if (fieldElement) {
|
|
1607
|
+
emitFieldValueChanged(fieldElement);
|
|
1608
|
+
scheduleDomSnapshot();
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
function handleFocusOut(event) {
|
|
1612
|
+
var target = event.target && event.target.nodeType === Node.ELEMENT_NODE ? event.target : event.target && event.target.parentElement;
|
|
1613
|
+
if (!target) return;
|
|
1614
|
+
var fieldElement = closestFieldElement(target);
|
|
1615
|
+
if (!fieldElement) return;
|
|
1616
|
+
if (fieldElement.getAttribute("data-parhelia-inline-editing") !== "true") {
|
|
1617
|
+
return;
|
|
1618
|
+
}
|
|
1619
|
+
var relatedTarget = event.relatedTarget;
|
|
1620
|
+
if (relatedTarget && fieldElement.contains(relatedTarget)) return;
|
|
1621
|
+
emitFieldValueChanged(fieldElement);
|
|
1622
|
+
emitInlineEditEnded(fieldElement, "blur");
|
|
1623
|
+
restoreInlineEditableState(fieldElement);
|
|
1624
|
+
}
|
|
1625
|
+
function emitFieldValueChanged(element) {
|
|
1626
|
+
var isRichText = element.getAttribute("data-is-richtext") === "true" || element.getAttribute("data-is-rich-text") === "true";
|
|
1627
|
+
postEvent("fieldValueChanged", {
|
|
1628
|
+
elementKey: ensureElementKey(element),
|
|
1629
|
+
fieldId: element.getAttribute("data-fieldid") || "",
|
|
1630
|
+
item: readItemDescriptor(element),
|
|
1631
|
+
value: getFieldValue(element, isRichText),
|
|
1632
|
+
isRichText
|
|
1633
|
+
});
|
|
1634
|
+
}
|
|
1635
|
+
function emitInlineEditEnded(element, reason) {
|
|
1636
|
+
var isRichText = element.getAttribute("data-is-richtext") === "true" || element.getAttribute("data-is-rich-text") === "true";
|
|
1637
|
+
postEvent("inlineEditEnded", {
|
|
1638
|
+
elementKey: ensureElementKey(element),
|
|
1639
|
+
fieldId: element.getAttribute("data-fieldid") || "",
|
|
1640
|
+
item: readItemDescriptor(element),
|
|
1641
|
+
value: getFieldValue(element, isRichText),
|
|
1642
|
+
isRichText,
|
|
1643
|
+
reason
|
|
1644
|
+
});
|
|
1645
|
+
}
|
|
1646
|
+
function postEvent(name, payload, requestId) {
|
|
1647
|
+
var message = {
|
|
1648
|
+
family,
|
|
1649
|
+
version,
|
|
1650
|
+
direction: "host-to-parent",
|
|
1651
|
+
id: "host-" + Date.now().toString(36) + "-" + sequence++,
|
|
1652
|
+
name,
|
|
1653
|
+
timestamp: Date.now(),
|
|
1654
|
+
requestId,
|
|
1655
|
+
payload: payload || {}
|
|
1656
|
+
};
|
|
1657
|
+
logBridgeMessage("host -> parent", message);
|
|
1658
|
+
parentWindow.postMessage(message, parentOrigin);
|
|
1659
|
+
}
|
|
1660
|
+
function isCommand(message) {
|
|
1661
|
+
return message && typeof message === "object" && message.family === family && message.version && typeof message.version.major === "number" && message.version.major === version.major && message.direction === "parent-to-host" && typeof message.name === "string" && typeof message.id === "string";
|
|
1662
|
+
}
|
|
1663
|
+
function scheduleStructure() {
|
|
1664
|
+
clearTimeout(structureTimer);
|
|
1665
|
+
structureTimer = setTimeout(emitStructure, 100);
|
|
1666
|
+
}
|
|
1667
|
+
function scheduleGeometry() {
|
|
1668
|
+
clearTimeout(geometryTimer);
|
|
1669
|
+
geometryTimer = setTimeout(emitGeometry, 50);
|
|
1670
|
+
scheduleScrollChanged();
|
|
1671
|
+
}
|
|
1672
|
+
function scheduleDomSnapshot() {
|
|
1673
|
+
clearTimeout(domTimer);
|
|
1674
|
+
domTimer = setTimeout(emitDomSnapshot, 150);
|
|
1675
|
+
}
|
|
1676
|
+
function scheduleScrollChanged() {
|
|
1677
|
+
var scroll = getScroll();
|
|
1678
|
+
if (scroll.x === lastPostedScrollX && scroll.y === lastPostedScrollY) {
|
|
1679
|
+
return false;
|
|
1680
|
+
}
|
|
1681
|
+
lastPostedScrollX = scroll.x;
|
|
1682
|
+
lastPostedScrollY = scroll.y;
|
|
1683
|
+
postEvent("scrollChanged", { scroll });
|
|
1684
|
+
return true;
|
|
1685
|
+
}
|
|
1686
|
+
function getViewport() {
|
|
1687
|
+
return {
|
|
1688
|
+
width: window.innerWidth || document.documentElement.clientWidth || 0,
|
|
1689
|
+
height: window.innerHeight || document.documentElement.clientHeight || 0
|
|
1690
|
+
};
|
|
1691
|
+
}
|
|
1692
|
+
function getScroll() {
|
|
1693
|
+
var scrollingElement = document.scrollingElement;
|
|
1694
|
+
var documentElement = document.documentElement;
|
|
1695
|
+
var body = document.body;
|
|
1696
|
+
return {
|
|
1697
|
+
x: Math.max(
|
|
1698
|
+
window.scrollX || 0,
|
|
1699
|
+
scrollingElement ? scrollingElement.scrollLeft || 0 : 0,
|
|
1700
|
+
documentElement ? documentElement.scrollLeft || 0 : 0,
|
|
1701
|
+
body ? body.scrollLeft || 0 : 0
|
|
1702
|
+
),
|
|
1703
|
+
y: Math.max(
|
|
1704
|
+
window.scrollY || 0,
|
|
1705
|
+
scrollingElement ? scrollingElement.scrollTop || 0 : 0,
|
|
1706
|
+
documentElement ? documentElement.scrollTop || 0 : 0,
|
|
1707
|
+
body ? body.scrollTop || 0 : 0
|
|
1708
|
+
)
|
|
1709
|
+
};
|
|
1710
|
+
}
|
|
1711
|
+
function getDocumentSize() {
|
|
1712
|
+
var documentElement = document.documentElement;
|
|
1713
|
+
var body = document.body;
|
|
1714
|
+
return {
|
|
1715
|
+
width: Math.max(
|
|
1716
|
+
window.innerWidth || 0,
|
|
1717
|
+
documentElement ? documentElement.clientWidth || 0 : 0,
|
|
1718
|
+
documentElement ? documentElement.scrollWidth || 0 : 0,
|
|
1719
|
+
body ? body.scrollWidth || 0 : 0
|
|
1720
|
+
),
|
|
1721
|
+
height: Math.max(
|
|
1722
|
+
window.innerHeight || 0,
|
|
1723
|
+
documentElement ? documentElement.clientHeight || 0 : 0,
|
|
1724
|
+
documentElement ? documentElement.scrollHeight || 0 : 0,
|
|
1725
|
+
body ? body.scrollHeight || 0 : 0
|
|
1726
|
+
)
|
|
1727
|
+
};
|
|
1728
|
+
}
|
|
1729
|
+
function getQueryValue(name) {
|
|
1730
|
+
return new URLSearchParams(location.search).get(name);
|
|
1731
|
+
}
|
|
1732
|
+
function rectToJson(rect) {
|
|
1733
|
+
return {
|
|
1734
|
+
x: rect.x,
|
|
1735
|
+
y: rect.y,
|
|
1736
|
+
width: rect.width,
|
|
1737
|
+
height: rect.height,
|
|
1738
|
+
top: rect.top,
|
|
1739
|
+
right: rect.right,
|
|
1740
|
+
bottom: rect.bottom,
|
|
1741
|
+
left: rect.left
|
|
1742
|
+
};
|
|
1743
|
+
}
|
|
1744
|
+
function measureComponentMarker(marker, componentId) {
|
|
1745
|
+
var endMarker = componentId ? document.querySelector(
|
|
1746
|
+
'[data-component-end="' + cssEscape(componentId) + '"]'
|
|
1747
|
+
) : null;
|
|
1748
|
+
var elements = collectComponentElements(marker, endMarker);
|
|
1749
|
+
var rect = unionElementRects(elements);
|
|
1750
|
+
if (rect) {
|
|
1751
|
+
return {
|
|
1752
|
+
element: elements[0],
|
|
1753
|
+
rect
|
|
1754
|
+
};
|
|
1755
|
+
}
|
|
1756
|
+
if (!isChromeMarkerElement(marker)) {
|
|
1757
|
+
var markerRect = marker.getBoundingClientRect();
|
|
1758
|
+
if (hasVisibleRect(markerRect)) {
|
|
1759
|
+
return {
|
|
1760
|
+
element: marker,
|
|
1761
|
+
rect: markerRect
|
|
1762
|
+
};
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
return null;
|
|
1766
|
+
}
|
|
1767
|
+
function measurePlaceholderMarker(marker) {
|
|
1768
|
+
var markerRect = marker.getBoundingClientRect();
|
|
1769
|
+
if (!isChromeMarkerElement(marker) && hasVisibleRect(markerRect)) {
|
|
1770
|
+
return {
|
|
1771
|
+
element: marker,
|
|
1772
|
+
rect: markerRect
|
|
1773
|
+
};
|
|
1774
|
+
}
|
|
1775
|
+
var previous = getPreviousContentElement(marker);
|
|
1776
|
+
if (previous) {
|
|
1777
|
+
return {
|
|
1778
|
+
element: previous,
|
|
1779
|
+
rect: previous.getBoundingClientRect()
|
|
1780
|
+
};
|
|
1781
|
+
}
|
|
1782
|
+
var next = getNextContentElement(marker);
|
|
1783
|
+
if (next) {
|
|
1784
|
+
return {
|
|
1785
|
+
element: next,
|
|
1786
|
+
rect: next.getBoundingClientRect()
|
|
1787
|
+
};
|
|
1788
|
+
}
|
|
1789
|
+
var parent = marker.parentElement;
|
|
1790
|
+
if (parent) {
|
|
1791
|
+
return {
|
|
1792
|
+
element: parent,
|
|
1793
|
+
rect: parent.getBoundingClientRect()
|
|
1794
|
+
};
|
|
1795
|
+
}
|
|
1796
|
+
return {
|
|
1797
|
+
element: marker,
|
|
1798
|
+
rect: markerRect
|
|
1799
|
+
};
|
|
1800
|
+
}
|
|
1801
|
+
function getPreviousContentElement(element) {
|
|
1802
|
+
var current = element.previousElementSibling;
|
|
1803
|
+
while (current) {
|
|
1804
|
+
if (!isChromeMarkerElement(current) && !isElementHidden(current)) {
|
|
1805
|
+
return current;
|
|
1806
|
+
}
|
|
1807
|
+
current = current.previousElementSibling;
|
|
1808
|
+
}
|
|
1809
|
+
return null;
|
|
1810
|
+
}
|
|
1811
|
+
function getNextContentElement(element) {
|
|
1812
|
+
var current = element.nextElementSibling;
|
|
1813
|
+
while (current) {
|
|
1814
|
+
if (!isChromeMarkerElement(current) && !isElementHidden(current)) {
|
|
1815
|
+
return current;
|
|
1816
|
+
}
|
|
1817
|
+
current = current.nextElementSibling;
|
|
1818
|
+
}
|
|
1819
|
+
return null;
|
|
1820
|
+
}
|
|
1821
|
+
function detectPlaceholderOrientation(marker) {
|
|
1822
|
+
var explicit = marker.getAttribute("data-orientation");
|
|
1823
|
+
if (explicit === "horizontal" || explicit === "vertical") return explicit;
|
|
1824
|
+
var parent = marker.parentElement;
|
|
1825
|
+
if (!parent) return "vertical";
|
|
1826
|
+
var style = getComputedStyle(parent);
|
|
1827
|
+
var display = style.getPropertyValue("display");
|
|
1828
|
+
if (display === "flex" || display === "inline-flex") {
|
|
1829
|
+
var flexDirection = style.getPropertyValue("flex-direction");
|
|
1830
|
+
if (flexDirection === "row" || flexDirection === "row-reverse") {
|
|
1831
|
+
return "horizontal";
|
|
1832
|
+
}
|
|
1833
|
+
if (flexDirection === "column" || flexDirection === "column-reverse") {
|
|
1834
|
+
return "vertical";
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
1837
|
+
if (display === "grid" || display === "inline-grid") {
|
|
1838
|
+
var gridAutoFlow = style.getPropertyValue("grid-auto-flow");
|
|
1839
|
+
if (gridAutoFlow.indexOf("column") >= 0) return "horizontal";
|
|
1840
|
+
if (gridAutoFlow.indexOf("row") >= 0) return "vertical";
|
|
1841
|
+
}
|
|
1842
|
+
var previous = getPreviousContentElement(marker);
|
|
1843
|
+
var next = getNextContentElement(marker);
|
|
1844
|
+
if (previous && next) {
|
|
1845
|
+
var previousRect = previous.getBoundingClientRect();
|
|
1846
|
+
var nextRect = next.getBoundingClientRect();
|
|
1847
|
+
var horizontalDiff = Math.abs(nextRect.x - previousRect.x);
|
|
1848
|
+
var verticalDiff = Math.abs(nextRect.y - previousRect.y);
|
|
1849
|
+
return horizontalDiff > verticalDiff ? "horizontal" : "vertical";
|
|
1850
|
+
}
|
|
1851
|
+
return "vertical";
|
|
1852
|
+
}
|
|
1853
|
+
function collectComponentElements(marker, endMarker) {
|
|
1854
|
+
if (!marker) return [];
|
|
1855
|
+
if (!isChromeMarkerElement(marker)) {
|
|
1856
|
+
return [marker];
|
|
1857
|
+
}
|
|
1858
|
+
var elements = [];
|
|
1859
|
+
var current = marker.nextElementSibling;
|
|
1860
|
+
var safety = 0;
|
|
1861
|
+
while (current && current !== endMarker && safety++ < 1e3) {
|
|
1862
|
+
if (!isChromeMarkerElement(current) && !isElementHidden(current)) {
|
|
1863
|
+
elements.push(current);
|
|
1864
|
+
}
|
|
1865
|
+
if (!endMarker) break;
|
|
1866
|
+
current = current.nextElementSibling;
|
|
1867
|
+
}
|
|
1868
|
+
return elements;
|
|
1869
|
+
}
|
|
1870
|
+
function unionElementRects(elements) {
|
|
1871
|
+
var left = Infinity;
|
|
1872
|
+
var top = Infinity;
|
|
1873
|
+
var right = -Infinity;
|
|
1874
|
+
var bottom = -Infinity;
|
|
1875
|
+
var hasRect = false;
|
|
1876
|
+
elements.forEach(function(element) {
|
|
1877
|
+
var rect = element.getBoundingClientRect();
|
|
1878
|
+
if (!hasVisibleRect(rect)) return;
|
|
1879
|
+
left = Math.min(left, rect.left);
|
|
1880
|
+
top = Math.min(top, rect.top);
|
|
1881
|
+
right = Math.max(right, rect.right);
|
|
1882
|
+
bottom = Math.max(bottom, rect.bottom);
|
|
1883
|
+
hasRect = true;
|
|
1884
|
+
});
|
|
1885
|
+
if (!hasRect) return null;
|
|
1886
|
+
return {
|
|
1887
|
+
x: left,
|
|
1888
|
+
y: top,
|
|
1889
|
+
left,
|
|
1890
|
+
top,
|
|
1891
|
+
right,
|
|
1892
|
+
bottom,
|
|
1893
|
+
width: right - left,
|
|
1894
|
+
height: bottom - top
|
|
1895
|
+
};
|
|
1896
|
+
}
|
|
1897
|
+
function hasVisibleRect(rect) {
|
|
1898
|
+
return !!rect && (rect.width > 0 || rect.height > 0);
|
|
1899
|
+
}
|
|
1900
|
+
function isChromeMarkerElement(element) {
|
|
1901
|
+
if (!element || !element.tagName) return false;
|
|
1902
|
+
return element.tagName === "SCRIPT" || element.tagName === "CODE" && element.getAttribute("type") === "text/sitecore" && element.hasAttribute("chrometype");
|
|
1903
|
+
}
|
|
1904
|
+
function ensureElementKey(element) {
|
|
1905
|
+
if (!element || !element.getAttribute) {
|
|
1906
|
+
return "";
|
|
1907
|
+
}
|
|
1908
|
+
var existing = element.getAttribute("data-parhelia-element-key");
|
|
1909
|
+
if (existing) return existing;
|
|
1910
|
+
var next = "el-" + ++elementSequence;
|
|
1911
|
+
element.setAttribute("data-parhelia-element-key", next);
|
|
1912
|
+
return next;
|
|
1913
|
+
}
|
|
1914
|
+
function getElementByKey(key) {
|
|
1915
|
+
if (!key) return null;
|
|
1916
|
+
return document.querySelector(
|
|
1917
|
+
'[data-parhelia-element-key="' + cssEscape(key) + '"]'
|
|
1918
|
+
);
|
|
1919
|
+
}
|
|
1920
|
+
function getInlineEditableElement(payload) {
|
|
1921
|
+
var target = payload.elementKey ? getElementByKey(payload.elementKey) : null;
|
|
1922
|
+
var fieldElement = target ? closestFieldElement(target) : null;
|
|
1923
|
+
if (!fieldElement && target && target.getAttribute("data-fieldid")) {
|
|
1924
|
+
fieldElement = target;
|
|
1925
|
+
}
|
|
1926
|
+
if (fieldElement && payload.fieldId && fieldElement.getAttribute("data-fieldid") !== payload.fieldId) {
|
|
1927
|
+
fieldElement = null;
|
|
1928
|
+
}
|
|
1929
|
+
if (!fieldElement && payload.fieldId) {
|
|
1930
|
+
fieldElement = document.querySelector(
|
|
1931
|
+
'[data-fieldid="' + cssEscape(payload.fieldId) + '"]'
|
|
1932
|
+
);
|
|
1933
|
+
}
|
|
1934
|
+
return fieldElement;
|
|
1935
|
+
}
|
|
1936
|
+
function beginInlineEdit(element, payload) {
|
|
1937
|
+
if (currentMode === "preview") return;
|
|
1938
|
+
var selectedRange = getSelectionRangeWithin(element);
|
|
1939
|
+
rememberInlineEditableState(element);
|
|
1940
|
+
element.setAttribute("contenteditable", "true");
|
|
1941
|
+
element.setAttribute("data-parhelia-inline-editing", "true");
|
|
1942
|
+
if (selectedRange) {
|
|
1943
|
+
restoreSelectionRange(element, selectedRange);
|
|
1944
|
+
} else {
|
|
1945
|
+
focusEditableElement(element, payload.clientX, payload.clientY);
|
|
1946
|
+
}
|
|
1947
|
+
emitSelection();
|
|
1948
|
+
emitGeometry();
|
|
1949
|
+
}
|
|
1950
|
+
function getSelectionRangeWithin(element) {
|
|
1951
|
+
var selection = document.getSelection();
|
|
1952
|
+
if (!selection || selection.rangeCount === 0) {
|
|
1953
|
+
return null;
|
|
1954
|
+
}
|
|
1955
|
+
try {
|
|
1956
|
+
var range = selection.getRangeAt(0);
|
|
1957
|
+
var ancestor = range.commonAncestorContainer;
|
|
1958
|
+
var ancestorElement = ancestor.nodeType === Node.ELEMENT_NODE ? ancestor : ancestor.parentElement;
|
|
1959
|
+
if (!ancestorElement || !element.contains(ancestorElement)) {
|
|
1960
|
+
return null;
|
|
1961
|
+
}
|
|
1962
|
+
return range.cloneRange();
|
|
1963
|
+
} catch (error) {
|
|
1964
|
+
return null;
|
|
1965
|
+
}
|
|
1966
|
+
}
|
|
1967
|
+
function rememberInlineEditableState(element) {
|
|
1968
|
+
if (element.hasAttribute("data-parhelia-previous-contenteditable")) return;
|
|
1969
|
+
element.setAttribute(
|
|
1970
|
+
"data-parhelia-previous-contenteditable",
|
|
1971
|
+
element.hasAttribute("contenteditable") ? element.getAttribute("contenteditable") || "" : "__missing__"
|
|
1972
|
+
);
|
|
1973
|
+
}
|
|
1974
|
+
function restoreInlineEditableState(element) {
|
|
1975
|
+
var previous = element.getAttribute(
|
|
1976
|
+
"data-parhelia-previous-contenteditable"
|
|
1977
|
+
);
|
|
1978
|
+
if (previous === "__missing__" || previous === null) {
|
|
1979
|
+
element.removeAttribute("contenteditable");
|
|
1980
|
+
} else {
|
|
1981
|
+
element.setAttribute("contenteditable", previous);
|
|
1982
|
+
}
|
|
1983
|
+
element.removeAttribute("data-parhelia-previous-contenteditable");
|
|
1984
|
+
element.removeAttribute("data-parhelia-inline-editing");
|
|
1985
|
+
emitGeometry();
|
|
1986
|
+
}
|
|
1987
|
+
function focusEditableElement(element, clientX, clientY) {
|
|
1988
|
+
try {
|
|
1989
|
+
element.focus({ preventScroll: true });
|
|
1990
|
+
} catch (error) {
|
|
1991
|
+
element.focus();
|
|
1992
|
+
}
|
|
1993
|
+
var range = createRangeFromPoint(clientX, clientY);
|
|
1994
|
+
if (!range || !element.contains(range.startContainer)) return;
|
|
1995
|
+
var selection = document.getSelection();
|
|
1996
|
+
if (!selection) return;
|
|
1997
|
+
selection.removeAllRanges();
|
|
1998
|
+
selection.addRange(range);
|
|
1999
|
+
}
|
|
2000
|
+
function restoreSelectionRange(element, range) {
|
|
2001
|
+
try {
|
|
2002
|
+
element.focus({ preventScroll: true });
|
|
2003
|
+
} catch (error) {
|
|
2004
|
+
element.focus();
|
|
2005
|
+
}
|
|
2006
|
+
var selection = document.getSelection();
|
|
2007
|
+
if (!selection) return;
|
|
2008
|
+
selection.removeAllRanges();
|
|
2009
|
+
selection.addRange(range);
|
|
2010
|
+
}
|
|
2011
|
+
function restoreSavedTextSelection(element, payload) {
|
|
2012
|
+
if (!savedTextSelection) return false;
|
|
2013
|
+
if (payload.elementKey && savedTextSelection.elementKey !== payload.elementKey) {
|
|
2014
|
+
return false;
|
|
2015
|
+
}
|
|
2016
|
+
if (payload.fieldId && savedTextSelection.fieldId !== payload.fieldId) {
|
|
2017
|
+
return false;
|
|
2018
|
+
}
|
|
2019
|
+
try {
|
|
2020
|
+
var selection = document.getSelection();
|
|
2021
|
+
if (!selection) return false;
|
|
2022
|
+
element.focus({ preventScroll: true });
|
|
2023
|
+
selection.removeAllRanges();
|
|
2024
|
+
selection.addRange(savedTextSelection.range.cloneRange());
|
|
2025
|
+
return true;
|
|
2026
|
+
} catch (error) {
|
|
2027
|
+
return false;
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
function applyRichTextCommand(payload) {
|
|
2031
|
+
if (payload.command === "createLink") {
|
|
2032
|
+
insertLinkAtCurrentSelection(payload.value || "#");
|
|
2033
|
+
return;
|
|
2034
|
+
}
|
|
2035
|
+
document.execCommand(payload.command, false, payload.value);
|
|
2036
|
+
}
|
|
2037
|
+
function insertLinkAtCurrentSelection(href) {
|
|
2038
|
+
var selection = document.getSelection();
|
|
2039
|
+
if (!selection || selection.rangeCount === 0) {
|
|
2040
|
+
document.execCommand("createLink", false, href);
|
|
2041
|
+
return;
|
|
2042
|
+
}
|
|
2043
|
+
var range = selection.getRangeAt(0);
|
|
2044
|
+
var link = document.createElement("a");
|
|
2045
|
+
link.href = href;
|
|
2046
|
+
link.textContent = href;
|
|
2047
|
+
range.deleteContents();
|
|
2048
|
+
range.insertNode(link);
|
|
2049
|
+
var afterLinkRange = document.createRange();
|
|
2050
|
+
afterLinkRange.setStartAfter(link);
|
|
2051
|
+
afterLinkRange.collapse(true);
|
|
2052
|
+
selection.removeAllRanges();
|
|
2053
|
+
selection.addRange(afterLinkRange);
|
|
2054
|
+
}
|
|
2055
|
+
function createRangeFromPoint(clientX, clientY) {
|
|
2056
|
+
if (typeof clientX !== "number" || typeof clientY !== "number") return null;
|
|
2057
|
+
if (typeof document.caretRangeFromPoint === "function") {
|
|
2058
|
+
return document.caretRangeFromPoint(clientX, clientY);
|
|
2059
|
+
}
|
|
2060
|
+
if (typeof document.caretPositionFromPoint === "function") {
|
|
2061
|
+
var position = document.caretPositionFromPoint(clientX, clientY);
|
|
2062
|
+
if (!position) return null;
|
|
2063
|
+
var range = document.createRange();
|
|
2064
|
+
range.setStart(position.offsetNode, position.offset);
|
|
2065
|
+
range.collapse(true);
|
|
2066
|
+
return range;
|
|
2067
|
+
}
|
|
2068
|
+
return null;
|
|
2069
|
+
}
|
|
2070
|
+
function getFieldValue(element, isRichText) {
|
|
2071
|
+
var value = isRichText ? element.innerHTML : element.textContent || "";
|
|
2072
|
+
return value.replace(/\u200B/g, "");
|
|
2073
|
+
}
|
|
2074
|
+
function findTargetElement(payload) {
|
|
2075
|
+
if (payload.elementKey) return getElementByKey(payload.elementKey);
|
|
2076
|
+
if (payload.componentId) {
|
|
2077
|
+
return document.querySelector(
|
|
2078
|
+
'[data-component-start="' + cssEscape(payload.componentId) + '"], [data-component-id="' + cssEscape(payload.componentId) + '"], [data-renderingid="' + cssEscape(payload.componentId) + '"], [data-rendering-id="' + cssEscape(payload.componentId) + '"]'
|
|
2079
|
+
);
|
|
2080
|
+
}
|
|
2081
|
+
if (payload.fieldId) {
|
|
2082
|
+
return document.querySelector(
|
|
2083
|
+
'[data-fieldid="' + cssEscape(payload.fieldId) + '"]'
|
|
2084
|
+
);
|
|
2085
|
+
}
|
|
2086
|
+
if (payload.placeholderKey) {
|
|
2087
|
+
return document.querySelector(
|
|
2088
|
+
'[data-placeholder-start="' + cssEscape(payload.placeholderKey) + '"], [data-placeholder-key="' + cssEscape(payload.placeholderKey) + '"], [data-placeholderkey="' + cssEscape(payload.placeholderKey) + '"]'
|
|
2089
|
+
);
|
|
2090
|
+
}
|
|
2091
|
+
return null;
|
|
2092
|
+
}
|
|
2093
|
+
function findFieldElementForGeometryQuery(query) {
|
|
2094
|
+
if (query.elementKey) {
|
|
2095
|
+
var element = getElementByKey(query.elementKey);
|
|
2096
|
+
if (element && element.hasAttribute("data-fieldid")) return element;
|
|
2097
|
+
}
|
|
2098
|
+
if (!query.fieldId) return null;
|
|
2099
|
+
var candidates = Array.prototype.slice.call(
|
|
2100
|
+
document.querySelectorAll(
|
|
2101
|
+
'[data-fieldid="' + cssEscape(query.fieldId) + '"]'
|
|
2102
|
+
)
|
|
2103
|
+
);
|
|
2104
|
+
if (!query.item || !query.item.id) return candidates[0] || null;
|
|
2105
|
+
return candidates.filter(function(element2) {
|
|
2106
|
+
return itemDescriptorMatches(readItemDescriptor(element2), query.item);
|
|
2107
|
+
})[0] || candidates[0] || null;
|
|
2108
|
+
}
|
|
2109
|
+
function itemDescriptorMatches(left, right) {
|
|
2110
|
+
if (!left || !right) return false;
|
|
2111
|
+
if (!idsMatch(left.id, right.id)) return false;
|
|
2112
|
+
if (right.language && left.language && left.language !== right.language) {
|
|
2113
|
+
return false;
|
|
2114
|
+
}
|
|
2115
|
+
if (right.version !== void 0 && left.version !== void 0 && left.version !== right.version) {
|
|
2116
|
+
return false;
|
|
2117
|
+
}
|
|
2118
|
+
return true;
|
|
2119
|
+
}
|
|
2120
|
+
function idsMatch(left, right) {
|
|
2121
|
+
return normalizeId(left) === normalizeId(right);
|
|
2122
|
+
}
|
|
2123
|
+
function normalizeId(value) {
|
|
2124
|
+
return String(value || "").replace(/[{}]/g, "").toLowerCase();
|
|
2125
|
+
}
|
|
2126
|
+
function readEndElementKey(componentId) {
|
|
2127
|
+
var end = document.querySelector(
|
|
2128
|
+
'[data-component-end="' + cssEscape(componentId) + '"]'
|
|
2129
|
+
);
|
|
2130
|
+
return end ? ensureElementKey(end) : void 0;
|
|
2131
|
+
}
|
|
2132
|
+
function readAttribute(element, names) {
|
|
2133
|
+
for (var index = 0; index < names.length; index++) {
|
|
2134
|
+
var value = element.getAttribute(names[index]);
|
|
2135
|
+
if (value) return value;
|
|
2136
|
+
}
|
|
2137
|
+
return void 0;
|
|
2138
|
+
}
|
|
2139
|
+
function readEditable(element) {
|
|
2140
|
+
if (element.getAttribute("data-parhelia-editable") === "false")
|
|
2141
|
+
return false;
|
|
2142
|
+
if (element.getAttribute("data-editable") === "false") return false;
|
|
2143
|
+
return true;
|
|
2144
|
+
}
|
|
2145
|
+
function readItemDescriptor(element) {
|
|
2146
|
+
var id = element.getAttribute("data-itemid");
|
|
2147
|
+
if (!id) return void 0;
|
|
2148
|
+
return {
|
|
2149
|
+
id,
|
|
2150
|
+
language: element.getAttribute("data-language") || void 0,
|
|
2151
|
+
version: toNumber(element.getAttribute("data-version")),
|
|
2152
|
+
database: element.getAttribute("data-database") || void 0
|
|
2153
|
+
};
|
|
2154
|
+
}
|
|
2155
|
+
function readPageItemDescriptor() {
|
|
2156
|
+
return {
|
|
2157
|
+
id: getQueryValue("sc_itemid") || getQueryValue("itemid") || config.pageId || "",
|
|
2158
|
+
language: getQueryValue("sc_lang") || getQueryValue("lang") || "",
|
|
2159
|
+
version: toNumber(getQueryValue("sc_version") || getQueryValue("version")) || 0,
|
|
2160
|
+
database: getQueryValue("sc_database") || getQueryValue("database") || void 0,
|
|
2161
|
+
renderedFieldIds: []
|
|
2162
|
+
};
|
|
2163
|
+
}
|
|
2164
|
+
function readRenderedItemDescriptor(element, fallbackItem) {
|
|
2165
|
+
var item = readItemDescriptor(element);
|
|
2166
|
+
if (!item || !item.id) return void 0;
|
|
2167
|
+
return {
|
|
2168
|
+
id: item.id,
|
|
2169
|
+
language: item.language || fallbackItem.language,
|
|
2170
|
+
version: item.version || fallbackItem.version,
|
|
2171
|
+
database: item.database || fallbackItem.database,
|
|
2172
|
+
renderedFieldIds: []
|
|
2173
|
+
};
|
|
2174
|
+
}
|
|
2175
|
+
function splitCsv(value) {
|
|
2176
|
+
if (!value) return [];
|
|
2177
|
+
return value.split(",").map(function(item) {
|
|
2178
|
+
return item.trim();
|
|
2179
|
+
}).filter(Boolean);
|
|
2180
|
+
}
|
|
2181
|
+
function stripSkeletonComponent(component) {
|
|
2182
|
+
return {
|
|
2183
|
+
id: component.id,
|
|
2184
|
+
name: component.name || "",
|
|
2185
|
+
type: component.type || "",
|
|
2186
|
+
typeId: component.typeId || "",
|
|
2187
|
+
parametersTemplateId: component.parametersTemplateId,
|
|
2188
|
+
parameters: component.parameters,
|
|
2189
|
+
items: component.items,
|
|
2190
|
+
placeholders: component.placeholders.map(stripSkeletonPlaceholder),
|
|
2191
|
+
datasourceItem: component.datasourceItem,
|
|
2192
|
+
visible: component.visible,
|
|
2193
|
+
renderedDictionaryKeys: component.renderedDictionaryKeys || [],
|
|
2194
|
+
isShared: component.isShared,
|
|
2195
|
+
hasPersonalization: component.hasPersonalization,
|
|
2196
|
+
layoutId: component.layoutId,
|
|
2197
|
+
isSharedLayout: component.isSharedLayout,
|
|
2198
|
+
editorFields: component.editorFields || {},
|
|
2199
|
+
editable: component.editable
|
|
2200
|
+
};
|
|
2201
|
+
}
|
|
2202
|
+
function stripSkeletonPlaceholder(placeholder) {
|
|
2203
|
+
return {
|
|
2204
|
+
key: placeholder.key,
|
|
2205
|
+
name: placeholder.name || placeholder.key,
|
|
2206
|
+
description: placeholder.description || "",
|
|
2207
|
+
components: placeholder.components.map(stripSkeletonComponent),
|
|
2208
|
+
editable: placeholder.editable
|
|
2209
|
+
};
|
|
2210
|
+
}
|
|
2211
|
+
function mergeRenderedField(items, item, fieldId) {
|
|
2212
|
+
if (!item || !item.id) return items;
|
|
2213
|
+
if (!fieldId) return items;
|
|
2214
|
+
var existing = items.filter(function(candidate) {
|
|
2215
|
+
return candidate.id === item.id && candidate.language === item.language && candidate.version === item.version;
|
|
2216
|
+
})[0];
|
|
2217
|
+
if (existing) {
|
|
2218
|
+
if (existing.renderedFieldIds.indexOf(fieldId) < 0) {
|
|
2219
|
+
existing.renderedFieldIds.push(fieldId);
|
|
2220
|
+
}
|
|
2221
|
+
return items;
|
|
2222
|
+
}
|
|
2223
|
+
items.push({
|
|
2224
|
+
id: item.id,
|
|
2225
|
+
language: item.language,
|
|
2226
|
+
version: item.version,
|
|
2227
|
+
renderedFieldIds: [fieldId]
|
|
2228
|
+
});
|
|
2229
|
+
return items;
|
|
2230
|
+
}
|
|
2231
|
+
function closestFieldElement(element) {
|
|
2232
|
+
return element && element.closest ? element.closest("[data-fieldid]") : null;
|
|
2233
|
+
}
|
|
2234
|
+
function closestMediaElement(element) {
|
|
2235
|
+
return element && element.closest ? element.closest("picture,img") : null;
|
|
2236
|
+
}
|
|
2237
|
+
function getCurrentPictureSource(mediaElement) {
|
|
2238
|
+
if (!mediaElement) return null;
|
|
2239
|
+
if (mediaElement.tagName && mediaElement.tagName.toLowerCase() === "img") {
|
|
2240
|
+
return mediaElement;
|
|
2241
|
+
}
|
|
2242
|
+
var sources = Array.prototype.slice.call(
|
|
2243
|
+
mediaElement.querySelectorAll("source")
|
|
2244
|
+
);
|
|
2245
|
+
var currentSource = null;
|
|
2246
|
+
sources.forEach(function(source) {
|
|
2247
|
+
var media = source.getAttribute("media");
|
|
2248
|
+
if (!media) return;
|
|
2249
|
+
try {
|
|
2250
|
+
if (window.matchMedia(media).matches) {
|
|
2251
|
+
currentSource = source;
|
|
2252
|
+
}
|
|
2253
|
+
} catch (error) {
|
|
2254
|
+
}
|
|
2255
|
+
});
|
|
2256
|
+
return currentSource || mediaElement.querySelector("img") || mediaElement;
|
|
2257
|
+
}
|
|
2258
|
+
function closestComponentElement(element) {
|
|
2259
|
+
return element && element.closest ? element.closest(
|
|
2260
|
+
"[data-component-start], [data-component-id], [data-renderingid], [data-rendering-id]"
|
|
2261
|
+
) : null;
|
|
2262
|
+
}
|
|
2263
|
+
function findEnclosingMarkerElement(startElement, startAttribute, endAttribute) {
|
|
2264
|
+
if (!startElement || !startElement.ownerDocument) return null;
|
|
2265
|
+
var markers = Array.prototype.slice.call(
|
|
2266
|
+
startElement.ownerDocument.querySelectorAll("[" + startAttribute + "]")
|
|
2267
|
+
);
|
|
2268
|
+
var bestMarker = null;
|
|
2269
|
+
markers.forEach(function(marker) {
|
|
2270
|
+
if (!isNodeBeforeOrContains(marker, startElement)) return;
|
|
2271
|
+
var endMarker = findMatchingEndMarker(marker, startAttribute, endAttribute);
|
|
2272
|
+
if (endMarker && !isNodeBeforeOrContains(startElement, endMarker)) {
|
|
2273
|
+
return;
|
|
2274
|
+
}
|
|
2275
|
+
if (!bestMarker || isNodeBefore(bestMarker, marker)) {
|
|
2276
|
+
bestMarker = marker;
|
|
2277
|
+
}
|
|
2278
|
+
});
|
|
2279
|
+
return bestMarker;
|
|
2280
|
+
}
|
|
2281
|
+
function findMatchingEndMarker(marker, startAttribute, endAttribute) {
|
|
2282
|
+
var markerId = marker.getAttribute(startAttribute);
|
|
2283
|
+
if (!markerId) return null;
|
|
2284
|
+
var endMarkers = Array.prototype.slice.call(
|
|
2285
|
+
marker.ownerDocument.querySelectorAll("[" + endAttribute + "]")
|
|
2286
|
+
);
|
|
2287
|
+
return endMarkers.filter(function(endMarker) {
|
|
2288
|
+
return endMarker.getAttribute(endAttribute) === markerId && isNodeBefore(marker, endMarker);
|
|
2289
|
+
})[0] || null;
|
|
2290
|
+
}
|
|
2291
|
+
function isNodeBefore(left, right) {
|
|
2292
|
+
if (!left || !right || left === right) return false;
|
|
2293
|
+
return !!(left.compareDocumentPosition(right) & Node.DOCUMENT_POSITION_FOLLOWING);
|
|
2294
|
+
}
|
|
2295
|
+
function isNodeBeforeOrContains(left, right) {
|
|
2296
|
+
if (!left || !right) return false;
|
|
2297
|
+
return left === right || left.contains && left.contains(right) || isNodeBefore(left, right);
|
|
2298
|
+
}
|
|
2299
|
+
function findNearestComponentElement(startElement) {
|
|
2300
|
+
var current = startElement;
|
|
2301
|
+
while (current) {
|
|
2302
|
+
var directComponentId = current.getAttribute("data-component-id");
|
|
2303
|
+
if (directComponentId && isEditableComponent(current)) return current;
|
|
2304
|
+
current = current.parentElement;
|
|
2305
|
+
}
|
|
2306
|
+
current = closestComponentElement(startElement);
|
|
2307
|
+
if (current && isEditableComponent(current)) return current;
|
|
2308
|
+
current = startElement;
|
|
2309
|
+
while (current) {
|
|
2310
|
+
if (current.hasAttribute("data-component-start") && !current.hasAttribute("data-skip-component") && isEditableComponent(current)) {
|
|
2311
|
+
return current;
|
|
2312
|
+
}
|
|
2313
|
+
current = current.parentElement;
|
|
2314
|
+
}
|
|
2315
|
+
current = findEnclosingMarkerElement(
|
|
2316
|
+
startElement,
|
|
2317
|
+
"data-component-start",
|
|
2318
|
+
"data-component-end"
|
|
2319
|
+
);
|
|
2320
|
+
if (current && !current.hasAttribute("data-skip-component") && isEditableComponent(current)) {
|
|
2321
|
+
return current;
|
|
2322
|
+
}
|
|
2323
|
+
current = startElement;
|
|
2324
|
+
while (current) {
|
|
2325
|
+
var itemId = current.getAttribute("data-itemid");
|
|
2326
|
+
if (itemId && isEditableComponent(current)) return current;
|
|
2327
|
+
current = current.parentElement;
|
|
2328
|
+
}
|
|
2329
|
+
current = startElement;
|
|
2330
|
+
while (current) {
|
|
2331
|
+
if (current.hasAttribute("data-component-start") && !current.hasAttribute("data-skip-component") && isEditableComponent(current)) {
|
|
2332
|
+
return current;
|
|
2333
|
+
}
|
|
2334
|
+
var previous = current.previousElementSibling;
|
|
2335
|
+
current = previous || current.parentElement;
|
|
2336
|
+
}
|
|
2337
|
+
return null;
|
|
2338
|
+
}
|
|
2339
|
+
function closestPlaceholderElement(element) {
|
|
2340
|
+
var current = element && element.closest ? element.closest(
|
|
2341
|
+
"[data-placeholder-start], [data-placeholder-key], [data-placeholderkey]"
|
|
2342
|
+
) : null;
|
|
2343
|
+
return current || findEnclosingMarkerElement(
|
|
2344
|
+
element,
|
|
2345
|
+
"data-placeholder-start",
|
|
2346
|
+
"data-placeholder-end"
|
|
2347
|
+
);
|
|
2348
|
+
}
|
|
2349
|
+
function isElementHidden(element) {
|
|
2350
|
+
return element.hidden || getComputedStyle(element).display === "none" || getComputedStyle(element).visibility === "hidden";
|
|
2351
|
+
}
|
|
2352
|
+
function isEditableComponent(element) {
|
|
2353
|
+
return element.getAttribute("data-editable") !== "false";
|
|
2354
|
+
}
|
|
2355
|
+
function applySelectionClasses() {
|
|
2356
|
+
Array.prototype.slice.call(document.querySelectorAll("[data-parhelia-selected='true']")).forEach(function(element) {
|
|
2357
|
+
element.removeAttribute("data-parhelia-selected");
|
|
2358
|
+
});
|
|
2359
|
+
knownSelectedComponents.forEach(function(componentId) {
|
|
2360
|
+
var element = findTargetElement({ componentId });
|
|
2361
|
+
if (element) {
|
|
2362
|
+
element.setAttribute("data-parhelia-selected", "true");
|
|
2363
|
+
}
|
|
2364
|
+
});
|
|
2365
|
+
}
|
|
2366
|
+
function setEditingEnabled(enabled) {
|
|
2367
|
+
Array.prototype.slice.call(document.querySelectorAll("[contenteditable]")).forEach(function(element) {
|
|
2368
|
+
if (enabled) {
|
|
2369
|
+
element.setAttribute("contenteditable", "true");
|
|
2370
|
+
} else {
|
|
2371
|
+
element.setAttribute("contenteditable", "false");
|
|
2372
|
+
element.blur();
|
|
2373
|
+
}
|
|
2374
|
+
});
|
|
2375
|
+
}
|
|
2376
|
+
function applyHostZoom(value) {
|
|
2377
|
+
var documentElement = document.documentElement;
|
|
2378
|
+
if (!documentElement) return;
|
|
2379
|
+
var zoom = Number(value);
|
|
2380
|
+
if (!Number.isFinite(zoom) || zoom <= 0) {
|
|
2381
|
+
zoom = 1;
|
|
2382
|
+
}
|
|
2383
|
+
if (zoomAnimationFrame) {
|
|
2384
|
+
cancelAnimationFrame(zoomAnimationFrame);
|
|
2385
|
+
zoomAnimationFrame = 0;
|
|
2386
|
+
}
|
|
2387
|
+
documentElement.style.removeProperty("transition");
|
|
2388
|
+
function getCurrentZoom() {
|
|
2389
|
+
var inlineZoom = Number.parseFloat(documentElement.style.zoom);
|
|
2390
|
+
if (Number.isFinite(inlineZoom) && inlineZoom > 0) return inlineZoom;
|
|
2391
|
+
var computedZoom = Number.parseFloat(
|
|
2392
|
+
getComputedStyle(documentElement).zoom
|
|
2393
|
+
);
|
|
2394
|
+
if (Number.isFinite(computedZoom) && computedZoom > 0) {
|
|
2395
|
+
return computedZoom;
|
|
2396
|
+
}
|
|
2397
|
+
return 1;
|
|
2398
|
+
}
|
|
2399
|
+
function applyFinalZoom() {
|
|
2400
|
+
if (Math.abs(zoom - 1) < 1e-3) {
|
|
2401
|
+
documentElement.style.removeProperty("zoom");
|
|
2402
|
+
documentElement.style.removeProperty("transform");
|
|
2403
|
+
documentElement.style.removeProperty("transform-origin");
|
|
2404
|
+
documentElement.style.removeProperty("overflow");
|
|
2405
|
+
documentElement.style.removeProperty("will-change");
|
|
2406
|
+
return;
|
|
2407
|
+
}
|
|
2408
|
+
documentElement.style.zoom = String(zoom);
|
|
2409
|
+
documentElement.style.removeProperty("transform");
|
|
2410
|
+
documentElement.style.removeProperty("transform-origin");
|
|
2411
|
+
documentElement.style.overflow = "auto";
|
|
2412
|
+
documentElement.style.removeProperty("will-change");
|
|
2413
|
+
}
|
|
2414
|
+
var reduceMotion = false;
|
|
2415
|
+
try {
|
|
2416
|
+
reduceMotion = matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
2417
|
+
} catch (error) {
|
|
2418
|
+
}
|
|
2419
|
+
if (reduceMotion) {
|
|
2420
|
+
applyFinalZoom();
|
|
2421
|
+
return;
|
|
2422
|
+
}
|
|
2423
|
+
var startZoom = getCurrentZoom();
|
|
2424
|
+
var targetZoom = zoom;
|
|
2425
|
+
if (Math.abs(startZoom - targetZoom) < 1e-3) {
|
|
2426
|
+
applyFinalZoom();
|
|
2427
|
+
return;
|
|
2428
|
+
}
|
|
2429
|
+
documentElement.style.willChange = "zoom";
|
|
2430
|
+
documentElement.style.zoom = String(startZoom);
|
|
2431
|
+
if (Math.abs(targetZoom - 1) >= 1e-3) {
|
|
2432
|
+
documentElement.style.overflow = "auto";
|
|
2433
|
+
}
|
|
2434
|
+
documentElement.style.removeProperty("transform");
|
|
2435
|
+
documentElement.style.removeProperty("transform-origin");
|
|
2436
|
+
var startedAt = performance.now();
|
|
2437
|
+
function easeInOut(value2) {
|
|
2438
|
+
return value2 < 0.5 ? 4 * value2 * value2 * value2 : 1 - Math.pow(-2 * value2 + 2, 3) / 2;
|
|
2439
|
+
}
|
|
2440
|
+
function step(now) {
|
|
2441
|
+
var progress = Math.min(1, (now - startedAt) / ZOOM_TRANSITION_MS);
|
|
2442
|
+
var currentZoom = startZoom + (targetZoom - startZoom) * easeInOut(progress);
|
|
2443
|
+
documentElement.style.zoom = String(currentZoom);
|
|
2444
|
+
documentElement.style.removeProperty("transform");
|
|
2445
|
+
documentElement.style.removeProperty("transform-origin");
|
|
2446
|
+
if (Math.abs(targetZoom - 1) >= 1e-3) {
|
|
2447
|
+
documentElement.style.overflow = "auto";
|
|
2448
|
+
}
|
|
2449
|
+
if (progress < 1) {
|
|
2450
|
+
zoomAnimationFrame = requestAnimationFrame(step);
|
|
2451
|
+
return;
|
|
2452
|
+
}
|
|
2453
|
+
zoomAnimationFrame = 0;
|
|
2454
|
+
applyFinalZoom();
|
|
2455
|
+
}
|
|
2456
|
+
zoomAnimationFrame = requestAnimationFrame(step);
|
|
2457
|
+
}
|
|
2458
|
+
function applyHostZoomInstant(value) {
|
|
2459
|
+
var documentElement = document.documentElement;
|
|
2460
|
+
if (!documentElement) return;
|
|
2461
|
+
var zoom = Number(value);
|
|
2462
|
+
if (!Number.isFinite(zoom) || zoom <= 0) {
|
|
2463
|
+
zoom = 1;
|
|
2464
|
+
}
|
|
2465
|
+
if (Math.abs(zoom - 1) < 1e-3) {
|
|
2466
|
+
documentElement.style.removeProperty("zoom");
|
|
2467
|
+
documentElement.style.removeProperty("transform");
|
|
2468
|
+
documentElement.style.removeProperty("transform-origin");
|
|
2469
|
+
documentElement.style.removeProperty("overflow");
|
|
2470
|
+
documentElement.style.removeProperty("will-change");
|
|
2471
|
+
return;
|
|
2472
|
+
}
|
|
2473
|
+
documentElement.style.zoom = String(zoom);
|
|
2474
|
+
documentElement.style.removeProperty("transform");
|
|
2475
|
+
documentElement.style.removeProperty("transform-origin");
|
|
2476
|
+
documentElement.style.overflow = "auto";
|
|
2477
|
+
documentElement.style.removeProperty("will-change");
|
|
2478
|
+
}
|
|
2479
|
+
function rememberHostZoom(value) {
|
|
2480
|
+
try {
|
|
2481
|
+
sessionStorage.setItem("parhelia:hostZoom", String(Number(value) || 1));
|
|
2482
|
+
} catch (error) {
|
|
2483
|
+
}
|
|
2484
|
+
}
|
|
2485
|
+
function hostScrollPageKey() {
|
|
2486
|
+
return location.pathname + "|" + (getQueryValue("sc_itemid") || "");
|
|
2487
|
+
}
|
|
2488
|
+
function rememberHostScroll() {
|
|
2489
|
+
try {
|
|
2490
|
+
var x = window.scrollX || 0;
|
|
2491
|
+
var y = window.scrollY || 0;
|
|
2492
|
+
if (!x && !y) return;
|
|
2493
|
+
sessionStorage.setItem(
|
|
2494
|
+
"parhelia:hostScroll",
|
|
2495
|
+
JSON.stringify({ x, y, key: hostScrollPageKey(), t: Date.now() })
|
|
2496
|
+
);
|
|
2497
|
+
} catch (error) {
|
|
2498
|
+
}
|
|
2499
|
+
}
|
|
2500
|
+
function applyRememberedHostScroll() {
|
|
2501
|
+
try {
|
|
2502
|
+
var raw = sessionStorage.getItem("parhelia:hostScroll");
|
|
2503
|
+
if (!raw) return;
|
|
2504
|
+
sessionStorage.removeItem("parhelia:hostScroll");
|
|
2505
|
+
var saved = JSON.parse(raw);
|
|
2506
|
+
if (!saved || saved.key !== hostScrollPageKey()) return;
|
|
2507
|
+
if (typeof saved.t === "number" && Date.now() - saved.t > 15e3) return;
|
|
2508
|
+
if (!saved.x && !saved.y) return;
|
|
2509
|
+
restoreRefreshScroll(saved.x, saved.y);
|
|
2510
|
+
} catch (error) {
|
|
2511
|
+
}
|
|
2512
|
+
}
|
|
2513
|
+
function applyRememberedHostZoom() {
|
|
2514
|
+
try {
|
|
2515
|
+
var value = sessionStorage.getItem("parhelia:hostZoom");
|
|
2516
|
+
if (value) applyHostZoomInstant(value);
|
|
2517
|
+
} catch (error) {
|
|
2518
|
+
}
|
|
2519
|
+
}
|
|
2520
|
+
function getTextOffset(root, targetNode, localOffset) {
|
|
2521
|
+
var offset = 0;
|
|
2522
|
+
var walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
|
|
2523
|
+
while (walker.nextNode()) {
|
|
2524
|
+
var current = walker.currentNode;
|
|
2525
|
+
if (current === targetNode) {
|
|
2526
|
+
return offset + localOffset;
|
|
2527
|
+
}
|
|
2528
|
+
offset += current.textContent ? current.textContent.length : 0;
|
|
2529
|
+
}
|
|
2530
|
+
return localOffset;
|
|
2531
|
+
}
|
|
2532
|
+
function measureTextRangeClientRects(root, start, end) {
|
|
2533
|
+
var text = root.textContent || "";
|
|
2534
|
+
var boundedStart = Math.max(0, Math.min(Number(start) || 0, text.length));
|
|
2535
|
+
var boundedEnd = Math.max(
|
|
2536
|
+
boundedStart,
|
|
2537
|
+
Math.min(Number(end) || boundedStart, text.length)
|
|
2538
|
+
);
|
|
2539
|
+
if (boundedStart >= boundedEnd) {
|
|
2540
|
+
return measureCollapsedTextRangeClientRects(root, boundedStart);
|
|
2541
|
+
}
|
|
2542
|
+
var range = createRangeFromTextOffsets(root, boundedStart, boundedEnd);
|
|
2543
|
+
if (!range) return [];
|
|
2544
|
+
return Array.prototype.slice.call(range.getClientRects()).filter(hasVisibleRect);
|
|
2545
|
+
}
|
|
2546
|
+
function measureCollapsedTextRangeClientRects(root, offset) {
|
|
2547
|
+
var range = createRangeFromTextOffsets(root, offset, offset);
|
|
2548
|
+
if (!range) return [];
|
|
2549
|
+
var rect = range.getBoundingClientRect();
|
|
2550
|
+
if (hasVisibleRect(rect)) return [rect];
|
|
2551
|
+
var marker = document.createElement("span");
|
|
2552
|
+
marker.setAttribute("data-parhelia-caret-measure", "true");
|
|
2553
|
+
marker.style.display = "inline-block";
|
|
2554
|
+
marker.style.width = "0";
|
|
2555
|
+
marker.style.height = "1em";
|
|
2556
|
+
marker.style.overflow = "hidden";
|
|
2557
|
+
marker.textContent = "\u200B";
|
|
2558
|
+
try {
|
|
2559
|
+
range.insertNode(marker);
|
|
2560
|
+
rect = marker.getBoundingClientRect();
|
|
2561
|
+
return hasVisibleRect(rect) ? [rect] : [];
|
|
2562
|
+
} catch (error) {
|
|
2563
|
+
return [];
|
|
2564
|
+
} finally {
|
|
2565
|
+
if (marker.parentNode) {
|
|
2566
|
+
marker.parentNode.removeChild(marker);
|
|
2567
|
+
}
|
|
2568
|
+
}
|
|
2569
|
+
}
|
|
2570
|
+
function createRangeFromTextOffsets(root, start, end) {
|
|
2571
|
+
var startData = getTextNodeAtOffset(root, start, "forward");
|
|
2572
|
+
var endData = getTextNodeAtOffset(root, end, "backward");
|
|
2573
|
+
if (!startData || !endData) return null;
|
|
2574
|
+
try {
|
|
2575
|
+
var textRange = document.createRange();
|
|
2576
|
+
textRange.setStart(startData.node, startData.offset);
|
|
2577
|
+
textRange.setEnd(endData.node, endData.offset);
|
|
2578
|
+
return textRange;
|
|
2579
|
+
} catch (error) {
|
|
2580
|
+
return null;
|
|
2581
|
+
}
|
|
2582
|
+
}
|
|
2583
|
+
function getTextNodeAtOffset(root, offset, affinity) {
|
|
2584
|
+
var walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
|
|
2585
|
+
var currentNode;
|
|
2586
|
+
var boundaryNode = null;
|
|
2587
|
+
var remaining = offset;
|
|
2588
|
+
while (currentNode = walker.nextNode()) {
|
|
2589
|
+
var nodeLength = currentNode.textContent ? currentNode.textContent.length : 0;
|
|
2590
|
+
if (remaining < nodeLength) {
|
|
2591
|
+
return { node: currentNode, offset: remaining };
|
|
2592
|
+
}
|
|
2593
|
+
if (remaining === nodeLength) {
|
|
2594
|
+
if (affinity === "backward") {
|
|
2595
|
+
return { node: currentNode, offset: remaining };
|
|
2596
|
+
}
|
|
2597
|
+
boundaryNode = currentNode;
|
|
2598
|
+
remaining = 0;
|
|
2599
|
+
continue;
|
|
2600
|
+
}
|
|
2601
|
+
remaining -= nodeLength;
|
|
2602
|
+
}
|
|
2603
|
+
return boundaryNode ? {
|
|
2604
|
+
node: boundaryNode,
|
|
2605
|
+
offset: boundaryNode.textContent ? boundaryNode.textContent.length : 0
|
|
2606
|
+
} : null;
|
|
2607
|
+
}
|
|
2608
|
+
function getSelectionContext(root, startOffset, endOffset) {
|
|
2609
|
+
var fullText = root.textContent || "";
|
|
2610
|
+
var contextSize = 100;
|
|
2611
|
+
return {
|
|
2612
|
+
contextBefore: sanitizeSelectionText(
|
|
2613
|
+
fullText.slice(Math.max(0, startOffset - contextSize), startOffset)
|
|
2614
|
+
),
|
|
2615
|
+
contextAfter: sanitizeSelectionText(
|
|
2616
|
+
fullText.slice(endOffset, endOffset + contextSize)
|
|
2617
|
+
)
|
|
2618
|
+
};
|
|
2619
|
+
}
|
|
2620
|
+
function sanitizeSelectionText(text) {
|
|
2621
|
+
return String(text || "").replace(/[\u200B\u200C\u200D\uFEFF\u2060]/g, "");
|
|
2622
|
+
}
|
|
2623
|
+
function getSelectionRect(range) {
|
|
2624
|
+
var rects = Array.prototype.slice.call(range.getClientRects());
|
|
2625
|
+
var visibleRects = rects.filter(hasVisibleRect);
|
|
2626
|
+
if (visibleRects.length > 0) {
|
|
2627
|
+
return visibleRects[visibleRects.length - 1];
|
|
2628
|
+
}
|
|
2629
|
+
var rect = range.getBoundingClientRect();
|
|
2630
|
+
return hasVisibleRect(rect) ? rect : void 0;
|
|
2631
|
+
}
|
|
2632
|
+
function toNumber(value) {
|
|
2633
|
+
if (!value) return void 0;
|
|
2634
|
+
var parsed = parseInt(value, 10);
|
|
2635
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
2636
|
+
}
|
|
2637
|
+
function cssEscape(value) {
|
|
2638
|
+
if (window.CSS && typeof window.CSS.escape === "function") {
|
|
2639
|
+
return window.CSS.escape(value);
|
|
2640
|
+
}
|
|
2641
|
+
return String(value).replace(/["\\]/g, "\\$&");
|
|
2642
|
+
}
|
|
2643
|
+
function isBridgeDebugEnabled() {
|
|
2644
|
+
try {
|
|
2645
|
+
var params = new URLSearchParams(location.search);
|
|
2646
|
+
if (params.get("parhelia_bridge_debug") === "1") return true;
|
|
2647
|
+
var stored = window.localStorage.getItem("parhelia:bridge:debug");
|
|
2648
|
+
return stored === "1" || stored === "true";
|
|
2649
|
+
} catch (error) {
|
|
2650
|
+
return false;
|
|
2651
|
+
}
|
|
2652
|
+
}
|
|
2653
|
+
function logBridgeMessage(direction, message) {
|
|
2654
|
+
if (!shouldLogBridgeMessage(message)) return;
|
|
2655
|
+
var safeMessage = redactSensitiveBridgeFields(message);
|
|
2656
|
+
var name = safeMessage && typeof safeMessage === "object" && typeof safeMessage.name === "string" ? ": " + safeMessage.name : "";
|
|
2657
|
+
console.log("[Parhelia bridge message] " + direction + name, safeMessage);
|
|
2658
|
+
console.groupCollapsed("[Parhelia bridge] " + direction + name);
|
|
2659
|
+
console.log(safeMessage);
|
|
2660
|
+
console.groupEnd();
|
|
2661
|
+
}
|
|
2662
|
+
function shouldLogBridgeMessage(message) {
|
|
2663
|
+
if (!isBridgeDebugEnabled()) return false;
|
|
2664
|
+
if (!isHighFrequencyBridgeMessage(message)) return true;
|
|
2665
|
+
return isVerboseBridgeDebugEnabled();
|
|
2666
|
+
}
|
|
2667
|
+
function isHighFrequencyBridgeMessage(message) {
|
|
2668
|
+
return message && typeof message === "object" && (message.name === "scrollChanged" || message.name === "geometryUpdated" || message.name === "domUpdated" || message.name === "selectionChanged");
|
|
2669
|
+
}
|
|
2670
|
+
function isVerboseBridgeDebugEnabled() {
|
|
2671
|
+
try {
|
|
2672
|
+
var params = new URLSearchParams(location.search);
|
|
2673
|
+
if (params.get("parhelia_bridge_debug") === "verbose") return true;
|
|
2674
|
+
if (params.get("parhelia_bridge_verbose") === "1") return true;
|
|
2675
|
+
var stored = window.localStorage.getItem("parhelia:bridge:debug:verbose");
|
|
2676
|
+
return stored === "1" || stored === "true";
|
|
2677
|
+
} catch (error) {
|
|
2678
|
+
return false;
|
|
2679
|
+
}
|
|
2680
|
+
}
|
|
2681
|
+
function redactSensitiveBridgeFields(value) {
|
|
2682
|
+
if (Array.isArray(value)) {
|
|
2683
|
+
return value.map(redactSensitiveBridgeFields);
|
|
2684
|
+
}
|
|
2685
|
+
if (!value || typeof value !== "object") {
|
|
2686
|
+
return value;
|
|
2687
|
+
}
|
|
2688
|
+
var result = {};
|
|
2689
|
+
Object.keys(value).forEach(function(key) {
|
|
2690
|
+
if (/token|secret|signedContext|sessionId/i.test(key)) {
|
|
2691
|
+
result[key] = "[redacted]";
|
|
2692
|
+
} else {
|
|
2693
|
+
result[key] = redactSensitiveBridgeFields(value[key]);
|
|
2694
|
+
}
|
|
2695
|
+
});
|
|
2696
|
+
return result;
|
|
2697
|
+
}
|
|
2698
|
+
})();
|
|
2699
|
+
})();
|