@editframe/elements 0.26.3-beta.0 → 0.26.4-beta.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.
Files changed (132) hide show
  1. package/package.json +2 -2
  2. package/scripts/build-css.js +3 -3
  3. package/tsdown.config.ts +1 -1
  4. package/src/elements/ContextProxiesController.ts +0 -124
  5. package/src/elements/CrossUpdateController.ts +0 -22
  6. package/src/elements/EFAudio.browsertest.ts +0 -706
  7. package/src/elements/EFAudio.ts +0 -56
  8. package/src/elements/EFCaptions.browsertest.ts +0 -1960
  9. package/src/elements/EFCaptions.ts +0 -823
  10. package/src/elements/EFImage.browsertest.ts +0 -120
  11. package/src/elements/EFImage.ts +0 -113
  12. package/src/elements/EFMedia/AssetIdMediaEngine.test.ts +0 -224
  13. package/src/elements/EFMedia/AssetIdMediaEngine.ts +0 -110
  14. package/src/elements/EFMedia/AssetMediaEngine.browsertest.ts +0 -140
  15. package/src/elements/EFMedia/AssetMediaEngine.ts +0 -385
  16. package/src/elements/EFMedia/BaseMediaEngine.browsertest.ts +0 -400
  17. package/src/elements/EFMedia/BaseMediaEngine.ts +0 -505
  18. package/src/elements/EFMedia/BufferedSeekingInput.browsertest.ts +0 -386
  19. package/src/elements/EFMedia/BufferedSeekingInput.ts +0 -430
  20. package/src/elements/EFMedia/JitMediaEngine.browsertest.ts +0 -226
  21. package/src/elements/EFMedia/JitMediaEngine.ts +0 -256
  22. package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.browsertest.ts +0 -679
  23. package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.ts +0 -117
  24. package/src/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.ts +0 -246
  25. package/src/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.browsertest.ts +0 -59
  26. package/src/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.ts +0 -27
  27. package/src/elements/EFMedia/audioTasks/makeAudioInputTask.browsertest.ts +0 -55
  28. package/src/elements/EFMedia/audioTasks/makeAudioInputTask.ts +0 -53
  29. package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.chunkboundary.regression.browsertest.ts +0 -207
  30. package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.ts +0 -72
  31. package/src/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.ts +0 -32
  32. package/src/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.ts +0 -29
  33. package/src/elements/EFMedia/audioTasks/makeAudioTasksVideoOnly.browsertest.ts +0 -95
  34. package/src/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.ts +0 -184
  35. package/src/elements/EFMedia/shared/AudioSpanUtils.ts +0 -129
  36. package/src/elements/EFMedia/shared/BufferUtils.ts +0 -342
  37. package/src/elements/EFMedia/shared/GlobalInputCache.ts +0 -77
  38. package/src/elements/EFMedia/shared/MediaTaskUtils.ts +0 -44
  39. package/src/elements/EFMedia/shared/PrecisionUtils.ts +0 -46
  40. package/src/elements/EFMedia/shared/RenditionHelpers.browsertest.ts +0 -246
  41. package/src/elements/EFMedia/shared/RenditionHelpers.ts +0 -56
  42. package/src/elements/EFMedia/shared/ThumbnailExtractor.ts +0 -227
  43. package/src/elements/EFMedia/tasks/makeMediaEngineTask.browsertest.ts +0 -167
  44. package/src/elements/EFMedia/tasks/makeMediaEngineTask.ts +0 -88
  45. package/src/elements/EFMedia/videoTasks/MainVideoInputCache.ts +0 -76
  46. package/src/elements/EFMedia/videoTasks/ScrubInputCache.ts +0 -61
  47. package/src/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.ts +0 -114
  48. package/src/elements/EFMedia/videoTasks/makeScrubVideoInitSegmentFetchTask.ts +0 -35
  49. package/src/elements/EFMedia/videoTasks/makeScrubVideoInputTask.ts +0 -52
  50. package/src/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.ts +0 -124
  51. package/src/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.ts +0 -44
  52. package/src/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.ts +0 -32
  53. package/src/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.ts +0 -370
  54. package/src/elements/EFMedia/videoTasks/makeVideoBufferTask.ts +0 -109
  55. package/src/elements/EFMedia.browsertest.ts +0 -872
  56. package/src/elements/EFMedia.ts +0 -341
  57. package/src/elements/EFSourceMixin.ts +0 -60
  58. package/src/elements/EFSurface.browsertest.ts +0 -151
  59. package/src/elements/EFSurface.ts +0 -142
  60. package/src/elements/EFTemporal.browsertest.ts +0 -215
  61. package/src/elements/EFTemporal.ts +0 -800
  62. package/src/elements/EFThumbnailStrip.browsertest.ts +0 -585
  63. package/src/elements/EFThumbnailStrip.media-engine.browsertest.ts +0 -714
  64. package/src/elements/EFThumbnailStrip.ts +0 -906
  65. package/src/elements/EFTimegroup.browsertest.ts +0 -934
  66. package/src/elements/EFTimegroup.ts +0 -882
  67. package/src/elements/EFVideo.browsertest.ts +0 -1482
  68. package/src/elements/EFVideo.ts +0 -564
  69. package/src/elements/EFWaveform.ts +0 -547
  70. package/src/elements/FetchContext.browsertest.ts +0 -401
  71. package/src/elements/FetchMixin.ts +0 -38
  72. package/src/elements/SampleBuffer.ts +0 -94
  73. package/src/elements/TargetController.browsertest.ts +0 -230
  74. package/src/elements/TargetController.ts +0 -224
  75. package/src/elements/TimegroupController.ts +0 -26
  76. package/src/elements/durationConverter.ts +0 -35
  77. package/src/elements/parseTimeToMs.ts +0 -9
  78. package/src/elements/printTaskStatus.ts +0 -16
  79. package/src/elements/renderTemporalAudio.ts +0 -108
  80. package/src/elements/updateAnimations.browsertest.ts +0 -1884
  81. package/src/elements/updateAnimations.ts +0 -217
  82. package/src/elements/util.ts +0 -24
  83. package/src/gui/ContextMixin.browsertest.ts +0 -860
  84. package/src/gui/ContextMixin.ts +0 -562
  85. package/src/gui/Controllable.browsertest.ts +0 -258
  86. package/src/gui/Controllable.ts +0 -41
  87. package/src/gui/EFConfiguration.ts +0 -40
  88. package/src/gui/EFControls.browsertest.ts +0 -389
  89. package/src/gui/EFControls.ts +0 -195
  90. package/src/gui/EFDial.browsertest.ts +0 -84
  91. package/src/gui/EFDial.ts +0 -172
  92. package/src/gui/EFFilmstrip.browsertest.ts +0 -712
  93. package/src/gui/EFFilmstrip.ts +0 -1349
  94. package/src/gui/EFFitScale.ts +0 -152
  95. package/src/gui/EFFocusOverlay.ts +0 -79
  96. package/src/gui/EFPause.browsertest.ts +0 -202
  97. package/src/gui/EFPause.ts +0 -73
  98. package/src/gui/EFPlay.browsertest.ts +0 -202
  99. package/src/gui/EFPlay.ts +0 -73
  100. package/src/gui/EFPreview.ts +0 -74
  101. package/src/gui/EFResizableBox.browsertest.ts +0 -79
  102. package/src/gui/EFResizableBox.ts +0 -898
  103. package/src/gui/EFScrubber.ts +0 -151
  104. package/src/gui/EFTimeDisplay.browsertest.ts +0 -237
  105. package/src/gui/EFTimeDisplay.ts +0 -55
  106. package/src/gui/EFToggleLoop.ts +0 -35
  107. package/src/gui/EFTogglePlay.ts +0 -70
  108. package/src/gui/EFWorkbench.ts +0 -115
  109. package/src/gui/PlaybackController.ts +0 -527
  110. package/src/gui/TWMixin.css +0 -6
  111. package/src/gui/TWMixin.ts +0 -61
  112. package/src/gui/TargetOrContextMixin.ts +0 -185
  113. package/src/gui/currentTimeContext.ts +0 -5
  114. package/src/gui/durationContext.ts +0 -3
  115. package/src/gui/efContext.ts +0 -6
  116. package/src/gui/fetchContext.ts +0 -5
  117. package/src/gui/focusContext.ts +0 -7
  118. package/src/gui/focusedElementContext.ts +0 -5
  119. package/src/gui/playingContext.ts +0 -5
  120. package/src/otel/BridgeSpanExporter.ts +0 -150
  121. package/src/otel/setupBrowserTracing.ts +0 -73
  122. package/src/otel/tracingHelpers.ts +0 -251
  123. package/src/transcoding/cache/RequestDeduplicator.test.ts +0 -170
  124. package/src/transcoding/cache/RequestDeduplicator.ts +0 -65
  125. package/src/transcoding/cache/URLTokenDeduplicator.test.ts +0 -182
  126. package/src/transcoding/cache/URLTokenDeduplicator.ts +0 -101
  127. package/src/transcoding/types/index.ts +0 -312
  128. package/src/transcoding/utils/MediaUtils.ts +0 -63
  129. package/src/transcoding/utils/UrlGenerator.ts +0 -68
  130. package/src/transcoding/utils/constants.ts +0 -36
  131. package/src/utils/LRUCache.test.ts +0 -274
  132. package/src/utils/LRUCache.ts +0 -696
