@kieransimkin/dancemoves 3.1.20 → 3.1.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -0
- package/docs/child-playback.md +94 -0
- package/docs/playback-sources.md +7 -0
- package/docs/shared-feature-coverage.md +1 -0
- package/docs/wordpress-org.md +6 -0
- package/lib/build-manifest.json +11 -11
- package/lib/dancemoves.min.js +3 -3
- package/lib/index.cjs +435 -5
- package/lib/index.d.ts +17 -0
- package/lib/index.mjs +435 -5
- package/lib/wordpress.js +3 -3
- package/package.json +1 -1
- package/readme.txt +6 -2
package/lib/index.cjs
CHANGED
|
@@ -24,6 +24,8 @@ __export(index_exports, {
|
|
|
24
24
|
VERSION: () => VERSION,
|
|
25
25
|
createDanceMoves: () => createDanceMoves,
|
|
26
26
|
createMemoryPageStore: () => createMemoryPageStore,
|
|
27
|
+
createPlaybackPublisher: () => createPlaybackPublisher,
|
|
28
|
+
createPlaybackReceiver: () => createPlaybackReceiver,
|
|
27
29
|
fromWordPressMeta: () => fromWordPressMeta,
|
|
28
30
|
validatePageConfig: () => validatePageConfig
|
|
29
31
|
});
|
|
@@ -6207,12 +6209,14 @@ function installPlayback(scope, api, host = scope.document.defaultView) {
|
|
|
6207
6209
|
groups.set(master, group);
|
|
6208
6210
|
return group;
|
|
6209
6211
|
}
|
|
6210
|
-
function
|
|
6212
|
+
function registerEndpoint(options, provider = false) {
|
|
6211
6213
|
if (!options || options.masterVerified !== true) throw new TypeError("An explicitly verified master association is required");
|
|
6212
6214
|
const group = groupFor(options.master), mapping = validatePlaybackMapping(options, group.duration());
|
|
6213
6215
|
const element = options.player;
|
|
6214
6216
|
if (!element?.addEventListener || !["AUDIO", "VIDEO"].includes(element.tagName)) throw new TypeError("player must be native audio or video");
|
|
6215
|
-
if (element.ownerDocument !== host.document || !scope.legacyGlobals && !scope.root.contains(element)) throw new TypeError("The player must belong to this runtime root");
|
|
6217
|
+
if (element.ownerDocument !== host.document || !provider && !scope.legacyGlobals && !scope.root.contains(element)) throw new TypeError("The player must belong to this runtime root");
|
|
6218
|
+
if (provider && (!element.removeEventListener || typeof element.play !== "function" || typeof element.pause !== "function")) throw new TypeError("An event-driven playback provider is required");
|
|
6219
|
+
if (provider && typeof host.Node === "function" && element instanceof host.Node) throw new TypeError("Use registerPlayer for native DOM players");
|
|
6216
6220
|
if (registered.has(element)) throw new Error("Player already has a playback owner");
|
|
6217
6221
|
if (element === options.master || element === group.transport.element) throw new TypeError("The master is already registered");
|
|
6218
6222
|
const previousGroup = groups.get(element);
|
|
@@ -6232,7 +6236,7 @@ function installPlayback(scope, api, host = scope.document.defaultView) {
|
|
|
6232
6236
|
registered.delete(element);
|
|
6233
6237
|
controllers.delete(controller);
|
|
6234
6238
|
}, snapshot() {
|
|
6235
|
-
return { kind: "native", mapping, active: group.isActive(source), destroyed };
|
|
6239
|
+
return { kind: provider ? "provider" : "native", mapping, active: group.isActive(source), destroyed };
|
|
6236
6240
|
} };
|
|
6237
6241
|
registered.set(element, controller);
|
|
6238
6242
|
if (!element.paused && !element.ended) group.forward(source, "playing");
|
|
@@ -6242,6 +6246,8 @@ function installPlayback(scope, api, host = scope.document.defaultView) {
|
|
|
6242
6246
|
controllers.add(controller);
|
|
6243
6247
|
return controller;
|
|
6244
6248
|
}
|
|
6249
|
+
const registerPlayer = (options) => registerEndpoint(options);
|
|
6250
|
+
const registerPlaybackProvider = (options) => registerEndpoint(options, true);
|
|
6245
6251
|
function registerYouTube(options) {
|
|
6246
6252
|
if (!options || options.masterVerified !== true) throw new TypeError("An explicitly verified master association is required");
|
|
6247
6253
|
const group = groupFor(options.master), mapping = validatePlaybackMapping(options, group.duration());
|
|
@@ -6421,7 +6427,7 @@ function installPlayback(scope, api, host = scope.document.defaultView) {
|
|
|
6421
6427
|
let source = transports.has(master) ? master : groupFor(master).transport;
|
|
6422
6428
|
while (aliases.has(source)) source = aliases.get(source);
|
|
6423
6429
|
return source;
|
|
6424
|
-
}, registerPlayer, registerYouTube });
|
|
6430
|
+
}, registerPlayer, registerPlaybackProvider, registerYouTube });
|
|
6425
6431
|
scope.onDispose(() => {
|
|
6426
6432
|
disposed = true;
|
|
6427
6433
|
for (const controller of [...controllers]) controller.teardown();
|
|
@@ -6430,8 +6436,431 @@ function installPlayback(scope, api, host = scope.document.defaultView) {
|
|
|
6430
6436
|
});
|
|
6431
6437
|
}
|
|
6432
6438
|
|
|
6439
|
+
// .build/modules/child-playback.mjs
|
|
6440
|
+
var PROTOCOL = "dancemoves.child-playback.v1";
|
|
6441
|
+
var EVENTS = /* @__PURE__ */ new Set(["snapshot", "loadedmetadata", "durationchange", "play", "playing", "pause", "ended", "seeking", "seeked", "ratechange", "timeupdate", "waiting", "error"]);
|
|
6442
|
+
var MAX_SECONDS = 86400;
|
|
6443
|
+
var publisherOwners = /* @__PURE__ */ new WeakMap();
|
|
6444
|
+
var receiverOwners = /* @__PURE__ */ new WeakMap();
|
|
6445
|
+
function identity(value, name) {
|
|
6446
|
+
if (typeof value !== "string" || !value.trim() || value.length > 200) throw new TypeError(`${name} must be a nonempty string of at most 200 characters`);
|
|
6447
|
+
return value;
|
|
6448
|
+
}
|
|
6449
|
+
function exactOrigin(value) {
|
|
6450
|
+
const url2 = new URL(value);
|
|
6451
|
+
if (!["http:", "https:"].includes(url2.protocol) || url2.origin !== value) throw new TypeError("An exact HTTP(S) origin is required");
|
|
6452
|
+
return value;
|
|
6453
|
+
}
|
|
6454
|
+
function context(options) {
|
|
6455
|
+
const host = options.window || globalThis.window;
|
|
6456
|
+
if (!host?.document || !host.crypto?.randomUUID) throw new TypeError("A browser Window with crypto.randomUUID is required");
|
|
6457
|
+
return { host, channel: identity(options.channel, "channel"), assetId: identity(options.assetId, "assetId"), origin: exactOrigin(options.origin) };
|
|
6458
|
+
}
|
|
6459
|
+
function validState(value) {
|
|
6460
|
+
return value && typeof value === "object" && typeof value.time === "number" && Number.isFinite(value.time) && value.time >= 0 && typeof value.duration === "number" && Number.isFinite(value.duration) && value.duration > 0 && value.duration <= MAX_SECONDS && value.time <= value.duration + 0.25 && typeof value.rate === "number" && Number.isFinite(value.rate) && value.rate >= 0.0625 && value.rate <= 16 && ["paused", "ended", "seeking", "buffering", "error"].every((key) => typeof value[key] === "boolean");
|
|
6461
|
+
}
|
|
6462
|
+
function packet(data, channel, assetId) {
|
|
6463
|
+
return data && typeof data === "object" && data.protocol === PROTOCOL && data.channel === channel && data.assetId === assetId;
|
|
6464
|
+
}
|
|
6465
|
+
function resources(host) {
|
|
6466
|
+
const removers = [], timers = /* @__PURE__ */ new Set();
|
|
6467
|
+
return {
|
|
6468
|
+
listen(target, type, callback) {
|
|
6469
|
+
target.addEventListener(type, callback);
|
|
6470
|
+
removers.push(() => target.removeEventListener(type, callback));
|
|
6471
|
+
},
|
|
6472
|
+
later(callback, delay) {
|
|
6473
|
+
const id = host.setTimeout(() => {
|
|
6474
|
+
timers.delete(id);
|
|
6475
|
+
callback();
|
|
6476
|
+
}, delay);
|
|
6477
|
+
timers.add(id);
|
|
6478
|
+
return id;
|
|
6479
|
+
},
|
|
6480
|
+
cancel(id) {
|
|
6481
|
+
if (id) {
|
|
6482
|
+
host.clearTimeout(id);
|
|
6483
|
+
timers.delete(id);
|
|
6484
|
+
}
|
|
6485
|
+
},
|
|
6486
|
+
dispose() {
|
|
6487
|
+
removers.splice(0).forEach((remove) => remove());
|
|
6488
|
+
for (const id of timers) host.clearTimeout(id);
|
|
6489
|
+
timers.clear();
|
|
6490
|
+
},
|
|
6491
|
+
snapshot() {
|
|
6492
|
+
return { listeners: removers.length, timers: timers.size };
|
|
6493
|
+
}
|
|
6494
|
+
};
|
|
6495
|
+
}
|
|
6496
|
+
function createPlaybackPublisher(options = {}) {
|
|
6497
|
+
const { host, channel, assetId, origin } = context(options), source = options.source;
|
|
6498
|
+
const target = options.targetWindow || host.parent;
|
|
6499
|
+
if (!source?.addEventListener || !source?.pause || !target?.postMessage || target === host) throw new TypeError("A playback source and distinct target Window are required");
|
|
6500
|
+
const owners = publisherOwners.get(host) || /* @__PURE__ */ new Map(), key = JSON.stringify([origin, channel, assetId]);
|
|
6501
|
+
const routes = owners.get(target) || /* @__PURE__ */ new Set();
|
|
6502
|
+
if (routes.has(key)) throw new Error("Child playback route already has a publisher");
|
|
6503
|
+
const owned = resources(host);
|
|
6504
|
+
let session = null, sequence = 0, commandSequence = 0, timer = 0, destroyed = false, suspended = false, lastPublished = null;
|
|
6505
|
+
const envelope = (type, extra = {}) => ({ protocol: PROTOCOL, channel, assetId, session, type, ...extra });
|
|
6506
|
+
function send(type, extra) {
|
|
6507
|
+
target.postMessage(envelope(type, extra), origin);
|
|
6508
|
+
}
|
|
6509
|
+
function stop() {
|
|
6510
|
+
owned.cancel(timer);
|
|
6511
|
+
timer = 0;
|
|
6512
|
+
}
|
|
6513
|
+
function sample(event = "snapshot") {
|
|
6514
|
+
if (event === "timeupdate" && lastPublished !== null && host.performance.now() - lastPublished < 250) return;
|
|
6515
|
+
stop();
|
|
6516
|
+
if (destroyed || suspended || !session) return;
|
|
6517
|
+
const state = { time: Number(source.currentTime), duration: Number(source.duration), rate: Number(source.playbackRate), paused: !!source.paused, ended: !!source.ended, seeking: !!source.seeking, buffering: event === "waiting", error: !!source.error };
|
|
6518
|
+
if (!validState(state)) return;
|
|
6519
|
+
lastPublished = host.performance.now();
|
|
6520
|
+
send("state", { sequence: ++sequence, event, state });
|
|
6521
|
+
if (!state.paused && !state.ended && !state.error && !state.buffering && !host.document.hidden) timer = owned.later(() => sample(), 250);
|
|
6522
|
+
}
|
|
6523
|
+
function disconnect(reason) {
|
|
6524
|
+
stop();
|
|
6525
|
+
source.pause();
|
|
6526
|
+
if (session) send("disconnect", { reason });
|
|
6527
|
+
session = null;
|
|
6528
|
+
}
|
|
6529
|
+
function message(event) {
|
|
6530
|
+
const data = event.data;
|
|
6531
|
+
if (destroyed || suspended || event.source !== target || event.origin !== origin || !packet(data, channel, assetId)) return;
|
|
6532
|
+
if (data.type === "connect" && typeof data.session === "string" && /^[0-9a-f-]{36}$/i.test(data.session)) {
|
|
6533
|
+
if (session !== data.session) {
|
|
6534
|
+
session = data.session;
|
|
6535
|
+
sequence = 0;
|
|
6536
|
+
commandSequence = 0;
|
|
6537
|
+
lastPublished = null;
|
|
6538
|
+
}
|
|
6539
|
+
sample();
|
|
6540
|
+
return;
|
|
6541
|
+
}
|
|
6542
|
+
if (data.session !== session || !session) return;
|
|
6543
|
+
if (data.type === "disconnect") {
|
|
6544
|
+
disconnect("parent-disconnected");
|
|
6545
|
+
return;
|
|
6546
|
+
}
|
|
6547
|
+
if (data.type !== "command" || !Number.isSafeInteger(data.sequence) || data.sequence <= commandSequence) return;
|
|
6548
|
+
if (!["pause", "seek", "play"].includes(data.command)) return;
|
|
6549
|
+
if (data.command === "seek" && (typeof data.time !== "number" || !Number.isFinite(data.time) || data.time < 0 || data.time > Number(source.duration))) return;
|
|
6550
|
+
commandSequence = data.sequence;
|
|
6551
|
+
const currentSession = session;
|
|
6552
|
+
Promise.resolve().then(() => {
|
|
6553
|
+
if (destroyed || session !== currentSession) return;
|
|
6554
|
+
if (data.command === "pause") source.pause();
|
|
6555
|
+
else if (data.command === "seek") source.currentTime = data.time;
|
|
6556
|
+
else if (options.allowPlayCommands === true) return source.play();
|
|
6557
|
+
else throw new Error("Parent play commands are disabled");
|
|
6558
|
+
}).then(() => {
|
|
6559
|
+
if (!destroyed && session === currentSession) {
|
|
6560
|
+
sample();
|
|
6561
|
+
send("ack", { sequence: data.sequence, ok: true });
|
|
6562
|
+
}
|
|
6563
|
+
}, () => {
|
|
6564
|
+
if (!destroyed && session === currentSession) send("ack", { sequence: data.sequence, ok: false });
|
|
6565
|
+
});
|
|
6566
|
+
}
|
|
6567
|
+
owned.listen(host, "message", message);
|
|
6568
|
+
function requestClose() {
|
|
6569
|
+
if (!destroyed && !suspended && session) send("dismiss", { sequence: ++sequence });
|
|
6570
|
+
}
|
|
6571
|
+
if (options.notifyEscape === true) owned.listen(host.document, "keydown", (event) => {
|
|
6572
|
+
if (event.key === "Escape" && !event.defaultPrevented && !host.document.fullscreenElement) requestClose();
|
|
6573
|
+
});
|
|
6574
|
+
for (const event of EVENTS) if (event !== "snapshot") owned.listen(source, event, () => sample(event));
|
|
6575
|
+
owned.listen(host.document, "visibilitychange", () => {
|
|
6576
|
+
if (host.document.hidden) {
|
|
6577
|
+
stop();
|
|
6578
|
+
if (session) {
|
|
6579
|
+
sample("waiting");
|
|
6580
|
+
stop();
|
|
6581
|
+
}
|
|
6582
|
+
} else sample();
|
|
6583
|
+
});
|
|
6584
|
+
owned.listen(host, "pagehide", () => {
|
|
6585
|
+
suspended = true;
|
|
6586
|
+
disconnect("pagehide");
|
|
6587
|
+
});
|
|
6588
|
+
owned.listen(host, "pageshow", () => {
|
|
6589
|
+
if (!destroyed) {
|
|
6590
|
+
suspended = false;
|
|
6591
|
+
send("available");
|
|
6592
|
+
}
|
|
6593
|
+
});
|
|
6594
|
+
routes.add(key);
|
|
6595
|
+
owners.set(target, routes);
|
|
6596
|
+
publisherOwners.set(host, owners);
|
|
6597
|
+
send("available");
|
|
6598
|
+
return { requestClose, teardown() {
|
|
6599
|
+
if (destroyed) return;
|
|
6600
|
+
disconnect("teardown");
|
|
6601
|
+
destroyed = true;
|
|
6602
|
+
owned.dispose();
|
|
6603
|
+
routes.delete(key);
|
|
6604
|
+
if (!routes.size) owners.delete(target);
|
|
6605
|
+
}, snapshot() {
|
|
6606
|
+
return { kind: "child-publisher", connected: !!session, destroyed, suspended, ...owned.snapshot() };
|
|
6607
|
+
} };
|
|
6608
|
+
}
|
|
6609
|
+
function createPlaybackReceiver(options = {}) {
|
|
6610
|
+
const { host, channel, assetId, origin } = context(options), iframe = options.iframe;
|
|
6611
|
+
if (iframe?.tagName !== "IFRAME" || !iframe.contentWindow) throw new TypeError("An attached iframe is required");
|
|
6612
|
+
if (receiverOwners.has(iframe)) throw new Error("The child frame already has a playback receiver");
|
|
6613
|
+
const staleAfter = options.staleAfterMilliseconds ?? 1500;
|
|
6614
|
+
if (typeof staleAfter !== "number" || !Number.isFinite(staleAfter) || staleAfter < 750 || staleAfter > 1e4) throw new RangeError("staleAfterMilliseconds must be between 750 and 10000");
|
|
6615
|
+
const owned = resources(host), player = new host.EventTarget(), pending = /* @__PURE__ */ new Map();
|
|
6616
|
+
let session = "", sequence = 0, commands = 0, lastWall = 0, timer = 0, connectTimer = 0, destroyed = false, connected = false, status = "connecting";
|
|
6617
|
+
let state = { time: 0, duration: 0, rate: 1, paused: true, ended: false, seeking: false, buffering: false, error: false };
|
|
6618
|
+
let resolveReady, rejectReady, ready, readySettled = false;
|
|
6619
|
+
function prepareReady() {
|
|
6620
|
+
readySettled = false;
|
|
6621
|
+
ready = new Promise((resolve, reject) => {
|
|
6622
|
+
resolveReady = (value) => {
|
|
6623
|
+
readySettled = true;
|
|
6624
|
+
resolve(value);
|
|
6625
|
+
};
|
|
6626
|
+
rejectReady = (error) => {
|
|
6627
|
+
readySettled = true;
|
|
6628
|
+
reject(error);
|
|
6629
|
+
};
|
|
6630
|
+
});
|
|
6631
|
+
ready.catch(() => {
|
|
6632
|
+
});
|
|
6633
|
+
}
|
|
6634
|
+
prepareReady();
|
|
6635
|
+
const emit = (event) => player.dispatchEvent(new host.Event(event));
|
|
6636
|
+
const envelope = (type, extra = {}) => ({ protocol: PROTOCOL, channel, assetId, session, type, ...extra });
|
|
6637
|
+
function send(type, extra) {
|
|
6638
|
+
iframe.contentWindow?.postMessage(envelope(type, extra), origin);
|
|
6639
|
+
}
|
|
6640
|
+
function cancelPending() {
|
|
6641
|
+
for (const request of pending.values()) {
|
|
6642
|
+
owned.cancel(request.timer);
|
|
6643
|
+
request.reject(new Error("Child playback disconnected"));
|
|
6644
|
+
}
|
|
6645
|
+
pending.clear();
|
|
6646
|
+
}
|
|
6647
|
+
function stop(reason) {
|
|
6648
|
+
owned.cancel(timer);
|
|
6649
|
+
owned.cancel(connectTimer);
|
|
6650
|
+
timer = connectTimer = 0;
|
|
6651
|
+
const playing = !player.paused;
|
|
6652
|
+
state = { ...state, time: player.currentTime, paused: true, buffering: false };
|
|
6653
|
+
connected = false;
|
|
6654
|
+
status = reason;
|
|
6655
|
+
cancelPending();
|
|
6656
|
+
if (playing) emit("pause");
|
|
6657
|
+
}
|
|
6658
|
+
function connect() {
|
|
6659
|
+
if (destroyed) return ready;
|
|
6660
|
+
if (readySettled) prepareReady();
|
|
6661
|
+
stop("connecting");
|
|
6662
|
+
session = host.crypto.randomUUID();
|
|
6663
|
+
sequence = 0;
|
|
6664
|
+
send("connect");
|
|
6665
|
+
const started = host.performance.now();
|
|
6666
|
+
const retry = () => {
|
|
6667
|
+
if (destroyed || connected) return;
|
|
6668
|
+
if (host.performance.now() - started >= 1e4) {
|
|
6669
|
+
send("disconnect");
|
|
6670
|
+
stop("unavailable");
|
|
6671
|
+
session = "";
|
|
6672
|
+
rejectReady(new Error("Child playback did not become ready"));
|
|
6673
|
+
return;
|
|
6674
|
+
}
|
|
6675
|
+
send("connect");
|
|
6676
|
+
connectTimer = owned.later(retry, 500);
|
|
6677
|
+
};
|
|
6678
|
+
connectTimer = owned.later(retry, 500);
|
|
6679
|
+
return ready;
|
|
6680
|
+
}
|
|
6681
|
+
function command(name, time) {
|
|
6682
|
+
if (destroyed || !connected) return Promise.reject(new Error("Child playback is unavailable"));
|
|
6683
|
+
if (pending.size >= 16) return Promise.reject(new Error("Too many pending child playback commands"));
|
|
6684
|
+
const id = ++commands;
|
|
6685
|
+
return new Promise((resolve, reject) => {
|
|
6686
|
+
const timeout = owned.later(() => {
|
|
6687
|
+
pending.delete(id);
|
|
6688
|
+
reject(new Error("Child playback command timed out"));
|
|
6689
|
+
}, 2e3);
|
|
6690
|
+
pending.set(id, { resolve, reject, timer: timeout });
|
|
6691
|
+
send("command", { sequence: id, command: name, ...name === "seek" ? { time } : {} });
|
|
6692
|
+
});
|
|
6693
|
+
}
|
|
6694
|
+
Object.defineProperties(player, {
|
|
6695
|
+
nodeType: { value: 1 },
|
|
6696
|
+
tagName: { value: "VIDEO" },
|
|
6697
|
+
ownerDocument: { value: host.document },
|
|
6698
|
+
isConnected: { get: () => !destroyed && iframe.isConnected },
|
|
6699
|
+
currentTime: { get: () => Math.min(state.duration || MAX_SECONDS, state.time + (!player.paused && !state.seeking ? Math.min(500, Math.max(0, host.performance.now() - lastWall)) / 1e3 * state.rate : 0)), set: (value) => {
|
|
6700
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0 || value > state.duration) throw new RangeError("Seek must be inside the child duration");
|
|
6701
|
+
command("seek", value).catch(() => {
|
|
6702
|
+
});
|
|
6703
|
+
} },
|
|
6704
|
+
duration: { get: () => state.duration },
|
|
6705
|
+
paused: { get: () => state.paused || state.buffering || state.error || !connected },
|
|
6706
|
+
ended: { get: () => state.ended },
|
|
6707
|
+
seeking: { get: () => state.seeking },
|
|
6708
|
+
playbackRate: { get: () => state.rate },
|
|
6709
|
+
error: { get: () => state.error ? new Error("Child media error") : null }
|
|
6710
|
+
});
|
|
6711
|
+
player.play = () => command("play");
|
|
6712
|
+
player.pause = () => {
|
|
6713
|
+
command("pause").catch(() => {
|
|
6714
|
+
});
|
|
6715
|
+
};
|
|
6716
|
+
function message(event) {
|
|
6717
|
+
const data = event.data;
|
|
6718
|
+
if (destroyed || event.source !== iframe.contentWindow || event.origin !== origin || !packet(data, channel, assetId)) return;
|
|
6719
|
+
if (data.type === "available") {
|
|
6720
|
+
if (!host.document.hidden) {
|
|
6721
|
+
if (connected) send("connect");
|
|
6722
|
+
else connect();
|
|
6723
|
+
}
|
|
6724
|
+
return;
|
|
6725
|
+
}
|
|
6726
|
+
if (data.session !== session) return;
|
|
6727
|
+
if (data.type === "dismiss" && Number.isSafeInteger(data.sequence) && data.sequence > sequence) {
|
|
6728
|
+
sequence = data.sequence;
|
|
6729
|
+
emit("dismiss");
|
|
6730
|
+
return;
|
|
6731
|
+
}
|
|
6732
|
+
if (data.type === "disconnect") {
|
|
6733
|
+
stop("disconnected");
|
|
6734
|
+
session = "";
|
|
6735
|
+
return;
|
|
6736
|
+
}
|
|
6737
|
+
if (data.type === "ack" && Number.isSafeInteger(data.sequence) && typeof data.ok === "boolean") {
|
|
6738
|
+
const request = pending.get(data.sequence);
|
|
6739
|
+
if (request) {
|
|
6740
|
+
owned.cancel(request.timer);
|
|
6741
|
+
pending.delete(data.sequence);
|
|
6742
|
+
data.ok ? request.resolve() : request.reject(new Error("Child playback rejected the command"));
|
|
6743
|
+
}
|
|
6744
|
+
return;
|
|
6745
|
+
}
|
|
6746
|
+
if (data.type !== "state" || !Number.isSafeInteger(data.sequence) || data.sequence <= sequence || !EVENTS.has(data.event) || !validState(data.state)) return;
|
|
6747
|
+
const previous = state, wasConnected = connected, wasPaused = player.paused;
|
|
6748
|
+
sequence = data.sequence;
|
|
6749
|
+
state = { ...data.state };
|
|
6750
|
+
lastWall = host.performance.now();
|
|
6751
|
+
connected = true;
|
|
6752
|
+
status = "connected";
|
|
6753
|
+
owned.cancel(connectTimer);
|
|
6754
|
+
connectTimer = 0;
|
|
6755
|
+
owned.cancel(timer);
|
|
6756
|
+
timer = 0;
|
|
6757
|
+
if (!wasConnected) {
|
|
6758
|
+
emit("loadedmetadata");
|
|
6759
|
+
resolveReady(controller);
|
|
6760
|
+
} else if (previous.duration !== state.duration) emit("durationchange");
|
|
6761
|
+
if (previous.rate !== state.rate) emit("ratechange");
|
|
6762
|
+
if (state.seeking && !previous.seeking) emit("seeking");
|
|
6763
|
+
if (!state.seeking && previous.seeking) emit("seeked");
|
|
6764
|
+
if (!state.seeking && !previous.seeking && wasConnected && Math.abs(state.time - previous.time - (wasPaused ? 0 : (lastWall - previousWall) / 1e3 * previous.rate)) > 0.4) {
|
|
6765
|
+
emit("seeking");
|
|
6766
|
+
emit("seeked");
|
|
6767
|
+
}
|
|
6768
|
+
previousWall = lastWall;
|
|
6769
|
+
if (wasPaused && !player.paused) {
|
|
6770
|
+
emit("play");
|
|
6771
|
+
emit("playing");
|
|
6772
|
+
}
|
|
6773
|
+
if (!wasPaused && player.paused) emit("pause");
|
|
6774
|
+
if (state.ended && !previous.ended) emit("ended");
|
|
6775
|
+
if (data.event === "waiting") emit("waiting");
|
|
6776
|
+
if (state.error && !previous.error) emit("error");
|
|
6777
|
+
emit("timeupdate");
|
|
6778
|
+
if (!player.paused) timer = owned.later(() => {
|
|
6779
|
+
send("disconnect");
|
|
6780
|
+
stop("stale");
|
|
6781
|
+
session = "";
|
|
6782
|
+
emit("waiting");
|
|
6783
|
+
}, staleAfter);
|
|
6784
|
+
}
|
|
6785
|
+
let previousWall = 0;
|
|
6786
|
+
owned.listen(host, "message", message);
|
|
6787
|
+
owned.listen(iframe, "load", connect);
|
|
6788
|
+
owned.listen(host, "pagehide", () => {
|
|
6789
|
+
send("disconnect");
|
|
6790
|
+
stop("suspended");
|
|
6791
|
+
session = "";
|
|
6792
|
+
});
|
|
6793
|
+
owned.listen(host, "pageshow", connect);
|
|
6794
|
+
owned.listen(host.document, "visibilitychange", () => {
|
|
6795
|
+
if (host.document.hidden) {
|
|
6796
|
+
send("command", { sequence: ++commands, command: "pause" });
|
|
6797
|
+
stop("hidden");
|
|
6798
|
+
session = "";
|
|
6799
|
+
} else connect();
|
|
6800
|
+
});
|
|
6801
|
+
const observer = new host.MutationObserver(() => {
|
|
6802
|
+
if (!iframe.isConnected) controller.teardown();
|
|
6803
|
+
});
|
|
6804
|
+
observer.observe(host.document.documentElement, { childList: true, subtree: true });
|
|
6805
|
+
const controller = { player, get ready() {
|
|
6806
|
+
return ready;
|
|
6807
|
+
}, reconnect: connect, teardown() {
|
|
6808
|
+
if (destroyed) return;
|
|
6809
|
+
send("command", { sequence: ++commands, command: "pause" });
|
|
6810
|
+
send("disconnect");
|
|
6811
|
+
stop("destroyed");
|
|
6812
|
+
destroyed = true;
|
|
6813
|
+
session = "";
|
|
6814
|
+
rejectReady(new Error("Child playback owner removed"));
|
|
6815
|
+
observer.disconnect();
|
|
6816
|
+
owned.dispose();
|
|
6817
|
+
receiverOwners.delete(iframe);
|
|
6818
|
+
}, snapshot() {
|
|
6819
|
+
return { kind: "child-receiver", status, connected, destroyed, time: player.currentTime, paused: player.paused, assetId, ...owned.snapshot() };
|
|
6820
|
+
} };
|
|
6821
|
+
receiverOwners.set(iframe, controller);
|
|
6822
|
+
connect();
|
|
6823
|
+
return controller;
|
|
6824
|
+
}
|
|
6825
|
+
function installChildPlayback(scope, api, host) {
|
|
6826
|
+
function own(controller) {
|
|
6827
|
+
const teardown = controller.teardown.bind(controller);
|
|
6828
|
+
const remove = scope.onDispose(() => teardown());
|
|
6829
|
+
controller.teardown = () => {
|
|
6830
|
+
remove();
|
|
6831
|
+
teardown();
|
|
6832
|
+
};
|
|
6833
|
+
return controller;
|
|
6834
|
+
}
|
|
6835
|
+
api.publishPlayback = (options) => {
|
|
6836
|
+
if (api.destroyed) throw new Error("DanceMoves owner is destroyed");
|
|
6837
|
+
return own(createPlaybackPublisher({ ...options, window: host }));
|
|
6838
|
+
};
|
|
6839
|
+
api.receiveChildPlayback = (options) => {
|
|
6840
|
+
if (api.destroyed) throw new Error("DanceMoves owner is destroyed");
|
|
6841
|
+
if (typeof api.registerPlaybackProvider !== "function") throw new Error("The shared playback provider API is required");
|
|
6842
|
+
if (options?.masterVerified !== true) throw new TypeError("An explicitly verified master association is required");
|
|
6843
|
+
const receiver = createPlaybackReceiver({ ...options, window: host });
|
|
6844
|
+
let registration;
|
|
6845
|
+
try {
|
|
6846
|
+
registration = api.registerPlaybackProvider({ ...options, player: receiver.player });
|
|
6847
|
+
} catch (error) {
|
|
6848
|
+
receiver.teardown();
|
|
6849
|
+
throw error;
|
|
6850
|
+
}
|
|
6851
|
+
return own({ source: registration.source, player: receiver.player, get ready() {
|
|
6852
|
+
return receiver.ready;
|
|
6853
|
+
}, mapping: registration.mapping, reconnect: receiver.reconnect, teardown() {
|
|
6854
|
+
receiver.teardown();
|
|
6855
|
+
registration.teardown();
|
|
6856
|
+
}, snapshot() {
|
|
6857
|
+
return { ...receiver.snapshot(), mapping: registration.mapping };
|
|
6858
|
+
} });
|
|
6859
|
+
};
|
|
6860
|
+
}
|
|
6861
|
+
|
|
6433
6862
|
// .build/modules/version.mjs
|
|
6434
|
-
var VERSION = "3.1.
|
|
6863
|
+
var VERSION = "3.1.22";
|
|
6435
6864
|
|
|
6436
6865
|
// .build/modules/index.mjs
|
|
6437
6866
|
var mounted = /* @__PURE__ */ new WeakMap();
|
|
@@ -6499,6 +6928,7 @@ function createDanceMoves(options = {}) {
|
|
|
6499
6928
|
}
|
|
6500
6929
|
});
|
|
6501
6930
|
Object.defineProperty(api, "destroyed", { get: () => scope.disposed });
|
|
6931
|
+
installChildPlayback(scope, api, ownerDocument.defaultView);
|
|
6502
6932
|
api.ready = Promise.resolve(api.ready).then(() => {
|
|
6503
6933
|
if (!scope.disposed) scope.document.dispatchEvent(new w.CustomEvent("dance-moves-ready", {
|
|
6504
6934
|
bubbles: true,
|
package/lib/index.d.ts
CHANGED
|
@@ -10,6 +10,20 @@ export interface PlaybackSource extends EventTarget {
|
|
|
10
10
|
}
|
|
11
11
|
export interface PlayerRegistration extends PlaybackMapping {master:MasterReference;masterVerified:true}
|
|
12
12
|
export interface PlaybackController extends Controller {source:PlaybackSource;mapping:Readonly<PlaybackMapping>;ready?:Promise<PlaybackController>}
|
|
13
|
+
|
|
14
|
+
export interface ChildPlaybackPlayer extends EventTarget {
|
|
15
|
+
readonly nodeType:1;readonly tagName:'VIDEO';readonly ownerDocument:Document;readonly isConnected:boolean;
|
|
16
|
+
currentTime:number;readonly duration:number;readonly paused:boolean;readonly ended:boolean;readonly seeking:boolean;readonly playbackRate:number;
|
|
17
|
+
play():Promise<void>;pause():void;
|
|
18
|
+
}
|
|
19
|
+
export interface ChildPlaybackIdentity {origin:string;channel:string;assetId:string}
|
|
20
|
+
export interface ChildPlaybackPublisherOptions extends ChildPlaybackIdentity {source:Pick<HTMLMediaElement,'currentTime'|'duration'|'playbackRate'|'paused'|'ended'|'seeking'|'pause'|'play'|'addEventListener'|'removeEventListener'> & {readonly error?:unknown};targetWindow?:Window;allowPlayCommands?:boolean;notifyEscape?:boolean;window?:Window}
|
|
21
|
+
export interface ChildPlaybackReceiverOptions extends ChildPlaybackIdentity {iframe:HTMLIFrameElement;staleAfterMilliseconds?:number;window?:Window}
|
|
22
|
+
export interface ChildPlaybackReceiver extends Controller {player:ChildPlaybackPlayer;readonly ready:Promise<ChildPlaybackReceiver>;reconnect():Promise<ChildPlaybackReceiver>}
|
|
23
|
+
export interface ChildPlaybackRegistration extends ChildPlaybackReceiverOptions, PlayerRegistration {}
|
|
24
|
+
export interface ChildPlaybackPublisher extends Controller {requestClose():void}
|
|
25
|
+
export function createPlaybackPublisher(options:ChildPlaybackPublisherOptions):ChildPlaybackPublisher;
|
|
26
|
+
export function createPlaybackReceiver(options:ChildPlaybackReceiverOptions):ChildPlaybackReceiver;
|
|
13
27
|
export type Offset = Readonly<{x:number;y:number;z?:number}>;
|
|
14
28
|
export type Metadata = string | {id?:string;handlerId?:string};
|
|
15
29
|
export interface PageConfig {
|
|
@@ -58,6 +72,7 @@ export interface MotionCapture {schema:'ks-epk-motion-recording/v1';samples:Arra
|
|
|
58
72
|
export interface DanceMoves {
|
|
59
73
|
playbackSource(master:MasterReference|PlaybackSource):PlaybackSource;
|
|
60
74
|
registerPlayer(options:PlayerRegistration & {player:HTMLMediaElement}):PlaybackController;
|
|
75
|
+
registerPlaybackProvider(options:PlayerRegistration & {player:ChildPlaybackPlayer}):PlaybackController;
|
|
61
76
|
registerYouTube(options:PlayerRegistration & {iframe:HTMLIFrameElement}):PlaybackController & {ready:Promise<PlaybackController>};
|
|
62
77
|
readonly version:string;readonly bpm:number;readonly bpmSource:string;readonly ticksPerBeat:16;readonly root:HTMLElement;readonly config:Readonly<PageConfig>;
|
|
63
78
|
readonly destroyed:boolean;ready:Promise<DanceMoves>;
|
|
@@ -83,6 +98,8 @@ export interface DanceMoves {
|
|
|
83
98
|
createOrientation(options:{render:(point:Offset & Record<string,unknown>)=>void;windowMilliseconds?:number;minimumSpanDegrees?:number;smoothingTimeConstantMilliseconds?:number}):OrientationController;
|
|
84
99
|
connectPhoneMotion(options?:PhoneMotionOptions):PhoneMotionConnection;
|
|
85
100
|
createRecorder(options?:{durationMilliseconds?:number;maxSamples?:number}):Recorder;
|
|
101
|
+
publishPlayback(options:ChildPlaybackPublisherOptions):ChildPlaybackPublisher;
|
|
102
|
+
receiveChildPlayback(options:ChildPlaybackRegistration):ChildPlaybackReceiver & {source:EventTarget & {currentTime:number;readonly paused:boolean};mapping:Readonly<{offset?:number;duration?:number;playerStart?:number}>};
|
|
86
103
|
on(type:string,callback:EventListener):Remove;resources():{id:string;disposed:boolean;listeners:number;timers:number;frames:number;observers:number;created:number};destroy():void;
|
|
87
104
|
}
|
|
88
105
|
export const VERSION:string;
|