@hanifhan1f/vidstack 1.12.34 → 1.12.35

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 (210) hide show
  1. package/cdn/with-layouts/chunks/{vidstack-BCLumCST.js → vidstack-BEgmmcDO.js} +56 -52
  2. package/cdn/with-layouts/chunks/{vidstack-DJyGEdCH.js → vidstack-t3PBZMbl.js} +1 -1
  3. package/cdn/with-layouts/vidstack.js +1 -1
  4. package/dev/chunks/{vidstack-B__DfQsT.js → vidstack-BReSQAMt.js} +10 -6
  5. package/dev/define/templates/vidstack-audio-layout.js +1 -1
  6. package/dev/define/templates/vidstack-video-layout.js +7 -5
  7. package/dev/define/vidstack-player-default-layout.js +1 -1
  8. package/dev/define/vidstack-player-layouts.js +1 -1
  9. package/dev/vidstack-elements.js +1 -1
  10. package/dev/vidstack.js +0 -4
  11. package/elements.d.ts +1 -1
  12. package/global/player.d.ts +1 -1
  13. package/global/plyr.d.ts +2 -2
  14. package/index.d.ts +2 -2
  15. package/package.json +1 -1
  16. package/prod/chunks/{vidstack-BnEo_Sla.js → vidstack-B0glDgAI.js} +10 -6
  17. package/prod/define/templates/vidstack-audio-layout.js +1 -1
  18. package/prod/define/templates/vidstack-video-layout.js +7 -5
  19. package/prod/define/vidstack-player-default-layout.js +1 -1
  20. package/prod/define/vidstack-player-layouts.js +1 -1
  21. package/prod/vidstack-elements.js +1 -1
  22. package/server/chunks/{vidstack-BIGdJnUK.js → vidstack-nANS1jfu.js} +16 -10
  23. package/server/define/vidstack-player-default-layout.js +1 -1
  24. package/server/define/vidstack-player-layouts.js +1 -1
  25. package/server/vidstack-elements.js +1 -1
  26. package/types/{vidstack-DYLKXUvI.d.ts → vidstack-CZqFq0VF.d.ts} +13 -1
  27. package/cdn/chunks/vidstack-8JHLDxl5.js +0 -1
  28. package/cdn/chunks/vidstack-BF7lZRtq.js +0 -3
  29. package/cdn/chunks/vidstack-BYgY9wmd.js +0 -1
  30. package/cdn/chunks/vidstack-BYpysj84.js +0 -1
  31. package/cdn/chunks/vidstack-Bjo5esRp.js +0 -1
  32. package/cdn/chunks/vidstack-BkxGdzTJ.js +0 -16
  33. package/cdn/chunks/vidstack-BuL67v3q.js +0 -1
  34. package/cdn/chunks/vidstack-Bzk6lVKb.js +0 -1
  35. package/cdn/chunks/vidstack-C0msPRTd.js +0 -3
  36. package/cdn/chunks/vidstack-C1FlyyzK.js +0 -1
  37. package/cdn/chunks/vidstack-CIjxJCz3.js +0 -1
  38. package/cdn/chunks/vidstack-CioT3Yw2.js +0 -1
  39. package/cdn/chunks/vidstack-Cj0I-Rec.js +0 -1
  40. package/cdn/chunks/vidstack-CmpbA3Yd.js +0 -16
  41. package/cdn/chunks/vidstack-CnWKPIKT.js +0 -16
  42. package/cdn/chunks/vidstack-CrqkytHl.js +0 -1
  43. package/cdn/chunks/vidstack-D0M8R0ZU.js +0 -1
  44. package/cdn/chunks/vidstack-D40FSa5B.js +0 -3
  45. package/cdn/chunks/vidstack-D84Fzc__.js +0 -16
  46. package/cdn/chunks/vidstack-DD2JwFVU.js +0 -1
  47. package/cdn/chunks/vidstack-DQvyz7Mm.js +0 -1
  48. package/cdn/chunks/vidstack-Dd9fqVv6.js +0 -1
  49. package/cdn/chunks/vidstack-DfDZuHNP.js +0 -1
  50. package/cdn/chunks/vidstack-uMxrPflF.js +0 -1
  51. package/cdn/chunks/vidstack-xjJ-ui_l.js +0 -1
  52. package/cdn/chunks/vidstack-zemsqC5d.js +0 -1
  53. package/cdn/providers/vidstack-audio-2Dt_Ivbp.js +0 -1
  54. package/cdn/providers/vidstack-audio-BOGYlExy.js +0 -1
  55. package/cdn/providers/vidstack-dash-CUtD4e6q.js +0 -1
  56. package/cdn/providers/vidstack-dash-D4ZARr66.js +0 -1
  57. package/cdn/providers/vidstack-google-cast-BdORATUX.js +0 -1
  58. package/cdn/providers/vidstack-hls-8-552IuX.js +0 -1
  59. package/cdn/providers/vidstack-hls-R25Kb6DP.js +0 -1
  60. package/cdn/providers/vidstack-html-BvVaN2VT.js +0 -1
  61. package/cdn/providers/vidstack-html-DaAUJYsD.js +0 -1
  62. package/cdn/providers/vidstack-video-BnwQZKER.js +0 -1
  63. package/cdn/providers/vidstack-video-Csvox7SO.js +0 -1
  64. package/cdn/providers/vidstack-vimeo-D4Z96kg2.js +0 -1
  65. package/cdn/providers/vidstack-vimeo-gJmBqtLK.js +0 -1
  66. package/cdn/providers/vidstack-youtube-Chl_dTAz.js +0 -1
  67. package/cdn/providers/vidstack-youtube-DiND6h3s.js +0 -1
  68. package/cdn/with-layouts/chunks/vidstack-4liSokT6.js +0 -1
  69. package/cdn/with-layouts/chunks/vidstack-B97B8XDc.js +0 -3
  70. package/cdn/with-layouts/chunks/vidstack-BD5YoTt5.js +0 -937
  71. package/cdn/with-layouts/chunks/vidstack-BGhRKayG.js +0 -914
  72. package/cdn/with-layouts/chunks/vidstack-BL_lNyW_.js +0 -1
  73. package/cdn/with-layouts/chunks/vidstack-BMhNagfl.js +0 -1
  74. package/cdn/with-layouts/chunks/vidstack-BP3ybDy9.js +0 -912
  75. package/cdn/with-layouts/chunks/vidstack-BbFHhcVG.js +0 -1
  76. package/cdn/with-layouts/chunks/vidstack-BjOOdDcQ.js +0 -1
  77. package/cdn/with-layouts/chunks/vidstack-C5AP9wid.js +0 -1
  78. package/cdn/with-layouts/chunks/vidstack-CS2aNc61.js +0 -1
  79. package/cdn/with-layouts/chunks/vidstack-CXEcXyBI.js +0 -1
  80. package/cdn/with-layouts/chunks/vidstack-Ciq-n5rg.js +0 -1
  81. package/cdn/with-layouts/chunks/vidstack-CmuGllcj.js +0 -1
  82. package/cdn/with-layouts/chunks/vidstack-CyNByJUW.js +0 -912
  83. package/cdn/with-layouts/chunks/vidstack-D-3_fAsK.js +0 -1
  84. package/cdn/with-layouts/chunks/vidstack-DCaNJN4T.js +0 -1
  85. package/cdn/with-layouts/chunks/vidstack-DKqYI_HJ.js +0 -1
  86. package/cdn/with-layouts/chunks/vidstack-DLGH9jfs.js +0 -1
  87. package/cdn/with-layouts/chunks/vidstack-DLVdcWrK.js +0 -3
  88. package/cdn/with-layouts/chunks/vidstack-DPO7J4-v.js +0 -3
  89. package/cdn/with-layouts/chunks/vidstack-DWjB11vV.js +0 -1
  90. package/cdn/with-layouts/chunks/vidstack-Dd3L-eQj.js +0 -1
  91. package/cdn/with-layouts/chunks/vidstack-Dh2GOjra.js +0 -1
  92. package/cdn/with-layouts/chunks/vidstack-DhNpv7SU.js +0 -1
  93. package/cdn/with-layouts/chunks/vidstack-QW5tTAS4.js +0 -897
  94. package/cdn/with-layouts/chunks/vidstack-T2rZVigk.js +0 -912
  95. package/cdn/with-layouts/chunks/vidstack-Xe_d7ovA.js +0 -1
  96. package/cdn/with-layouts/chunks/vidstack-wt2OT4N7.js +0 -1
  97. package/cdn/with-layouts/providers/vidstack-audio-Bw1csc6N.js +0 -1
  98. package/cdn/with-layouts/providers/vidstack-audio-CwoQJvl2.js +0 -1
  99. package/cdn/with-layouts/providers/vidstack-dash-CJsKJfLI.js +0 -1
  100. package/cdn/with-layouts/providers/vidstack-dash-DHRMFG4Y.js +0 -1
  101. package/cdn/with-layouts/providers/vidstack-google-cast-BSYJYn-o.js +0 -1
  102. package/cdn/with-layouts/providers/vidstack-hls-DG1rTEqu.js +0 -1
  103. package/cdn/with-layouts/providers/vidstack-hls-ji26kFdQ.js +0 -1
  104. package/cdn/with-layouts/providers/vidstack-html-BvHMxtoe.js +0 -1
  105. package/cdn/with-layouts/providers/vidstack-html-CoKFAYW5.js +0 -1
  106. package/cdn/with-layouts/providers/vidstack-video-1Uj5cNP2.js +0 -1
  107. package/cdn/with-layouts/providers/vidstack-video-CIxFJ9Z1.js +0 -1
  108. package/cdn/with-layouts/providers/vidstack-vimeo-CNLKOGMa.js +0 -1
  109. package/cdn/with-layouts/providers/vidstack-vimeo-DACTbJaQ.js +0 -1
  110. package/cdn/with-layouts/providers/vidstack-youtube-D1e-LE-8.js +0 -1
  111. package/cdn/with-layouts/providers/vidstack-youtube-RoLp-I6u.js +0 -1
  112. package/dev/chunks/vidstack-03oQOdB7.js +0 -58
  113. package/dev/chunks/vidstack-0XhA3AD_.js +0 -5181
  114. package/dev/chunks/vidstack-44ILR0Cb.js +0 -1521
  115. package/dev/chunks/vidstack-B4XOm7dP.js +0 -104
  116. package/dev/chunks/vidstack-BJsZjPkB.js +0 -204
  117. package/dev/chunks/vidstack-BXSB7eI9.js +0 -58
  118. package/dev/chunks/vidstack-BaGbgcvz.js +0 -107
  119. package/dev/chunks/vidstack-Blfm1k-4.js +0 -1520
  120. package/dev/chunks/vidstack-Bo8BNFJ2.js +0 -2986
  121. package/dev/chunks/vidstack-Bs54kFSz.js +0 -66
  122. package/dev/chunks/vidstack-C3N4zIuV.js +0 -254
  123. package/dev/chunks/vidstack-C4aPQ7hZ.js +0 -1482
  124. package/dev/chunks/vidstack-C6OqdJO7.js +0 -114
  125. package/dev/chunks/vidstack-CAL4iu_K.js +0 -1482
  126. package/dev/chunks/vidstack-CEjYxSqZ.js +0 -297
  127. package/dev/chunks/vidstack-CJCnHmKE.js +0 -104
  128. package/dev/chunks/vidstack-CQdFhXSo.js +0 -204
  129. package/dev/chunks/vidstack-CSryZFvY.js +0 -1521
  130. package/dev/chunks/vidstack-C_rvOKWp.js +0 -33
  131. package/dev/chunks/vidstack-CaudO1jl.js +0 -109
  132. package/dev/chunks/vidstack-CcQdBWil.js +0 -58
  133. package/dev/chunks/vidstack-Cky9ors4.js +0 -297
  134. package/dev/chunks/vidstack-DAOcbKGP.js +0 -254
  135. package/dev/chunks/vidstack-DD_3HszA.js +0 -1520
  136. package/dev/chunks/vidstack-DKaohJzR.js +0 -5181
  137. package/dev/chunks/vidstack-DLXCqdYV.js +0 -3010
  138. package/dev/chunks/vidstack-DS7nRfge.js +0 -204
  139. package/dev/chunks/vidstack-DWtK42Sh.js +0 -1483
  140. package/dev/chunks/vidstack-D_LvMxPr.js +0 -204
  141. package/dev/chunks/vidstack-Db1-Hg_U.js +0 -297
  142. package/dev/chunks/vidstack-DrczgsqN.js +0 -297
  143. package/dev/chunks/vidstack-EoLRQZbs.js +0 -2986
  144. package/dev/chunks/vidstack-FKkY62Dr.js +0 -104
  145. package/dev/chunks/vidstack-el2dbO0m.js +0 -5181
  146. package/dev/chunks/vidstack-rvhuswgi.js +0 -2986
  147. package/prod/chunks/vidstack-BAqdCFIm.js +0 -4771
  148. package/prod/chunks/vidstack-BHqGlnGz.js +0 -1482
  149. package/prod/chunks/vidstack-BP49Gz0m.js +0 -58
  150. package/prod/chunks/vidstack-BRZe2BNi.js +0 -107
  151. package/prod/chunks/vidstack-BRnfTkxi.js +0 -297
  152. package/prod/chunks/vidstack-BaaRY-9x.js +0 -201
  153. package/prod/chunks/vidstack-BexQYZop.js +0 -2976
  154. package/prod/chunks/vidstack-BpLd9ASW.js +0 -246
  155. package/prod/chunks/vidstack-C-yd_bAJ.js +0 -4771
  156. package/prod/chunks/vidstack-C05ipjAK.js +0 -1520
  157. package/prod/chunks/vidstack-CA4tDJdF.js +0 -33
  158. package/prod/chunks/vidstack-CFXAYpuh.js +0 -1521
  159. package/prod/chunks/vidstack-CIvL96_j.js +0 -297
  160. package/prod/chunks/vidstack-CYVCrFjx.js +0 -201
  161. package/prod/chunks/vidstack-Cs0fH84E.js +0 -1521
  162. package/prod/chunks/vidstack-D7hJcnN-.js +0 -297
  163. package/prod/chunks/vidstack-DDePVDjt.js +0 -2976
  164. package/prod/chunks/vidstack-DESBVLFp.js +0 -104
  165. package/prod/chunks/vidstack-DMDDSV3t.js +0 -104
  166. package/prod/chunks/vidstack-DXfGRhxZ.js +0 -201
  167. package/prod/chunks/vidstack-D_atbNqH.js +0 -3000
  168. package/prod/chunks/vidstack-DcMkaIHJ.js +0 -2976
  169. package/prod/chunks/vidstack-DnRxQoqP.js +0 -104
  170. package/prod/chunks/vidstack-DwenML7x.js +0 -4771
  171. package/prod/chunks/vidstack-IDWYvfna.js +0 -58
  172. package/prod/chunks/vidstack-Ko2EJadT.js +0 -1483
  173. package/prod/chunks/vidstack-MbEMbVfP.js +0 -109
  174. package/prod/chunks/vidstack-ShUhyBfI.js +0 -201
  175. package/prod/chunks/vidstack-SnIdjCkV.js +0 -58
  176. package/prod/chunks/vidstack-V1jwkH0s.js +0 -66
  177. package/prod/chunks/vidstack-V9U6gsde.js +0 -1482
  178. package/prod/chunks/vidstack-XA3zT5W9.js +0 -297
  179. package/prod/chunks/vidstack-bdt7uOlN.js +0 -114
  180. package/prod/chunks/vidstack-kdaDngIm.js +0 -1520
  181. package/prod/chunks/vidstack-oNEzlviH.js +0 -246
  182. package/server/chunks/vidstack-B2Bc9g7_.js +0 -2000
  183. package/server/chunks/vidstack-B4CWj0Hp.js +0 -381
  184. package/server/chunks/vidstack-B8P1aUCK.js +0 -1503
  185. package/server/chunks/vidstack-B8_v1VQn.js +0 -3059
  186. package/server/chunks/vidstack-BGgfNYAH.js +0 -141
  187. package/server/chunks/vidstack-BGmwlunt.js +0 -3035
  188. package/server/chunks/vidstack-BO8FLks6.js +0 -295
  189. package/server/chunks/vidstack-BosyhF3p.js +0 -207
  190. package/server/chunks/vidstack-C19bj3Wq.js +0 -307
  191. package/server/chunks/vidstack-C8F1EUBn.js +0 -104
  192. package/server/chunks/vidstack-CFTkUXGK.js +0 -295
  193. package/server/chunks/vidstack-CQMB7Msg.js +0 -1502
  194. package/server/chunks/vidstack-CWho6PlG.js +0 -141
  195. package/server/chunks/vidstack-CdBfecZT.js +0 -205
  196. package/server/chunks/vidstack-Cv_Art04.js +0 -4635
  197. package/server/chunks/vidstack-DE4b5Bgx.js +0 -2002
  198. package/server/chunks/vidstack-Db22EuE_.js +0 -207
  199. package/server/chunks/vidstack-DbvCOsqU.js +0 -107
  200. package/server/chunks/vidstack-DgHfFDiw.js +0 -1962
  201. package/server/chunks/vidstack-DhF59-Up.js +0 -4635
  202. package/server/chunks/vidstack-DnkB7eGO.js +0 -207
  203. package/server/chunks/vidstack-DoHmOxNm.js +0 -295
  204. package/server/chunks/vidstack-DsnTqzpL.js +0 -29
  205. package/server/chunks/vidstack-DzWvfg1d.js +0 -1503
  206. package/server/chunks/vidstack-FHGkN5xj.js +0 -566
  207. package/server/chunks/vidstack-PnFpou7g.js +0 -3035
  208. package/server/chunks/vidstack-f5-aflD2.js +0 -104
  209. package/server/chunks/vidstack-gEJMQpTE.js +0 -2001
  210. package/server/chunks/vidstack-n4zAyLEV.js +0 -2139
