@memberjunction/ng-media-player 0.0.1 → 5.44.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 (34) hide show
  1. package/README.md +241 -28
  2. package/dist/lib/media-player/cue-utils.d.ts +24 -0
  3. package/dist/lib/media-player/cue-utils.d.ts.map +1 -0
  4. package/dist/lib/media-player/cue-utils.js +47 -0
  5. package/dist/lib/media-player/cue-utils.js.map +1 -0
  6. package/dist/lib/media-player/media-player.component.d.ts +326 -0
  7. package/dist/lib/media-player/media-player.component.d.ts.map +1 -0
  8. package/dist/lib/media-player/media-player.component.js +1545 -0
  9. package/dist/lib/media-player/media-player.component.js.map +1 -0
  10. package/dist/lib/media-player/playback-state.d.ts +46 -0
  11. package/dist/lib/media-player/playback-state.d.ts.map +1 -0
  12. package/dist/lib/media-player/playback-state.js +86 -0
  13. package/dist/lib/media-player/playback-state.js.map +1 -0
  14. package/dist/lib/media-player/transcript-layout.d.ts +35 -0
  15. package/dist/lib/media-player/transcript-layout.d.ts.map +1 -0
  16. package/dist/lib/media-player/transcript-layout.js +37 -0
  17. package/dist/lib/media-player/transcript-layout.js.map +1 -0
  18. package/dist/lib/media-player/waveform-utils.d.ts +45 -0
  19. package/dist/lib/media-player/waveform-utils.d.ts.map +1 -0
  20. package/dist/lib/media-player/waveform-utils.js +108 -0
  21. package/dist/lib/media-player/waveform-utils.js.map +1 -0
  22. package/dist/lib/media-player.types.d.ts +107 -0
  23. package/dist/lib/media-player.types.d.ts.map +1 -0
  24. package/dist/lib/media-player.types.js +9 -0
  25. package/dist/lib/media-player.types.js.map +1 -0
  26. package/dist/lib/storage-media-player/storage-media-player.component.d.ts +77 -0
  27. package/dist/lib/storage-media-player/storage-media-player.component.d.ts.map +1 -0
  28. package/dist/lib/storage-media-player/storage-media-player.component.js +283 -0
  29. package/dist/lib/storage-media-player/storage-media-player.component.js.map +1 -0
  30. package/dist/public-api.d.ts +8 -0
  31. package/dist/public-api.d.ts.map +1 -0
  32. package/dist/public-api.js +11 -0
  33. package/dist/public-api.js.map +1 -0
  34. package/package.json +36 -7
