@lingxia/elements 0.14.0 → 0.16.0
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/index.d.ts +3 -2
- package/dist/index.js +1 -0
- package/dist/inline-native/commit.d.ts +39 -0
- package/dist/inline-native/commit.js +94 -0
- package/dist/inline-native/elements.d.ts +104 -0
- package/dist/inline-native/elements.js +749 -0
- package/dist/inline-native/errors.d.ts +8 -0
- package/dist/inline-native/errors.js +23 -0
- package/dist/inline-native/events.d.ts +10 -0
- package/dist/inline-native/events.js +16 -0
- package/dist/inline-native/geometry.d.ts +78 -0
- package/dist/inline-native/geometry.js +40 -0
- package/dist/inline-native/identity.d.ts +28 -0
- package/dist/inline-native/identity.js +51 -0
- package/dist/inline-native/index.d.ts +14 -0
- package/dist/inline-native/index.js +19 -0
- package/dist/inline-native/layout.d.ts +2 -0
- package/dist/inline-native/layout.js +58 -0
- package/dist/inline-native/lease.d.ts +13 -0
- package/dist/inline-native/lease.js +31 -0
- package/dist/inline-native/runtime.d.ts +64 -0
- package/dist/inline-native/runtime.js +119 -0
- package/dist/inline-native/schema.d.ts +111 -0
- package/dist/inline-native/schema.js +210 -0
- package/dist/inline-native/structure.d.ts +22 -0
- package/dist/inline-native/structure.js +706 -0
- package/dist/inline-native/style.d.ts +7 -0
- package/dist/inline-native/style.js +119 -0
- package/dist/inline-native/types.d.ts +82 -0
- package/dist/inline-native/types.js +7 -0
- package/dist/inline-native/unwrap.d.ts +10 -0
- package/dist/inline-native/unwrap.js +32 -0
- package/dist/inline-native/video.d.ts +65 -0
- package/dist/inline-native/video.js +69 -0
- package/dist/native_component_wrapper_shared.d.ts +3 -2
- package/dist/native_component_wrapper_shared.js +6 -3
- package/dist/nativecomponent.d.ts +1 -1
- package/dist/nativecomponent.js +8 -11
- package/dist/navigator.d.ts +2 -2
- package/dist/navigator.js +5 -5
- package/dist/picker.js +12 -4
- package/dist/video-events.d.ts +2 -0
- package/dist/video-events.js +18 -0
- package/dist/video.d.ts +98 -79
- package/dist/video.js +182 -757
- package/package.json +2 -1
- package/src/index.ts +58 -1
- package/src/inline-native/commit.ts +141 -0
- package/src/inline-native/elements.ts +829 -0
- package/src/inline-native/errors.ts +35 -0
- package/src/inline-native/events.ts +31 -0
- package/src/inline-native/geometry.ts +86 -0
- package/src/inline-native/identity.ts +102 -0
- package/src/inline-native/index.ts +103 -0
- package/src/inline-native/layout.ts +58 -0
- package/src/inline-native/lease.ts +48 -0
- package/src/inline-native/runtime.ts +185 -0
- package/src/inline-native/schema.ts +247 -0
- package/src/inline-native/structure.ts +823 -0
- package/src/inline-native/style.ts +111 -0
- package/src/inline-native/types.ts +105 -0
- package/src/inline-native/unwrap.ts +34 -0
- package/src/inline-native/video.ts +125 -0
- package/src/native_component_wrapper_shared.ts +8 -6
- package/src/nativecomponent.ts +8 -13
- package/src/navigator.ts +9 -9
- package/src/picker.ts +13 -4
- package/src/video-events.ts +16 -0
- package/src/video.ts +245 -839
package/dist/video.js
CHANGED
|
@@ -1,47 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import { normalizeVideoEventDetail } from "./video-events.js";
|
|
2
|
+
import { ensureComponentId } from "./component.js";
|
|
3
|
+
import { registerNativeComponentHandler } from "./nativecomponent.js";
|
|
4
|
+
import { findInlineNativeRoot } from "./inline-native/structure.js";
|
|
5
|
+
/**
|
|
6
|
+
* The DOM leaf for an inline-native video.
|
|
7
|
+
*
|
|
8
|
+
* Rendering, geometry and lifecycle are owned by the nearest LxNativeRoot.
|
|
9
|
+
* A bare lx-video is invalid and deliberately has no legacy overlay path.
|
|
10
|
+
*/
|
|
7
11
|
export class LxVideoElement extends HTMLElement {
|
|
8
12
|
constructor() {
|
|
9
13
|
super(...arguments);
|
|
10
14
|
this.componentId = null;
|
|
11
|
-
this.
|
|
12
|
-
this.
|
|
13
|
-
this.winSettleFrame = null;
|
|
14
|
-
this.pendingLayoutFrame = null;
|
|
15
|
-
this.boundUpdatePosition = this.updatePosition.bind(this);
|
|
16
|
-
this._handlers = {};
|
|
17
|
-
this.pendingHarmonyRetryTimer = null;
|
|
18
|
-
this.pendingHarmonyRetryCount = 0;
|
|
19
|
-
this.iOSBootstrapFrame = null;
|
|
20
|
-
this.iOSBootstrapRemaining = 0;
|
|
21
|
-
this.forceHarmonyEmbedRecreate = false;
|
|
22
|
-
this.resizeEventListener = {
|
|
23
|
-
handleEvent: () => this.boundUpdatePosition(),
|
|
24
|
-
};
|
|
25
|
-
this.iOSScrollEventListener = {
|
|
26
|
-
handleEvent: () => this.boundUpdatePosition(),
|
|
27
|
-
};
|
|
28
|
-
this.scrollSettleFrame = null;
|
|
29
|
-
// Scroll events do not bubble, so a plain window listener misses inner
|
|
30
|
-
// scroll containers; the capture phase sees them all. Any ancestor
|
|
31
|
-
// scrolling moves the element's document rect, so re-measure (coalesced
|
|
32
|
-
// to one update per frame; decide() dedups unchanged rects).
|
|
33
|
-
this.containerScrollListener = {
|
|
34
|
-
handleEvent: () => {
|
|
35
|
-
if (this.scrollSettleFrame !== null)
|
|
36
|
-
return;
|
|
37
|
-
this.scrollSettleFrame = requestAnimationFrame(() => {
|
|
38
|
-
this.scrollSettleFrame = null;
|
|
39
|
-
this.updatePosition();
|
|
40
|
-
});
|
|
41
|
-
},
|
|
42
|
-
};
|
|
15
|
+
this.islandPlaybackIdle = false;
|
|
16
|
+
this.handlers = {};
|
|
43
17
|
this.rawHandlers = {};
|
|
44
|
-
this.
|
|
18
|
+
this.bindings = {};
|
|
45
19
|
}
|
|
46
20
|
static get observedAttributes() {
|
|
47
21
|
return [
|
|
@@ -58,796 +32,247 @@ export class LxVideoElement extends HTMLElement {
|
|
|
58
32
|
"live",
|
|
59
33
|
"volume",
|
|
60
34
|
"qualities",
|
|
61
|
-
"playback-rates"
|
|
35
|
+
"playback-rates",
|
|
62
36
|
];
|
|
63
37
|
}
|
|
64
|
-
parseRotateValue(value) {
|
|
65
|
-
const parsed = (() => {
|
|
66
|
-
if (typeof value === "number") {
|
|
67
|
-
return Number.isInteger(value) ? value : NaN;
|
|
68
|
-
}
|
|
69
|
-
if (typeof value === "string") {
|
|
70
|
-
const normalized = value.trim();
|
|
71
|
-
if (!/^(0|90|180|270)$/.test(normalized))
|
|
72
|
-
return NaN;
|
|
73
|
-
return Number(normalized);
|
|
74
|
-
}
|
|
75
|
-
return NaN;
|
|
76
|
-
})();
|
|
77
|
-
if (parsed === 0 || parsed === 90 || parsed === 180 || parsed === 270) {
|
|
78
|
-
return parsed;
|
|
79
|
-
}
|
|
80
|
-
return undefined;
|
|
81
|
-
}
|
|
82
|
-
parseObjectFitValue(value) {
|
|
83
|
-
if (typeof value !== "string")
|
|
84
|
-
return undefined;
|
|
85
|
-
const normalized = value.trim().toLowerCase();
|
|
86
|
-
if (normalized === "cover" ||
|
|
87
|
-
normalized === "contain" ||
|
|
88
|
-
normalized === "fill" ||
|
|
89
|
-
normalized === "fit") {
|
|
90
|
-
return normalized;
|
|
91
|
-
}
|
|
92
|
-
return undefined;
|
|
93
|
-
}
|
|
94
38
|
set pageBindings(bindings) {
|
|
95
|
-
this.
|
|
96
|
-
if (this.isConnected)
|
|
97
|
-
this.
|
|
98
|
-
}
|
|
39
|
+
this.bindings = bindings !== null && bindings !== void 0 ? bindings : {};
|
|
40
|
+
if (this.isConnected)
|
|
41
|
+
this.requestRootCompile();
|
|
99
42
|
}
|
|
100
43
|
get pageBindings() {
|
|
101
|
-
return this.
|
|
44
|
+
return this.bindings;
|
|
45
|
+
}
|
|
46
|
+
get pageFuncBindings() {
|
|
47
|
+
return this.bindings;
|
|
48
|
+
}
|
|
49
|
+
get pageFuncBindingsJson() {
|
|
50
|
+
return JSON.stringify(this.bindings);
|
|
102
51
|
}
|
|
103
52
|
set src(value) {
|
|
104
53
|
if (value == null) {
|
|
105
54
|
this.removeAttribute("src");
|
|
106
|
-
return;
|
|
107
55
|
}
|
|
108
|
-
|
|
56
|
+
else {
|
|
57
|
+
this.setAttribute("src", String(value));
|
|
58
|
+
}
|
|
109
59
|
}
|
|
110
60
|
get src() {
|
|
111
61
|
return this.getAttribute("src");
|
|
112
62
|
}
|
|
113
|
-
dataAttrToDatasetKey(attr) {
|
|
114
|
-
const raw = attr.slice(5).trim();
|
|
115
|
-
if (!raw)
|
|
116
|
-
return "";
|
|
117
|
-
const parts = raw.split("-").filter(Boolean);
|
|
118
|
-
if (parts.length === 0)
|
|
119
|
-
return "";
|
|
120
|
-
return parts
|
|
121
|
-
.map((segment, index) => {
|
|
122
|
-
if (index === 0)
|
|
123
|
-
return segment.toLowerCase();
|
|
124
|
-
return segment.charAt(0).toUpperCase() + segment.slice(1);
|
|
125
|
-
})
|
|
126
|
-
.join("");
|
|
127
|
-
}
|
|
128
|
-
collectDataset() {
|
|
129
|
-
const dataset = {};
|
|
130
|
-
const attrs = this.getAttributeNames();
|
|
131
|
-
for (const attr of attrs) {
|
|
132
|
-
if (!attr.startsWith("data-"))
|
|
133
|
-
continue;
|
|
134
|
-
const key = this.dataAttrToDatasetKey(attr);
|
|
135
|
-
if (!key)
|
|
136
|
-
continue;
|
|
137
|
-
const value = this.getAttribute(attr);
|
|
138
|
-
if (value == null)
|
|
139
|
-
continue;
|
|
140
|
-
dataset[key] = value;
|
|
141
|
-
}
|
|
142
|
-
return dataset;
|
|
143
|
-
}
|
|
144
|
-
upgradeProperty(propName) {
|
|
145
|
-
const self = this;
|
|
146
|
-
if (!Object.prototype.hasOwnProperty.call(self, propName))
|
|
147
|
-
return;
|
|
148
|
-
const value = self[propName];
|
|
149
|
-
delete self[propName];
|
|
150
|
-
self[propName] = value;
|
|
151
|
-
}
|
|
152
63
|
set contentRotate(value) {
|
|
153
64
|
const normalized = this.parseRotateValue(value);
|
|
154
65
|
if (normalized === undefined) {
|
|
155
66
|
this.removeAttribute("content-rotate");
|
|
156
|
-
return;
|
|
157
67
|
}
|
|
158
|
-
|
|
68
|
+
else {
|
|
69
|
+
this.setAttribute("content-rotate", String(normalized));
|
|
70
|
+
}
|
|
159
71
|
}
|
|
160
72
|
get contentRotate() {
|
|
161
73
|
return this.parseRotateValue(this.getAttribute("content-rotate"));
|
|
162
74
|
}
|
|
163
75
|
connectedCallback() {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
76
|
+
for (const property of [
|
|
77
|
+
"pageBindings",
|
|
78
|
+
"contentRotate",
|
|
79
|
+
"src",
|
|
80
|
+
"onplayrequest",
|
|
81
|
+
"onplay",
|
|
82
|
+
"onplaying",
|
|
83
|
+
"onpause",
|
|
84
|
+
"onstop",
|
|
85
|
+
"onended",
|
|
86
|
+
"ontimeupdate",
|
|
87
|
+
"onloadedmetadata",
|
|
88
|
+
"onfullscreenchange",
|
|
89
|
+
"onwaiting",
|
|
90
|
+
"onqualitychange",
|
|
91
|
+
"onratechange",
|
|
92
|
+
]) {
|
|
93
|
+
this.upgradeProperty(property);
|
|
94
|
+
}
|
|
180
95
|
this.componentId = ensureComponentId(this, "lx-video", this.componentId);
|
|
181
|
-
|
|
96
|
+
this.registerEventBridge();
|
|
97
|
+
if (!findInlineNativeRoot(this)) {
|
|
98
|
+
queueMicrotask(() => {
|
|
99
|
+
if (!this.isConnected || findInlineNativeRoot(this))
|
|
100
|
+
return;
|
|
101
|
+
this.dispatchEvent(new CustomEvent("error", {
|
|
102
|
+
detail: {
|
|
103
|
+
code: "NATIVE_ROOT_INVALID_STRUCTURE",
|
|
104
|
+
message: "LxVideo must be a direct child of LxNativeRoot",
|
|
105
|
+
},
|
|
106
|
+
}));
|
|
107
|
+
});
|
|
108
|
+
this.ensurePlaceholderStyle();
|
|
182
109
|
return;
|
|
183
110
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
detail = {};
|
|
193
|
-
}
|
|
194
|
-
const ev = new CustomEvent(message.event, {
|
|
195
|
-
detail: detail,
|
|
196
|
-
bubbles: true,
|
|
197
|
-
cancelable: false
|
|
198
|
-
});
|
|
199
|
-
this.dispatchEvent(ev);
|
|
111
|
+
if (this.hasAttribute("autoplay")) {
|
|
112
|
+
const latchAutoplay = () => {
|
|
113
|
+
if (!this.isConnected || this.islandPlaybackIdle || !this.hasAttribute("autoplay"))
|
|
114
|
+
return;
|
|
115
|
+
this.setAttribute("data-lx-playing", "true");
|
|
116
|
+
};
|
|
117
|
+
if (typeof requestAnimationFrame === "function") {
|
|
118
|
+
requestAnimationFrame(latchAutoplay);
|
|
200
119
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
// Setup iOS native component rendering helper (no-op on other platforms)
|
|
204
|
-
this.iOSHelper = new iOSNativeComponentHelper(this, this.componentId);
|
|
205
|
-
this.iOSHelper.setup();
|
|
206
|
-
this.startAttrObserver();
|
|
207
|
-
// iOS bootstrap:
|
|
208
|
-
// avoid hardcoded timeout; run an immediate mount plus a few frame-based refreshes.
|
|
209
|
-
if (isIOS()) {
|
|
210
|
-
this.mountOrUpdate(true);
|
|
211
|
-
this.startTracking();
|
|
212
|
-
this.scheduleIOSBootstrapFrames();
|
|
213
|
-
}
|
|
214
|
-
else {
|
|
215
|
-
this.mountOrUpdate();
|
|
216
|
-
this.startTracking();
|
|
217
|
-
// Windows only: the WebView2 surface is created at an intermediate size
|
|
218
|
-
// and resized to its final content bounds shortly after this element
|
|
219
|
-
// mounts, which reflows the page and moves the element. Unlike the other
|
|
220
|
-
// platforms' webviews, WebView2 doesn't reliably fire the resize /
|
|
221
|
-
// ResizeObserver that would make us re-report, and an early update can be
|
|
222
|
-
// lost in the bridge transport during page load — so the native overlay
|
|
223
|
-
// can stay pinned at the mount-time rect. Re-measure + re-report across the
|
|
224
|
-
// settle window to land the final position. No-op on every other platform.
|
|
225
|
-
if (isWindows()) {
|
|
226
|
-
this.scheduleWindowsMountSettle();
|
|
120
|
+
else {
|
|
121
|
+
queueMicrotask(latchAutoplay);
|
|
227
122
|
}
|
|
228
123
|
}
|
|
124
|
+
this.ensurePlaceholderStyle();
|
|
229
125
|
}
|
|
230
126
|
disconnectedCallback() {
|
|
231
127
|
var _a;
|
|
232
|
-
this.
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
id: this.componentId
|
|
237
|
-
});
|
|
128
|
+
(_a = this.unregister) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
129
|
+
this.unregister = undefined;
|
|
130
|
+
for (const [name, handler] of Object.entries(this.handlers)) {
|
|
131
|
+
this.removeEventListener(name, handler);
|
|
238
132
|
}
|
|
239
|
-
this.
|
|
240
|
-
if (this.unregister) {
|
|
241
|
-
this.unregister();
|
|
242
|
-
this.unregister = undefined;
|
|
243
|
-
}
|
|
244
|
-
if (this.harmonyEmbed && this.contains(this.harmonyEmbed)) {
|
|
245
|
-
this.removeChild(this.harmonyEmbed);
|
|
246
|
-
}
|
|
247
|
-
this.harmonyEmbed = undefined;
|
|
248
|
-
this.lastHarmonyProps = undefined;
|
|
249
|
-
// Cleanup iOS helper
|
|
250
|
-
(_a = this.iOSHelper) === null || _a === void 0 ? void 0 : _a.cleanup();
|
|
251
|
-
this.iOSHelper = undefined;
|
|
252
|
-
this.stopAttrObserver();
|
|
253
|
-
// Cleanup manually attached property handlers
|
|
254
|
-
Object.keys(this._handlers).forEach(name => {
|
|
255
|
-
this.removeEventListener(name, this._handlers[name]);
|
|
256
|
-
});
|
|
257
|
-
this._handlers = {};
|
|
133
|
+
this.handlers = {};
|
|
258
134
|
this.rawHandlers = {};
|
|
259
135
|
}
|
|
260
136
|
attributeChangedCallback(name) {
|
|
261
|
-
if (name === "id") {
|
|
262
|
-
const
|
|
137
|
+
if (name === "id" && this.isConnected) {
|
|
138
|
+
const previous = this.componentId;
|
|
263
139
|
this.componentId = ensureComponentId(this, "lx-video", this.componentId);
|
|
264
|
-
if (
|
|
265
|
-
this.
|
|
266
|
-
this.updateState.reset();
|
|
267
|
-
}
|
|
140
|
+
if (previous !== this.componentId)
|
|
141
|
+
this.registerEventBridge();
|
|
268
142
|
}
|
|
269
|
-
if (
|
|
270
|
-
return;
|
|
271
|
-
if (name === "poster" || name === "object-fit" || name === "objectfit") {
|
|
143
|
+
if (name === "poster" || name === "object-fit") {
|
|
272
144
|
this.syncPosterPlaceholder();
|
|
273
145
|
}
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
this.
|
|
146
|
+
}
|
|
147
|
+
registerEventBridge() {
|
|
148
|
+
var _a;
|
|
149
|
+
(_a = this.unregister) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
150
|
+
if (!this.componentId)
|
|
151
|
+
return;
|
|
152
|
+
this.unregister = registerNativeComponentHandler(this.componentId, (message) => {
|
|
153
|
+
if (!message.event)
|
|
154
|
+
return;
|
|
155
|
+
const detail = normalizeVideoEventDetail(message.event, message.detail || message.payload || {});
|
|
156
|
+
if (message.event === "playing" || message.event === "play") {
|
|
157
|
+
this.islandPlaybackIdle = false;
|
|
158
|
+
this.setAttribute("data-lx-playing", "true");
|
|
159
|
+
}
|
|
160
|
+
else if (["pause", "stop", "ended"].includes(message.event)) {
|
|
161
|
+
this.islandPlaybackIdle = true;
|
|
162
|
+
this.removeAttribute("data-lx-playing");
|
|
163
|
+
}
|
|
164
|
+
this.dispatchEvent(new CustomEvent(message.event, {
|
|
165
|
+
detail,
|
|
166
|
+
bubbles: true,
|
|
167
|
+
cancelable: false,
|
|
168
|
+
}));
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
requestRootCompile() {
|
|
172
|
+
var _a;
|
|
173
|
+
const root = findInlineNativeRoot(this);
|
|
174
|
+
void ((_a = root === null || root === void 0 ? void 0 : root.retry) === null || _a === void 0 ? void 0 : _a.call(root));
|
|
175
|
+
}
|
|
176
|
+
upgradeProperty(property) {
|
|
177
|
+
const self = this;
|
|
178
|
+
if (!Object.prototype.hasOwnProperty.call(self, property))
|
|
179
|
+
return;
|
|
180
|
+
const value = self[property];
|
|
181
|
+
delete self[property];
|
|
182
|
+
self[property] = value;
|
|
183
|
+
}
|
|
184
|
+
parseRotateValue(value) {
|
|
185
|
+
const parsed = typeof value === "number"
|
|
186
|
+
? value
|
|
187
|
+
: typeof value === "string" && /^(0|90|180|270)$/.test(value.trim())
|
|
188
|
+
? Number(value)
|
|
189
|
+
: Number.NaN;
|
|
190
|
+
return parsed === 0 || parsed === 90 || parsed === 180 || parsed === 270
|
|
191
|
+
? parsed
|
|
192
|
+
: undefined;
|
|
193
|
+
}
|
|
194
|
+
parseObjectFitValue(value) {
|
|
195
|
+
if (typeof value !== "string")
|
|
196
|
+
return undefined;
|
|
197
|
+
const normalized = value.trim().toLowerCase();
|
|
198
|
+
return normalized === "cover" ||
|
|
199
|
+
normalized === "contain" ||
|
|
200
|
+
normalized === "fill" ||
|
|
201
|
+
normalized === "fit"
|
|
202
|
+
? normalized
|
|
203
|
+
: undefined;
|
|
281
204
|
}
|
|
282
205
|
isEventListener(value) {
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
return typeof value.handleEvent === "function";
|
|
206
|
+
return (typeof value === "function" ||
|
|
207
|
+
(!!value &&
|
|
208
|
+
typeof value === "object" &&
|
|
209
|
+
typeof value.handleEvent === "function"));
|
|
288
210
|
}
|
|
289
211
|
setEventHandler(name, value) {
|
|
290
|
-
const current = this.
|
|
291
|
-
if (current)
|
|
212
|
+
const current = this.handlers[name];
|
|
213
|
+
if (current)
|
|
292
214
|
this.removeEventListener(name, current);
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
const listener = typeof value === "function"
|
|
296
|
-
? { handleEvent: value }
|
|
297
|
-
: value;
|
|
298
|
-
this._handlers[name] = listener;
|
|
299
|
-
this.rawHandlers[name] = value;
|
|
300
|
-
this.addEventListener(name, listener);
|
|
301
|
-
}
|
|
302
|
-
else {
|
|
303
|
-
delete this._handlers[name];
|
|
215
|
+
if (!this.isEventListener(value)) {
|
|
216
|
+
delete this.handlers[name];
|
|
304
217
|
delete this.rawHandlers[name];
|
|
218
|
+
return;
|
|
305
219
|
}
|
|
220
|
+
const listener = typeof value === "function" ? { handleEvent: value } : value;
|
|
221
|
+
this.handlers[name] = listener;
|
|
222
|
+
this.rawHandlers[name] = value;
|
|
223
|
+
this.addEventListener(name, listener);
|
|
306
224
|
}
|
|
307
225
|
getEventHandler(name) {
|
|
308
|
-
return
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
get onqualitychange() { return this.getEventHandler('qualitychange'); }
|
|
335
|
-
set onratechange(cb) { this.setEventHandler('ratechange', cb); }
|
|
336
|
-
get onratechange() { return this.getEventHandler('ratechange'); }
|
|
337
|
-
// Internal
|
|
226
|
+
return this.rawHandlers[name] || null;
|
|
227
|
+
}
|
|
228
|
+
set onplayrequest(value) { this.setEventHandler("playrequest", value); }
|
|
229
|
+
get onplayrequest() { return this.getEventHandler("playrequest"); }
|
|
230
|
+
set onplay(value) { this.setEventHandler("play", value); }
|
|
231
|
+
get onplay() { return this.getEventHandler("play"); }
|
|
232
|
+
set onplaying(value) { this.setEventHandler("playing", value); }
|
|
233
|
+
get onplaying() { return this.getEventHandler("playing"); }
|
|
234
|
+
set onpause(value) { this.setEventHandler("pause", value); }
|
|
235
|
+
get onpause() { return this.getEventHandler("pause"); }
|
|
236
|
+
set onstop(value) { this.setEventHandler("stop", value); }
|
|
237
|
+
get onstop() { return this.getEventHandler("stop"); }
|
|
238
|
+
set onended(value) { this.setEventHandler("ended", value); }
|
|
239
|
+
get onended() { return this.getEventHandler("ended"); }
|
|
240
|
+
set ontimeupdate(value) { this.setEventHandler("timeupdate", value); }
|
|
241
|
+
get ontimeupdate() { return this.getEventHandler("timeupdate"); }
|
|
242
|
+
set onloadedmetadata(value) { this.setEventHandler("loadedmetadata", value); }
|
|
243
|
+
get onloadedmetadata() { return this.getEventHandler("loadedmetadata"); }
|
|
244
|
+
set onfullscreenchange(value) { this.setEventHandler("fullscreenchange", value); }
|
|
245
|
+
get onfullscreenchange() { return this.getEventHandler("fullscreenchange"); }
|
|
246
|
+
set onwaiting(value) { this.setEventHandler("waiting", value); }
|
|
247
|
+
get onwaiting() { return this.getEventHandler("waiting"); }
|
|
248
|
+
set onqualitychange(value) { this.setEventHandler("qualitychange", value); }
|
|
249
|
+
get onqualitychange() { return this.getEventHandler("qualitychange"); }
|
|
250
|
+
set onratechange(value) { this.setEventHandler("ratechange", value); }
|
|
251
|
+
get onratechange() { return this.getEventHandler("ratechange"); }
|
|
338
252
|
ensurePlaceholderStyle() {
|
|
339
253
|
if (!this.style.display)
|
|
340
254
|
this.style.display = "block";
|
|
341
255
|
if (!this.style.position)
|
|
342
|
-
this.style.position = "relative";
|
|
256
|
+
this.style.position = "relative";
|
|
343
257
|
if (!this.style.backgroundColor)
|
|
344
258
|
this.style.backgroundColor = "black";
|
|
345
259
|
if (!this.style.aspectRatio)
|
|
346
260
|
this.style.aspectRatio = "16 / 9";
|
|
347
261
|
this.syncPosterPlaceholder();
|
|
348
262
|
}
|
|
349
|
-
/// The poster doubles as the element's CSS background: on platforms
|
|
350
|
-
/// where the native layer hides while no media is presented (Windows:
|
|
351
|
-
/// initial state, after stop, on error), the placeholder shows it.
|
|
352
263
|
syncPosterPlaceholder() {
|
|
353
|
-
var _a;
|
|
354
264
|
const poster = this.getAttribute("poster");
|
|
355
265
|
if (!poster) {
|
|
356
266
|
this.style.backgroundImage = "";
|
|
357
267
|
return;
|
|
358
268
|
}
|
|
359
|
-
const objectFit = this.parseObjectFitValue(
|
|
269
|
+
const objectFit = this.parseObjectFitValue(this.getAttribute("object-fit"));
|
|
360
270
|
const size = objectFit === "cover" ? "cover" : objectFit === "fill" ? "100% 100%" : "contain";
|
|
361
271
|
this.style.backgroundImage = `url("${poster.replace(/"/g, '\\"')}")`;
|
|
362
272
|
this.style.backgroundSize = size;
|
|
363
273
|
this.style.backgroundPosition = "center";
|
|
364
274
|
this.style.backgroundRepeat = "no-repeat";
|
|
365
275
|
}
|
|
366
|
-
collectProps() {
|
|
367
|
-
var _a;
|
|
368
|
-
const volumeAttr = this.getAttribute("volume");
|
|
369
|
-
const volume = volumeAttr != null ? parseFloat(volumeAttr) : undefined;
|
|
370
|
-
const rawObjectFit = (_a = this.getAttribute("object-fit")) !== null && _a !== void 0 ? _a : this.getAttribute("objectfit");
|
|
371
|
-
const objectFit = this.parseObjectFitValue(rawObjectFit);
|
|
372
|
-
// JSON-encoded arrays (React wrapper sets these via JSON.stringify)
|
|
373
|
-
const qualitiesAttr = this.getAttribute("qualities");
|
|
374
|
-
let qualities;
|
|
375
|
-
if (qualitiesAttr) {
|
|
376
|
-
try {
|
|
377
|
-
const parsed = JSON.parse(qualitiesAttr);
|
|
378
|
-
if (Array.isArray(parsed)) {
|
|
379
|
-
qualities = parsed.filter((q) => q && typeof q === "object" && typeof q.label === "string");
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
catch { }
|
|
383
|
-
}
|
|
384
|
-
// Parse playbackRates array from JSON attribute
|
|
385
|
-
const playbackRatesAttr = this.getAttribute("playback-rates");
|
|
386
|
-
let playbackRates;
|
|
387
|
-
if (playbackRatesAttr) {
|
|
388
|
-
try {
|
|
389
|
-
const parsed = JSON.parse(playbackRatesAttr);
|
|
390
|
-
if (Array.isArray(parsed)) {
|
|
391
|
-
playbackRates = parsed.filter((n) => typeof n === "number");
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
catch { }
|
|
395
|
-
}
|
|
396
|
-
const isLive = this.hasAttribute("live");
|
|
397
|
-
// When live, default progressBar to false unless explicitly enabled.
|
|
398
|
-
const progressBarAttr = this.getAttribute("progress-bar");
|
|
399
|
-
const progressBar = isLive
|
|
400
|
-
? (progressBarAttr !== null && progressBarAttr !== "false")
|
|
401
|
-
: (progressBarAttr !== "false");
|
|
402
|
-
const rawContentRotate = this.getAttribute("content-rotate");
|
|
403
|
-
const validContentRotate = this.parseRotateValue(rawContentRotate);
|
|
404
|
-
const pageFuncBindings = this._pageBindings;
|
|
405
|
-
const dataset = this.collectDataset();
|
|
406
|
-
const clearProps = [];
|
|
407
|
-
if (rawContentRotate === null || validContentRotate === undefined) {
|
|
408
|
-
clearProps.push("contentRotate");
|
|
409
|
-
}
|
|
410
|
-
if (rawObjectFit == null || objectFit === undefined) {
|
|
411
|
-
clearProps.push("objectFit");
|
|
412
|
-
}
|
|
413
|
-
return {
|
|
414
|
-
src: this.getAttribute("src") || undefined,
|
|
415
|
-
poster: this.getAttribute("poster") || undefined,
|
|
416
|
-
autoplay: this.hasAttribute("autoplay"),
|
|
417
|
-
loop: this.hasAttribute("loop"),
|
|
418
|
-
muted: this.hasAttribute("muted"),
|
|
419
|
-
controls: this.hasAttribute("controls"),
|
|
420
|
-
progressBar,
|
|
421
|
-
live: isLive,
|
|
422
|
-
volume: !Number.isNaN(volume !== null && volume !== void 0 ? volume : NaN) ? volume : undefined,
|
|
423
|
-
objectFit,
|
|
424
|
-
contentRotate: validContentRotate,
|
|
425
|
-
...(clearProps.length > 0 ? { __clearProps: clearProps } : {}),
|
|
426
|
-
qualities,
|
|
427
|
-
playbackRates,
|
|
428
|
-
dataset,
|
|
429
|
-
datasetJson: JSON.stringify(dataset),
|
|
430
|
-
pageFuncBindings,
|
|
431
|
-
pageFuncBindingsJson: JSON.stringify(pageFuncBindings)
|
|
432
|
-
};
|
|
433
|
-
}
|
|
434
|
-
shouldRefreshForAttribute(name) {
|
|
435
|
-
const normalized = name.trim().toLowerCase();
|
|
436
|
-
return normalized.startsWith("data-");
|
|
437
|
-
}
|
|
438
|
-
startAttrObserver() {
|
|
439
|
-
if (typeof MutationObserver === "undefined" || this.attrObserver) {
|
|
440
|
-
return;
|
|
441
|
-
}
|
|
442
|
-
this.attrObserver = new MutationObserver((mutations) => {
|
|
443
|
-
for (const mutation of mutations) {
|
|
444
|
-
const attrName = mutation.attributeName;
|
|
445
|
-
if (!attrName || !this.shouldRefreshForAttribute(attrName)) {
|
|
446
|
-
continue;
|
|
447
|
-
}
|
|
448
|
-
this.mountOrUpdate(true);
|
|
449
|
-
break;
|
|
450
|
-
}
|
|
451
|
-
});
|
|
452
|
-
this.attrObserver.observe(this, { attributes: true });
|
|
453
|
-
}
|
|
454
|
-
stopAttrObserver() {
|
|
455
|
-
if (!this.attrObserver) {
|
|
456
|
-
return;
|
|
457
|
-
}
|
|
458
|
-
this.attrObserver.disconnect();
|
|
459
|
-
this.attrObserver = undefined;
|
|
460
|
-
}
|
|
461
|
-
measureForNative() {
|
|
462
|
-
const measured = measureElement(this);
|
|
463
|
-
if (!isIOS()) {
|
|
464
|
-
return measured;
|
|
465
|
-
}
|
|
466
|
-
const rect = this.getBoundingClientRect();
|
|
467
|
-
return {
|
|
468
|
-
rect: {
|
|
469
|
-
x: rect.left,
|
|
470
|
-
y: rect.top,
|
|
471
|
-
width: rect.width,
|
|
472
|
-
height: rect.height
|
|
473
|
-
},
|
|
474
|
-
cornerRadius: measured.cornerRadius
|
|
475
|
-
};
|
|
476
|
-
}
|
|
477
|
-
mountOrUpdate(forceUpdate = false) {
|
|
478
|
-
var _a;
|
|
479
|
-
if (isHarmony()) {
|
|
480
|
-
this.mountOrUpdateHarmony(forceUpdate);
|
|
481
|
-
return;
|
|
482
|
-
}
|
|
483
|
-
if (!this.componentId)
|
|
484
|
-
return;
|
|
485
|
-
const { rect, cornerRadius } = this.measureForNative();
|
|
486
|
-
const hasSize = rect.width > 0 && rect.height > 0;
|
|
487
|
-
if (!hasSize) {
|
|
488
|
-
// Defer and try again once layout stabilizes; only needed before first mount
|
|
489
|
-
if (!this.mounted) {
|
|
490
|
-
this.scheduleMountRetry();
|
|
491
|
-
}
|
|
492
|
-
return;
|
|
493
|
-
}
|
|
494
|
-
const props = this.collectProps();
|
|
495
|
-
if (cornerRadius !== undefined) {
|
|
496
|
-
props.cornerRadius = cornerRadius;
|
|
497
|
-
}
|
|
498
|
-
const zIndex = parseFloat(this.style.zIndex || "0") || 0;
|
|
499
|
-
const decision = this.updateState.decide(rect, props, zIndex, !this.mounted || forceUpdate);
|
|
500
|
-
if (!decision.shouldSend)
|
|
501
|
-
return;
|
|
502
|
-
const payload = {
|
|
503
|
-
action: this.mounted ? "component.update" : "component.mount",
|
|
504
|
-
id: this.componentId,
|
|
505
|
-
type: "video.native",
|
|
506
|
-
rect,
|
|
507
|
-
zIndex,
|
|
508
|
-
...(cornerRadius !== undefined ? { cornerRadius } : {})
|
|
509
|
-
};
|
|
510
|
-
// Enhance payload with platform-specific data (iOS scroll container rect)
|
|
511
|
-
(_a = this.iOSHelper) === null || _a === void 0 ? void 0 : _a.enhancePayload(payload);
|
|
512
|
-
// Only include props when they changed or on first mount
|
|
513
|
-
if (decision.propsChanged || !this.mounted) {
|
|
514
|
-
payload.props = props;
|
|
515
|
-
}
|
|
516
|
-
sendNativeComponentMessage(payload);
|
|
517
|
-
this.mounted = true;
|
|
518
|
-
}
|
|
519
|
-
updatePosition() {
|
|
520
|
-
var _a;
|
|
521
|
-
if (isHarmony()) {
|
|
522
|
-
this.mountOrUpdateHarmony();
|
|
523
|
-
return;
|
|
524
|
-
}
|
|
525
|
-
if (!this.mounted || !this.componentId)
|
|
526
|
-
return;
|
|
527
|
-
const { rect, cornerRadius } = this.measureForNative();
|
|
528
|
-
if (!rect.width || !rect.height)
|
|
529
|
-
return;
|
|
530
|
-
const zIndex = parseFloat(this.style.zIndex || "0") || 0;
|
|
531
|
-
const decision = this.updateState.decide(rect, null, zIndex);
|
|
532
|
-
if (!decision.shouldSend)
|
|
533
|
-
return;
|
|
534
|
-
const payload = {
|
|
535
|
-
action: "component.update",
|
|
536
|
-
id: this.componentId,
|
|
537
|
-
rect,
|
|
538
|
-
zIndex
|
|
539
|
-
};
|
|
540
|
-
// Keep iOS scroll-container metadata on update so native side can still
|
|
541
|
-
// promote fallback overlay into WKChildScrollView when it becomes available.
|
|
542
|
-
(_a = this.iOSHelper) === null || _a === void 0 ? void 0 : _a.enhancePayload(payload);
|
|
543
|
-
if (cornerRadius !== undefined) {
|
|
544
|
-
payload.cornerRadius = cornerRadius;
|
|
545
|
-
}
|
|
546
|
-
sendNativeComponentMessage(payload);
|
|
547
|
-
}
|
|
548
|
-
/**
|
|
549
|
-
* Start tracking component size changes.
|
|
550
|
-
*/
|
|
551
|
-
startTracking() {
|
|
552
|
-
window.addEventListener("resize", this.resizeEventListener);
|
|
553
|
-
if (isIOS()) {
|
|
554
|
-
window.addEventListener("scroll", this.iOSScrollEventListener, { passive: true });
|
|
555
|
-
}
|
|
556
|
-
else if (isWindows()) {
|
|
557
|
-
window.addEventListener("scroll", this.containerScrollListener, {
|
|
558
|
-
capture: true,
|
|
559
|
-
passive: true,
|
|
560
|
-
});
|
|
561
|
-
}
|
|
562
|
-
if (isAndroid()) {
|
|
563
|
-
this.removeLayoutInvalidationListener = addNativeComponentLayoutInvalidationListener(this.resizeEventListener);
|
|
564
|
-
}
|
|
565
|
-
this.startSizeObserver();
|
|
566
|
-
this.updatePosition();
|
|
567
|
-
}
|
|
568
|
-
stopTracking() {
|
|
569
|
-
var _a;
|
|
570
|
-
window.removeEventListener("resize", this.resizeEventListener);
|
|
571
|
-
if (isIOS()) {
|
|
572
|
-
window.removeEventListener("scroll", this.iOSScrollEventListener);
|
|
573
|
-
}
|
|
574
|
-
else if (isWindows()) {
|
|
575
|
-
window.removeEventListener("scroll", this.containerScrollListener, { capture: true });
|
|
576
|
-
}
|
|
577
|
-
(_a = this.removeLayoutInvalidationListener) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
578
|
-
this.removeLayoutInvalidationListener = undefined;
|
|
579
|
-
this.stopSizeObserver();
|
|
580
|
-
if (this.scrollSettleFrame !== null) {
|
|
581
|
-
cancelAnimationFrame(this.scrollSettleFrame);
|
|
582
|
-
this.scrollSettleFrame = null;
|
|
583
|
-
}
|
|
584
|
-
if (this.winSettleFrame !== null) {
|
|
585
|
-
cancelAnimationFrame(this.winSettleFrame);
|
|
586
|
-
this.winSettleFrame = null;
|
|
587
|
-
}
|
|
588
|
-
if (this.pendingLayoutFrame !== null) {
|
|
589
|
-
cancelAnimationFrame(this.pendingLayoutFrame);
|
|
590
|
-
this.pendingLayoutFrame = null;
|
|
591
|
-
}
|
|
592
|
-
if (this.pendingHarmonyRetryTimer !== null) {
|
|
593
|
-
clearTimeout(this.pendingHarmonyRetryTimer);
|
|
594
|
-
this.pendingHarmonyRetryTimer = null;
|
|
595
|
-
}
|
|
596
|
-
if (this.iOSBootstrapFrame !== null) {
|
|
597
|
-
cancelAnimationFrame(this.iOSBootstrapFrame);
|
|
598
|
-
this.iOSBootstrapFrame = null;
|
|
599
|
-
}
|
|
600
|
-
this.iOSBootstrapRemaining = 0;
|
|
601
|
-
this.pendingHarmonyProps = undefined;
|
|
602
|
-
this.pendingHarmonyRetryCount = 0;
|
|
603
|
-
}
|
|
604
|
-
startSizeObserver() {
|
|
605
|
-
if (typeof ResizeObserver === "undefined") {
|
|
606
|
-
// ResizeObserver unavailable: retry mount on next frame.
|
|
607
|
-
this.scheduleMountRetry();
|
|
608
|
-
return;
|
|
609
|
-
}
|
|
610
|
-
if (this.resizeObserver)
|
|
611
|
-
return;
|
|
612
|
-
this.resizeObserver = new ResizeObserver((entries) => {
|
|
613
|
-
for (const entry of entries) {
|
|
614
|
-
if (entry.target !== this)
|
|
615
|
-
continue;
|
|
616
|
-
const { width, height } = entry.contentRect;
|
|
617
|
-
if (!width || !height)
|
|
618
|
-
continue;
|
|
619
|
-
if (this.mounted) {
|
|
620
|
-
this.updatePosition();
|
|
621
|
-
}
|
|
622
|
-
else {
|
|
623
|
-
this.mountOrUpdate();
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
});
|
|
627
|
-
this.resizeObserver.observe(this);
|
|
628
|
-
}
|
|
629
|
-
stopSizeObserver() {
|
|
630
|
-
if (this.resizeObserver) {
|
|
631
|
-
this.resizeObserver.disconnect();
|
|
632
|
-
this.resizeObserver = undefined;
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
scheduleMountRetry() {
|
|
636
|
-
if (this.pendingLayoutFrame !== null || this.mounted)
|
|
637
|
-
return;
|
|
638
|
-
this.pendingLayoutFrame = requestAnimationFrame(() => {
|
|
639
|
-
this.pendingLayoutFrame = null;
|
|
640
|
-
this.mountOrUpdate();
|
|
641
|
-
});
|
|
642
|
-
}
|
|
643
|
-
scheduleWindowsMountSettle() {
|
|
644
|
-
if (typeof requestAnimationFrame === "undefined")
|
|
645
|
-
return;
|
|
646
|
-
if (this.winSettleFrame !== null)
|
|
647
|
-
return;
|
|
648
|
-
// Re-measure across the page-load / WebView2-resize settle window. The
|
|
649
|
-
// reflow that moves this element can land well after mount, so keep checking
|
|
650
|
-
// until the rect has been stable for a while; decide() dedups, so updates
|
|
651
|
-
// only fire when the rect changes.
|
|
652
|
-
let lastKey = "";
|
|
653
|
-
let stable = 0;
|
|
654
|
-
let elapsed = 0;
|
|
655
|
-
const tick = () => {
|
|
656
|
-
this.winSettleFrame = null;
|
|
657
|
-
if (!this.isConnected)
|
|
658
|
-
return;
|
|
659
|
-
// Force the re-send (don't dedup): an early update can be lost in the
|
|
660
|
-
// bridge transport during page load, and once the element has settled a
|
|
661
|
-
// dedup'd re-measure sees "no change" and never re-delivers it. Forcing
|
|
662
|
-
// each frame re-delivers the current rect until it lands.
|
|
663
|
-
this.mountOrUpdate(true);
|
|
664
|
-
const r = this.getBoundingClientRect();
|
|
665
|
-
const key = `${Math.round(r.top)}:${Math.round(r.width)}`;
|
|
666
|
-
if (key === lastKey) {
|
|
667
|
-
stable += 1;
|
|
668
|
-
}
|
|
669
|
-
else {
|
|
670
|
-
stable = 0;
|
|
671
|
-
lastKey = key;
|
|
672
|
-
}
|
|
673
|
-
elapsed += 1;
|
|
674
|
-
// Keep re-sending until the layout has held steady for a while (so a late
|
|
675
|
-
// settle is still caught), capped so it never runs unbounded.
|
|
676
|
-
if (stable < 12 && elapsed < 180) {
|
|
677
|
-
this.winSettleFrame = requestAnimationFrame(tick);
|
|
678
|
-
}
|
|
679
|
-
};
|
|
680
|
-
this.winSettleFrame = requestAnimationFrame(tick);
|
|
681
|
-
}
|
|
682
|
-
scheduleIOSBootstrapFrames() {
|
|
683
|
-
if (!isIOS())
|
|
684
|
-
return;
|
|
685
|
-
// Keep a short frame-based bootstrap window so late-created WKChildScrollView
|
|
686
|
-
// can still receive mount/update without relying on hardcoded millisecond delay.
|
|
687
|
-
this.iOSBootstrapRemaining = 8;
|
|
688
|
-
const tick = () => {
|
|
689
|
-
if (!this.isConnected) {
|
|
690
|
-
this.iOSBootstrapFrame = null;
|
|
691
|
-
this.iOSBootstrapRemaining = 0;
|
|
692
|
-
return;
|
|
693
|
-
}
|
|
694
|
-
this.mountOrUpdate(true);
|
|
695
|
-
this.iOSBootstrapRemaining -= 1;
|
|
696
|
-
if (this.iOSBootstrapRemaining <= 0) {
|
|
697
|
-
this.iOSBootstrapFrame = null;
|
|
698
|
-
return;
|
|
699
|
-
}
|
|
700
|
-
this.iOSBootstrapFrame = requestAnimationFrame(tick);
|
|
701
|
-
};
|
|
702
|
-
this.iOSBootstrapFrame = requestAnimationFrame(tick);
|
|
703
|
-
}
|
|
704
|
-
mountOrUpdateHarmony(forceUpdate = false) {
|
|
705
|
-
if (!this.componentId)
|
|
706
|
-
return;
|
|
707
|
-
const { rect, cornerRadius } = measureElement(this);
|
|
708
|
-
const hasSize = rect.width > 0 && rect.height > 0;
|
|
709
|
-
if (!hasSize) {
|
|
710
|
-
if (!this.mounted) {
|
|
711
|
-
this.scheduleMountRetry();
|
|
712
|
-
}
|
|
713
|
-
return;
|
|
714
|
-
}
|
|
715
|
-
const props = this.collectProps();
|
|
716
|
-
if (cornerRadius !== undefined) {
|
|
717
|
-
props.cornerRadius = cornerRadius;
|
|
718
|
-
}
|
|
719
|
-
const zIndex = parseFloat(this.style.zIndex || "0") || 0;
|
|
720
|
-
const decision = this.updateState.decide(rect, props, zIndex, !this.mounted || forceUpdate);
|
|
721
|
-
if (!decision.shouldSend)
|
|
722
|
-
return;
|
|
723
|
-
this.ensureHarmonyEmbed(rect, props, cornerRadius, this.forceHarmonyEmbedRecreate);
|
|
724
|
-
this.forceHarmonyEmbedRecreate = false;
|
|
725
|
-
this.mounted = true;
|
|
726
|
-
}
|
|
727
|
-
ensureHarmonyEmbed(rect, props, cornerRadius, forceRecreate = false) {
|
|
728
|
-
const createEmbed = () => {
|
|
729
|
-
const embed = document.createElement("embed");
|
|
730
|
-
embed.setAttribute("type", "native/video");
|
|
731
|
-
embed.setAttribute("width", `${rect.width}`);
|
|
732
|
-
embed.setAttribute("height", `${rect.height}`);
|
|
733
|
-
embed.setAttribute("id", this.componentId);
|
|
734
|
-
embed.setAttribute("data-lx-component-id", this.componentId);
|
|
735
|
-
embed.style.display = "block";
|
|
736
|
-
embed.style.width = "100%";
|
|
737
|
-
embed.style.height = "100%";
|
|
738
|
-
embed.style.border = "none";
|
|
739
|
-
if (cornerRadius !== undefined) {
|
|
740
|
-
embed.style.borderRadius = `${cornerRadius}px`;
|
|
741
|
-
embed.style.overflow = "hidden";
|
|
742
|
-
}
|
|
743
|
-
const encodedProps = this.encodeHarmonyProps(props);
|
|
744
|
-
embed.setAttribute("src", encodedProps);
|
|
745
|
-
this.lastHarmonyProps = encodedProps;
|
|
746
|
-
return embed;
|
|
747
|
-
};
|
|
748
|
-
if (forceRecreate && this.harmonyEmbed) {
|
|
749
|
-
const oldEmbed = this.harmonyEmbed;
|
|
750
|
-
this.harmonyEmbed = undefined;
|
|
751
|
-
if (this.contains(oldEmbed)) {
|
|
752
|
-
this.removeChild(oldEmbed);
|
|
753
|
-
}
|
|
754
|
-
this.lastHarmonyProps = undefined;
|
|
755
|
-
const recreated = createEmbed();
|
|
756
|
-
this.appendChild(recreated);
|
|
757
|
-
this.harmonyEmbed = recreated;
|
|
758
|
-
this.pushHarmonyPropsUpdate(props);
|
|
759
|
-
this.pendingHarmonyRetryCount = 0;
|
|
760
|
-
return;
|
|
761
|
-
}
|
|
762
|
-
// Create embed element only once - ArkWeb triggers DESTROY+CREATE on attribute changes
|
|
763
|
-
if (!this.harmonyEmbed) {
|
|
764
|
-
const embed = createEmbed();
|
|
765
|
-
this.pushHarmonyPropsUpdate(props);
|
|
766
|
-
this.appendChild(embed);
|
|
767
|
-
this.harmonyEmbed = embed;
|
|
768
|
-
return;
|
|
769
|
-
}
|
|
770
|
-
// Only update props when they changed.
|
|
771
|
-
const encodedProps = this.encodeHarmonyProps(props);
|
|
772
|
-
if (encodedProps !== this.lastHarmonyProps) {
|
|
773
|
-
const pushed = this.pushHarmonyPropsUpdate(props);
|
|
774
|
-
if (pushed) {
|
|
775
|
-
this.lastHarmonyProps = encodedProps;
|
|
776
|
-
this.pendingHarmonyProps = undefined;
|
|
777
|
-
this.pendingHarmonyRetryCount = 0;
|
|
778
|
-
}
|
|
779
|
-
else {
|
|
780
|
-
this.scheduleHarmonyPropsRetry(props);
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
// Ensure embed element matches latest corner radius for clipping
|
|
784
|
-
if (cornerRadius !== undefined && this.harmonyEmbed.style.borderRadius !== `${cornerRadius}px`) {
|
|
785
|
-
this.harmonyEmbed.style.borderRadius = `${cornerRadius}px`;
|
|
786
|
-
this.harmonyEmbed.style.overflow = "hidden";
|
|
787
|
-
}
|
|
788
|
-
}
|
|
789
|
-
encodeHarmonyProps(props) {
|
|
790
|
-
try {
|
|
791
|
-
const payload = { componentId: this.componentId, ...props };
|
|
792
|
-
return `${HARMONY_PROPS_PREFIX}${encodeURIComponent(JSON.stringify(payload))}`;
|
|
793
|
-
}
|
|
794
|
-
catch (_e) {
|
|
795
|
-
return `${HARMONY_PROPS_PREFIX}%7B%7D`;
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
pushHarmonyPropsUpdate(props) {
|
|
799
|
-
if (!this.componentId || !isHarmony())
|
|
800
|
-
return false;
|
|
801
|
-
try {
|
|
802
|
-
const proxy = window.LingXiaProxy;
|
|
803
|
-
const updateFn = proxy === null || proxy === void 0 ? void 0 : proxy.nativeComponentUpdate;
|
|
804
|
-
if (typeof updateFn !== "function") {
|
|
805
|
-
return false;
|
|
806
|
-
}
|
|
807
|
-
const payload = JSON.stringify({ componentId: this.componentId, ...props });
|
|
808
|
-
try {
|
|
809
|
-
updateFn.call(proxy, payload);
|
|
810
|
-
return true;
|
|
811
|
-
}
|
|
812
|
-
catch {
|
|
813
|
-
try {
|
|
814
|
-
updateFn.call(proxy, this.componentId, payload);
|
|
815
|
-
return true;
|
|
816
|
-
}
|
|
817
|
-
catch {
|
|
818
|
-
return false;
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
catch (error) {
|
|
823
|
-
return false;
|
|
824
|
-
}
|
|
825
|
-
}
|
|
826
|
-
scheduleHarmonyPropsRetry(props) {
|
|
827
|
-
this.pendingHarmonyProps = { ...props };
|
|
828
|
-
if (this.pendingHarmonyRetryTimer !== null) {
|
|
829
|
-
return;
|
|
830
|
-
}
|
|
831
|
-
this.pendingHarmonyRetryTimer = window.setTimeout(() => {
|
|
832
|
-
this.pendingHarmonyRetryTimer = null;
|
|
833
|
-
if (!this.isConnected || !this.pendingHarmonyProps) {
|
|
834
|
-
return;
|
|
835
|
-
}
|
|
836
|
-
const pendingProps = this.pendingHarmonyProps;
|
|
837
|
-
const pushed = this.pushHarmonyPropsUpdate(pendingProps);
|
|
838
|
-
if (pushed) {
|
|
839
|
-
this.lastHarmonyProps = this.encodeHarmonyProps(pendingProps);
|
|
840
|
-
this.pendingHarmonyProps = undefined;
|
|
841
|
-
this.pendingHarmonyRetryCount = 0;
|
|
842
|
-
}
|
|
843
|
-
else {
|
|
844
|
-
this.pendingHarmonyRetryCount += 1;
|
|
845
|
-
if (this.pendingHarmonyRetryCount < 20) {
|
|
846
|
-
this.scheduleHarmonyPropsRetry(pendingProps);
|
|
847
|
-
}
|
|
848
|
-
}
|
|
849
|
-
}, 100);
|
|
850
|
-
}
|
|
851
276
|
}
|
|
852
277
|
export function registerVideoComponent() {
|
|
853
278
|
if (!customElements.get("lx-video")) {
|