@@ -1,1502 +0,0 @@
1
- import { Component, State, effect, tick, peek, setAttribute, isString, setStyle, createContext, signal, EventsController, provideContext, onDispose, useContext, prop, useState, isNull, functionThrottle, computed, method, scoped, createScope, animationFrameThrottle, functionDebounce, hasProvidedContext, isNumber, listenEvent, isPointerEvent, isTouchEvent, isMouseEvent, DOMEvent, kebabToCamelCase } from './vidstack-B8LynzY5.js';
2
- import { useMediaContext, setAttributeIfEmpty, requestScopedAnimationFrame, autoPlacement, setARIALabel, watchActiveTextTrack, isCueActive, isTouchPinchEvent } from './vidstack-B4CWj0Hp.js';
3
- import { formatSpokenTime, Popper, ToggleButtonController, Slider, SliderController, sliderState, sliderValueFormatContext, TimeSlider, RadioGroupController, menuContext, formatTime } from './vidstack-BGmwlunt.js';
4
- import { FocusVisibleController, $keyboard, round, isTrackCaptionKind } from './vidstack-C19bj3Wq.js';
5
- import { $ariaBool, sortVideoQualities } from './vidstack-BOTZD4tC.js';
6
-
7
- class MediaAnnouncer extends Component {
8
- static props = {
9
- translations: null
10
- };
11
- static state = new State({
12
- label: null,
13
- busy: false
14
- });
15
- #media;
16
- #initializing = false;
17
- onSetup() {
18
- this.#media = useMediaContext();
19
- }
20
- onAttach(el) {
21
- el.style.display = "contents";
22
- }
23
- onConnect(el) {
24
- el.setAttribute("data-media-announcer", "");
25
- setAttributeIfEmpty(el, "role", "status");
26
- setAttributeIfEmpty(el, "aria-live", "polite");
27
- const { busy } = this.$state;
28
- this.setAttributes({
29
- "aria-busy": () => busy() ? "true" : null
30
- });
31
- this.#initializing = true;
32
- effect(this.#watchPaused.bind(this));
33
- effect(this.#watchVolume.bind(this));
34
- effect(this.#watchCaptions.bind(this));
35
- effect(this.#watchFullscreen.bind(this));
36
- effect(this.#watchPiP.bind(this));
37
- effect(this.#watchSeeking.bind(this));
38
- effect(this.#watchLabel.bind(this));
39
- tick();
40
- this.#initializing = false;
41
- }
42
- #watchPaused() {
43
- const { paused } = this.#media.$state;
44
- this.#setLabel(!paused() ? "Play" : "Pause");
45
- }
46
- #watchFullscreen() {
47
- const { fullscreen } = this.#media.$state;
48
- this.#setLabel(fullscreen() ? "Enter Fullscreen" : "Exit Fullscreen");
49
- }
50
- #watchPiP() {
51
- const { pictureInPicture } = this.#media.$state;
52
- this.#setLabel(pictureInPicture() ? "Enter PiP" : "Exit PiP");
53
- }
54
- #watchCaptions() {
55
- const { textTrack } = this.#media.$state;
56
- this.#setLabel(textTrack() ? "Closed-Captions On" : "Closed-Captions Off");
57
- }
58
- #watchVolume() {
59
- const { muted, volume, audioGain } = this.#media.$state;
60
- this.#setLabel(
61
- muted() || volume() === 0 ? "Mute" : `${Math.round(volume() * (audioGain() ?? 1) * 100)}% ${this.#translate("Volume")}`
62
- );
63
- }
64
- #startedSeekingAt = -1;
65
- #seekTimer = -1;
66
- #watchSeeking() {
67
- const { seeking, currentTime } = this.#media.$state, isSeeking = seeking();
68
- if (this.#startedSeekingAt > 0) {
69
- window.clearTimeout(this.#seekTimer);
70
- this.#seekTimer = window.setTimeout(() => {
71
- if (!this.scope) return;
72
- const newTime = peek(currentTime), seconds = Math.abs(newTime - this.#startedSeekingAt);
73
- if (seconds >= 1) {
74
- const isForward = newTime >= this.#startedSeekingAt, spokenTime = formatSpokenTime(seconds);
75
- this.#setLabel(
76
- `${this.#translate(isForward ? "Seek Forward" : "Seek Backward")} ${spokenTime}`
77
- );
78
- }
79
- this.#startedSeekingAt = -1;
80
- this.#seekTimer = -1;
81
- }, 300);
82
- } else if (isSeeking) {
83
- this.#startedSeekingAt = peek(currentTime);
84
- }
85
- }
86
- #translate(word) {
87
- const { translations } = this.$props;
88
- return translations?.()?.[word || ""] ?? word;
89
- }
90
- #watchLabel() {
91
- const { label, busy } = this.$state, $label = this.#translate(label());
92
- if (this.#initializing) return;
93
- busy.set(true);
94
- const id = window.setTimeout(() => void busy.set(false), 150);
95
- this.el && setAttribute(this.el, "aria-label", $label);
96
- if (isString($label)) {
97
- this.dispatch("change", { detail: $label });
98
- }
99
- return () => window.clearTimeout(id);
100
- }
101
- #setLabel(word) {
102
- const { label } = this.$state;
103
- label.set(word);
104
- }
105
- }
106
-
107
- class Controls extends Component {
108
- static props = {
109
- hideDelay: 2e3,
110
- hideOnMouseLeave: false
111
- };
112
- #media;
113
- onSetup() {
114
- this.#media = useMediaContext();
115
- effect(this.#watchProps.bind(this));
116
- }
117
- onAttach(el) {
118
- const { pictureInPicture, fullscreen } = this.#media.$state;
119
- setStyle(el, "pointer-events", "none");
120
- setAttributeIfEmpty(el, "role", "group");
121
- this.setAttributes({
122
- "data-visible": this.#isShowing.bind(this),
123
- "data-fullscreen": fullscreen,
124
- "data-pip": pictureInPicture
125
- });
126
- effect(() => {
127
- this.dispatch("change", { detail: this.#isShowing() });
128
- });
129
- effect(this.#hideControls.bind(this));
130
- effect(() => {
131
- const isFullscreen = fullscreen();
132
- for (const side of ["top", "right", "bottom", "left"]) {
133
- setStyle(el, `padding-${side}`, isFullscreen && `env(safe-area-inset-${side})`);
134
- }
135
- });
136
- }
137
- #hideControls() {
138
- if (!this.el) return;
139
- const { nativeControls } = this.#media.$state, isHidden = nativeControls();
140
- setAttribute(this.el, "aria-hidden", isHidden ? "true" : null);
141
- setStyle(this.el, "display", isHidden ? "none" : null);
142
- }
143
- #watchProps() {
144
- const { controls } = this.#media.player, { hideDelay, hideOnMouseLeave } = this.$props;
145
- controls.defaultDelay = hideDelay() === 2e3 ? this.#media.$props.controlsDelay() : hideDelay();
146
- controls.hideOnMouseLeave = hideOnMouseLeave();
147
- }
148
- #isShowing() {
149
- const { controlsVisible } = this.#media.$state;
150
- return controlsVisible();
151
- }
152
- }
153
-
154
- class ControlsGroup extends Component {
155
- onAttach(el) {
156
- if (!el.style.pointerEvents) setStyle(el, "pointer-events", "auto");
157
- }
158
- }
159
-
160
- const tooltipContext = createContext();
161
-
162
- let id = 0;
163
- class Tooltip extends Component {
164
- static props = {
165
- showDelay: 700
166
- };
167
- #id = `media-tooltip-${++id}`;
168
- #trigger = signal(null);
169
- #content = signal(null);
170
- #showing = signal(false);
171
- constructor() {
172
- super();
173
- new FocusVisibleController();
174
- const { showDelay } = this.$props;
175
- new Popper({
176
- trigger: this.#trigger,
177
- content: this.#content,
178
- showDelay,
179
- listen(trigger, show, hide) {
180
- effect(() => {
181
- if ($keyboard()) ;
182
- });
183
- new EventsController(trigger).add("touchstart", (e) => e.preventDefault(), { passive: false }).add("mouseenter", show).add("mouseleave", hide);
184
- },
185
- onChange: this.#onShowingChange.bind(this)
186
- });
187
- }
188
- onAttach(el) {
189
- el.style.setProperty("display", "contents");
190
- }
191
- onSetup() {
192
- provideContext(tooltipContext, {
193
- trigger: this.#trigger,
194
- content: this.#content,
195
- showing: this.#showing,
196
- attachTrigger: this.#attachTrigger.bind(this),
197
- detachTrigger: this.#detachTrigger.bind(this),
198
- attachContent: this.#attachContent.bind(this),
199
- detachContent: this.#detachContent.bind(this)
200
- });
201
- }
202
- #attachTrigger(el) {
203
- this.#trigger.set(el);
204
- let tooltipName = el.getAttribute("data-media-tooltip");
205
- if (tooltipName) {
206
- this.el?.setAttribute(`data-media-${tooltipName}-tooltip`, "");
207
- }
208
- setAttribute(el, "data-describedby", this.#id);
209
- }
210
- #detachTrigger(el) {
211
- el.removeAttribute("data-describedby");
212
- el.removeAttribute("aria-describedby");
213
- this.#trigger.set(null);
214
- }
215
- #attachContent(el) {
216
- el.setAttribute("id", this.#id);
217
- el.style.display = "none";
218
- setAttributeIfEmpty(el, "role", "tooltip");
219
- this.#content.set(el);
220
- }
221
- #detachContent(el) {
222
- el.removeAttribute("id");
223
- el.removeAttribute("role");
224
- this.#content.set(null);
225
- }
226
- #onShowingChange(isShowing) {
227
- const trigger = this.#trigger(), content = this.#content();
228
- if (trigger) {
229
- setAttribute(trigger, "aria-describedby", isShowing ? this.#id : null);
230
- }
231
- for (const el of [this.el, trigger, content]) {
232
- el && setAttribute(el, "data-visible", isShowing);
233
- }
234
- this.#showing.set(isShowing);
235
- }
236
- }
237
-
238
- class TooltipTrigger extends Component {
239
- constructor() {
240
- super();
241
- new FocusVisibleController();
242
- }
243
- onConnect(el) {
244
- onDispose(
245
- requestScopedAnimationFrame(() => {
246
- if (!this.connectScope) return;
247
- this.#attach();
248
- const tooltip = useContext(tooltipContext);
249
- onDispose(() => {
250
- const button = this.#getButton();
251
- button && tooltip.detachTrigger(button);
252
- });
253
- })
254
- );
255
- }
256
- #attach() {
257
- const button = this.#getButton(), tooltip = useContext(tooltipContext);
258
- button && tooltip.attachTrigger(button);
259
- }
260
- #getButton() {
261
- const candidate = this.el.firstElementChild;
262
- return candidate?.localName === "button" || candidate?.getAttribute("role") === "button" ? candidate : this.el;
263
- }
264
- }
265
-
266
- class TooltipContent extends Component {
267
- static props = {
268
- placement: "top center",
269
- offset: 0,
270
- alignOffset: 0
271
- };
272
- constructor() {
273
- super();
274
- new FocusVisibleController();
275
- const { placement } = this.$props;
276
- this.setAttributes({
277
- "data-placement": placement
278
- });
279
- }
280
- onAttach(el) {
281
- this.#attach(el);
282
- Object.assign(el.style, {
283
- position: "absolute",
284
- top: 0,
285
- left: 0,
286
- width: "max-content"
287
- });
288
- }
289
- onConnect(el) {
290
- this.#attach(el);
291
- const tooltip = useContext(tooltipContext);
292
- onDispose(() => tooltip.detachContent(el));
293
- onDispose(
294
- requestScopedAnimationFrame(() => {
295
- if (!this.connectScope) return;
296
- effect(this.#watchPlacement.bind(this));
297
- })
298
- );
299
- }
300
- #attach(el) {
301
- const tooltip = useContext(tooltipContext);
302
- tooltip.attachContent(el);
303
- }
304
- #watchPlacement() {
305
- const { showing } = useContext(tooltipContext);
306
- if (!showing()) return;
307
- const { placement, offset: mainOffset, alignOffset } = this.$props;
308
- return autoPlacement(this.el, this.#getTrigger(), placement(), {
309
- offsetVarName: "media-tooltip",
310
- xOffset: alignOffset(),
311
- yOffset: mainOffset()
312
- });
313
- }
314
- #getTrigger() {
315
- return useContext(tooltipContext).trigger();
316
- }
317
- }
318
-
319
- class ToggleButton extends Component {
320
- static props = {
321
- disabled: false,
322
- defaultPressed: false
323
- };
324
- #pressed = signal(false);
325
- /**
326
- * Whether the toggle is currently in a `pressed` state.
327
- */
328
- get pressed() {
329
- return this.#pressed();
330
- }
331
- constructor() {
332
- super();
333
- new ToggleButtonController({
334
- isPresssed: this.#pressed
335
- });
336
- }
337
- }
338
- const togglebutton__proto = ToggleButton.prototype;
339
- prop(togglebutton__proto, "pressed");
340
-
341
- class GoogleCastButton extends Component {
342
- static props = ToggleButtonController.props;
343
- #media;
344
- constructor() {
345
- super();
346
- new ToggleButtonController({
347
- isPresssed: this.#isPressed.bind(this),
348
- onPress: this.#onPress.bind(this)
349
- });
350
- }
351
- onSetup() {
352
- this.#media = useMediaContext();
353
- const { canGoogleCast, isGoogleCastConnected } = this.#media.$state;
354
- this.setAttributes({
355
- "data-active": isGoogleCastConnected,
356
- "data-supported": canGoogleCast,
357
- "data-state": this.#getState.bind(this),
358
- "aria-hidden": $ariaBool(() => !canGoogleCast())
359
- });
360
- }
361
- onAttach(el) {
362
- el.setAttribute("data-media-tooltip", "google-cast");
363
- setARIALabel(el, this.#getDefaultLabel.bind(this));
364
- }
365
- #onPress(event) {
366
- const remote = this.#media.remote;
367
- remote.requestGoogleCast(event);
368
- }
369
- #isPressed() {
370
- const { remotePlaybackType, remotePlaybackState } = this.#media.$state;
371
- return remotePlaybackType() === "google-cast" && remotePlaybackState() !== "disconnected";
372
- }
373
- #getState() {
374
- const { remotePlaybackType, remotePlaybackState } = this.#media.$state;
375
- return remotePlaybackType() === "google-cast" && remotePlaybackState();
376
- }
377
- #getDefaultLabel() {
378
- const { remotePlaybackState } = this.#media.$state;
379
- return `Google Cast ${remotePlaybackState()}`;
380
- }
381
- }
382
-
383
- class SliderVideo extends Component {
384
- static props = {
385
- src: null,
386
- crossOrigin: null
387
- };
388
- static state = new State({
389
- video: null,
390
- src: null,
391
- crossOrigin: null,
392
- canPlay: false,
393
- error: null,
394
- hidden: false
395
- });
396
- #media;
397
- #slider;
398
- get video() {
399
- return this.$state.video();
400
- }
401
- onSetup() {
402
- this.#media = useMediaContext();
403
- this.#slider = useState(Slider.state);
404
- this.#watchCrossOrigin();
405
- this.setAttributes({
406
- "data-loading": this.#isLoading.bind(this),
407
- "data-hidden": this.$state.hidden,
408
- "data-error": this.#hasError.bind(this),
409
- "aria-hidden": $ariaBool(this.$state.hidden)
410
- });
411
- }
412
- onAttach(el) {
413
- effect(this.#watchVideo.bind(this));
414
- effect(this.#watchSrc.bind(this));
415
- effect(this.#watchCrossOrigin.bind(this));
416
- effect(this.#watchHidden.bind(this));
417
- effect(this.#onSrcChange.bind(this));
418
- effect(this.#onUpdateTime.bind(this));
419
- }
420
- #watchVideo() {
421
- const video = this.$state.video();
422
- if (!video) return;
423
- if (video.readyState >= 2) this.#onCanPlay();
424
- new EventsController(video).add("canplay", this.#onCanPlay.bind(this)).add("error", this.#onError.bind(this));
425
- }
426
- #watchSrc() {
427
- const { src } = this.$state, { canLoad } = this.#media.$state;
428
- src.set(canLoad() ? this.$props.src() : null);
429
- }
430
- #watchCrossOrigin() {
431
- const { crossOrigin: crossOriginProp } = this.$props, { crossOrigin: crossOriginState } = this.$state, { crossOrigin: mediaCrossOrigin } = this.#media.$state, crossOrigin = crossOriginProp() !== null ? crossOriginProp() : mediaCrossOrigin();
432
- crossOriginState.set(crossOrigin === true ? "anonymous" : crossOrigin);
433
- }
434
- #isLoading() {
435
- const { canPlay, hidden } = this.$state;
436
- return !canPlay() && !hidden();
437
- }
438
- #hasError() {
439
- const { error } = this.$state;
440
- return !isNull(error);
441
- }
442
- #watchHidden() {
443
- const { src, hidden } = this.$state, { canLoad, duration } = this.#media.$state;
444
- hidden.set(canLoad() && (!src() || this.#hasError() || !Number.isFinite(duration())));
445
- }
446
- #onSrcChange() {
447
- const { src, canPlay, error } = this.$state;
448
- src();
449
- canPlay.set(false);
450
- error.set(null);
451
- }
452
- #onCanPlay(event) {
453
- const { canPlay, error } = this.$state;
454
- canPlay.set(true);
455
- error.set(null);
456
- this.dispatch("can-play", { trigger: event });
457
- }
458
- #onError(event) {
459
- const { canPlay, error } = this.$state;
460
- canPlay.set(false);
461
- error.set(event);
462
- this.dispatch("error", { trigger: event });
463
- }
464
- #onUpdateTime() {
465
- const { video, canPlay } = this.$state, { duration } = this.#media.$state, { pointerRate } = this.#slider, media = video(), canUpdate = canPlay() && media && Number.isFinite(duration()) && Number.isFinite(pointerRate());
466
- if (canUpdate) {
467
- media.currentTime = pointerRate() * duration();
468
- }
469
- }
470
- }
471
- const slidervideo__proto = SliderVideo.prototype;
472
- prop(slidervideo__proto, "video");
473
-
474
- class AudioGainSlider extends Component {
475
- static props = {
476
- ...SliderController.props,
477
- step: 25,
478
- keyStep: 25,
479
- shiftKeyMultiplier: 2,
480
- min: 0,
481
- max: 300
482
- };
483
- static state = sliderState;
484
- #media;
485
- onSetup() {
486
- this.#media = useMediaContext();
487
- provideContext(sliderValueFormatContext, {
488
- default: "percent",
489
- percent: (_, decimalPlaces) => {
490
- return round(this.$state.value(), decimalPlaces) + "%";
491
- }
492
- });
493
- new SliderController({
494
- getStep: this.$props.step,
495
- getKeyStep: this.$props.keyStep,
496
- roundValue: Math.round,
497
- isDisabled: this.#isDisabled.bind(this),
498
- aria: {
499
- valueNow: this.#getARIAValueNow.bind(this),
500
- valueText: this.#getARIAValueText.bind(this)
501
- },
502
- onDragValueChange: this.#onDragValueChange.bind(this),
503
- onValueChange: this.#onValueChange.bind(this)
504
- }).attach(this);
505
- effect(this.#watchMinMax.bind(this));
506
- effect(this.#watchAudioGain.bind(this));
507
- }
508
- onAttach(el) {
509
- el.setAttribute("data-media-audio-gain-slider", "");
510
- setAttributeIfEmpty(el, "aria-label", "Audio Boost");
511
- const { canSetAudioGain } = this.#media.$state;
512
- this.setAttributes({
513
- "data-supported": canSetAudioGain,
514
- "aria-hidden": $ariaBool(() => !canSetAudioGain())
515
- });
516
- }
517
- #getARIAValueNow() {
518
- const { value } = this.$state;
519
- return Math.round(value());
520
- }
521
- #getARIAValueText() {
522
- const { value } = this.$state;
523
- return value() + "%";
524
- }
525
- #watchMinMax() {
526
- const { min, max } = this.$props;
527
- this.$state.min.set(min());
528
- this.$state.max.set(max());
529
- }
530
- #watchAudioGain() {
531
- const { audioGain } = this.#media.$state, value = ((audioGain() ?? 1) - 1) * 100;
532
- this.$state.value.set(value);
533
- this.dispatch("value-change", { detail: value });
534
- }
535
- #isDisabled() {
536
- const { disabled } = this.$props, { canSetAudioGain } = this.#media.$state;
537
- return disabled() || !canSetAudioGain();
538
- }
539
- #onAudioGainChange(event) {
540
- if (!event.trigger) return;
541
- const gain = round(1 + event.detail / 100, 2);
542
- this.#media.remote.changeAudioGain(gain, event);
543
- }
544
- #onValueChange(event) {
545
- this.#onAudioGainChange(event);
546
- }
547
- #onDragValueChange(event) {
548
- this.#onAudioGainChange(event);
549
- }
550
- }
551
-
552
- class SpeedSlider extends Component {
553
- static props = {
554
- ...SliderController.props,
555
- step: 0.25,
556
- keyStep: 0.25,
557
- shiftKeyMultiplier: 2,
558
- min: 0,
559
- max: 2
560
- };
561
- static state = sliderState;
562
- #media;
563
- onSetup() {
564
- this.#media = useMediaContext();
565
- new SliderController({
566
- getStep: this.$props.step,
567
- getKeyStep: this.$props.keyStep,
568
- roundValue: this.#roundValue,
569
- isDisabled: this.#isDisabled.bind(this),
570
- aria: {
571
- valueNow: this.#getARIAValueNow.bind(this),
572
- valueText: this.#getARIAValueText.bind(this)
573
- },
574
- onDragValueChange: this.#onDragValueChange.bind(this),
575
- onValueChange: this.#onValueChange.bind(this)
576
- }).attach(this);
577
- effect(this.#watchMinMax.bind(this));
578
- effect(this.#watchPlaybackRate.bind(this));
579
- }
580
- onAttach(el) {
581
- el.setAttribute("data-media-speed-slider", "");
582
- setAttributeIfEmpty(el, "aria-label", "Speed");
583
- const { canSetPlaybackRate } = this.#media.$state;
584
- this.setAttributes({
585
- "data-supported": canSetPlaybackRate,
586
- "aria-hidden": $ariaBool(() => !canSetPlaybackRate())
587
- });
588
- }
589
- #getARIAValueNow() {
590
- const { value } = this.$state;
591
- return value();
592
- }
593
- #getARIAValueText() {
594
- const { value } = this.$state;
595
- return value() + "x";
596
- }
597
- #watchMinMax() {
598
- const { min, max } = this.$props;
599
- this.$state.min.set(min());
600
- this.$state.max.set(max());
601
- }
602
- #watchPlaybackRate() {
603
- const { playbackRate } = this.#media.$state;
604
- const newValue = playbackRate();
605
- this.$state.value.set(newValue);
606
- this.dispatch("value-change", { detail: newValue });
607
- }
608
- #roundValue(value) {
609
- return round(value, 2);
610
- }
611
- #isDisabled() {
612
- const { disabled } = this.$props, { canSetPlaybackRate } = this.#media.$state;
613
- return disabled() || !canSetPlaybackRate();
614
- }
615
- #throttledSpeedChange = functionThrottle(this.#onPlaybackRateChange.bind(this), 25);
616
- #onPlaybackRateChange(event) {
617
- if (!event.trigger) return;
618
- const rate = event.detail;
619
- this.#media.remote.changePlaybackRate(rate, event);
620
- }
621
- #onValueChange(event) {
622
- this.#throttledSpeedChange(event);
623
- }
624
- #onDragValueChange(event) {
625
- this.#throttledSpeedChange(event);
626
- }
627
- }
628
-
629
- class QualitySlider extends Component {
630
- static props = {
631
- ...SliderController.props,
632
- step: 1,
633
- keyStep: 1,
634
- shiftKeyMultiplier: 1
635
- };
636
- static state = sliderState;
637
- #media;
638
- #sortedQualities = computed(() => {
639
- const { qualities } = this.#media.$state;
640
- return sortVideoQualities(qualities());
641
- });
642
- onSetup() {
643
- this.#media = useMediaContext();
644
- new SliderController({
645
- getStep: this.$props.step,
646
- getKeyStep: this.$props.keyStep,
647
- roundValue: Math.round,
648
- isDisabled: this.#isDisabled.bind(this),
649
- aria: {
650
- valueNow: this.#getARIAValueNow.bind(this),
651
- valueText: this.#getARIAValueText.bind(this)
652
- },
653
- onDragValueChange: this.#onDragValueChange.bind(this),
654
- onValueChange: this.#onValueChange.bind(this)
655
- }).attach(this);
656
- effect(this.#watchMax.bind(this));
657
- effect(this.#watchQuality.bind(this));
658
- }
659
- onAttach(el) {
660
- el.setAttribute("data-media-quality-slider", "");
661
- setAttributeIfEmpty(el, "aria-label", "Video Quality");
662
- const { qualities, canSetQuality } = this.#media.$state, $supported = computed(() => canSetQuality() && qualities().length > 0);
663
- this.setAttributes({
664
- "data-supported": $supported,
665
- "aria-hidden": $ariaBool(() => !$supported())
666
- });
667
- }
668
- #getARIAValueNow() {
669
- const { value } = this.$state;
670
- return value();
671
- }
672
- #getARIAValueText() {
673
- const { quality } = this.#media.$state;
674
- if (!quality()) return "";
675
- const { height, bitrate } = quality(), bitrateText = bitrate && bitrate > 0 ? `${(bitrate / 1e6).toFixed(2)} Mbps` : null;
676
- return height ? `${height}p${bitrateText ? ` (${bitrateText})` : ""}` : "Auto";
677
- }
678
- #watchMax() {
679
- const $qualities = this.#sortedQualities();
680
- this.$state.max.set(Math.max(0, $qualities.length - 1));
681
- }
682
- #watchQuality() {
683
- let { quality } = this.#media.$state, $qualities = this.#sortedQualities(), value = Math.max(0, $qualities.indexOf(quality()));
684
- this.$state.value.set(value);
685
- this.dispatch("value-change", { detail: value });
686
- }
687
- #isDisabled() {
688
- const { disabled } = this.$props, { canSetQuality, qualities } = this.#media.$state;
689
- return disabled() || qualities().length <= 1 || !canSetQuality();
690
- }
691
- #throttledQualityChange = functionThrottle(this.#onQualityChange.bind(this), 25);
692
- #onQualityChange(event) {
693
- if (!event.trigger) return;
694
- const { qualities } = this.#media, quality = peek(this.#sortedQualities)[event.detail];
695
- this.#media.remote.changeQuality(qualities.indexOf(quality), event);
696
- }
697
- #onValueChange(event) {
698
- this.#throttledQualityChange(event);
699
- }
700
- #onDragValueChange(event) {
701
- this.#throttledQualityChange(event);
702
- }
703
- }
704
-
705
- class SliderChapters extends Component {
706
- static props = {
707
- disabled: false
708
- };
709
- #media;
710
- #sliderState;
711
- #updateScope;
712
- #titleRef = null;
713
- #refs = [];
714
- #$track = signal(null);
715
- #$cues = signal([]);
716
- #activeIndex = signal(-1);
717
- #activePointerIndex = signal(-1);
718
- #bufferedIndex = 0;
719
- get cues() {
720
- return this.#$cues();
721
- }
722
- get activeCue() {
723
- return this.#$cues()[this.#activeIndex()] || null;
724
- }
725
- get activePointerCue() {
726
- return this.#$cues()[this.#activePointerIndex()] || null;
727
- }
728
- onSetup() {
729
- this.#media = useMediaContext();
730
- this.#sliderState = useState(TimeSlider.state);
731
- }
732
- onAttach(el) {
733
- watchActiveTextTrack(this.#media.textTracks, "chapters", this.#setTrack.bind(this));
734
- effect(this.#watchSource.bind(this));
735
- }
736
- onConnect() {
737
- onDispose(() => this.#reset.bind(this));
738
- }
739
- onDestroy() {
740
- this.#setTrack(null);
741
- }
742
- setRefs(refs) {
743
- this.#refs = refs;
744
- this.#updateScope?.dispose();
745
- if (this.#refs.length === 1) {
746
- const el = this.#refs[0];
747
- el.style.width = "100%";
748
- el.style.setProperty("--chapter-fill", "var(--slider-fill)");
749
- el.style.setProperty("--chapter-progress", "var(--slider-progress)");
750
- } else if (this.#refs.length > 0) {
751
- scoped(() => this.#watch(), this.#updateScope = createScope());
752
- }
753
- }
754
- #setTrack(track) {
755
- if (peek(this.#$track) === track) return;
756
- this.#reset();
757
- this.#$track.set(track);
758
- }
759
- #reset() {
760
- this.#refs = [];
761
- this.#$cues.set([]);
762
- this.#activeIndex.set(-1);
763
- this.#activePointerIndex.set(-1);
764
- this.#bufferedIndex = 0;
765
- this.#updateScope?.dispose();
766
- }
767
- #watch() {
768
- if (!this.#refs.length) return;
769
- effect(this.#watchUpdates.bind(this));
770
- }
771
- #watchUpdates() {
772
- const { hidden } = this.#sliderState;
773
- if (hidden()) return;
774
- effect(this.#watchContainerWidths.bind(this));
775
- effect(this.#watchFillPercent.bind(this));
776
- effect(this.#watchPointerPercent.bind(this));
777
- effect(this.#watchBufferedPercent.bind(this));
778
- }
779
- #watchContainerWidths() {
780
- const cues = this.#$cues();
781
- if (!cues.length) return;
782
- let cue, { seekableStart, seekableEnd } = this.#media.$state, startTime = seekableStart(), endTime = seekableEnd() || cues[cues.length - 1].endTime, duration = endTime - startTime, remainingWidth = 100;
783
- for (let i = 0; i < cues.length; i++) {
784
- cue = cues[i];
785
- if (this.#refs[i]) {
786
- const width = i === cues.length - 1 ? remainingWidth : round((cue.endTime - Math.max(startTime, cue.startTime)) / duration * 100, 3);
787
- this.#refs[i].style.width = width + "%";
788
- remainingWidth -= width;
789
- }
790
- }
791
- }
792
- #watchFillPercent() {
793
- let { liveEdge, seekableStart, seekableEnd } = this.#media.$state, { fillPercent, value } = this.#sliderState, cues = this.#$cues(), isLiveEdge = liveEdge(), prevActiveIndex = peek(this.#activeIndex), currentChapter = cues[prevActiveIndex];
794
- let currentActiveIndex = isLiveEdge ? this.#$cues.length - 1 : this.#findActiveChapterIndex(
795
- currentChapter ? currentChapter.startTime / seekableEnd() * 100 <= peek(value) ? prevActiveIndex : 0 : 0,
796
- fillPercent()
797
- );
798
- if (isLiveEdge || !currentChapter) {
799
- this.#updateFillPercents(0, cues.length, 100);
800
- } else if (currentActiveIndex > prevActiveIndex) {
801
- this.#updateFillPercents(prevActiveIndex, currentActiveIndex, 100);
802
- } else if (currentActiveIndex < prevActiveIndex) {
803
- this.#updateFillPercents(currentActiveIndex + 1, prevActiveIndex + 1, 0);
804
- }
805
- const percent = isLiveEdge ? 100 : this.#calcPercent(
806
- cues[currentActiveIndex],
807
- fillPercent(),
808
- seekableStart(),
809
- this.#getEndTime(cues)
810
- );
811
- this.#updateFillPercent(this.#refs[currentActiveIndex], percent);
812
- this.#activeIndex.set(currentActiveIndex);
813
- }
814
- #watchPointerPercent() {
815
- let { hidden, pointerPercent } = this.#sliderState;
816
- if (hidden()) {
817
- this.#activePointerIndex.set(-1);
818
- return;
819
- }
820
- const activeIndex = this.#findActiveChapterIndex(0, pointerPercent());
821
- this.#activePointerIndex.set(activeIndex);
822
- }
823
- #updateFillPercents(start, end, percent) {
824
- for (let i = start; i < end; i++) this.#updateFillPercent(this.#refs[i], percent);
825
- }
826
- #updateFillPercent(ref, percent) {
827
- if (!ref) return;
828
- ref.style.setProperty("--chapter-fill", percent + "%");
829
- setAttribute(ref, "data-active", percent > 0 && percent < 100);
830
- setAttribute(ref, "data-ended", percent === 100);
831
- }
832
- #findActiveChapterIndex(startIndex, percent) {
833
- let chapterPercent = 0, cues = this.#$cues();
834
- if (percent === 0) return 0;
835
- else if (percent === 100) return cues.length - 1;
836
- let { seekableStart } = this.#media.$state, startTime = seekableStart(), endTime = this.#getEndTime(cues);
837
- for (let i = startIndex; i < cues.length; i++) {
838
- chapterPercent = this.#calcPercent(cues[i], percent, startTime, endTime);
839
- if (chapterPercent >= 0 && chapterPercent < 100) return i;
840
- }
841
- return 0;
842
- }
843
- #watchBufferedPercent() {
844
- this.#updateBufferedPercent(this.#bufferedPercent());
845
- }
846
- #updateBufferedPercent = animationFrameThrottle();
847
- #bufferedPercent = computed(this.#calcMediaBufferedPercent.bind(this));
848
- #calcMediaBufferedPercent() {
849
- const { bufferedEnd, duration } = this.#media.$state;
850
- return round(Math.min(bufferedEnd() / Math.max(duration(), 1), 1), 3) * 100;
851
- }
852
- #getEndTime(cues) {
853
- const { seekableEnd } = this.#media.$state, endTime = seekableEnd();
854
- return Number.isFinite(endTime) ? endTime : cues[cues.length - 1]?.endTime || 0;
855
- }
856
- #calcPercent(cue, percent, startTime, endTime) {
857
- if (!cue) return 0;
858
- const cues = this.#$cues();
859
- if (cues.length === 0) return 0;
860
- const duration = endTime - startTime, cueStartTime = Math.max(0, cue.startTime - startTime), cueEndTime = Math.min(endTime, cue.endTime) - startTime;
861
- const startRatio = cueStartTime / duration, startPercent = startRatio * 100, endPercent = Math.min(1, startRatio + (cueEndTime - cueStartTime) / duration) * 100;
862
- return Math.max(
863
- 0,
864
- round(
865
- percent >= endPercent ? 100 : (percent - startPercent) / (endPercent - startPercent) * 100,
866
- 3
867
- )
868
- );
869
- }
870
- #fillGaps(cues) {
871
- let chapters = [], { seekableStart, seekableEnd, duration } = this.#media.$state, startTime = seekableStart(), endTime = seekableEnd();
872
- cues = cues.filter((cue) => cue.startTime <= endTime && cue.endTime >= startTime);
873
- const firstCue = cues[0];
874
- if (firstCue && firstCue.startTime > startTime) {
875
- chapters.push(new window.VTTCue(startTime, firstCue.startTime, ""));
876
- }
877
- for (let i = 0; i < cues.length - 1; i++) {
878
- const currentCue = cues[i], nextCue = cues[i + 1];
879
- chapters.push(currentCue);
880
- if (nextCue) {
881
- const timeDiff = nextCue.startTime - currentCue.endTime;
882
- if (timeDiff > 0) {
883
- chapters.push(new window.VTTCue(currentCue.endTime, currentCue.endTime + timeDiff, ""));
884
- }
885
- }
886
- }
887
- const lastCue = cues[cues.length - 1];
888
- if (lastCue) {
889
- chapters.push(lastCue);
890
- const endTime2 = duration();
891
- if (endTime2 >= 0 && endTime2 - lastCue.endTime > 1) {
892
- chapters.push(new window.VTTCue(lastCue.endTime, duration(), ""));
893
- }
894
- }
895
- return chapters;
896
- }
897
- #watchSource() {
898
- const { source } = this.#media.$state;
899
- source();
900
- this.#onTrackChange();
901
- }
902
- #onTrackChange() {
903
- if (!this.scope) return;
904
- const { disabled } = this.$props;
905
- if (disabled()) {
906
- this.#$cues.set([]);
907
- this.#activeIndex.set(0);
908
- this.#bufferedIndex = 0;
909
- return;
910
- }
911
- const track = this.#$track();
912
- if (track) {
913
- const onCuesChange = this.#onCuesChange.bind(this);
914
- onCuesChange();
915
- new EventsController(track).add("add-cue", onCuesChange).add("remove-cue", onCuesChange);
916
- effect(this.#watchMediaDuration.bind(this));
917
- }
918
- this.#titleRef = this.#findChapterTitleRef();
919
- if (this.#titleRef) effect(this.#onChapterTitleChange.bind(this));
920
- return () => {
921
- if (this.#titleRef) {
922
- this.#titleRef.textContent = "";
923
- this.#titleRef = null;
924
- }
925
- };
926
- }
927
- #watchMediaDuration() {
928
- this.#media.$state.duration();
929
- this.#onCuesChange();
930
- }
931
- #onCuesChange = functionDebounce(
932
- () => {
933
- const track = peek(this.#$track);
934
- if (!this.scope || !track || !track.cues.length) return;
935
- this.#$cues.set(this.#fillGaps(track.cues));
936
- this.#activeIndex.set(0);
937
- this.#bufferedIndex = 0;
938
- },
939
- 150,
940
- true
941
- );
942
- #onChapterTitleChange() {
943
- const cue = this.activePointerCue || this.activeCue;
944
- if (this.#titleRef) this.#titleRef.textContent = cue?.text || "";
945
- }
946
- #findParentSlider() {
947
- let node = this.el;
948
- while (node && node.getAttribute("role") !== "slider") {
949
- node = node.parentElement;
950
- }
951
- return node;
952
- }
953
- #findChapterTitleRef() {
954
- const slider = this.#findParentSlider();
955
- return slider ? slider.querySelector('[data-part="chapter-title"]') : null;
956
- }
957
- }
958
- const sliderchapters__proto = SliderChapters.prototype;
959
- prop(sliderchapters__proto, "cues");
960
- prop(sliderchapters__proto, "activeCue");
961
- prop(sliderchapters__proto, "activePointerCue");
962
- method(sliderchapters__proto, "setRefs");
963
-
964
- class RadioGroup extends Component {
965
- static props = {
966
- value: ""
967
- };
968
- #controller;
969
- /**
970
- * A list of radio values that belong this group.
971
- */
972
- get values() {
973
- return this.#controller.values;
974
- }
975
- /**
976
- * The radio value that is checked in this group.
977
- */
978
- get value() {
979
- return this.#controller.value;
980
- }
981
- set value(newValue) {
982
- this.#controller.value = newValue;
983
- }
984
- constructor() {
985
- super();
986
- this.#controller = new RadioGroupController();
987
- this.#controller.onValueChange = this.#onValueChange.bind(this);
988
- }
989
- onSetup() {
990
- this.#watchValue();
991
- }
992
- #watchValue() {
993
- this.#controller.value = this.$props.value();
994
- }
995
- #onValueChange(value, trigger) {
996
- const event = this.createEvent("change", { detail: value, trigger });
997
- this.dispatch(event);
998
- }
999
- }
1000
- const radiogroup__proto = RadioGroup.prototype;
1001
- prop(radiogroup__proto, "values");
1002
- prop(radiogroup__proto, "value");
1003
-
1004
- var __defProp = Object.defineProperty;
1005
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
1006
- var __decorateClass = (decorators, target, key, kind) => {
1007
- var result = __getOwnPropDesc(target, key) ;
1008
- for (var i = decorators.length - 1, decorator; i >= 0; i--)
1009
- if (decorator = decorators[i])
1010
- result = (decorator(target, key, result) ) || result;
1011
- if (result) __defProp(target, key, result);
1012
- return result;
1013
- };
1014
- class ChaptersRadioGroup extends Component {
1015
- static props = {
1016
- thumbnails: null
1017
- };
1018
- #media;
1019
- #menu;
1020
- #controller;
1021
- #track = signal(null);
1022
- #cues = signal([]);
1023
- get value() {
1024
- return this.#controller.value;
1025
- }
1026
- get disabled() {
1027
- return !this.#cues()?.length;
1028
- }
1029
- constructor() {
1030
- super();
1031
- this.#controller = new RadioGroupController();
1032
- this.#controller.onValueChange = this.#onValueChange.bind(this);
1033
- }
1034
- onSetup() {
1035
- this.#media = useMediaContext();
1036
- if (hasProvidedContext(menuContext)) {
1037
- this.#menu = useContext(menuContext);
1038
- }
1039
- const { thumbnails } = this.$props;
1040
- this.setAttributes({
1041
- "data-thumbnails": () => !!thumbnails()
1042
- });
1043
- }
1044
- onAttach(el) {
1045
- this.#menu?.attachObserver({
1046
- onOpen: this.#onOpen.bind(this)
1047
- });
1048
- }
1049
- getOptions() {
1050
- const { seekableStart, seekableEnd } = this.#media.$state, startTime = seekableStart(), endTime = seekableEnd();
1051
- return this.#cues().map((cue, i) => ({
1052
- cue,
1053
- value: i.toString(),
1054
- label: cue.text,
1055
- startTime: formatTime(Math.max(0, cue.startTime - startTime)),
1056
- duration: formatSpokenTime(
1057
- Math.min(endTime, cue.endTime) - Math.max(startTime, cue.startTime)
1058
- )
1059
- }));
1060
- }
1061
- #onOpen() {
1062
- peek(() => this.#watchCurrentTime());
1063
- }
1064
- onConnect(el) {
1065
- effect(this.#watchCurrentTime.bind(this));
1066
- effect(this.#watchControllerDisabled.bind(this));
1067
- effect(this.#watchTrack.bind(this));
1068
- watchActiveTextTrack(this.#media.textTracks, "chapters", this.#track.set);
1069
- }
1070
- #watchTrack() {
1071
- const track = this.#track();
1072
- if (!track) return;
1073
- const onCuesChange = this.#onCuesChange.bind(this, track);
1074
- onCuesChange();
1075
- new EventsController(track).add("add-cue", onCuesChange).add("remove-cue", onCuesChange);
1076
- return () => {
1077
- this.#cues.set([]);
1078
- };
1079
- }
1080
- #onCuesChange(track) {
1081
- const { seekableStart, seekableEnd } = this.#media.$state, startTime = seekableStart(), endTime = seekableEnd();
1082
- this.#cues.set(
1083
- [...track.cues].filter((cue) => cue.startTime <= endTime && cue.endTime >= startTime)
1084
- );
1085
- }
1086
- #watchCurrentTime() {
1087
- if (!this.#menu?.expanded()) return;
1088
- const track = this.#track();
1089
- if (!track) {
1090
- this.#controller.value = "-1";
1091
- return;
1092
- }
1093
- const { realCurrentTime, seekableStart, seekableEnd } = this.#media.$state, startTime = seekableStart(), endTime = seekableEnd(), time = realCurrentTime(), activeCueIndex = this.#cues().findIndex((cue) => isCueActive(cue, time));
1094
- this.#controller.value = activeCueIndex.toString();
1095
- if (activeCueIndex >= 0) {
1096
- requestScopedAnimationFrame(() => {
1097
- if (!this.connectScope) return;
1098
- const cue = this.#cues()[activeCueIndex], radio = this.el.querySelector(`[aria-checked='true']`), cueStartTime = Math.max(startTime, cue.startTime), duration = Math.min(endTime, cue.endTime) - cueStartTime, playedPercent = Math.max(0, time - cueStartTime) / duration * 100;
1099
- radio && setStyle(radio, "--progress", round(playedPercent, 3) + "%");
1100
- });
1101
- }
1102
- }
1103
- #watchControllerDisabled() {
1104
- this.#menu?.disable(this.disabled);
1105
- }
1106
- #onValueChange(value, trigger) {
1107
- if (this.disabled || !trigger) return;
1108
- const index = +value, cues = this.#cues(), { clipStartTime } = this.#media.$state;
1109
- if (isNumber(index) && cues?.[index]) {
1110
- this.#controller.value = index.toString();
1111
- this.#media.remote.seek(cues[index].startTime - clipStartTime(), trigger);
1112
- this.dispatch("change", { detail: cues[index], trigger });
1113
- }
1114
- }
1115
- }
1116
- __decorateClass([
1117
- prop
1118
- ], ChaptersRadioGroup.prototype, "value");
1119
- __decorateClass([
1120
- prop
1121
- ], ChaptersRadioGroup.prototype, "disabled");
1122
- __decorateClass([
1123
- method
1124
- ], ChaptersRadioGroup.prototype, "getOptions");
1125
-
1126
- const DEFAULT_AUDIO_GAINS = [1, 1.25, 1.5, 1.75, 2, 2.5, 3, 4];
1127
- class AudioGainRadioGroup extends Component {
1128
- static props = {
1129
- normalLabel: "Disabled",
1130
- gains: DEFAULT_AUDIO_GAINS
1131
- };
1132
- #media;
1133
- #menu;
1134
- #controller;
1135
- get value() {
1136
- return this.#controller.value;
1137
- }
1138
- get disabled() {
1139
- const { gains } = this.$props, { canSetAudioGain } = this.#media.$state;
1140
- return !canSetAudioGain() || gains().length === 0;
1141
- }
1142
- constructor() {
1143
- super();
1144
- this.#controller = new RadioGroupController();
1145
- this.#controller.onValueChange = this.#onValueChange.bind(this);
1146
- }
1147
- onSetup() {
1148
- this.#media = useMediaContext();
1149
- if (hasProvidedContext(menuContext)) {
1150
- this.#menu = useContext(menuContext);
1151
- }
1152
- }
1153
- onConnect(el) {
1154
- effect(this.#watchValue.bind(this));
1155
- effect(this.#watchHintText.bind(this));
1156
- effect(this.#watchControllerDisabled.bind(this));
1157
- }
1158
- getOptions() {
1159
- const { gains, normalLabel } = this.$props;
1160
- return gains().map((gain) => ({
1161
- label: gain === 1 || gain === null ? normalLabel : String(gain * 100) + "%",
1162
- value: gain.toString()
1163
- }));
1164
- }
1165
- #watchValue() {
1166
- this.#controller.value = this.#getValue();
1167
- }
1168
- #watchHintText() {
1169
- const { normalLabel } = this.$props, { audioGain } = this.#media.$state, gain = audioGain();
1170
- this.#menu?.hint.set(gain === 1 || gain == null ? normalLabel() : String(gain * 100) + "%");
1171
- }
1172
- #watchControllerDisabled() {
1173
- this.#menu?.disable(this.disabled);
1174
- }
1175
- #getValue() {
1176
- const { audioGain } = this.#media.$state;
1177
- return audioGain()?.toString() ?? "1";
1178
- }
1179
- #onValueChange(value, trigger) {
1180
- if (this.disabled) return;
1181
- const gain = +value;
1182
- this.#media.remote.changeAudioGain(gain, trigger);
1183
- this.dispatch("change", { detail: gain, trigger });
1184
- }
1185
- }
1186
- const audiogainradiogroup__proto = AudioGainRadioGroup.prototype;
1187
- prop(audiogainradiogroup__proto, "value");
1188
- prop(audiogainradiogroup__proto, "disabled");
1189
- method(audiogainradiogroup__proto, "getOptions");
1190
-
1191
- class Gesture extends Component {
1192
- static props = {
1193
- disabled: false,
1194
- event: void 0,
1195
- action: void 0
1196
- };
1197
- #media;
1198
- #provider = null;
1199
- onSetup() {
1200
- this.#media = useMediaContext();
1201
- const { event, action } = this.$props;
1202
- this.setAttributes({
1203
- event,
1204
- action
1205
- });
1206
- }
1207
- onAttach(el) {
1208
- el.setAttribute("data-media-gesture", "");
1209
- el.style.setProperty("pointer-events", "none");
1210
- }
1211
- onConnect(el) {
1212
- this.#provider = this.#media.player.el?.querySelector(
1213
- "[data-media-provider]"
1214
- );
1215
- effect(this.#attachListener.bind(this));
1216
- }
1217
- #attachListener() {
1218
- let eventType = this.$props.event(), disabled = this.$props.disabled();
1219
- if (!this.#provider || !eventType || disabled) return;
1220
- if (/^dbl/.test(eventType)) {
1221
- eventType = eventType.split(/^dbl/)[1];
1222
- }
1223
- if (eventType === "pointerup" || eventType === "pointerdown") {
1224
- const pointer = this.#media.$state.pointer();
1225
- if (pointer === "coarse") {
1226
- eventType = eventType === "pointerup" ? "touchend" : "touchstart";
1227
- }
1228
- }
1229
- listenEvent(
1230
- this.#provider,
1231
- eventType,
1232
- this.#acceptEvent.bind(this));
1233
- }
1234
- #presses = 0;
1235
- #pressTimerId = -1;
1236
- #acceptEvent(event) {
1237
- if (this.$props.disabled() || isPointerEvent(event) && (event.button !== 0 || this.#media.activeMenu) || isTouchEvent(event) && this.#media.activeMenu || isTouchPinchEvent(event) || !this.#inBounds(event)) {
1238
- return;
1239
- }
1240
- event.MEDIA_GESTURE = true;
1241
- event.preventDefault();
1242
- const eventType = peek(this.$props.event), isDblEvent = eventType?.startsWith("dbl");
1243
- if (!isDblEvent) {
1244
- if (this.#presses === 0) {
1245
- setTimeout(() => {
1246
- if (this.#presses === 1) this.#handleEvent(event);
1247
- }, 250);
1248
- }
1249
- } else if (this.#presses === 1) {
1250
- queueMicrotask(() => this.#handleEvent(event));
1251
- clearTimeout(this.#pressTimerId);
1252
- this.#presses = 0;
1253
- return;
1254
- }
1255
- if (this.#presses === 0) {
1256
- this.#pressTimerId = window.setTimeout(() => {
1257
- this.#presses = 0;
1258
- }, 275);
1259
- }
1260
- this.#presses++;
1261
- }
1262
- #handleEvent(event) {
1263
- this.el.setAttribute("data-triggered", "");
1264
- requestAnimationFrame(() => {
1265
- if (this.#isTopLayer()) {
1266
- this.#performAction(peek(this.$props.action), event);
1267
- }
1268
- requestAnimationFrame(() => {
1269
- this.el.removeAttribute("data-triggered");
1270
- });
1271
- });
1272
- }
1273
- /** Validate event occurred in gesture bounds. */
1274
- #inBounds(event) {
1275
- if (!this.el) return false;
1276
- if (isPointerEvent(event) || isMouseEvent(event) || isTouchEvent(event)) {
1277
- const touch = isTouchEvent(event) ? event.changedTouches[0] ?? event.touches[0] : void 0;
1278
- const clientX = touch?.clientX ?? event.clientX;
1279
- const clientY = touch?.clientY ?? event.clientY;
1280
- const rect = this.el.getBoundingClientRect();
1281
- const inBounds = clientY >= rect.top && clientY <= rect.bottom && clientX >= rect.left && clientX <= rect.right;
1282
- return event.type.includes("leave") ? !inBounds : inBounds;
1283
- }
1284
- return true;
1285
- }
1286
- /** Validate gesture has the highest z-index in this triggered group. */
1287
- #isTopLayer() {
1288
- const gestures = this.#media.player.el.querySelectorAll(
1289
- "[data-media-gesture][data-triggered]"
1290
- );
1291
- return Array.from(gestures).sort(
1292
- (a, b) => +getComputedStyle(b).zIndex - +getComputedStyle(a).zIndex
1293
- )[0] === this.el;
1294
- }
1295
- #performAction(action, trigger) {
1296
- if (!action) return;
1297
- const willTriggerEvent = new DOMEvent("will-trigger", {
1298
- detail: action,
1299
- cancelable: true,
1300
- trigger
1301
- });
1302
- this.dispatchEvent(willTriggerEvent);
1303
- if (willTriggerEvent.defaultPrevented) return;
1304
- const [method, value] = action.replace(/:([a-z])/, "-$1").split(":");
1305
- if (action.includes(":fullscreen")) {
1306
- this.#media.remote.toggleFullscreen("prefer-media", trigger);
1307
- } else if (action.includes("seek:")) {
1308
- this.#media.remote.seek(peek(this.#media.$state.currentTime) + (+value || 0), trigger);
1309
- } else {
1310
- this.#media.remote[kebabToCamelCase(method)](trigger);
1311
- }
1312
- this.dispatch("trigger", {
1313
- detail: action,
1314
- trigger
1315
- });
1316
- }
1317
- }
1318
-
1319
- class CaptionsTextRenderer {
1320
- priority = 10;
1321
- #track = null;
1322
- #renderer;
1323
- #events;
1324
- constructor(renderer) {
1325
- this.#renderer = renderer;
1326
- }
1327
- attach() {
1328
- }
1329
- canRender() {
1330
- return true;
1331
- }
1332
- detach() {
1333
- this.#events?.abort();
1334
- this.#events = void 0;
1335
- this.#renderer.reset();
1336
- this.#track = null;
1337
- }
1338
- changeTrack(track) {
1339
- if (!track || this.#track === track) return;
1340
- this.#events?.abort();
1341
- this.#events = new EventsController(track);
1342
- if (track.readyState < 2) {
1343
- this.#renderer.reset();
1344
- this.#events.add("load", () => this.#changeTrack(track), { once: true });
1345
- } else {
1346
- this.#changeTrack(track);
1347
- }
1348
- this.#events.add("add-cue", (event) => {
1349
- this.#renderer.addCue(event.detail);
1350
- }).add("remove-cue", (event) => {
1351
- this.#renderer.removeCue(event.detail);
1352
- });
1353
- this.#track = track;
1354
- }
1355
- #changeTrack(track) {
1356
- this.#renderer.changeTrack({
1357
- cues: [...track.cues],
1358
- regions: [...track.regions]
1359
- });
1360
- }
1361
- }
1362
-
1363
- class Captions extends Component {
1364
- static props = {
1365
- textDir: "ltr",
1366
- exampleText: "Captions look like this."
1367
- };
1368
- #media;
1369
- static lib = signal(null);
1370
- onSetup() {
1371
- this.#media = useMediaContext();
1372
- this.setAttributes({
1373
- "aria-hidden": $ariaBool(this.#isHidden.bind(this))
1374
- });
1375
- }
1376
- onAttach(el) {
1377
- el.style.setProperty("pointer-events", "none");
1378
- }
1379
- onConnect(el) {
1380
- if (!Captions.lib()) {
1381
- import('media-captions').then((lib) => Captions.lib.set(lib));
1382
- }
1383
- effect(this.#watchViewType.bind(this));
1384
- }
1385
- #isHidden() {
1386
- const { textTrack, remotePlaybackState, iOSControls } = this.#media.$state, track = textTrack();
1387
- return iOSControls() || remotePlaybackState() === "connected" || !track || !isTrackCaptionKind(track);
1388
- }
1389
- #watchViewType() {
1390
- if (!Captions.lib()) return;
1391
- const { viewType } = this.#media.$state;
1392
- if (viewType() === "audio") {
1393
- return this.#setupAudioView();
1394
- } else {
1395
- return this.#setupVideoView();
1396
- }
1397
- }
1398
- #setupAudioView() {
1399
- effect(this.#onTrackChange.bind(this));
1400
- this.#listenToFontStyleChanges(null);
1401
- return () => {
1402
- this.el.textContent = "";
1403
- };
1404
- }
1405
- #onTrackChange() {
1406
- if (this.#isHidden()) return;
1407
- this.#onCueChange();
1408
- const { textTrack } = this.#media.$state;
1409
- listenEvent(textTrack(), "cue-change", this.#onCueChange.bind(this));
1410
- effect(this.#onUpdateTimedNodes.bind(this));
1411
- }
1412
- #onCueChange() {
1413
- this.el.textContent = "";
1414
- if (this.#hideExampleTimer >= 0) {
1415
- this.#removeExample();
1416
- }
1417
- const { realCurrentTime, textTrack } = this.#media.$state, { renderVTTCueString } = Captions.lib(), time = peek(realCurrentTime), activeCues = peek(textTrack).activeCues;
1418
- for (const cue of activeCues) {
1419
- const displayEl = this.#createCueDisplayElement(), cueEl = this.#createCueElement();
1420
- cueEl.innerHTML = renderVTTCueString(cue, time);
1421
- displayEl.append(cueEl);
1422
- this.el.append(cueEl);
1423
- }
1424
- }
1425
- #onUpdateTimedNodes() {
1426
- const { realCurrentTime } = this.#media.$state, { updateTimedVTTCueNodes } = Captions.lib();
1427
- updateTimedVTTCueNodes(this.el, realCurrentTime());
1428
- }
1429
- #setupVideoView() {
1430
- const { CaptionsRenderer } = Captions.lib(), renderer = new CaptionsRenderer(this.el), textRenderer = new CaptionsTextRenderer(renderer);
1431
- this.#media.textRenderers.add(textRenderer);
1432
- effect(this.#watchTextDirection.bind(this, renderer));
1433
- effect(this.#watchMediaTime.bind(this, renderer));
1434
- this.#listenToFontStyleChanges(renderer);
1435
- return () => {
1436
- this.el.textContent = "";
1437
- this.#media.textRenderers.remove(textRenderer);
1438
- renderer.destroy();
1439
- };
1440
- }
1441
- #watchTextDirection(renderer) {
1442
- renderer.dir = this.$props.textDir();
1443
- }
1444
- #watchMediaTime(renderer) {
1445
- if (this.#isHidden()) return;
1446
- const { realCurrentTime, textTrack } = this.#media.$state;
1447
- renderer.currentTime = realCurrentTime();
1448
- if (this.#hideExampleTimer >= 0 && textTrack()?.activeCues[0]) {
1449
- this.#removeExample();
1450
- }
1451
- }
1452
- #listenToFontStyleChanges(renderer) {
1453
- const player = this.#media.player;
1454
- if (!player) return;
1455
- this.#onFontStyleChange.bind(this, renderer);
1456
- }
1457
- #onFontStyleChange(renderer) {
1458
- if (this.#hideExampleTimer >= 0) {
1459
- this.#hideExample();
1460
- return;
1461
- }
1462
- const { textTrack } = this.#media.$state;
1463
- if (!textTrack()?.activeCues[0]) {
1464
- this.#showExample();
1465
- } else {
1466
- renderer?.update(true);
1467
- }
1468
- }
1469
- #showExample() {
1470
- const display = this.#createCueDisplayElement();
1471
- setAttribute(display, "data-example", "");
1472
- const cue = this.#createCueElement();
1473
- setAttribute(cue, "data-example", "");
1474
- cue.textContent = this.$props.exampleText();
1475
- display?.append(cue);
1476
- this.el?.append(display);
1477
- this.el?.setAttribute("data-example", "");
1478
- this.#hideExample();
1479
- }
1480
- #hideExampleTimer = -1;
1481
- #hideExample() {
1482
- window.clearTimeout(this.#hideExampleTimer);
1483
- this.#hideExampleTimer = window.setTimeout(this.#removeExample.bind(this), 2500);
1484
- }
1485
- #removeExample() {
1486
- this.el?.removeAttribute("data-example");
1487
- if (this.el?.querySelector("[data-example]")) this.el.textContent = "";
1488
- this.#hideExampleTimer = -1;
1489
- }
1490
- #createCueDisplayElement() {
1491
- const el = document.createElement("div");
1492
- setAttribute(el, "data-part", "cue-display");
1493
- return el;
1494
- }
1495
- #createCueElement() {
1496
- const el = document.createElement("div");
1497
- setAttribute(el, "data-part", "cue");
1498
- return el;
1499
- }
1500
- }
1501
-
1502
- export { AudioGainRadioGroup, AudioGainSlider, Captions, ChaptersRadioGroup, Controls, ControlsGroup, DEFAULT_AUDIO_GAINS, Gesture, GoogleCastButton, MediaAnnouncer, QualitySlider, RadioGroup, SliderChapters, SliderVideo, SpeedSlider, ToggleButton, Tooltip, TooltipContent, TooltipTrigger };