@kieransimkin/dancemoves 3.1.19 → 3.1.21
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/AGENTS.md +6 -0
- package/README.md +21 -0
- package/docs/child-playback.md +94 -0
- package/docs/playback-sources.md +85 -0
- package/docs/shared-feature-coverage.md +1 -0
- package/lib/build-manifest.json +13 -13
- package/lib/dancemoves.min.js +3 -3
- package/lib/index.cjs +934 -22
- package/lib/index.d.ts +40 -10
- package/lib/index.mjs +934 -22
- package/lib/rudiments.d.ts +4 -2
- package/lib/wordpress.js +3 -3
- package/package.json +1 -1
- package/readme.txt +2 -2
package/lib/index.mjs
CHANGED
|
@@ -17,6 +17,7 @@ function install(__dmScope) {
|
|
|
17
17
|
var lyricList = [];
|
|
18
18
|
var lyricRenderer = null;
|
|
19
19
|
var boundAudio = /* @__PURE__ */ new WeakSet();
|
|
20
|
+
var playbackBindings = /* @__PURE__ */ new Map();
|
|
20
21
|
var activeAudio = null;
|
|
21
22
|
var pageClockStarted = window2.performance && typeof window2.performance.now === "function" ? window2.performance.now() : Date.now();
|
|
22
23
|
var referenceDurationSeconds = positiveNumber(rawConfig.masterDurationMilliseconds) / 1e3;
|
|
@@ -106,13 +107,16 @@ function install(__dmScope) {
|
|
|
106
107
|
}
|
|
107
108
|
function currentTick(options) {
|
|
108
109
|
var settings = options || {};
|
|
109
|
-
var audio = settings.audio || (settings.clock === "audio" ? activeAudio : null);
|
|
110
|
+
var audio = playbackAudio(settings.audio) || (settings.clock === "audio" ? activeAudio : null);
|
|
110
111
|
if (audio && !audio.paused && !audio.ended && Number.isFinite(Number(audio.currentTime))) {
|
|
111
112
|
return Number(audio.currentTime) * effectiveBpm(settings.bpm || bpm) * TICKS_PER_BEAT / 60;
|
|
112
113
|
}
|
|
113
114
|
var now = window2.performance && typeof window2.performance.now === "function" ? window2.performance.now() : Date.now();
|
|
114
115
|
return Math.max(0, now - pageClockStarted) * effectiveBpm(settings.bpm || bpm) * TICKS_PER_BEAT / 6e4;
|
|
115
116
|
}
|
|
117
|
+
function playbackAudio(audio) {
|
|
118
|
+
return audio && api && api.playbackSource ? api.playbackSource(audio) : audio;
|
|
119
|
+
}
|
|
116
120
|
function nextIntervalTick(intervalTicks, fromTick, strictlyFuture) {
|
|
117
121
|
var interval = Math.max(1, roundHalfUp(intervalTicks));
|
|
118
122
|
var tick = Math.max(0, Number(fromTick) || 0);
|
|
@@ -169,7 +173,7 @@ function install(__dmScope) {
|
|
|
169
173
|
var pollAudio = function() {
|
|
170
174
|
if (cancelled)
|
|
171
175
|
return;
|
|
172
|
-
var candidate = settings.audio || activeAudio;
|
|
176
|
+
var candidate = playbackAudio(settings.audio) || activeAudio;
|
|
173
177
|
if (!candidate || candidate.paused || candidate.ended) {
|
|
174
178
|
if (sourceAudio && sourceAudio.ended) {
|
|
175
179
|
cancel();
|
|
@@ -731,9 +735,18 @@ function install(__dmScope) {
|
|
|
731
735
|
return Math.abs(duration - referenceDurationSeconds) <= Math.max(0.25, duration / 1e5);
|
|
732
736
|
}
|
|
733
737
|
function bindAudio(audio) {
|
|
738
|
+
if (api.playbackSource)
|
|
739
|
+
audio = api.playbackSource(audio);
|
|
734
740
|
if (boundAudio.has(audio) || !matchesMasterDuration(audio))
|
|
735
741
|
return;
|
|
736
742
|
boundAudio.add(audio);
|
|
743
|
+
var removers = [];
|
|
744
|
+
function listen(target, name, handler) {
|
|
745
|
+
__dmScope.listen(target, name, handler);
|
|
746
|
+
removers.push(function() {
|
|
747
|
+
__dmScope.unlisten(target, name, handler);
|
|
748
|
+
});
|
|
749
|
+
}
|
|
737
750
|
__dmScope.dataset(audio, "danceMovesTiming", "master-length");
|
|
738
751
|
var state = {
|
|
739
752
|
next: firstCueAfter(audio.currentTime || 0),
|
|
@@ -741,7 +754,8 @@ function install(__dmScope) {
|
|
|
741
754
|
last: audio.currentTime || 0,
|
|
742
755
|
seeking: false,
|
|
743
756
|
frame: 0,
|
|
744
|
-
pendingCue: cueAtLanding(audio.currentTime || 0)
|
|
757
|
+
pendingCue: cueAtLanding(audio.currentTime || 0),
|
|
758
|
+
endedPending: false
|
|
745
759
|
};
|
|
746
760
|
function stopFrame() {
|
|
747
761
|
if (state.frame)
|
|
@@ -750,7 +764,7 @@ function install(__dmScope) {
|
|
|
750
764
|
}
|
|
751
765
|
function tick() {
|
|
752
766
|
state.frame = 0;
|
|
753
|
-
if (audio.paused || audio.ended || state.seeking)
|
|
767
|
+
if (audio.paused || audio.ended || state.seeking || document2.hidden)
|
|
754
768
|
return;
|
|
755
769
|
var diagnosticStartedAt = diagnosticsSink ? diagnosticNow() : 0;
|
|
756
770
|
var firedCount = 0;
|
|
@@ -788,8 +802,14 @@ function install(__dmScope) {
|
|
|
788
802
|
}
|
|
789
803
|
function start() {
|
|
790
804
|
stopFrame();
|
|
805
|
+
if (document2.hidden || audio.paused || audio.ended)
|
|
806
|
+
return;
|
|
791
807
|
activeAudio = audio;
|
|
792
808
|
state.last = positiveNumber(audio.currentTime);
|
|
809
|
+
if (state.endedPending) {
|
|
810
|
+
state.pendingCue = cueAtLanding(state.last);
|
|
811
|
+
state.endedPending = false;
|
|
812
|
+
}
|
|
793
813
|
state.next = firstCueAfter(state.last);
|
|
794
814
|
state.lyric = -2;
|
|
795
815
|
if (state.pendingCue) {
|
|
@@ -799,47 +819,70 @@ function install(__dmScope) {
|
|
|
799
819
|
setOwnedPlaybackRate(audio.playbackRate);
|
|
800
820
|
state.frame = window2.requestAnimationFrame(tick);
|
|
801
821
|
}
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
822
|
+
listen(audio, "play", start);
|
|
823
|
+
listen(audio, "playing", start);
|
|
824
|
+
listen(audio, "pause", function() {
|
|
805
825
|
stopFrame();
|
|
806
826
|
if (activeAudio === audio)
|
|
807
827
|
activeAudio = null;
|
|
808
828
|
if (lyricRenderer)
|
|
809
829
|
lyricRenderer.hide();
|
|
810
830
|
});
|
|
811
|
-
|
|
831
|
+
listen(audio, "ended", function() {
|
|
812
832
|
stopFrame();
|
|
813
833
|
if (activeAudio === audio)
|
|
814
834
|
activeAudio = null;
|
|
815
835
|
state.last = 0;
|
|
816
836
|
state.next = 0;
|
|
817
837
|
state.lyric = -2;
|
|
818
|
-
state.pendingCue =
|
|
838
|
+
state.pendingCue = null;
|
|
839
|
+
state.endedPending = true;
|
|
819
840
|
if (lyricRenderer)
|
|
820
841
|
lyricRenderer.hide();
|
|
821
842
|
});
|
|
822
|
-
|
|
843
|
+
listen(audio, "seeking", function() {
|
|
823
844
|
state.seeking = true;
|
|
824
845
|
state.pendingCue = null;
|
|
825
846
|
stopFrame();
|
|
826
847
|
});
|
|
827
|
-
|
|
848
|
+
listen(audio, "seeked", function() {
|
|
828
849
|
state.seeking = false;
|
|
829
850
|
state.last = positiveNumber(audio.currentTime);
|
|
830
851
|
state.next = firstCueAfter(state.last);
|
|
831
852
|
state.lyric = -2;
|
|
832
853
|
state.pendingCue = cueAtLanding(state.last);
|
|
854
|
+
playbackBindings.set(audio, function() {
|
|
855
|
+
stopFrame();
|
|
856
|
+
removers.splice(0).forEach(function(remove) {
|
|
857
|
+
remove();
|
|
858
|
+
});
|
|
859
|
+
boundAudio.delete(audio);
|
|
860
|
+
if (activeAudio === audio)
|
|
861
|
+
activeAudio = null;
|
|
862
|
+
playbackBindings.delete(audio);
|
|
863
|
+
});
|
|
833
864
|
if (!audio.paused && !audio.ended)
|
|
834
865
|
start();
|
|
835
866
|
});
|
|
836
|
-
|
|
867
|
+
listen(audio, "ratechange", function() {
|
|
837
868
|
setOwnedPlaybackRate(audio.playbackRate);
|
|
838
869
|
});
|
|
839
|
-
|
|
870
|
+
listen(audio, "timeupdate", function() {
|
|
840
871
|
if (!audio.paused && !state.frame && !state.seeking)
|
|
841
872
|
tick();
|
|
842
873
|
});
|
|
874
|
+
listen(document2, "visibilitychange", function() {
|
|
875
|
+
if (document2.hidden) {
|
|
876
|
+
stopFrame();
|
|
877
|
+
if (lyricRenderer)
|
|
878
|
+
lyricRenderer.hide();
|
|
879
|
+
} else if (!audio.paused && !audio.ended && !state.seeking) {
|
|
880
|
+
state.pendingCue = null;
|
|
881
|
+
start();
|
|
882
|
+
}
|
|
883
|
+
});
|
|
884
|
+
if (!audio.paused && !audio.ended)
|
|
885
|
+
start();
|
|
843
886
|
}
|
|
844
887
|
function discoverAudio() {
|
|
845
888
|
var audioElements = Array.from(document2.querySelectorAll("audio"));
|
|
@@ -953,6 +996,16 @@ function install(__dmScope) {
|
|
|
953
996
|
registerAnimationScope,
|
|
954
997
|
resetRunningAnimations,
|
|
955
998
|
discoverAudio,
|
|
999
|
+
unregisterPlaybackSource: function(source) {
|
|
1000
|
+
var remove = playbackBindings.get(source);
|
|
1001
|
+
if (remove)
|
|
1002
|
+
remove();
|
|
1003
|
+
},
|
|
1004
|
+
registerPlaybackSource: function(source) {
|
|
1005
|
+
if (!referenceDurationSeconds)
|
|
1006
|
+
referenceDurationSeconds = positiveNumber(source.duration);
|
|
1007
|
+
bindAudio(source);
|
|
1008
|
+
},
|
|
956
1009
|
setDiagnosticsSink,
|
|
957
1010
|
diagnosticsEnabled: function() {
|
|
958
1011
|
return diagnosticsAllowed && Boolean(diagnosticsSink);
|
|
@@ -993,6 +1046,10 @@ function install2(__dmScope) {
|
|
|
993
1046
|
return document2.querySelector(value);
|
|
994
1047
|
return fallback || null;
|
|
995
1048
|
}
|
|
1049
|
+
function playbackElement(value, fallback) {
|
|
1050
|
+
var target = element(value, fallback);
|
|
1051
|
+
return target && motion.playbackSource ? motion.playbackSource(target) : target;
|
|
1052
|
+
}
|
|
996
1053
|
function media(query) {
|
|
997
1054
|
return window2.matchMedia(query);
|
|
998
1055
|
}
|
|
@@ -1118,7 +1175,7 @@ function install2(__dmScope) {
|
|
|
1118
1175
|
function playbackPulse(options) {
|
|
1119
1176
|
options = options || {};
|
|
1120
1177
|
var state = commonState(options, "pulse");
|
|
1121
|
-
var audio =
|
|
1178
|
+
var audio = playbackElement(options.audio, state.root.querySelector("audio"));
|
|
1122
1179
|
if (!audio)
|
|
1123
1180
|
throw new Error("DanceMovesEffects pulse requires an audio element");
|
|
1124
1181
|
var ticks = Math.max(1, Math.round(finite2(options.ticks, 16)));
|
|
@@ -1218,7 +1275,7 @@ function install2(__dmScope) {
|
|
|
1218
1275
|
function cueTimeline(options) {
|
|
1219
1276
|
options = options || {};
|
|
1220
1277
|
var state = commonState(options, "cue-timeline");
|
|
1221
|
-
var audio =
|
|
1278
|
+
var audio = playbackElement(options.audio, state.root.querySelector("audio"));
|
|
1222
1279
|
if (!audio)
|
|
1223
1280
|
throw new Error("DanceMovesEffects cueTimeline requires an audio element");
|
|
1224
1281
|
var cues = (Array.isArray(options.cues) ? options.cues : []).map(function(cue, index) {
|
|
@@ -1379,7 +1436,7 @@ function install2(__dmScope) {
|
|
|
1379
1436
|
function spritePlayback(options) {
|
|
1380
1437
|
options = options || {};
|
|
1381
1438
|
var state = commonState(options, "sprite-playback");
|
|
1382
|
-
var audio =
|
|
1439
|
+
var audio = playbackElement(options.audio, state.root.querySelector("audio"));
|
|
1383
1440
|
var target = element(options.target, state.root);
|
|
1384
1441
|
if (!audio)
|
|
1385
1442
|
throw new Error("DanceMovesEffects spritePlayback requires an audio element");
|
|
@@ -1573,7 +1630,7 @@ function install2(__dmScope) {
|
|
|
1573
1630
|
function lyricStage(options) {
|
|
1574
1631
|
options = options || {};
|
|
1575
1632
|
var state = commonState(options, "lyric-stage");
|
|
1576
|
-
var audio =
|
|
1633
|
+
var audio = playbackElement(options.audio, state.root.querySelector("audio"));
|
|
1577
1634
|
var popover = element(options.popover, document2.querySelector(".dance-moves-lyric-popover"));
|
|
1578
1635
|
if (!audio)
|
|
1579
1636
|
throw new Error("DanceMovesEffects lyricStage requires an audio element");
|
|
@@ -4792,9 +4849,11 @@ function install9(__dmScope) {
|
|
|
4792
4849
|
const clock = options.clock === void 0 ? "page" : options.clock;
|
|
4793
4850
|
if (!["page", "audio", "auto"].includes(clock))
|
|
4794
4851
|
throw new TypeError("clock must be page, audio or auto");
|
|
4795
|
-
|
|
4796
|
-
if (audio &&
|
|
4797
|
-
|
|
4852
|
+
let audio = clock === "page" ? null : element(options.audio, "audio");
|
|
4853
|
+
if (audio && motion.playbackSource)
|
|
4854
|
+
audio = motion.playbackSource(audio);
|
|
4855
|
+
if (audio && !["audio", "video"].includes(String(audio.tagName).toLowerCase()))
|
|
4856
|
+
throw new TypeError("audio must resolve to native media or a shared playback source");
|
|
4798
4857
|
const rate = options.rate === void 0 ? 1 : number(options.rate, "rate");
|
|
4799
4858
|
if (rate <= 0 || rate > 64)
|
|
4800
4859
|
throw new RangeError("rate must be in (0, 64]");
|
|
@@ -5042,7 +5101,7 @@ function install9(__dmScope) {
|
|
|
5042
5101
|
}
|
|
5043
5102
|
if (options.resetOnCue)
|
|
5044
5103
|
disposers.push(motion.onCue(typeof options.resetOnCue === "string" ? options.resetOnCue : "*", (detail) => {
|
|
5045
|
-
if (!detail.audio || !audio || detail.audio === audio)
|
|
5104
|
+
if (!detail.audio || !audio || detail.audio === audio || motion.playbackSource && motion.playbackSource(detail.audio) === motion.playbackSource(audio))
|
|
5046
5105
|
reset();
|
|
5047
5106
|
}, { id: id + ":cue-reset" }));
|
|
5048
5107
|
if (options.offscreen !== false && window2.IntersectionObserver) {
|
|
@@ -5316,6 +5375,8 @@ function createScope(root, config, { legacyGlobals = false, nonce, orientation =
|
|
|
5316
5375
|
}
|
|
5317
5376
|
const api = {
|
|
5318
5377
|
id,
|
|
5378
|
+
root,
|
|
5379
|
+
legacyGlobals,
|
|
5319
5380
|
window,
|
|
5320
5381
|
document,
|
|
5321
5382
|
get disposed() {
|
|
@@ -5918,8 +5979,855 @@ function createMotionRecorder(scope, { durationMilliseconds = 3e4, maxSamples =
|
|
|
5918
5979
|
});
|
|
5919
5980
|
}
|
|
5920
5981
|
|
|
5982
|
+
// .build/modules/playback.mjs
|
|
5983
|
+
var youtubeLoaders = /* @__PURE__ */ new WeakMap();
|
|
5984
|
+
function validatePlaybackMapping(input, masterDuration) {
|
|
5985
|
+
const offset = input.offset ?? 0, duration = input.duration ?? masterDuration, playerStart = input.playerStart ?? 0;
|
|
5986
|
+
for (const [key, value] of Object.entries({ offset, duration, playerStart, masterDuration })) {
|
|
5987
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0 || (key === "duration" || key === "masterDuration") && value === 0) throw new RangeError(`${key} must be a finite positive duration or nonnegative offset`);
|
|
5988
|
+
}
|
|
5989
|
+
if (offset + duration > masterDuration + 1e-3) throw new RangeError("The clip extends beyond its associated master");
|
|
5990
|
+
return Object.freeze({ offset, duration, playerStart, masterDuration });
|
|
5991
|
+
}
|
|
5992
|
+
function loadYouTubeAPI(host) {
|
|
5993
|
+
if (host.YT?.Player) return Promise.resolve(host.YT);
|
|
5994
|
+
if (youtubeLoaders.has(host)) return youtubeLoaders.get(host);
|
|
5995
|
+
const pending = new Promise((resolve, reject) => {
|
|
5996
|
+
const previous = host.onYouTubeIframeAPIReady;
|
|
5997
|
+
let timeout;
|
|
5998
|
+
const script = host.document.createElement("script");
|
|
5999
|
+
function finish(error) {
|
|
6000
|
+
host.clearTimeout(timeout);
|
|
6001
|
+
if (host.onYouTubeIframeAPIReady === ready) host.onYouTubeIframeAPIReady = previous;
|
|
6002
|
+
script.removeEventListener("error", failed);
|
|
6003
|
+
if (error) {
|
|
6004
|
+
script.remove();
|
|
6005
|
+
youtubeLoaders.delete(host);
|
|
6006
|
+
reject(error);
|
|
6007
|
+
} else resolve(host.YT);
|
|
6008
|
+
}
|
|
6009
|
+
function ready() {
|
|
6010
|
+
try {
|
|
6011
|
+
previous?.();
|
|
6012
|
+
} finally {
|
|
6013
|
+
finish(host.YT?.Player ? null : new Error("YouTube API unavailable"));
|
|
6014
|
+
}
|
|
6015
|
+
}
|
|
6016
|
+
function failed() {
|
|
6017
|
+
finish(new Error("YouTube API failed to load"));
|
|
6018
|
+
}
|
|
6019
|
+
host.onYouTubeIframeAPIReady = ready;
|
|
6020
|
+
script.src = "https://www.youtube.com/iframe_api";
|
|
6021
|
+
script.addEventListener("error", failed, { once: true });
|
|
6022
|
+
timeout = host.setTimeout(() => finish(new Error("YouTube API timed out")), 2e4);
|
|
6023
|
+
host.document.head.appendChild(script);
|
|
6024
|
+
});
|
|
6025
|
+
youtubeLoaders.set(host, pending);
|
|
6026
|
+
return pending;
|
|
6027
|
+
}
|
|
6028
|
+
function installPlayback(scope, api, host = scope.document.defaultView) {
|
|
6029
|
+
const groups = /* @__PURE__ */ new Map(), transports = /* @__PURE__ */ new WeakSet(), aliases = /* @__PURE__ */ new WeakMap(), controllers = /* @__PURE__ */ new Set(), registered = /* @__PURE__ */ new WeakMap();
|
|
6030
|
+
let disposed = false;
|
|
6031
|
+
function emit(target, type) {
|
|
6032
|
+
target.dispatchEvent(new host.Event(type));
|
|
6033
|
+
}
|
|
6034
|
+
function groupFor(reference) {
|
|
6035
|
+
if (disposed) throw new Error("DanceMoves playback owner is destroyed");
|
|
6036
|
+
const master = reference?.element || reference, declared = reference?.element ? reference.duration : void 0;
|
|
6037
|
+
const isNative = !!master?.addEventListener && ["AUDIO", "VIDEO"].includes(master.tagName);
|
|
6038
|
+
if (!isNative && (!master || typeof master.id !== "string" || !master.id.trim() || master.id.length > 160 || typeof master.duration !== "number" || !Number.isFinite(master.duration) || master.duration <= 0)) throw new TypeError("A native master or identified master duration is required");
|
|
6039
|
+
if (isNative && (master.ownerDocument !== host.document || !scope.legacyGlobals && !scope.root.contains(master))) throw new TypeError("The master must belong to this runtime root");
|
|
6040
|
+
if (groups.has(master)) {
|
|
6041
|
+
const group2 = groups.get(master);
|
|
6042
|
+
group2.setDuration(declared);
|
|
6043
|
+
return group2;
|
|
6044
|
+
}
|
|
6045
|
+
const transport = new host.EventTarget();
|
|
6046
|
+
transports.add(transport);
|
|
6047
|
+
let active = null, lastPaused = true, delegate = null;
|
|
6048
|
+
let declaredDuration = declared || 0;
|
|
6049
|
+
function setDuration(value) {
|
|
6050
|
+
if (value === void 0) return;
|
|
6051
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) throw new RangeError("Declared master duration must be positive and finite");
|
|
6052
|
+
if (declaredDuration && Math.abs(declaredDuration - value) > 1e-3) throw new RangeError("Conflicting master durations");
|
|
6053
|
+
if (isNative && Number(master.duration) > 0 && Math.abs(Number(master.duration) - value) > 0.25) throw new RangeError("Declared master duration does not match its native reference");
|
|
6054
|
+
declaredDuration = value;
|
|
6055
|
+
}
|
|
6056
|
+
setDuration(declared);
|
|
6057
|
+
const duration = () => delegate?.duration || declaredDuration || (Number(master.duration) > 0 && Number.isFinite(Number(master.duration)) ? Number(master.duration) : Number(api.config?.masterDurationMilliseconds || 0) / 1e3);
|
|
6058
|
+
const target = isNative ? master : scope.root;
|
|
6059
|
+
const native = { element: isNative ? master : null, read: () => ({ time: Number(master.currentTime) || 0, paused: isNative ? master.paused : true, ended: master.ended || false, rate: master.playbackRate || 1, seeking: master.seeking || false, error: master.error }), pause: () => master.pause?.(), play: () => master.play?.(), seek: (time) => {
|
|
6060
|
+
if (isNative) master.currentTime = time;
|
|
6061
|
+
}, mapping: null };
|
|
6062
|
+
active = native;
|
|
6063
|
+
function read() {
|
|
6064
|
+
if (delegate) return { time: delegate.currentTime, paused: delegate.paused, ended: delegate.ended, rate: delegate.playbackRate, seeking: delegate.seeking, error: delegate.error };
|
|
6065
|
+
const raw = active.read(), map = active.mapping || { offset: 0, playerStart: 0, duration: duration() };
|
|
6066
|
+
const local = raw.time - map.playerStart, outside = local < -1e-3 || local > map.duration + 1e-3;
|
|
6067
|
+
return { ...raw, time: map.offset + Math.max(0, Math.min(map.duration, local)), paused: raw.paused || outside, ended: raw.ended || local > map.duration + 1e-3 };
|
|
6068
|
+
}
|
|
6069
|
+
Object.defineProperties(transport, {
|
|
6070
|
+
nodeType: { value: 1 },
|
|
6071
|
+
tagName: { get: () => isNative ? master.tagName : "VIDEO" },
|
|
6072
|
+
ownerDocument: { get: () => target.ownerDocument },
|
|
6073
|
+
dataset: { get: () => target.dataset },
|
|
6074
|
+
id: { get: () => master.id },
|
|
6075
|
+
isConnected: { get: () => target.isConnected },
|
|
6076
|
+
currentTime: { get: () => read().time, set: (value) => {
|
|
6077
|
+
if (delegate) {
|
|
6078
|
+
delegate.currentTime = value;
|
|
6079
|
+
return;
|
|
6080
|
+
}
|
|
6081
|
+
const map = active.mapping || { offset: 0, playerStart: 0, duration: duration() };
|
|
6082
|
+
active.seek(map.playerStart + Math.max(0, Math.min(map.duration, Number(value) - map.offset)));
|
|
6083
|
+
} },
|
|
6084
|
+
duration: { get: duration },
|
|
6085
|
+
paused: { get: () => read().paused },
|
|
6086
|
+
ended: { get: () => read().ended },
|
|
6087
|
+
seeking: { get: () => read().seeking || false },
|
|
6088
|
+
playbackRate: { get: () => read().rate },
|
|
6089
|
+
error: { get: () => read().error || null },
|
|
6090
|
+
element: { value: isNative ? master : null },
|
|
6091
|
+
activePlayer: { get: () => delegate ? delegate.activePlayer : active.element },
|
|
6092
|
+
masterId: { get: () => delegate ? delegate.masterId : master.currentSrc || master.src || master.id }
|
|
6093
|
+
});
|
|
6094
|
+
transport.matches = (selector) => target.matches(selector);
|
|
6095
|
+
transport.getAttribute = (name) => target.getAttribute(name);
|
|
6096
|
+
transport.setAttribute = (name, value) => target.setAttribute(name, value);
|
|
6097
|
+
transport.removeAttribute = (name) => target.removeAttribute(name);
|
|
6098
|
+
transport.play = () => delegate ? delegate.play() : active.play();
|
|
6099
|
+
transport.pause = () => delegate ? delegate.pause() : active.pause();
|
|
6100
|
+
function activate(source) {
|
|
6101
|
+
if (active !== source) {
|
|
6102
|
+
const previous = active;
|
|
6103
|
+
active = source;
|
|
6104
|
+
previous.pause();
|
|
6105
|
+
emit(transport, "seeking");
|
|
6106
|
+
emit(transport, "seeked");
|
|
6107
|
+
}
|
|
6108
|
+
}
|
|
6109
|
+
function forward(source, type) {
|
|
6110
|
+
if (type === "play" || type === "playing") activate(source);
|
|
6111
|
+
if (active !== source) return;
|
|
6112
|
+
const paused = read().paused;
|
|
6113
|
+
if (type === "timeupdate" && paused !== lastPaused) emit(transport, paused ? "pause" : "playing");
|
|
6114
|
+
lastPaused = paused;
|
|
6115
|
+
emit(transport, type);
|
|
6116
|
+
}
|
|
6117
|
+
const events = ["loadedmetadata", "durationchange", "play", "playing", "pause", "ended", "seeking", "seeked", "ratechange", "timeupdate", "waiting", "error"];
|
|
6118
|
+
const removers = [];
|
|
6119
|
+
function bindNative(element, mapping) {
|
|
6120
|
+
const source = element === master ? native : { element, mapping, read: () => ({ time: Number(element.currentTime) || 0, paused: element.paused, ended: element.ended, rate: element.playbackRate || 1, seeking: element.seeking, error: element.error }), pause: () => element.pause(), play: () => element.play(), seek: (time) => {
|
|
6121
|
+
element.currentTime = time;
|
|
6122
|
+
} };
|
|
6123
|
+
source.removers = [];
|
|
6124
|
+
for (const type of events) {
|
|
6125
|
+
const listener = () => forward(source, type);
|
|
6126
|
+
scope.listen(element, type, listener);
|
|
6127
|
+
const remove = () => scope.unlisten(element, type, listener);
|
|
6128
|
+
removers.push(remove);
|
|
6129
|
+
source.removers.push(remove);
|
|
6130
|
+
}
|
|
6131
|
+
return source;
|
|
6132
|
+
}
|
|
6133
|
+
if (isNative) bindNative(master, null);
|
|
6134
|
+
const group = {
|
|
6135
|
+
transport,
|
|
6136
|
+
duration,
|
|
6137
|
+
setDuration,
|
|
6138
|
+
activate,
|
|
6139
|
+
forward,
|
|
6140
|
+
bindNative,
|
|
6141
|
+
isActive: (source) => active === source,
|
|
6142
|
+
aliasTo(target2) {
|
|
6143
|
+
removers.splice(0).forEach((remove) => remove());
|
|
6144
|
+
delegate = target2;
|
|
6145
|
+
aliases.set(transport, target2);
|
|
6146
|
+
for (const type of events) {
|
|
6147
|
+
const relay = () => emit(transport, type);
|
|
6148
|
+
scope.listen(target2, type, relay);
|
|
6149
|
+
removers.push(() => scope.unlisten(target2, type, relay));
|
|
6150
|
+
}
|
|
6151
|
+
},
|
|
6152
|
+
reconcile(source) {
|
|
6153
|
+
if (active !== source) return;
|
|
6154
|
+
const paused = read().paused;
|
|
6155
|
+
if (paused !== lastPaused) {
|
|
6156
|
+
lastPaused = paused;
|
|
6157
|
+
emit(transport, paused ? "pause" : "playing");
|
|
6158
|
+
}
|
|
6159
|
+
emit(transport, "timeupdate");
|
|
6160
|
+
},
|
|
6161
|
+
remove(source) {
|
|
6162
|
+
if (active === source) {
|
|
6163
|
+
source.pause();
|
|
6164
|
+
active = native;
|
|
6165
|
+
lastPaused = true;
|
|
6166
|
+
emit(transport, "pause");
|
|
6167
|
+
emit(transport, "seeking");
|
|
6168
|
+
emit(transport, "seeked");
|
|
6169
|
+
}
|
|
6170
|
+
},
|
|
6171
|
+
destroy() {
|
|
6172
|
+
removers.splice(0).forEach((remove) => remove());
|
|
6173
|
+
emit(transport, "pause");
|
|
6174
|
+
}
|
|
6175
|
+
};
|
|
6176
|
+
groups.set(master, group);
|
|
6177
|
+
return group;
|
|
6178
|
+
}
|
|
6179
|
+
function registerEndpoint(options, provider = false) {
|
|
6180
|
+
if (!options || options.masterVerified !== true) throw new TypeError("An explicitly verified master association is required");
|
|
6181
|
+
const group = groupFor(options.master), mapping = validatePlaybackMapping(options, group.duration());
|
|
6182
|
+
const element = options.player;
|
|
6183
|
+
if (!element?.addEventListener || !["AUDIO", "VIDEO"].includes(element.tagName)) throw new TypeError("player must be native audio or video");
|
|
6184
|
+
if (element.ownerDocument !== host.document || !provider && !scope.legacyGlobals && !scope.root.contains(element)) throw new TypeError("The player must belong to this runtime root");
|
|
6185
|
+
if (provider && (!element.removeEventListener || typeof element.play !== "function" || typeof element.pause !== "function")) throw new TypeError("An event-driven playback provider is required");
|
|
6186
|
+
if (provider && typeof host.Node === "function" && element instanceof host.Node) throw new TypeError("Use registerPlayer for native DOM players");
|
|
6187
|
+
if (registered.has(element)) throw new Error("Player already has a playback owner");
|
|
6188
|
+
if (element === options.master || element === group.transport.element) throw new TypeError("The master is already registered");
|
|
6189
|
+
const previousGroup = groups.get(element);
|
|
6190
|
+
if (previousGroup && previousGroup !== group) {
|
|
6191
|
+
if ([...controllers].some((owner) => owner.source === previousGroup.transport)) throw new Error("This player already owns a different master group");
|
|
6192
|
+
api.unregisterPlaybackSource?.(previousGroup.transport);
|
|
6193
|
+
previousGroup.aliasTo(group.transport);
|
|
6194
|
+
for (const [key, value] of groups) if (value === previousGroup) groups.set(key, group);
|
|
6195
|
+
}
|
|
6196
|
+
const source = group.bindNative(element, mapping);
|
|
6197
|
+
let destroyed = false;
|
|
6198
|
+
const controller = { source: group.transport, mapping, teardown() {
|
|
6199
|
+
if (destroyed) return;
|
|
6200
|
+
destroyed = true;
|
|
6201
|
+
source.removers.splice(0).forEach((remove) => remove());
|
|
6202
|
+
group.remove(source);
|
|
6203
|
+
registered.delete(element);
|
|
6204
|
+
controllers.delete(controller);
|
|
6205
|
+
}, snapshot() {
|
|
6206
|
+
return { kind: provider ? "provider" : "native", mapping, active: group.isActive(source), destroyed };
|
|
6207
|
+
} };
|
|
6208
|
+
registered.set(element, controller);
|
|
6209
|
+
if (!element.paused && !element.ended) group.forward(source, "playing");
|
|
6210
|
+
Promise.resolve(api.ready).then(() => {
|
|
6211
|
+
if (!destroyed && !disposed) api.registerPlaybackSource?.(group.transport);
|
|
6212
|
+
});
|
|
6213
|
+
controllers.add(controller);
|
|
6214
|
+
return controller;
|
|
6215
|
+
}
|
|
6216
|
+
const registerPlayer = (options) => registerEndpoint(options);
|
|
6217
|
+
const registerPlaybackProvider = (options) => registerEndpoint(options, true);
|
|
6218
|
+
function registerYouTube(options) {
|
|
6219
|
+
if (!options || options.masterVerified !== true) throw new TypeError("An explicitly verified master association is required");
|
|
6220
|
+
const group = groupFor(options.master), mapping = validatePlaybackMapping(options, group.duration());
|
|
6221
|
+
const iframe = options.iframe;
|
|
6222
|
+
if (iframe?.tagName !== "IFRAME") throw new TypeError("A YouTube iframe is required");
|
|
6223
|
+
if (iframe.ownerDocument !== host.document || !scope.legacyGlobals && !scope.root.contains(iframe)) throw new TypeError("The iframe must belong to this runtime root");
|
|
6224
|
+
if (registered.has(iframe)) throw new Error("Player already has a playback owner");
|
|
6225
|
+
const url2 = new URL(iframe.src, host.location.href);
|
|
6226
|
+
if (!/^https:\/\/(www\.)?youtube(-nocookie)?\.com\/embed\/[A-Za-z0-9_-]{11}(\?|$)/.test(url2.href)) throw new TypeError("Unsupported YouTube embed");
|
|
6227
|
+
url2.searchParams.set("enablejsapi", "1");
|
|
6228
|
+
url2.searchParams.set("origin", host.location.origin);
|
|
6229
|
+
const expectedVideoId = url2.pathname.split("/").at(-1);
|
|
6230
|
+
if (iframe.src !== url2.href) iframe.src = url2.href;
|
|
6231
|
+
let player = null, state = -1, error = null, frame = 0, destroyed = false, lastTime = null, lastWall = null, lastRate = 1, readyTimer = 0, rejectReady = null;
|
|
6232
|
+
const source = {
|
|
6233
|
+
element: iframe,
|
|
6234
|
+
mapping,
|
|
6235
|
+
read: () => ({ time: player?.getCurrentTime?.() || 0, paused: state !== 1, ended: state === 0, rate: player?.getPlaybackRate?.() || 1, error }),
|
|
6236
|
+
pause: () => player?.pauseVideo?.(),
|
|
6237
|
+
play: () => player?.playVideo?.(),
|
|
6238
|
+
seek: (time) => player?.seekTo?.(time, true)
|
|
6239
|
+
};
|
|
6240
|
+
function stop() {
|
|
6241
|
+
if (frame) scope.window.cancelAnimationFrame(frame);
|
|
6242
|
+
frame = 0;
|
|
6243
|
+
lastTime = null;
|
|
6244
|
+
lastWall = null;
|
|
6245
|
+
}
|
|
6246
|
+
function sample(wall) {
|
|
6247
|
+
frame = 0;
|
|
6248
|
+
if (destroyed || state !== 1 || scope.document.hidden || !group.isActive(source)) return;
|
|
6249
|
+
const raw = source.read();
|
|
6250
|
+
if (lastTime !== null && Math.abs(raw.time - lastTime - (wall - lastWall) / 1e3 * raw.rate) > 0.4) {
|
|
6251
|
+
group.forward(source, "seeking");
|
|
6252
|
+
group.forward(source, "seeked");
|
|
6253
|
+
}
|
|
6254
|
+
if (raw.rate !== lastRate) {
|
|
6255
|
+
lastRate = raw.rate;
|
|
6256
|
+
group.forward(source, "ratechange");
|
|
6257
|
+
}
|
|
6258
|
+
lastTime = raw.time;
|
|
6259
|
+
lastWall = wall;
|
|
6260
|
+
group.reconcile(source);
|
|
6261
|
+
frame = scope.window.requestAnimationFrame(sample);
|
|
6262
|
+
}
|
|
6263
|
+
function resume() {
|
|
6264
|
+
stop();
|
|
6265
|
+
if (!destroyed && state === 1 && !scope.document.hidden && group.isActive(source)) frame = scope.window.requestAnimationFrame(sample);
|
|
6266
|
+
}
|
|
6267
|
+
const visibility = () => {
|
|
6268
|
+
if (scope.document.hidden) stop();
|
|
6269
|
+
else {
|
|
6270
|
+
group.forward(source, "seeking");
|
|
6271
|
+
group.forward(source, "seeked");
|
|
6272
|
+
resume();
|
|
6273
|
+
}
|
|
6274
|
+
};
|
|
6275
|
+
const pageHide = () => stop(), pageShow = () => {
|
|
6276
|
+
if (player?.getPlayerState) state = player.getPlayerState();
|
|
6277
|
+
visibility();
|
|
6278
|
+
};
|
|
6279
|
+
scope.listen(scope.document, "visibilitychange", visibility);
|
|
6280
|
+
scope.listen(host, "pagehide", pageHide);
|
|
6281
|
+
scope.listen(host, "pageshow", pageShow);
|
|
6282
|
+
const controller = {
|
|
6283
|
+
source: group.transport,
|
|
6284
|
+
mapping,
|
|
6285
|
+
ready: null,
|
|
6286
|
+
teardown() {
|
|
6287
|
+
if (destroyed) return;
|
|
6288
|
+
destroyed = true;
|
|
6289
|
+
stop();
|
|
6290
|
+
if (readyTimer) scope.window.clearTimeout(readyTimer);
|
|
6291
|
+
readyTimer = 0;
|
|
6292
|
+
rejectReady?.(new Error("YouTube playback owner removed"));
|
|
6293
|
+
rejectReady = null;
|
|
6294
|
+
group.remove(source);
|
|
6295
|
+
scope.unlisten(scope.document, "visibilitychange", visibility);
|
|
6296
|
+
scope.unlisten(host, "pagehide", pageHide);
|
|
6297
|
+
scope.unlisten(host, "pageshow", pageShow);
|
|
6298
|
+
player?.destroy?.();
|
|
6299
|
+
registered.delete(iframe);
|
|
6300
|
+
controllers.delete(controller);
|
|
6301
|
+
},
|
|
6302
|
+
snapshot() {
|
|
6303
|
+
return { kind: "youtube", mapping, state, error, active: group.isActive(source), destroyed, polling: !!frame, time: group.transport.currentTime };
|
|
6304
|
+
}
|
|
6305
|
+
};
|
|
6306
|
+
controllers.add(controller);
|
|
6307
|
+
registered.set(iframe, controller);
|
|
6308
|
+
Promise.resolve(api.ready).then(() => {
|
|
6309
|
+
if (!destroyed && !disposed) api.registerPlaybackSource?.(group.transport);
|
|
6310
|
+
});
|
|
6311
|
+
controller.ready = loadYouTubeAPI(host).then((YT) => {
|
|
6312
|
+
if (destroyed || disposed) return controller;
|
|
6313
|
+
return new Promise((resolve, reject) => {
|
|
6314
|
+
rejectReady = reject;
|
|
6315
|
+
readyTimer = scope.window.setTimeout(() => {
|
|
6316
|
+
readyTimer = 0;
|
|
6317
|
+
error = new Error("YouTube player readiness timed out");
|
|
6318
|
+
rejectReady = null;
|
|
6319
|
+
reject(error);
|
|
6320
|
+
}, 15e3);
|
|
6321
|
+
player = new YT.Player(iframe, { events: {
|
|
6322
|
+
onReady() {
|
|
6323
|
+
if (destroyed) return;
|
|
6324
|
+
if (readyTimer) scope.window.clearTimeout(readyTimer);
|
|
6325
|
+
readyTimer = 0;
|
|
6326
|
+
rejectReady = null;
|
|
6327
|
+
const actual = Number(player.getDuration?.());
|
|
6328
|
+
if (actual > 0 && actual + 0.25 < mapping.playerStart + mapping.duration) {
|
|
6329
|
+
error = new Error("The mapped soundtrack window exceeds the video duration");
|
|
6330
|
+
reject(error);
|
|
6331
|
+
return;
|
|
6332
|
+
}
|
|
6333
|
+
group.forward(source, "loadedmetadata");
|
|
6334
|
+
const initialState = player.getPlayerState?.();
|
|
6335
|
+
if (initialState === 1) controllerState({ data: initialState });
|
|
6336
|
+
resolve(controller);
|
|
6337
|
+
},
|
|
6338
|
+
onStateChange: controllerState,
|
|
6339
|
+
onPlaybackRateChange() {
|
|
6340
|
+
if (!destroyed) group.forward(source, "ratechange");
|
|
6341
|
+
},
|
|
6342
|
+
onError(event) {
|
|
6343
|
+
if (destroyed) return;
|
|
6344
|
+
error = new Error("YouTube playback error " + event.data);
|
|
6345
|
+
state = -1;
|
|
6346
|
+
stop();
|
|
6347
|
+
if (readyTimer) scope.window.clearTimeout(readyTimer);
|
|
6348
|
+
readyTimer = 0;
|
|
6349
|
+
rejectReady = null;
|
|
6350
|
+
group.forward(source, "pause");
|
|
6351
|
+
group.forward(source, "error");
|
|
6352
|
+
reject(error);
|
|
6353
|
+
}
|
|
6354
|
+
} });
|
|
6355
|
+
function controllerState(event) {
|
|
6356
|
+
if (destroyed) return;
|
|
6357
|
+
state = event.data;
|
|
6358
|
+
stop();
|
|
6359
|
+
if (state === 1) {
|
|
6360
|
+
let actualId;
|
|
6361
|
+
try {
|
|
6362
|
+
const actualUrl = new URL(player.getVideoUrl());
|
|
6363
|
+
actualId = actualUrl.searchParams.get("v") || actualUrl.pathname.match(/^\/(?:shorts|embed)\/([A-Za-z0-9_-]{11})\/?$/)?.[1];
|
|
6364
|
+
} catch {
|
|
6365
|
+
}
|
|
6366
|
+
if (actualId !== expectedVideoId) {
|
|
6367
|
+
error = new Error("YouTube switched away from the verified soundtrack asset");
|
|
6368
|
+
state = -1;
|
|
6369
|
+
group.forward(source, "pause");
|
|
6370
|
+
group.forward(source, "error");
|
|
6371
|
+
return;
|
|
6372
|
+
}
|
|
6373
|
+
const actualDuration = Number(player.getDuration?.());
|
|
6374
|
+
if (actualDuration > 0 && actualDuration + 0.25 < mapping.playerStart + mapping.duration) {
|
|
6375
|
+
error = new Error("The mapped soundtrack window exceeds the video duration");
|
|
6376
|
+
state = -1;
|
|
6377
|
+
group.forward(source, "pause");
|
|
6378
|
+
group.forward(source, "error");
|
|
6379
|
+
return;
|
|
6380
|
+
}
|
|
6381
|
+
group.forward(source, "play");
|
|
6382
|
+
group.forward(source, "playing");
|
|
6383
|
+
resume();
|
|
6384
|
+
} else group.forward(source, state === 0 ? "ended" : state === 3 ? "waiting" : "pause");
|
|
6385
|
+
if (state === 3) group.forward(source, "pause");
|
|
6386
|
+
}
|
|
6387
|
+
});
|
|
6388
|
+
});
|
|
6389
|
+
controller.ready.catch(() => {
|
|
6390
|
+
});
|
|
6391
|
+
return controller;
|
|
6392
|
+
}
|
|
6393
|
+
Object.assign(api, { playbackSource: (master) => {
|
|
6394
|
+
let source = transports.has(master) ? master : groupFor(master).transport;
|
|
6395
|
+
while (aliases.has(source)) source = aliases.get(source);
|
|
6396
|
+
return source;
|
|
6397
|
+
}, registerPlayer, registerPlaybackProvider, registerYouTube });
|
|
6398
|
+
scope.onDispose(() => {
|
|
6399
|
+
disposed = true;
|
|
6400
|
+
for (const controller of [...controllers]) controller.teardown();
|
|
6401
|
+
for (const group of new Set(groups.values())) group.destroy();
|
|
6402
|
+
groups.clear();
|
|
6403
|
+
});
|
|
6404
|
+
}
|
|
6405
|
+
|
|
6406
|
+
// .build/modules/child-playback.mjs
|
|
6407
|
+
var PROTOCOL = "dancemoves.child-playback.v1";
|
|
6408
|
+
var EVENTS = /* @__PURE__ */ new Set(["snapshot", "loadedmetadata", "durationchange", "play", "playing", "pause", "ended", "seeking", "seeked", "ratechange", "timeupdate", "waiting", "error"]);
|
|
6409
|
+
var MAX_SECONDS = 86400;
|
|
6410
|
+
var publisherOwners = /* @__PURE__ */ new WeakMap();
|
|
6411
|
+
var receiverOwners = /* @__PURE__ */ new WeakMap();
|
|
6412
|
+
function identity(value, name) {
|
|
6413
|
+
if (typeof value !== "string" || !value.trim() || value.length > 200) throw new TypeError(`${name} must be a nonempty string of at most 200 characters`);
|
|
6414
|
+
return value;
|
|
6415
|
+
}
|
|
6416
|
+
function exactOrigin(value) {
|
|
6417
|
+
const url2 = new URL(value);
|
|
6418
|
+
if (!["http:", "https:"].includes(url2.protocol) || url2.origin !== value) throw new TypeError("An exact HTTP(S) origin is required");
|
|
6419
|
+
return value;
|
|
6420
|
+
}
|
|
6421
|
+
function context(options) {
|
|
6422
|
+
const host = options.window || globalThis.window;
|
|
6423
|
+
if (!host?.document || !host.crypto?.randomUUID) throw new TypeError("A browser Window with crypto.randomUUID is required");
|
|
6424
|
+
return { host, channel: identity(options.channel, "channel"), assetId: identity(options.assetId, "assetId"), origin: exactOrigin(options.origin) };
|
|
6425
|
+
}
|
|
6426
|
+
function validState(value) {
|
|
6427
|
+
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");
|
|
6428
|
+
}
|
|
6429
|
+
function packet(data, channel, assetId) {
|
|
6430
|
+
return data && typeof data === "object" && data.protocol === PROTOCOL && data.channel === channel && data.assetId === assetId;
|
|
6431
|
+
}
|
|
6432
|
+
function resources(host) {
|
|
6433
|
+
const removers = [], timers = /* @__PURE__ */ new Set();
|
|
6434
|
+
return {
|
|
6435
|
+
listen(target, type, callback) {
|
|
6436
|
+
target.addEventListener(type, callback);
|
|
6437
|
+
removers.push(() => target.removeEventListener(type, callback));
|
|
6438
|
+
},
|
|
6439
|
+
later(callback, delay) {
|
|
6440
|
+
const id = host.setTimeout(() => {
|
|
6441
|
+
timers.delete(id);
|
|
6442
|
+
callback();
|
|
6443
|
+
}, delay);
|
|
6444
|
+
timers.add(id);
|
|
6445
|
+
return id;
|
|
6446
|
+
},
|
|
6447
|
+
cancel(id) {
|
|
6448
|
+
if (id) {
|
|
6449
|
+
host.clearTimeout(id);
|
|
6450
|
+
timers.delete(id);
|
|
6451
|
+
}
|
|
6452
|
+
},
|
|
6453
|
+
dispose() {
|
|
6454
|
+
removers.splice(0).forEach((remove) => remove());
|
|
6455
|
+
for (const id of timers) host.clearTimeout(id);
|
|
6456
|
+
timers.clear();
|
|
6457
|
+
},
|
|
6458
|
+
snapshot() {
|
|
6459
|
+
return { listeners: removers.length, timers: timers.size };
|
|
6460
|
+
}
|
|
6461
|
+
};
|
|
6462
|
+
}
|
|
6463
|
+
function createPlaybackPublisher(options = {}) {
|
|
6464
|
+
const { host, channel, assetId, origin } = context(options), source = options.source;
|
|
6465
|
+
const target = options.targetWindow || host.parent;
|
|
6466
|
+
if (!source?.addEventListener || !source?.pause || !target?.postMessage || target === host) throw new TypeError("A playback source and distinct target Window are required");
|
|
6467
|
+
const owners = publisherOwners.get(host) || /* @__PURE__ */ new Map(), key = JSON.stringify([origin, channel, assetId]);
|
|
6468
|
+
const routes = owners.get(target) || /* @__PURE__ */ new Set();
|
|
6469
|
+
if (routes.has(key)) throw new Error("Child playback route already has a publisher");
|
|
6470
|
+
const owned = resources(host);
|
|
6471
|
+
let session = null, sequence = 0, commandSequence = 0, timer = 0, destroyed = false, suspended = false, lastPublished = null;
|
|
6472
|
+
const envelope = (type, extra = {}) => ({ protocol: PROTOCOL, channel, assetId, session, type, ...extra });
|
|
6473
|
+
function send(type, extra) {
|
|
6474
|
+
target.postMessage(envelope(type, extra), origin);
|
|
6475
|
+
}
|
|
6476
|
+
function stop() {
|
|
6477
|
+
owned.cancel(timer);
|
|
6478
|
+
timer = 0;
|
|
6479
|
+
}
|
|
6480
|
+
function sample(event = "snapshot") {
|
|
6481
|
+
if (event === "timeupdate" && lastPublished !== null && host.performance.now() - lastPublished < 250) return;
|
|
6482
|
+
stop();
|
|
6483
|
+
if (destroyed || suspended || !session) return;
|
|
6484
|
+
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 };
|
|
6485
|
+
if (!validState(state)) return;
|
|
6486
|
+
lastPublished = host.performance.now();
|
|
6487
|
+
send("state", { sequence: ++sequence, event, state });
|
|
6488
|
+
if (!state.paused && !state.ended && !state.error && !state.buffering && !host.document.hidden) timer = owned.later(() => sample(), 250);
|
|
6489
|
+
}
|
|
6490
|
+
function disconnect(reason) {
|
|
6491
|
+
stop();
|
|
6492
|
+
source.pause();
|
|
6493
|
+
if (session) send("disconnect", { reason });
|
|
6494
|
+
session = null;
|
|
6495
|
+
}
|
|
6496
|
+
function message(event) {
|
|
6497
|
+
const data = event.data;
|
|
6498
|
+
if (destroyed || suspended || event.source !== target || event.origin !== origin || !packet(data, channel, assetId)) return;
|
|
6499
|
+
if (data.type === "connect" && typeof data.session === "string" && /^[0-9a-f-]{36}$/i.test(data.session)) {
|
|
6500
|
+
if (session !== data.session) {
|
|
6501
|
+
session = data.session;
|
|
6502
|
+
sequence = 0;
|
|
6503
|
+
commandSequence = 0;
|
|
6504
|
+
lastPublished = null;
|
|
6505
|
+
}
|
|
6506
|
+
sample();
|
|
6507
|
+
return;
|
|
6508
|
+
}
|
|
6509
|
+
if (data.session !== session || !session) return;
|
|
6510
|
+
if (data.type === "disconnect") {
|
|
6511
|
+
disconnect("parent-disconnected");
|
|
6512
|
+
return;
|
|
6513
|
+
}
|
|
6514
|
+
if (data.type !== "command" || !Number.isSafeInteger(data.sequence) || data.sequence <= commandSequence) return;
|
|
6515
|
+
if (!["pause", "seek", "play"].includes(data.command)) return;
|
|
6516
|
+
if (data.command === "seek" && (typeof data.time !== "number" || !Number.isFinite(data.time) || data.time < 0 || data.time > Number(source.duration))) return;
|
|
6517
|
+
commandSequence = data.sequence;
|
|
6518
|
+
const currentSession = session;
|
|
6519
|
+
Promise.resolve().then(() => {
|
|
6520
|
+
if (destroyed || session !== currentSession) return;
|
|
6521
|
+
if (data.command === "pause") source.pause();
|
|
6522
|
+
else if (data.command === "seek") source.currentTime = data.time;
|
|
6523
|
+
else if (options.allowPlayCommands === true) return source.play();
|
|
6524
|
+
else throw new Error("Parent play commands are disabled");
|
|
6525
|
+
}).then(() => {
|
|
6526
|
+
if (!destroyed && session === currentSession) {
|
|
6527
|
+
sample();
|
|
6528
|
+
send("ack", { sequence: data.sequence, ok: true });
|
|
6529
|
+
}
|
|
6530
|
+
}, () => {
|
|
6531
|
+
if (!destroyed && session === currentSession) send("ack", { sequence: data.sequence, ok: false });
|
|
6532
|
+
});
|
|
6533
|
+
}
|
|
6534
|
+
owned.listen(host, "message", message);
|
|
6535
|
+
function requestClose() {
|
|
6536
|
+
if (!destroyed && !suspended && session) send("dismiss", { sequence: ++sequence });
|
|
6537
|
+
}
|
|
6538
|
+
if (options.notifyEscape === true) owned.listen(host.document, "keydown", (event) => {
|
|
6539
|
+
if (event.key === "Escape" && !event.defaultPrevented && !host.document.fullscreenElement) requestClose();
|
|
6540
|
+
});
|
|
6541
|
+
for (const event of EVENTS) if (event !== "snapshot") owned.listen(source, event, () => sample(event));
|
|
6542
|
+
owned.listen(host.document, "visibilitychange", () => {
|
|
6543
|
+
if (host.document.hidden) {
|
|
6544
|
+
stop();
|
|
6545
|
+
if (session) {
|
|
6546
|
+
sample("waiting");
|
|
6547
|
+
stop();
|
|
6548
|
+
}
|
|
6549
|
+
} else sample();
|
|
6550
|
+
});
|
|
6551
|
+
owned.listen(host, "pagehide", () => {
|
|
6552
|
+
suspended = true;
|
|
6553
|
+
disconnect("pagehide");
|
|
6554
|
+
});
|
|
6555
|
+
owned.listen(host, "pageshow", () => {
|
|
6556
|
+
if (!destroyed) {
|
|
6557
|
+
suspended = false;
|
|
6558
|
+
send("available");
|
|
6559
|
+
}
|
|
6560
|
+
});
|
|
6561
|
+
routes.add(key);
|
|
6562
|
+
owners.set(target, routes);
|
|
6563
|
+
publisherOwners.set(host, owners);
|
|
6564
|
+
send("available");
|
|
6565
|
+
return { requestClose, teardown() {
|
|
6566
|
+
if (destroyed) return;
|
|
6567
|
+
disconnect("teardown");
|
|
6568
|
+
destroyed = true;
|
|
6569
|
+
owned.dispose();
|
|
6570
|
+
routes.delete(key);
|
|
6571
|
+
if (!routes.size) owners.delete(target);
|
|
6572
|
+
}, snapshot() {
|
|
6573
|
+
return { kind: "child-publisher", connected: !!session, destroyed, suspended, ...owned.snapshot() };
|
|
6574
|
+
} };
|
|
6575
|
+
}
|
|
6576
|
+
function createPlaybackReceiver(options = {}) {
|
|
6577
|
+
const { host, channel, assetId, origin } = context(options), iframe = options.iframe;
|
|
6578
|
+
if (iframe?.tagName !== "IFRAME" || !iframe.contentWindow) throw new TypeError("An attached iframe is required");
|
|
6579
|
+
if (receiverOwners.has(iframe)) throw new Error("The child frame already has a playback receiver");
|
|
6580
|
+
const staleAfter = options.staleAfterMilliseconds ?? 1500;
|
|
6581
|
+
if (typeof staleAfter !== "number" || !Number.isFinite(staleAfter) || staleAfter < 750 || staleAfter > 1e4) throw new RangeError("staleAfterMilliseconds must be between 750 and 10000");
|
|
6582
|
+
const owned = resources(host), player = new host.EventTarget(), pending = /* @__PURE__ */ new Map();
|
|
6583
|
+
let session = "", sequence = 0, commands = 0, lastWall = 0, timer = 0, connectTimer = 0, destroyed = false, connected = false, status = "connecting";
|
|
6584
|
+
let state = { time: 0, duration: 0, rate: 1, paused: true, ended: false, seeking: false, buffering: false, error: false };
|
|
6585
|
+
let resolveReady, rejectReady, ready, readySettled = false;
|
|
6586
|
+
function prepareReady() {
|
|
6587
|
+
readySettled = false;
|
|
6588
|
+
ready = new Promise((resolve, reject) => {
|
|
6589
|
+
resolveReady = (value) => {
|
|
6590
|
+
readySettled = true;
|
|
6591
|
+
resolve(value);
|
|
6592
|
+
};
|
|
6593
|
+
rejectReady = (error) => {
|
|
6594
|
+
readySettled = true;
|
|
6595
|
+
reject(error);
|
|
6596
|
+
};
|
|
6597
|
+
});
|
|
6598
|
+
ready.catch(() => {
|
|
6599
|
+
});
|
|
6600
|
+
}
|
|
6601
|
+
prepareReady();
|
|
6602
|
+
const emit = (event) => player.dispatchEvent(new host.Event(event));
|
|
6603
|
+
const envelope = (type, extra = {}) => ({ protocol: PROTOCOL, channel, assetId, session, type, ...extra });
|
|
6604
|
+
function send(type, extra) {
|
|
6605
|
+
iframe.contentWindow?.postMessage(envelope(type, extra), origin);
|
|
6606
|
+
}
|
|
6607
|
+
function cancelPending() {
|
|
6608
|
+
for (const request of pending.values()) {
|
|
6609
|
+
owned.cancel(request.timer);
|
|
6610
|
+
request.reject(new Error("Child playback disconnected"));
|
|
6611
|
+
}
|
|
6612
|
+
pending.clear();
|
|
6613
|
+
}
|
|
6614
|
+
function stop(reason) {
|
|
6615
|
+
owned.cancel(timer);
|
|
6616
|
+
owned.cancel(connectTimer);
|
|
6617
|
+
timer = connectTimer = 0;
|
|
6618
|
+
const playing = !player.paused;
|
|
6619
|
+
state = { ...state, time: player.currentTime, paused: true, buffering: false };
|
|
6620
|
+
connected = false;
|
|
6621
|
+
status = reason;
|
|
6622
|
+
cancelPending();
|
|
6623
|
+
if (playing) emit("pause");
|
|
6624
|
+
}
|
|
6625
|
+
function connect() {
|
|
6626
|
+
if (destroyed) return ready;
|
|
6627
|
+
if (readySettled) prepareReady();
|
|
6628
|
+
stop("connecting");
|
|
6629
|
+
session = host.crypto.randomUUID();
|
|
6630
|
+
sequence = 0;
|
|
6631
|
+
send("connect");
|
|
6632
|
+
const started = host.performance.now();
|
|
6633
|
+
const retry = () => {
|
|
6634
|
+
if (destroyed || connected) return;
|
|
6635
|
+
if (host.performance.now() - started >= 1e4) {
|
|
6636
|
+
send("disconnect");
|
|
6637
|
+
stop("unavailable");
|
|
6638
|
+
session = "";
|
|
6639
|
+
rejectReady(new Error("Child playback did not become ready"));
|
|
6640
|
+
return;
|
|
6641
|
+
}
|
|
6642
|
+
send("connect");
|
|
6643
|
+
connectTimer = owned.later(retry, 500);
|
|
6644
|
+
};
|
|
6645
|
+
connectTimer = owned.later(retry, 500);
|
|
6646
|
+
return ready;
|
|
6647
|
+
}
|
|
6648
|
+
function command(name, time) {
|
|
6649
|
+
if (destroyed || !connected) return Promise.reject(new Error("Child playback is unavailable"));
|
|
6650
|
+
if (pending.size >= 16) return Promise.reject(new Error("Too many pending child playback commands"));
|
|
6651
|
+
const id = ++commands;
|
|
6652
|
+
return new Promise((resolve, reject) => {
|
|
6653
|
+
const timeout = owned.later(() => {
|
|
6654
|
+
pending.delete(id);
|
|
6655
|
+
reject(new Error("Child playback command timed out"));
|
|
6656
|
+
}, 2e3);
|
|
6657
|
+
pending.set(id, { resolve, reject, timer: timeout });
|
|
6658
|
+
send("command", { sequence: id, command: name, ...name === "seek" ? { time } : {} });
|
|
6659
|
+
});
|
|
6660
|
+
}
|
|
6661
|
+
Object.defineProperties(player, {
|
|
6662
|
+
nodeType: { value: 1 },
|
|
6663
|
+
tagName: { value: "VIDEO" },
|
|
6664
|
+
ownerDocument: { value: host.document },
|
|
6665
|
+
isConnected: { get: () => !destroyed && iframe.isConnected },
|
|
6666
|
+
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) => {
|
|
6667
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0 || value > state.duration) throw new RangeError("Seek must be inside the child duration");
|
|
6668
|
+
command("seek", value).catch(() => {
|
|
6669
|
+
});
|
|
6670
|
+
} },
|
|
6671
|
+
duration: { get: () => state.duration },
|
|
6672
|
+
paused: { get: () => state.paused || state.buffering || state.error || !connected },
|
|
6673
|
+
ended: { get: () => state.ended },
|
|
6674
|
+
seeking: { get: () => state.seeking },
|
|
6675
|
+
playbackRate: { get: () => state.rate },
|
|
6676
|
+
error: { get: () => state.error ? new Error("Child media error") : null }
|
|
6677
|
+
});
|
|
6678
|
+
player.play = () => command("play");
|
|
6679
|
+
player.pause = () => {
|
|
6680
|
+
command("pause").catch(() => {
|
|
6681
|
+
});
|
|
6682
|
+
};
|
|
6683
|
+
function message(event) {
|
|
6684
|
+
const data = event.data;
|
|
6685
|
+
if (destroyed || event.source !== iframe.contentWindow || event.origin !== origin || !packet(data, channel, assetId)) return;
|
|
6686
|
+
if (data.type === "available") {
|
|
6687
|
+
if (!host.document.hidden) {
|
|
6688
|
+
if (connected) send("connect");
|
|
6689
|
+
else connect();
|
|
6690
|
+
}
|
|
6691
|
+
return;
|
|
6692
|
+
}
|
|
6693
|
+
if (data.session !== session) return;
|
|
6694
|
+
if (data.type === "dismiss" && Number.isSafeInteger(data.sequence) && data.sequence > sequence) {
|
|
6695
|
+
sequence = data.sequence;
|
|
6696
|
+
emit("dismiss");
|
|
6697
|
+
return;
|
|
6698
|
+
}
|
|
6699
|
+
if (data.type === "disconnect") {
|
|
6700
|
+
stop("disconnected");
|
|
6701
|
+
session = "";
|
|
6702
|
+
return;
|
|
6703
|
+
}
|
|
6704
|
+
if (data.type === "ack" && Number.isSafeInteger(data.sequence) && typeof data.ok === "boolean") {
|
|
6705
|
+
const request = pending.get(data.sequence);
|
|
6706
|
+
if (request) {
|
|
6707
|
+
owned.cancel(request.timer);
|
|
6708
|
+
pending.delete(data.sequence);
|
|
6709
|
+
data.ok ? request.resolve() : request.reject(new Error("Child playback rejected the command"));
|
|
6710
|
+
}
|
|
6711
|
+
return;
|
|
6712
|
+
}
|
|
6713
|
+
if (data.type !== "state" || !Number.isSafeInteger(data.sequence) || data.sequence <= sequence || !EVENTS.has(data.event) || !validState(data.state)) return;
|
|
6714
|
+
const previous = state, wasConnected = connected, wasPaused = player.paused;
|
|
6715
|
+
sequence = data.sequence;
|
|
6716
|
+
state = { ...data.state };
|
|
6717
|
+
lastWall = host.performance.now();
|
|
6718
|
+
connected = true;
|
|
6719
|
+
status = "connected";
|
|
6720
|
+
owned.cancel(connectTimer);
|
|
6721
|
+
connectTimer = 0;
|
|
6722
|
+
owned.cancel(timer);
|
|
6723
|
+
timer = 0;
|
|
6724
|
+
if (!wasConnected) {
|
|
6725
|
+
emit("loadedmetadata");
|
|
6726
|
+
resolveReady(controller);
|
|
6727
|
+
} else if (previous.duration !== state.duration) emit("durationchange");
|
|
6728
|
+
if (previous.rate !== state.rate) emit("ratechange");
|
|
6729
|
+
if (state.seeking && !previous.seeking) emit("seeking");
|
|
6730
|
+
if (!state.seeking && previous.seeking) emit("seeked");
|
|
6731
|
+
if (!state.seeking && !previous.seeking && wasConnected && Math.abs(state.time - previous.time - (wasPaused ? 0 : (lastWall - previousWall) / 1e3 * previous.rate)) > 0.4) {
|
|
6732
|
+
emit("seeking");
|
|
6733
|
+
emit("seeked");
|
|
6734
|
+
}
|
|
6735
|
+
previousWall = lastWall;
|
|
6736
|
+
if (wasPaused && !player.paused) {
|
|
6737
|
+
emit("play");
|
|
6738
|
+
emit("playing");
|
|
6739
|
+
}
|
|
6740
|
+
if (!wasPaused && player.paused) emit("pause");
|
|
6741
|
+
if (state.ended && !previous.ended) emit("ended");
|
|
6742
|
+
if (data.event === "waiting") emit("waiting");
|
|
6743
|
+
if (state.error && !previous.error) emit("error");
|
|
6744
|
+
emit("timeupdate");
|
|
6745
|
+
if (!player.paused) timer = owned.later(() => {
|
|
6746
|
+
send("disconnect");
|
|
6747
|
+
stop("stale");
|
|
6748
|
+
session = "";
|
|
6749
|
+
emit("waiting");
|
|
6750
|
+
}, staleAfter);
|
|
6751
|
+
}
|
|
6752
|
+
let previousWall = 0;
|
|
6753
|
+
owned.listen(host, "message", message);
|
|
6754
|
+
owned.listen(iframe, "load", connect);
|
|
6755
|
+
owned.listen(host, "pagehide", () => {
|
|
6756
|
+
send("disconnect");
|
|
6757
|
+
stop("suspended");
|
|
6758
|
+
session = "";
|
|
6759
|
+
});
|
|
6760
|
+
owned.listen(host, "pageshow", connect);
|
|
6761
|
+
owned.listen(host.document, "visibilitychange", () => {
|
|
6762
|
+
if (host.document.hidden) {
|
|
6763
|
+
send("command", { sequence: ++commands, command: "pause" });
|
|
6764
|
+
stop("hidden");
|
|
6765
|
+
session = "";
|
|
6766
|
+
} else connect();
|
|
6767
|
+
});
|
|
6768
|
+
const observer = new host.MutationObserver(() => {
|
|
6769
|
+
if (!iframe.isConnected) controller.teardown();
|
|
6770
|
+
});
|
|
6771
|
+
observer.observe(host.document.documentElement, { childList: true, subtree: true });
|
|
6772
|
+
const controller = { player, get ready() {
|
|
6773
|
+
return ready;
|
|
6774
|
+
}, reconnect: connect, teardown() {
|
|
6775
|
+
if (destroyed) return;
|
|
6776
|
+
send("command", { sequence: ++commands, command: "pause" });
|
|
6777
|
+
send("disconnect");
|
|
6778
|
+
stop("destroyed");
|
|
6779
|
+
destroyed = true;
|
|
6780
|
+
session = "";
|
|
6781
|
+
rejectReady(new Error("Child playback owner removed"));
|
|
6782
|
+
observer.disconnect();
|
|
6783
|
+
owned.dispose();
|
|
6784
|
+
receiverOwners.delete(iframe);
|
|
6785
|
+
}, snapshot() {
|
|
6786
|
+
return { kind: "child-receiver", status, connected, destroyed, time: player.currentTime, paused: player.paused, assetId, ...owned.snapshot() };
|
|
6787
|
+
} };
|
|
6788
|
+
receiverOwners.set(iframe, controller);
|
|
6789
|
+
connect();
|
|
6790
|
+
return controller;
|
|
6791
|
+
}
|
|
6792
|
+
function installChildPlayback(scope, api, host) {
|
|
6793
|
+
function own(controller) {
|
|
6794
|
+
const teardown = controller.teardown.bind(controller);
|
|
6795
|
+
const remove = scope.onDispose(() => teardown());
|
|
6796
|
+
controller.teardown = () => {
|
|
6797
|
+
remove();
|
|
6798
|
+
teardown();
|
|
6799
|
+
};
|
|
6800
|
+
return controller;
|
|
6801
|
+
}
|
|
6802
|
+
api.publishPlayback = (options) => {
|
|
6803
|
+
if (api.destroyed) throw new Error("DanceMoves owner is destroyed");
|
|
6804
|
+
return own(createPlaybackPublisher({ ...options, window: host }));
|
|
6805
|
+
};
|
|
6806
|
+
api.receiveChildPlayback = (options) => {
|
|
6807
|
+
if (api.destroyed) throw new Error("DanceMoves owner is destroyed");
|
|
6808
|
+
if (typeof api.registerPlaybackProvider !== "function") throw new Error("The shared playback provider API is required");
|
|
6809
|
+
if (options?.masterVerified !== true) throw new TypeError("An explicitly verified master association is required");
|
|
6810
|
+
const receiver = createPlaybackReceiver({ ...options, window: host });
|
|
6811
|
+
let registration;
|
|
6812
|
+
try {
|
|
6813
|
+
registration = api.registerPlaybackProvider({ ...options, player: receiver.player });
|
|
6814
|
+
} catch (error) {
|
|
6815
|
+
receiver.teardown();
|
|
6816
|
+
throw error;
|
|
6817
|
+
}
|
|
6818
|
+
return own({ source: registration.source, player: receiver.player, get ready() {
|
|
6819
|
+
return receiver.ready;
|
|
6820
|
+
}, mapping: registration.mapping, reconnect: receiver.reconnect, teardown() {
|
|
6821
|
+
receiver.teardown();
|
|
6822
|
+
registration.teardown();
|
|
6823
|
+
}, snapshot() {
|
|
6824
|
+
return { ...receiver.snapshot(), mapping: registration.mapping };
|
|
6825
|
+
} });
|
|
6826
|
+
};
|
|
6827
|
+
}
|
|
6828
|
+
|
|
5921
6829
|
// .build/modules/version.mjs
|
|
5922
|
-
var VERSION = "3.1.
|
|
6830
|
+
var VERSION = "3.1.21";
|
|
5923
6831
|
|
|
5924
6832
|
// .build/modules/index.mjs
|
|
5925
6833
|
var mounted = /* @__PURE__ */ new WeakMap();
|
|
@@ -5954,6 +6862,7 @@ function createDanceMoves(options = {}) {
|
|
|
5954
6862
|
}
|
|
5955
6863
|
try {
|
|
5956
6864
|
runtime_default.core(scope);
|
|
6865
|
+
installPlayback(scope, w.DanceMoves, root.ownerDocument.defaultView);
|
|
5957
6866
|
runtime_default.effects(scope);
|
|
5958
6867
|
runtime_default.orientationCore(scope);
|
|
5959
6868
|
runtime_default.native(scope);
|
|
@@ -5986,6 +6895,7 @@ function createDanceMoves(options = {}) {
|
|
|
5986
6895
|
}
|
|
5987
6896
|
});
|
|
5988
6897
|
Object.defineProperty(api, "destroyed", { get: () => scope.disposed });
|
|
6898
|
+
installChildPlayback(scope, api, ownerDocument.defaultView);
|
|
5989
6899
|
api.ready = Promise.resolve(api.ready).then(() => {
|
|
5990
6900
|
if (!scope.disposed) scope.document.dispatchEvent(new w.CustomEvent("dance-moves-ready", {
|
|
5991
6901
|
bubbles: true,
|
|
@@ -6008,6 +6918,8 @@ export {
|
|
|
6008
6918
|
VERSION,
|
|
6009
6919
|
createDanceMoves,
|
|
6010
6920
|
createMemoryPageStore,
|
|
6921
|
+
createPlaybackPublisher,
|
|
6922
|
+
createPlaybackReceiver,
|
|
6011
6923
|
fromWordPressMeta,
|
|
6012
6924
|
validatePageConfig
|
|
6013
6925
|
};
|