@@ -1,934 +0,0 @@
1
- import {
2
- html,
3
- LitElement,
4
- render as litRender,
5
- type TemplateResult,
6
- } from "lit";
7
- import { assert, beforeEach, describe, test } from "vitest";
8
- import { EFTimegroup } from "./EFTimegroup.js";
9
- import "./EFTimegroup.js";
10
- import { customElement } from "lit/decorators/custom-element.js";
11
- import { ContextMixin } from "../gui/ContextMixin.js";
12
- import { EFTemporal } from "./EFTemporal.js";
13
- // Need workbench to make workbench wrapping occurs
14
- import "../gui/EFWorkbench.js";
15
- // Additional imports for sequence boundary test
16
- import "./EFVideo.js";
17
- import "../gui/EFConfiguration.js";
18
- import { Task } from "@lit/task";
19
- import type { MediaEngine } from "../transcoding/types/index.js";
20
- import { EFMedia } from "./EFMedia.js";
21
-
22
- beforeEach(() => {
23
- for (let i = 0; i < localStorage.length; i++) {
24
- const key = localStorage.key(i);
25
- if (typeof key !== "string") continue;
26
- localStorage.removeItem(key);
27
- }
28
- while (document.body.children.length) {
29
- document.body.children[0]?.remove();
30
- }
31
- });
32
-
33
- @customElement("test-context")
34
- class TestContext extends ContextMixin(LitElement) {}
35
-
36
- @customElement("timegroup-test-media")
37
- class TimegroupTestMedia extends EFMedia {
38
- mediaEngineTaskCount = 0;
39
- mediaEngineTask = new Task(this, {
40
- autoRun: false,
41
- args: () => ["source", null] as const,
42
- task: () => {
43
- this.mediaEngineTaskCount++;
44
- return Promise.resolve({} as unknown as MediaEngine);
45
- },
46
- });
47
- }
48
-
49
- @customElement("test-frame-task-a")
50
- class TestFrameTaskA extends EFTemporal(LitElement) {
51
- frameTaskCount = 0;
52
-
53
- frameTask = new Task(this, {
54
- autoRun: false,
55
- args: () => [],
56
- task: () => {
57
- this.frameTaskCount++;
58
- return Promise.resolve();
59
- },
60
- });
61
- }
62
-
63
- @customElement("test-frame-task-b")
64
- class TestFrameTaskB extends EFTemporal(LitElement) {
65
- frameTaskCount = 0;
66
-
67
- frameTask = new Task(this, {
68
- autoRun: false,
69
- args: () => [],
70
- task: () => {
71
- this.frameTaskCount++;
72
- return Promise.resolve();
73
- },
74
- });
75
- }
76
-
77
- @customElement("test-frame-task-c")
78
- class TestFrameTaskC extends EFTemporal(LitElement) {
79
- frameTaskCount = 0;
80
-
81
- frameTask = new Task(this, {
82
- autoRun: false,
83
- args: () => [],
84
- task: () => {
85
- this.frameTaskCount++;
86
- return Promise.resolve();
87
- },
88
- });
89
- }
90
-
91
- @customElement("test-temporal")
92
- class TestTemporal extends EFTemporal(LitElement) {
93
- get hasOwnDuration(): boolean {
94
- return true;
95
- }
96
- }
97
-
98
- declare global {
99
- interface HTMLElementTagNameMap {
100
- "test-temporal": TestTemporal;
101
- "test-context": TestContext;
102
- "timegroup-test-media": TimegroupTestMedia;
103
- "test-frame-task-a": TestFrameTaskA;
104
- "test-frame-task-b": TestFrameTaskB;
105
- "test-frame-task-c": TestFrameTaskC;
106
- }
107
- }
108
-
109
- const renderTimegroup = (result: TemplateResult) => {
110
- const container = document.createElement("div");
111
- litRender(result, container);
112
- const firstChild = container.querySelector("ef-timegroup");
113
- if (!firstChild) {
114
- throw new Error("No first child found");
115
- }
116
- if (!(firstChild instanceof EFTimegroup)) {
117
- throw new Error("First child is not an EFTimegroup");
118
- }
119
- document.body.appendChild(container);
120
- return firstChild;
121
- };
122
-
123
- describe(`<ef-timegroup mode='fit'>`, () => {
124
- test("duration is zero when there is no parent to fit into", () => {
125
- const timegroup = renderTimegroup(
126
- html`<ef-timegroup mode="fit"></ef-timegroup>`,
127
- );
128
- assert.equal(timegroup.durationMs, 0);
129
- });
130
-
131
- test("duration is zero when there is no parent to fit into, even if there are children with duration", () => {
132
- const timegroup = renderTimegroup(
133
- html`<ef-timegroup mode="fit">
134
- <ef-timegroup mode="fixed" duration="5s"></ef-timegroup>
135
- </ef-timegroup>`,
136
- );
137
- assert.equal(timegroup.durationMs, 0);
138
- });
139
-
140
- test("duration is the duration of the parent timegroup", () => {
141
- const timegroup = renderTimegroup(
142
- html`<ef-timegroup mode="fixed" duration="10s">
143
- <ef-timegroup id="child" mode="fit"></ef-timegroup>
144
- </ef-timegroup>`,
145
- );
146
- const child = timegroup.querySelector("#child") as EFTimegroup;
147
- assert.equal(child.durationMs, 10_000);
148
- });
149
-
150
- test("fit mode items inside a sequence are given zero duration and do not factor into the duration of the sequence", () => {
151
- const timegroup = renderTimegroup(
152
- html`<ef-timegroup mode="sequence">
153
- <ef-timegroup id="child" mode="fit"></ef-timegroup>
154
- </ef-timegroup>`,
155
- );
156
- const child = timegroup.querySelector("#child") as EFTimegroup;
157
- assert.equal(child.durationMs, 0);
158
- assert.equal(timegroup.durationMs, 0);
159
- });
160
-
161
- test("fit mode can be used to constrain a 'background' timegroup into a 'foreground' sequence", async () => {
162
- const timegroup = renderTimegroup(
163
- html`
164
- <ef-timegroup mode="contain">
165
- <ef-timegroup id="foreground" mode="sequence">
166
- <ef-timegroup mode="fixed" duration="10s"></ef-timegroup>
167
- <ef-timegroup mode="fixed" duration="10s"></ef-timegroup>
168
- </ef-timegroup>
169
- <ef-timegroup id="background" mode="fit">
170
- <ef-timegroup mode="fixed" duration="30s"></ef-timegroup>
171
- </ef-timegroup>
172
- </ef-timegroup>
173
- `,
174
- );
175
-
176
- const foreground = timegroup.querySelector("#foreground") as EFTimegroup;
177
- const background = timegroup.querySelector("#background") as EFTimegroup;
178
- assert.equal(foreground.durationMs, 20_000);
179
- assert.equal(background.durationMs, 20_000);
180
- });
181
- });
182
-
183
- describe(`<ef-timegroup mode="fixed">`, () => {
184
- test("can explicitly set a duration in seconds", async () => {
185
- const timegroup = renderTimegroup(
186
- html`<ef-timegroup mode="fixed" duration="10s"></ef-timegroup>`,
187
- );
188
- assert.equal(timegroup.durationMs, 10_000);
189
- });
190
-
191
- test("can explicitly set a duration in milliseconds", async () => {
192
- const timegroup = renderTimegroup(
193
- html`<ef-timegroup mode="fixed" duration="10ms"></ef-timegroup>`,
194
- );
195
- assert.equal(timegroup.durationMs, 10);
196
- });
197
- });
198
-
199
- describe(`<ef-timegroup mode="sequence">`, () => {
200
- test("fixed duration is ignored", () => {
201
- const timegroup = renderTimegroup(
202
- html`<ef-timegroup mode="sequence" duration="10s"></ef-timegroup>`,
203
- );
204
- assert.equal(timegroup.durationMs, 0);
205
- });
206
-
207
- test("duration is the sum of child time groups", async () => {
208
- const timegroup = renderTimegroup(
209
- html`
210
- <ef-timegroup mode="sequence">
211
- <ef-timegroup mode="fixed" duration="5s"></ef-timegroup>
212
- <ef-timegroup mode="fixed" duration="5s"></ef-timegroup>
213
- </ef-timegroup>
214
- `,
215
- );
216
- assert.equal(timegroup.durationMs, 10_000);
217
- });
218
-
219
- test("duration can include any element with a durationMs value", async () => {
220
- const timegroup = renderTimegroup(
221
- html`
222
- <ef-timegroup mode="sequence">
223
- <ef-timegroup mode="fixed" duration="5s"></ef-timegroup>
224
- <test-temporal duration="5s"></test-temporal>
225
- </ef-timegroup>
226
- `,
227
- );
228
-
229
- assert.equal(timegroup.durationMs, 10_000);
230
- });
231
-
232
- test("arbitrary html does not factor into the calculation of a sequence duration", () => {
233
- const timegroup = renderTimegroup(
234
- html`
235
- <ef-timegroup mode="sequence">
236
- <div>
237
- <ef-timegroup mode="fixed" duration="5s"></ef-timegroup>
238
- </div>
239
- </ef-timegroup>
240
- `,
241
- );
242
- assert.equal(timegroup.durationMs, 5_000);
243
- });
244
-
245
- test("nested time groups do not factor into the calculation of a sequence duration", async () => {
246
- const timegroup = renderTimegroup(
247
- html`
248
- <ef-timegroup mode="sequence">
249
- <ef-timegroup mode="fixed" duration="5s">
250
- <ef-timegroup mode="fixed" duration="5s"></ef-timegroup>
251
- </ef-timegroup>
252
- </ef-timegroup>
253
- `,
254
- );
255
- assert.equal(timegroup.durationMs, 5_000);
256
- });
257
- });
258
-
259
- describe(`<ef-timegroup mode="contain">`, () => {
260
- test("fixed duration is ignored", () => {
261
- const timegroup = renderTimegroup(
262
- html`<ef-timegroup mode="contain" duration="10s"></ef-timegroup>`,
263
- );
264
- assert.equal(timegroup.durationMs, 0);
265
- });
266
-
267
- test("duration is the maximum of it's child time groups", async () => {
268
- const timegroup = renderTimegroup(
269
- html`
270
- <ef-timegroup mode="contain">
271
- <ef-timegroup mode="fixed" duration="5s"></ef-timegroup>
272
- <ef-timegroup mode="fixed" duration="10s"></ef-timegroup>
273
- </ef-timegroup>
274
- `,
275
- );
276
- assert.equal(timegroup.durationMs, 10_000);
277
- });
278
- });
279
-
280
- describe("startTimeMs", () => {
281
- test("is computed relative to the root time group", async () => {
282
- const timegroup = renderTimegroup(
283
- html`<ef-timegroup id="root" mode="sequence">
284
- <ef-timegroup id="a" mode="fixed" duration="5s"></ef-timegroup>
285
- <ef-timegroup id="b" mode="sequence">
286
- <ef-timegroup id="c" mode="fixed" duration="5s"></ef-timegroup>
287
- <ef-timegroup id="d" mode="contain">
288
- <ef-timegroup id="e" mode="fixed" duration="5s"></ef-timegroup>
289
- <ef-timegroup id="f" mode="fixed" duration="5s"></ef-timegroup>
290
- </ef-timegroup>
291
- </ef-timegroup>
292
- </ef-timegroup>`,
293
- );
294
-
295
- const a = timegroup.querySelector("#a") as EFTimegroup;
296
- const b = timegroup.querySelector("#b") as EFTimegroup;
297
- const c = timegroup.querySelector("#c") as EFTimegroup;
298
- const d = timegroup.querySelector("#d") as EFTimegroup;
299
- const e = timegroup.querySelector("#e") as EFTimegroup;
300
- const f = timegroup.querySelector("#f") as EFTimegroup;
301
-
302
- assert.equal(a.startTimeMs, 0);
303
- assert.equal(b.startTimeMs, 5_000);
304
- assert.equal(c.startTimeMs, 5_000);
305
- assert.equal(d.startTimeMs, 10_000);
306
- assert.equal(e.startTimeMs, 10_000);
307
- assert.equal(f.startTimeMs, 10_000);
308
- });
309
-
310
- // // TODO: Rethink offset math, it shouldn't effect the duration of a temporal item
311
- // // but actually change the start and end time.
312
- // litTest.skip("can be offset with offset attribute", async ({ container }) => {
313
- // render(
314
- // html`<ef-timegroup id="root" mode="contain">
315
- // <test-temporal id="a" duration="5s" offset="5s"></test-temporal>
316
- // </ef-timegroup> `,
317
- // container,
318
- // );
319
-
320
- // const root = container.querySelector("#root") as EFTimegroup;
321
- // const a = container.querySelector("#a") as TestTemporal;
322
-
323
- // assert.equal(a.durationMs, 5_000);
324
- // assert.equal(root.durationMs, 10_000);
325
- // assert.equal(a.startTimeMs, 5_000);
326
- // });
327
-
328
- // litTest.skip(
329
- // "offsets do not affect start time when in a sequence group",
330
- // async ({ container }) => {
331
- // render(
332
- // html`<ef-timegroup id="root" mode="sequence">
333
- // <test-temporal id="a" duration="5s"></test-temporal>
334
- // <test-temporal id="b" duration="5s" offset="5s"></test-temporal>
335
- // </ef-timegroup> `,
336
- // container,
337
- // );
338
-
339
- // const root = container.querySelector("#root") as EFTimegroup;
340
- // const a = container.querySelector("#a") as TestTemporal;
341
- // const b = container.querySelector("#b") as TestTemporal;
342
-
343
- // assert.equal(root.durationMs, 10_000);
344
- // assert.equal(a.startTimeMs, 0);
345
- // assert.equal(b.startTimeMs, 5_000);
346
- // },
347
- // );
348
-
349
- test("temporal elements default to expand to fill a timegroup", async () => {
350
- const timegroup = renderTimegroup(
351
- html`<ef-timegroup id="root" mode="fixed" duration="10s">
352
- <test-temporal id="a"></test-temporal>
353
- </ef-timegroup> `,
354
- );
355
-
356
- const a = timegroup.querySelector("#a") as TestTemporal;
357
-
358
- assert.equal(timegroup.durationMs, 10_000);
359
- assert.equal(a.durationMs, 10_000);
360
- });
361
-
362
- test("element's parentTimegroup updates as they move", async () => {
363
- const container = document.createElement("div");
364
- document.body.appendChild(container);
365
- const timegroup1 = document.createElement("ef-timegroup");
366
- timegroup1.setAttribute("mode", "fixed");
367
- timegroup1.setAttribute("duration", "5s");
368
-
369
- const timegroup2 = document.createElement("ef-timegroup");
370
- timegroup2.setAttribute("mode", "fixed");
371
- timegroup2.setAttribute("duration", "5s");
372
-
373
- container.appendChild(timegroup1);
374
- container.appendChild(timegroup2);
375
-
376
- const temporal = document.createElement("test-temporal");
377
-
378
- timegroup1.appendChild(temporal);
379
-
380
- assert.equal(temporal.parentTimegroup, timegroup1);
381
-
382
- timegroup2.appendChild(temporal);
383
- assert.equal(temporal.parentTimegroup, timegroup2);
384
- });
385
-
386
- test("elements can access their root temporal element", async () => {
387
- const root = renderTimegroup(
388
- html`<ef-timegroup id="root" mode="contain" duration="10s">
389
- <ef-timegroup id="a" mode="contain">
390
- <div>
391
- <ef-timegroup id="b" mode="contain">
392
- <div>
393
- <test-temporal id="c" duration="5s"></test-temporal>
394
- </div>
395
- </ef-timegroup>
396
- </div>
397
- </ef-timegroup>
398
- </ef-timegroup> `,
399
- );
400
-
401
- const a = root.querySelector("#a") as EFTimegroup;
402
- const b = root.querySelector("#b") as EFTimegroup;
403
- const c = root.querySelector("#c") as TestTemporal;
404
-
405
- assert.equal(root.rootTimegroup, root);
406
-
407
- assert.equal(a.rootTimegroup, root);
408
- assert.equal(b.rootTimegroup, root);
409
- assert.equal(c.rootTimegroup, root);
410
- });
411
- });
412
-
413
- describe("setting currentTime", () => {
414
- test("persists in localStorage if the timegroup has an id and is in the dom", async () => {
415
- const timegroup = renderTimegroup(
416
- html`<ef-timegroup id="localStorage-test" mode="fixed" duration="10s"></ef-timegroup>`,
417
- );
418
- localStorage.removeItem(timegroup.storageKey);
419
- document.body.appendChild(timegroup);
420
- await timegroup.updateComplete;
421
- await timegroup.waitForMediaDurations();
422
-
423
- // Use the new seek() method which ensures everything is ready
424
- await timegroup.seek(5_000_000); // 5000 seconds in ms, should clamp to 10s
425
-
426
- const storedValue = localStorage.getItem(timegroup.storageKey);
427
- assert.equal(storedValue, "10"); // Should store 10 (clamped from 5000 to duration)
428
- timegroup.remove();
429
- });
430
-
431
- test("root timegroup remains visible when currentTime equals duration exactly", async () => {
432
- const timegroup = renderTimegroup(
433
- html`<ef-timegroup id="end-time-test" mode="fixed" duration="10s"></ef-timegroup>`,
434
- );
435
- await timegroup.waitForMediaDurations();
436
-
437
- // Set currentTime to exactly the duration
438
- timegroup.currentTime = 10; // 10 seconds
439
- await timegroup.seekTask.taskComplete;
440
- await timegroup.frameTask.taskComplete;
441
-
442
- // The root timegroup should still be visible at the exact end time
443
- assert.notEqual(
444
- timegroup.style.display,
445
- "none",
446
- "Root timegroup should be visible at exact end time",
447
- );
448
- });
449
-
450
- test("root timegroup becomes hidden only after currentTime exceeds duration", async () => {
451
- const timegroup = renderTimegroup(
452
- html`<ef-timegroup id="beyond-end-test" mode="fixed" duration="10s"></ef-timegroup>`,
453
- );
454
- await timegroup.waitForMediaDurations();
455
-
456
- // Set currentTime beyond the duration (should be clamped to duration)
457
- timegroup.currentTime = 15; // 15 seconds, should clamp to 10s
458
- await timegroup.seekTask.taskComplete;
459
- await timegroup.frameTask.taskComplete;
460
-
461
- // Even when clamped, it should still be visible at the end
462
- assert.notEqual(
463
- timegroup.style.display,
464
- "none",
465
- "Root timegroup should be visible even when time is clamped to duration",
466
- );
467
- // Verify that the time was actually clamped
468
- assert.equal(
469
- timegroup.currentTime,
470
- 10,
471
- "Time should be clamped to duration",
472
- );
473
- });
474
-
475
- test("does not persist in localStorage if the timegroup has no id", async () => {
476
- const timegroup = renderTimegroup(
477
- html`<ef-timegroup mode="fixed" duration="10s"></ef-timegroup>`,
478
- );
479
- document.body.appendChild(timegroup);
480
- timegroup.currentTime = 5_000;
481
- timegroup.removeAttribute("id");
482
- assert.throws(() => {
483
- assert.isNull(localStorage.getItem(timegroup.storageKey));
484
- }, "Timegroup must have an id to use localStorage");
485
- timegroup.remove();
486
- });
487
-
488
- test("nested items derive their ownCurrentTimeMs", async () => {
489
- const timegroup = renderTimegroup(
490
- html`
491
- <ef-timegroup id="root" mode="sequence">
492
- <ef-timegroup id="a" mode="fixed" duration="5s"> </ef-timegroup>
493
- <ef-timegroup id="b" mode="fixed" duration="5s"> </ef-timegroup>
494
- </ef-timegroup>
495
- `,
496
- );
497
-
498
- const root = timegroup;
499
- const a = timegroup.querySelector("#a") as EFTimegroup;
500
- const b = timegroup.querySelector("#b") as EFTimegroup;
501
-
502
- assert.equal(a.ownCurrentTimeMs, 0);
503
- assert.equal(b.ownCurrentTimeMs, 0);
504
-
505
- root.currentTimeMs = 2_500;
506
-
507
- assert.equal(a.ownCurrentTimeMs, 2_500);
508
- assert.equal(b.ownCurrentTimeMs, 0);
509
-
510
- // Wait for frame update to complete before next assignment
511
- await new Promise((resolve) => setTimeout(resolve, 10));
512
-
513
- root.currentTimeMs = 7_500;
514
-
515
- assert.equal(a.ownCurrentTimeMs, 5_000);
516
- assert.equal(b.ownCurrentTimeMs, 2_500);
517
- });
518
- });
519
-
520
- describe("shouldWrapWithWorkbench", () => {
521
- test.skip("should not wrap if EF_INTERACTIVE is false", () => {
522
- // TODO: need a way to define EF_INTERACTIVE in a test
523
- });
524
-
525
- test.skip("should wrap if root-most timegroup", () => {
526
- // TODO: This test requires EF_INTERACTIVE to be true, which is a module-level constant
527
- // that cannot be modified in tests. Need a way to test this behavior.
528
- });
529
-
530
- test("should not wrap if contained within a preview context", () => {
531
- const timegorup = document.createElement("ef-timegroup");
532
- const context = document.createElement("test-context");
533
- context.append(timegorup);
534
- assert.isFalse(timegorup.shouldWrapWithWorkbench());
535
- });
536
- });
537
-
538
- describe("DOM nodes", () => {
539
- test("can have mode and duration set as attributes", () => {
540
- const timegroup = document.createElement("ef-timegroup");
541
- timegroup.setAttribute("mode", "fixed");
542
- timegroup.setAttribute("duration", "10s");
543
- assert.equal(timegroup.mode, "fixed");
544
- assert.equal(timegroup.durationMs, 10_000);
545
- });
546
- });
547
-
548
- describe("Dynamic content updates", () => {
549
- test("updates duration when new child temporal elements are added dynamically", async () => {
550
- // Create a sequence timegroup with initial children
551
- const timegroup = renderTimegroup(
552
- html`<ef-timegroup mode="sequence">
553
- <ef-timegroup mode="fixed" duration="3s"></ef-timegroup>
554
- <ef-timegroup mode="fixed" duration="2s"></ef-timegroup>
555
- </ef-timegroup>`,
556
- );
557
-
558
- // Initial duration should be 5 seconds (3s + 2s)
559
- assert.equal(timegroup.durationMs, 5_000);
560
- assert.equal(timegroup.childTemporals.length, 2);
561
-
562
- // Dynamically add a new child element
563
- const newChild = document.createElement("ef-timegroup");
564
- newChild.setAttribute("mode", "fixed");
565
- newChild.setAttribute("duration", "4s");
566
- timegroup.appendChild(newChild);
567
-
568
- // Wait for slot change event to process
569
- await timegroup.updateComplete;
570
-
571
- // Duration should now include the new child (3s + 2s + 4s = 9s)
572
- assert.equal(timegroup.durationMs, 9_000);
573
- assert.equal(timegroup.childTemporals.length, 3);
574
- });
575
-
576
- test("updates duration when child temporal elements are removed dynamically", async () => {
577
- // Create a contain timegroup with initial children
578
- const timegroup = renderTimegroup(
579
- html`<ef-timegroup mode="contain">
580
- <ef-timegroup id="child1" mode="fixed" duration="5s"></ef-timegroup>
581
- <ef-timegroup id="child2" mode="fixed" duration="8s"></ef-timegroup>
582
- <ef-timegroup id="child3" mode="fixed" duration="3s"></ef-timegroup>
583
- </ef-timegroup>`,
584
- );
585
-
586
- // Initial duration should be max of children (8s)
587
- assert.equal(timegroup.durationMs, 8_000);
588
- assert.equal(timegroup.childTemporals.length, 3);
589
-
590
- // Remove the longest duration child
591
- const child2 = timegroup.querySelector("#child2");
592
- child2?.remove();
593
-
594
- // Wait for slot change event to process
595
- await timegroup.updateComplete;
596
-
597
- // Duration should now be max of remaining children (5s)
598
- assert.equal(timegroup.durationMs, 5_000);
599
- assert.equal(timegroup.childTemporals.length, 2);
600
- });
601
-
602
- describe("frameTask", () => {
603
- test("executes all nested frame tasks", async () => {
604
- const timegroup = renderTimegroup(
605
- html`<ef-timegroup mode="sequence">
606
- <test-frame-task-a duration="1s"></test-frame-task-a>
607
- <div>
608
- <test-frame-task-b duration="1s">
609
- <test-frame-task-c duration="1s"></test-frame-task-c>
610
- </test-frame-task-b>
611
- </div>
612
- </ef-timegroup>`,
613
- );
614
- const frameTaskA = timegroup.querySelector("test-frame-task-a")!;
615
- const frameTaskB = timegroup.querySelector("test-frame-task-b")!;
616
- const frameTaskC = timegroup.querySelector("test-frame-task-c")!;
617
-
618
- // Following the initial update, frame tasks may run during initialization
619
- await timegroup.updateComplete;
620
-
621
- // frameTaskB should never run (not visible at time 0ms in sequence)
622
- assert.equal(frameTaskB.frameTaskCount, 0);
623
-
624
- // Then we run them manually.
625
- await timegroup.frameTask.run();
626
-
627
- // At timeline time 0ms:
628
- // - frameTaskA (0-1000ms) should have run (visible)
629
- // - frameTaskB (1000-2000ms) should NOT run (not visible at time 0)
630
- // - frameTaskC (0-1000ms) should have run (inherits root positioning, visible)
631
-
632
- // Verify visible tasks have run at least once
633
- assert.ok(frameTaskA.frameTaskCount > 0, "frameTaskA should have run");
634
- assert.ok(frameTaskC.frameTaskCount > 0, "frameTaskC should have run");
635
- // Verify non-visible task has never run
636
- assert.equal(frameTaskB.frameTaskCount, 0); // Not visible at time 0
637
- });
638
- });
639
-
640
- describe("seekTask", () => {
641
- test("does not execute if not the root timegroup", async () => {
642
- const timegroup = renderTimegroup(
643
- html`<ef-timegroup mode="sequence">
644
- <ef-timegroup mode="fixed" duration="3s">
645
- <test-frame-task-a></test-frame-task-a>
646
- </ef-timegroup>
647
- </ef-timegroup>`,
648
- );
649
- const nonRootTimegroup = timegroup.querySelector("ef-timegroup")!;
650
- const frameTaskA = timegroup.querySelector("test-frame-task-a")!;
651
- await timegroup.updateComplete;
652
- assert.equal(frameTaskA.frameTaskCount, 1);
653
- await nonRootTimegroup.seekTask.run();
654
- assert.equal(frameTaskA.frameTaskCount, 1);
655
- });
656
-
657
- test("waits for media durations", async () => {
658
- const timegroup = renderTimegroup(
659
- html`
660
- <ef-preview>
661
- <ef-timegroup mode="sequence">
662
- <timegroup-test-media></timegroup-test-media>
663
- </ef-timegroup>
664
- </ef-preview>`,
665
- );
666
- const media = timegroup.querySelector("timegroup-test-media")!;
667
- assert.equal(media.mediaEngineTaskCount, 0);
668
- await timegroup.seekTask.run();
669
- assert.equal(media.mediaEngineTaskCount, 1);
670
- });
671
- });
672
-
673
- describe("custom frame tasks", () => {
674
- test("executes registered callback on frame update", async () => {
675
- const timegroup = renderTimegroup(
676
- html`<ef-timegroup mode="fixed" duration="5s"></ef-timegroup>`,
677
- );
678
-
679
- let callbackExecuted = false;
680
- const callback = () => {
681
- callbackExecuted = true;
682
- };
683
-
684
- timegroup.addFrameTask(callback);
685
- await timegroup.seek(1000);
686
-
687
- assert.equal(callbackExecuted, true);
688
- }, 1000);
689
-
690
- test("callback receives correct timing information", async () => {
691
- const timegroup = renderTimegroup(
692
- html`<ef-timegroup mode="fixed" duration="5000ms"></ef-timegroup>`,
693
- );
694
-
695
- let receivedInfo: any = null;
696
- const callback = (info: any) => {
697
- receivedInfo = info;
698
- };
699
-
700
- timegroup.addFrameTask(callback);
701
- await timegroup.seek(2000);
702
-
703
- assert.equal(receivedInfo.ownCurrentTimeMs, 2000);
704
- assert.equal(receivedInfo.currentTimeMs, 2000);
705
- assert.equal(receivedInfo.durationMs, 5000);
706
- assert.equal(receivedInfo.percentComplete, 0.4);
707
- assert.equal(receivedInfo.element, timegroup);
708
- }, 1000);
709
-
710
- test("executes multiple callbacks in parallel", async () => {
711
- const timegroup = renderTimegroup(
712
- html`<ef-timegroup mode="fixed" duration="5s"></ef-timegroup>`,
713
- );
714
-
715
- let callback1Executed = false;
716
- let callback2Executed = false;
717
- let callback3Executed = false;
718
-
719
- timegroup.addFrameTask(() => {
720
- callback1Executed = true;
721
- });
722
- timegroup.addFrameTask(() => {
723
- callback2Executed = true;
724
- });
725
- timegroup.addFrameTask(() => {
726
- callback3Executed = true;
727
- });
728
-
729
- await timegroup.seek(1000);
730
-
731
- assert.equal(callback1Executed, true);
732
- assert.equal(callback2Executed, true);
733
- assert.equal(callback3Executed, true);
734
- }, 1000);
735
-
736
- test("async callbacks block frame pipeline", async () => {
737
- const timegroup = renderTimegroup(
738
- html`<ef-timegroup mode="fixed" duration="5s"></ef-timegroup>`,
739
- );
740
-
741
- let asyncCallbackCompleted = false;
742
- const executionOrder: string[] = [];
743
-
744
- const asyncCallback = async () => {
745
- executionOrder.push("async-start");
746
- await new Promise((resolve) => setTimeout(resolve, 50));
747
- asyncCallbackCompleted = true;
748
- executionOrder.push("async-end");
749
- };
750
-
751
- timegroup.addFrameTask(asyncCallback);
752
-
753
- const seekPromise = timegroup.seek(1000);
754
- executionOrder.push("seek-called");
755
-
756
- await seekPromise;
757
- executionOrder.push("seek-complete");
758
-
759
- assert.equal(asyncCallbackCompleted, true);
760
- assert.deepEqual(executionOrder, [
761
- "seek-called",
762
- "async-start",
763
- "async-end",
764
- "seek-complete",
765
- ]);
766
- }, 1000);
767
-
768
- test("cleanup function removes callback", async () => {
769
- const timegroup = renderTimegroup(
770
- html`<ef-timegroup mode="fixed" duration="5s"></ef-timegroup>`,
771
- );
772
-
773
- let callbackExecutionCount = 0;
774
- const cleanup = timegroup.addFrameTask(() => {
775
- callbackExecutionCount++;
776
- });
777
-
778
- await timegroup.seek(1000);
779
- assert.equal(callbackExecutionCount, 1);
780
-
781
- cleanup();
782
- await timegroup.seek(2000);
783
- assert.equal(callbackExecutionCount, 1);
784
- }, 1000);
785
-
786
- test("removeFrameTask removes callback", async () => {
787
- const timegroup = renderTimegroup(
788
- html`<ef-timegroup mode="fixed" duration="5s"></ef-timegroup>`,
789
- );
790
-
791
- let callbackExecutionCount = 0;
792
- const callback = () => {
793
- callbackExecutionCount++;
794
- };
795
-
796
- timegroup.addFrameTask(callback);
797
- await timegroup.seek(1000);
798
- assert.equal(callbackExecutionCount, 1);
799
-
800
- timegroup.removeFrameTask(callback);
801
- await timegroup.seek(2000);
802
- assert.equal(callbackExecutionCount, 1);
803
- }, 1000);
804
-
805
- test("addFrameTask throws error for non-function", () => {
806
- const timegroup = renderTimegroup(
807
- html`<ef-timegroup mode="fixed" duration="5s"></ef-timegroup>`,
808
- );
809
-
810
- assert.throws(() => {
811
- timegroup.addFrameTask("not a function" as any);
812
- }, "Frame task callback must be a function");
813
- }, 1000);
814
-
815
- test("custom frame tasks persist after disconnect and reconnect", async () => {
816
- const container = document.createElement("div");
817
- document.body.appendChild(container);
818
-
819
- const timegroup = document.createElement("ef-timegroup") as EFTimegroup;
820
- timegroup.setAttribute("mode", "fixed");
821
- timegroup.setAttribute("duration", "5s");
822
- container.appendChild(timegroup);
823
-
824
- let callbackWorkedAfterReconnect = false;
825
- const callback = () => {
826
- callbackWorkedAfterReconnect = true;
827
- };
828
-
829
- timegroup.addFrameTask(callback);
830
-
831
- // Disconnect and reconnect
832
- container.removeChild(timegroup);
833
- callbackWorkedAfterReconnect = false; // Reset after disconnect
834
- container.appendChild(timegroup);
835
-
836
- // Callback should still work after reconnect
837
- await timegroup.seek(2000);
838
- assert.equal(
839
- callbackWorkedAfterReconnect,
840
- true,
841
- "Callback should still work after reconnect",
842
- );
843
-
844
- container.remove();
845
- }, 1000);
846
-
847
- test("sync and async callbacks execute together", async () => {
848
- const timegroup = renderTimegroup(
849
- html`<ef-timegroup mode="fixed" duration="5s"></ef-timegroup>`,
850
- );
851
-
852
- let syncExecuted = false;
853
- let asyncExecuted = false;
854
-
855
- timegroup.addFrameTask(() => {
856
- syncExecuted = true;
857
- });
858
-
859
- timegroup.addFrameTask(async () => {
860
- await new Promise((resolve) => setTimeout(resolve, 10));
861
- asyncExecuted = true;
862
- });
863
-
864
- await timegroup.seek(1000);
865
-
866
- assert.equal(syncExecuted, true);
867
- assert.equal(asyncExecuted, true);
868
- }, 1000);
869
-
870
- test("executes callbacks when currentTime is loaded from localStorage", async () => {
871
- const timegroupId = "localStorage-frame-task-test";
872
- const storageKey = `ef-timegroup-${timegroupId}`;
873
-
874
- localStorage.setItem(storageKey, "2.5");
875
-
876
- const container = document.createElement("div");
877
- document.body.appendChild(container);
878
-
879
- const timegroup = document.createElement("ef-timegroup") as EFTimegroup;
880
- timegroup.setAttribute("id", timegroupId);
881
- timegroup.setAttribute("mode", "fixed");
882
- timegroup.setAttribute("duration", "5s");
883
-
884
- let callbackExecutionCount = 0;
885
- const receivedTimes: number[] = [];
886
-
887
- timegroup.addFrameTask((info) => {
888
- callbackExecutionCount++;
889
- receivedTimes.push(info.currentTimeMs);
890
- });
891
-
892
- container.appendChild(timegroup);
893
-
894
- await timegroup.updateComplete;
895
- await timegroup.waitForMediaDurations();
896
-
897
- await new Promise((resolve) => {
898
- const checkComplete = async () => {
899
- if (
900
- timegroup.currentTime === 2.5 &&
901
- receivedTimes[receivedTimes.length - 1] === 2500
902
- ) {
903
- resolve(undefined);
904
- } else if (timegroup.currentTime === 2.5) {
905
- await new Promise((r) => setTimeout(r, 10));
906
- checkComplete();
907
- } else {
908
- setTimeout(checkComplete, 10);
909
- }
910
- };
911
- checkComplete();
912
- });
913
-
914
- assert.equal(
915
- timegroup.currentTime,
916
- 2.5,
917
- "Timegroup should have loaded time from localStorage",
918
- );
919
- assert.isAtLeast(
920
- callbackExecutionCount,
921
- 1,
922
- "Frame callback should be executed at least once",
923
- );
924
- assert.equal(
925
- receivedTimes[receivedTimes.length - 1],
926
- 2500,
927
- `Last callback execution should receive the time loaded from localStorage. Got: ${receivedTimes.join(", ")}`,
928
- );
929
-
930
- container.remove();
931
- localStorage.removeItem(storageKey);
932
- }, 1000);
933
- });
934
- });