@remotion/studio 4.0.468 → 4.0.470
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Studio.js +1 -1
- package/dist/api/save-render-output.js +3 -12
- package/dist/components/AudioWaveform.d.ts +1 -0
- package/dist/components/AudioWaveform.js +41 -23
- package/dist/components/CompositionSelectorItem.js +1 -1
- package/dist/components/ContextMenu.d.ts +1 -0
- package/dist/components/ContextMenu.js +8 -3
- package/dist/components/EditorContent.js +5 -4
- package/dist/components/EditorGuides/Guide.js +1 -1
- package/dist/components/ExpandedTracksProvider.js +3 -8
- package/dist/components/Menu/MenuItem.d.ts +1 -1
- package/dist/components/Menu/MenuItem.js +2 -2
- package/dist/components/Menu/MenuSubItem.js +5 -5
- package/dist/components/MenuBuildIndicator.js +0 -1
- package/dist/components/NewComposition/InputDragger.js +1 -0
- package/dist/components/Preview.js +4 -1
- package/dist/components/PreviewToolbar.js +2 -4
- package/dist/components/SelectedOutlineOverlay.d.ts +18 -0
- package/dist/components/SelectedOutlineOverlay.js +645 -0
- package/dist/components/Timeline/Timeline.js +18 -3
- package/dist/components/Timeline/TimelineDeleteKeybindings.d.ts +2 -0
- package/dist/components/Timeline/TimelineDeleteKeybindings.js +86 -0
- package/dist/components/Timeline/TimelineDragHandler.js +19 -244
- package/dist/components/Timeline/{TimelineEffectGroupRow.d.ts → TimelineEffectItem.d.ts} +1 -1
- package/dist/components/Timeline/{TimelineEffectGroupRow.js → TimelineEffectItem.js} +61 -35
- package/dist/components/Timeline/{TimelineEffectFieldRow.d.ts → TimelineEffectPropItem.d.ts} +4 -1
- package/dist/components/Timeline/{TimelineEffectFieldRow.js → TimelineEffectPropItem.js} +100 -16
- package/dist/components/Timeline/TimelineExpandArrowButton.js +5 -1
- package/dist/components/Timeline/TimelineExpandedKeyframeRow.d.ts +11 -0
- package/dist/components/Timeline/TimelineExpandedKeyframeRow.js +55 -0
- package/dist/components/Timeline/TimelineExpandedRow.d.ts +1 -0
- package/dist/components/Timeline/TimelineExpandedRow.js +29 -11
- package/dist/components/Timeline/TimelineExpandedSection.d.ts +1 -0
- package/dist/components/Timeline/TimelineExpandedSection.js +4 -4
- package/dist/components/Timeline/TimelineExpandedTrackKeyframes.d.ts +1 -1
- package/dist/components/Timeline/TimelineExpandedTrackKeyframes.js +9 -120
- package/dist/components/Timeline/TimelineFieldLabel.d.ts +6 -0
- package/dist/components/Timeline/TimelineFieldLabel.js +30 -0
- package/dist/components/Timeline/TimelineInOutDragHandler.d.ts +2 -0
- package/dist/components/Timeline/TimelineInOutDragHandler.js +324 -0
- package/dist/components/Timeline/TimelineInOutPointer.js +3 -1
- package/dist/components/Timeline/TimelineInOutPointerHandle.d.ts +1 -0
- package/dist/components/Timeline/TimelineInOutPointerHandle.js +5 -4
- package/dist/components/Timeline/TimelineItemStack.d.ts +5 -0
- package/dist/components/Timeline/TimelineItemStack.js +82 -0
- package/dist/components/Timeline/TimelineKeyframeControls.d.ts +17 -0
- package/dist/components/Timeline/TimelineKeyframeControls.js +217 -0
- package/dist/components/Timeline/TimelineKeyframeDiamond.d.ts +7 -0
- package/dist/components/Timeline/TimelineKeyframeDiamond.js +88 -0
- package/dist/components/Timeline/TimelineKeyframedValue.d.ts +8 -0
- package/dist/components/Timeline/TimelineKeyframedValue.js +36 -0
- package/dist/components/Timeline/TimelineLayerEye.js +1 -0
- package/dist/components/Timeline/TimelineList.js +3 -11
- package/dist/components/Timeline/TimelineMediaInfo.d.ts +13 -1
- package/dist/components/Timeline/TimelineMediaInfo.js +33 -80
- package/dist/components/Timeline/TimelineRowChrome.d.ts +10 -1
- package/dist/components/Timeline/TimelineRowChrome.js +64 -6
- package/dist/components/Timeline/TimelineSchemaField.js +7 -2
- package/dist/components/Timeline/TimelineSelection.d.ts +85 -0
- package/dist/components/Timeline/TimelineSelection.js +417 -0
- package/dist/components/Timeline/TimelineSequence.d.ts +2 -0
- package/dist/components/Timeline/TimelineSequence.js +18 -6
- package/dist/components/Timeline/TimelineSequenceFrame.js +1 -0
- package/dist/components/Timeline/{TimelineListItem.d.ts → TimelineSequenceItem.d.ts} +2 -2
- package/dist/components/Timeline/{TimelineListItem.js → TimelineSequenceItem.js} +148 -63
- package/dist/components/Timeline/TimelineSequenceName.d.ts +7 -0
- package/dist/components/Timeline/TimelineSequenceName.js +50 -0
- package/dist/components/Timeline/{TimelineFieldRow.d.ts → TimelineSequencePropItem.d.ts} +5 -3
- package/dist/components/Timeline/TimelineSequencePropItem.js +183 -0
- package/dist/components/Timeline/TimelineTimeIndicators.js +0 -1
- package/dist/components/Timeline/TimelineTrack.d.ts +5 -0
- package/dist/components/Timeline/TimelineTrack.js +66 -0
- package/dist/components/Timeline/TimelineTracks.js +2 -16
- package/dist/components/Timeline/TimelineTranslateField.js +14 -22
- package/dist/components/Timeline/TimelineUvCoordinateField.d.ts +11 -0
- package/dist/components/Timeline/TimelineUvCoordinateField.js +126 -0
- package/dist/components/Timeline/TimelineVideoInfo.js +11 -12
- package/dist/components/Timeline/call-add-keyframe.d.ts +23 -0
- package/dist/components/Timeline/call-add-keyframe.js +54 -0
- package/dist/components/Timeline/call-delete-keyframe.d.ts +21 -0
- package/dist/components/Timeline/call-delete-keyframe.js +50 -0
- package/dist/components/Timeline/delete-selected-keyframe.d.ts +11 -0
- package/dist/components/Timeline/delete-selected-keyframe.js +51 -0
- package/dist/components/Timeline/delete-selected-timeline-item.d.ts +17 -0
- package/dist/components/Timeline/delete-selected-timeline-item.js +183 -0
- package/dist/components/Timeline/duplicate-selected-timeline-item.d.ts +13 -0
- package/dist/components/Timeline/duplicate-selected-timeline-item.js +66 -0
- package/dist/components/Timeline/find-track-for-node-path-info.d.ts +7 -0
- package/dist/components/Timeline/find-track-for-node-path-info.js +13 -0
- package/dist/components/Timeline/get-keyframe-navigation.d.ts +9 -0
- package/dist/components/Timeline/get-keyframe-navigation.js +26 -0
- package/dist/components/Timeline/get-node-keyframes.d.ts +11 -0
- package/dist/components/Timeline/get-node-keyframes.js +23 -0
- package/dist/components/Timeline/get-timeline-keyframes.js +6 -8
- package/dist/components/Timeline/parse-keyframe-field-from-node-path.d.ts +8 -0
- package/dist/components/Timeline/parse-keyframe-field-from-node-path.js +26 -0
- package/dist/components/Timeline/save-sequence-prop.d.ts +14 -2
- package/dist/components/Timeline/save-sequence-prop.js +42 -7
- package/dist/components/Timeline/timeline-field-row-layout.d.ts +1 -0
- package/dist/components/Timeline/timeline-field-row-layout.js +11 -1
- package/dist/components/Timeline/timeline-row-layout.d.ts +1 -0
- package/dist/components/Timeline/timeline-row-layout.js +2 -1
- package/dist/components/Timeline/timeline-translate-utils.d.ts +2 -0
- package/dist/components/Timeline/timeline-translate-utils.js +20 -0
- package/dist/components/Timeline/use-expanded-track-keyframe-rows.d.ts +17 -0
- package/dist/components/Timeline/use-expanded-track-keyframe-rows.js +52 -0
- package/dist/components/Timeline/use-open-sequence-in-editor.d.ts +6 -0
- package/dist/components/Timeline/use-open-sequence-in-editor.js +31 -0
- package/dist/components/composition-menu-items.js +32 -1
- package/dist/error-overlay/remotion-overlay/OpenInEditor.js +0 -1
- package/dist/esm/{chunk-8q828zk7.js → chunk-dny42qnq.js} +10566 -6221
- package/dist/esm/internals.mjs +10566 -6221
- package/dist/esm/previewEntry.mjs +10548 -6203
- package/dist/esm/renderEntry.mjs +3 -1
- package/dist/helpers/colors.d.ts +1 -1
- package/dist/helpers/colors.js +1 -1
- package/dist/helpers/format-file-location.d.ts +9 -0
- package/dist/helpers/format-file-location.js +27 -0
- package/dist/helpers/get-box-quads-polyfill-internals.d.ts +82 -0
- package/dist/helpers/get-box-quads-polyfill-internals.js +2395 -0
- package/dist/helpers/get-box-quads-ponyfill.d.ts +10 -0
- package/dist/helpers/get-box-quads-ponyfill.js +23 -0
- package/dist/helpers/open-in-editor.d.ts +1 -1
- package/dist/helpers/open-in-editor.js +11 -26
- package/dist/helpers/timeline-layout.d.ts +8 -7
- package/dist/helpers/timeline-layout.js +9 -8
- package/dist/helpers/timeline-node-path-key.d.ts +2 -0
- package/dist/helpers/timeline-node-path-key.js +10 -0
- package/dist/helpers/use-menu-structure.js +8 -16
- package/dist/renderEntry.js +2 -2
- package/package.json +10 -10
- package/dist/components/Timeline/TimelineFieldRow.js +0 -113
- package/dist/components/Timeline/TimelineStack/index.d.ts +0 -8
- package/dist/components/Timeline/TimelineStack/index.js +0 -119
|
@@ -0,0 +1,2395 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/* eslint-disable */
|
|
3
|
+
/*
|
|
4
|
+
* Geometry helpers adapted from getBoxQuadsPolyfill
|
|
5
|
+
* https://github.com/jogibear9988/getBoxQuadsPolyfill (MIT)
|
|
6
|
+
*
|
|
7
|
+
* Used as a Remotion ponyfill only — does not patch globals.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.addPolyfill = addPolyfill;
|
|
11
|
+
exports.patchAdoptNode = patchAdoptNode;
|
|
12
|
+
exports.convertQuadFromNode = convertQuadFromNode;
|
|
13
|
+
exports.convertRectFromNode = convertRectFromNode;
|
|
14
|
+
exports.convertPointFromNode = convertPointFromNode;
|
|
15
|
+
exports.clearCache = clearCache;
|
|
16
|
+
exports.useCache = useCache;
|
|
17
|
+
exports.getBoxQuads = getBoxQuads;
|
|
18
|
+
exports.getElementSize = getElementSize;
|
|
19
|
+
exports.getResultingTransformationBetweenElementAndAllAncestors = getResultingTransformationBetweenElementAndAllAncestors;
|
|
20
|
+
exports.getElementCombinedTransform = getElementCombinedTransform;
|
|
21
|
+
//todo:
|
|
22
|
+
//transform-box (SVGs) https://developer.mozilla.org/en-US/docs/Web/CSS/transform-box
|
|
23
|
+
// =============================================================================
|
|
24
|
+
// PERFORMANCE FIXES APPLIED (14 total):
|
|
25
|
+
//
|
|
26
|
+
// FIX 1 – getElementCombinedTransform: check parent perspective inside fast-path
|
|
27
|
+
// so identity elements return immediately on non-3D pages.
|
|
28
|
+
// FIX 2 – getResultingTransformationBetweenElementAndAllAncestors: guard initial
|
|
29
|
+
// self-transform multiply with isIdentity check.
|
|
30
|
+
// FIX 3 – All translate matrix calls: skip new DOMMatrix().translateSelf(0,0)
|
|
31
|
+
// when both offsets are zero (hot loop, flat layouts).
|
|
32
|
+
// FIX 4 – getElementOffsetsInContainer/HTMLElement: defer getCachedComputedStyle
|
|
33
|
+
// until inside the `includeScroll` branch where it's actually needed.
|
|
34
|
+
// FIX 5 – getResultingTransformationBetweenElementAndAllAncestors: guard
|
|
35
|
+
// projectTo2D calls with !is2D check to skip style reads on 2D pages.
|
|
36
|
+
// FIX 6 – getResultingTransformationBetweenElementAndAllAncestors: cache the
|
|
37
|
+
// result on the early-return (ancestor found) path, not just fallthrough.
|
|
38
|
+
// FIX 7 – getBoxQuads: reuse the Range already created for the multi-fragment
|
|
39
|
+
// text check; pass clientRects/boundingRect into getElementSize to
|
|
40
|
+
// avoid creating a second Range for Text nodes.
|
|
41
|
+
// FIX 8 – getBoxQuads: split the per-point `!o` branch out of the loop into
|
|
42
|
+
// two separate loops to eliminate the branch test on every iteration.
|
|
43
|
+
// FIX 9 – getBoxQuads: hoist parseFloat calls for box offsets (margin/padding/
|
|
44
|
+
// content) so values are computed once, not 4x inside the loop.
|
|
45
|
+
// FIX 10 – getElementCombinedTransform: check hasTransform first (most common
|
|
46
|
+
// non-identity case) so cheaper checks fire before heavier ones.
|
|
47
|
+
// FIX 11 – getElementCombinedTransform: skip transform-origin wrap/unwrap when
|
|
48
|
+
// origin is (0, 0, 0), saving two DOMMatrix allocations.
|
|
49
|
+
// FIX 12 – getResultingTransformationBetweenElementAndAllAncestors: reuse
|
|
50
|
+
// getElementCombinedTransform result across loop iterations (carry
|
|
51
|
+
// parent transform forward instead of recomputing next iteration).
|
|
52
|
+
// FIX 13 – Repeated cross-realm instanceof checks: cache constructors from the
|
|
53
|
+
// node's window at function entry to avoid repeated property lookups.
|
|
54
|
+
// (Applied in getElementSize and getBoxQuads hot paths.)
|
|
55
|
+
// FIX 14 – transformPointBox: parse style values once per call, not once per
|
|
56
|
+
// property access (avoids repeated parseFloat on shared string props).
|
|
57
|
+
// =============================================================================
|
|
58
|
+
/**
|
|
59
|
+
* @param {globalThis} windowObj?
|
|
60
|
+
* @param {boolean=} force
|
|
61
|
+
*/
|
|
62
|
+
function addPolyfill(windowObj = window, force = false) {
|
|
63
|
+
var _a;
|
|
64
|
+
(_a = windowObj.__getBoxQuadsPolyfillFns) !== null && _a !== void 0 ? _a : (windowObj.__getBoxQuadsPolyfillFns = {});
|
|
65
|
+
windowObj.__getBoxQuadsPolyfillFns.getBoxQuads = getBoxQuads;
|
|
66
|
+
windowObj.__getBoxQuadsPolyfillFns.convertQuadFromNode = convertQuadFromNode;
|
|
67
|
+
windowObj.__getBoxQuadsPolyfillFns.convertRectFromNode = convertRectFromNode;
|
|
68
|
+
windowObj.__getBoxQuadsPolyfillFns.convertPointFromNode =
|
|
69
|
+
convertPointFromNode;
|
|
70
|
+
if (force || !windowObj.Node.prototype.getBoxQuads) {
|
|
71
|
+
//@ts-ignore
|
|
72
|
+
windowObj.Node.prototype.getBoxQuads = function (options) {
|
|
73
|
+
return windowObj.__getBoxQuadsPolyfillFns.getBoxQuads(this, options);
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
if (force || !windowObj.Node.prototype.convertQuadFromNode) {
|
|
77
|
+
//@ts-ignore
|
|
78
|
+
windowObj.Node.prototype.convertQuadFromNode = function (quad, from, options) {
|
|
79
|
+
return windowObj.__getBoxQuadsPolyfillFns.convertQuadFromNode(this, quad, from, options);
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
if (force || !windowObj.Node.prototype.convertRectFromNode) {
|
|
83
|
+
//@ts-ignore
|
|
84
|
+
windowObj.Node.prototype.convertRectFromNode = function (rect, from, options) {
|
|
85
|
+
return windowObj.__getBoxQuadsPolyfillFns.convertRectFromNode(this, rect, from, options);
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
if (force || !windowObj.Node.prototype.convertPointFromNode) {
|
|
89
|
+
//@ts-ignore
|
|
90
|
+
windowObj.Node.prototype.convertPointFromNode = function (point, from, options) {
|
|
91
|
+
return windowObj.__getBoxQuadsPolyfillFns.convertPointFromNode(this, point, from, options);
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* @param {globalThis} windowObj?
|
|
97
|
+
*/
|
|
98
|
+
function patchAdoptNode(windowObj = window) {
|
|
99
|
+
if (!windowObj.Node.prototype.getBoxQuads) {
|
|
100
|
+
//@ts-ignore
|
|
101
|
+
windowObj.Node.prototype.getBoxQuads = function (options) {
|
|
102
|
+
return getBoxQuads(this, options);
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
if (!windowObj.Node.prototype.convertQuadFromNode) {
|
|
106
|
+
//@ts-ignore
|
|
107
|
+
windowObj.Node.prototype.convertQuadFromNode = function (quad, from, options) {
|
|
108
|
+
return convertQuadFromNode(this, quad, from, options);
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
if (!windowObj.Node.prototype.convertRectFromNode) {
|
|
112
|
+
//@ts-ignore
|
|
113
|
+
windowObj.Node.prototype.convertRectFromNode = function (rect, from, options) {
|
|
114
|
+
return convertRectFromNode(this, rect, from, options);
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
if (!windowObj.Node.prototype.convertPointFromNode) {
|
|
118
|
+
//@ts-ignore
|
|
119
|
+
windowObj.Node.prototype.convertPointFromNode = function (point, from, options) {
|
|
120
|
+
return convertPointFromNode(this, point, from, options);
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* @param {Node} node
|
|
126
|
+
* @param {DOMQuadInit} quad
|
|
127
|
+
* @param {Element} from
|
|
128
|
+
* @param {{fromBox?: 'margin'|'border'|'padding'|'content', toBox?: 'margin'|'border'|'padding'|'content', iframes?: HTMLIFrameElement[]}=} options
|
|
129
|
+
* @returns {DOMQuad}
|
|
130
|
+
*/
|
|
131
|
+
function convertQuadFromNode(node, quad, from, options) {
|
|
132
|
+
var _a, _b;
|
|
133
|
+
const ancestor = ((_a = node.ownerDocument.defaultView) !== null && _a !== void 0 ? _a : window).document.body;
|
|
134
|
+
const m1 = getResultingTransformationBetweenElementAndAllAncestors(from, ancestor, options === null || options === void 0 ? void 0 : options.iframes, true);
|
|
135
|
+
const m2 = getResultingTransformationBetweenElementAndAllAncestors(node, ancestor, options === null || options === void 0 ? void 0 : options.iframes, true).inverse();
|
|
136
|
+
if ((options === null || options === void 0 ? void 0 : options.fromBox) && (options === null || options === void 0 ? void 0 : options.fromBox) !== 'border') {
|
|
137
|
+
const fromStyle = getCachedComputedStyle(from);
|
|
138
|
+
quad = new DOMQuad(transformPointBox(quad.p1, options.fromBox, fromStyle, -1), transformPointBox(quad.p2, options.fromBox, fromStyle, -1), transformPointBox(quad.p3, options.fromBox, fromStyle, -1), transformPointBox(quad.p4, options.fromBox, fromStyle, -1));
|
|
139
|
+
}
|
|
140
|
+
let res = new DOMQuad(m2.transformPoint(m1.transformPoint(quad.p1)), m2.transformPoint(m1.transformPoint(quad.p2)), m2.transformPoint(m1.transformPoint(quad.p3)), m2.transformPoint(m1.transformPoint(quad.p4)));
|
|
141
|
+
if ((options === null || options === void 0 ? void 0 : options.toBox) &&
|
|
142
|
+
(options === null || options === void 0 ? void 0 : options.toBox) !== 'border' &&
|
|
143
|
+
(node instanceof Element ||
|
|
144
|
+
node instanceof ((_b = node.ownerDocument.defaultView) !== null && _b !== void 0 ? _b : window).Element)) {
|
|
145
|
+
const nodeStyle = getCachedComputedStyle(node);
|
|
146
|
+
res = new DOMQuad(transformPointBox(res.p1, options.toBox, nodeStyle, -1), transformPointBox(res.p2, options.toBox, nodeStyle, -1), transformPointBox(res.p3, options.toBox, nodeStyle, -1), transformPointBox(res.p4, options.toBox, nodeStyle, -1));
|
|
147
|
+
}
|
|
148
|
+
return res;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* @param {Node} node
|
|
152
|
+
* @param {{x: number, y: number, width: number, height: number}} rect
|
|
153
|
+
* @param {Element} from
|
|
154
|
+
* @param {{fromBox?: 'margin'|'border'|'padding'|'content', toBox?: 'margin'|'border'|'padding'|'content', iframes?: HTMLIFrameElement[]}=} options
|
|
155
|
+
* @returns {DOMQuad}
|
|
156
|
+
*/
|
|
157
|
+
function convertRectFromNode(node, rect, from, options) {
|
|
158
|
+
var _a, _b;
|
|
159
|
+
const ancestor = ((_a = node.ownerDocument.defaultView) !== null && _a !== void 0 ? _a : window).document.body
|
|
160
|
+
.parentElement;
|
|
161
|
+
const m1 = getResultingTransformationBetweenElementAndAllAncestors(from, ancestor, options === null || options === void 0 ? void 0 : options.iframes, true);
|
|
162
|
+
const m2 = getResultingTransformationBetweenElementAndAllAncestors(node, ancestor, options === null || options === void 0 ? void 0 : options.iframes, true).inverse();
|
|
163
|
+
if ((options === null || options === void 0 ? void 0 : options.fromBox) && (options === null || options === void 0 ? void 0 : options.fromBox) !== 'border') {
|
|
164
|
+
const p = transformPointBox(new DOMPoint(rect.x, rect.y), options.fromBox, getCachedComputedStyle(from), 1);
|
|
165
|
+
rect = new DOMRect(p.x, p.y, rect.width, rect.height);
|
|
166
|
+
}
|
|
167
|
+
let res = new DOMQuad(m2.transformPoint(m1.transformPoint(new DOMPoint(rect.x, rect.y))), m2.transformPoint(m1.transformPoint(new DOMPoint(rect.x + rect.width, rect.y))), m2.transformPoint(m1.transformPoint(new DOMPoint(rect.x + rect.width, rect.y + rect.height))), m2.transformPoint(m1.transformPoint(new DOMPoint(rect.x, rect.y + rect.height))));
|
|
168
|
+
if ((options === null || options === void 0 ? void 0 : options.toBox) &&
|
|
169
|
+
(options === null || options === void 0 ? void 0 : options.toBox) !== 'border' &&
|
|
170
|
+
(node instanceof Element ||
|
|
171
|
+
node instanceof ((_b = node.ownerDocument.defaultView) !== null && _b !== void 0 ? _b : window).Element)) {
|
|
172
|
+
const nodeStyle = getCachedComputedStyle(node);
|
|
173
|
+
res = new DOMQuad(transformPointBox(res.p1, options.toBox, nodeStyle, -1), transformPointBox(res.p2, options.toBox, nodeStyle, -1), transformPointBox(res.p3, options.toBox, nodeStyle, -1), transformPointBox(res.p4, options.toBox, nodeStyle, -1));
|
|
174
|
+
}
|
|
175
|
+
return res;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* @param {Node} node
|
|
179
|
+
* @param {DOMPointInit} point
|
|
180
|
+
* @param {Element} from
|
|
181
|
+
* @param {{fromBox?: 'margin'|'border'|'padding'|'content', toBox?: 'margin'|'border'|'padding'|'content', iframes?: HTMLIFrameElement[]}=} options
|
|
182
|
+
* @returns {DOMPoint}
|
|
183
|
+
*/
|
|
184
|
+
function convertPointFromNode(node, point, from, options) {
|
|
185
|
+
var _a, _b;
|
|
186
|
+
const ancestor = ((_a = node.ownerDocument.defaultView) !== null && _a !== void 0 ? _a : window).document.body
|
|
187
|
+
.parentElement;
|
|
188
|
+
const m1 = getResultingTransformationBetweenElementAndAllAncestors(from, ancestor, options === null || options === void 0 ? void 0 : options.iframes, true);
|
|
189
|
+
const m2 = getResultingTransformationBetweenElementAndAllAncestors(node, ancestor, options === null || options === void 0 ? void 0 : options.iframes, true).inverse();
|
|
190
|
+
if ((options === null || options === void 0 ? void 0 : options.fromBox) && (options === null || options === void 0 ? void 0 : options.fromBox) !== 'border') {
|
|
191
|
+
point = transformPointBox(point, options.fromBox, getCachedComputedStyle(from), 1);
|
|
192
|
+
}
|
|
193
|
+
let res = m2.transformPoint(m1.transformPoint(point));
|
|
194
|
+
if ((options === null || options === void 0 ? void 0 : options.toBox) &&
|
|
195
|
+
(options === null || options === void 0 ? void 0 : options.toBox) !== 'border' &&
|
|
196
|
+
(node instanceof Element ||
|
|
197
|
+
node instanceof ((_b = node.ownerDocument.defaultView) !== null && _b !== void 0 ? _b : window).Element)) {
|
|
198
|
+
res = transformPointBox(res, options.toBox, getCachedComputedStyle(node), -1);
|
|
199
|
+
}
|
|
200
|
+
return res;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* @param {DOMPointInit} point
|
|
204
|
+
* @param {'margin'|'border'|'padding'|'content'} box
|
|
205
|
+
* @param {CSSStyleDeclaration} style
|
|
206
|
+
* @param {number} operator
|
|
207
|
+
* @returns {DOMPoint}
|
|
208
|
+
*
|
|
209
|
+
* FIX 14: Parse each style value once and reuse, rather than calling parseFloat
|
|
210
|
+
* multiple times on the same property (e.g. borderLeftWidth used twice in 'content').
|
|
211
|
+
*/
|
|
212
|
+
function transformPointBox(point, box, style, operator) {
|
|
213
|
+
if (box === 'margin') {
|
|
214
|
+
const mLeft = parseFloat(style.marginLeft);
|
|
215
|
+
const mTop = parseFloat(style.marginTop);
|
|
216
|
+
return new DOMPoint(point.x - operator * mLeft, point.y - operator * mTop);
|
|
217
|
+
}
|
|
218
|
+
else if (box === 'padding') {
|
|
219
|
+
const bLeft = parseFloat(style.borderLeftWidth);
|
|
220
|
+
const bTop = parseFloat(style.borderTopWidth);
|
|
221
|
+
return new DOMPoint(point.x + operator * bLeft, point.y + operator * bTop);
|
|
222
|
+
}
|
|
223
|
+
else if (box === 'content') {
|
|
224
|
+
const bLeft = parseFloat(style.borderLeftWidth);
|
|
225
|
+
const bTop = parseFloat(style.borderTopWidth);
|
|
226
|
+
const pLeft = parseFloat(style.paddingLeft);
|
|
227
|
+
const pTop = parseFloat(style.paddingTop);
|
|
228
|
+
return new DOMPoint(point.x + operator * (bLeft + pLeft), point.y + operator * (bTop + pTop));
|
|
229
|
+
}
|
|
230
|
+
//@ts-ignore
|
|
231
|
+
return point;
|
|
232
|
+
}
|
|
233
|
+
/** @type { WeakMap<Node, number> } */
|
|
234
|
+
let hash;
|
|
235
|
+
/** @type { Map<string, DOMQuad[]> } */
|
|
236
|
+
let boxQuadsCache;
|
|
237
|
+
/** @type { Map<string, DOMMatrix> } */
|
|
238
|
+
let transformCache;
|
|
239
|
+
/** @type { WeakMap<Node, CSSStyleDeclaration> } */
|
|
240
|
+
let computedStyleCache;
|
|
241
|
+
let hashId = 0;
|
|
242
|
+
function clearCache() {
|
|
243
|
+
boxQuadsCache.clear();
|
|
244
|
+
transformCache.clear();
|
|
245
|
+
computedStyleCache = new WeakMap();
|
|
246
|
+
}
|
|
247
|
+
function useCache() {
|
|
248
|
+
hash = new WeakMap();
|
|
249
|
+
boxQuadsCache = new Map();
|
|
250
|
+
transformCache = new Map();
|
|
251
|
+
computedStyleCache = new WeakMap();
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* @param {Element} element
|
|
255
|
+
* @returns {CSSStyleDeclaration}
|
|
256
|
+
*/
|
|
257
|
+
function getCachedComputedStyle(element) {
|
|
258
|
+
var _a, _b;
|
|
259
|
+
if (!computedStyleCache) {
|
|
260
|
+
return ((_a = element.ownerDocument.defaultView) !== null && _a !== void 0 ? _a : window).getComputedStyle(element);
|
|
261
|
+
}
|
|
262
|
+
let style = computedStyleCache.get(element);
|
|
263
|
+
if (!style) {
|
|
264
|
+
style = ((_b = element.ownerDocument.defaultView) !== null && _b !== void 0 ? _b : window).getComputedStyle(element);
|
|
265
|
+
computedStyleCache.set(element, style);
|
|
266
|
+
}
|
|
267
|
+
return style;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* @param {Node} node
|
|
271
|
+
* @returns {boolean}
|
|
272
|
+
*/
|
|
273
|
+
function isElementNode(node) {
|
|
274
|
+
return !!node && node.nodeType === Node.ELEMENT_NODE;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* @param {Node} element
|
|
278
|
+
* @returns {number}
|
|
279
|
+
*/
|
|
280
|
+
function getElementZoom(element) {
|
|
281
|
+
if (!isElementNode(element)) {
|
|
282
|
+
return 1;
|
|
283
|
+
}
|
|
284
|
+
/** @type {Element} */
|
|
285
|
+
// @ts-ignore
|
|
286
|
+
const actualElement = element;
|
|
287
|
+
const zoom = getCachedComputedStyle(actualElement).zoom;
|
|
288
|
+
if (!zoom || zoom === 'normal') {
|
|
289
|
+
return 1;
|
|
290
|
+
}
|
|
291
|
+
if (zoom.endsWith('%')) {
|
|
292
|
+
const percentage = parseFloat(zoom);
|
|
293
|
+
return Number.isFinite(percentage) && percentage > 0 ? percentage / 100 : 1;
|
|
294
|
+
}
|
|
295
|
+
const value = parseFloat(zoom);
|
|
296
|
+
return Number.isFinite(value) && value > 0 ? value : 1;
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* `zoom` scales the element's internal coordinate space while also shifting the
|
|
300
|
+
* element within its parent from the top-center anchor in the default writing-mode.
|
|
301
|
+
* For descendant coordinates we keep the scale in the matrix pipeline and apply
|
|
302
|
+
* the parent-position shift separately in the layout translation step.
|
|
303
|
+
* @param {Node} element
|
|
304
|
+
* @returns {DOMMatrix}
|
|
305
|
+
*/
|
|
306
|
+
function getElementZoomScaleTransform(element) {
|
|
307
|
+
if (!isElementNode(element)) {
|
|
308
|
+
return new DOMMatrix();
|
|
309
|
+
}
|
|
310
|
+
const zoom = getElementZoom(element);
|
|
311
|
+
if (zoom === 1) {
|
|
312
|
+
return new DOMMatrix();
|
|
313
|
+
}
|
|
314
|
+
return new DOMMatrix().scaleSelf(zoom);
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* @param {Node} element
|
|
318
|
+
* @param {HTMLIFrameElement[]=} iframes
|
|
319
|
+
* @param {boolean=} includeZoom
|
|
320
|
+
* @returns {DOMMatrix}
|
|
321
|
+
*/
|
|
322
|
+
function getElementTransformWithZoom(element, iframes, includeZoom = true) {
|
|
323
|
+
const transform = getElementCombinedTransform(element, iframes);
|
|
324
|
+
if (!includeZoom || !isElementNode(element)) {
|
|
325
|
+
return transform;
|
|
326
|
+
}
|
|
327
|
+
const zoomTransform = getElementZoomScaleTransform(element);
|
|
328
|
+
if (zoomTransform.isIdentity) {
|
|
329
|
+
return transform;
|
|
330
|
+
}
|
|
331
|
+
// `zoom` scales transform translations as well as the element's local axes,
|
|
332
|
+
// so it needs to wrap the transform matrix instead of being appended to it.
|
|
333
|
+
return zoomTransform.multiply(transform);
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* @param {Node} node
|
|
337
|
+
* @param {{box?: 'margin'|'border'|'padding'|'content', relativeTo?: Element, iframes?: HTMLIFrameElement[]}=} options
|
|
338
|
+
* @returns {DOMQuad[]}
|
|
339
|
+
*/
|
|
340
|
+
function getBoxQuads(node, options) {
|
|
341
|
+
var _a, _b, _c, _d, _e, _f;
|
|
342
|
+
const defaultRelativeTo = (_a = node.ownerDocument.documentElement) !== null && _a !== void 0 ? _a : node.ownerDocument.body;
|
|
343
|
+
const relativeTo = (_b = options === null || options === void 0 ? void 0 : options.relativeTo) !== null && _b !== void 0 ? _b : defaultRelativeTo;
|
|
344
|
+
const viewportRoot = (_c = node.ownerDocument.documentElement) !== null && _c !== void 0 ? _c : node.ownerDocument.body;
|
|
345
|
+
const resolveFixedThroughViewport = relativeTo !== viewportRoot &&
|
|
346
|
+
shouldResolveFixedPositionThroughViewport(node, relativeTo, options === null || options === void 0 ? void 0 : options.iframes);
|
|
347
|
+
let key;
|
|
348
|
+
if (boxQuadsCache) {
|
|
349
|
+
let i1 = hash.get(node);
|
|
350
|
+
if (i1 === undefined)
|
|
351
|
+
hash.set(node, (i1 = hashId++));
|
|
352
|
+
let i2 = hash.get(relativeTo);
|
|
353
|
+
if (i2 === undefined)
|
|
354
|
+
hash.set(relativeTo, (i2 = hashId++));
|
|
355
|
+
key = i1 + '_' + i2 + '_' + ((_d = options === null || options === void 0 ? void 0 : options.box) !== null && _d !== void 0 ? _d : 'border');
|
|
356
|
+
const q = boxQuadsCache.get(key);
|
|
357
|
+
if (q)
|
|
358
|
+
return q;
|
|
359
|
+
}
|
|
360
|
+
/** @type {DOMMatrix} */
|
|
361
|
+
let originalElementAndAllParentsMultipliedMatrix = getResultingTransformationBetweenElementAndAllAncestors(node, resolveFixedThroughViewport ? viewportRoot : relativeTo, options === null || options === void 0 ? void 0 : options.iframes);
|
|
362
|
+
// FIX 13: Cache cross-realm constructors once per call.
|
|
363
|
+
const win = (_e = node.ownerDocument.defaultView) !== null && _e !== void 0 ? _e : window;
|
|
364
|
+
const _Text = win.Text;
|
|
365
|
+
const _SVGGraphicsElement = win.SVGGraphicsElement;
|
|
366
|
+
const _SVGSVGElement = win.SVGSVGElement;
|
|
367
|
+
// For text nodes, check for multiple fragments (multi-column layout, line-wrapping).
|
|
368
|
+
// getClientRects() returns one rect per line-box fragment; getBoundingClientRect()
|
|
369
|
+
// only returns the union AABB, so without this we'd always get one quad.
|
|
370
|
+
if (node instanceof Text || node instanceof _Text) {
|
|
371
|
+
const canUseViewportTextRects = originalElementAndAllParentsMultipliedMatrix.is2D &&
|
|
372
|
+
Math.abs(originalElementAndAllParentsMultipliedMatrix.b) < 1e-10 &&
|
|
373
|
+
Math.abs(originalElementAndAllParentsMultipliedMatrix.c) < 1e-10;
|
|
374
|
+
// FIX 7: Create the Range once here and reuse it for both the multi-fragment
|
|
375
|
+
// check and the single-fragment size fallback, avoiding a second Range
|
|
376
|
+
// allocation inside getElementSize for Text nodes.
|
|
377
|
+
const range = node.ownerDocument.createRange();
|
|
378
|
+
range.selectNodeContents(node);
|
|
379
|
+
const clientRects = range.getClientRects();
|
|
380
|
+
const viewportRoot = (_f = node.ownerDocument.documentElement) !== null && _f !== void 0 ? _f : node.ownerDocument.body;
|
|
381
|
+
const viewportRootRect = viewportRoot === null || viewportRoot === void 0 ? void 0 : viewportRoot.getBoundingClientRect();
|
|
382
|
+
const convertViewportRectToRelativeQuad = (rect) => {
|
|
383
|
+
var _a, _b;
|
|
384
|
+
if (relativeTo === viewportRoot) {
|
|
385
|
+
return new DOMQuad(new DOMPoint(rect.x, rect.y), new DOMPoint(rect.x + rect.width, rect.y), new DOMPoint(rect.x + rect.width, rect.y + rect.height), new DOMPoint(rect.x, rect.y + rect.height));
|
|
386
|
+
}
|
|
387
|
+
const rectInViewportRoot = new DOMRect(rect.x - ((_a = viewportRootRect === null || viewportRootRect === void 0 ? void 0 : viewportRootRect.x) !== null && _a !== void 0 ? _a : 0), rect.y - ((_b = viewportRootRect === null || viewportRootRect === void 0 ? void 0 : viewportRootRect.y) !== null && _b !== void 0 ? _b : 0), rect.width, rect.height);
|
|
388
|
+
return convertRectFromNode(relativeTo, rectInViewportRoot, viewportRoot, {
|
|
389
|
+
iframes: options === null || options === void 0 ? void 0 : options.iframes,
|
|
390
|
+
});
|
|
391
|
+
};
|
|
392
|
+
if (clientRects.length > 1) {
|
|
393
|
+
if (canUseViewportTextRects) {
|
|
394
|
+
const quads = [];
|
|
395
|
+
for (const cr of clientRects) {
|
|
396
|
+
if (cr.width < 1 && cr.height < 1)
|
|
397
|
+
continue;
|
|
398
|
+
quads.push(convertViewportRectToRelativeQuad(cr));
|
|
399
|
+
}
|
|
400
|
+
if (quads.length > 0) {
|
|
401
|
+
if (boxQuadsCache)
|
|
402
|
+
boxQuadsCache.set(key, quads);
|
|
403
|
+
return quads;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
// Work via the parent element so rotation is handled correctly.
|
|
407
|
+
// Each fragment's viewport rect (from getClientRects) is an AABB;
|
|
408
|
+
// its center equals the actual geometric center regardless of rotation.
|
|
409
|
+
// We convert that center to parent-local space, recover the fragment's
|
|
410
|
+
// local dimensions via the 2x2 AABB system, then apply the parent's
|
|
411
|
+
// accumulated matrix to build proper (rotated) quads in relativeTo-space.
|
|
412
|
+
const parent = getParentElementIncludingSlots(node, options === null || options === void 0 ? void 0 : options.iframes);
|
|
413
|
+
const M_parent = getResultingTransformationBetweenElementAndAllAncestors(parent, resolveFixedThroughViewport ? viewportRoot : relativeTo, options === null || options === void 0 ? void 0 : options.iframes);
|
|
414
|
+
const parentCss = getElementCombinedTransform(parent, options === null || options === void 0 ? void 0 : options.iframes);
|
|
415
|
+
const pr = parent.getBoundingClientRect();
|
|
416
|
+
const pa = parentCss.a, pb = parentCss.b, pc = parentCss.c, pd = parentCss.d;
|
|
417
|
+
// AABB center of the transformed parent equals its geometric center.
|
|
418
|
+
// geometric_center_screen = screen(0,0) + L * (pw/2, ph/2)
|
|
419
|
+
// => screen(0,0) = AABB_center - L * (pw/2, ph/2)
|
|
420
|
+
//@ts-ignore
|
|
421
|
+
const pw = parent.offsetWidth;
|
|
422
|
+
//@ts-ignore
|
|
423
|
+
const ph = parent.offsetHeight;
|
|
424
|
+
const parentOriginX = pr.x + pr.width / 2 - ((pa * pw) / 2 + (pc * ph) / 2);
|
|
425
|
+
const parentOriginY = pr.y + pr.height / 2 - ((pb * pw) / 2 + (pd * ph) / 2);
|
|
426
|
+
const linearDet = pa * pd - pb * pc;
|
|
427
|
+
const absA = Math.abs(pa), absB = Math.abs(pb);
|
|
428
|
+
const absDet = absA * absA - absB * absB;
|
|
429
|
+
const quads = [];
|
|
430
|
+
for (const cr of clientRects) {
|
|
431
|
+
if (cr.width < 1 && cr.height < 1)
|
|
432
|
+
continue;
|
|
433
|
+
// Fragment AABB center -> parent-local center via inverse CSS transform
|
|
434
|
+
const dx = cr.x + cr.width / 2 - parentOriginX;
|
|
435
|
+
const dy = cr.y + cr.height / 2 - parentOriginY;
|
|
436
|
+
let lcx, lcy;
|
|
437
|
+
if (Math.abs(linearDet) > 1e-10) {
|
|
438
|
+
lcx = (pd * dx - pc * dy) / linearDet;
|
|
439
|
+
lcy = (pa * dy - pb * dx) / linearDet;
|
|
440
|
+
}
|
|
441
|
+
else {
|
|
442
|
+
lcx = dx;
|
|
443
|
+
lcy = dy;
|
|
444
|
+
}
|
|
445
|
+
// Fragment dimensions in parent-local via 2x2 AABB system
|
|
446
|
+
let tw, th;
|
|
447
|
+
if (Math.abs(absDet) > 1e-6) {
|
|
448
|
+
tw = Math.max(0, (absA * cr.width - absB * cr.height) / absDet);
|
|
449
|
+
th = Math.max(0, (absA * cr.height - absB * cr.width) / absDet);
|
|
450
|
+
}
|
|
451
|
+
else {
|
|
452
|
+
// Singular (~45 deg): use CSS line-height as th
|
|
453
|
+
const cs = getCachedComputedStyle(parent);
|
|
454
|
+
th = Math.max(0, parseFloat(cs.lineHeight) || parseFloat(cs.fontSize) * 1.2 || 16);
|
|
455
|
+
const denom = Math.max(absA, absB);
|
|
456
|
+
tw =
|
|
457
|
+
denom > 1e-6
|
|
458
|
+
? Math.max(0, (cr.width - th * absB) / denom)
|
|
459
|
+
: cr.width;
|
|
460
|
+
}
|
|
461
|
+
// Fragment top-left in parent-local, then transform all 4 corners via M_parent
|
|
462
|
+
const lx = lcx - tw / 2, ly = lcy - th / 2;
|
|
463
|
+
const quad = new DOMQuad(M_parent.transformPoint(new DOMPoint(lx, ly)), M_parent.transformPoint(new DOMPoint(lx + tw, ly)), M_parent.transformPoint(new DOMPoint(lx + tw, ly + th)), M_parent.transformPoint(new DOMPoint(lx, ly + th)));
|
|
464
|
+
quads.push(finishQuadRelativeToTarget(quad, node, relativeTo, options === null || options === void 0 ? void 0 : options.iframes, resolveFixedThroughViewport, options === null || options === void 0 ? void 0 : options.box));
|
|
465
|
+
}
|
|
466
|
+
if (quads.length > 0) {
|
|
467
|
+
if (boxQuadsCache)
|
|
468
|
+
boxQuadsCache.set(key, quads);
|
|
469
|
+
return quads;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
// FIX 7 (continued): Single-fragment text — reuse the already-fetched
|
|
473
|
+
// bounding rect so getElementSize doesn't create a second Range.
|
|
474
|
+
const textBoundingRect = range.getBoundingClientRect();
|
|
475
|
+
if (canUseViewportTextRects) {
|
|
476
|
+
const tQuad = [convertViewportRectToRelativeQuad(textBoundingRect)];
|
|
477
|
+
if (boxQuadsCache)
|
|
478
|
+
boxQuadsCache.set(key, tQuad);
|
|
479
|
+
return tQuad;
|
|
480
|
+
}
|
|
481
|
+
const { width: tw, height: th } = _getTextNodeSize(originalElementAndAllParentsMultipliedMatrix, textBoundingRect, node);
|
|
482
|
+
const is2Dt = originalElementAndAllParentsMultipliedMatrix.is2D;
|
|
483
|
+
const tCorners = [
|
|
484
|
+
new DOMPoint(0, 0),
|
|
485
|
+
new DOMPoint(tw, 0),
|
|
486
|
+
new DOMPoint(tw, th),
|
|
487
|
+
new DOMPoint(0, th),
|
|
488
|
+
];
|
|
489
|
+
/** @type {[DOMPoint,DOMPoint,DOMPoint,DOMPoint]} */
|
|
490
|
+
//@ts-ignore
|
|
491
|
+
const tPoints = Array(4);
|
|
492
|
+
if (is2Dt) {
|
|
493
|
+
for (let i = 0; i < 4; i++)
|
|
494
|
+
tPoints[i] = tCorners[i].matrixTransform(originalElementAndAllParentsMultipliedMatrix);
|
|
495
|
+
}
|
|
496
|
+
else {
|
|
497
|
+
for (let i = 0; i < 4; i++) {
|
|
498
|
+
tPoints[i] = projectPoint(tCorners[i], originalElementAndAllParentsMultipliedMatrix).matrixTransform(originalElementAndAllParentsMultipliedMatrix);
|
|
499
|
+
tPoints[i] = as2DPoint(tPoints[i]);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
const tQuad = [
|
|
503
|
+
finishQuadRelativeToTarget(new DOMQuad(tPoints[0], tPoints[1], tPoints[2], tPoints[3]), node, relativeTo, options === null || options === void 0 ? void 0 : options.iframes, resolveFixedThroughViewport, options === null || options === void 0 ? void 0 : options.box),
|
|
504
|
+
];
|
|
505
|
+
if (boxQuadsCache)
|
|
506
|
+
boxQuadsCache.set(key, tQuad);
|
|
507
|
+
return tQuad;
|
|
508
|
+
}
|
|
509
|
+
if ((node instanceof SVGGraphicsElement ||
|
|
510
|
+
node instanceof _SVGGraphicsElement) &&
|
|
511
|
+
!(node instanceof SVGSVGElement || node instanceof _SVGSVGElement)) {
|
|
512
|
+
const bbox = node.getBBox();
|
|
513
|
+
const visualBox = getSvgVisualBox(node, bbox);
|
|
514
|
+
const x0 = visualBox.x - bbox.x;
|
|
515
|
+
const y0 = visualBox.y - bbox.y;
|
|
516
|
+
const x1 = x0 + visualBox.width;
|
|
517
|
+
const y1 = y0 + visualBox.height;
|
|
518
|
+
const screenPts = [
|
|
519
|
+
new DOMPoint(visualBox.x, visualBox.y),
|
|
520
|
+
new DOMPoint(visualBox.x + visualBox.width, visualBox.y),
|
|
521
|
+
new DOMPoint(visualBox.x + visualBox.width, visualBox.y + visualBox.height),
|
|
522
|
+
new DOMPoint(visualBox.x, visualBox.y + visualBox.height),
|
|
523
|
+
];
|
|
524
|
+
const pts = [
|
|
525
|
+
new DOMPoint(x0, y0),
|
|
526
|
+
new DOMPoint(x1, y0),
|
|
527
|
+
new DOMPoint(x1, y1),
|
|
528
|
+
new DOMPoint(x0, y1),
|
|
529
|
+
];
|
|
530
|
+
const screenCtm = !hasTransformedHtmlAncestor(node, relativeTo, options === null || options === void 0 ? void 0 : options.iframes)
|
|
531
|
+
? node.getScreenCTM()
|
|
532
|
+
: null;
|
|
533
|
+
if (screenCtm) {
|
|
534
|
+
const screenQuad = new DOMQuad(screenPts[0].matrixTransform(screenCtm), screenPts[1].matrixTransform(screenCtm), screenPts[2].matrixTransform(screenCtm), screenPts[3].matrixTransform(screenCtm));
|
|
535
|
+
const svgQuad = [
|
|
536
|
+
convertViewportQuadToRelativeNode(screenQuad, node, relativeTo, options === null || options === void 0 ? void 0 : options.iframes),
|
|
537
|
+
];
|
|
538
|
+
if (boxQuadsCache)
|
|
539
|
+
boxQuadsCache.set(key, svgQuad);
|
|
540
|
+
return svgQuad;
|
|
541
|
+
}
|
|
542
|
+
/** @type {[DOMPoint,DOMPoint,DOMPoint,DOMPoint]} */
|
|
543
|
+
//@ts-ignore
|
|
544
|
+
const points = Array(4);
|
|
545
|
+
if (originalElementAndAllParentsMultipliedMatrix.is2D) {
|
|
546
|
+
for (let i = 0; i < 4; i++)
|
|
547
|
+
points[i] = pts[i].matrixTransform(originalElementAndAllParentsMultipliedMatrix);
|
|
548
|
+
}
|
|
549
|
+
else {
|
|
550
|
+
for (let i = 0; i < 4; i++) {
|
|
551
|
+
points[i] = projectPoint(pts[i], originalElementAndAllParentsMultipliedMatrix).matrixTransform(originalElementAndAllParentsMultipliedMatrix);
|
|
552
|
+
points[i] = as2DPoint(points[i]);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
const svgQuad = [
|
|
556
|
+
finishQuadRelativeToTarget(new DOMQuad(points[0], points[1], points[2], points[3]), node, relativeTo, options === null || options === void 0 ? void 0 : options.iframes, resolveFixedThroughViewport, options === null || options === void 0 ? void 0 : options.box),
|
|
557
|
+
];
|
|
558
|
+
if (boxQuadsCache)
|
|
559
|
+
boxQuadsCache.set(key, svgQuad);
|
|
560
|
+
return svgQuad;
|
|
561
|
+
}
|
|
562
|
+
if (node instanceof win.Element &&
|
|
563
|
+
relativeTo === node.ownerDocument.documentElement &&
|
|
564
|
+
(!(options === null || options === void 0 ? void 0 : options.box) || options.box === 'border') &&
|
|
565
|
+
originalElementAndAllParentsMultipliedMatrix.is2D &&
|
|
566
|
+
Math.abs(originalElementAndAllParentsMultipliedMatrix.b) < 1e-10 &&
|
|
567
|
+
Math.abs(originalElementAndAllParentsMultipliedMatrix.c) < 1e-10) {
|
|
568
|
+
const rect = node.getBoundingClientRect();
|
|
569
|
+
const viewportQuad = [
|
|
570
|
+
new DOMQuad(new DOMPoint(rect.x, rect.y), new DOMPoint(rect.x + rect.width, rect.y), new DOMPoint(rect.x + rect.width, rect.y + rect.height), new DOMPoint(rect.x, rect.y + rect.height)),
|
|
571
|
+
];
|
|
572
|
+
if (boxQuadsCache)
|
|
573
|
+
boxQuadsCache.set(key, viewportQuad);
|
|
574
|
+
return viewportQuad;
|
|
575
|
+
}
|
|
576
|
+
let { width, height } = getElementSize(node, originalElementAndAllParentsMultipliedMatrix);
|
|
577
|
+
// FIX 13: cache cross-realm Element constructor.
|
|
578
|
+
const _Element = win.Element;
|
|
579
|
+
const is2D = originalElementAndAllParentsMultipliedMatrix.is2D;
|
|
580
|
+
// FIX 8 + FIX 9: Split the `!o` branch out of the point loop into two
|
|
581
|
+
// separate code paths. In the box-offset path, parse style values once
|
|
582
|
+
// (FIX 9) and use them directly, eliminating 4x redundant parseFloat calls.
|
|
583
|
+
if (node instanceof Element || node instanceof _Element) {
|
|
584
|
+
const box = options === null || options === void 0 ? void 0 : options.box;
|
|
585
|
+
if (box === 'margin' || box === 'padding' || box === 'content') {
|
|
586
|
+
const cs = getCachedComputedStyle(node);
|
|
587
|
+
let x0, y0, x1, y1, x2, y2, x3, y3;
|
|
588
|
+
if (box === 'margin') {
|
|
589
|
+
const mL = parseFloat(cs.marginLeft);
|
|
590
|
+
const mT = parseFloat(cs.marginTop);
|
|
591
|
+
const mR = parseFloat(cs.marginRight);
|
|
592
|
+
const mB = parseFloat(cs.marginBottom);
|
|
593
|
+
x0 = -mL;
|
|
594
|
+
y0 = -mT;
|
|
595
|
+
x1 = width + mR;
|
|
596
|
+
y1 = -mT;
|
|
597
|
+
x2 = width + mR;
|
|
598
|
+
y2 = height + mB;
|
|
599
|
+
x3 = -mL;
|
|
600
|
+
y3 = height + mB;
|
|
601
|
+
}
|
|
602
|
+
else if (box === 'padding') {
|
|
603
|
+
const bL = parseFloat(cs.borderLeftWidth);
|
|
604
|
+
const bT = parseFloat(cs.borderTopWidth);
|
|
605
|
+
const bR = parseFloat(cs.borderRightWidth);
|
|
606
|
+
const bB = parseFloat(cs.borderBottomWidth);
|
|
607
|
+
x0 = bL;
|
|
608
|
+
y0 = bT;
|
|
609
|
+
x1 = width - bR;
|
|
610
|
+
y1 = bT;
|
|
611
|
+
x2 = width - bR;
|
|
612
|
+
y2 = height - bB;
|
|
613
|
+
x3 = bL;
|
|
614
|
+
y3 = height - bB;
|
|
615
|
+
}
|
|
616
|
+
else {
|
|
617
|
+
// content
|
|
618
|
+
const bL = parseFloat(cs.borderLeftWidth);
|
|
619
|
+
const bT = parseFloat(cs.borderTopWidth);
|
|
620
|
+
const bR = parseFloat(cs.borderRightWidth);
|
|
621
|
+
const bB = parseFloat(cs.borderBottomWidth);
|
|
622
|
+
const pL = parseFloat(cs.paddingLeft);
|
|
623
|
+
const pT = parseFloat(cs.paddingTop);
|
|
624
|
+
const pR = parseFloat(cs.paddingRight);
|
|
625
|
+
const pB = parseFloat(cs.paddingBottom);
|
|
626
|
+
x0 = bL + pL;
|
|
627
|
+
y0 = bT + pT;
|
|
628
|
+
x1 = width - bR - pR;
|
|
629
|
+
y1 = bT + pT;
|
|
630
|
+
x2 = width - bR - pR;
|
|
631
|
+
y2 = height - bB - pB;
|
|
632
|
+
x3 = bL + pL;
|
|
633
|
+
y3 = height - bB - pB;
|
|
634
|
+
}
|
|
635
|
+
const pts = [
|
|
636
|
+
new DOMPoint(x0, y0),
|
|
637
|
+
new DOMPoint(x1, y1),
|
|
638
|
+
new DOMPoint(x2, y2),
|
|
639
|
+
new DOMPoint(x3, y3),
|
|
640
|
+
];
|
|
641
|
+
/** @type {[DOMPoint,DOMPoint,DOMPoint,DOMPoint]} */
|
|
642
|
+
//@ts-ignore
|
|
643
|
+
const points = Array(4);
|
|
644
|
+
if (is2D) {
|
|
645
|
+
for (let i = 0; i < 4; i++)
|
|
646
|
+
points[i] = pts[i].matrixTransform(originalElementAndAllParentsMultipliedMatrix);
|
|
647
|
+
}
|
|
648
|
+
else {
|
|
649
|
+
for (let i = 0; i < 4; i++) {
|
|
650
|
+
points[i] = projectPoint(pts[i], originalElementAndAllParentsMultipliedMatrix).matrixTransform(originalElementAndAllParentsMultipliedMatrix);
|
|
651
|
+
points[i] = as2DPoint(points[i]);
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
const quad = [
|
|
655
|
+
finishQuadRelativeToTarget(new DOMQuad(points[0], points[1], points[2], points[3]), node, relativeTo, options === null || options === void 0 ? void 0 : options.iframes, resolveFixedThroughViewport, options === null || options === void 0 ? void 0 : options.box),
|
|
656
|
+
];
|
|
657
|
+
if (boxQuadsCache)
|
|
658
|
+
boxQuadsCache.set(key, quad);
|
|
659
|
+
return quad;
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
// FIX 8: No-offset path — plain loop, no `!o` branch test per iteration.
|
|
663
|
+
const corners = [
|
|
664
|
+
new DOMPoint(0, 0),
|
|
665
|
+
new DOMPoint(width, 0),
|
|
666
|
+
new DOMPoint(width, height),
|
|
667
|
+
new DOMPoint(0, height),
|
|
668
|
+
];
|
|
669
|
+
/** @type {[DOMPoint,DOMPoint,DOMPoint,DOMPoint]} */
|
|
670
|
+
//@ts-ignore
|
|
671
|
+
const points = Array(4);
|
|
672
|
+
if (is2D) {
|
|
673
|
+
for (let i = 0; i < 4; i++)
|
|
674
|
+
points[i] = corners[i].matrixTransform(originalElementAndAllParentsMultipliedMatrix);
|
|
675
|
+
}
|
|
676
|
+
else {
|
|
677
|
+
for (let i = 0; i < 4; i++) {
|
|
678
|
+
points[i] = projectPoint(corners[i], originalElementAndAllParentsMultipliedMatrix).matrixTransform(originalElementAndAllParentsMultipliedMatrix);
|
|
679
|
+
points[i] = as2DPoint(points[i]);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
const quad = [
|
|
683
|
+
finishQuadRelativeToTarget(new DOMQuad(points[0], points[1], points[2], points[3]), node, relativeTo, options === null || options === void 0 ? void 0 : options.iframes, resolveFixedThroughViewport, options === null || options === void 0 ? void 0 : options.box),
|
|
684
|
+
];
|
|
685
|
+
if (boxQuadsCache)
|
|
686
|
+
boxQuadsCache.set(key, quad);
|
|
687
|
+
return quad;
|
|
688
|
+
}
|
|
689
|
+
function finishQuadRelativeToTarget(quad, node, relativeTo, iframes, resolveFixedThroughViewport, box) {
|
|
690
|
+
if (resolveFixedThroughViewport) {
|
|
691
|
+
quad = alignViewportBorderQuadToRenderedBox(quad, node, box);
|
|
692
|
+
return convertViewportQuadToRelativeNode(quad, node, relativeTo, iframes);
|
|
693
|
+
}
|
|
694
|
+
return toViewportRelativeDocumentElementQuad(quad, node, relativeTo, iframes);
|
|
695
|
+
}
|
|
696
|
+
function alignViewportBorderQuadToRenderedBox(quad, node, box) {
|
|
697
|
+
var _a;
|
|
698
|
+
if (box && box !== 'border') {
|
|
699
|
+
return quad;
|
|
700
|
+
}
|
|
701
|
+
const win = (_a = node.ownerDocument.defaultView) !== null && _a !== void 0 ? _a : window;
|
|
702
|
+
if (!(node instanceof win.Element)) {
|
|
703
|
+
return quad;
|
|
704
|
+
}
|
|
705
|
+
const rect = node.getBoundingClientRect();
|
|
706
|
+
const left = Math.min(quad.p1.x, quad.p2.x, quad.p3.x, quad.p4.x);
|
|
707
|
+
const top = Math.min(quad.p1.y, quad.p2.y, quad.p3.y, quad.p4.y);
|
|
708
|
+
const dx = rect.left - left;
|
|
709
|
+
const dy = rect.top - top;
|
|
710
|
+
if (Math.abs(dx) < 1e-10 && Math.abs(dy) < 1e-10) {
|
|
711
|
+
return quad;
|
|
712
|
+
}
|
|
713
|
+
const translate = (point) => new DOMPoint(point.x + dx, point.y + dy, point.z, point.w);
|
|
714
|
+
return new DOMQuad(translate(quad.p1), translate(quad.p2), translate(quad.p3), translate(quad.p4));
|
|
715
|
+
}
|
|
716
|
+
function convertViewportQuadToRelativeNode(quad, node, relativeTo, iframes) {
|
|
717
|
+
var _a, _b;
|
|
718
|
+
const viewportRoot = (_a = node.ownerDocument.documentElement) !== null && _a !== void 0 ? _a : node.ownerDocument.body;
|
|
719
|
+
if (relativeTo === viewportRoot) {
|
|
720
|
+
return quad;
|
|
721
|
+
}
|
|
722
|
+
const win = (_b = node.ownerDocument.defaultView) !== null && _b !== void 0 ? _b : window;
|
|
723
|
+
if (relativeTo instanceof win.SVGElement) {
|
|
724
|
+
const relativeScreenCtm = relativeTo.getScreenCTM();
|
|
725
|
+
if (relativeScreenCtm) {
|
|
726
|
+
const inverse = relativeScreenCtm.inverse();
|
|
727
|
+
return new DOMQuad(quad.p1.matrixTransform(inverse), quad.p2.matrixTransform(inverse), quad.p3.matrixTransform(inverse), quad.p4.matrixTransform(inverse));
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
if (relativeTo.ownerDocument === node.ownerDocument &&
|
|
731
|
+
relativeTo instanceof win.HTMLElement) {
|
|
732
|
+
const htmlQuad = convertViewportQuadToHtmlElement(quad, relativeTo, iframes);
|
|
733
|
+
if (htmlQuad) {
|
|
734
|
+
return htmlQuad;
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
if (relativeTo === node.ownerDocument.body) {
|
|
738
|
+
const relativeRect = relativeTo.getBoundingClientRect();
|
|
739
|
+
const scrollLeft = relativeTo.scrollLeft || 0;
|
|
740
|
+
const scrollTop = relativeTo.scrollTop || 0;
|
|
741
|
+
return new DOMQuad(new DOMPoint(quad.p1.x - relativeRect.x + scrollLeft, quad.p1.y - relativeRect.y + scrollTop), new DOMPoint(quad.p2.x - relativeRect.x + scrollLeft, quad.p2.y - relativeRect.y + scrollTop), new DOMPoint(quad.p3.x - relativeRect.x + scrollLeft, quad.p3.y - relativeRect.y + scrollTop), new DOMPoint(quad.p4.x - relativeRect.x + scrollLeft, quad.p4.y - relativeRect.y + scrollTop));
|
|
742
|
+
}
|
|
743
|
+
return convertQuadFromNode(relativeTo, quad, viewportRoot, { iframes });
|
|
744
|
+
}
|
|
745
|
+
function convertViewportQuadToHtmlElement(quad, relativeTo, iframes) {
|
|
746
|
+
const scale = getPositiveAxisAlignedViewportScale(relativeTo, iframes);
|
|
747
|
+
if (!scale) {
|
|
748
|
+
return null;
|
|
749
|
+
}
|
|
750
|
+
const relativeRect = relativeTo.getBoundingClientRect();
|
|
751
|
+
const scaleX = scale.x;
|
|
752
|
+
const scaleY = scale.y;
|
|
753
|
+
if (!Number.isFinite(scaleX) ||
|
|
754
|
+
!Number.isFinite(scaleY) ||
|
|
755
|
+
Math.abs(scaleX) < 1e-10 ||
|
|
756
|
+
Math.abs(scaleY) < 1e-10) {
|
|
757
|
+
return null;
|
|
758
|
+
}
|
|
759
|
+
const scrollLeft = relativeTo.scrollLeft || 0;
|
|
760
|
+
const scrollTop = relativeTo.scrollTop || 0;
|
|
761
|
+
const convertPoint = (point) => new DOMPoint((point.x - relativeRect.x) / scaleX + scrollLeft, (point.y - relativeRect.y) / scaleY + scrollTop);
|
|
762
|
+
return new DOMQuad(convertPoint(quad.p1), convertPoint(quad.p2), convertPoint(quad.p3), convertPoint(quad.p4));
|
|
763
|
+
}
|
|
764
|
+
function getPositiveAxisAlignedViewportScale(element, iframes) {
|
|
765
|
+
var _a;
|
|
766
|
+
const win = (_a = element.ownerDocument.defaultView) !== null && _a !== void 0 ? _a : window;
|
|
767
|
+
const scale = { x: 1, y: 1 };
|
|
768
|
+
let current = element;
|
|
769
|
+
while (current && current !== element.ownerDocument.documentElement) {
|
|
770
|
+
if (current instanceof win.Element) {
|
|
771
|
+
const style = getCachedComputedStyle(current);
|
|
772
|
+
const transformScale = getPositiveAxisAlignedTransformScale(style);
|
|
773
|
+
if (!transformScale) {
|
|
774
|
+
return null;
|
|
775
|
+
}
|
|
776
|
+
const zoom = getElementZoom(current);
|
|
777
|
+
scale.x *= transformScale.x * zoom;
|
|
778
|
+
scale.y *= transformScale.y * zoom;
|
|
779
|
+
}
|
|
780
|
+
current = getParentElementIncludingSlots(current, iframes);
|
|
781
|
+
}
|
|
782
|
+
return scale;
|
|
783
|
+
}
|
|
784
|
+
function getPositiveAxisAlignedTransformScale(style) {
|
|
785
|
+
if (style.perspective && style.perspective !== 'none') {
|
|
786
|
+
return null;
|
|
787
|
+
}
|
|
788
|
+
if (style.rotate &&
|
|
789
|
+
style.rotate !== 'none' &&
|
|
790
|
+
!isZeroAngleValue(style.rotate)) {
|
|
791
|
+
return null;
|
|
792
|
+
}
|
|
793
|
+
const individualScale = parsePositiveScaleValue(style.scale);
|
|
794
|
+
if (!individualScale) {
|
|
795
|
+
return null;
|
|
796
|
+
}
|
|
797
|
+
const transform = style.transform;
|
|
798
|
+
if (!transform || transform === 'none') {
|
|
799
|
+
return individualScale;
|
|
800
|
+
}
|
|
801
|
+
if (transform.startsWith('matrix3d(')) {
|
|
802
|
+
const values = parseCssMatrixValues(transform);
|
|
803
|
+
if ((values === null || values === void 0 ? void 0 : values.length) === 16 &&
|
|
804
|
+
values[0] > 0 &&
|
|
805
|
+
values[5] > 0 &&
|
|
806
|
+
Math.abs(values[1]) < 1e-10 &&
|
|
807
|
+
Math.abs(values[4]) < 1e-10 &&
|
|
808
|
+
Math.abs(values[3]) < 1e-10 &&
|
|
809
|
+
Math.abs(values[7]) < 1e-10 &&
|
|
810
|
+
Math.abs(values[11]) < 1e-10) {
|
|
811
|
+
return {
|
|
812
|
+
x: individualScale.x * values[0],
|
|
813
|
+
y: individualScale.y * values[5],
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
return null;
|
|
817
|
+
}
|
|
818
|
+
if (transform.startsWith('matrix(')) {
|
|
819
|
+
const values = parseCssMatrixValues(transform);
|
|
820
|
+
if ((values === null || values === void 0 ? void 0 : values.length) === 6 &&
|
|
821
|
+
values[0] > 0 &&
|
|
822
|
+
values[3] > 0 &&
|
|
823
|
+
Math.abs(values[1]) < 1e-10 &&
|
|
824
|
+
Math.abs(values[2]) < 1e-10) {
|
|
825
|
+
return {
|
|
826
|
+
x: individualScale.x * values[0],
|
|
827
|
+
y: individualScale.y * values[3],
|
|
828
|
+
};
|
|
829
|
+
}
|
|
830
|
+
return null;
|
|
831
|
+
}
|
|
832
|
+
return null;
|
|
833
|
+
}
|
|
834
|
+
function parsePositiveScaleValue(value) {
|
|
835
|
+
var _a;
|
|
836
|
+
if (!value || value === 'none') {
|
|
837
|
+
return { x: 1, y: 1 };
|
|
838
|
+
}
|
|
839
|
+
const parts = value
|
|
840
|
+
.split(/\s+/)
|
|
841
|
+
.map((part) => parseFloat(part))
|
|
842
|
+
.filter(Number.isFinite);
|
|
843
|
+
if (parts.length === 0 ||
|
|
844
|
+
parts[0] <= 0 ||
|
|
845
|
+
(parts[1] != null && parts[1] <= 0)) {
|
|
846
|
+
return null;
|
|
847
|
+
}
|
|
848
|
+
return { x: parts[0], y: (_a = parts[1]) !== null && _a !== void 0 ? _a : parts[0] };
|
|
849
|
+
}
|
|
850
|
+
function parseCssMatrixValues(transform) {
|
|
851
|
+
const start = transform.indexOf('(');
|
|
852
|
+
const end = transform.lastIndexOf(')');
|
|
853
|
+
if (start < 0 || end <= start) {
|
|
854
|
+
return null;
|
|
855
|
+
}
|
|
856
|
+
const values = transform
|
|
857
|
+
.slice(start + 1, end)
|
|
858
|
+
.split(',')
|
|
859
|
+
.map((value) => parseFloat(value.trim()));
|
|
860
|
+
return values.every(Number.isFinite) ? values : null;
|
|
861
|
+
}
|
|
862
|
+
function isZeroAngleValue(value) {
|
|
863
|
+
const matches = value.match(/-?\d*\.?\d+(?:e[-+]?\d+)?/gi);
|
|
864
|
+
if (!(matches === null || matches === void 0 ? void 0 : matches.length)) {
|
|
865
|
+
return false;
|
|
866
|
+
}
|
|
867
|
+
const angle = parseFloat(matches[matches.length - 1]);
|
|
868
|
+
return Number.isFinite(angle) && Math.abs(angle) < 1e-10;
|
|
869
|
+
}
|
|
870
|
+
function hasTransformedHtmlAncestor(node, relativeTo, iframes) {
|
|
871
|
+
var _a;
|
|
872
|
+
const win = (_a = node.ownerDocument.defaultView) !== null && _a !== void 0 ? _a : window;
|
|
873
|
+
let element = getParentElementIncludingSlots(node, iframes);
|
|
874
|
+
while (element &&
|
|
875
|
+
element !== relativeTo &&
|
|
876
|
+
element !== node.ownerDocument.documentElement) {
|
|
877
|
+
if (element instanceof win.HTMLElement) {
|
|
878
|
+
const css = getCachedComputedStyle(element);
|
|
879
|
+
if (transformProperties.some((value) => css[value] ? css[value] !== 'none' : false)) {
|
|
880
|
+
return true;
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
element = getParentElementIncludingSlots(element, iframes);
|
|
884
|
+
}
|
|
885
|
+
return false;
|
|
886
|
+
}
|
|
887
|
+
function toViewportRelativeDocumentElementQuad(quad, node, relativeTo, iframes) {
|
|
888
|
+
var _a, _b, _c, _d, _e;
|
|
889
|
+
if (relativeTo !== node.ownerDocument.documentElement) {
|
|
890
|
+
return quad;
|
|
891
|
+
}
|
|
892
|
+
if (isViewportFixedAnchoredNode(node, iframes)) {
|
|
893
|
+
return quad;
|
|
894
|
+
}
|
|
895
|
+
const win = (_a = node.ownerDocument.defaultView) !== null && _a !== void 0 ? _a : window;
|
|
896
|
+
const scrollX = (_c = (_b = win.scrollX) !== null && _b !== void 0 ? _b : node.ownerDocument.documentElement.scrollLeft) !== null && _c !== void 0 ? _c : 0;
|
|
897
|
+
const scrollY = (_e = (_d = win.scrollY) !== null && _d !== void 0 ? _d : node.ownerDocument.documentElement.scrollTop) !== null && _e !== void 0 ? _e : 0;
|
|
898
|
+
if (scrollX === 0 && scrollY === 0) {
|
|
899
|
+
return quad;
|
|
900
|
+
}
|
|
901
|
+
return new DOMQuad(new DOMPoint(quad.p1.x - scrollX, quad.p1.y - scrollY), new DOMPoint(quad.p2.x - scrollX, quad.p2.y - scrollY), new DOMPoint(quad.p3.x - scrollX, quad.p3.y - scrollY), new DOMPoint(quad.p4.x - scrollX, quad.p4.y - scrollY));
|
|
902
|
+
}
|
|
903
|
+
function isViewportFixedAnchoredNode(node, iframes) {
|
|
904
|
+
var _a;
|
|
905
|
+
const win = (_a = node.ownerDocument.defaultView) !== null && _a !== void 0 ? _a : window;
|
|
906
|
+
let element = null;
|
|
907
|
+
if (node instanceof win.Text) {
|
|
908
|
+
element = getParentElementIncludingSlots(node, iframes);
|
|
909
|
+
}
|
|
910
|
+
else if (node instanceof win.Element) {
|
|
911
|
+
element = node;
|
|
912
|
+
}
|
|
913
|
+
while (element && element !== node.ownerDocument.documentElement) {
|
|
914
|
+
const cs = getCachedComputedStyle(element);
|
|
915
|
+
if (cs.position === 'fixed') {
|
|
916
|
+
return getNearestFixedContainingBlock(element, iframes) == null;
|
|
917
|
+
}
|
|
918
|
+
element = getParentElementIncludingSlots(element, iframes);
|
|
919
|
+
}
|
|
920
|
+
return false;
|
|
921
|
+
}
|
|
922
|
+
function shouldResolveFixedPositionThroughViewport(node, relativeTo, iframes) {
|
|
923
|
+
var _a;
|
|
924
|
+
const win = (_a = node.ownerDocument.defaultView) !== null && _a !== void 0 ? _a : window;
|
|
925
|
+
let element = null;
|
|
926
|
+
if (node instanceof win.Text) {
|
|
927
|
+
element = getParentElementIncludingSlots(node, iframes);
|
|
928
|
+
}
|
|
929
|
+
else if (node instanceof win.Element) {
|
|
930
|
+
element = node;
|
|
931
|
+
}
|
|
932
|
+
while (element && element !== node.ownerDocument.documentElement) {
|
|
933
|
+
const cs = getCachedComputedStyle(element);
|
|
934
|
+
if (cs.position === 'fixed') {
|
|
935
|
+
const fixedContainer = getNearestFixedContainingBlock(element, iframes);
|
|
936
|
+
return (fixedContainer == null ||
|
|
937
|
+
(fixedContainer !== relativeTo &&
|
|
938
|
+
!isFlatTreeInclusiveAncestor(relativeTo, fixedContainer)));
|
|
939
|
+
}
|
|
940
|
+
if (element === relativeTo) {
|
|
941
|
+
break;
|
|
942
|
+
}
|
|
943
|
+
element = getParentElementIncludingSlots(element, iframes);
|
|
944
|
+
}
|
|
945
|
+
return false;
|
|
946
|
+
}
|
|
947
|
+
/**
|
|
948
|
+
* Compute width/height for a Text node given an already-fetched bounding rect.
|
|
949
|
+
* Extracted from getElementSize so getBoxQuads (FIX 7) can reuse the Range it
|
|
950
|
+
* already created, avoiding a second Range allocation.
|
|
951
|
+
* @param {DOMMatrix} matrix
|
|
952
|
+
* @param {DOMRect} targetRect
|
|
953
|
+
* @param {Text} node
|
|
954
|
+
*/
|
|
955
|
+
function _getTextNodeSize(matrix, targetRect, node) {
|
|
956
|
+
var _a, _b;
|
|
957
|
+
const absA = Math.abs((_a = matrix === null || matrix === void 0 ? void 0 : matrix.a) !== null && _a !== void 0 ? _a : 1);
|
|
958
|
+
const absB = Math.abs((_b = matrix === null || matrix === void 0 ? void 0 : matrix.b) !== null && _b !== void 0 ? _b : 0);
|
|
959
|
+
const det = absA * absA - absB * absB; // cos(2*angle)*scale^2
|
|
960
|
+
let width, height;
|
|
961
|
+
if (Math.abs(det) > 1e-6) {
|
|
962
|
+
width = Math.max(0, (absA * targetRect.width - absB * targetRect.height) / det);
|
|
963
|
+
height = Math.max(0, (absA * targetRect.height - absB * targetRect.width) / det);
|
|
964
|
+
}
|
|
965
|
+
else {
|
|
966
|
+
// Singular (~45 deg rotation): use CSS line-height as the known height
|
|
967
|
+
const parentEl = node.parentElement;
|
|
968
|
+
let lineH = 16;
|
|
969
|
+
if (parentEl) {
|
|
970
|
+
const cs = getCachedComputedStyle(parentEl);
|
|
971
|
+
lineH = parseFloat(cs.lineHeight) || parseFloat(cs.fontSize) * 1.2 || 16;
|
|
972
|
+
}
|
|
973
|
+
height = Math.max(0, lineH);
|
|
974
|
+
const denom = Math.max(absA, absB);
|
|
975
|
+
width =
|
|
976
|
+
denom > 1e-6
|
|
977
|
+
? Math.max(0, (targetRect.width - height * absB) / denom)
|
|
978
|
+
: targetRect.width;
|
|
979
|
+
}
|
|
980
|
+
return { width, height };
|
|
981
|
+
}
|
|
982
|
+
//todo: https://drafts.csswg.org/css-transforms-2/#accumulated-3d-transformation-matrix-computation
|
|
983
|
+
// also good for writing a spec
|
|
984
|
+
// Find a value for z that will transform to 0. (from firefox matrix.h)
|
|
985
|
+
// or chromium https://github.com/chromium/chromium/blob/main/ui/gfx/geometry/transform.cc#L849
|
|
986
|
+
/**
|
|
987
|
+
* @param {DOMPoint} point
|
|
988
|
+
*/
|
|
989
|
+
function projectPoint(point, m) {
|
|
990
|
+
const z = -(point.x * m.m13 + point.y * m.m23 + m.m43) / m.m33;
|
|
991
|
+
return new DOMPoint(point.x, point.y, z, 1);
|
|
992
|
+
}
|
|
993
|
+
/**
|
|
994
|
+
* convert a DOM-Point to 2D
|
|
995
|
+
* @param {DOMPoint} point
|
|
996
|
+
*/
|
|
997
|
+
function as2DPoint(point) {
|
|
998
|
+
return new DOMPoint(point.x / point.w, point.y / point.w);
|
|
999
|
+
}
|
|
1000
|
+
/**
|
|
1001
|
+
* @param {Node} node
|
|
1002
|
+
* @param {DOMMatrix=} matrix
|
|
1003
|
+
*/
|
|
1004
|
+
function getElementSize(node, matrix) {
|
|
1005
|
+
var _a, _b, _c;
|
|
1006
|
+
let width = 0;
|
|
1007
|
+
let height = 0;
|
|
1008
|
+
// FIX 13: Cache cross-realm constructors once.
|
|
1009
|
+
const win = (_a = node.ownerDocument.defaultView) !== null && _a !== void 0 ? _a : window;
|
|
1010
|
+
if (node instanceof HTMLElement || node instanceof win.HTMLElement) {
|
|
1011
|
+
width = node.offsetWidth;
|
|
1012
|
+
height = node.offsetHeight;
|
|
1013
|
+
}
|
|
1014
|
+
else if (node instanceof SVGSVGElement ||
|
|
1015
|
+
node instanceof win.SVGSVGElement) {
|
|
1016
|
+
width = node.width.baseVal.value;
|
|
1017
|
+
height = node.height.baseVal.value;
|
|
1018
|
+
}
|
|
1019
|
+
else if (node instanceof SVGGraphicsElement ||
|
|
1020
|
+
node instanceof win.SVGGraphicsElement) {
|
|
1021
|
+
const bbox = node.getBBox();
|
|
1022
|
+
width = bbox.width;
|
|
1023
|
+
height = bbox.height;
|
|
1024
|
+
}
|
|
1025
|
+
else if (node instanceof MathMLElement ||
|
|
1026
|
+
node instanceof win.MathMLElement) {
|
|
1027
|
+
const bbox = node.getBoundingClientRect();
|
|
1028
|
+
width = bbox.width / ((_b = matrix === null || matrix === void 0 ? void 0 : matrix.a) !== null && _b !== void 0 ? _b : 1);
|
|
1029
|
+
height = bbox.height / ((_c = matrix === null || matrix === void 0 ? void 0 : matrix.d) !== null && _c !== void 0 ? _c : 1);
|
|
1030
|
+
}
|
|
1031
|
+
else if (node instanceof Text || node instanceof win.Text) {
|
|
1032
|
+
// Note: getBoxQuads passes an already-fetched rect via _getTextNodeSize to
|
|
1033
|
+
// avoid this Range creation. This path serves external callers of getElementSize.
|
|
1034
|
+
const range = node.ownerDocument.createRange();
|
|
1035
|
+
range.selectNodeContents(node);
|
|
1036
|
+
const targetRect = range.getBoundingClientRect();
|
|
1037
|
+
const result = _getTextNodeSize(matrix, targetRect, node);
|
|
1038
|
+
width = result.width;
|
|
1039
|
+
height = result.height;
|
|
1040
|
+
}
|
|
1041
|
+
return { width, height };
|
|
1042
|
+
}
|
|
1043
|
+
function getSvgVisualBox(node, bbox) {
|
|
1044
|
+
const svgStyle = getCachedComputedStyle(node);
|
|
1045
|
+
const strokeWidth = svgStyle.stroke !== 'none' ? parseFloat(svgStyle.strokeWidth) || 0 : 0;
|
|
1046
|
+
if (strokeWidth <= 0) {
|
|
1047
|
+
return bbox;
|
|
1048
|
+
}
|
|
1049
|
+
const strokeInflation = getSvgStrokeInflation(node, bbox, strokeWidth);
|
|
1050
|
+
return new DOMRect(bbox.x - strokeInflation.left, bbox.y - strokeInflation.top, bbox.width + strokeInflation.left + strokeInflation.right, bbox.height + strokeInflation.top + strokeInflation.bottom);
|
|
1051
|
+
}
|
|
1052
|
+
function getSvgStrokeInflation(node, bbox, strokeWidth) {
|
|
1053
|
+
var _a;
|
|
1054
|
+
const halfStrokeWidth = strokeWidth / 2;
|
|
1055
|
+
if (node instanceof SVGLineElement ||
|
|
1056
|
+
node instanceof ((_a = node.ownerDocument.defaultView) !== null && _a !== void 0 ? _a : window).SVGLineElement) {
|
|
1057
|
+
const x1 = node.x1.baseVal.value;
|
|
1058
|
+
const y1 = node.y1.baseVal.value;
|
|
1059
|
+
const x2 = node.x2.baseVal.value;
|
|
1060
|
+
const y2 = node.y2.baseVal.value;
|
|
1061
|
+
const dx = x2 - x1;
|
|
1062
|
+
const dy = y2 - y1;
|
|
1063
|
+
const length = Math.hypot(dx, dy);
|
|
1064
|
+
if (length > 1e-10) {
|
|
1065
|
+
let inflateX = (halfStrokeWidth * Math.abs(dy)) / length;
|
|
1066
|
+
let inflateY = (halfStrokeWidth * Math.abs(dx)) / length;
|
|
1067
|
+
const lineCap = getCachedComputedStyle(node).strokeLinecap;
|
|
1068
|
+
if (lineCap === 'round' || lineCap === 'square') {
|
|
1069
|
+
inflateX += (halfStrokeWidth * Math.abs(dx)) / length;
|
|
1070
|
+
inflateY += (halfStrokeWidth * Math.abs(dy)) / length;
|
|
1071
|
+
}
|
|
1072
|
+
return { left: inflateX, right: inflateX, top: inflateY, bottom: inflateY };
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
const genericInflation = strokeWidth * 2;
|
|
1076
|
+
return {
|
|
1077
|
+
left: genericInflation,
|
|
1078
|
+
right: genericInflation,
|
|
1079
|
+
top: genericInflation,
|
|
1080
|
+
bottom: genericInflation,
|
|
1081
|
+
};
|
|
1082
|
+
}
|
|
1083
|
+
/**
|
|
1084
|
+
* @param {Node} node
|
|
1085
|
+
* @param {boolean} includeScroll
|
|
1086
|
+
* @param {HTMLIFrameElement[]} iframes
|
|
1087
|
+
*/
|
|
1088
|
+
function getElementOffsetsInContainer(node, includeScroll, iframes) {
|
|
1089
|
+
var _a, _b, _c, _d, _e, _f;
|
|
1090
|
+
if (node instanceof HTMLElement ||
|
|
1091
|
+
node instanceof ((_a = node.ownerDocument.defaultView) !== null && _a !== void 0 ? _a : window).HTMLElement) {
|
|
1092
|
+
// When <html> appears inside a shadow DOM canvas the browser reflects
|
|
1093
|
+
// body's top margin into html.offsetTop (but not offsetLeft), causing
|
|
1094
|
+
// a Y-only shift in the transform walk. The html element has no real
|
|
1095
|
+
// layout offset relative to its shadow-host container, so return 0,0.
|
|
1096
|
+
if (node instanceof HTMLHtmlElement ||
|
|
1097
|
+
node instanceof ((_b = node.ownerDocument.defaultView) !== null && _b !== void 0 ? _b : window).HTMLHtmlElement) {
|
|
1098
|
+
return new DOMPoint(0, 0);
|
|
1099
|
+
}
|
|
1100
|
+
const cs = getCachedComputedStyle(node);
|
|
1101
|
+
if (cs.position === 'fixed') {
|
|
1102
|
+
const fixedContainer = getNearestFixedContainingBlock(node, iframes);
|
|
1103
|
+
if (!fixedContainer) {
|
|
1104
|
+
const left = cs.left && cs.left !== 'auto' ? parseFloat(cs.left) : node.offsetLeft;
|
|
1105
|
+
const top = cs.top && cs.top !== 'auto' ? parseFloat(cs.top) : node.offsetTop;
|
|
1106
|
+
return new DOMPoint(left, top);
|
|
1107
|
+
}
|
|
1108
|
+
const m = getResultingTransformationBetweenElementAndAllAncestors(fixedContainer, node.ownerDocument.body, iframes, true).inverse();
|
|
1109
|
+
const r1 = node.getBoundingClientRect();
|
|
1110
|
+
const r1t = m.transformPoint(r1);
|
|
1111
|
+
const r2 = fixedContainer.getBoundingClientRect();
|
|
1112
|
+
const r2t = m.transformPoint(r2);
|
|
1113
|
+
return new DOMPoint(r1t.x - r2t.x, r1t.y - r2t.y);
|
|
1114
|
+
}
|
|
1115
|
+
// FIX 4: Only call getCachedComputedStyle when includeScroll is true —
|
|
1116
|
+
// cs is unused in the plain offsetLeft/offsetTop path.
|
|
1117
|
+
if (includeScroll) {
|
|
1118
|
+
return new DOMPoint(node.offsetLeft - (node.scrollLeft - parseFloat(cs.borderLeftWidth)), node.offsetTop - (node.scrollTop - parseFloat(cs.borderTopWidth)));
|
|
1119
|
+
}
|
|
1120
|
+
else {
|
|
1121
|
+
return new DOMPoint(node.offsetLeft, node.offsetTop);
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
else if (node instanceof Text ||
|
|
1125
|
+
node instanceof ((_c = node.ownerDocument.defaultView) !== null && _c !== void 0 ? _c : window).Text) {
|
|
1126
|
+
const range = node.ownerDocument.createRange();
|
|
1127
|
+
range.selectNodeContents(node);
|
|
1128
|
+
const r1 = range.getBoundingClientRect();
|
|
1129
|
+
/** @type {HTMLElement} */
|
|
1130
|
+
//@ts-ignore
|
|
1131
|
+
const parent = getParentElementIncludingSlots(node, iframes);
|
|
1132
|
+
const r2 = parent.getBoundingClientRect();
|
|
1133
|
+
// Get the parent's CSS transform so we can work in local space even when rotated.
|
|
1134
|
+
const pt = getElementCombinedTransform(parent, iframes);
|
|
1135
|
+
const pa = pt.a, pb = pt.b, pc = pt.c, pd = pt.d;
|
|
1136
|
+
// AABB center of the transformed parent equals its geometric center.
|
|
1137
|
+
// geometric_center_screen = screen(0,0) + L * (pw/2, ph/2)
|
|
1138
|
+
// => screen(0,0) = AABB_center - L * (pw/2, ph/2)
|
|
1139
|
+
const pw = parent.offsetWidth;
|
|
1140
|
+
const ph = parent.offsetHeight;
|
|
1141
|
+
const parentOriginX = r2.x + r2.width / 2 - ((pa * pw) / 2 + (pc * ph) / 2);
|
|
1142
|
+
const parentOriginY = r2.y + r2.height / 2 - ((pb * pw) / 2 + (pd * ph) / 2);
|
|
1143
|
+
// Delta from parent origin to text AABB center in screen space
|
|
1144
|
+
const dx = r1.x + r1.width / 2 - parentOriginX;
|
|
1145
|
+
const dy = r1.y + r1.height / 2 - parentOriginY;
|
|
1146
|
+
// Apply inverse of CSS transform linear part: local_center = L^-1 * screen_delta
|
|
1147
|
+
const transformDet = pa * pd - pb * pc;
|
|
1148
|
+
let localCenterX, localCenterY;
|
|
1149
|
+
if (Math.abs(transformDet) > 1e-10) {
|
|
1150
|
+
localCenterX = (pd * dx - pc * dy) / transformDet;
|
|
1151
|
+
localCenterY = (pa * dy - pb * dx) / transformDet;
|
|
1152
|
+
}
|
|
1153
|
+
else {
|
|
1154
|
+
localCenterX = dx;
|
|
1155
|
+
localCenterY = dy;
|
|
1156
|
+
}
|
|
1157
|
+
// Recover tw and th in local space from the AABB using the 2x2 system:
|
|
1158
|
+
// aabb_w = tw*|cos| + th*|sin|, aabb_h = tw*|sin| + th*|cos|
|
|
1159
|
+
const absA = Math.abs(pa), absB = Math.abs(pb);
|
|
1160
|
+
const absDet = absA * absA - absB * absB;
|
|
1161
|
+
let tw, th;
|
|
1162
|
+
if (Math.abs(absDet) > 1e-6) {
|
|
1163
|
+
tw = Math.max(0, (absA * r1.width - absB * r1.height) / absDet);
|
|
1164
|
+
th = Math.max(0, (absA * r1.height - absB * r1.width) / absDet);
|
|
1165
|
+
}
|
|
1166
|
+
else {
|
|
1167
|
+
// Singular (~45 deg): use CSS line-height as th
|
|
1168
|
+
const cs = getCachedComputedStyle(parent);
|
|
1169
|
+
th = parseFloat(cs.lineHeight) || parseFloat(cs.fontSize) * 1.2 || 16;
|
|
1170
|
+
th = Math.max(0, th);
|
|
1171
|
+
const denom = Math.max(absA, absB);
|
|
1172
|
+
tw =
|
|
1173
|
+
denom > 1e-6 ? Math.max(0, (r1.width - th * absB) / denom) : r1.width;
|
|
1174
|
+
}
|
|
1175
|
+
// local origin = center minus half-dimensions
|
|
1176
|
+
return new DOMPoint(localCenterX - tw / 2, localCenterY - th / 2);
|
|
1177
|
+
}
|
|
1178
|
+
else if (node instanceof Element ||
|
|
1179
|
+
node instanceof ((_d = node.ownerDocument.defaultView) !== null && _d !== void 0 ? _d : window).Element) {
|
|
1180
|
+
if ((node instanceof SVGGraphicsElement ||
|
|
1181
|
+
node instanceof
|
|
1182
|
+
((_e = node.ownerDocument.defaultView) !== null && _e !== void 0 ? _e : window).SVGGraphicsElement) &&
|
|
1183
|
+
!(node instanceof SVGSVGElement ||
|
|
1184
|
+
node instanceof ((_f = node.ownerDocument.defaultView) !== null && _f !== void 0 ? _f : window).SVGSVGElement)) {
|
|
1185
|
+
const bb = node.getBBox();
|
|
1186
|
+
return new DOMPoint(bb.x, bb.y);
|
|
1187
|
+
}
|
|
1188
|
+
const cs = getCachedComputedStyle(node);
|
|
1189
|
+
if (cs.position === 'absolute') {
|
|
1190
|
+
return new DOMPoint(parseFloat(cs.left), parseFloat(cs.top));
|
|
1191
|
+
}
|
|
1192
|
+
const par = getParentElementIncludingSlots(node, iframes);
|
|
1193
|
+
const m = getResultingTransformationBetweenElementAndAllAncestors(par, document.body, iframes, true).inverse();
|
|
1194
|
+
const r1 = node.getBoundingClientRect();
|
|
1195
|
+
const r1t = m.transformPoint(r1);
|
|
1196
|
+
const r2 = par.getBoundingClientRect();
|
|
1197
|
+
const r2t = m.transformPoint(r2);
|
|
1198
|
+
return new DOMPoint(r1t.x - r2t.x, r1t.y - r2t.y);
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
/**
|
|
1202
|
+
* @param {Node} node
|
|
1203
|
+
* @param {Element} ancestor
|
|
1204
|
+
* @param {HTMLIFrameElement[]} iframes
|
|
1205
|
+
* @param {boolean=} excludeSelfZoom
|
|
1206
|
+
*/
|
|
1207
|
+
function getResultingTransformationBetweenElementAndAllAncestors(node, ancestor, iframes, excludeSelfZoom = false) {
|
|
1208
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
1209
|
+
let key;
|
|
1210
|
+
if (transformCache) {
|
|
1211
|
+
let i1 = hash.get(node);
|
|
1212
|
+
if (i1 === undefined)
|
|
1213
|
+
hash.set(node, (i1 = hashId++));
|
|
1214
|
+
let i2 = hash.get(ancestor);
|
|
1215
|
+
if (i2 === undefined)
|
|
1216
|
+
hash.set(ancestor, (i2 = hashId++));
|
|
1217
|
+
key = i1 + '_' + i2 + '_' + (excludeSelfZoom ? 'no-self-zoom' : 'full');
|
|
1218
|
+
const q = transformCache.get(key);
|
|
1219
|
+
if (q)
|
|
1220
|
+
return q;
|
|
1221
|
+
}
|
|
1222
|
+
/** @type {Element } */
|
|
1223
|
+
//@ts-ignore
|
|
1224
|
+
let actualElement = node;
|
|
1225
|
+
/** @type {DOMMatrix } */
|
|
1226
|
+
let parentElementMatrix;
|
|
1227
|
+
// FIX 12: Compute self-transform once; we'll carry parent transforms forward
|
|
1228
|
+
// each iteration instead of recomputing them.
|
|
1229
|
+
const useOwnSvgCtm = (actualElement instanceof SVGGraphicsElement ||
|
|
1230
|
+
actualElement instanceof
|
|
1231
|
+
((_a = actualElement.ownerDocument.defaultView) !== null && _a !== void 0 ? _a : window)
|
|
1232
|
+
.SVGGraphicsElement) &&
|
|
1233
|
+
!(actualElement instanceof SVGSVGElement ||
|
|
1234
|
+
actualElement instanceof
|
|
1235
|
+
((_b = actualElement.ownerDocument.defaultView) !== null && _b !== void 0 ? _b : window).SVGSVGElement);
|
|
1236
|
+
// SVGGraphicsElement.getCTM() already includes the element's local SVG/CSS transform.
|
|
1237
|
+
// Starting with getElementTransformWithZoom here would double-apply self rotate/scale.
|
|
1238
|
+
let currentElementTransform = useOwnSvgCtm
|
|
1239
|
+
? new DOMMatrix()
|
|
1240
|
+
: getElementTransformWithZoom(actualElement, iframes, !excludeSelfZoom);
|
|
1241
|
+
/** @type {DOMMatrix } */
|
|
1242
|
+
// FIX 2: Only use a non-identity starting matrix when the element itself has
|
|
1243
|
+
// a CSS transform. Most plain elements have identity, avoiding a multiply.
|
|
1244
|
+
let originalElementAndAllParentsMultipliedMatrix = currentElementTransform.isIdentity
|
|
1245
|
+
? new DOMMatrix()
|
|
1246
|
+
: currentElementTransform;
|
|
1247
|
+
let perspectiveParentElement = getParentElementIncludingSlots(actualElement, iframes);
|
|
1248
|
+
if (perspectiveParentElement) {
|
|
1249
|
+
// FIX 5: Guard transformStyle read behind is2D — on a standard 2D page
|
|
1250
|
+
// the matrix is always 2D here so we skip the style read entirely.
|
|
1251
|
+
if (!originalElementAndAllParentsMultipliedMatrix.is2D) {
|
|
1252
|
+
const s = getCachedComputedStyle(perspectiveParentElement);
|
|
1253
|
+
if (s.transformStyle !== 'preserve-3d') {
|
|
1254
|
+
projectTo2D(originalElementAndAllParentsMultipliedMatrix);
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
let lastOffsetParent = null;
|
|
1259
|
+
while (actualElement != ancestor && actualElement != null) {
|
|
1260
|
+
let parentElement = getParentElementIncludingSlots(actualElement, iframes);
|
|
1261
|
+
if (actualElement instanceof HTMLElement ||
|
|
1262
|
+
actualElement instanceof
|
|
1263
|
+
((_c = actualElement.ownerDocument.defaultView) !== null && _c !== void 0 ? _c : window).HTMLElement) {
|
|
1264
|
+
const fixedStyle = getCachedComputedStyle(actualElement);
|
|
1265
|
+
if (fixedStyle.position === 'fixed') {
|
|
1266
|
+
const fixedContainer = getNearestFixedContainingBlock(actualElement, iframes);
|
|
1267
|
+
parentElement = fixedContainer !== null && fixedContainer !== void 0 ? fixedContainer : ancestor;
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
if (actualElement.assignedSlot != null) {
|
|
1271
|
+
if (actualElement.nodeType === Node.ELEMENT_NODE) {
|
|
1272
|
+
const slotOffsetParent = offsetParentPolyfill(actualElement);
|
|
1273
|
+
const shouldApplySlottedOffset = lastOffsetParent !== slotOffsetParent &&
|
|
1274
|
+
(lastOffsetParent === null ||
|
|
1275
|
+
actualElement === lastOffsetParent ||
|
|
1276
|
+
!isFlatTreeInclusiveAncestor(lastOffsetParent, actualElement));
|
|
1277
|
+
if (shouldApplySlottedOffset) {
|
|
1278
|
+
const l = offsetTopLeftPolyfill(actualElement, 'offsetLeft');
|
|
1279
|
+
const t = offsetTopLeftPolyfill(actualElement, 'offsetTop');
|
|
1280
|
+
// FIX 3: Skip zero-translation matrix allocations.
|
|
1281
|
+
if (l !== 0 || t !== 0) {
|
|
1282
|
+
const mvMat = new DOMMatrix().translateSelf(l, t);
|
|
1283
|
+
originalElementAndAllParentsMultipliedMatrix = mvMat.multiplySelf(originalElementAndAllParentsMultipliedMatrix);
|
|
1284
|
+
}
|
|
1285
|
+
lastOffsetParent = slotOffsetParent;
|
|
1286
|
+
}
|
|
1287
|
+
if (lastOffsetParent === null)
|
|
1288
|
+
lastOffsetParent = slotOffsetParent;
|
|
1289
|
+
}
|
|
1290
|
+
else if (actualElement.nodeType === Node.TEXT_NODE) {
|
|
1291
|
+
const offsets = getElementOffsetsInContainer(actualElement, actualElement !== node, iframes);
|
|
1292
|
+
// FIX 3
|
|
1293
|
+
if (offsets.x !== 0 || offsets.y !== 0) {
|
|
1294
|
+
const mvMat = new DOMMatrix().translateSelf(offsets.x, offsets.y);
|
|
1295
|
+
originalElementAndAllParentsMultipliedMatrix = mvMat.multiplySelf(originalElementAndAllParentsMultipliedMatrix);
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
/*
|
|
1299
|
+
following code should be used instead of above to fix:
|
|
1300
|
+
but it does not work with:
|
|
1301
|
+
<div>
|
|
1302
|
+
<visu-tag-root-canvas id="outer-tag-root-canvas" tag-root="SRM.RBG01">
|
|
1303
|
+
<template shadowrootmode="open">
|
|
1304
|
+
<div id="rootObj" style="height:100%;width:100%;position:absolute;top:400px;">
|
|
1305
|
+
<slot></slot>
|
|
1306
|
+
</div>
|
|
1307
|
+
</template>
|
|
1308
|
+
<div class="wrapper" id="aaaaabb" style="height:50px;width:50px;"></div>
|
|
1309
|
+
</visu-tag-root-canvas>
|
|
1310
|
+
</div>
|
|
1311
|
+
*/
|
|
1312
|
+
/*
|
|
1313
|
+
const l = offsetTopLeftPolyfill(actualElement, 'offsetLeft');
|
|
1314
|
+
const t = offsetTopLeftPolyfill(actualElement, 'offsetTop');
|
|
1315
|
+
const mvMat = new DOMMatrix().translateSelf(l, t);
|
|
1316
|
+
originalElementAndAllParentsMultipliedMatrix = mvMat.multiplySelf(originalElementAndAllParentsMultipliedMatrix);
|
|
1317
|
+
*/
|
|
1318
|
+
}
|
|
1319
|
+
else {
|
|
1320
|
+
if (!(actualElement instanceof SVGSVGElement) &&
|
|
1321
|
+
!(actualElement instanceof
|
|
1322
|
+
((_d = actualElement.ownerDocument.defaultView) !== null && _d !== void 0 ? _d : window).SVGSVGElement) &&
|
|
1323
|
+
(actualElement instanceof SVGGraphicsElement ||
|
|
1324
|
+
actualElement instanceof
|
|
1325
|
+
((_e = actualElement.ownerDocument.defaultView) !== null && _e !== void 0 ? _e : window)
|
|
1326
|
+
.SVGGraphicsElement)) {
|
|
1327
|
+
const ctm = actualElement.getCTM();
|
|
1328
|
+
const bb = actualElement.getBBox();
|
|
1329
|
+
// FIX 3
|
|
1330
|
+
if (bb.x !== 0 || bb.y !== 0) {
|
|
1331
|
+
const mvMat = new DOMMatrix().translateSelf(bb.x, bb.y);
|
|
1332
|
+
originalElementAndAllParentsMultipliedMatrix = mvMat.multiplySelf(originalElementAndAllParentsMultipliedMatrix);
|
|
1333
|
+
}
|
|
1334
|
+
originalElementAndAllParentsMultipliedMatrix = new DOMMatrix([
|
|
1335
|
+
ctm.a,
|
|
1336
|
+
ctm.b,
|
|
1337
|
+
ctm.c,
|
|
1338
|
+
ctm.d,
|
|
1339
|
+
ctm.e,
|
|
1340
|
+
ctm.f,
|
|
1341
|
+
]).multiplySelf(originalElementAndAllParentsMultipliedMatrix);
|
|
1342
|
+
parentElement = actualElement.ownerSVGElement;
|
|
1343
|
+
}
|
|
1344
|
+
else if (actualElement instanceof HTMLElement ||
|
|
1345
|
+
actualElement instanceof
|
|
1346
|
+
((_f = actualElement.ownerDocument.defaultView) !== null && _f !== void 0 ? _f : window).HTMLElement) {
|
|
1347
|
+
const actualStyle = getCachedComputedStyle(actualElement);
|
|
1348
|
+
const isFixedSelf = actualStyle.position === 'fixed' && actualElement === node;
|
|
1349
|
+
if ((isFixedSelf || lastOffsetParent !== actualElement.offsetParent) &&
|
|
1350
|
+
!(actualElement instanceof HTMLSlotElement ||
|
|
1351
|
+
actualElement instanceof
|
|
1352
|
+
((_g = actualElement.ownerDocument.defaultView) !== null && _g !== void 0 ? _g : window)
|
|
1353
|
+
.HTMLSlotElement) &&
|
|
1354
|
+
(lastOffsetParent === null ||
|
|
1355
|
+
actualElement === lastOffsetParent ||
|
|
1356
|
+
!isFlatTreeInclusiveAncestor(lastOffsetParent, actualElement))) {
|
|
1357
|
+
const offsets = getElementOffsetsInContainer(actualElement, actualElement !== node, iframes);
|
|
1358
|
+
const zoom = getElementZoom(actualElement);
|
|
1359
|
+
lastOffsetParent = actualElement.offsetParent;
|
|
1360
|
+
// FIX 3
|
|
1361
|
+
if (offsets.x !== 0 || offsets.y !== 0) {
|
|
1362
|
+
const mvMat = new DOMMatrix().translateSelf(offsets.x * zoom, offsets.y * zoom);
|
|
1363
|
+
originalElementAndAllParentsMultipliedMatrix = mvMat.multiplySelf(originalElementAndAllParentsMultipliedMatrix);
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
else {
|
|
1368
|
+
const offsets = getElementOffsetsInContainer(actualElement, actualElement !== node, iframes);
|
|
1369
|
+
lastOffsetParent = null;
|
|
1370
|
+
// FIX 3
|
|
1371
|
+
if (offsets.x !== 0 || offsets.y !== 0) {
|
|
1372
|
+
const mvMat = new DOMMatrix().translateSelf(offsets.x, offsets.y);
|
|
1373
|
+
originalElementAndAllParentsMultipliedMatrix = mvMat.multiplySelf(originalElementAndAllParentsMultipliedMatrix);
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
if (parentElement) {
|
|
1378
|
+
if (parentElement === ancestor) {
|
|
1379
|
+
// The ancestor's own transform is excluded from the returned matrix,
|
|
1380
|
+
// so avoid computing it on the hot return path.
|
|
1381
|
+
if (lastOffsetParent !== null &&
|
|
1382
|
+
(parentElement instanceof HTMLElement ||
|
|
1383
|
+
parentElement instanceof
|
|
1384
|
+
((_h = parentElement.ownerDocument.defaultView) !== null && _h !== void 0 ? _h : window)
|
|
1385
|
+
.HTMLElement) &&
|
|
1386
|
+
parentElement.offsetParent === lastOffsetParent) {
|
|
1387
|
+
const ancOff = getElementOffsetsInContainer(parentElement, false, iframes);
|
|
1388
|
+
// FIX 3
|
|
1389
|
+
if (ancOff.x !== 0 || ancOff.y !== 0) {
|
|
1390
|
+
originalElementAndAllParentsMultipliedMatrix = new DOMMatrix()
|
|
1391
|
+
.translate(-ancOff.x, -ancOff.y)
|
|
1392
|
+
.multiply(originalElementAndAllParentsMultipliedMatrix);
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
// FIX 15: Do NOT subtract the scroll of documentElement (the viewport/window
|
|
1396
|
+
// scroll). The offsetLeft/offsetTop walk already yields document-absolute
|
|
1397
|
+
// coordinates; subtracting documentElement.scrollTop would wrongly
|
|
1398
|
+
// shift positions to viewport-space when the page is scrolled.
|
|
1399
|
+
// Only subtract scroll for a non-root ancestor that is itself a
|
|
1400
|
+
// scroll container (e.g. an overflow:scroll div used as relativeTo).
|
|
1401
|
+
const isViewportScrollContainer = parentElement === parentElement.ownerDocument.documentElement;
|
|
1402
|
+
if (!isViewportScrollContainer &&
|
|
1403
|
+
(parentElement.scrollTop || parentElement.scrollLeft))
|
|
1404
|
+
originalElementAndAllParentsMultipliedMatrix = new DOMMatrix()
|
|
1405
|
+
.translate(-parentElement.scrollLeft, -parentElement.scrollTop)
|
|
1406
|
+
.multiply(originalElementAndAllParentsMultipliedMatrix);
|
|
1407
|
+
const ancestorZoom = getElementZoomScaleTransform(parentElement);
|
|
1408
|
+
if (!ancestorZoom.isIdentity)
|
|
1409
|
+
originalElementAndAllParentsMultipliedMatrix = ancestorZoom.multiply(originalElementAndAllParentsMultipliedMatrix);
|
|
1410
|
+
// FIX 6: Cache result on the early-return path. Originally, the
|
|
1411
|
+
// cache.set() only ran after the while-loop (the null/root
|
|
1412
|
+
// fallthrough), so the most common case — element IS a
|
|
1413
|
+
// descendant of ancestor — was NEVER cached.
|
|
1414
|
+
if (transformCache)
|
|
1415
|
+
transformCache.set(key, originalElementAndAllParentsMultipliedMatrix);
|
|
1416
|
+
return originalElementAndAllParentsMultipliedMatrix;
|
|
1417
|
+
}
|
|
1418
|
+
// FIX 12: parentElementMatrix computed here; in the next iteration this
|
|
1419
|
+
// becomes the element's own transform, so we can reuse it without
|
|
1420
|
+
// calling getElementCombinedTransform again.
|
|
1421
|
+
parentElementMatrix = getElementTransformWithZoom(parentElement, iframes);
|
|
1422
|
+
if (!parentElementMatrix.isIdentity)
|
|
1423
|
+
originalElementAndAllParentsMultipliedMatrix =
|
|
1424
|
+
parentElementMatrix.multiply(originalElementAndAllParentsMultipliedMatrix);
|
|
1425
|
+
perspectiveParentElement = getParentElementIncludingSlots(parentElement, iframes);
|
|
1426
|
+
if (perspectiveParentElement) {
|
|
1427
|
+
// FIX 5: Skip transformStyle read when matrix is already 2D.
|
|
1428
|
+
if (!originalElementAndAllParentsMultipliedMatrix.is2D) {
|
|
1429
|
+
const s = getCachedComputedStyle(perspectiveParentElement);
|
|
1430
|
+
if (s.transformStyle !== 'preserve-3d') {
|
|
1431
|
+
projectTo2D(originalElementAndAllParentsMultipliedMatrix);
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
actualElement = parentElement;
|
|
1437
|
+
}
|
|
1438
|
+
if (transformCache) {
|
|
1439
|
+
transformCache.set(key, originalElementAndAllParentsMultipliedMatrix);
|
|
1440
|
+
}
|
|
1441
|
+
return originalElementAndAllParentsMultipliedMatrix;
|
|
1442
|
+
}
|
|
1443
|
+
/*
|
|
1444
|
+
getResultingTransformationBetweenElementAndAllAncestors -> but with extra layout matrix (does not work yet....)
|
|
1445
|
+
|
|
1446
|
+
export function getResultingTransformationBetweenElementAndAllAncestors(node, ancestor, iframes) {
|
|
1447
|
+
let key;
|
|
1448
|
+
if (transformCache) {
|
|
1449
|
+
let i1 = hash.get(node);
|
|
1450
|
+
if (i1 === undefined)
|
|
1451
|
+
hash.set(node, i1 = hashId++);
|
|
1452
|
+
let i2 = hash.get(ancestor);
|
|
1453
|
+
if (i2 === undefined)
|
|
1454
|
+
hash.set(ancestor, i2 = hashId++);
|
|
1455
|
+
key = i1 + '_' + i2;
|
|
1456
|
+
const q = transformCache.get(key);
|
|
1457
|
+
if (q)
|
|
1458
|
+
return q;
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
// NEW - two matrices instead of one
|
|
1462
|
+
let layoutMatrix = new DOMMatrix();
|
|
1463
|
+
|
|
1464
|
+
let actualElement = node;
|
|
1465
|
+
|
|
1466
|
+
let transformMatrix = getElementCombinedTransform(actualElement, iframes); //.multiplySelf(transformMatrix);
|
|
1467
|
+
|
|
1468
|
+
|
|
1469
|
+
const perspectiveParent = getParentElementIncludingSlots(actualElement, iframes);
|
|
1470
|
+
if (perspectiveParent) {
|
|
1471
|
+
const s = getCachedComputedStyle(perspectiveParent);
|
|
1472
|
+
if (s.transformStyle !== "preserve-3d")
|
|
1473
|
+
projectTo2D(transformMatrix);
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
|
|
1477
|
+
let lastOffsetParent = null;
|
|
1478
|
+
|
|
1479
|
+
while (actualElement !== ancestor && actualElement != null) {
|
|
1480
|
+
|
|
1481
|
+
const parentElement = getParentElementIncludingSlots(actualElement, iframes);
|
|
1482
|
+
|
|
1483
|
+
// ------------------------
|
|
1484
|
+
// LAYOUT MATRIX (offsets)
|
|
1485
|
+
// ------------------------
|
|
1486
|
+
|
|
1487
|
+
if (actualElement.assignedSlot != null) {
|
|
1488
|
+
|
|
1489
|
+
const l = offsetTopLeftPolyfill(actualElement, "offsetLeft");
|
|
1490
|
+
const t = offsetTopLeftPolyfill(actualElement, "offsetTop");
|
|
1491
|
+
layoutMatrix = new DOMMatrix().translateSelf(l, t).multiplySelf(layoutMatrix);
|
|
1492
|
+
|
|
1493
|
+
} else {
|
|
1494
|
+
|
|
1495
|
+
if (actualElement instanceof HTMLElement ||
|
|
1496
|
+
actualElement instanceof (actualElement.ownerDocument.defaultView ?? window).HTMLElement) {
|
|
1497
|
+
|
|
1498
|
+
if (lastOffsetParent !== actualElement.offsetParent &&
|
|
1499
|
+
!(actualElement instanceof HTMLSlotElement)) {
|
|
1500
|
+
|
|
1501
|
+
const offsets = getElementOffsetsInContainer(actualElement, actualElement !== node, iframes);
|
|
1502
|
+
lastOffsetParent = actualElement.offsetParent;
|
|
1503
|
+
|
|
1504
|
+
layoutMatrix = new DOMMatrix().translateSelf(offsets.x, offsets.y).multiplySelf(layoutMatrix);
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
} else {
|
|
1508
|
+
|
|
1509
|
+
const offsets = getElementOffsetsInContainer(actualElement, actualElement !== node, iframes);
|
|
1510
|
+
lastOffsetParent = null;
|
|
1511
|
+
|
|
1512
|
+
layoutMatrix = new DOMMatrix().translateSelf(offsets.x, offsets.y).multiplySelf(layoutMatrix);
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
// ------------------------
|
|
1517
|
+
// TRANSFORM MATRIX (CSS)
|
|
1518
|
+
// ------------------------
|
|
1519
|
+
|
|
1520
|
+
if (parentElement) {
|
|
1521
|
+
|
|
1522
|
+
// NEW - only affects transform pipeline
|
|
1523
|
+
const parentTransform = getElementCombinedTransform(parentElement, iframes);
|
|
1524
|
+
transformMatrix = parentTransform.multiply(transformMatrix);
|
|
1525
|
+
|
|
1526
|
+
// flattening boundary
|
|
1527
|
+
const perspectiveParent = getParentElementIncludingSlots(parentElement, iframes);
|
|
1528
|
+
if (perspectiveParent) {
|
|
1529
|
+
const s = getCachedComputedStyle(perspectiveParent);
|
|
1530
|
+
if (s.transformStyle !== "preserve-3d")
|
|
1531
|
+
projectTo2D(transformMatrix);
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
// ------------------------
|
|
1535
|
+
// EXIT CONDITION
|
|
1536
|
+
// ------------------------
|
|
1537
|
+
|
|
1538
|
+
if (parentElement === ancestor) {
|
|
1539
|
+
|
|
1540
|
+
// NEW - scroll offsets belong to layout
|
|
1541
|
+
if (parentElement.scrollTop || parentElement.scrollLeft) {
|
|
1542
|
+
layoutMatrix = new DOMMatrix()
|
|
1543
|
+
.translate(-parentElement.scrollLeft, -parentElement.scrollTop)
|
|
1544
|
+
.multiply(layoutMatrix);
|
|
1545
|
+
}
|
|
1546
|
+
|
|
1547
|
+
const result = layoutMatrix.multiply(transformMatrix);
|
|
1548
|
+
|
|
1549
|
+
if (transformCache)
|
|
1550
|
+
transformCache.set(key, result);
|
|
1551
|
+
|
|
1552
|
+
return result;
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
actualElement = parentElement;
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
const result = layoutMatrix.multiply(transformMatrix);
|
|
1560
|
+
|
|
1561
|
+
if (transformCache)
|
|
1562
|
+
transformCache.set(key, result);
|
|
1563
|
+
|
|
1564
|
+
return result;
|
|
1565
|
+
}
|
|
1566
|
+
*/
|
|
1567
|
+
/**
|
|
1568
|
+
* @param {Node} node
|
|
1569
|
+
* @param {HTMLIFrameElement[]} iframes
|
|
1570
|
+
* @returns {Element}
|
|
1571
|
+
*/
|
|
1572
|
+
function getParentElementIncludingSlots(node, iframes) {
|
|
1573
|
+
var _a, _b, _c;
|
|
1574
|
+
if ((node instanceof Element ||
|
|
1575
|
+
node instanceof ((_a = node.ownerDocument.defaultView) !== null && _a !== void 0 ? _a : window).Element) &&
|
|
1576
|
+
node.assignedSlot)
|
|
1577
|
+
return node.assignedSlot;
|
|
1578
|
+
if (node.parentElement == null) {
|
|
1579
|
+
if (node.parentNode instanceof ShadowRoot ||
|
|
1580
|
+
node.parentNode instanceof
|
|
1581
|
+
((_b = node.ownerDocument.defaultView) !== null && _b !== void 0 ? _b : window).ShadowRoot) {
|
|
1582
|
+
return node.parentNode.host;
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
if (node instanceof HTMLHtmlElement ||
|
|
1586
|
+
node instanceof ((_c = node.ownerDocument.defaultView) !== null && _c !== void 0 ? _c : window).HTMLHtmlElement) {
|
|
1587
|
+
if (iframes) {
|
|
1588
|
+
for (const f of iframes)
|
|
1589
|
+
if ((f === null || f === void 0 ? void 0 : f.contentDocument) == node.ownerDocument)
|
|
1590
|
+
return f;
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
return node.parentElement;
|
|
1594
|
+
}
|
|
1595
|
+
/**
|
|
1596
|
+
* @param {Node} element
|
|
1597
|
+
* @param {HTMLIFrameElement[]=} iframes
|
|
1598
|
+
*/
|
|
1599
|
+
function getElementCombinedTransform(element, iframes) {
|
|
1600
|
+
var _a;
|
|
1601
|
+
var _b;
|
|
1602
|
+
if (element instanceof Text ||
|
|
1603
|
+
element instanceof ((_b = element.ownerDocument.defaultView) !== null && _b !== void 0 ? _b : window).Text)
|
|
1604
|
+
return new DOMMatrix();
|
|
1605
|
+
/** @type {Element} */
|
|
1606
|
+
// @ts-ignore
|
|
1607
|
+
const actualElement = element;
|
|
1608
|
+
//https://www.w3.org/TR/css-transforms-2/#ctm
|
|
1609
|
+
let s = getCachedComputedStyle(actualElement);
|
|
1610
|
+
// FIX 10: Check hasTransform first — it's the most common non-identity case.
|
|
1611
|
+
// Reordering so the most frequent hit is evaluated first.
|
|
1612
|
+
const hasTransform = s.transform !== 'none' && !!s.transform;
|
|
1613
|
+
const hasTranslate = s.translate !== 'none' && !!s.translate;
|
|
1614
|
+
const hasRotate = s.rotate !== 'none' && !!s.rotate;
|
|
1615
|
+
const hasScale = s.scale !== 'none' && !!s.scale;
|
|
1616
|
+
const hasOffsetPath = !!s.offsetPath && s.offsetPath !== 'none';
|
|
1617
|
+
if (!hasTransform &&
|
|
1618
|
+
!hasTranslate &&
|
|
1619
|
+
!hasRotate &&
|
|
1620
|
+
!hasScale &&
|
|
1621
|
+
!hasOffsetPath) {
|
|
1622
|
+
// FIX 1: Check parent perspective right here in the fast-path, so identity
|
|
1623
|
+
// elements on non-3D pages return a new DOMMatrix() immediately
|
|
1624
|
+
// without calling getElementPerspectiveTransform at all.
|
|
1625
|
+
const parent = getParentElementIncludingSlots(actualElement, iframes);
|
|
1626
|
+
if (!parent)
|
|
1627
|
+
return new DOMMatrix();
|
|
1628
|
+
const ps = getCachedComputedStyle(parent);
|
|
1629
|
+
if (!ps.perspective || ps.perspective === 'none')
|
|
1630
|
+
return new DOMMatrix();
|
|
1631
|
+
// Parent has a perspective — fall through to compute it properly.
|
|
1632
|
+
//@ts-ignore
|
|
1633
|
+
const pt = getElementPerspectiveTransform(actualElement, iframes);
|
|
1634
|
+
return pt != null ? pt : new DOMMatrix();
|
|
1635
|
+
}
|
|
1636
|
+
let m = new DOMMatrix();
|
|
1637
|
+
const origin = s.transformOrigin.split(' ');
|
|
1638
|
+
const originX = parseFloat(origin[0]);
|
|
1639
|
+
const originY = parseFloat(origin[1]);
|
|
1640
|
+
const originZ = origin[2] ? parseFloat(origin[2]) : 0;
|
|
1641
|
+
// FIX 11: Skip the origin wrap/unwrap entirely when origin is (0,0,0).
|
|
1642
|
+
// Saves two DOMMatrix allocations and two multiply calls per element.
|
|
1643
|
+
const hasNonZeroOrigin = originX !== 0 || originY !== 0 || originZ !== 0;
|
|
1644
|
+
const mOri = hasNonZeroOrigin
|
|
1645
|
+
? new DOMMatrix().translateSelf(originX, originY, originZ)
|
|
1646
|
+
: null;
|
|
1647
|
+
if (hasTranslate) {
|
|
1648
|
+
let tr = s.translate;
|
|
1649
|
+
if (tr.includes('%')) {
|
|
1650
|
+
const v = tr.split(' ');
|
|
1651
|
+
const r = actualElement.getBoundingClientRect();
|
|
1652
|
+
if (v[0].endsWith('%'))
|
|
1653
|
+
v[0] = (parseFloat(v[0]) * r.width) / 100 + 'px';
|
|
1654
|
+
if ((_a = v[1]) === null || _a === void 0 ? void 0 : _a.endsWith('%'))
|
|
1655
|
+
v[1] = (parseFloat(v[1]) * r.height) / 100 + 'px';
|
|
1656
|
+
tr = v.join(',');
|
|
1657
|
+
}
|
|
1658
|
+
m.multiplySelf(new DOMMatrix('translate(' + tr.replaceAll(' ', ',') + ')'));
|
|
1659
|
+
}
|
|
1660
|
+
if (hasRotate) {
|
|
1661
|
+
m.multiplySelf(new DOMMatrix('rotate(' + s.rotate.replaceAll(' ', ',') + ')'));
|
|
1662
|
+
}
|
|
1663
|
+
if (hasScale) {
|
|
1664
|
+
m.multiplySelf(new DOMMatrix('scale(' + s.scale.replaceAll(' ', ',') + ')'));
|
|
1665
|
+
}
|
|
1666
|
+
if (hasOffsetPath) {
|
|
1667
|
+
m.multiplySelf(computeOffsetTransformMatrix(element));
|
|
1668
|
+
}
|
|
1669
|
+
if (hasTransform) {
|
|
1670
|
+
m.multiplySelf(new DOMMatrix(s.transform));
|
|
1671
|
+
}
|
|
1672
|
+
// FIX 11 (continued): Only wrap with origin if non-zero.
|
|
1673
|
+
if (hasNonZeroOrigin) {
|
|
1674
|
+
m = mOri.multiply(m.multiplySelf(mOri.inverse()));
|
|
1675
|
+
}
|
|
1676
|
+
//@ts-ignore
|
|
1677
|
+
const pt = getElementPerspectiveTransform(element, iframes);
|
|
1678
|
+
if (pt != null) {
|
|
1679
|
+
m = pt.multiplySelf(m);
|
|
1680
|
+
}
|
|
1681
|
+
return m;
|
|
1682
|
+
}
|
|
1683
|
+
/**
|
|
1684
|
+
* project a DOM-Matrix to 2D (from firefox matrix.h)
|
|
1685
|
+
* @param {DOMMatrix} m
|
|
1686
|
+
*/
|
|
1687
|
+
function projectTo2D(m) {
|
|
1688
|
+
m.m31 = 0.0;
|
|
1689
|
+
m.m32 = 0.0;
|
|
1690
|
+
m.m13 = 0.0;
|
|
1691
|
+
m.m23 = 0.0;
|
|
1692
|
+
m.m33 = 1.0;
|
|
1693
|
+
m.m43 = 0.0;
|
|
1694
|
+
m.m34 = 0.0;
|
|
1695
|
+
// Some matrices, such as those derived from perspective transforms,
|
|
1696
|
+
// can modify _44 from 1, while leaving the rest of the fourth column
|
|
1697
|
+
// (_14, _24) at 0. In this case, after resetting the third row and
|
|
1698
|
+
// third column above, the value of _44 functions only to scale the
|
|
1699
|
+
// coordinate transform divide by W. The matrix can be converted to
|
|
1700
|
+
// a true 2D matrix by normalizing out the scaling effect of _44 on
|
|
1701
|
+
// the remaining components ahead of time.
|
|
1702
|
+
if (m.m14 == 0.0 && m.m24 == 0.0 && m.m44 != 1.0 && m.m44 != 0.0) {
|
|
1703
|
+
const scale = 1.0 / m.m44;
|
|
1704
|
+
m.m11 *= scale;
|
|
1705
|
+
m.m12 *= scale;
|
|
1706
|
+
m.m21 *= scale;
|
|
1707
|
+
m.m22 *= scale;
|
|
1708
|
+
m.m41 *= scale;
|
|
1709
|
+
m.m42 *= scale;
|
|
1710
|
+
m.m44 = 1.0;
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
/**
|
|
1714
|
+
* @param {HTMLElement} element
|
|
1715
|
+
* @param {HTMLIFrameElement[]} iframes
|
|
1716
|
+
*/
|
|
1717
|
+
function getElementPerspectiveTransform(element, iframes) {
|
|
1718
|
+
/** @type { Element } */
|
|
1719
|
+
//@ts-ignore
|
|
1720
|
+
const perspectiveNode = getParentElementIncludingSlots(element, iframes);
|
|
1721
|
+
if (perspectiveNode) {
|
|
1722
|
+
//https://drafts.csswg.org/css-transforms-2/#perspective-matrix-computation
|
|
1723
|
+
let s = getCachedComputedStyle(perspectiveNode);
|
|
1724
|
+
if (s.perspective !== 'none') {
|
|
1725
|
+
let m = new DOMMatrix();
|
|
1726
|
+
let p = parseFloat(s.perspective);
|
|
1727
|
+
m.m34 = -1.0 / p;
|
|
1728
|
+
//https://drafts.csswg.org/css-transforms-2/#PerspectiveDefined
|
|
1729
|
+
if (s.perspectiveOrigin) {
|
|
1730
|
+
const origin = s.perspectiveOrigin.split(' ');
|
|
1731
|
+
const originX = parseFloat(origin[0]) - element.offsetLeft;
|
|
1732
|
+
const originY = parseFloat(origin[1]) - element.offsetTop;
|
|
1733
|
+
const mOri = new DOMMatrix().translateSelf(originX, originY);
|
|
1734
|
+
const mOriInv = new DOMMatrix().translateSelf(-originX, -originY);
|
|
1735
|
+
return mOri.multiplySelf(m.multiplySelf(mOriInv));
|
|
1736
|
+
}
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
return null;
|
|
1740
|
+
}
|
|
1741
|
+
function computeOffsetTransformMatrix(elem) {
|
|
1742
|
+
var _a;
|
|
1743
|
+
const cs = getCachedComputedStyle(elem);
|
|
1744
|
+
const offsetPath = cs.offsetPath; // e.g. "path('M0,0 ...')"
|
|
1745
|
+
const offsetDistance = cs.offsetDistance; // e.g. "50%"
|
|
1746
|
+
const offsetRotate = cs.offsetRotate; // e.g. "auto", "45deg", "auto 30deg"
|
|
1747
|
+
const offsetAnchor = cs.offsetAnchor;
|
|
1748
|
+
const transformOrigin = cs.transformOrigin;
|
|
1749
|
+
// Parse offset-distance (px or %)
|
|
1750
|
+
let distance = parseOffsetDistance(offsetDistance);
|
|
1751
|
+
// Compute position & tangent on path (in containing block coordinates)
|
|
1752
|
+
let { x, y, angle } = computeOffsetPathPoint(elem, offsetPath, distance);
|
|
1753
|
+
// Subtract the element's flow position within its containing block.
|
|
1754
|
+
// The offset-path positions the element absolutely within the containing block,
|
|
1755
|
+
// but the walk already adds offsetLeft/offsetTop (flow position). To avoid
|
|
1756
|
+
// double-counting, make the offset relative to the flow position.
|
|
1757
|
+
const parent = elem.parentElement;
|
|
1758
|
+
if (parent instanceof HTMLElement ||
|
|
1759
|
+
parent instanceof ((_a = parent.ownerDocument.defaultView) !== null && _a !== void 0 ? _a : window).HTMLElement) {
|
|
1760
|
+
if (elem.offsetParent === parent) {
|
|
1761
|
+
// Containing block = parent = offsetParent
|
|
1762
|
+
x -= elem.offsetLeft;
|
|
1763
|
+
y -= elem.offsetTop;
|
|
1764
|
+
}
|
|
1765
|
+
else if (elem.offsetParent === parent.offsetParent) {
|
|
1766
|
+
// Both share the same offsetParent
|
|
1767
|
+
x -= elem.offsetLeft - parent.offsetLeft;
|
|
1768
|
+
y -= elem.offsetTop - parent.offsetTop;
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1771
|
+
// Handle offset-rotate
|
|
1772
|
+
let rotateFinal = 0;
|
|
1773
|
+
if (offsetRotate.startsWith('auto')) {
|
|
1774
|
+
let parts = offsetRotate.split(/\s+/);
|
|
1775
|
+
let extra = parts.length === 2 ? parseFloat(parts[1]) : 0;
|
|
1776
|
+
rotateFinal = angle + extra;
|
|
1777
|
+
}
|
|
1778
|
+
else {
|
|
1779
|
+
rotateFinal = parseFloat(offsetRotate);
|
|
1780
|
+
}
|
|
1781
|
+
const anchor = parseOffsetAnchor(offsetAnchor, transformOrigin, elem);
|
|
1782
|
+
const anchorMatrix = new DOMMatrix().translateSelf(-anchor.x, -anchor.y);
|
|
1783
|
+
let m = anchorMatrix.translate(x, y);
|
|
1784
|
+
m.multiplySelf(anchorMatrix.invertSelf());
|
|
1785
|
+
m.rotateSelf(rotateFinal);
|
|
1786
|
+
m.translateSelf(-anchor.x, -anchor.y);
|
|
1787
|
+
return m;
|
|
1788
|
+
}
|
|
1789
|
+
function parseOffsetAnchor(str, transformOrigin, elem) {
|
|
1790
|
+
const width = elem.offsetWidth;
|
|
1791
|
+
const height = elem.offsetHeight;
|
|
1792
|
+
if (!str || str === 'auto') {
|
|
1793
|
+
str = transformOrigin;
|
|
1794
|
+
}
|
|
1795
|
+
const parts = str.split(/\s+/);
|
|
1796
|
+
if (parts.length === 1) {
|
|
1797
|
+
// 1-value syntax = x only, y = center
|
|
1798
|
+
const x = parsePosition(parts[0], width);
|
|
1799
|
+
return { x, y: height / 2 };
|
|
1800
|
+
}
|
|
1801
|
+
const x = parsePosition(parts[0], width);
|
|
1802
|
+
const y = parsePosition(parts[1], height);
|
|
1803
|
+
return { x, y };
|
|
1804
|
+
}
|
|
1805
|
+
function parsePosition(part, size) {
|
|
1806
|
+
part = part.trim();
|
|
1807
|
+
if (part.endsWith('%')) {
|
|
1808
|
+
return (parseFloat(part) / 100) * size;
|
|
1809
|
+
}
|
|
1810
|
+
if (part.endsWith('px')) {
|
|
1811
|
+
return parseFloat(part);
|
|
1812
|
+
}
|
|
1813
|
+
// keywords
|
|
1814
|
+
switch (part) {
|
|
1815
|
+
case 'left':
|
|
1816
|
+
return 0;
|
|
1817
|
+
case 'top':
|
|
1818
|
+
return 0;
|
|
1819
|
+
case 'center':
|
|
1820
|
+
return size / 2;
|
|
1821
|
+
case 'right':
|
|
1822
|
+
return size;
|
|
1823
|
+
case 'bottom':
|
|
1824
|
+
return size;
|
|
1825
|
+
}
|
|
1826
|
+
return parseFloat(part);
|
|
1827
|
+
}
|
|
1828
|
+
function parseOffsetDistance(str) {
|
|
1829
|
+
str = str.trim();
|
|
1830
|
+
if (str.endsWith('%')) {
|
|
1831
|
+
return parseFloat(str) / 100; // normalized (0..1)
|
|
1832
|
+
}
|
|
1833
|
+
return parseFloat(str); // px value if pathLength = 1
|
|
1834
|
+
}
|
|
1835
|
+
function parseAngle(str) {
|
|
1836
|
+
if (!str)
|
|
1837
|
+
return 0;
|
|
1838
|
+
str = str.trim();
|
|
1839
|
+
if (str.endsWith('deg'))
|
|
1840
|
+
return parseFloat(str);
|
|
1841
|
+
if (str.endsWith('rad'))
|
|
1842
|
+
return parseFloat(str) * (180 / Math.PI);
|
|
1843
|
+
if (str.endsWith('grad'))
|
|
1844
|
+
return parseFloat(str) * 0.9;
|
|
1845
|
+
return parseFloat(str);
|
|
1846
|
+
}
|
|
1847
|
+
function computeOffsetPathPoint(elem, offsetPath, distNorm) {
|
|
1848
|
+
if (!offsetPath || offsetPath === 'none') {
|
|
1849
|
+
return { x: 0, y: 0, angle: 0 };
|
|
1850
|
+
}
|
|
1851
|
+
const value = offsetPath.trim();
|
|
1852
|
+
let m = value.match(/path\(["'](.+)["']\)/);
|
|
1853
|
+
if (m)
|
|
1854
|
+
return computePathType(m[1], distNorm);
|
|
1855
|
+
if (value.startsWith('circle('))
|
|
1856
|
+
return computeCircle(value, distNorm);
|
|
1857
|
+
if (value.startsWith('ellipse('))
|
|
1858
|
+
return computeEllipse(value, distNorm);
|
|
1859
|
+
if (value.startsWith('inset('))
|
|
1860
|
+
return computeInset(value, elem, distNorm);
|
|
1861
|
+
if (value.startsWith('rect('))
|
|
1862
|
+
return computeRect(value, distNorm);
|
|
1863
|
+
if (value.startsWith('xywh('))
|
|
1864
|
+
return computeXYWH(value, distNorm);
|
|
1865
|
+
if (value.startsWith('ray('))
|
|
1866
|
+
return computeRay(value, distNorm);
|
|
1867
|
+
if (value.startsWith('polygon('))
|
|
1868
|
+
return computePolygon(value, distNorm);
|
|
1869
|
+
console.warn('Unsupported offset-path:', offsetPath);
|
|
1870
|
+
return { x: 0, y: 0, angle: 0 };
|
|
1871
|
+
}
|
|
1872
|
+
function computePathType(pathData, distNorm) {
|
|
1873
|
+
let svgPath = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
1874
|
+
svgPath.setAttribute('d', pathData);
|
|
1875
|
+
const total = svgPath.getTotalLength();
|
|
1876
|
+
const dist = distNorm <= 1 ? distNorm * total : distNorm;
|
|
1877
|
+
const p1 = svgPath.getPointAtLength(dist);
|
|
1878
|
+
const p2 = svgPath.getPointAtLength(Math.min(total, dist + 0.01));
|
|
1879
|
+
let angle = (Math.atan2(p2.y - p1.y, p2.x - p1.x) * 180) / Math.PI;
|
|
1880
|
+
return { x: p1.x, y: p1.y, angle };
|
|
1881
|
+
}
|
|
1882
|
+
function computeRay(str, t) {
|
|
1883
|
+
let m = str.match(/ray\(([^)]+)\)/);
|
|
1884
|
+
let inside = m[1].trim();
|
|
1885
|
+
// Split on "at" (optional)
|
|
1886
|
+
let [beforeAt, atPart] = inside.split('at').map((s) => s && s.trim());
|
|
1887
|
+
// angle
|
|
1888
|
+
let parts = beforeAt.split(/\s+/);
|
|
1889
|
+
let angleDeg = parseAngle(parts[0]);
|
|
1890
|
+
let angleRad = (angleDeg * Math.PI) / 180;
|
|
1891
|
+
// point of origin
|
|
1892
|
+
let ox = 0, oy = 0;
|
|
1893
|
+
if (atPart) {
|
|
1894
|
+
const pos = atPart.split(/\s+/);
|
|
1895
|
+
ox = parseFloat(pos[0]);
|
|
1896
|
+
oy = parseFloat(pos[1]);
|
|
1897
|
+
}
|
|
1898
|
+
// Ray: infinite line; offset-distance is distance along ray
|
|
1899
|
+
let dist = t <= 1 ? t : t; // percentage normalized already
|
|
1900
|
+
let x = ox + Math.cos(angleRad) * dist;
|
|
1901
|
+
let y = oy + Math.sin(angleRad) * dist;
|
|
1902
|
+
// tangent is ray direction
|
|
1903
|
+
return { x, y, angle: angleDeg };
|
|
1904
|
+
}
|
|
1905
|
+
function computeCircle(str, t) {
|
|
1906
|
+
let m = str.match(/circle\(([^)]+)\)/);
|
|
1907
|
+
let inner = m[1];
|
|
1908
|
+
let [radiusPart, atPart] = inner.split('at').map((s) => s.trim());
|
|
1909
|
+
let r = parseFloat(radiusPart);
|
|
1910
|
+
let [cx, cy] = atPart.split(/\s+/).map(parseFloat);
|
|
1911
|
+
let angleRad = t * 2 * Math.PI;
|
|
1912
|
+
let x = cx + Math.cos(angleRad) * r;
|
|
1913
|
+
let y = cy + Math.sin(angleRad) * r;
|
|
1914
|
+
let tangentAngleDeg = (angleRad * 180) / Math.PI + 90;
|
|
1915
|
+
return { x, y, angle: tangentAngleDeg };
|
|
1916
|
+
}
|
|
1917
|
+
function computeEllipse(str, t) {
|
|
1918
|
+
let m = str.match(/ellipse\(([^)]+)\)/);
|
|
1919
|
+
let parts = m[1].split('at');
|
|
1920
|
+
let radii = parts[0].trim().split(/\s+/).map(parseFloat);
|
|
1921
|
+
let center = parts[1].trim().split(/\s+/).map(parseFloat);
|
|
1922
|
+
let rx = radii[0];
|
|
1923
|
+
let ry = radii[1];
|
|
1924
|
+
let cx = center[0];
|
|
1925
|
+
let cy = center[1];
|
|
1926
|
+
let angleRad = t * 2 * Math.PI;
|
|
1927
|
+
let x = cx + Math.cos(angleRad) * rx;
|
|
1928
|
+
let y = cy + Math.sin(angleRad) * ry;
|
|
1929
|
+
// tangent direction derivative
|
|
1930
|
+
let dx = -Math.sin(angleRad) * rx;
|
|
1931
|
+
let dy = Math.cos(angleRad) * ry;
|
|
1932
|
+
let tangentAngleDeg = (Math.atan2(dy, dx) * 180) / Math.PI;
|
|
1933
|
+
return { x, y, angle: tangentAngleDeg };
|
|
1934
|
+
}
|
|
1935
|
+
function computeRect(str, t) {
|
|
1936
|
+
let m = str.match(/rect\(([^)]+)\)/);
|
|
1937
|
+
let nums = m[1].split(/\s+/).map((s) => parseFloat(s));
|
|
1938
|
+
let top = nums[0], right = nums[1], bottom = nums[2], left = nums[3];
|
|
1939
|
+
return rectPath(top, left, right, bottom, t);
|
|
1940
|
+
}
|
|
1941
|
+
function computeXYWH(str, t) {
|
|
1942
|
+
let m = str.match(/xywh\(([^)]+)\)/);
|
|
1943
|
+
let nums = m[1].split(/\s+/).map(parseFloat);
|
|
1944
|
+
let left = nums[0];
|
|
1945
|
+
let top = nums[1];
|
|
1946
|
+
let width = nums[2];
|
|
1947
|
+
let height = nums[3];
|
|
1948
|
+
return rectPath(top, left, left + width, top + height, t);
|
|
1949
|
+
}
|
|
1950
|
+
function computePolygon(str, t) {
|
|
1951
|
+
let m = str.match(/polygon\(([^)]+)\)/);
|
|
1952
|
+
let pairs = m[1].split(',').map((p) => p.trim().split(/\s+/).map(parseFloat));
|
|
1953
|
+
// Build cumulative lengths
|
|
1954
|
+
let pts = pairs;
|
|
1955
|
+
let lengths = [0];
|
|
1956
|
+
for (let i = 1; i < pts.length; i++) {
|
|
1957
|
+
let dx = pts[i][0] - pts[i - 1][0];
|
|
1958
|
+
let dy = pts[i][1] - pts[i - 1][1];
|
|
1959
|
+
lengths.push(Math.hypot(dx, dy) + lengths[i - 1]);
|
|
1960
|
+
}
|
|
1961
|
+
// close polygon
|
|
1962
|
+
let dx = pts[0][0] - pts[pts.length - 1][0];
|
|
1963
|
+
let dy = pts[0][1] - pts[pts.length - 1][1];
|
|
1964
|
+
lengths.push(Math.hypot(dx, dy) + lengths[lengths.length - 1]);
|
|
1965
|
+
let total = lengths[lengths.length - 1];
|
|
1966
|
+
let target = t * total;
|
|
1967
|
+
// find segment
|
|
1968
|
+
let i = lengths.findIndex((len) => len >= target);
|
|
1969
|
+
if (i <= 0)
|
|
1970
|
+
i = 1;
|
|
1971
|
+
let prevLen = lengths[i - 1];
|
|
1972
|
+
let nextLen = lengths[i];
|
|
1973
|
+
let segT = (target - prevLen) / (nextLen - prevLen);
|
|
1974
|
+
// segment points
|
|
1975
|
+
let a = pts[(i - 1) % pts.length];
|
|
1976
|
+
let b = pts[i % pts.length];
|
|
1977
|
+
let x = a[0] + (b[0] - a[0]) * segT;
|
|
1978
|
+
let y = a[1] + (b[1] - a[1]) * segT;
|
|
1979
|
+
let angle = (Math.atan2(b[1] - a[1], b[0] - a[0]) * 180) / Math.PI;
|
|
1980
|
+
return { x, y, angle };
|
|
1981
|
+
}
|
|
1982
|
+
function rectPath(top, left, right, bottom, t) {
|
|
1983
|
+
let w = right - left;
|
|
1984
|
+
let h = bottom - top;
|
|
1985
|
+
let perimeter = 2 * (w + h);
|
|
1986
|
+
let dist = t * perimeter;
|
|
1987
|
+
// go around edges
|
|
1988
|
+
if (dist < w) {
|
|
1989
|
+
// top edge
|
|
1990
|
+
let x = left + dist;
|
|
1991
|
+
return { x, y: top, angle: 0 };
|
|
1992
|
+
}
|
|
1993
|
+
dist -= w;
|
|
1994
|
+
if (dist < h) {
|
|
1995
|
+
// right edge
|
|
1996
|
+
let y = top + dist;
|
|
1997
|
+
return { x: right, y, angle: 90 };
|
|
1998
|
+
}
|
|
1999
|
+
dist -= h;
|
|
2000
|
+
if (dist < w) {
|
|
2001
|
+
// bottom edge
|
|
2002
|
+
let x = right - dist;
|
|
2003
|
+
return { x, y: bottom, angle: 180 };
|
|
2004
|
+
}
|
|
2005
|
+
dist -= w;
|
|
2006
|
+
// left edge
|
|
2007
|
+
let y = bottom - dist;
|
|
2008
|
+
return { x: left, y, angle: 270 };
|
|
2009
|
+
}
|
|
2010
|
+
// normalized inset uses calc
|
|
2011
|
+
function tokenizeCalc(input) {
|
|
2012
|
+
let tokens = [];
|
|
2013
|
+
let i = 0;
|
|
2014
|
+
while (i < input.length) {
|
|
2015
|
+
let ch = input[i];
|
|
2016
|
+
if (/\s/.test(ch)) {
|
|
2017
|
+
i++;
|
|
2018
|
+
continue;
|
|
2019
|
+
}
|
|
2020
|
+
// operators & parentheses
|
|
2021
|
+
if ('+-*/()'.includes(ch)) {
|
|
2022
|
+
tokens.push({ type: ch, value: ch });
|
|
2023
|
+
i++;
|
|
2024
|
+
continue;
|
|
2025
|
+
}
|
|
2026
|
+
// numbers or dimensions or %
|
|
2027
|
+
if (/[0-9.]/.test(ch)) {
|
|
2028
|
+
let start = i;
|
|
2029
|
+
while (/[0-9.]/.test(input[i]))
|
|
2030
|
+
i++;
|
|
2031
|
+
let num = input.slice(start, i);
|
|
2032
|
+
if (input[i] === '%') {
|
|
2033
|
+
i++;
|
|
2034
|
+
tokens.push({ type: 'percentage', value: parseFloat(num) });
|
|
2035
|
+
continue;
|
|
2036
|
+
}
|
|
2037
|
+
// only px supported
|
|
2038
|
+
if (input.slice(i, i + 2) === 'px') {
|
|
2039
|
+
i += 2;
|
|
2040
|
+
tokens.push({ type: 'dimension', value: parseFloat(num), unit: 'px' });
|
|
2041
|
+
continue;
|
|
2042
|
+
}
|
|
2043
|
+
// plain number
|
|
2044
|
+
tokens.push({ type: 'number', value: parseFloat(num) });
|
|
2045
|
+
continue;
|
|
2046
|
+
}
|
|
2047
|
+
// function name (calc)
|
|
2048
|
+
if (/[a-zA-Z]/.test(ch)) {
|
|
2049
|
+
let start = i;
|
|
2050
|
+
while (/[a-zA-Z]/.test(input[i]))
|
|
2051
|
+
i++;
|
|
2052
|
+
let name = input.slice(start, i);
|
|
2053
|
+
if (name === 'calc' && input[i] === '(') {
|
|
2054
|
+
tokens.push({ type: 'func', value: 'calc' });
|
|
2055
|
+
continue;
|
|
2056
|
+
}
|
|
2057
|
+
throw new Error('Unsupported function: ' + name);
|
|
2058
|
+
}
|
|
2059
|
+
throw new Error('Unexpected character in calc(): ' + ch);
|
|
2060
|
+
}
|
|
2061
|
+
return tokens;
|
|
2062
|
+
}
|
|
2063
|
+
// normalized inset uses calc
|
|
2064
|
+
function parseCalc(tokens) {
|
|
2065
|
+
let i = 0;
|
|
2066
|
+
function peek() {
|
|
2067
|
+
return tokens[i];
|
|
2068
|
+
}
|
|
2069
|
+
function consume() {
|
|
2070
|
+
return tokens[i++];
|
|
2071
|
+
}
|
|
2072
|
+
function parseExpression() {
|
|
2073
|
+
let node = parseTerm();
|
|
2074
|
+
while (peek() && (peek().type === '+' || peek().type === '-')) {
|
|
2075
|
+
let op = consume().type;
|
|
2076
|
+
let right = parseTerm();
|
|
2077
|
+
node = { type: 'binary', op, left: node, right };
|
|
2078
|
+
}
|
|
2079
|
+
return node;
|
|
2080
|
+
}
|
|
2081
|
+
function parseTerm() {
|
|
2082
|
+
let node = parseFactor();
|
|
2083
|
+
while (peek() && (peek().type === '*' || peek().type === '/')) {
|
|
2084
|
+
let op = consume().type;
|
|
2085
|
+
let right = parseFactor();
|
|
2086
|
+
node = { type: 'binary', op, left: node, right };
|
|
2087
|
+
}
|
|
2088
|
+
return node;
|
|
2089
|
+
}
|
|
2090
|
+
function parseFactor() {
|
|
2091
|
+
let t = peek();
|
|
2092
|
+
if (!t)
|
|
2093
|
+
throw 'Unexpected end in calc()';
|
|
2094
|
+
if (t.type === 'number') {
|
|
2095
|
+
consume();
|
|
2096
|
+
return { type: 'number', value: t.value };
|
|
2097
|
+
}
|
|
2098
|
+
if (t.type === 'dimension') {
|
|
2099
|
+
consume();
|
|
2100
|
+
return { type: 'dimension', value: t.value, unit: t.unit };
|
|
2101
|
+
}
|
|
2102
|
+
if (t.type === 'percentage') {
|
|
2103
|
+
consume();
|
|
2104
|
+
return { type: 'percentage', value: t.value };
|
|
2105
|
+
}
|
|
2106
|
+
if (t.type === 'func') {
|
|
2107
|
+
consume(); // "calc"
|
|
2108
|
+
if (peek().type !== '(')
|
|
2109
|
+
throw "Expected '(' after calc";
|
|
2110
|
+
consume();
|
|
2111
|
+
let node = parseExpression();
|
|
2112
|
+
if (!peek() || peek().type !== ')')
|
|
2113
|
+
throw "Expected ')'";
|
|
2114
|
+
consume();
|
|
2115
|
+
return node;
|
|
2116
|
+
}
|
|
2117
|
+
if (t.type === '(') {
|
|
2118
|
+
consume();
|
|
2119
|
+
let node = parseExpression();
|
|
2120
|
+
if (!peek() || peek().type !== ')')
|
|
2121
|
+
throw "Expected ')'";
|
|
2122
|
+
consume();
|
|
2123
|
+
return node;
|
|
2124
|
+
}
|
|
2125
|
+
throw new Error('Unexpected calc token ' + JSON.stringify(t));
|
|
2126
|
+
}
|
|
2127
|
+
let ast = parseExpression();
|
|
2128
|
+
if (i !== tokens.length)
|
|
2129
|
+
throw 'Extra tokens after calc';
|
|
2130
|
+
return ast;
|
|
2131
|
+
}
|
|
2132
|
+
// normalized inset uses calc
|
|
2133
|
+
function evalCalc(ast, env) {
|
|
2134
|
+
switch (ast.type) {
|
|
2135
|
+
case 'number':
|
|
2136
|
+
return ast.value;
|
|
2137
|
+
case 'dimension':
|
|
2138
|
+
return ast.value; // px only -> already a number
|
|
2139
|
+
case 'percentage':
|
|
2140
|
+
return env.percentBase * (ast.value / 100);
|
|
2141
|
+
case 'binary': {
|
|
2142
|
+
let l = evalCalc(ast.left, env);
|
|
2143
|
+
let r = evalCalc(ast.right, env);
|
|
2144
|
+
switch (ast.op) {
|
|
2145
|
+
case '+':
|
|
2146
|
+
return l + r;
|
|
2147
|
+
case '-':
|
|
2148
|
+
return l - r;
|
|
2149
|
+
case '*':
|
|
2150
|
+
return l * r;
|
|
2151
|
+
case '/':
|
|
2152
|
+
return l / r;
|
|
2153
|
+
}
|
|
2154
|
+
}
|
|
2155
|
+
}
|
|
2156
|
+
throw 'Invalid AST node ' + ast.type;
|
|
2157
|
+
}
|
|
2158
|
+
function resolveLength(expr, element, useHeight = false) {
|
|
2159
|
+
expr = expr.trim();
|
|
2160
|
+
// Fast path: pure px
|
|
2161
|
+
if (/^[0-9.]+px$/.test(expr))
|
|
2162
|
+
return parseFloat(expr);
|
|
2163
|
+
let base = useHeight ? element.offsetHeight : element.offsetWidth;
|
|
2164
|
+
// Pure %
|
|
2165
|
+
if (/^[0-9.]+%$/.test(expr)) {
|
|
2166
|
+
let p = parseFloat(expr);
|
|
2167
|
+
return base * (p / 100);
|
|
2168
|
+
}
|
|
2169
|
+
// calc(...) or mixed values
|
|
2170
|
+
const ast = parseCalc(tokenizeCalc(expr));
|
|
2171
|
+
return evalCalc(ast, {
|
|
2172
|
+
percentBase: base,
|
|
2173
|
+
});
|
|
2174
|
+
}
|
|
2175
|
+
function parseInsetArgs(str) {
|
|
2176
|
+
let inside = str
|
|
2177
|
+
.trim()
|
|
2178
|
+
.replace(/^inset\s*\(/, '')
|
|
2179
|
+
.replace(/\)\s*$/, '');
|
|
2180
|
+
let args = [];
|
|
2181
|
+
let current = '';
|
|
2182
|
+
let depth = 0;
|
|
2183
|
+
for (let i = 0; i < inside.length; i++) {
|
|
2184
|
+
let ch = inside[i];
|
|
2185
|
+
if (ch === '(') {
|
|
2186
|
+
depth++;
|
|
2187
|
+
current += ch;
|
|
2188
|
+
}
|
|
2189
|
+
else if (ch === ')') {
|
|
2190
|
+
depth--;
|
|
2191
|
+
current += ch;
|
|
2192
|
+
}
|
|
2193
|
+
else if (/\s/.test(ch) && depth === 0) {
|
|
2194
|
+
if (current.trim() !== '') {
|
|
2195
|
+
args.push(current.trim());
|
|
2196
|
+
current = '';
|
|
2197
|
+
}
|
|
2198
|
+
}
|
|
2199
|
+
else {
|
|
2200
|
+
current += ch;
|
|
2201
|
+
}
|
|
2202
|
+
}
|
|
2203
|
+
if (current.trim() !== '') {
|
|
2204
|
+
args.push(current.trim());
|
|
2205
|
+
}
|
|
2206
|
+
return args;
|
|
2207
|
+
}
|
|
2208
|
+
/**
|
|
2209
|
+
*
|
|
2210
|
+
* @param {string} str
|
|
2211
|
+
* @param {HTMLElement} element
|
|
2212
|
+
* @param {number} progress
|
|
2213
|
+
* @returns
|
|
2214
|
+
*/
|
|
2215
|
+
function computeInset(str, element, progress) {
|
|
2216
|
+
const args = parseInsetArgs(str);
|
|
2217
|
+
if (args.length !== 4)
|
|
2218
|
+
throw new Error('inset() must have 4 arguments');
|
|
2219
|
+
const topPx = resolveLength(args[0], element, true);
|
|
2220
|
+
const rightPx = resolveLength(args[1], element, false);
|
|
2221
|
+
const bottomPx = resolveLength(args[2], element, true);
|
|
2222
|
+
const leftPx = resolveLength(args[3], element, false);
|
|
2223
|
+
const w = element.offsetWidth;
|
|
2224
|
+
const h = element.offsetHeight;
|
|
2225
|
+
// Actual rectangle coordinates
|
|
2226
|
+
const x1 = leftPx;
|
|
2227
|
+
const y1 = topPx;
|
|
2228
|
+
const x2 = w - rightPx;
|
|
2229
|
+
const y2 = h - bottomPx;
|
|
2230
|
+
// Rectangle perimeter
|
|
2231
|
+
const P = 2 * (x2 - x1 + (y2 - y1));
|
|
2232
|
+
let d = P * progress;
|
|
2233
|
+
// Walk the rectangle clockwise, return point
|
|
2234
|
+
// Top edge: (x1 -> x2, y1)
|
|
2235
|
+
let len = x2 - x1;
|
|
2236
|
+
if (d <= len)
|
|
2237
|
+
return { x: x1 + d, y: y1, angle: 0 };
|
|
2238
|
+
d -= len;
|
|
2239
|
+
// Right edge: (x2, y1 -> y2)
|
|
2240
|
+
len = y2 - y1;
|
|
2241
|
+
if (d <= len)
|
|
2242
|
+
return { x: x2, y: y1 + d, angle: 90 };
|
|
2243
|
+
d -= len;
|
|
2244
|
+
// Bottom edge: (x2 -> x1, y2)
|
|
2245
|
+
len = x2 - x1;
|
|
2246
|
+
if (d <= len)
|
|
2247
|
+
return { x: x2 - d, y: y2, angle: 180 };
|
|
2248
|
+
d -= len;
|
|
2249
|
+
// Left edge: (x1, y2 -> y1)
|
|
2250
|
+
return { x: x1, y: y2 - d, angle: 270 };
|
|
2251
|
+
}
|
|
2252
|
+
//Code from: https://github.com/floating-ui/floating-ui/blob/master/packages/utils/src/dom.ts
|
|
2253
|
+
const transformProperties = [
|
|
2254
|
+
'transform',
|
|
2255
|
+
'translate',
|
|
2256
|
+
'scale',
|
|
2257
|
+
'rotate',
|
|
2258
|
+
'perspective',
|
|
2259
|
+
];
|
|
2260
|
+
const willChangeValues = [
|
|
2261
|
+
'transform',
|
|
2262
|
+
'translate',
|
|
2263
|
+
'scale',
|
|
2264
|
+
'rotate',
|
|
2265
|
+
'perspective',
|
|
2266
|
+
'filter',
|
|
2267
|
+
];
|
|
2268
|
+
const containValues = ['paint', 'layout', 'strict', 'content'];
|
|
2269
|
+
function isElement(value) {
|
|
2270
|
+
var _a, _b;
|
|
2271
|
+
const elType = (_b = (_a = value === null || value === void 0 ? void 0 : value.ownerDocument) === null || _a === void 0 ? void 0 : _a.defaultView) === null || _b === void 0 ? void 0 : _b.Element;
|
|
2272
|
+
return (value instanceof Element || (elType != null && value instanceof elType));
|
|
2273
|
+
}
|
|
2274
|
+
/**
|
|
2275
|
+
*
|
|
2276
|
+
* @param {CSSStyleDeclaration} css
|
|
2277
|
+
* @returns {boolean}
|
|
2278
|
+
*/
|
|
2279
|
+
function isContainingBlock(css) {
|
|
2280
|
+
// https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block
|
|
2281
|
+
// https://drafts.csswg.org/css-transforms-2/#individual-transforms
|
|
2282
|
+
return (transformProperties.some((value) => css[value] ? css[value] !== 'none' : false) ||
|
|
2283
|
+
(css.containerType ? css.containerType !== 'normal' : false) ||
|
|
2284
|
+
(css.backdropFilter ? css.backdropFilter !== 'none' : false) ||
|
|
2285
|
+
(css.filter ? css.filter !== 'none' : false) ||
|
|
2286
|
+
willChangeValues.some((value) => (css.willChange || '').includes(value)) ||
|
|
2287
|
+
containValues.some((value) => (css.contain || '').includes(value)));
|
|
2288
|
+
}
|
|
2289
|
+
//Code from: https://github.com/jcfranco/composed-offset-position/blob/main/src/index.ts
|
|
2290
|
+
function flatTreeParent(element) {
|
|
2291
|
+
if (element.assignedSlot)
|
|
2292
|
+
return element.assignedSlot;
|
|
2293
|
+
if (element.parentNode instanceof ShadowRoot)
|
|
2294
|
+
return element.parentNode.host;
|
|
2295
|
+
return element.parentNode;
|
|
2296
|
+
}
|
|
2297
|
+
function isFlatTreeInclusiveAncestor(ancestor, node) {
|
|
2298
|
+
for (let current = node; current; current = flatTreeParent(current)) {
|
|
2299
|
+
if (current === ancestor)
|
|
2300
|
+
return true;
|
|
2301
|
+
}
|
|
2302
|
+
return false;
|
|
2303
|
+
}
|
|
2304
|
+
function ancestorTreeScopes(element) {
|
|
2305
|
+
var _a, _b, _c;
|
|
2306
|
+
var _d, _e;
|
|
2307
|
+
const scopes = new Set();
|
|
2308
|
+
let currentScope = element.getRootNode();
|
|
2309
|
+
const shadowRootCtor = (_d = (_b = (_a = element.ownerDocument) === null || _a === void 0 ? void 0 : _a.defaultView) === null || _b === void 0 ? void 0 : _b.ShadowRoot) !== null && _d !== void 0 ? _d : ShadowRoot;
|
|
2310
|
+
while (currentScope) {
|
|
2311
|
+
scopes.add(currentScope);
|
|
2312
|
+
if (currentScope instanceof shadowRootCtor) {
|
|
2313
|
+
currentScope = (_e = (_c = currentScope.host) === null || _c === void 0 ? void 0 : _c.getRootNode()) !== null && _e !== void 0 ? _e : null;
|
|
2314
|
+
}
|
|
2315
|
+
else {
|
|
2316
|
+
currentScope = currentScope.parentNode
|
|
2317
|
+
? currentScope.parentNode.getRootNode()
|
|
2318
|
+
: null;
|
|
2319
|
+
}
|
|
2320
|
+
}
|
|
2321
|
+
return scopes;
|
|
2322
|
+
}
|
|
2323
|
+
function offsetParentPolyfill(element) {
|
|
2324
|
+
// Do an initial walk to check for display:none ancestors.
|
|
2325
|
+
for (let ancestor = element; ancestor; ancestor = flatTreeParent(ancestor)) {
|
|
2326
|
+
if (!(ancestor instanceof Element))
|
|
2327
|
+
continue;
|
|
2328
|
+
if (getCachedComputedStyle(ancestor).display === 'none')
|
|
2329
|
+
return null;
|
|
2330
|
+
}
|
|
2331
|
+
for (let ancestor = flatTreeParent(element); ancestor; ancestor = flatTreeParent(ancestor)) {
|
|
2332
|
+
if (!(ancestor instanceof Element))
|
|
2333
|
+
continue;
|
|
2334
|
+
const style = getCachedComputedStyle(ancestor);
|
|
2335
|
+
if (style.display === 'contents')
|
|
2336
|
+
continue;
|
|
2337
|
+
if (style.position !== 'static' || isContainingBlock(style))
|
|
2338
|
+
return ancestor;
|
|
2339
|
+
if (ancestor.tagName === 'BODY')
|
|
2340
|
+
return ancestor;
|
|
2341
|
+
}
|
|
2342
|
+
return null;
|
|
2343
|
+
}
|
|
2344
|
+
/**
|
|
2345
|
+
*
|
|
2346
|
+
* @param {*} element
|
|
2347
|
+
* @param {'offsetTop' | 'offsetLeft'} offsetTopOrLeft
|
|
2348
|
+
* @returns
|
|
2349
|
+
*/
|
|
2350
|
+
function offsetTopLeftPolyfill(element, offsetTopOrLeft) {
|
|
2351
|
+
let value = element[offsetTopOrLeft];
|
|
2352
|
+
let nextOffsetParent = offsetParentPolyfill(element);
|
|
2353
|
+
const scopes = ancestorTreeScopes(element);
|
|
2354
|
+
while (nextOffsetParent && !scopes.has(nextOffsetParent.getRootNode())) {
|
|
2355
|
+
value -= nextOffsetParent[offsetTopOrLeft];
|
|
2356
|
+
nextOffsetParent = offsetParentPolyfill(nextOffsetParent);
|
|
2357
|
+
}
|
|
2358
|
+
return value;
|
|
2359
|
+
}
|
|
2360
|
+
/**
|
|
2361
|
+
* @param {Element} element
|
|
2362
|
+
* @returns {boolean}
|
|
2363
|
+
*/
|
|
2364
|
+
function createsFixedContainingBlock(element) {
|
|
2365
|
+
const cs = getCachedComputedStyle(element);
|
|
2366
|
+
if ((cs.transform && cs.transform !== 'none') ||
|
|
2367
|
+
(cs.perspective && cs.perspective !== 'none') ||
|
|
2368
|
+
(cs.filter && cs.filter !== 'none') ||
|
|
2369
|
+
(cs.backdropFilter && cs.backdropFilter !== 'none')) {
|
|
2370
|
+
return true;
|
|
2371
|
+
}
|
|
2372
|
+
const contain = cs.contain || '';
|
|
2373
|
+
if (contain.includes('paint') ||
|
|
2374
|
+
contain.includes('layout') ||
|
|
2375
|
+
contain.includes('strict') ||
|
|
2376
|
+
contain.includes('content')) {
|
|
2377
|
+
return true;
|
|
2378
|
+
}
|
|
2379
|
+
const willChange = cs.willChange || '';
|
|
2380
|
+
return /transform|perspective|filter|backdrop-filter/.test(willChange);
|
|
2381
|
+
}
|
|
2382
|
+
/**
|
|
2383
|
+
* @param {HTMLElement} element
|
|
2384
|
+
* @param {HTMLIFrameElement[]=} iframes
|
|
2385
|
+
* @returns {Element | null}
|
|
2386
|
+
*/
|
|
2387
|
+
function getNearestFixedContainingBlock(element, iframes) {
|
|
2388
|
+
let parent = getParentElementIncludingSlots(element, iframes);
|
|
2389
|
+
while (parent) {
|
|
2390
|
+
if (createsFixedContainingBlock(parent))
|
|
2391
|
+
return parent;
|
|
2392
|
+
parent = getParentElementIncludingSlots(parent, iframes);
|
|
2393
|
+
}
|
|
2394
|
+
return null;
|
|
2395
|
+
}
|