@@ -0,0 +1,1545 @@
1
+ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, HostListener, Input, Output, ViewChild, inject, } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { computeActiveCueIndex } from './cue-utils';
4
+ import { nextPlaybackState } from './playback-state';
5
+ import { resolveTranscriptToggleVisible, resolveTranscriptVisible, } from './transcript-layout';
6
+ import { DEFAULT_WAVEFORM_BARS, downsamplePeaks } from './waveform-utils';
7
+ import * as i0 from "@angular/core";
8
+ const _c0 = ["primaryMedia"];
9
+ function MJMediaPlayerComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
10
+ i0.ɵɵdomElementStart(0, "div", 4);
11
+ i0.ɵɵdomElement(1, "i", 5);
12
+ i0.ɵɵdomElementStart(2, "span", 6);
13
+ i0.ɵɵtext(3, "No media to play.");
14
+ i0.ɵɵdomElementEnd()();
15
+ } }
16
+ function MJMediaPlayerComponent_Conditional_2_Conditional_2_Template(rf, ctx) { if (rf & 1) {
17
+ i0.ɵɵdomElementStart(0, "div", 9);
18
+ i0.ɵɵdomElement(1, "i", 33);
19
+ i0.ɵɵdomElementStart(2, "span");
20
+ i0.ɵɵtext(3, "This media could not be played.");
21
+ i0.ɵɵdomElementEnd()();
22
+ } }
23
+ function MJMediaPlayerComponent_Conditional_2_Conditional_3_Template(rf, ctx) { if (rf & 1) {
24
+ i0.ɵɵdomElementStart(0, "div", 34);
25
+ i0.ɵɵdomElement(1, "span", 35);
26
+ i0.ɵɵdomElementStart(2, "span", 36);
27
+ i0.ɵɵtext(3);
28
+ i0.ɵɵdomElementEnd()();
29
+ } if (rf & 2) {
30
+ const ctx_r1 = i0.ɵɵnextContext(2);
31
+ i0.ɵɵclassProp("mj-media-busy--static", ctx_r1.PrefersReducedMotion);
32
+ i0.ɵɵadvance(3);
33
+ i0.ɵɵtextInterpolate(ctx_r1.BusyLabel);
34
+ } }
35
+ function MJMediaPlayerComponent_Conditional_2_Conditional_4_For_1_Conditional_0_Template(rf, ctx) { if (rf & 1) {
36
+ const _r3 = i0.ɵɵgetCurrentView();
37
+ i0.ɵɵdomElementStart(0, "audio", 41, 1);
38
+ i0.ɵɵdomListener("loadstart", function MJMediaPlayerComponent_Conditional_2_Conditional_4_For_1_Conditional_0_Template_audio_loadstart_0_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.OnMediaLoadStart($event.target)); })("loadedmetadata", function MJMediaPlayerComponent_Conditional_2_Conditional_4_For_1_Conditional_0_Template_audio_loadedmetadata_0_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.OnMediaLoadedMetadata($event.target)); })("canplay", function MJMediaPlayerComponent_Conditional_2_Conditional_4_For_1_Conditional_0_Template_audio_canplay_0_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.OnMediaCanPlay($event.target)); })("canplaythrough", function MJMediaPlayerComponent_Conditional_2_Conditional_4_For_1_Conditional_0_Template_audio_canplaythrough_0_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.OnMediaCanPlay($event.target)); })("waiting", function MJMediaPlayerComponent_Conditional_2_Conditional_4_For_1_Conditional_0_Template_audio_waiting_0_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.OnMediaWaiting($event.target)); })("seeking", function MJMediaPlayerComponent_Conditional_2_Conditional_4_For_1_Conditional_0_Template_audio_seeking_0_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.OnMediaSeeking($event.target)); })("seeked", function MJMediaPlayerComponent_Conditional_2_Conditional_4_For_1_Conditional_0_Template_audio_seeked_0_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.OnMediaSeeked($event.target)); })("playing", function MJMediaPlayerComponent_Conditional_2_Conditional_4_For_1_Conditional_0_Template_audio_playing_0_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.OnMediaPlaying($event.target)); })("stalled", function MJMediaPlayerComponent_Conditional_2_Conditional_4_For_1_Conditional_0_Template_audio_stalled_0_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.OnMediaStalled($event.target)); })("suspend", function MJMediaPlayerComponent_Conditional_2_Conditional_4_For_1_Conditional_0_Template_audio_suspend_0_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.OnMediaSuspend($event.target)); })("emptied", function MJMediaPlayerComponent_Conditional_2_Conditional_4_For_1_Conditional_0_Template_audio_emptied_0_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.OnMediaEmptied($event.target)); })("timeupdate", function MJMediaPlayerComponent_Conditional_2_Conditional_4_For_1_Conditional_0_Template_audio_timeupdate_0_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.OnMediaTimeUpdate($event.target)); })("durationchange", function MJMediaPlayerComponent_Conditional_2_Conditional_4_For_1_Conditional_0_Template_audio_durationchange_0_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.OnMediaDurationChange($event.target)); })("play", function MJMediaPlayerComponent_Conditional_2_Conditional_4_For_1_Conditional_0_Template_audio_play_0_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.OnMediaPlay($event.target)); })("pause", function MJMediaPlayerComponent_Conditional_2_Conditional_4_For_1_Conditional_0_Template_audio_pause_0_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.OnMediaPause($event.target)); })("ended", function MJMediaPlayerComponent_Conditional_2_Conditional_4_For_1_Conditional_0_Template_audio_ended_0_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.OnMediaEnded($event.target)); })("error", function MJMediaPlayerComponent_Conditional_2_Conditional_4_For_1_Conditional_0_Template_audio_error_0_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.OnMediaError($event.target)); });
39
+ i0.ɵɵdomElementEnd();
40
+ } if (rf & 2) {
41
+ const track_r4 = i0.ɵɵnextContext().$implicit;
42
+ i0.ɵɵdomProperty("src", track_r4.Url);
43
+ } }
44
+ function MJMediaPlayerComponent_Conditional_2_Conditional_4_For_1_Template(rf, ctx) { if (rf & 1) {
45
+ i0.ɵɵconditionalCreate(0, MJMediaPlayerComponent_Conditional_2_Conditional_4_For_1_Conditional_0_Template, 2, 1, "audio", 40);
46
+ } if (rf & 2) {
47
+ const ɵ$index_33_r5 = ctx.$index;
48
+ i0.ɵɵconditional(ɵ$index_33_r5 === 0 ? 0 : -1);
49
+ } }
50
+ function MJMediaPlayerComponent_Conditional_2_Conditional_4_Conditional_2_For_3_Template(rf, ctx) { if (rf & 1) {
51
+ i0.ɵɵdomElement(0, "span", 45);
52
+ } if (rf & 2) {
53
+ const p_r8 = ctx.$implicit;
54
+ const ɵ$index_40_r9 = ctx.$index;
55
+ const ɵ$count_40_r10 = ctx.$count;
56
+ const ctx_r1 = i0.ɵɵnextContext(4);
57
+ i0.ɵɵstyleProp("height", 2 + p_r8 * 98, "%");
58
+ i0.ɵɵclassProp("mj-media-waveform__bar--played", (ɵ$index_40_r9 + 1) / ɵ$count_40_r10 <= ctx_r1.WaveformPlayedFraction);
59
+ } }
60
+ function MJMediaPlayerComponent_Conditional_2_Conditional_4_Conditional_2_Template(rf, ctx) { if (rf & 1) {
61
+ const _r6 = i0.ɵɵgetCurrentView();
62
+ i0.ɵɵdomElementStart(0, "div", 42, 2);
63
+ i0.ɵɵdomListener("pointerdown", function MJMediaPlayerComponent_Conditional_2_Conditional_4_Conditional_2_Template_div_pointerdown_0_listener($event) { i0.ɵɵrestoreView(_r6); const waveTrack_r7 = i0.ɵɵreference(1); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.OnWaveformPointerDown($event, waveTrack_r7)); })("pointermove", function MJMediaPlayerComponent_Conditional_2_Conditional_4_Conditional_2_Template_div_pointermove_0_listener($event) { i0.ɵɵrestoreView(_r6); const waveTrack_r7 = i0.ɵɵreference(1); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.OnWaveformPointerMove($event, waveTrack_r7)); })("pointerup", function MJMediaPlayerComponent_Conditional_2_Conditional_4_Conditional_2_Template_div_pointerup_0_listener($event) { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.OnWaveformPointerUp($event)); })("pointercancel", function MJMediaPlayerComponent_Conditional_2_Conditional_4_Conditional_2_Template_div_pointercancel_0_listener($event) { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.OnWaveformPointerUp($event)); })("keydown", function MJMediaPlayerComponent_Conditional_2_Conditional_4_Conditional_2_Template_div_keydown_0_listener($event) { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.OnScrubKeyDown($event)); });
64
+ i0.ɵɵrepeaterCreate(2, MJMediaPlayerComponent_Conditional_2_Conditional_4_Conditional_2_For_3_Template, 1, 4, "span", 43, i0.ɵɵrepeaterTrackByIndex);
65
+ i0.ɵɵdomElement(4, "div", 44);
66
+ i0.ɵɵdomElementEnd();
67
+ } if (rf & 2) {
68
+ const ctx_r1 = i0.ɵɵnextContext(3);
69
+ i0.ɵɵclassProp("mj-media-waveform--static", ctx_r1.PrefersReducedMotion);
70
+ i0.ɵɵattribute("aria-valuemin", 0)("aria-valuemax", ctx_r1.DurationMs)("aria-valuenow", ctx_r1.CurrentTimeMs)("aria-valuetext", ctx_r1.FormatTime(ctx_r1.CurrentTimeMs) + " of " + ctx_r1.FormatTime(ctx_r1.DurationMs));
71
+ i0.ɵɵadvance(2);
72
+ i0.ɵɵrepeater(ctx_r1.WaveformPeaks);
73
+ i0.ɵɵadvance(2);
74
+ i0.ɵɵstyleProp("left", ctx_r1.ScrubPercent, "%");
75
+ } }
76
+ function MJMediaPlayerComponent_Conditional_2_Conditional_4_Conditional_3_Template(rf, ctx) { if (rf & 1) {
77
+ const _r11 = i0.ɵɵgetCurrentView();
78
+ i0.ɵɵdomElementStart(0, "div", 46, 2);
79
+ i0.ɵɵdomListener("pointerdown", function MJMediaPlayerComponent_Conditional_2_Conditional_4_Conditional_3_Template_div_pointerdown_0_listener($event) { i0.ɵɵrestoreView(_r11); const waveTrack_r12 = i0.ɵɵreference(1); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.OnWaveformPointerDown($event, waveTrack_r12)); })("pointermove", function MJMediaPlayerComponent_Conditional_2_Conditional_4_Conditional_3_Template_div_pointermove_0_listener($event) { i0.ɵɵrestoreView(_r11); const waveTrack_r12 = i0.ɵɵreference(1); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.OnWaveformPointerMove($event, waveTrack_r12)); })("pointerup", function MJMediaPlayerComponent_Conditional_2_Conditional_4_Conditional_3_Template_div_pointerup_0_listener($event) { i0.ɵɵrestoreView(_r11); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.OnWaveformPointerUp($event)); })("pointercancel", function MJMediaPlayerComponent_Conditional_2_Conditional_4_Conditional_3_Template_div_pointercancel_0_listener($event) { i0.ɵɵrestoreView(_r11); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.OnWaveformPointerUp($event)); })("keydown", function MJMediaPlayerComponent_Conditional_2_Conditional_4_Conditional_3_Template_div_keydown_0_listener($event) { i0.ɵɵrestoreView(_r11); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.OnScrubKeyDown($event)); });
80
+ i0.ɵɵdomElementStart(2, "div", 47);
81
+ i0.ɵɵdomElement(3, "div", 48);
82
+ i0.ɵɵdomElementEnd()();
83
+ } if (rf & 2) {
84
+ const ctx_r1 = i0.ɵɵnextContext(3);
85
+ i0.ɵɵattribute("aria-valuemin", 0)("aria-valuemax", ctx_r1.DurationMs)("aria-valuenow", ctx_r1.CurrentTimeMs)("aria-valuetext", ctx_r1.FormatTime(ctx_r1.CurrentTimeMs) + " of " + ctx_r1.FormatTime(ctx_r1.DurationMs));
86
+ i0.ɵɵadvance(3);
87
+ i0.ɵɵstyleProp("width", ctx_r1.ScrubPercent, "%");
88
+ } }
89
+ function MJMediaPlayerComponent_Conditional_2_Conditional_4_Conditional_4_Template(rf, ctx) { if (rf & 1) {
90
+ i0.ɵɵdomElementStart(0, "div", 39);
91
+ i0.ɵɵdomElement(1, "i", 49);
92
+ i0.ɵɵdomElementEnd();
93
+ } }
94
+ function MJMediaPlayerComponent_Conditional_2_Conditional_4_Template(rf, ctx) { if (rf & 1) {
95
+ i0.ɵɵrepeaterCreate(0, MJMediaPlayerComponent_Conditional_2_Conditional_4_For_1_Template, 1, 1, null, null, i0.ɵɵcomponentInstance().TrackById, true);
96
+ i0.ɵɵconditionalCreate(2, MJMediaPlayerComponent_Conditional_2_Conditional_4_Conditional_2_Template, 5, 8, "div", 37)(3, MJMediaPlayerComponent_Conditional_2_Conditional_4_Conditional_3_Template, 4, 6, "div", 38)(4, MJMediaPlayerComponent_Conditional_2_Conditional_4_Conditional_4_Template, 2, 0, "div", 39);
97
+ } if (rf & 2) {
98
+ const ctx_r1 = i0.ɵɵnextContext(2);
99
+ i0.ɵɵrepeater(ctx_r1.AudioTracks);
100
+ i0.ɵɵadvance(2);
101
+ i0.ɵɵconditional(ctx_r1.ShowWaveform && ctx_r1.ShowWaveformBars ? 2 : ctx_r1.ShowWaveform ? 3 : 4);
102
+ } }
103
+ function MJMediaPlayerComponent_Conditional_2_Conditional_5_For_2_Conditional_1_Template(rf, ctx) { if (rf & 1) {
104
+ i0.ɵɵdomElementStart(0, "span", 52);
105
+ i0.ɵɵtext(1);
106
+ i0.ɵɵdomElementEnd();
107
+ } if (rf & 2) {
108
+ const track_r13 = i0.ɵɵnextContext().$implicit;
109
+ i0.ɵɵadvance();
110
+ i0.ɵɵtextInterpolate(track_r13.Label);
111
+ } }
112
+ function MJMediaPlayerComponent_Conditional_2_Conditional_5_For_2_Conditional_2_Template(rf, ctx) { if (rf & 1) {
113
+ const _r14 = i0.ɵɵgetCurrentView();
114
+ i0.ɵɵdomElementStart(0, "video", 55, 1);
115
+ i0.ɵɵdomListener("loadstart", function MJMediaPlayerComponent_Conditional_2_Conditional_5_For_2_Conditional_2_Template_video_loadstart_0_listener($event) { i0.ɵɵrestoreView(_r14); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.OnMediaLoadStart($event.target)); })("loadedmetadata", function MJMediaPlayerComponent_Conditional_2_Conditional_5_For_2_Conditional_2_Template_video_loadedmetadata_0_listener($event) { i0.ɵɵrestoreView(_r14); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.OnMediaLoadedMetadata($event.target)); })("canplay", function MJMediaPlayerComponent_Conditional_2_Conditional_5_For_2_Conditional_2_Template_video_canplay_0_listener($event) { i0.ɵɵrestoreView(_r14); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.OnMediaCanPlay($event.target)); })("canplaythrough", function MJMediaPlayerComponent_Conditional_2_Conditional_5_For_2_Conditional_2_Template_video_canplaythrough_0_listener($event) { i0.ɵɵrestoreView(_r14); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.OnMediaCanPlay($event.target)); })("waiting", function MJMediaPlayerComponent_Conditional_2_Conditional_5_For_2_Conditional_2_Template_video_waiting_0_listener($event) { i0.ɵɵrestoreView(_r14); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.OnMediaWaiting($event.target)); })("seeking", function MJMediaPlayerComponent_Conditional_2_Conditional_5_For_2_Conditional_2_Template_video_seeking_0_listener($event) { i0.ɵɵrestoreView(_r14); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.OnMediaSeeking($event.target)); })("seeked", function MJMediaPlayerComponent_Conditional_2_Conditional_5_For_2_Conditional_2_Template_video_seeked_0_listener($event) { i0.ɵɵrestoreView(_r14); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.OnMediaSeeked($event.target)); })("playing", function MJMediaPlayerComponent_Conditional_2_Conditional_5_For_2_Conditional_2_Template_video_playing_0_listener($event) { i0.ɵɵrestoreView(_r14); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.OnMediaPlaying($event.target)); })("stalled", function MJMediaPlayerComponent_Conditional_2_Conditional_5_For_2_Conditional_2_Template_video_stalled_0_listener($event) { i0.ɵɵrestoreView(_r14); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.OnMediaStalled($event.target)); })("suspend", function MJMediaPlayerComponent_Conditional_2_Conditional_5_For_2_Conditional_2_Template_video_suspend_0_listener($event) { i0.ɵɵrestoreView(_r14); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.OnMediaSuspend($event.target)); })("emptied", function MJMediaPlayerComponent_Conditional_2_Conditional_5_For_2_Conditional_2_Template_video_emptied_0_listener($event) { i0.ɵɵrestoreView(_r14); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.OnMediaEmptied($event.target)); })("timeupdate", function MJMediaPlayerComponent_Conditional_2_Conditional_5_For_2_Conditional_2_Template_video_timeupdate_0_listener($event) { i0.ɵɵrestoreView(_r14); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.OnMediaTimeUpdate($event.target)); })("durationchange", function MJMediaPlayerComponent_Conditional_2_Conditional_5_For_2_Conditional_2_Template_video_durationchange_0_listener($event) { i0.ɵɵrestoreView(_r14); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.OnMediaDurationChange($event.target)); })("play", function MJMediaPlayerComponent_Conditional_2_Conditional_5_For_2_Conditional_2_Template_video_play_0_listener($event) { i0.ɵɵrestoreView(_r14); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.OnMediaPlay($event.target)); })("pause", function MJMediaPlayerComponent_Conditional_2_Conditional_5_For_2_Conditional_2_Template_video_pause_0_listener($event) { i0.ɵɵrestoreView(_r14); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.OnMediaPause($event.target)); })("ended", function MJMediaPlayerComponent_Conditional_2_Conditional_5_For_2_Conditional_2_Template_video_ended_0_listener($event) { i0.ɵɵrestoreView(_r14); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.OnMediaEnded($event.target)); })("error", function MJMediaPlayerComponent_Conditional_2_Conditional_5_For_2_Conditional_2_Template_video_error_0_listener($event) { i0.ɵɵrestoreView(_r14); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.OnMediaError($event.target)); });
116
+ i0.ɵɵdomElementEnd();
117
+ } if (rf & 2) {
118
+ const track_r13 = i0.ɵɵnextContext().$implicit;
119
+ i0.ɵɵdomProperty("src", track_r13.Url, i0.ɵɵsanitizeUrl)("poster", track_r13.PosterUrl || null);
120
+ } }
121
+ function MJMediaPlayerComponent_Conditional_2_Conditional_5_For_2_Conditional_3_Template(rf, ctx) { if (rf & 1) {
122
+ i0.ɵɵdomElement(0, "video", 54);
123
+ } if (rf & 2) {
124
+ const track_r13 = i0.ɵɵnextContext().$implicit;
125
+ i0.ɵɵdomProperty("src", track_r13.Url, i0.ɵɵsanitizeUrl)("poster", track_r13.PosterUrl || null);
126
+ } }
127
+ function MJMediaPlayerComponent_Conditional_2_Conditional_5_For_2_Template(rf, ctx) { if (rf & 1) {
128
+ i0.ɵɵdomElementStart(0, "div", 51);
129
+ i0.ɵɵconditionalCreate(1, MJMediaPlayerComponent_Conditional_2_Conditional_5_For_2_Conditional_1_Template, 2, 1, "span", 52);
130
+ i0.ɵɵconditionalCreate(2, MJMediaPlayerComponent_Conditional_2_Conditional_5_For_2_Conditional_2_Template, 2, 2, "video", 53)(3, MJMediaPlayerComponent_Conditional_2_Conditional_5_For_2_Conditional_3_Template, 1, 2, "video", 54);
131
+ i0.ɵɵdomElementEnd();
132
+ } if (rf & 2) {
133
+ const track_r13 = ctx.$implicit;
134
+ const ɵ$index_60_r15 = ctx.$index;
135
+ i0.ɵɵadvance();
136
+ i0.ɵɵconditional(track_r13.Label ? 1 : -1);
137
+ i0.ɵɵadvance();
138
+ i0.ɵɵconditional(ɵ$index_60_r15 === 0 ? 2 : 3);
139
+ } }
140
+ function MJMediaPlayerComponent_Conditional_2_Conditional_5_Template(rf, ctx) { if (rf & 1) {
141
+ i0.ɵɵdomElementStart(0, "div", 50);
142
+ i0.ɵɵrepeaterCreate(1, MJMediaPlayerComponent_Conditional_2_Conditional_5_For_2_Template, 4, 2, "div", 51, i0.ɵɵcomponentInstance().TrackById, true);
143
+ i0.ɵɵdomElementEnd();
144
+ } if (rf & 2) {
145
+ const ctx_r1 = i0.ɵɵnextContext(2);
146
+ i0.ɵɵclassProp("mj-media-video-grid--multi", ctx_r1.IsMultiVideo);
147
+ i0.ɵɵadvance();
148
+ i0.ɵɵrepeater(ctx_r1.VideoTracks);
149
+ } }
150
+ function MJMediaPlayerComponent_Conditional_2_Conditional_8_Template(rf, ctx) { if (rf & 1) {
151
+ i0.ɵɵdomElement(0, "span", 14);
152
+ } }
153
+ function MJMediaPlayerComponent_Conditional_2_Conditional_23_Template(rf, ctx) { if (rf & 1) {
154
+ const _r17 = i0.ɵɵgetCurrentView();
155
+ i0.ɵɵdomElementStart(0, "button", 56);
156
+ i0.ɵɵdomListener("click", function MJMediaPlayerComponent_Conditional_2_Conditional_23_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r17); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.SkipBackward()); });
157
+ i0.ɵɵdomElement(1, "i", 57);
158
+ i0.ɵɵdomElementStart(2, "span", 58);
159
+ i0.ɵɵtext(3);
160
+ i0.ɵɵdomElementEnd()();
161
+ } if (rf & 2) {
162
+ const ctx_r1 = i0.ɵɵnextContext(2);
163
+ i0.ɵɵdomProperty("title", i0.ɵɵinterpolate1("Skip back ", ctx_r1.SkipSeconds, "s"));
164
+ i0.ɵɵattribute("aria-label", "Skip back " + ctx_r1.SkipSeconds + " seconds");
165
+ i0.ɵɵadvance(3);
166
+ i0.ɵɵtextInterpolate(ctx_r1.SkipSeconds);
167
+ } }
168
+ function MJMediaPlayerComponent_Conditional_2_Conditional_26_Template(rf, ctx) { if (rf & 1) {
169
+ const _r18 = i0.ɵɵgetCurrentView();
170
+ i0.ɵɵdomElementStart(0, "button", 56);
171
+ i0.ɵɵdomListener("click", function MJMediaPlayerComponent_Conditional_2_Conditional_26_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r18); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.SkipForward()); });
172
+ i0.ɵɵdomElement(1, "i", 59);
173
+ i0.ɵɵdomElementStart(2, "span", 58);
174
+ i0.ɵɵtext(3);
175
+ i0.ɵɵdomElementEnd()();
176
+ } if (rf & 2) {
177
+ const ctx_r1 = i0.ɵɵnextContext(2);
178
+ i0.ɵɵdomProperty("title", i0.ɵɵinterpolate1("Skip forward ", ctx_r1.SkipSeconds, "s"));
179
+ i0.ɵɵattribute("aria-label", "Skip forward " + ctx_r1.SkipSeconds + " seconds");
180
+ i0.ɵɵadvance(3);
181
+ i0.ɵɵtextInterpolate(ctx_r1.SkipSeconds);
182
+ } }
183
+ function MJMediaPlayerComponent_Conditional_2_Conditional_28_Template(rf, ctx) { if (rf & 1) {
184
+ const _r19 = i0.ɵɵgetCurrentView();
185
+ i0.ɵɵdomElementStart(0, "div", 29)(1, "button", 56);
186
+ i0.ɵɵdomListener("click", function MJMediaPlayerComponent_Conditional_2_Conditional_28_Template_button_click_1_listener() { i0.ɵɵrestoreView(_r19); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.ToggleMute()); });
187
+ i0.ɵɵdomElement(2, "i", 27);
188
+ i0.ɵɵdomElementEnd();
189
+ i0.ɵɵdomElementStart(3, "input", 60);
190
+ i0.ɵɵdomListener("input", function MJMediaPlayerComponent_Conditional_2_Conditional_28_Template_input_input_3_listener($event) { i0.ɵɵrestoreView(_r19); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.OnVolumeInput($event)); });
191
+ i0.ɵɵdomElementEnd()();
192
+ } if (rf & 2) {
193
+ const ctx_r1 = i0.ɵɵnextContext(2);
194
+ i0.ɵɵadvance();
195
+ i0.ɵɵdomProperty("title", ctx_r1.Muted ? "Unmute" : "Mute");
196
+ i0.ɵɵattribute("aria-label", ctx_r1.Muted ? "Unmute" : "Mute");
197
+ i0.ɵɵadvance();
198
+ i0.ɵɵclassProp("fa-volume-xmark", ctx_r1.Muted || ctx_r1.Volume === 0)("fa-volume-low", !ctx_r1.Muted && ctx_r1.Volume > 0 && ctx_r1.Volume <= 0.5)("fa-volume-high", !ctx_r1.Muted && ctx_r1.Volume > 0.5);
199
+ i0.ɵɵadvance();
200
+ i0.ɵɵdomProperty("value", ctx_r1.Muted ? 0 : ctx_r1.Volume);
201
+ } }
202
+ function MJMediaPlayerComponent_Conditional_2_Conditional_29_Conditional_3_For_2_Template(rf, ctx) { if (rf & 1) {
203
+ const _r21 = i0.ɵɵgetCurrentView();
204
+ i0.ɵɵdomElementStart(0, "li", 63)(1, "button", 64);
205
+ i0.ɵɵdomListener("click", function MJMediaPlayerComponent_Conditional_2_Conditional_29_Conditional_3_For_2_Template_button_click_1_listener() { const rate_r22 = i0.ɵɵrestoreView(_r21).$implicit; const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.SetPlaybackRate(rate_r22)); });
206
+ i0.ɵɵtext(2);
207
+ i0.ɵɵdomElementEnd()();
208
+ } if (rf & 2) {
209
+ const rate_r22 = ctx.$implicit;
210
+ const ctx_r1 = i0.ɵɵnextContext(4);
211
+ i0.ɵɵadvance();
212
+ i0.ɵɵclassProp("mj-media-rate__item--active", rate_r22 === ctx_r1.PlaybackRate);
213
+ i0.ɵɵattribute("aria-checked", rate_r22 === ctx_r1.PlaybackRate);
214
+ i0.ɵɵadvance();
215
+ i0.ɵɵtextInterpolate1(" ", rate_r22, "\u00D7 ");
216
+ } }
217
+ function MJMediaPlayerComponent_Conditional_2_Conditional_29_Conditional_3_Template(rf, ctx) { if (rf & 1) {
218
+ i0.ɵɵdomElementStart(0, "ul", 62);
219
+ i0.ɵɵrepeaterCreate(1, MJMediaPlayerComponent_Conditional_2_Conditional_29_Conditional_3_For_2_Template, 3, 4, "li", 63, i0.ɵɵrepeaterTrackByIdentity);
220
+ i0.ɵɵdomElementEnd();
221
+ } if (rf & 2) {
222
+ const ctx_r1 = i0.ɵɵnextContext(3);
223
+ i0.ɵɵadvance();
224
+ i0.ɵɵrepeater(ctx_r1.PlaybackRates);
225
+ } }
226
+ function MJMediaPlayerComponent_Conditional_2_Conditional_29_Template(rf, ctx) { if (rf & 1) {
227
+ const _r20 = i0.ɵɵgetCurrentView();
228
+ i0.ɵɵdomElementStart(0, "div", 30)(1, "button", 61);
229
+ i0.ɵɵdomListener("click", function MJMediaPlayerComponent_Conditional_2_Conditional_29_Template_button_click_1_listener() { i0.ɵɵrestoreView(_r20); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.ToggleRateMenu()); });
230
+ i0.ɵɵtext(2);
231
+ i0.ɵɵdomElementEnd();
232
+ i0.ɵɵconditionalCreate(3, MJMediaPlayerComponent_Conditional_2_Conditional_29_Conditional_3_Template, 3, 0, "ul", 62);
233
+ i0.ɵɵdomElementEnd();
234
+ } if (rf & 2) {
235
+ const ctx_r1 = i0.ɵɵnextContext(2);
236
+ i0.ɵɵadvance();
237
+ i0.ɵɵattribute("aria-expanded", ctx_r1.RateMenuOpen);
238
+ i0.ɵɵadvance();
239
+ i0.ɵɵtextInterpolate1(" ", ctx_r1.PlaybackRate, "\u00D7 ");
240
+ i0.ɵɵadvance();
241
+ i0.ɵɵconditional(ctx_r1.RateMenuOpen ? 3 : -1);
242
+ } }
243
+ function MJMediaPlayerComponent_Conditional_2_Conditional_30_Template(rf, ctx) { if (rf & 1) {
244
+ const _r23 = i0.ɵɵgetCurrentView();
245
+ i0.ɵɵdomElementStart(0, "button", 56);
246
+ i0.ɵɵdomListener("click", function MJMediaPlayerComponent_Conditional_2_Conditional_30_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r23); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.ToggleTranscript()); });
247
+ i0.ɵɵdomElement(1, "i", 65);
248
+ i0.ɵɵdomElementEnd();
249
+ } if (rf & 2) {
250
+ const ctx_r1 = i0.ɵɵnextContext(2);
251
+ i0.ɵɵclassProp("mj-media-btn--active", ctx_r1.TranscriptVisible);
252
+ i0.ɵɵdomProperty("title", ctx_r1.TranscriptVisible ? "Hide transcript" : "Show transcript");
253
+ i0.ɵɵattribute("aria-pressed", ctx_r1.TranscriptVisible)("aria-label", ctx_r1.TranscriptVisible ? "Hide transcript" : "Show transcript");
254
+ } }
255
+ function MJMediaPlayerComponent_Conditional_2_Conditional_31_Template(rf, ctx) { if (rf & 1) {
256
+ const _r24 = i0.ɵɵgetCurrentView();
257
+ i0.ɵɵdomElementStart(0, "button", 56);
258
+ i0.ɵɵdomListener("click", function MJMediaPlayerComponent_Conditional_2_Conditional_31_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r24); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.ToggleFullscreen()); });
259
+ i0.ɵɵdomElement(1, "i", 27);
260
+ i0.ɵɵdomElementEnd();
261
+ } if (rf & 2) {
262
+ const ctx_r1 = i0.ɵɵnextContext(2);
263
+ i0.ɵɵdomProperty("title", ctx_r1.IsFullscreen ? "Exit fullscreen" : "Enter fullscreen");
264
+ i0.ɵɵattribute("aria-label", ctx_r1.IsFullscreen ? "Exit fullscreen" : "Enter fullscreen");
265
+ i0.ɵɵadvance();
266
+ i0.ɵɵclassProp("fa-compress", ctx_r1.IsFullscreen)("fa-expand", !ctx_r1.IsFullscreen);
267
+ } }
268
+ function MJMediaPlayerComponent_Conditional_2_Conditional_32_For_2_Conditional_4_Template(rf, ctx) { if (rf & 1) {
269
+ i0.ɵɵdomElementStart(0, "span", 72);
270
+ i0.ɵɵtext(1);
271
+ i0.ɵɵdomElementEnd();
272
+ } if (rf & 2) {
273
+ const cue_r27 = i0.ɵɵnextContext().$implicit;
274
+ const ctx_r1 = i0.ɵɵnextContext(3);
275
+ i0.ɵɵstyleProp("color", ctx_r1.SpeakerColor(cue_r27.SpeakerLabel));
276
+ i0.ɵɵadvance();
277
+ i0.ɵɵtextInterpolate1(" ", cue_r27.SpeakerLabel, " ");
278
+ } }
279
+ function MJMediaPlayerComponent_Conditional_2_Conditional_32_For_2_Template(rf, ctx) { if (rf & 1) {
280
+ const _r25 = i0.ɵɵgetCurrentView();
281
+ i0.ɵɵdomElementStart(0, "button", 67);
282
+ i0.ɵɵdomListener("click", function MJMediaPlayerComponent_Conditional_2_Conditional_32_For_2_Template_button_click_0_listener() { const ɵ$index_162_r26 = i0.ɵɵrestoreView(_r25).$index; const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.OnCueClicked(ɵ$index_162_r26)); })("keydown", function MJMediaPlayerComponent_Conditional_2_Conditional_32_For_2_Template_button_keydown_0_listener($event) { const ɵ$index_162_r26 = i0.ɵɵrestoreView(_r25).$index; const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.OnCueKeyDown($event, ɵ$index_162_r26)); });
283
+ i0.ɵɵdomElementStart(1, "span", 68);
284
+ i0.ɵɵtext(2);
285
+ i0.ɵɵdomElementEnd();
286
+ i0.ɵɵdomElementStart(3, "span", 69);
287
+ i0.ɵɵconditionalCreate(4, MJMediaPlayerComponent_Conditional_2_Conditional_32_For_2_Conditional_4_Template, 2, 3, "span", 70);
288
+ i0.ɵɵdomElementStart(5, "span", 71);
289
+ i0.ɵɵtext(6);
290
+ i0.ɵɵdomElementEnd()()();
291
+ } if (rf & 2) {
292
+ const cue_r27 = ctx.$implicit;
293
+ const ɵ$index_162_r26 = ctx.$index;
294
+ const ctx_r1 = i0.ɵɵnextContext(3);
295
+ i0.ɵɵclassProp("mj-media-cue--active", ɵ$index_162_r26 === ctx_r1.ActiveCueIndex);
296
+ i0.ɵɵattribute("aria-current", ɵ$index_162_r26 === ctx_r1.ActiveCueIndex ? "true" : null);
297
+ i0.ɵɵadvance(2);
298
+ i0.ɵɵtextInterpolate(ctx_r1.FormatTime(cue_r27.StartMs));
299
+ i0.ɵɵadvance(2);
300
+ i0.ɵɵconditional(cue_r27.SpeakerLabel ? 4 : -1);
301
+ i0.ɵɵadvance(2);
302
+ i0.ɵɵtextInterpolate(cue_r27.Text);
303
+ } }
304
+ function MJMediaPlayerComponent_Conditional_2_Conditional_32_Template(rf, ctx) { if (rf & 1) {
305
+ i0.ɵɵdomElementStart(0, "div", 32);
306
+ i0.ɵɵrepeaterCreate(1, MJMediaPlayerComponent_Conditional_2_Conditional_32_For_2_Template, 7, 6, "button", 66, i0.ɵɵcomponentInstance().CueTrackById, true);
307
+ i0.ɵɵdomElementEnd();
308
+ } if (rf & 2) {
309
+ const ctx_r1 = i0.ɵɵnextContext(2);
310
+ i0.ɵɵadvance();
311
+ i0.ɵɵrepeater(ctx_r1.Transcript);
312
+ } }
313
+ function MJMediaPlayerComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) {
314
+ const _r1 = i0.ɵɵgetCurrentView();
315
+ i0.ɵɵdomElementStart(0, "div", 7)(1, "div", 8);
316
+ i0.ɵɵconditionalCreate(2, MJMediaPlayerComponent_Conditional_2_Conditional_2_Template, 4, 0, "div", 9);
317
+ i0.ɵɵconditionalCreate(3, MJMediaPlayerComponent_Conditional_2_Conditional_3_Template, 4, 3, "div", 10);
318
+ i0.ɵɵconditionalCreate(4, MJMediaPlayerComponent_Conditional_2_Conditional_4_Template, 5, 1)(5, MJMediaPlayerComponent_Conditional_2_Conditional_5_Template, 3, 2, "div", 11);
319
+ i0.ɵɵdomElementEnd();
320
+ i0.ɵɵdomElementStart(6, "div", 12)(7, "div", 13);
321
+ i0.ɵɵconditionalCreate(8, MJMediaPlayerComponent_Conditional_2_Conditional_8_Template, 1, 0, "span", 14);
322
+ i0.ɵɵdomElementStart(9, "span", 15);
323
+ i0.ɵɵtext(10);
324
+ i0.ɵɵdomElementEnd()();
325
+ i0.ɵɵdomElementStart(11, "div", 16)(12, "span", 17);
326
+ i0.ɵɵtext(13);
327
+ i0.ɵɵdomElementEnd();
328
+ i0.ɵɵdomElementStart(14, "div", 18, 0);
329
+ i0.ɵɵdomListener("pointerdown", function MJMediaPlayerComponent_Conditional_2_Template_div_pointerdown_14_listener($event) { i0.ɵɵrestoreView(_r1); const scrubTrack_r16 = i0.ɵɵreference(15); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnScrubPointerDown($event, scrubTrack_r16)); })("pointermove", function MJMediaPlayerComponent_Conditional_2_Template_div_pointermove_14_listener($event) { i0.ɵɵrestoreView(_r1); const scrubTrack_r16 = i0.ɵɵreference(15); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnScrubPointerMove($event, scrubTrack_r16)); })("pointerup", function MJMediaPlayerComponent_Conditional_2_Template_div_pointerup_14_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnScrubPointerUp($event)); })("pointercancel", function MJMediaPlayerComponent_Conditional_2_Template_div_pointercancel_14_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnScrubPointerUp($event)); })("keydown", function MJMediaPlayerComponent_Conditional_2_Template_div_keydown_14_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnScrubKeyDown($event)); });
330
+ i0.ɵɵdomElementStart(16, "div", 19);
331
+ i0.ɵɵdomElement(17, "div", 20);
332
+ i0.ɵɵdomElementEnd();
333
+ i0.ɵɵdomElement(18, "div", 21);
334
+ i0.ɵɵdomElementEnd();
335
+ i0.ɵɵdomElementStart(19, "span", 22);
336
+ i0.ɵɵtext(20);
337
+ i0.ɵɵdomElementEnd()();
338
+ i0.ɵɵdomElementStart(21, "div", 23)(22, "div", 24);
339
+ i0.ɵɵconditionalCreate(23, MJMediaPlayerComponent_Conditional_2_Conditional_23_Template, 4, 4, "button", 25);
340
+ i0.ɵɵdomElementStart(24, "button", 26);
341
+ i0.ɵɵdomListener("click", function MJMediaPlayerComponent_Conditional_2_Template_button_click_24_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.TogglePlay()); });
342
+ i0.ɵɵdomElement(25, "i", 27);
343
+ i0.ɵɵdomElementEnd();
344
+ i0.ɵɵconditionalCreate(26, MJMediaPlayerComponent_Conditional_2_Conditional_26_Template, 4, 4, "button", 25);
345
+ i0.ɵɵdomElementEnd();
346
+ i0.ɵɵdomElementStart(27, "div", 28);
347
+ i0.ɵɵconditionalCreate(28, MJMediaPlayerComponent_Conditional_2_Conditional_28_Template, 4, 9, "div", 29);
348
+ i0.ɵɵconditionalCreate(29, MJMediaPlayerComponent_Conditional_2_Conditional_29_Template, 4, 3, "div", 30);
349
+ i0.ɵɵconditionalCreate(30, MJMediaPlayerComponent_Conditional_2_Conditional_30_Template, 2, 5, "button", 31);
350
+ i0.ɵɵconditionalCreate(31, MJMediaPlayerComponent_Conditional_2_Conditional_31_Template, 2, 6, "button", 25);
351
+ i0.ɵɵdomElementEnd()()()();
352
+ i0.ɵɵconditionalCreate(32, MJMediaPlayerComponent_Conditional_2_Conditional_32_Template, 3, 0, "div", 32);
353
+ } if (rf & 2) {
354
+ const ctx_r1 = i0.ɵɵnextContext();
355
+ i0.ɵɵadvance(2);
356
+ i0.ɵɵconditional(ctx_r1.HasError ? 2 : -1);
357
+ i0.ɵɵadvance();
358
+ i0.ɵɵconditional(ctx_r1.ShowBusyIndicator && !ctx_r1.HasError ? 3 : -1);
359
+ i0.ɵɵadvance();
360
+ i0.ɵɵconditional(ctx_r1.IsAudioOnly ? 4 : 5);
361
+ i0.ɵɵadvance(3);
362
+ i0.ɵɵclassProp("mj-media-status--busy", ctx_r1.ShowBusyIndicator);
363
+ i0.ɵɵadvance();
364
+ i0.ɵɵconditional(ctx_r1.ShowBusyIndicator ? 8 : -1);
365
+ i0.ɵɵadvance(2);
366
+ i0.ɵɵtextInterpolate(ctx_r1.StatusLabel);
367
+ i0.ɵɵadvance(3);
368
+ i0.ɵɵtextInterpolate(ctx_r1.FormatTime(ctx_r1.CurrentTimeMs));
369
+ i0.ɵɵadvance();
370
+ i0.ɵɵattribute("aria-valuemin", 0)("aria-valuemax", ctx_r1.DurationMs)("aria-valuenow", ctx_r1.CurrentTimeMs)("aria-valuetext", ctx_r1.FormatTime(ctx_r1.CurrentTimeMs) + " of " + ctx_r1.FormatTime(ctx_r1.DurationMs));
371
+ i0.ɵɵadvance(3);
372
+ i0.ɵɵstyleProp("width", ctx_r1.ScrubPercent, "%");
373
+ i0.ɵɵadvance();
374
+ i0.ɵɵstyleProp("left", ctx_r1.ScrubPercent, "%");
375
+ i0.ɵɵadvance(2);
376
+ i0.ɵɵtextInterpolate(ctx_r1.FormatTime(ctx_r1.DurationMs));
377
+ i0.ɵɵadvance(3);
378
+ i0.ɵɵconditional(ctx_r1.ShowSkipControls ? 23 : -1);
379
+ i0.ɵɵadvance();
380
+ i0.ɵɵdomProperty("title", ctx_r1.IsPlaying ? "Pause" : "Play");
381
+ i0.ɵɵattribute("aria-label", ctx_r1.IsPlaying ? "Pause" : "Play");
382
+ i0.ɵɵadvance();
383
+ i0.ɵɵclassProp("fa-pause", ctx_r1.IsPlaying)("fa-play", !ctx_r1.IsPlaying);
384
+ i0.ɵɵadvance();
385
+ i0.ɵɵconditional(ctx_r1.ShowSkipControls ? 26 : -1);
386
+ i0.ɵɵadvance(2);
387
+ i0.ɵɵconditional(ctx_r1.ShowVolume ? 28 : -1);
388
+ i0.ɵɵadvance();
389
+ i0.ɵɵconditional(ctx_r1.ShowSpeedControl ? 29 : -1);
390
+ i0.ɵɵadvance();
391
+ i0.ɵɵconditional(ctx_r1.ShowTranscriptToggleButton ? 30 : -1);
392
+ i0.ɵɵadvance();
393
+ i0.ɵɵconditional(ctx_r1.ShowFullscreen && ctx_r1.VideoTracks.length > 0 ? 31 : -1);
394
+ i0.ɵɵadvance();
395
+ i0.ɵɵconditional(ctx_r1.ShowTranscriptPanel ? 32 : -1);
396
+ } }
397
+ /**
398
+ * Delay (ms) before the *buffering* spinner is shown, to avoid flicker on instant seeks
399
+ * that resolve immediately. The *loading* indicator shows with no delay. If a buffering
400
+ * state clears before this elapses, the spinner never appears.
401
+ */
402
+ const BUFFERING_INDICATOR_DELAY_MS = 200;
403
+ /**
404
+ * `mj-media-player` — a generic, framework-agnostic media player.
405
+ *
406
+ * Renders one or many audio/video tracks with a custom transport bar (play/pause,
407
+ * click-and-drag scrubber, skip ±N, playback-rate menu, volume + mute, fullscreen),
408
+ * a **real** audio waveform (decoded client-side, or supplied precomputed via
409
+ * `MediaTrack.Peaks`) that doubles as the scrubber, and an optional time-synced
410
+ * transcript panel whose cues are clickable and auto-highlight as playback advances.
411
+ *
412
+ * This component has ZERO MemberJunction-core dependencies — it is pure Angular and
413
+ * is safe to reuse in any application. The MJStorage binding lives in the separate
414
+ * `mj-storage-media-player` wrapper.
415
+ */
416
+ export class MJMediaPlayerComponent {
417
+ cdr = inject(ChangeDetectorRef);
418
+ host = inject((ElementRef));
419
+ constructor() {
420
+ this._prefersReducedMotion = this.detectReducedMotion();
421
+ }
422
+ // ---------------------------------------------------------------------------
423
+ // Inputs
424
+ // ---------------------------------------------------------------------------
425
+ /** The track(s) to play. One audio track → transport bar; many videos → grid. */
426
+ set Tracks(value) {
427
+ this._tracks = value ?? [];
428
+ this._activeMediaEl = null;
429
+ this.resetPlaybackState();
430
+ this.ensureWaveforms();
431
+ this.cdr.markForCheck();
432
+ }
433
+ get Tracks() {
434
+ return this._tracks;
435
+ }
436
+ _tracks = [];
437
+ /** Optional transcript cues. When set (and `ShowTranscript`), a transcript panel renders. */
438
+ set Transcript(value) {
439
+ this._transcript = value;
440
+ this._activeCueIndex = -1;
441
+ // A newly-supplied transcript defaults to visible (the user can hide it via the toggle).
442
+ this._transcriptUserVisible = true;
443
+ this.cdr.markForCheck();
444
+ }
445
+ get Transcript() {
446
+ return this._transcript;
447
+ }
448
+ _transcript = null;
449
+ /** Begin playback automatically once media is ready. */
450
+ Autoplay = false;
451
+ /** Start playback at this position (ms) once metadata loads. */
452
+ StartAtMs = null;
453
+ /** Whether the transcript panel is shown (when a transcript is provided). */
454
+ ShowTranscript = true;
455
+ /** Whether the transcript show/hide toggle button renders in the transport bar. */
456
+ ShowTranscriptToggle = true;
457
+ /** Whether the playback-rate menu is shown. */
458
+ ShowSpeedControl = true;
459
+ /** Whether the ±skip buttons are shown. */
460
+ ShowSkipControls = true;
461
+ /** Whether the volume slider + mute are shown. */
462
+ ShowVolume = true;
463
+ /**
464
+ * Whether the real audio waveform (which doubles as the scrubber) is shown for
465
+ * audio-only tracks. On by default. When a track supplies {@link MediaTrack.Peaks}
466
+ * those are rendered directly; otherwise the player decodes the track URL client-side
467
+ * to extract real peaks, falling back to a plain progress bar if decoding fails.
468
+ */
469
+ ShowWaveform = true;
470
+ /** Number of bars the waveform renders / downsamples to. */
471
+ WaveformBarCount = DEFAULT_WAVEFORM_BARS;
472
+ /** Whether the fullscreen button is shown for video. */
473
+ ShowFullscreen = true;
474
+ /** Number of seconds the skip buttons jump. */
475
+ SkipSeconds = 30;
476
+ /** The playback rates offered in the rate menu. */
477
+ PlaybackRates = [0.5, 1, 1.25, 1.5, 2];
478
+ /** The initial playback rate. */
479
+ InitialRate = 1;
480
+ /** The initial volume (0..1). */
481
+ InitialVolume = 1;
482
+ /**
483
+ * Where the transcript panel sits relative to the media. Defaults to `'bottom'`
484
+ * (full width below the player, filling remaining height) which reads better than
485
+ * the narrower side layout; consumers can opt into `'side'`.
486
+ */
487
+ TranscriptPosition = 'bottom';
488
+ // ---------------------------------------------------------------------------
489
+ // Outputs — Before* are cancelable (set event.Cancel = true to abort)
490
+ // ---------------------------------------------------------------------------
491
+ BeforePlay = new EventEmitter();
492
+ AfterPlay = new EventEmitter();
493
+ BeforePause = new EventEmitter();
494
+ AfterPause = new EventEmitter();
495
+ BeforeSeek = new EventEmitter();
496
+ AfterSeek = new EventEmitter();
497
+ BeforeRateChange = new EventEmitter();
498
+ AfterRateChange = new EventEmitter();
499
+ CueActivated = new EventEmitter();
500
+ CueClicked = new EventEmitter();
501
+ TimeUpdate = new EventEmitter();
502
+ DurationChange = new EventEmitter();
503
+ Ended = new EventEmitter();
504
+ /** Emits whenever the primary element's {@link MediaPlaybackState} transitions. */
505
+ StateChanged = new EventEmitter();
506
+ /** Emits the new visibility (`true` = shown) whenever the transcript is toggled. */
507
+ TranscriptVisibilityChanged = new EventEmitter();
508
+ // ---------------------------------------------------------------------------
509
+ // View references
510
+ // ---------------------------------------------------------------------------
511
+ /** The primary media element (the single audio/video, or the first video in a grid). */
512
+ primaryMedia;
513
+ // ---------------------------------------------------------------------------
514
+ // Internal state
515
+ // ---------------------------------------------------------------------------
516
+ _currentTimeMs = 0;
517
+ _durationMs = 0;
518
+ _isPlaying = false;
519
+ _playbackRate = 1;
520
+ _volume = 1;
521
+ _muted = false;
522
+ _activeCueIndex = -1;
523
+ _isFullscreen = false;
524
+ _isScrubbing = false;
525
+ _hasError = false;
526
+ _rateMenuOpen = false;
527
+ /** Media elements we've already nudged to resolve a real (finite) duration. */
528
+ _durationForced = new WeakSet();
529
+ /** True while a force-duration nudge is in flight — suppresses the transient time/position jump. */
530
+ _suppressTimeUpdate = false;
531
+ _activeMediaEl = null;
532
+ _rateInitialized = false;
533
+ /** Runtime (toggle-driven) transcript visibility. Reset to `true` when a transcript is set. */
534
+ _transcriptUserVisible = true;
535
+ /** The high-level playback lifecycle state of the primary element. */
536
+ _mediaState = 'idle';
537
+ /**
538
+ * Whether the buffering spinner is currently visible. Distinct from `_mediaState ===
539
+ * 'buffering'`: the state flips immediately, but the *visible* spinner is debounced by
540
+ * {@link BUFFERING_INDICATOR_DELAY_MS} so instant seeks don't flash it.
541
+ */
542
+ _bufferingVisible = false;
543
+ /** Pending timer that promotes a `buffering` state to a visible spinner after the delay. */
544
+ _bufferingTimer = null;
545
+ /** Per-track computed/supplied waveform peaks (0..1), keyed by track Id. */
546
+ _peaksByTrackId = new Map();
547
+ /** Track Ids whose client-side decode failed (→ fall back to a plain progress bar). */
548
+ _waveformFailedTrackIds = new Set();
549
+ /** Track Ids currently being decoded (avoid duplicate in-flight decodes). */
550
+ _waveformPendingTrackIds = new Set();
551
+ /** Lazily-created AudioContext used only for client-side peak extraction. */
552
+ _audioCtx = null;
553
+ /** Whether the user prefers reduced motion (suppresses the bar draw-in animation). */
554
+ _prefersReducedMotion = false;
555
+ // ---------------------------------------------------------------------------
556
+ // Public getters (imperative read API + template bindings)
557
+ // ---------------------------------------------------------------------------
558
+ get CurrentTimeMs() {
559
+ return this._currentTimeMs;
560
+ }
561
+ get DurationMs() {
562
+ return this._durationMs;
563
+ }
564
+ get IsPlaying() {
565
+ return this._isPlaying;
566
+ }
567
+ get PlaybackRate() {
568
+ return this._playbackRate;
569
+ }
570
+ get ActiveCueIndex() {
571
+ return this._activeCueIndex;
572
+ }
573
+ // Template-only convenience getters
574
+ get Muted() {
575
+ return this._muted;
576
+ }
577
+ get Volume() {
578
+ return this._volume;
579
+ }
580
+ get IsFullscreen() {
581
+ return this._isFullscreen;
582
+ }
583
+ get RateMenuOpen() {
584
+ return this._rateMenuOpen;
585
+ }
586
+ get HasError() {
587
+ return this._hasError;
588
+ }
589
+ /** The current high-level playback lifecycle state of the primary element. */
590
+ get MediaState() {
591
+ return this._mediaState;
592
+ }
593
+ /** True during the initial source load (shows the "Loading…" stage indicator). */
594
+ get IsLoading() {
595
+ return this._mediaState === 'loading';
596
+ }
597
+ /**
598
+ * True while buffering — but reflects the *debounced* visible flag, so an instant seek
599
+ * that resolves within {@link BUFFERING_INDICATOR_DELAY_MS} never shows the spinner.
600
+ */
601
+ get IsBuffering() {
602
+ return this._bufferingVisible;
603
+ }
604
+ /** Whether the stage should show ANY busy indicator (loading or visible buffering). */
605
+ get ShowBusyIndicator() {
606
+ return this.IsLoading || this.IsBuffering;
607
+ }
608
+ /** The label shown alongside the busy spinner. */
609
+ get BusyLabel() {
610
+ return this.IsLoading ? 'Loading…' : 'Buffering…';
611
+ }
612
+ /** A short, screen-reader-friendly status phrase reflecting the current state. */
613
+ get StatusLabel() {
614
+ switch (this._mediaState) {
615
+ case 'loading':
616
+ return 'Loading';
617
+ case 'buffering':
618
+ return 'Buffering';
619
+ case 'playing':
620
+ return 'Playing';
621
+ case 'paused':
622
+ return 'Paused';
623
+ case 'ready':
624
+ return 'Ready';
625
+ case 'ended':
626
+ return 'Ended';
627
+ case 'error':
628
+ return 'Error';
629
+ default:
630
+ return 'Idle';
631
+ }
632
+ }
633
+ get HasTracks() {
634
+ return this._tracks.length > 0;
635
+ }
636
+ /** Whether a transcript (one or more cues) is available, regardless of visibility. */
637
+ get HasTranscript() {
638
+ return !!this._transcript && this._transcript.length > 0;
639
+ }
640
+ /**
641
+ * Whether the transcript panel should render right now — requires a transcript,
642
+ * the `ShowTranscript` master switch, AND the runtime toggle to be visible.
643
+ */
644
+ get ShowTranscriptPanel() {
645
+ return resolveTranscriptVisible(this.HasTranscript, this.ShowTranscript, this._transcriptUserVisible);
646
+ }
647
+ /** The current runtime visibility of the transcript (toggle-driven). */
648
+ get TranscriptVisible() {
649
+ return this._transcriptUserVisible;
650
+ }
651
+ /** Whether the transcript show/hide toggle button should render in the transport. */
652
+ get ShowTranscriptToggleButton() {
653
+ return resolveTranscriptToggleVisible(this.HasTranscript, this.ShowTranscript, this.ShowTranscriptToggle);
654
+ }
655
+ get ScrubFraction() {
656
+ return this._durationMs > 0 ? this._currentTimeMs / this._durationMs : 0;
657
+ }
658
+ get ScrubPercent() {
659
+ return Math.min(100, Math.max(0, this.ScrubFraction * 100));
660
+ }
661
+ get PrefersReducedMotion() {
662
+ return this._prefersReducedMotion;
663
+ }
664
+ /** The active audio track that drives the waveform (the first audio track, if any). */
665
+ get WaveformTrack() {
666
+ return this.IsAudioOnly ? this.AudioTracks[0] ?? null : null;
667
+ }
668
+ /**
669
+ * True when the waveform should render as bars — i.e. it's enabled, the active track
670
+ * is audio, and we have peaks (supplied or successfully decoded) for it. When false
671
+ * (decode pending/failed, no track), the template shows a plain progress bar instead.
672
+ */
673
+ get ShowWaveformBars() {
674
+ const track = this.WaveformTrack;
675
+ if (!this.ShowWaveform || !track) {
676
+ return false;
677
+ }
678
+ return this.peaksForTrack(track) !== null;
679
+ }
680
+ /**
681
+ * The normalized `0..1` peaks for the active audio track, or `null` when none are
682
+ * available yet (still decoding) or decoding failed. Reading this lazily kicks off
683
+ * client-side extraction when needed — re-renders/seeks never re-decode (cached by Id).
684
+ */
685
+ get WaveformPeaks() {
686
+ const track = this.WaveformTrack;
687
+ if (!this.ShowWaveform || !track) {
688
+ return null;
689
+ }
690
+ return this.peaksForTrack(track);
691
+ }
692
+ /** The played/unplayed split point for the waveform, as a `0..1` fraction. */
693
+ get WaveformPlayedFraction() {
694
+ return this.ScrubFraction;
695
+ }
696
+ /** The single audio track when this is an audio-only player (drives waveform). */
697
+ get AudioTracks() {
698
+ return this._tracks.filter((t) => t.Kind === 'audio');
699
+ }
700
+ get VideoTracks() {
701
+ return this._tracks.filter((t) => t.Kind === 'video');
702
+ }
703
+ /** True when the layout is a single audio track with no video. */
704
+ get IsAudioOnly() {
705
+ return this.VideoTracks.length === 0 && this.AudioTracks.length > 0;
706
+ }
707
+ get IsMultiVideo() {
708
+ return this.VideoTracks.length > 1;
709
+ }
710
+ // ---------------------------------------------------------------------------
711
+ // Imperative control API
712
+ // ---------------------------------------------------------------------------
713
+ /** Begin playback. Honors {@link BeforePlay}. */
714
+ Play() {
715
+ const el = this.resolveMediaElement();
716
+ if (!el) {
717
+ return;
718
+ }
719
+ const evt = { Cancel: false };
720
+ this.BeforePlay.emit(evt);
721
+ if (evt.Cancel) {
722
+ return;
723
+ }
724
+ void el.play().catch(() => {
725
+ /* autoplay/user-gesture rejections are non-fatal */
726
+ });
727
+ }
728
+ /** Pause playback. Honors {@link BeforePause}. */
729
+ Pause() {
730
+ const el = this.resolveMediaElement();
731
+ if (!el) {
732
+ return;
733
+ }
734
+ const evt = { Cancel: false };
735
+ this.BeforePause.emit(evt);
736
+ if (evt.Cancel) {
737
+ return;
738
+ }
739
+ el.pause();
740
+ }
741
+ /** Toggle play/pause. */
742
+ TogglePlay() {
743
+ if (this._isPlaying) {
744
+ this.Pause();
745
+ }
746
+ else {
747
+ this.Play();
748
+ }
749
+ }
750
+ /** Seek to an absolute position in milliseconds. Honors {@link BeforeSeek}. */
751
+ SeekToMs(ms) {
752
+ const el = this.resolveMediaElement();
753
+ if (!el) {
754
+ return;
755
+ }
756
+ const target = this.clampMs(ms);
757
+ const evt = { Cancel: false, FromMs: this._currentTimeMs, ToMs: target };
758
+ this.BeforeSeek.emit(evt);
759
+ if (evt.Cancel) {
760
+ return;
761
+ }
762
+ el.currentTime = target / 1000;
763
+ this._currentTimeMs = target;
764
+ this.AfterSeek.emit(target);
765
+ this.refreshActiveCue();
766
+ this.cdr.markForCheck();
767
+ }
768
+ /** Skip forward by {@link SkipSeconds}. */
769
+ SkipForward() {
770
+ this.SeekToMs(this._currentTimeMs + this.SkipSeconds * 1000);
771
+ }
772
+ /** Skip backward by {@link SkipSeconds}. */
773
+ SkipBackward() {
774
+ this.SeekToMs(this._currentTimeMs - this.SkipSeconds * 1000);
775
+ }
776
+ /** Set the playback rate. Honors {@link BeforeRateChange}. */
777
+ SetPlaybackRate(rate) {
778
+ const evt = { Cancel: false, FromRate: this._playbackRate, ToRate: rate };
779
+ this.BeforeRateChange.emit(evt);
780
+ if (evt.Cancel) {
781
+ return;
782
+ }
783
+ this._playbackRate = rate;
784
+ this.applyToAllMedia((el) => (el.playbackRate = rate));
785
+ this._rateMenuOpen = false;
786
+ this.AfterRateChange.emit(rate);
787
+ this.cdr.markForCheck();
788
+ }
789
+ /**
790
+ * Seek to the cue at `index`. If currently playing, playback continues from the
791
+ * cue's start; if paused, the timeline is repositioned without starting playback.
792
+ */
793
+ SeekToCue(index) {
794
+ const cues = this._transcript;
795
+ if (!cues || index < 0 || index >= cues.length) {
796
+ return;
797
+ }
798
+ const wasPlaying = this._isPlaying;
799
+ this.SeekToMs(cues[index].StartMs);
800
+ if (wasPlaying) {
801
+ // keep playing — currentTime change above does not pause; ensure we're playing
802
+ this.Play();
803
+ }
804
+ // when paused, we leave it paused (timeline repositioned only)
805
+ }
806
+ /** Set the volume (0..1). Unmutes if volume > 0. */
807
+ SetVolume(v) {
808
+ this._volume = Math.min(1, Math.max(0, v));
809
+ this._muted = this._volume === 0;
810
+ this.applyToAllMedia((el) => {
811
+ el.volume = this._volume;
812
+ el.muted = this._muted;
813
+ });
814
+ this.cdr.markForCheck();
815
+ }
816
+ /** Toggle mute. */
817
+ ToggleMute() {
818
+ this._muted = !this._muted;
819
+ this.applyToAllMedia((el) => (el.muted = this._muted));
820
+ this.cdr.markForCheck();
821
+ }
822
+ /** Request fullscreen for the player surface (video). */
823
+ EnterFullscreen() {
824
+ const target = this.host.nativeElement.querySelector('.mj-media-stage');
825
+ if (target?.requestFullscreen) {
826
+ void target.requestFullscreen().catch(() => undefined);
827
+ }
828
+ }
829
+ /** Exit fullscreen. */
830
+ ExitFullscreen() {
831
+ if (document.fullscreenElement && document.exitFullscreen) {
832
+ void document.exitFullscreen().catch(() => undefined);
833
+ }
834
+ }
835
+ /** Toggle fullscreen. */
836
+ ToggleFullscreen() {
837
+ if (this._isFullscreen) {
838
+ this.ExitFullscreen();
839
+ }
840
+ else {
841
+ this.EnterFullscreen();
842
+ }
843
+ }
844
+ /**
845
+ * Show/hide the transcript panel (pure component state — nothing is persisted).
846
+ * Emits {@link TranscriptVisibilityChanged} with the new visibility.
847
+ */
848
+ ToggleTranscript() {
849
+ this._transcriptUserVisible = !this._transcriptUserVisible;
850
+ this.TranscriptVisibilityChanged.emit(this._transcriptUserVisible);
851
+ this.cdr.markForCheck();
852
+ }
853
+ // ---------------------------------------------------------------------------
854
+ // Media element event handlers (bound in template)
855
+ // ---------------------------------------------------------------------------
856
+ /** Initial source fetch began — drives the "Loading…" indicator. */
857
+ OnMediaLoadStart(el) {
858
+ if (el !== this.resolveMediaElement()) {
859
+ return;
860
+ }
861
+ this.applyMediaEvent('loadstart', el);
862
+ }
863
+ /** Wire-up when a media element fires its first event — caches the primary element & applies initial state. */
864
+ OnMediaLoadedMetadata(el) {
865
+ this._hasError = false;
866
+ if (!this._activeMediaEl) {
867
+ this._activeMediaEl = el;
868
+ }
869
+ this.initializeMediaElement(el);
870
+ const dur = el.duration;
871
+ if (isFinite(dur) && dur > 0) {
872
+ this._durationMs = Math.round(dur * 1000);
873
+ this.DurationChange.emit(this._durationMs);
874
+ }
875
+ else {
876
+ // MediaRecorder/streamed webm files often carry no duration in their header, so the
877
+ // element reports `Infinity` — which makes fraction-based seeking compute 0 (every
878
+ // timeline click snaps to the start). Nudge the element once to resolve a real duration;
879
+ // this also builds its internal seek index. The resulting `durationchange` is handled in
880
+ // OnMediaDurationChange.
881
+ this.forceDurationResolution(el);
882
+ }
883
+ if (this.StartAtMs != null && el === this._activeMediaEl) {
884
+ el.currentTime = this.clampMs(this.StartAtMs) / 1000;
885
+ }
886
+ if (this.Autoplay && el === this._activeMediaEl) {
887
+ this.Play();
888
+ }
889
+ this.applyMediaEvent('loadedmetadata', el);
890
+ this.cdr.markForCheck();
891
+ }
892
+ /** Enough data buffered to begin playback — clears loading/buffering. */
893
+ OnMediaCanPlay(el) {
894
+ if (el !== this.resolveMediaElement()) {
895
+ return;
896
+ }
897
+ this.applyMediaEvent('canplay', el);
898
+ }
899
+ /** Ran out of buffered data mid-playback — enters buffering. */
900
+ OnMediaWaiting(el) {
901
+ if (el !== this.resolveMediaElement()) {
902
+ return;
903
+ }
904
+ this.applyMediaEvent('waiting', el);
905
+ }
906
+ /** Seek began (skip / scrub into un-buffered territory) — show buffering during the seek. */
907
+ OnMediaSeeking(el) {
908
+ if (el !== this.resolveMediaElement()) {
909
+ return;
910
+ }
911
+ this.applyMediaEvent('seeking', el);
912
+ }
913
+ /** Seek completed — clears buffering once the element has playable data. */
914
+ OnMediaSeeked(el) {
915
+ if (el !== this.resolveMediaElement()) {
916
+ return;
917
+ }
918
+ this.applyMediaEvent('seeked', el);
919
+ }
920
+ /** Element is actively playing — clears loading + buffering. */
921
+ OnMediaPlaying(el) {
922
+ if (el !== this.resolveMediaElement()) {
923
+ return;
924
+ }
925
+ this.applyMediaEvent('playing', el);
926
+ }
927
+ /** Conservative buffering signals — only buffer when actively trying to play. */
928
+ OnMediaStalled(el) {
929
+ if (el !== this.resolveMediaElement()) {
930
+ return;
931
+ }
932
+ this.applyMediaEvent('stalled', el);
933
+ }
934
+ OnMediaSuspend(el) {
935
+ if (el !== this.resolveMediaElement()) {
936
+ return;
937
+ }
938
+ this.applyMediaEvent('suspend', el);
939
+ }
940
+ /** Track switch / source cleared — resets the lifecycle. */
941
+ OnMediaEmptied(el) {
942
+ if (el !== this.resolveMediaElement()) {
943
+ return;
944
+ }
945
+ this.applyMediaEvent('emptied', el);
946
+ }
947
+ OnMediaTimeUpdate(el) {
948
+ if (el !== this.resolveMediaElement()) {
949
+ return;
950
+ }
951
+ if (this._suppressTimeUpdate) {
952
+ // Ignore the transient end-seek the duration nudge produces.
953
+ return;
954
+ }
955
+ this._currentTimeMs = Math.round(el.currentTime * 1000);
956
+ this.TimeUpdate.emit(this._currentTimeMs);
957
+ this.refreshActiveCue();
958
+ this.cdr.markForCheck();
959
+ }
960
+ OnMediaDurationChange(el) {
961
+ if (el !== this.resolveMediaElement()) {
962
+ return;
963
+ }
964
+ if (isFinite(el.duration)) {
965
+ this._durationMs = Math.round(el.duration * 1000);
966
+ this.DurationChange.emit(this._durationMs);
967
+ this.cdr.markForCheck();
968
+ }
969
+ }
970
+ /**
971
+ * Forces a header-less media element (e.g. a MediaRecorder webm that reports `Infinity`
972
+ * duration) to resolve a real, finite duration by briefly seeking far past the end. The browser
973
+ * clamps to the true end, fires `durationchange` with the real value (picked up by
974
+ * {@link OnMediaDurationChange}) and builds its seek index — after which fraction-based seeking
975
+ * works. We then restore the prior position and suppress the transient time jump. Runs at most
976
+ * once per element.
977
+ */
978
+ forceDurationResolution(el) {
979
+ if (this._durationForced.has(el)) {
980
+ return;
981
+ }
982
+ this._durationForced.add(el);
983
+ const restoreSeconds = el.currentTime || 0;
984
+ this._suppressTimeUpdate = true;
985
+ const onResolved = () => {
986
+ if (!isFinite(el.duration)) {
987
+ return; // not the real value yet — wait for the next durationchange
988
+ }
989
+ el.removeEventListener('durationchange', onResolved);
990
+ this._suppressTimeUpdate = false;
991
+ try {
992
+ el.currentTime = restoreSeconds;
993
+ }
994
+ catch {
995
+ /* best effort — restoring position is non-critical */
996
+ }
997
+ };
998
+ el.addEventListener('durationchange', onResolved);
999
+ try {
1000
+ el.currentTime = 1e101;
1001
+ }
1002
+ catch {
1003
+ el.removeEventListener('durationchange', onResolved);
1004
+ this._suppressTimeUpdate = false;
1005
+ }
1006
+ }
1007
+ OnMediaPlay(el) {
1008
+ if (el !== this.resolveMediaElement()) {
1009
+ return;
1010
+ }
1011
+ this._isPlaying = true;
1012
+ this.applyMediaEvent('play', el);
1013
+ this.AfterPlay.emit();
1014
+ this.cdr.markForCheck();
1015
+ }
1016
+ OnMediaPause(el) {
1017
+ if (el !== this.resolveMediaElement()) {
1018
+ return;
1019
+ }
1020
+ this._isPlaying = false;
1021
+ this.applyMediaEvent('pause', el);
1022
+ this.AfterPause.emit();
1023
+ this.cdr.markForCheck();
1024
+ }
1025
+ OnMediaEnded(el) {
1026
+ if (el !== this.resolveMediaElement()) {
1027
+ return;
1028
+ }
1029
+ this._isPlaying = false;
1030
+ this.applyMediaEvent('ended', el);
1031
+ this.Ended.emit();
1032
+ this.cdr.markForCheck();
1033
+ }
1034
+ OnMediaError(el) {
1035
+ this._hasError = true;
1036
+ this.applyMediaEvent('error', el ?? this.resolveMediaElement());
1037
+ this.cdr.markForCheck();
1038
+ }
1039
+ // ---------------------------------------------------------------------------
1040
+ // Playback state machine (driven by the pure reducer + buffering debounce)
1041
+ // ---------------------------------------------------------------------------
1042
+ /**
1043
+ * Runs the pure {@link nextPlaybackState} reducer for a native media event, commits the
1044
+ * resulting state, manages the debounced buffering spinner, and emits {@link StateChanged}
1045
+ * on transitions. The single funnel for all media-event → state changes.
1046
+ */
1047
+ applyMediaEvent(event, el) {
1048
+ const ctx = {
1049
+ IsPlaying: this._isPlaying,
1050
+ ReadyState: el?.readyState ?? 0,
1051
+ Ended: el?.ended ?? false,
1052
+ };
1053
+ const next = nextPlaybackState(event, this._mediaState, ctx);
1054
+ this.setMediaState(next);
1055
+ }
1056
+ /** Commits a new state, syncs the buffering spinner, and emits on change. */
1057
+ setMediaState(next) {
1058
+ this.syncBufferingIndicator(next);
1059
+ if (next !== this._mediaState) {
1060
+ this._mediaState = next;
1061
+ this.StateChanged.emit(next);
1062
+ this.cdr.markForCheck();
1063
+ }
1064
+ }
1065
+ /**
1066
+ * Keeps the visible buffering spinner in sync with the target state. `loading` shows
1067
+ * immediately (no debounce); `buffering` is debounced so instant seeks don't flash it;
1068
+ * any non-busy state clears the spinner and cancels a pending show.
1069
+ */
1070
+ syncBufferingIndicator(next) {
1071
+ if (next === 'buffering') {
1072
+ if (this._bufferingVisible || this._bufferingTimer) {
1073
+ return; // already shown or scheduled
1074
+ }
1075
+ this._bufferingTimer = setTimeout(() => {
1076
+ this._bufferingTimer = null;
1077
+ if (this._mediaState === 'buffering') {
1078
+ this._bufferingVisible = true;
1079
+ this.cdr.markForCheck();
1080
+ }
1081
+ }, BUFFERING_INDICATOR_DELAY_MS);
1082
+ return;
1083
+ }
1084
+ // Any non-buffering target clears the pending/visible buffering spinner.
1085
+ this.clearBufferingTimer();
1086
+ if (this._bufferingVisible) {
1087
+ this._bufferingVisible = false;
1088
+ }
1089
+ }
1090
+ clearBufferingTimer() {
1091
+ if (this._bufferingTimer) {
1092
+ clearTimeout(this._bufferingTimer);
1093
+ this._bufferingTimer = null;
1094
+ }
1095
+ }
1096
+ // ---------------------------------------------------------------------------
1097
+ // Scrubber (click + drag) — pointer-based
1098
+ // ---------------------------------------------------------------------------
1099
+ OnScrubPointerDown(event, trackEl) {
1100
+ event.preventDefault();
1101
+ this._isScrubbing = true;
1102
+ event.target.setPointerCapture?.(event.pointerId);
1103
+ this.applyScrubFromPointer(event, trackEl);
1104
+ }
1105
+ OnScrubPointerMove(event, trackEl) {
1106
+ if (!this._isScrubbing) {
1107
+ return;
1108
+ }
1109
+ this.applyScrubFromPointer(event, trackEl);
1110
+ }
1111
+ OnScrubPointerUp(event) {
1112
+ if (!this._isScrubbing) {
1113
+ return;
1114
+ }
1115
+ this._isScrubbing = false;
1116
+ event.target.releasePointerCapture?.(event.pointerId);
1117
+ }
1118
+ // The waveform IS a scrubber: reuse the exact same pointer/keyboard seek logic.
1119
+ OnWaveformPointerDown(event, trackEl) {
1120
+ this.OnScrubPointerDown(event, trackEl);
1121
+ }
1122
+ OnWaveformPointerMove(event, trackEl) {
1123
+ this.OnScrubPointerMove(event, trackEl);
1124
+ }
1125
+ OnWaveformPointerUp(event) {
1126
+ this.OnScrubPointerUp(event);
1127
+ }
1128
+ /** Keyboard support for the scrubber (role="slider"). */
1129
+ OnScrubKeyDown(event) {
1130
+ let handled = true;
1131
+ switch (event.key) {
1132
+ case 'ArrowLeft':
1133
+ this.SeekToMs(this._currentTimeMs - 5000);
1134
+ break;
1135
+ case 'ArrowRight':
1136
+ this.SeekToMs(this._currentTimeMs + 5000);
1137
+ break;
1138
+ case 'Home':
1139
+ this.SeekToMs(0);
1140
+ break;
1141
+ case 'End':
1142
+ this.SeekToMs(this._durationMs);
1143
+ break;
1144
+ default:
1145
+ handled = false;
1146
+ }
1147
+ if (handled) {
1148
+ event.preventDefault();
1149
+ }
1150
+ }
1151
+ // ---------------------------------------------------------------------------
1152
+ // Transcript cue interaction
1153
+ // ---------------------------------------------------------------------------
1154
+ OnCueClicked(index) {
1155
+ const cues = this._transcript;
1156
+ if (!cues || index < 0 || index >= cues.length) {
1157
+ return;
1158
+ }
1159
+ this.CueClicked.emit({ Cue: cues[index], Index: index });
1160
+ this.SeekToCue(index);
1161
+ }
1162
+ OnCueKeyDown(event, index) {
1163
+ if (event.key === 'Enter' || event.key === ' ') {
1164
+ event.preventDefault();
1165
+ this.OnCueClicked(index);
1166
+ }
1167
+ }
1168
+ // ---------------------------------------------------------------------------
1169
+ // Rate menu
1170
+ // ---------------------------------------------------------------------------
1171
+ ToggleRateMenu() {
1172
+ this._rateMenuOpen = !this._rateMenuOpen;
1173
+ this.cdr.markForCheck();
1174
+ }
1175
+ // ---------------------------------------------------------------------------
1176
+ // Volume slider input
1177
+ // ---------------------------------------------------------------------------
1178
+ OnVolumeInput(event) {
1179
+ const value = parseFloat(event.target.value);
1180
+ this.SetVolume(value);
1181
+ }
1182
+ // ---------------------------------------------------------------------------
1183
+ // Keyboard shortcuts (host-level, when the player has focus)
1184
+ // ---------------------------------------------------------------------------
1185
+ OnHostKeyDown(event) {
1186
+ // ignore typing in form controls (e.g. the volume range / scrubber handle has its own handler)
1187
+ const target = event.target;
1188
+ if (target?.tagName === 'INPUT' || target?.getAttribute('role') === 'slider') {
1189
+ return;
1190
+ }
1191
+ switch (event.key) {
1192
+ case ' ':
1193
+ event.preventDefault();
1194
+ this.TogglePlay();
1195
+ break;
1196
+ case 'ArrowLeft':
1197
+ event.preventDefault();
1198
+ this.SeekToMs(this._currentTimeMs - 5000);
1199
+ break;
1200
+ case 'ArrowRight':
1201
+ event.preventDefault();
1202
+ this.SeekToMs(this._currentTimeMs + 5000);
1203
+ break;
1204
+ case 'j':
1205
+ case 'J':
1206
+ event.preventDefault();
1207
+ this.SeekToMs(this._currentTimeMs - 10000);
1208
+ break;
1209
+ case 'l':
1210
+ case 'L':
1211
+ event.preventDefault();
1212
+ this.SeekToMs(this._currentTimeMs + 10000);
1213
+ break;
1214
+ case 'ArrowUp':
1215
+ event.preventDefault();
1216
+ this.SetVolume(this._volume + 0.05);
1217
+ break;
1218
+ case 'ArrowDown':
1219
+ event.preventDefault();
1220
+ this.SetVolume(this._volume - 0.05);
1221
+ break;
1222
+ case 'm':
1223
+ case 'M':
1224
+ event.preventDefault();
1225
+ this.ToggleMute();
1226
+ break;
1227
+ case 'f':
1228
+ case 'F':
1229
+ if (this.ShowFullscreen && this.VideoTracks.length > 0) {
1230
+ event.preventDefault();
1231
+ this.ToggleFullscreen();
1232
+ }
1233
+ break;
1234
+ default:
1235
+ break;
1236
+ }
1237
+ }
1238
+ OnFullscreenChange() {
1239
+ this._isFullscreen = !!document.fullscreenElement;
1240
+ this.cdr.markForCheck();
1241
+ }
1242
+ // ---------------------------------------------------------------------------
1243
+ // Formatting helpers (template)
1244
+ // ---------------------------------------------------------------------------
1245
+ /** Formats a millisecond value as `mm:ss` (or `h:mm:ss` for long media). */
1246
+ FormatTime(ms) {
1247
+ if (!isFinite(ms) || ms < 0) {
1248
+ ms = 0;
1249
+ }
1250
+ const totalSeconds = Math.floor(ms / 1000);
1251
+ const hours = Math.floor(totalSeconds / 3600);
1252
+ const minutes = Math.floor((totalSeconds % 3600) / 60);
1253
+ const seconds = totalSeconds % 60;
1254
+ const mm = hours > 0 ? String(minutes).padStart(2, '0') : String(minutes);
1255
+ const ss = String(seconds).padStart(2, '0');
1256
+ return hours > 0 ? `${hours}:${mm}:${ss}` : `${mm}:${ss}`;
1257
+ }
1258
+ /** Stable color for a speaker label (deterministic hue from the label string). */
1259
+ SpeakerColor(label) {
1260
+ if (!label) {
1261
+ return 'var(--mj-text-secondary)';
1262
+ }
1263
+ let hash = 0;
1264
+ for (let i = 0; i < label.length; i++) {
1265
+ hash = (hash * 31 + label.charCodeAt(i)) & 0xffffffff;
1266
+ }
1267
+ const hue = Math.abs(hash) % 360;
1268
+ return `hsl(${hue}, 55%, 45%)`;
1269
+ }
1270
+ TrackById(_index, track) {
1271
+ return track.Id;
1272
+ }
1273
+ CueTrackById(_index, cue) {
1274
+ return cue.Id;
1275
+ }
1276
+ // ---------------------------------------------------------------------------
1277
+ // Internal helpers
1278
+ // ---------------------------------------------------------------------------
1279
+ /**
1280
+ * The element that owns the master timeline. In a single audio track or a single
1281
+ * video, that's the primary element. For one-video-plus-audio, the video carries
1282
+ * playback (audio track is muted/secondary). For multi-video, the first video leads.
1283
+ */
1284
+ resolveMediaElement() {
1285
+ if (this._activeMediaEl) {
1286
+ return this._activeMediaEl;
1287
+ }
1288
+ return this.primaryMedia?.nativeElement ?? null;
1289
+ }
1290
+ initializeMediaElement(el) {
1291
+ if (!this._rateInitialized) {
1292
+ this._playbackRate = this.InitialRate;
1293
+ this._volume = this.InitialVolume;
1294
+ this._rateInitialized = true;
1295
+ }
1296
+ el.playbackRate = this._playbackRate;
1297
+ el.volume = this._volume;
1298
+ el.muted = this._muted;
1299
+ }
1300
+ applyToAllMedia(fn) {
1301
+ const els = this.host.nativeElement.querySelectorAll('audio, video');
1302
+ els.forEach((el) => fn(el));
1303
+ }
1304
+ applyScrubFromPointer(event, trackEl) {
1305
+ const rect = trackEl.getBoundingClientRect();
1306
+ if (rect.width <= 0) {
1307
+ return;
1308
+ }
1309
+ const fraction = Math.min(1, Math.max(0, (event.clientX - rect.left) / rect.width));
1310
+ this.SeekToMs(fraction * this._durationMs);
1311
+ }
1312
+ refreshActiveCue() {
1313
+ const newIndex = computeActiveCueIndex(this._currentTimeMs, this._transcript);
1314
+ if (newIndex !== this._activeCueIndex) {
1315
+ this._activeCueIndex = newIndex;
1316
+ if (newIndex >= 0 && this._transcript) {
1317
+ this.CueActivated.emit({ Cue: this._transcript[newIndex], Index: newIndex });
1318
+ }
1319
+ }
1320
+ }
1321
+ clampMs(ms) {
1322
+ if (ms < 0) {
1323
+ return 0;
1324
+ }
1325
+ if (this._durationMs > 0 && ms > this._durationMs) {
1326
+ return this._durationMs;
1327
+ }
1328
+ return ms;
1329
+ }
1330
+ resetPlaybackState() {
1331
+ this._currentTimeMs = 0;
1332
+ this._durationMs = 0;
1333
+ this._isPlaying = false;
1334
+ this._activeCueIndex = -1;
1335
+ this._hasError = false;
1336
+ this._rateInitialized = false;
1337
+ this.clearBufferingTimer();
1338
+ this._bufferingVisible = false;
1339
+ this.setMediaState(this._tracks.length > 0 ? 'loading' : 'idle');
1340
+ }
1341
+ // ---------------------------------------------------------------------------
1342
+ // Waveform peak extraction (client-side, best-effort, cached per track Id)
1343
+ // ---------------------------------------------------------------------------
1344
+ /**
1345
+ * Resolves peaks for a track: supplied {@link MediaTrack.Peaks} win (rendered as-is,
1346
+ * no decode); otherwise returns any cached/decoded peaks, kicking off a best-effort
1347
+ * client-side decode on first miss. Returns `null` while decoding or after a failure.
1348
+ */
1349
+ peaksForTrack(track) {
1350
+ if (track.Peaks && track.Peaks.length > 0) {
1351
+ return track.Peaks;
1352
+ }
1353
+ const cached = this._peaksByTrackId.get(track.Id);
1354
+ if (cached) {
1355
+ return cached;
1356
+ }
1357
+ if (this._waveformFailedTrackIds.has(track.Id)) {
1358
+ return null;
1359
+ }
1360
+ void this.extractPeaks(track);
1361
+ return null;
1362
+ }
1363
+ /** Kicks off client-side extraction for every audio track that still needs it. */
1364
+ ensureWaveforms() {
1365
+ if (!this.ShowWaveform) {
1366
+ return;
1367
+ }
1368
+ const track = this.IsAudioOnly ? this.AudioTracks[0] : null;
1369
+ if (track) {
1370
+ // Reading via peaksForTrack triggers a decode when needed (and is cache-safe).
1371
+ this.peaksForTrack(track);
1372
+ }
1373
+ }
1374
+ /**
1375
+ * Best-effort: fetch → arrayBuffer → decodeAudioData → downsample channel 0 → cache.
1376
+ * Never throws into the view; on any failure marks the track as failed so the template
1377
+ * falls back to a plain progress bar (not the old synthetic bars).
1378
+ */
1379
+ async extractPeaks(track) {
1380
+ if (this._waveformPendingTrackIds.has(track.Id)) {
1381
+ return;
1382
+ }
1383
+ this._waveformPendingTrackIds.add(track.Id);
1384
+ try {
1385
+ const ctx = this.resolveAudioContext();
1386
+ if (!ctx) {
1387
+ this.markWaveformFailed(track.Id);
1388
+ return;
1389
+ }
1390
+ const response = await fetch(track.Url);
1391
+ if (!response.ok) {
1392
+ this.markWaveformFailed(track.Id);
1393
+ return;
1394
+ }
1395
+ const arrayBuffer = await response.arrayBuffer();
1396
+ const audioBuffer = await ctx.decodeAudioData(arrayBuffer.slice(0));
1397
+ const channel = audioBuffer.getChannelData(0);
1398
+ const peaks = downsamplePeaks(channel, this.WaveformBarCount, 'max-abs');
1399
+ this._peaksByTrackId.set(track.Id, peaks);
1400
+ this.cdr.markForCheck();
1401
+ }
1402
+ catch {
1403
+ // CORS, unsupported codec, decode error → graceful fallback (no throw).
1404
+ this.markWaveformFailed(track.Id);
1405
+ }
1406
+ finally {
1407
+ this._waveformPendingTrackIds.delete(track.Id);
1408
+ }
1409
+ }
1410
+ markWaveformFailed(trackId) {
1411
+ this._waveformFailedTrackIds.add(trackId);
1412
+ this.cdr.markForCheck();
1413
+ }
1414
+ /** Lazily creates a single AudioContext used only for offline peak extraction. */
1415
+ resolveAudioContext() {
1416
+ if (this._audioCtx) {
1417
+ return this._audioCtx;
1418
+ }
1419
+ const Ctor = typeof window !== 'undefined'
1420
+ ? window.AudioContext ?? window.webkitAudioContext
1421
+ : undefined;
1422
+ if (!Ctor) {
1423
+ return null;
1424
+ }
1425
+ try {
1426
+ this._audioCtx = new Ctor();
1427
+ return this._audioCtx;
1428
+ }
1429
+ catch {
1430
+ return null;
1431
+ }
1432
+ }
1433
+ detectReducedMotion() {
1434
+ try {
1435
+ return typeof window !== 'undefined' && !!window.matchMedia?.('(prefers-reduced-motion: reduce)').matches;
1436
+ }
1437
+ catch {
1438
+ return false;
1439
+ }
1440
+ }
1441
+ ngOnDestroy() {
1442
+ this.clearBufferingTimer();
1443
+ // object URLs are owned by the wrapper that created them; close our decode context.
1444
+ if (this._audioCtx) {
1445
+ void this._audioCtx.close().catch(() => undefined);
1446
+ this._audioCtx = null;
1447
+ }
1448
+ }
1449
+ static ɵfac = function MJMediaPlayerComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || MJMediaPlayerComponent)(); };
1450
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MJMediaPlayerComponent, selectors: [["mj-media-player"]], viewQuery: function MJMediaPlayerComponent_Query(rf, ctx) { if (rf & 1) {
1451
+ i0.ɵɵviewQuery(_c0, 5);
1452
+ } if (rf & 2) {
1453
+ let _t;
1454
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.primaryMedia = _t.first);
1455
+ } }, hostBindings: function MJMediaPlayerComponent_HostBindings(rf, ctx) { if (rf & 1) {
1456
+ i0.ɵɵlistener("keydown", function MJMediaPlayerComponent_keydown_HostBindingHandler($event) { return ctx.OnHostKeyDown($event); })("fullscreenchange", function MJMediaPlayerComponent_fullscreenchange_HostBindingHandler() { return ctx.OnFullscreenChange(); }, i0.ɵɵresolveDocument);
1457
+ } }, inputs: { Tracks: "Tracks", Transcript: "Transcript", Autoplay: "Autoplay", StartAtMs: "StartAtMs", ShowTranscript: "ShowTranscript", ShowTranscriptToggle: "ShowTranscriptToggle", ShowSpeedControl: "ShowSpeedControl", ShowSkipControls: "ShowSkipControls", ShowVolume: "ShowVolume", ShowWaveform: "ShowWaveform", WaveformBarCount: "WaveformBarCount", ShowFullscreen: "ShowFullscreen", SkipSeconds: "SkipSeconds", PlaybackRates: "PlaybackRates", InitialRate: "InitialRate", InitialVolume: "InitialVolume", TranscriptPosition: "TranscriptPosition" }, outputs: { BeforePlay: "BeforePlay", AfterPlay: "AfterPlay", BeforePause: "BeforePause", AfterPause: "AfterPause", BeforeSeek: "BeforeSeek", AfterSeek: "AfterSeek", BeforeRateChange: "BeforeRateChange", AfterRateChange: "AfterRateChange", CueActivated: "CueActivated", CueClicked: "CueClicked", TimeUpdate: "TimeUpdate", DurationChange: "DurationChange", Ended: "Ended", StateChanged: "StateChanged", TranscriptVisibilityChanged: "TranscriptVisibilityChanged" }, decls: 3, vars: 9, consts: [["scrubTrack", ""], ["primaryMedia", ""], ["waveTrack", ""], ["tabindex", "0", "role", "group", "aria-label", "Media player", 1, "mj-media-player"], ["role", "status", 1, "mj-media-empty"], ["aria-hidden", "true", 1, "fa-solid", "fa-circle-play", "mj-media-empty__icon"], [1, "mj-media-empty__text"], [1, "mj-media-main"], [1, "mj-media-stage"], ["role", "alert", 1, "mj-media-error"], ["role", "status", "aria-hidden", "true", 1, "mj-media-busy", 3, "mj-media-busy--static"], [1, "mj-media-video-grid", 3, "mj-media-video-grid--multi"], [1, "mj-media-transport"], ["role", "status", "aria-live", "polite", 1, "mj-media-status"], ["aria-hidden", "true", 1, "mj-media-status__dot"], [1, "mj-media-status__text"], [1, "mj-media-scrubber-row"], [1, "mj-media-time"], ["role", "slider", "tabindex", "0", "aria-label", "Seek", 1, "mj-media-scrubber", 3, "pointerdown", "pointermove", "pointerup", "pointercancel", "keydown"], [1, "mj-media-scrubber__rail"], [1, "mj-media-scrubber__fill"], [1, "mj-media-scrubber__handle"], [1, "mj-media-time", "mj-media-time--total"], [1, "mj-media-controls"], [1, "mj-media-controls__left"], ["type", "button", 1, "mj-media-btn", 3, "title"], ["type", "button", 1, "mj-media-btn", "mj-media-btn--primary", 3, "click", "title"], ["aria-hidden", "true", 1, "fa-solid"], [1, "mj-media-controls__right"], [1, "mj-media-volume"], [1, "mj-media-rate"], ["type", "button", 1, "mj-media-btn", 3, "mj-media-btn--active", "title"], ["role", "list", "aria-label", "Transcript", 1, "mj-media-transcript"], ["aria-hidden", "true", 1, "fa-solid", "fa-triangle-exclamation"], ["role", "status", "aria-hidden", "true", 1, "mj-media-busy"], [1, "mj-media-busy__spinner"], [1, "mj-media-busy__label"], ["role", "slider", "tabindex", "0", "aria-label", "Seek", 1, "mj-media-waveform", 3, "mj-media-waveform--static"], ["role", "slider", "tabindex", "0", "aria-label", "Seek", 1, "mj-media-waveform-fallback"], ["aria-hidden", "true", 1, "mj-media-audio-art"], ["preload", "metadata", 1, "mj-media-audio", 3, "src"], ["preload", "metadata", 1, "mj-media-audio", 3, "loadstart", "loadedmetadata", "canplay", "canplaythrough", "waiting", "seeking", "seeked", "playing", "stalled", "suspend", "emptied", "timeupdate", "durationchange", "play", "pause", "ended", "error", "src"], ["role", "slider", "tabindex", "0", "aria-label", "Seek", 1, "mj-media-waveform", 3, "pointerdown", "pointermove", "pointerup", "pointercancel", "keydown"], [1, "mj-media-waveform__bar", 3, "mj-media-waveform__bar--played", "height"], [1, "mj-media-waveform__progress"], [1, "mj-media-waveform__bar"], ["role", "slider", "tabindex", "0", "aria-label", "Seek", 1, "mj-media-waveform-fallback", 3, "pointerdown", "pointermove", "pointerup", "pointercancel", "keydown"], [1, "mj-media-waveform-fallback__rail"], [1, "mj-media-waveform-fallback__fill"], [1, "fa-solid", "fa-music"], [1, "mj-media-video-grid"], [1, "mj-media-video-cell"], [1, "mj-media-video-cell__label"], ["playsinline", "", "preload", "metadata", 1, "mj-media-video", 3, "src", "poster"], ["playsinline", "", "preload", "metadata", "muted", "", 1, "mj-media-video", 3, "src", "poster"], ["playsinline", "", "preload", "metadata", 1, "mj-media-video", 3, "loadstart", "loadedmetadata", "canplay", "canplaythrough", "waiting", "seeking", "seeked", "playing", "stalled", "suspend", "emptied", "timeupdate", "durationchange", "play", "pause", "ended", "error", "src", "poster"], ["type", "button", 1, "mj-media-btn", 3, "click", "title"], ["aria-hidden", "true", 1, "fa-solid", "fa-rotate-left"], [1, "mj-media-btn__skip-label"], ["aria-hidden", "true", 1, "fa-solid", "fa-rotate-right"], ["type", "range", "min", "0", "max", "1", "step", "0.05", "aria-label", "Volume", 1, "mj-media-volume__slider", 3, "input", "value"], ["type", "button", "aria-haspopup", "true", "aria-label", "Playback speed", "title", "Playback speed", 1, "mj-media-btn", "mj-media-rate__toggle", 3, "click"], ["role", "menu", 1, "mj-media-rate__menu"], ["role", "none"], ["type", "button", "role", "menuitemradio", 1, "mj-media-rate__item", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-closed-captioning"], ["type", "button", "role", "listitem", 1, "mj-media-cue", 3, "mj-media-cue--active"], ["type", "button", "role", "listitem", 1, "mj-media-cue", 3, "click", "keydown"], [1, "mj-media-cue__time"], [1, "mj-media-cue__body"], [1, "mj-media-cue__speaker", 3, "color"], [1, "mj-media-cue__text"], [1, "mj-media-cue__speaker"]], template: function MJMediaPlayerComponent_Template(rf, ctx) { if (rf & 1) {
1458
+ i0.ɵɵdomElementStart(0, "div", 3);
1459
+ i0.ɵɵconditionalCreate(1, MJMediaPlayerComponent_Conditional_1_Template, 4, 0, "div", 4)(2, MJMediaPlayerComponent_Conditional_2_Template, 33, 30);
1460
+ i0.ɵɵdomElementEnd();
1461
+ } if (rf & 2) {
1462
+ i0.ɵɵclassProp("mj-media-player--bottom-transcript", ctx.TranscriptPosition === "bottom")("mj-media-player--has-transcript", ctx.ShowTranscriptPanel)("mj-media-player--audio", ctx.IsAudioOnly)("mj-media-player--video", ctx.VideoTracks.length > 0);
1463
+ i0.ɵɵadvance();
1464
+ i0.ɵɵconditional(!ctx.HasTracks ? 1 : 2);
1465
+ } }, dependencies: [CommonModule], styles: ["[_nghost-%COMP%] {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n min-height: 0;\n color: var(--mj-text-primary);\n}\n\n.mj-media-player[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n flex: 1 1 auto;\n min-height: 0;\n gap: 0;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md, 8px);\n overflow: hidden;\n outline: none;\n}\n\n.mj-media-player[_ngcontent-%COMP%]:focus-visible {\n box-shadow: 0 0 0 2px var(--mj-border-focus);\n}\n\n\n\n.mj-media-player--has-transcript[_ngcontent-%COMP%]:not(.mj-media-player--bottom-transcript) {\n flex-direction: row;\n align-items: stretch;\n}\n\n.mj-media-main[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n flex: 1 1 auto;\n min-width: 0;\n min-height: 0;\n}\n\n\n\n\n\n\n.mj-media-player--audio.mj-media-player--bottom-transcript[_ngcontent-%COMP%] .mj-media-main[_ngcontent-%COMP%] {\n flex: 0 0 auto;\n}\n\n\n\n\n.mj-media-empty[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n padding: 2.5rem 1rem;\n color: var(--mj-text-muted);\n}\n\n.mj-media-empty__icon[_ngcontent-%COMP%] {\n font-size: 2rem;\n}\n\n.mj-media-error[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.75rem 1rem;\n color: var(--mj-status-error-text);\n background: var(--mj-status-error-bg);\n}\n\n\n\n\n.mj-media-stage[_ngcontent-%COMP%] {\n position: relative;\n background: var(--mj-bg-surface-sunken);\n min-height: 80px;\n}\n\n\n\n.mj-media-player--video[_ngcontent-%COMP%] .mj-media-stage[_ngcontent-%COMP%] {\n flex: 1 1 auto;\n min-height: 0;\n display: flex;\n}\n\n.mj-media-audio[_ngcontent-%COMP%] {\n display: none;\n}\n\n.mj-media-audio-art[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 2rem;\n font-size: 2.5rem;\n color: var(--mj-text-disabled);\n}\n\n\n\n.mj-media-waveform[_ngcontent-%COMP%] {\n position: relative;\n display: flex;\n align-items: center;\n gap: 1px;\n width: 100%;\n box-sizing: border-box;\n height: 96px;\n padding: 0.75rem 1rem;\n overflow: hidden;\n cursor: pointer;\n touch-action: none;\n outline: none;\n}\n\n.mj-media-waveform[_ngcontent-%COMP%]:focus-visible {\n box-shadow: inset 0 0 0 2px var(--mj-border-focus);\n border-radius: var(--mj-radius-sm, 4px);\n}\n\n.mj-media-waveform__bar[_ngcontent-%COMP%] {\n \n\n\n\n\n\n\n flex: 1 1 0;\n min-width: 0;\n min-height: 2px;\n align-self: center;\n background: var(--mj-border-strong);\n border-radius: 1px;\n pointer-events: none;\n transform-origin: center;\n transition: background-color 0.1s ease, transform 0.18s ease;\n}\n\n.mj-media-waveform__bar--played[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary);\n}\n\n\n\n.mj-media-waveform__progress[_ngcontent-%COMP%] {\n position: absolute;\n top: 0.5rem;\n bottom: 0.5rem;\n width: 2px;\n margin-left: -1px;\n background: var(--mj-brand-primary);\n opacity: 0.85;\n pointer-events: none;\n}\n\n\n\n.mj-media-waveform-fallback[_ngcontent-%COMP%] {\n position: relative;\n display: flex;\n align-items: center;\n height: 96px;\n padding: 0 1rem;\n cursor: pointer;\n touch-action: none;\n outline: none;\n}\n\n.mj-media-waveform-fallback[_ngcontent-%COMP%]:focus-visible {\n box-shadow: inset 0 0 0 2px var(--mj-border-focus);\n border-radius: var(--mj-radius-sm, 4px);\n}\n\n.mj-media-waveform-fallback__rail[_ngcontent-%COMP%] {\n position: relative;\n flex: 1 1 auto;\n height: 5px;\n background: var(--mj-border-strong);\n border-radius: var(--mj-radius-full, 999px);\n overflow: hidden;\n}\n\n.mj-media-waveform-fallback__fill[_ngcontent-%COMP%] {\n position: absolute;\n inset: 0 auto 0 0;\n background: var(--mj-brand-primary);\n}\n\n\n\n.mj-media-video-grid[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: 1fr;\n gap: 4px;\n flex: 1 1 auto;\n min-height: 0;\n width: 100%;\n background: var(--mj-bg-surface-sunken);\n}\n\n.mj-media-video-grid--multi[_ngcontent-%COMP%] {\n grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));\n}\n\n.mj-media-video-cell[_ngcontent-%COMP%] {\n position: relative;\n display: flex;\n min-height: 0;\n}\n\n.mj-media-video-cell__label[_ngcontent-%COMP%] {\n position: absolute;\n top: 0.5rem;\n left: 0.5rem;\n z-index: 1;\n padding: 0.125rem 0.5rem;\n font-size: 0.75rem;\n border-radius: var(--mj-radius-sm, 4px);\n color: var(--mj-text-inverse);\n background: rgba(0, 0, 0, 0.55);\n}\n\n.mj-media-video[_ngcontent-%COMP%] {\n width: 100%;\n height: auto;\n display: block;\n background: var(--mj-bg-surface-sunken);\n}\n\n\n\n\n\n\n.mj-media-video-grid[_ngcontent-%COMP%]:not(.mj-media-video-grid--multi) .mj-media-video[_ngcontent-%COMP%] {\n height: 100%;\n min-height: 0;\n object-fit: contain;\n}\n\n\n\n\n.mj-media-busy[_ngcontent-%COMP%] {\n position: absolute;\n inset: 0;\n z-index: 2;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 0.625rem;\n pointer-events: none;\n color: var(--mj-text-inverse);\n background: color-mix(in srgb, var(--mj-bg-overlay) 55%, transparent);\n}\n\n.mj-media-busy__spinner[_ngcontent-%COMP%] {\n width: 36px;\n height: 36px;\n border-radius: 50%;\n border: 3px solid color-mix(in srgb, var(--mj-text-inverse) 35%, transparent);\n border-top-color: var(--mj-text-inverse);\n animation: _ngcontent-%COMP%_mj-media-spin 0.8s linear infinite;\n}\n\n.mj-media-busy__label[_ngcontent-%COMP%] {\n font-size: 0.8125rem;\n font-weight: 600;\n letter-spacing: 0.01em;\n}\n\n@keyframes _ngcontent-%COMP%_mj-media-spin {\n to {\n transform: rotate(360deg);\n }\n}\n\n\n\n.mj-media-busy--static[_ngcontent-%COMP%] .mj-media-busy__spinner[_ngcontent-%COMP%] {\n animation: _ngcontent-%COMP%_mj-media-busy-pulse 1.4s ease-in-out infinite;\n border-top-color: color-mix(in srgb, var(--mj-text-inverse) 35%, transparent);\n}\n\n@keyframes _ngcontent-%COMP%_mj-media-busy-pulse {\n 0%,\n 100% {\n opacity: 0.45;\n }\n 50% {\n opacity: 1;\n }\n}\n\n\n\n\n.mj-media-status[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 0.375rem;\n align-self: flex-start;\n min-height: 1rem;\n font-size: 0.6875rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--mj-text-muted);\n}\n\n.mj-media-status--busy[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n}\n\n.mj-media-status__dot[_ngcontent-%COMP%] {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: var(--mj-brand-primary);\n animation: _ngcontent-%COMP%_mj-media-busy-pulse 1.2s ease-in-out infinite;\n}\n\n\n\n\n.mj-media-transport[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n padding: 0.625rem 0.75rem 0.75rem;\n background: var(--mj-bg-surface-card);\n border-top: 1px solid var(--mj-border-subtle);\n}\n\n.mj-media-scrubber-row[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.625rem;\n}\n\n.mj-media-time[_ngcontent-%COMP%] {\n font-variant-numeric: tabular-nums;\n font-size: 0.8125rem;\n color: var(--mj-text-secondary);\n min-width: 3.25rem;\n}\n\n.mj-media-time--total[_ngcontent-%COMP%] {\n text-align: right;\n}\n\n.mj-media-scrubber[_ngcontent-%COMP%] {\n position: relative;\n flex: 1 1 auto;\n height: 18px;\n display: flex;\n align-items: center;\n cursor: pointer;\n touch-action: none;\n outline: none;\n}\n\n.mj-media-scrubber[_ngcontent-%COMP%]:focus-visible {\n box-shadow: 0 0 0 2px var(--mj-border-focus);\n border-radius: var(--mj-radius-full, 999px);\n}\n\n.mj-media-scrubber__rail[_ngcontent-%COMP%] {\n position: relative;\n flex: 1 1 auto;\n height: 5px;\n background: var(--mj-border-strong);\n border-radius: var(--mj-radius-full, 999px);\n overflow: hidden;\n}\n\n.mj-media-scrubber__fill[_ngcontent-%COMP%] {\n position: absolute;\n inset: 0 auto 0 0;\n background: var(--mj-brand-primary);\n}\n\n.mj-media-scrubber__handle[_ngcontent-%COMP%] {\n position: absolute;\n top: 50%;\n width: 13px;\n height: 13px;\n margin-left: -6.5px;\n transform: translateY(-50%);\n background: var(--mj-brand-primary);\n border: 2px solid var(--mj-bg-surface);\n border-radius: 50%;\n pointer-events: none;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);\n}\n\n.mj-media-controls[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 0.75rem;\n}\n\n.mj-media-controls__left[_ngcontent-%COMP%], \n.mj-media-controls__right[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.375rem;\n}\n\n\n\n.mj-media-btn[_ngcontent-%COMP%] {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 0.25rem;\n min-width: 32px;\n height: 32px;\n padding: 0 0.5rem;\n font-size: 0.875rem;\n color: var(--mj-text-primary);\n background: transparent;\n border: 1px solid transparent;\n border-radius: var(--mj-radius-sm, 4px);\n cursor: pointer;\n transition: background-color 0.12s ease, color 0.12s ease;\n}\n\n.mj-media-btn[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.mj-media-btn[_ngcontent-%COMP%]:active {\n background: var(--mj-bg-surface-active);\n}\n\n.mj-media-btn[_ngcontent-%COMP%]:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--mj-border-focus);\n}\n\n.mj-media-btn--primary[_ngcontent-%COMP%] {\n color: var(--mj-text-inverse);\n background: var(--mj-brand-primary);\n min-width: 40px;\n height: 40px;\n border-radius: 50%;\n}\n\n.mj-media-btn--primary[_ngcontent-%COMP%]:hover {\n background: var(--mj-brand-primary-hover);\n}\n\n.mj-media-btn--primary[_ngcontent-%COMP%]:active {\n background: var(--mj-brand-primary-active);\n}\n\n\n\n.mj-media-btn--active[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, transparent);\n}\n\n.mj-media-btn--active[_ngcontent-%COMP%]:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 18%, transparent);\n}\n\n.mj-media-btn__skip-label[_ngcontent-%COMP%] {\n font-size: 0.625rem;\n font-variant-numeric: tabular-nums;\n}\n\n\n\n.mj-media-volume[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.25rem;\n}\n\n.mj-media-volume__slider[_ngcontent-%COMP%] {\n width: 80px;\n accent-color: var(--mj-brand-primary);\n cursor: pointer;\n}\n\n.mj-media-volume__slider[_ngcontent-%COMP%]:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--mj-border-focus);\n border-radius: var(--mj-radius-full, 999px);\n}\n\n\n\n.mj-media-rate[_ngcontent-%COMP%] {\n position: relative;\n}\n\n.mj-media-rate__toggle[_ngcontent-%COMP%] {\n font-variant-numeric: tabular-nums;\n font-weight: 600;\n}\n\n.mj-media-rate__menu[_ngcontent-%COMP%] {\n position: absolute;\n bottom: calc(100% + 4px);\n right: 0;\n z-index: 10;\n margin: 0;\n padding: 0.25rem;\n list-style: none;\n min-width: 4rem;\n background: var(--mj-bg-surface-elevated);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md, 8px);\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.18);\n}\n\n.mj-media-rate__item[_ngcontent-%COMP%] {\n display: block;\n width: 100%;\n text-align: left;\n padding: 0.375rem 0.625rem;\n font-size: 0.8125rem;\n color: var(--mj-text-primary);\n background: transparent;\n border: none;\n border-radius: var(--mj-radius-sm, 4px);\n cursor: pointer;\n}\n\n.mj-media-rate__item[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.mj-media-rate__item[_ngcontent-%COMP%]:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--mj-border-focus);\n}\n\n.mj-media-rate__item--active[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n font-weight: 600;\n}\n\n\n\n\n.mj-media-transcript[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 1px;\n overflow-y: auto;\n background: var(--mj-bg-surface);\n border-left: 1px solid var(--mj-border-subtle);\n max-height: 480px;\n width: 340px;\n flex: 0 0 auto;\n padding: 0.5rem;\n}\n\n\n\n\n\n\n\n.mj-media-player--bottom-transcript[_ngcontent-%COMP%] .mj-media-transcript[_ngcontent-%COMP%] {\n width: auto;\n border-left: none;\n border-top: 1px solid var(--mj-border-subtle);\n flex: 1 1 auto;\n min-height: 0;\n max-height: none;\n}\n\n.mj-media-cue[_ngcontent-%COMP%] {\n display: flex;\n gap: 0.625rem;\n align-items: flex-start;\n width: 100%;\n text-align: left;\n padding: 0.5rem 0.625rem;\n background: transparent;\n border: none;\n border-left: 3px solid transparent;\n border-radius: var(--mj-radius-sm, 4px);\n cursor: pointer;\n color: var(--mj-text-primary);\n font: inherit;\n transition: background-color 0.12s ease;\n}\n\n.mj-media-cue[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.mj-media-cue[_ngcontent-%COMP%]:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--mj-border-focus);\n}\n\n.mj-media-cue--active[_ngcontent-%COMP%] {\n border-left-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n}\n\n.mj-media-cue__time[_ngcontent-%COMP%] {\n flex: 0 0 auto;\n font-variant-numeric: tabular-nums;\n font-size: 0.75rem;\n color: var(--mj-text-muted);\n padding-top: 0.125rem;\n}\n\n.mj-media-cue__body[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 0.125rem;\n min-width: 0;\n}\n\n.mj-media-cue__speaker[_ngcontent-%COMP%] {\n font-size: 0.6875rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.02em;\n}\n\n.mj-media-cue__text[_ngcontent-%COMP%] {\n font-size: 0.875rem;\n line-height: 1.4;\n}\n\n\n\n\n\n\n.mj-media-waveform[_ngcontent-%COMP%]:not(.mj-media-waveform--static) .mj-media-waveform__bar[_ngcontent-%COMP%] {\n animation: _ngcontent-%COMP%_mj-media-bar-in 0.25s ease both;\n}\n\n@keyframes _ngcontent-%COMP%_mj-media-bar-in {\n from {\n transform: scaleY(0.15);\n opacity: 0.4;\n }\n to {\n transform: scaleY(1);\n opacity: 1;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .mj-media-btn[_ngcontent-%COMP%], \n .mj-media-cue[_ngcontent-%COMP%], \n .mj-media-waveform__bar[_ngcontent-%COMP%], \n .mj-media-scrubber__fill[_ngcontent-%COMP%] {\n transition: none;\n animation: none;\n }\n\n \n\n .mj-media-busy__spinner[_ngcontent-%COMP%] {\n animation: _ngcontent-%COMP%_mj-media-busy-pulse 1.4s ease-in-out infinite;\n border-top-color: color-mix(in srgb, var(--mj-text-inverse) 35%, transparent);\n }\n\n .mj-media-status__dot[_ngcontent-%COMP%] {\n animation: none;\n }\n}"], changeDetection: 0 });
1466
+ }
1467
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MJMediaPlayerComponent, [{
1468
+ type: Component,
1469
+ args: [{ selector: 'mj-media-player', standalone: true, imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n class=\"mj-media-player\"\n [class.mj-media-player--bottom-transcript]=\"TranscriptPosition === 'bottom'\"\n [class.mj-media-player--has-transcript]=\"ShowTranscriptPanel\"\n [class.mj-media-player--audio]=\"IsAudioOnly\"\n [class.mj-media-player--video]=\"VideoTracks.length > 0\"\n tabindex=\"0\"\n role=\"group\"\n aria-label=\"Media player\"\n>\n @if (!HasTracks) {\n <!-- Empty / no-track state -->\n <div class=\"mj-media-empty\" role=\"status\">\n <i class=\"fa-solid fa-circle-play mj-media-empty__icon\" aria-hidden=\"true\"></i>\n <span class=\"mj-media-empty__text\">No media to play.</span>\n </div>\n } @else {\n <div class=\"mj-media-main\">\n <!-- Media stage -->\n <div class=\"mj-media-stage\">\n @if (HasError) {\n <div class=\"mj-media-error\" role=\"alert\">\n <i class=\"fa-solid fa-triangle-exclamation\" aria-hidden=\"true\"></i>\n <span>This media could not be played.</span>\n </div>\n }\n\n <!-- Loading / buffering overlay (primary element only). -->\n @if (ShowBusyIndicator && !HasError) {\n <div\n class=\"mj-media-busy\"\n [class.mj-media-busy--static]=\"PrefersReducedMotion\"\n role=\"status\"\n aria-hidden=\"true\"\n >\n <span class=\"mj-media-busy__spinner\"></span>\n <span class=\"mj-media-busy__label\">{{ BusyLabel }}</span>\n </div>\n }\n\n @if (IsAudioOnly) {\n <!-- Single audio track: hidden <audio> + optional waveform -->\n @for (track of AudioTracks; track TrackById($index, track); let first = $first) {\n @if (first) {\n <audio\n #primaryMedia\n class=\"mj-media-audio\"\n [src]=\"track.Url\"\n preload=\"metadata\"\n (loadstart)=\"OnMediaLoadStart($any($event.target))\"\n (loadedmetadata)=\"OnMediaLoadedMetadata($any($event.target))\"\n (canplay)=\"OnMediaCanPlay($any($event.target))\"\n (canplaythrough)=\"OnMediaCanPlay($any($event.target))\"\n (waiting)=\"OnMediaWaiting($any($event.target))\"\n (seeking)=\"OnMediaSeeking($any($event.target))\"\n (seeked)=\"OnMediaSeeked($any($event.target))\"\n (playing)=\"OnMediaPlaying($any($event.target))\"\n (stalled)=\"OnMediaStalled($any($event.target))\"\n (suspend)=\"OnMediaSuspend($any($event.target))\"\n (emptied)=\"OnMediaEmptied($any($event.target))\"\n (timeupdate)=\"OnMediaTimeUpdate($any($event.target))\"\n (durationchange)=\"OnMediaDurationChange($any($event.target))\"\n (play)=\"OnMediaPlay($any($event.target))\"\n (pause)=\"OnMediaPause($any($event.target))\"\n (ended)=\"OnMediaEnded($any($event.target))\"\n (error)=\"OnMediaError($any($event.target))\"\n ></audio>\n }\n }\n @if (ShowWaveform && ShowWaveformBars) {\n <!-- Real audio waveform \u2014 doubles as the scrubber (click/drag to seek). -->\n <div\n #waveTrack\n class=\"mj-media-waveform\"\n [class.mj-media-waveform--static]=\"PrefersReducedMotion\"\n role=\"slider\"\n tabindex=\"0\"\n aria-label=\"Seek\"\n [attr.aria-valuemin]=\"0\"\n [attr.aria-valuemax]=\"DurationMs\"\n [attr.aria-valuenow]=\"CurrentTimeMs\"\n [attr.aria-valuetext]=\"FormatTime(CurrentTimeMs) + ' of ' + FormatTime(DurationMs)\"\n (pointerdown)=\"OnWaveformPointerDown($event, waveTrack)\"\n (pointermove)=\"OnWaveformPointerMove($event, waveTrack)\"\n (pointerup)=\"OnWaveformPointerUp($event)\"\n (pointercancel)=\"OnWaveformPointerUp($event)\"\n (keydown)=\"OnScrubKeyDown($event)\"\n >\n @for (p of WaveformPeaks; track $index; let i = $index; let count = $count) {\n <span\n class=\"mj-media-waveform__bar\"\n [class.mj-media-waveform__bar--played]=\"(i + 1) / count <= WaveformPlayedFraction\"\n [style.height.%]=\"2 + p * 98\"\n ></span>\n }\n <div class=\"mj-media-waveform__progress\" [style.left.%]=\"ScrubPercent\"></div>\n </div>\n } @else if (ShowWaveform) {\n <!-- Decode pending or failed (CORS/codec/etc) \u2192 plain progress bar fallback. -->\n <div\n #waveTrack\n class=\"mj-media-waveform-fallback\"\n role=\"slider\"\n tabindex=\"0\"\n aria-label=\"Seek\"\n [attr.aria-valuemin]=\"0\"\n [attr.aria-valuemax]=\"DurationMs\"\n [attr.aria-valuenow]=\"CurrentTimeMs\"\n [attr.aria-valuetext]=\"FormatTime(CurrentTimeMs) + ' of ' + FormatTime(DurationMs)\"\n (pointerdown)=\"OnWaveformPointerDown($event, waveTrack)\"\n (pointermove)=\"OnWaveformPointerMove($event, waveTrack)\"\n (pointerup)=\"OnWaveformPointerUp($event)\"\n (pointercancel)=\"OnWaveformPointerUp($event)\"\n (keydown)=\"OnScrubKeyDown($event)\"\n >\n <div class=\"mj-media-waveform-fallback__rail\">\n <div class=\"mj-media-waveform-fallback__fill\" [style.width.%]=\"ScrubPercent\"></div>\n </div>\n </div>\n } @else {\n <div class=\"mj-media-audio-art\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-music\"></i>\n </div>\n }\n } @else {\n <!-- Video: single, or responsive grid for multiple -->\n <div\n class=\"mj-media-video-grid\"\n [class.mj-media-video-grid--multi]=\"IsMultiVideo\"\n >\n @for (track of VideoTracks; track TrackById($index, track); let i = $index) {\n <div class=\"mj-media-video-cell\">\n @if (track.Label) {\n <span class=\"mj-media-video-cell__label\">{{ track.Label }}</span>\n }\n @if (i === 0) {\n <video\n #primaryMedia\n class=\"mj-media-video\"\n [src]=\"track.Url\"\n [poster]=\"track.PosterUrl || null\"\n playsinline\n preload=\"metadata\"\n (loadstart)=\"OnMediaLoadStart($any($event.target))\"\n (loadedmetadata)=\"OnMediaLoadedMetadata($any($event.target))\"\n (canplay)=\"OnMediaCanPlay($any($event.target))\"\n (canplaythrough)=\"OnMediaCanPlay($any($event.target))\"\n (waiting)=\"OnMediaWaiting($any($event.target))\"\n (seeking)=\"OnMediaSeeking($any($event.target))\"\n (seeked)=\"OnMediaSeeked($any($event.target))\"\n (playing)=\"OnMediaPlaying($any($event.target))\"\n (stalled)=\"OnMediaStalled($any($event.target))\"\n (suspend)=\"OnMediaSuspend($any($event.target))\"\n (emptied)=\"OnMediaEmptied($any($event.target))\"\n (timeupdate)=\"OnMediaTimeUpdate($any($event.target))\"\n (durationchange)=\"OnMediaDurationChange($any($event.target))\"\n (play)=\"OnMediaPlay($any($event.target))\"\n (pause)=\"OnMediaPause($any($event.target))\"\n (ended)=\"OnMediaEnded($any($event.target))\"\n (error)=\"OnMediaError($any($event.target))\"\n ></video>\n } @else {\n <!-- Secondary videos follow the leader's timeline; no own controls -->\n <video\n class=\"mj-media-video\"\n [src]=\"track.Url\"\n [poster]=\"track.PosterUrl || null\"\n playsinline\n preload=\"metadata\"\n muted\n ></video>\n }\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Transport bar -->\n <div class=\"mj-media-transport\">\n <!-- Screen-reader + visible playback status (announced on transitions). -->\n <div\n class=\"mj-media-status\"\n [class.mj-media-status--busy]=\"ShowBusyIndicator\"\n role=\"status\"\n aria-live=\"polite\"\n >\n @if (ShowBusyIndicator) {\n <span class=\"mj-media-status__dot\" aria-hidden=\"true\"></span>\n }\n <span class=\"mj-media-status__text\">{{ StatusLabel }}</span>\n </div>\n\n <!-- Scrubber -->\n <div class=\"mj-media-scrubber-row\">\n <span class=\"mj-media-time\">{{ FormatTime(CurrentTimeMs) }}</span>\n <div\n #scrubTrack\n class=\"mj-media-scrubber\"\n role=\"slider\"\n tabindex=\"0\"\n aria-label=\"Seek\"\n [attr.aria-valuemin]=\"0\"\n [attr.aria-valuemax]=\"DurationMs\"\n [attr.aria-valuenow]=\"CurrentTimeMs\"\n [attr.aria-valuetext]=\"FormatTime(CurrentTimeMs) + ' of ' + FormatTime(DurationMs)\"\n (pointerdown)=\"OnScrubPointerDown($event, scrubTrack)\"\n (pointermove)=\"OnScrubPointerMove($event, scrubTrack)\"\n (pointerup)=\"OnScrubPointerUp($event)\"\n (pointercancel)=\"OnScrubPointerUp($event)\"\n (keydown)=\"OnScrubKeyDown($event)\"\n >\n <div class=\"mj-media-scrubber__rail\">\n <div class=\"mj-media-scrubber__fill\" [style.width.%]=\"ScrubPercent\"></div>\n </div>\n <div class=\"mj-media-scrubber__handle\" [style.left.%]=\"ScrubPercent\"></div>\n </div>\n <span class=\"mj-media-time mj-media-time--total\">{{ FormatTime(DurationMs) }}</span>\n </div>\n\n <!-- Controls -->\n <div class=\"mj-media-controls\">\n <div class=\"mj-media-controls__left\">\n @if (ShowSkipControls) {\n <button\n type=\"button\"\n class=\"mj-media-btn\"\n [attr.aria-label]=\"'Skip back ' + SkipSeconds + ' seconds'\"\n title=\"Skip back {{ SkipSeconds }}s\"\n (click)=\"SkipBackward()\"\n >\n <i class=\"fa-solid fa-rotate-left\" aria-hidden=\"true\"></i>\n <span class=\"mj-media-btn__skip-label\">{{ SkipSeconds }}</span>\n </button>\n }\n\n <button\n type=\"button\"\n class=\"mj-media-btn mj-media-btn--primary\"\n [attr.aria-label]=\"IsPlaying ? 'Pause' : 'Play'\"\n [title]=\"IsPlaying ? 'Pause' : 'Play'\"\n (click)=\"TogglePlay()\"\n >\n <i class=\"fa-solid\" [class.fa-pause]=\"IsPlaying\" [class.fa-play]=\"!IsPlaying\" aria-hidden=\"true\"></i>\n </button>\n\n @if (ShowSkipControls) {\n <button\n type=\"button\"\n class=\"mj-media-btn\"\n [attr.aria-label]=\"'Skip forward ' + SkipSeconds + ' seconds'\"\n title=\"Skip forward {{ SkipSeconds }}s\"\n (click)=\"SkipForward()\"\n >\n <i class=\"fa-solid fa-rotate-right\" aria-hidden=\"true\"></i>\n <span class=\"mj-media-btn__skip-label\">{{ SkipSeconds }}</span>\n </button>\n }\n </div>\n\n <div class=\"mj-media-controls__right\">\n @if (ShowVolume) {\n <div class=\"mj-media-volume\">\n <button\n type=\"button\"\n class=\"mj-media-btn\"\n [attr.aria-label]=\"Muted ? 'Unmute' : 'Mute'\"\n [title]=\"Muted ? 'Unmute' : 'Mute'\"\n (click)=\"ToggleMute()\"\n >\n <i\n class=\"fa-solid\"\n [class.fa-volume-xmark]=\"Muted || Volume === 0\"\n [class.fa-volume-low]=\"!Muted && Volume > 0 && Volume <= 0.5\"\n [class.fa-volume-high]=\"!Muted && Volume > 0.5\"\n aria-hidden=\"true\"\n ></i>\n </button>\n <input\n type=\"range\"\n class=\"mj-media-volume__slider\"\n min=\"0\"\n max=\"1\"\n step=\"0.05\"\n [value]=\"Muted ? 0 : Volume\"\n aria-label=\"Volume\"\n (input)=\"OnVolumeInput($event)\"\n />\n </div>\n }\n\n @if (ShowSpeedControl) {\n <div class=\"mj-media-rate\">\n <button\n type=\"button\"\n class=\"mj-media-btn mj-media-rate__toggle\"\n aria-haspopup=\"true\"\n [attr.aria-expanded]=\"RateMenuOpen\"\n aria-label=\"Playback speed\"\n title=\"Playback speed\"\n (click)=\"ToggleRateMenu()\"\n >\n {{ PlaybackRate }}&times;\n </button>\n @if (RateMenuOpen) {\n <ul class=\"mj-media-rate__menu\" role=\"menu\">\n @for (rate of PlaybackRates; track rate) {\n <li role=\"none\">\n <button\n type=\"button\"\n role=\"menuitemradio\"\n [attr.aria-checked]=\"rate === PlaybackRate\"\n class=\"mj-media-rate__item\"\n [class.mj-media-rate__item--active]=\"rate === PlaybackRate\"\n (click)=\"SetPlaybackRate(rate)\"\n >\n {{ rate }}&times;\n </button>\n </li>\n }\n </ul>\n }\n </div>\n }\n\n @if (ShowTranscriptToggleButton) {\n <button\n type=\"button\"\n class=\"mj-media-btn\"\n [class.mj-media-btn--active]=\"TranscriptVisible\"\n [attr.aria-pressed]=\"TranscriptVisible\"\n [attr.aria-label]=\"TranscriptVisible ? 'Hide transcript' : 'Show transcript'\"\n [title]=\"TranscriptVisible ? 'Hide transcript' : 'Show transcript'\"\n (click)=\"ToggleTranscript()\"\n >\n <i class=\"fa-solid fa-closed-captioning\" aria-hidden=\"true\"></i>\n </button>\n }\n\n @if (ShowFullscreen && VideoTracks.length > 0) {\n <button\n type=\"button\"\n class=\"mj-media-btn\"\n [attr.aria-label]=\"IsFullscreen ? 'Exit fullscreen' : 'Enter fullscreen'\"\n [title]=\"IsFullscreen ? 'Exit fullscreen' : 'Enter fullscreen'\"\n (click)=\"ToggleFullscreen()\"\n >\n <i\n class=\"fa-solid\"\n [class.fa-compress]=\"IsFullscreen\"\n [class.fa-expand]=\"!IsFullscreen\"\n aria-hidden=\"true\"\n ></i>\n </button>\n }\n </div>\n </div>\n </div>\n </div>\n\n <!-- Transcript panel -->\n @if (ShowTranscriptPanel) {\n <div class=\"mj-media-transcript\" role=\"list\" aria-label=\"Transcript\">\n @for (cue of Transcript; track CueTrackById($index, cue); let i = $index) {\n <button\n type=\"button\"\n role=\"listitem\"\n class=\"mj-media-cue\"\n [class.mj-media-cue--active]=\"i === ActiveCueIndex\"\n [attr.aria-current]=\"i === ActiveCueIndex ? 'true' : null\"\n (click)=\"OnCueClicked(i)\"\n (keydown)=\"OnCueKeyDown($event, i)\"\n >\n <span class=\"mj-media-cue__time\">{{ FormatTime(cue.StartMs) }}</span>\n <span class=\"mj-media-cue__body\">\n @if (cue.SpeakerLabel) {\n <span class=\"mj-media-cue__speaker\" [style.color]=\"SpeakerColor(cue.SpeakerLabel)\">\n {{ cue.SpeakerLabel }}\n </span>\n }\n <span class=\"mj-media-cue__text\">{{ cue.Text }}</span>\n </span>\n </button>\n }\n </div>\n }\n }\n</div>\n", styles: [":host {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n min-height: 0;\n color: var(--mj-text-primary);\n}\n\n.mj-media-player {\n display: flex;\n flex-direction: column;\n flex: 1 1 auto;\n min-height: 0;\n gap: 0;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md, 8px);\n overflow: hidden;\n outline: none;\n}\n\n.mj-media-player:focus-visible {\n box-shadow: 0 0 0 2px var(--mj-border-focus);\n}\n\n/* When transcript sits to the side, lay out main + transcript horizontally. */\n.mj-media-player--has-transcript:not(.mj-media-player--bottom-transcript) {\n flex-direction: row;\n align-items: stretch;\n}\n\n.mj-media-main {\n display: flex;\n flex-direction: column;\n flex: 1 1 auto;\n min-width: 0;\n min-height: 0;\n}\n\n/*\n * Audio layouts keep the stage + transport compact at the top so the transcript can\n * grow to fill the remaining height. Video layouts let the main area grow to fill space.\n */\n.mj-media-player--audio.mj-media-player--bottom-transcript .mj-media-main {\n flex: 0 0 auto;\n}\n\n/* ---------------------------------------------------------------- Empty / error */\n\n.mj-media-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n padding: 2.5rem 1rem;\n color: var(--mj-text-muted);\n}\n\n.mj-media-empty__icon {\n font-size: 2rem;\n}\n\n.mj-media-error {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.75rem 1rem;\n color: var(--mj-status-error-text);\n background: var(--mj-status-error-bg);\n}\n\n/* ---------------------------------------------------------------- Stage */\n\n.mj-media-stage {\n position: relative;\n background: var(--mj-bg-surface-sunken);\n min-height: 80px;\n}\n\n/* Video stages grow to fill the available height of the player. */\n.mj-media-player--video .mj-media-stage {\n flex: 1 1 auto;\n min-height: 0;\n display: flex;\n}\n\n.mj-media-audio {\n display: none;\n}\n\n.mj-media-audio-art {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 2rem;\n font-size: 2.5rem;\n color: var(--mj-text-disabled);\n}\n\n/* Real audio waveform \u2014 doubles as the scrubber */\n.mj-media-waveform {\n position: relative;\n display: flex;\n align-items: center;\n gap: 1px;\n width: 100%;\n box-sizing: border-box;\n height: 96px;\n padding: 0.75rem 1rem;\n overflow: hidden;\n cursor: pointer;\n touch-action: none;\n outline: none;\n}\n\n.mj-media-waveform:focus-visible {\n box-shadow: inset 0 0 0 2px var(--mj-border-focus);\n border-radius: var(--mj-radius-sm, 4px);\n}\n\n.mj-media-waveform__bar {\n /*\n * flex: 1 1 0 + min-width: 0 makes every bar share the row equally and compress to\n * fit \u2014 N bars always span exactly the container width with no horizontal overflow,\n * however narrow the widget gets. (A non-zero flex-basis or min-width would let the\n * bars sum past 100% and spill.)\n */\n flex: 1 1 0;\n min-width: 0;\n min-height: 2px;\n align-self: center;\n background: var(--mj-border-strong);\n border-radius: 1px;\n pointer-events: none;\n transform-origin: center;\n transition: background-color 0.1s ease, transform 0.18s ease;\n}\n\n.mj-media-waveform__bar--played {\n background: var(--mj-brand-primary);\n}\n\n/* Hover/seek position indicator. */\n.mj-media-waveform__progress {\n position: absolute;\n top: 0.5rem;\n bottom: 0.5rem;\n width: 2px;\n margin-left: -1px;\n background: var(--mj-brand-primary);\n opacity: 0.85;\n pointer-events: none;\n}\n\n/* Plain progress-bar fallback when decoding is pending or failed (no synthetic bars). */\n.mj-media-waveform-fallback {\n position: relative;\n display: flex;\n align-items: center;\n height: 96px;\n padding: 0 1rem;\n cursor: pointer;\n touch-action: none;\n outline: none;\n}\n\n.mj-media-waveform-fallback:focus-visible {\n box-shadow: inset 0 0 0 2px var(--mj-border-focus);\n border-radius: var(--mj-radius-sm, 4px);\n}\n\n.mj-media-waveform-fallback__rail {\n position: relative;\n flex: 1 1 auto;\n height: 5px;\n background: var(--mj-border-strong);\n border-radius: var(--mj-radius-full, 999px);\n overflow: hidden;\n}\n\n.mj-media-waveform-fallback__fill {\n position: absolute;\n inset: 0 auto 0 0;\n background: var(--mj-brand-primary);\n}\n\n/* Video grid */\n.mj-media-video-grid {\n display: grid;\n grid-template-columns: 1fr;\n gap: 4px;\n flex: 1 1 auto;\n min-height: 0;\n width: 100%;\n background: var(--mj-bg-surface-sunken);\n}\n\n.mj-media-video-grid--multi {\n grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));\n}\n\n.mj-media-video-cell {\n position: relative;\n display: flex;\n min-height: 0;\n}\n\n.mj-media-video-cell__label {\n position: absolute;\n top: 0.5rem;\n left: 0.5rem;\n z-index: 1;\n padding: 0.125rem 0.5rem;\n font-size: 0.75rem;\n border-radius: var(--mj-radius-sm, 4px);\n color: var(--mj-text-inverse);\n background: rgba(0, 0, 0, 0.55);\n}\n\n.mj-media-video {\n width: 100%;\n height: auto;\n display: block;\n background: var(--mj-bg-surface-sunken);\n}\n\n/*\n * A single video fills the available stage height, letterboxed (object-fit: contain)\n * so it never crops or stretches. Multi-video cells keep their natural height-by-width.\n */\n.mj-media-video-grid:not(.mj-media-video-grid--multi) .mj-media-video {\n height: 100%;\n min-height: 0;\n object-fit: contain;\n}\n\n/* ---------------------------------------------------------------- Busy overlay (loading / buffering) */\n\n.mj-media-busy {\n position: absolute;\n inset: 0;\n z-index: 2;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 0.625rem;\n pointer-events: none;\n color: var(--mj-text-inverse);\n background: color-mix(in srgb, var(--mj-bg-overlay) 55%, transparent);\n}\n\n.mj-media-busy__spinner {\n width: 36px;\n height: 36px;\n border-radius: 50%;\n border: 3px solid color-mix(in srgb, var(--mj-text-inverse) 35%, transparent);\n border-top-color: var(--mj-text-inverse);\n animation: mj-media-spin 0.8s linear infinite;\n}\n\n.mj-media-busy__label {\n font-size: 0.8125rem;\n font-weight: 600;\n letter-spacing: 0.01em;\n}\n\n@keyframes mj-media-spin {\n to {\n transform: rotate(360deg);\n }\n}\n\n/* Reduced motion: no spin \u2014 a gentle opacity pulse (or static if pulse also suppressed). */\n.mj-media-busy--static .mj-media-busy__spinner {\n animation: mj-media-busy-pulse 1.4s ease-in-out infinite;\n border-top-color: color-mix(in srgb, var(--mj-text-inverse) 35%, transparent);\n}\n\n@keyframes mj-media-busy-pulse {\n 0%,\n 100% {\n opacity: 0.45;\n }\n 50% {\n opacity: 1;\n }\n}\n\n/* ---------------------------------------------------------------- Status chip */\n\n.mj-media-status {\n display: inline-flex;\n align-items: center;\n gap: 0.375rem;\n align-self: flex-start;\n min-height: 1rem;\n font-size: 0.6875rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--mj-text-muted);\n}\n\n.mj-media-status--busy {\n color: var(--mj-brand-primary);\n}\n\n.mj-media-status__dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: var(--mj-brand-primary);\n animation: mj-media-busy-pulse 1.2s ease-in-out infinite;\n}\n\n/* ---------------------------------------------------------------- Transport */\n\n.mj-media-transport {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n padding: 0.625rem 0.75rem 0.75rem;\n background: var(--mj-bg-surface-card);\n border-top: 1px solid var(--mj-border-subtle);\n}\n\n.mj-media-scrubber-row {\n display: flex;\n align-items: center;\n gap: 0.625rem;\n}\n\n.mj-media-time {\n font-variant-numeric: tabular-nums;\n font-size: 0.8125rem;\n color: var(--mj-text-secondary);\n min-width: 3.25rem;\n}\n\n.mj-media-time--total {\n text-align: right;\n}\n\n.mj-media-scrubber {\n position: relative;\n flex: 1 1 auto;\n height: 18px;\n display: flex;\n align-items: center;\n cursor: pointer;\n touch-action: none;\n outline: none;\n}\n\n.mj-media-scrubber:focus-visible {\n box-shadow: 0 0 0 2px var(--mj-border-focus);\n border-radius: var(--mj-radius-full, 999px);\n}\n\n.mj-media-scrubber__rail {\n position: relative;\n flex: 1 1 auto;\n height: 5px;\n background: var(--mj-border-strong);\n border-radius: var(--mj-radius-full, 999px);\n overflow: hidden;\n}\n\n.mj-media-scrubber__fill {\n position: absolute;\n inset: 0 auto 0 0;\n background: var(--mj-brand-primary);\n}\n\n.mj-media-scrubber__handle {\n position: absolute;\n top: 50%;\n width: 13px;\n height: 13px;\n margin-left: -6.5px;\n transform: translateY(-50%);\n background: var(--mj-brand-primary);\n border: 2px solid var(--mj-bg-surface);\n border-radius: 50%;\n pointer-events: none;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);\n}\n\n.mj-media-controls {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 0.75rem;\n}\n\n.mj-media-controls__left,\n.mj-media-controls__right {\n display: flex;\n align-items: center;\n gap: 0.375rem;\n}\n\n/* Buttons (component-local \u2014 not the mjButton directive, this is a generic widget) */\n.mj-media-btn {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 0.25rem;\n min-width: 32px;\n height: 32px;\n padding: 0 0.5rem;\n font-size: 0.875rem;\n color: var(--mj-text-primary);\n background: transparent;\n border: 1px solid transparent;\n border-radius: var(--mj-radius-sm, 4px);\n cursor: pointer;\n transition: background-color 0.12s ease, color 0.12s ease;\n}\n\n.mj-media-btn:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.mj-media-btn:active {\n background: var(--mj-bg-surface-active);\n}\n\n.mj-media-btn:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--mj-border-focus);\n}\n\n.mj-media-btn--primary {\n color: var(--mj-text-inverse);\n background: var(--mj-brand-primary);\n min-width: 40px;\n height: 40px;\n border-radius: 50%;\n}\n\n.mj-media-btn--primary:hover {\n background: var(--mj-brand-primary-hover);\n}\n\n.mj-media-btn--primary:active {\n background: var(--mj-brand-primary-active);\n}\n\n/* Active/pressed toggle state (e.g. transcript visible) \u2014 tinted to read as \"on\". */\n.mj-media-btn--active {\n color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, transparent);\n}\n\n.mj-media-btn--active:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 18%, transparent);\n}\n\n.mj-media-btn__skip-label {\n font-size: 0.625rem;\n font-variant-numeric: tabular-nums;\n}\n\n/* Volume */\n.mj-media-volume {\n display: flex;\n align-items: center;\n gap: 0.25rem;\n}\n\n.mj-media-volume__slider {\n width: 80px;\n accent-color: var(--mj-brand-primary);\n cursor: pointer;\n}\n\n.mj-media-volume__slider:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--mj-border-focus);\n border-radius: var(--mj-radius-full, 999px);\n}\n\n/* Rate menu */\n.mj-media-rate {\n position: relative;\n}\n\n.mj-media-rate__toggle {\n font-variant-numeric: tabular-nums;\n font-weight: 600;\n}\n\n.mj-media-rate__menu {\n position: absolute;\n bottom: calc(100% + 4px);\n right: 0;\n z-index: 10;\n margin: 0;\n padding: 0.25rem;\n list-style: none;\n min-width: 4rem;\n background: var(--mj-bg-surface-elevated);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md, 8px);\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.18);\n}\n\n.mj-media-rate__item {\n display: block;\n width: 100%;\n text-align: left;\n padding: 0.375rem 0.625rem;\n font-size: 0.8125rem;\n color: var(--mj-text-primary);\n background: transparent;\n border: none;\n border-radius: var(--mj-radius-sm, 4px);\n cursor: pointer;\n}\n\n.mj-media-rate__item:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.mj-media-rate__item:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--mj-border-focus);\n}\n\n.mj-media-rate__item--active {\n color: var(--mj-brand-primary);\n font-weight: 600;\n}\n\n/* ---------------------------------------------------------------- Transcript */\n\n.mj-media-transcript {\n display: flex;\n flex-direction: column;\n gap: 1px;\n overflow-y: auto;\n background: var(--mj-bg-surface);\n border-left: 1px solid var(--mj-border-subtle);\n max-height: 480px;\n width: 340px;\n flex: 0 0 auto;\n padding: 0.5rem;\n}\n\n/*\n * Bottom transcript: full width, below the player, growing to fill the remaining\n * height of the container (min-height: 0 lets it shrink so its own overflow-y scrolls\n * internally instead of pushing the player taller). No fixed max-height \u2192 no dead space.\n */\n.mj-media-player--bottom-transcript .mj-media-transcript {\n width: auto;\n border-left: none;\n border-top: 1px solid var(--mj-border-subtle);\n flex: 1 1 auto;\n min-height: 0;\n max-height: none;\n}\n\n.mj-media-cue {\n display: flex;\n gap: 0.625rem;\n align-items: flex-start;\n width: 100%;\n text-align: left;\n padding: 0.5rem 0.625rem;\n background: transparent;\n border: none;\n border-left: 3px solid transparent;\n border-radius: var(--mj-radius-sm, 4px);\n cursor: pointer;\n color: var(--mj-text-primary);\n font: inherit;\n transition: background-color 0.12s ease;\n}\n\n.mj-media-cue:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.mj-media-cue:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--mj-border-focus);\n}\n\n.mj-media-cue--active {\n border-left-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n}\n\n.mj-media-cue__time {\n flex: 0 0 auto;\n font-variant-numeric: tabular-nums;\n font-size: 0.75rem;\n color: var(--mj-text-muted);\n padding-top: 0.125rem;\n}\n\n.mj-media-cue__body {\n display: flex;\n flex-direction: column;\n gap: 0.125rem;\n min-width: 0;\n}\n\n.mj-media-cue__speaker {\n font-size: 0.6875rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.02em;\n}\n\n.mj-media-cue__text {\n font-size: 0.875rem;\n line-height: 1.4;\n}\n\n/* ---------------------------------------------------------------- Motion */\n\n/* Subtle draw-in for the waveform bars (skipped under reduced-motion). */\n.mj-media-waveform:not(.mj-media-waveform--static) .mj-media-waveform__bar {\n animation: mj-media-bar-in 0.25s ease both;\n}\n\n@keyframes mj-media-bar-in {\n from {\n transform: scaleY(0.15);\n opacity: 0.4;\n }\n to {\n transform: scaleY(1);\n opacity: 1;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .mj-media-btn,\n .mj-media-cue,\n .mj-media-waveform__bar,\n .mj-media-scrubber__fill {\n transition: none;\n animation: none;\n }\n\n /* Spinner stops spinning; falls back to a gentle opacity pulse for affordance. */\n .mj-media-busy__spinner {\n animation: mj-media-busy-pulse 1.4s ease-in-out infinite;\n border-top-color: color-mix(in srgb, var(--mj-text-inverse) 35%, transparent);\n }\n\n .mj-media-status__dot {\n animation: none;\n }\n}\n"] }]
1470
+ }], () => [], { Tracks: [{
1471
+ type: Input
1472
+ }], Transcript: [{
1473
+ type: Input
1474
+ }], Autoplay: [{
1475
+ type: Input
1476
+ }], StartAtMs: [{
1477
+ type: Input
1478
+ }], ShowTranscript: [{
1479
+ type: Input
1480
+ }], ShowTranscriptToggle: [{
1481
+ type: Input
1482
+ }], ShowSpeedControl: [{
1483
+ type: Input
1484
+ }], ShowSkipControls: [{
1485
+ type: Input
1486
+ }], ShowVolume: [{
1487
+ type: Input
1488
+ }], ShowWaveform: [{
1489
+ type: Input
1490
+ }], WaveformBarCount: [{
1491
+ type: Input
1492
+ }], ShowFullscreen: [{
1493
+ type: Input
1494
+ }], SkipSeconds: [{
1495
+ type: Input
1496
+ }], PlaybackRates: [{
1497
+ type: Input
1498
+ }], InitialRate: [{
1499
+ type: Input
1500
+ }], InitialVolume: [{
1501
+ type: Input
1502
+ }], TranscriptPosition: [{
1503
+ type: Input
1504
+ }], BeforePlay: [{
1505
+ type: Output
1506
+ }], AfterPlay: [{
1507
+ type: Output
1508
+ }], BeforePause: [{
1509
+ type: Output
1510
+ }], AfterPause: [{
1511
+ type: Output
1512
+ }], BeforeSeek: [{
1513
+ type: Output
1514
+ }], AfterSeek: [{
1515
+ type: Output
1516
+ }], BeforeRateChange: [{
1517
+ type: Output
1518
+ }], AfterRateChange: [{
1519
+ type: Output
1520
+ }], CueActivated: [{
1521
+ type: Output
1522
+ }], CueClicked: [{
1523
+ type: Output
1524
+ }], TimeUpdate: [{
1525
+ type: Output
1526
+ }], DurationChange: [{
1527
+ type: Output
1528
+ }], Ended: [{
1529
+ type: Output
1530
+ }], StateChanged: [{
1531
+ type: Output
1532
+ }], TranscriptVisibilityChanged: [{
1533
+ type: Output
1534
+ }], primaryMedia: [{
1535
+ type: ViewChild,
1536
+ args: ['primaryMedia']
1537
+ }], OnHostKeyDown: [{
1538
+ type: HostListener,
1539
+ args: ['keydown', ['$event']]
1540
+ }], OnFullscreenChange: [{
1541
+ type: HostListener,
1542
+ args: ['document:fullscreenchange']
1543
+ }] }); })();
1544
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MJMediaPlayerComponent, { className: "MJMediaPlayerComponent", filePath: "src/lib/media-player/media-player.component.ts", lineNumber: 61 }); })();
1545
+ //# sourceMappingURL=media-player.component.js.map