@mango-iiif/av 0.4.0 → 1.0.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 (69) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +24 -17
  3. package/dist/annotations.d.ts +2 -2
  4. package/dist/annotations.js +4 -4
  5. package/dist/captions.d.ts +23 -0
  6. package/dist/captions.js +16 -0
  7. package/dist/captions.js.map +1 -0
  8. package/dist/chapters.d.ts +2 -2
  9. package/dist/chapters.js +3 -3
  10. package/dist/{chunk-4NOWGMI5.js → chunk-53ZN4Y4L.js} +28 -66
  11. package/dist/chunk-53ZN4Y4L.js.map +1 -0
  12. package/dist/chunk-AWLDINQR.js +73 -0
  13. package/dist/chunk-AWLDINQR.js.map +1 -0
  14. package/dist/chunk-BB7RE7EO.js +407 -0
  15. package/dist/chunk-BB7RE7EO.js.map +1 -0
  16. package/dist/chunk-BMMQFCD3.js +86 -0
  17. package/dist/chunk-BMMQFCD3.js.map +1 -0
  18. package/dist/chunk-IZYEGSGT.js +148 -0
  19. package/dist/chunk-IZYEGSGT.js.map +1 -0
  20. package/dist/chunk-JXZR5DL2.js +160 -0
  21. package/dist/chunk-JXZR5DL2.js.map +1 -0
  22. package/dist/{chunk-CIAQZ2EB.js → chunk-LMZIFVHN.js} +8 -4
  23. package/dist/chunk-LMZIFVHN.js.map +1 -0
  24. package/dist/chunk-TQNEVWUH.js +39 -0
  25. package/dist/chunk-TQNEVWUH.js.map +1 -0
  26. package/dist/chunk-XXM4G3K5.js +296 -0
  27. package/dist/chunk-XXM4G3K5.js.map +1 -0
  28. package/dist/chunk-YVI7J43Q.js +29 -0
  29. package/dist/chunk-YVI7J43Q.js.map +1 -0
  30. package/dist/chunk-Z2OF3RKT.js +152 -0
  31. package/dist/chunk-Z2OF3RKT.js.map +1 -0
  32. package/dist/{controller-SkwGkVE8.d.ts → controller-Ag3nFtwP.d.ts} +16 -24
  33. package/dist/{controller-element-CkXIA6w2.d.ts → controller-element-Cn5cxKnD.d.ts} +4 -2
  34. package/dist/core.d.ts +2 -2
  35. package/dist/core.js +2 -2
  36. package/dist/index.d.ts +6 -3
  37. package/dist/index.js +21 -14
  38. package/dist/index.js.map +1 -1
  39. package/dist/media.d.ts +3 -3
  40. package/dist/media.js +11 -5
  41. package/dist/media.js.map +1 -1
  42. package/dist/player.d.ts +8 -2
  43. package/dist/player.js +3 -5
  44. package/dist/player.js.map +1 -1
  45. package/dist/ramp-2XKALEGA.js +98 -0
  46. package/dist/ramp-2XKALEGA.js.map +1 -0
  47. package/dist/transcript-element.d.ts +2 -2
  48. package/dist/transcript-element.js +6 -5
  49. package/dist/transcript-element.js.map +1 -1
  50. package/docs/API.md +70 -45
  51. package/package.json +18 -11
  52. package/dist/chunk-4NOWGMI5.js.map +0 -1
  53. package/dist/chunk-BAE4FKBJ.js +0 -645
  54. package/dist/chunk-BAE4FKBJ.js.map +0 -1
  55. package/dist/chunk-C4H46HDS.js +0 -95
  56. package/dist/chunk-C4H46HDS.js.map +0 -1
  57. package/dist/chunk-CDLYT3KB.js +0 -17
  58. package/dist/chunk-CDLYT3KB.js.map +0 -1
  59. package/dist/chunk-CIAQZ2EB.js.map +0 -1
  60. package/dist/chunk-EHIKFRFV.js +0 -75
  61. package/dist/chunk-EHIKFRFV.js.map +0 -1
  62. package/dist/chunk-FVYEV2LO.js +0 -163
  63. package/dist/chunk-FVYEV2LO.js.map +0 -1
  64. package/dist/chunk-GQD675FA.js +0 -289
  65. package/dist/chunk-GQD675FA.js.map +0 -1
  66. package/dist/chunk-M5ZZRBGD.js +0 -357
  67. package/dist/chunk-M5ZZRBGD.js.map +0 -1
  68. package/dist/chunk-YX32ZQPR.js +0 -83
  69. package/dist/chunk-YX32ZQPR.js.map +0 -1
@@ -0,0 +1,296 @@
1
+ import {
2
+ MangoAVAnnotationsElement,
3
+ defineMangoAVAnnotations
4
+ } from "./chunk-AWLDINQR.js";
5
+ import {
6
+ MangoAVCaptionsElement,
7
+ defineMangoAVCaptions
8
+ } from "./chunk-IZYEGSGT.js";
9
+ import {
10
+ MangoAVChaptersElement,
11
+ defineMangoAVChapters
12
+ } from "./chunk-BMMQFCD3.js";
13
+ import {
14
+ MangoAVPlayerElement,
15
+ defineMangoAVPlayer
16
+ } from "./chunk-BB7RE7EO.js";
17
+ import {
18
+ MangoAVTranscriptElement,
19
+ defineMangoAVTranscript
20
+ } from "./chunk-Z2OF3RKT.js";
21
+ import {
22
+ HTMLElementBase,
23
+ componentTheme,
24
+ controllerEventTypes,
25
+ panelStyles
26
+ } from "./chunk-JXZR5DL2.js";
27
+ import {
28
+ AVPlayerController,
29
+ escapeHtml,
30
+ formatTime,
31
+ resolveStrings
32
+ } from "./chunk-53ZN4Y4L.js";
33
+
34
+ // src/components/mango-av-media.ts
35
+ var tags = () => ({
36
+ player: MangoAVPlayerElement.registeredTag ?? "mango-av-player",
37
+ transcript: MangoAVTranscriptElement.registeredTag ?? "mango-av-transcript",
38
+ chapters: MangoAVChaptersElement.registeredTag ?? "mango-av-chapters",
39
+ annotations: MangoAVAnnotationsElement.registeredTag ?? "mango-av-annotations",
40
+ captions: MangoAVCaptionsElement.registeredTag ?? "mango-av-captions"
41
+ });
42
+ var inheritedTokens = ["accent", "accent-contrast", "accent-tint", "background", "surface", "text", "muted", "border", "danger", "radius", "font"].map((token) => `--mango-av-${token}: inherit;`).join(" ");
43
+ var styles = `${componentTheme}${panelStyles}
44
+ .shell {
45
+ background: var(--mango-av-background);
46
+ border: 1px solid var(--mango-av-border);
47
+ border-radius: var(--mango-av-radius);
48
+ display: flex;
49
+ flex-direction: column;
50
+ gap: 12px;
51
+ padding: 16px 20px;
52
+ }
53
+ .header h2 { font-size: 1.15rem; margin: 0; }
54
+ .header p { color: var(--mango-av-muted); margin: .35rem 0 0; }
55
+ .embedded { ${inheritedTokens} --mango-av-panel-background: transparent; --mango-av-panel-border: 0; --mango-av-panel-padding: 0; }
56
+ .position { font-size: 14px; margin: 0; }
57
+ .position span { color: var(--mango-av-muted); }
58
+ .tabs { background: var(--mango-av-surface); border-radius: 8px; display: flex; gap: 4px; overflow-x: auto; padding: 4px; }
59
+ .tab {
60
+ background: transparent;
61
+ border: 0;
62
+ border-radius: 6px;
63
+ color: var(--mango-av-muted);
64
+ flex: 1 0 auto;
65
+ font-size: 14px;
66
+ font-weight: 600;
67
+ height: 34px;
68
+ padding: 0 12px;
69
+ }
70
+ .tab[aria-selected=true] { background: var(--mango-av-background); color: var(--mango-av-text); }
71
+ .details { min-height: 200px; }
72
+ .details > [hidden] { display: none; }
73
+ .canvas-label { font-size: 1rem; margin: 0 0 .5rem; }
74
+ dl { font-size: 14px; margin: 0; }
75
+ dt { font-weight: 700; margin-top: .75rem; }
76
+ dd { color: var(--mango-av-muted); margin: .2rem 0 0; }
77
+ .files { font-size: 14px; list-style: none; margin: 0; padding: 0; }
78
+ .files li { border-bottom: 1px solid var(--mango-av-border); padding: .55rem 0; }
79
+ .files small { color: var(--mango-av-muted); display: block; }
80
+ a { color: var(--mango-av-accent); }
81
+ `;
82
+ var metadataHtml = (entries) => `<dl>${entries.map((entry) => `
83
+ <dt>${escapeHtml(entry.label)}</dt>
84
+ <dd>${entry.values.map((value) => escapeHtml(value.value)).join("<br>")}</dd>
85
+ `).join("")}</dl>`;
86
+ var metadataPanelHtml = (manifest, canvas, ui) => {
87
+ const entries = [...manifest.metadata, ...canvas.metadata];
88
+ const statement = manifest.requiredStatement ? [manifest.requiredStatement] : [];
89
+ if (entries.length === 0 && statement.length === 0 && !manifest.rights) return `<p class="empty">${escapeHtml(ui.noMetadata)}</p>`;
90
+ return `<h3 class="canvas-label">${escapeHtml(canvas.label)}</h3>
91
+ ${metadataHtml([...entries, ...statement])}
92
+ ${manifest.rights ? `<p><strong>${escapeHtml(ui.rights)}:</strong> <a href="${escapeHtml(manifest.rights)}" target="_blank" rel="noreferrer">${escapeHtml(manifest.rights)}</a></p>` : ""}`;
93
+ };
94
+ var filesPanelHtml = (manifest, canvas, ui) => {
95
+ const files = [...manifest.rendering, ...canvas.rendering];
96
+ if (files.length === 0) return `<p class="empty">${escapeHtml(ui.noSupplementalFiles)}</p>`;
97
+ return `<ul class="files">${files.map((file) => `<li>
98
+ <a href="${escapeHtml(file.id)}" download="${escapeHtml(file.filename ?? "")}">${escapeHtml(file.label)}</a>
99
+ ${file.format ? `<small>${escapeHtml(file.format)}</small>` : ""}
100
+ </li>`).join("")}</ul>`;
101
+ };
102
+ var MangoAVMediaElement = class extends HTMLElementBase {
103
+ static observedAttributes = ["manifest-url", "config"];
104
+ player;
105
+ #root;
106
+ #inputManifest;
107
+ #config = {};
108
+ #activePanel = "transcript";
109
+ constructor() {
110
+ super();
111
+ this.#root = this.attachShadow({ mode: "open" });
112
+ this.player = new AVPlayerController();
113
+ for (const type of controllerEventTypes) {
114
+ this.player.addEventListener(type, ((event) => {
115
+ if (type === "av-ready" || type === "av-configchange") {
116
+ this.#activePanel = this.player.config.activePanel;
117
+ this.#render();
118
+ } else if (type === "av-canvaschange") {
119
+ this.#renderDetails();
120
+ } else if (type === "av-statechange") {
121
+ this.#renderPosition();
122
+ }
123
+ this.dispatchEvent(new CustomEvent(type, {
124
+ detail: event.detail,
125
+ bubbles: true,
126
+ composed: true
127
+ }));
128
+ }));
129
+ }
130
+ }
131
+ get manifest() {
132
+ return this.#inputManifest;
133
+ }
134
+ set manifest(value) {
135
+ this.#inputManifest = value;
136
+ if (this.isConnected && value !== void 0) void this.load(value);
137
+ }
138
+ get controller() {
139
+ return this.player;
140
+ }
141
+ get config() {
142
+ return this.#config;
143
+ }
144
+ set config(value) {
145
+ this.#config = value;
146
+ this.player.configure(value);
147
+ }
148
+ connectedCallback() {
149
+ this.#readConfigAttribute();
150
+ this.#render();
151
+ const source = this.#inputManifest ?? this.getAttribute("manifest-url") ?? void 0;
152
+ if (source !== void 0 && !this.player.manifest) void this.load(source);
153
+ }
154
+ disconnectedCallback() {
155
+ this.player.destroy();
156
+ }
157
+ attributeChangedCallback(name, oldValue, newValue) {
158
+ if (!this.isConnected || oldValue === newValue) return;
159
+ if (name === "manifest-url" && newValue) {
160
+ this.#inputManifest = newValue;
161
+ void this.load(newValue);
162
+ }
163
+ if (name === "config") this.#readConfigAttribute();
164
+ }
165
+ async load(input) {
166
+ this.#inputManifest = input;
167
+ return await this.player.load(input);
168
+ }
169
+ play() {
170
+ return this.player.play();
171
+ }
172
+ pause() {
173
+ this.player.pause();
174
+ }
175
+ seekTo(time) {
176
+ this.player.seekTo(time);
177
+ }
178
+ selectCanvas(indexOrId, options) {
179
+ return this.player.selectCanvas(indexOrId, options);
180
+ }
181
+ #readConfigAttribute() {
182
+ const attribute = this.getAttribute("config");
183
+ if (!attribute) return;
184
+ try {
185
+ this.#config = JSON.parse(attribute);
186
+ this.player.configure(this.#config);
187
+ } catch (cause) {
188
+ const error = cause instanceof Error ? cause : new Error(String(cause));
189
+ this.dispatchEvent(new CustomEvent("av-error", { detail: { error }, bubbles: true, composed: true }));
190
+ }
191
+ }
192
+ #panels(ui) {
193
+ const panels = this.player.config.panels;
194
+ return [
195
+ panels.transcript ? ["transcript", ui.transcript] : void 0,
196
+ panels.chapters ? ["chapters", ui.chapters] : void 0,
197
+ panels.annotations ? ["annotations", ui.annotations] : void 0,
198
+ panels.metadata ? ["metadata", ui.metadata] : void 0,
199
+ panels.supplementalFiles ? ["files", ui.files] : void 0
200
+ ].filter((value) => Boolean(value));
201
+ }
202
+ /* Rebuilt for a new Manifest or configuration; the components inside follow the controller from there. */
203
+ #render() {
204
+ const manifest = this.player.manifest;
205
+ const ui = resolveStrings(this.player.config.strings);
206
+ const panels = this.#panels(ui);
207
+ if (!panels.some(([id]) => id === this.#activePanel) && this.#activePanel !== "none") this.#activePanel = panels[0]?.[0] ?? "none";
208
+ const showPanels = this.#activePanel !== "none" && panels.length > 0;
209
+ const hiddenHeading = '<span slot="heading" hidden></span>';
210
+ const tag = tags();
211
+ const captions = this.player.config.audioArt.captions;
212
+ this.#root.innerHTML = `<style>${styles}</style><div class="shell">
213
+ <slot name="header">
214
+ ${manifest ? `<header class="header"><h2>${escapeHtml(manifest.label)}</h2>${manifest.summary ? `<p>${escapeHtml(manifest.summary)}</p>` : ""}</header>` : ""}
215
+ </slot>
216
+ <slot name="before-player"></slot>
217
+ <${tag.player} class="embedded" data-component>${captions ? `<${tag.captions} class="embedded" data-component slot="stage" audio-only ${captions === "line" ? "line" : ""}></${tag.captions}>` : ""}</${tag.player}>
218
+ <slot name="after-player"></slot>
219
+ ${manifest ? '<p class="position" aria-live="off"></p>' : ""}
220
+ ${manifest && showPanels ? `
221
+ <div class="tabs" role="tablist" aria-label="${escapeHtml(ui.mediaInformation)}">
222
+ ${panels.map(([id, text]) => `<button class="tab" type="button" role="tab" id="tab-${id}" aria-controls="panel-${id}" data-panel="${id}" aria-selected="${this.#activePanel === id}">${escapeHtml(text)}</button>`).join("")}
223
+ </div>
224
+ <div class="details">
225
+ ${panels.map(([id]) => {
226
+ const attributes = `id="panel-${id}" role="tabpanel" aria-labelledby="tab-${id}" data-panel-body="${id}" ${this.#activePanel === id ? "" : "hidden"}`;
227
+ switch (id) {
228
+ case "transcript":
229
+ case "chapters":
230
+ case "annotations":
231
+ return `<${tag[id]} class="embedded" data-component ${attributes}>${hiddenHeading}</${tag[id]}>`;
232
+ default:
233
+ return `<div ${attributes}></div>`;
234
+ }
235
+ }).join("")}
236
+ </div>` : ""}
237
+ </div>`;
238
+ const player = this.#root.querySelector(tag.player);
239
+ if (player) player.forwardEvents = false;
240
+ this.#root.querySelectorAll("[data-component]").forEach((element) => {
241
+ element.controller = this.player;
242
+ });
243
+ this.#root.querySelectorAll("[data-panel]").forEach((tab) => tab.addEventListener("click", () => this.#selectPanel(tab.dataset.panel)));
244
+ this.#root.querySelector(".tabs")?.addEventListener("keydown", (event) => this.#moveTab(event));
245
+ this.#renderDetails();
246
+ this.#renderPosition();
247
+ }
248
+ #selectPanel(panel) {
249
+ this.#activePanel = panel;
250
+ this.#root.querySelectorAll("[data-panel]").forEach((tab) => tab.setAttribute("aria-selected", String(tab.dataset.panel === panel)));
251
+ this.#root.querySelectorAll("[data-panel-body]").forEach((body) => {
252
+ body.hidden = body.dataset.panelBody !== panel;
253
+ });
254
+ }
255
+ #moveTab(event) {
256
+ if (event.key !== "ArrowLeft" && event.key !== "ArrowRight") return;
257
+ const tabs = [...this.#root.querySelectorAll("[data-panel]")];
258
+ const index = tabs.findIndex((tab) => tab.dataset.panel === this.#activePanel);
259
+ const next = tabs[(index + (event.key === "ArrowRight" ? 1 : -1) + tabs.length) % tabs.length];
260
+ if (!next) return;
261
+ this.#selectPanel(next.dataset.panel);
262
+ next.focus();
263
+ }
264
+ #renderDetails() {
265
+ const manifest = this.player.manifest;
266
+ const canvas = this.player.canvas;
267
+ const ui = resolveStrings(this.player.config.strings);
268
+ const metadata = this.#root.querySelector("[data-panel-body=metadata]");
269
+ const files = this.#root.querySelector("[data-panel-body=files]");
270
+ if (metadata) metadata.innerHTML = manifest && canvas ? metadataPanelHtml(manifest, canvas, ui) : "";
271
+ if (files) files.innerHTML = manifest && canvas ? filesPanelHtml(manifest, canvas, ui) : "";
272
+ }
273
+ #renderPosition() {
274
+ const position = this.#root.querySelector(".position");
275
+ if (!position) return;
276
+ const ui = resolveStrings(this.player.config.strings);
277
+ const state = this.player.state;
278
+ position.innerHTML = `${escapeHtml(ui.position)} <strong>${formatTime(state.currentTime)}</strong> <span>/ ${formatTime(state.duration)}</span>`;
279
+ }
280
+ };
281
+ var defineMangoAVMedia = (tagName = "mango-av-media") => {
282
+ defineMangoAVPlayer();
283
+ defineMangoAVChapters();
284
+ defineMangoAVTranscript();
285
+ defineMangoAVAnnotations();
286
+ defineMangoAVCaptions();
287
+ if (typeof customElements === "undefined") return MangoAVMediaElement;
288
+ if (!customElements.get(tagName)) customElements.define(tagName, MangoAVMediaElement);
289
+ return MangoAVMediaElement;
290
+ };
291
+
292
+ export {
293
+ MangoAVMediaElement,
294
+ defineMangoAVMedia
295
+ };
296
+ //# sourceMappingURL=chunk-XXM4G3K5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/mango-av-media.ts"],"sourcesContent":["import { AVPlayerController } from '../controller';\nimport { componentTheme, panelStyles } from './component-styles';\nimport { defineMangoAVPlayer, MangoAVPlayerElement } from './mango-av-player';\nimport { defineMangoAVChapters, MangoAVChaptersElement } from './mango-av-chapters';\nimport { defineMangoAVTranscript, MangoAVTranscriptElement } from './mango-av-transcript';\nimport { defineMangoAVAnnotations, MangoAVAnnotationsElement } from './mango-av-annotations';\nimport { defineMangoAVCaptions, MangoAVCaptionsElement } from './mango-av-captions';\nimport type { AVControllerElement } from './controller-element';\nimport { HTMLElementBase, controllerEventTypes } from './controller-element';\nimport type {\n AVCanvas,\n AVManifest,\n AVPlayerConfig,\n AVStrings,\n JsonObject,\n MetadataEntry,\n} from '../types';\nimport { escapeHtml, formatTime, resolveStrings } from '../utils';\n\ntype Panel = NonNullable<AVPlayerConfig['activePanel']>;\n\n/* The components are rendered under whatever tags they were defined with. */\nconst tags = () => ({\n player: MangoAVPlayerElement.registeredTag ?? 'mango-av-player',\n transcript: MangoAVTranscriptElement.registeredTag ?? 'mango-av-transcript',\n chapters: MangoAVChaptersElement.registeredTag ?? 'mango-av-chapters',\n annotations: MangoAVAnnotationsElement.registeredTag ?? 'mango-av-annotations',\n captions: MangoAVCaptionsElement.registeredTag ?? 'mango-av-captions',\n});\n\n/* The theme set on this element reaches the components inside it, over their own defaults. */\nconst inheritedTokens = ['accent', 'accent-contrast', 'accent-tint', 'background', 'surface', 'text', 'muted', 'border', 'danger', 'radius', 'font']\n .map((token) => `--mango-av-${token}: inherit;`)\n .join(' ');\n\n/*\n * The player, its position, and the recording's transcript, chapters and\n * annotations underneath it in tabs. The panels keep their size order: the\n * player and tabs never shrink, and the active list scrolls on its own.\n */\nconst styles = `${componentTheme}${panelStyles}\n .shell {\n background: var(--mango-av-background);\n border: 1px solid var(--mango-av-border);\n border-radius: var(--mango-av-radius);\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 16px 20px;\n }\n .header h2 { font-size: 1.15rem; margin: 0; }\n .header p { color: var(--mango-av-muted); margin: .35rem 0 0; }\n .embedded { ${inheritedTokens} --mango-av-panel-background: transparent; --mango-av-panel-border: 0; --mango-av-panel-padding: 0; }\n .position { font-size: 14px; margin: 0; }\n .position span { color: var(--mango-av-muted); }\n .tabs { background: var(--mango-av-surface); border-radius: 8px; display: flex; gap: 4px; overflow-x: auto; padding: 4px; }\n .tab {\n background: transparent;\n border: 0;\n border-radius: 6px;\n color: var(--mango-av-muted);\n flex: 1 0 auto;\n font-size: 14px;\n font-weight: 600;\n height: 34px;\n padding: 0 12px;\n }\n .tab[aria-selected=true] { background: var(--mango-av-background); color: var(--mango-av-text); }\n .details { min-height: 200px; }\n .details > [hidden] { display: none; }\n .canvas-label { font-size: 1rem; margin: 0 0 .5rem; }\n dl { font-size: 14px; margin: 0; }\n dt { font-weight: 700; margin-top: .75rem; }\n dd { color: var(--mango-av-muted); margin: .2rem 0 0; }\n .files { font-size: 14px; list-style: none; margin: 0; padding: 0; }\n .files li { border-bottom: 1px solid var(--mango-av-border); padding: .55rem 0; }\n .files small { color: var(--mango-av-muted); display: block; }\n a { color: var(--mango-av-accent); }\n`;\n\nconst metadataHtml = (entries: MetadataEntry[]): string => `<dl>${entries.map((entry) => `\n <dt>${escapeHtml(entry.label)}</dt>\n <dd>${entry.values.map((value) => escapeHtml(value.value)).join('<br>')}</dd>\n`).join('')}</dl>`;\n\nconst metadataPanelHtml = (manifest: AVManifest, canvas: AVCanvas, ui: AVStrings): string => {\n const entries = [...manifest.metadata, ...canvas.metadata];\n const statement = manifest.requiredStatement ? [manifest.requiredStatement] : [];\n if (entries.length === 0 && statement.length === 0 && !manifest.rights) return `<p class=\"empty\">${escapeHtml(ui.noMetadata)}</p>`;\n return `<h3 class=\"canvas-label\">${escapeHtml(canvas.label)}</h3>\n ${metadataHtml([...entries, ...statement])}\n ${manifest.rights ? `<p><strong>${escapeHtml(ui.rights)}:</strong> <a href=\"${escapeHtml(manifest.rights)}\" target=\"_blank\" rel=\"noreferrer\">${escapeHtml(manifest.rights)}</a></p>` : ''}`;\n};\n\nconst filesPanelHtml = (manifest: AVManifest, canvas: AVCanvas, ui: AVStrings): string => {\n const files = [...manifest.rendering, ...canvas.rendering];\n if (files.length === 0) return `<p class=\"empty\">${escapeHtml(ui.noSupplementalFiles)}</p>`;\n return `<ul class=\"files\">${files.map((file) => `<li>\n <a href=\"${escapeHtml(file.id)}\" download=\"${escapeHtml(file.filename ?? '')}\">${escapeHtml(file.label)}</a>\n ${file.format ? `<small>${escapeHtml(file.format)}</small>` : ''}\n </li>`).join('')}</ul>`;\n};\n\nexport class MangoAVMediaElement extends HTMLElementBase {\n static observedAttributes = ['manifest-url', 'config'];\n\n readonly player: AVPlayerController;\n #root: ShadowRoot;\n #inputManifest: JsonObject | string | undefined;\n #config: AVPlayerConfig = {};\n #activePanel: Panel = 'transcript';\n\n constructor() {\n super();\n this.#root = this.attachShadow({ mode: 'open' });\n this.player = new AVPlayerController();\n for (const type of controllerEventTypes) {\n this.player.addEventListener(type, ((event: CustomEvent) => {\n if (type === 'av-ready' || type === 'av-configchange') {\n this.#activePanel = this.player.config.activePanel;\n this.#render();\n } else if (type === 'av-canvaschange') {\n this.#renderDetails();\n } else if (type === 'av-statechange') {\n this.#renderPosition();\n }\n this.dispatchEvent(new CustomEvent(type, {\n detail: event.detail,\n bubbles: true,\n composed: true,\n }));\n }) as EventListener);\n }\n }\n\n get manifest(): JsonObject | string | undefined {\n return this.#inputManifest;\n }\n\n set manifest(value: JsonObject | string | undefined) {\n this.#inputManifest = value;\n if (this.isConnected && value !== undefined) void this.load(value);\n }\n\n get controller(): AVPlayerController {\n return this.player;\n }\n\n get config(): AVPlayerConfig {\n return this.#config;\n }\n\n set config(value: AVPlayerConfig) {\n this.#config = value;\n this.player.configure(value);\n }\n\n connectedCallback(): void {\n this.#readConfigAttribute();\n this.#render();\n const source = this.#inputManifest ?? this.getAttribute('manifest-url') ?? undefined;\n if (source !== undefined && !this.player.manifest) void this.load(source);\n }\n\n disconnectedCallback(): void {\n this.player.destroy();\n }\n\n attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {\n if (!this.isConnected || oldValue === newValue) return;\n if (name === 'manifest-url' && newValue) {\n this.#inputManifest = newValue;\n void this.load(newValue);\n }\n if (name === 'config') this.#readConfigAttribute();\n }\n\n async load(input: JsonObject | string): Promise<AVManifest> {\n this.#inputManifest = input;\n return await this.player.load(input);\n }\n\n play(): Promise<void> {\n return this.player.play();\n }\n\n pause(): void {\n this.player.pause();\n }\n\n seekTo(time: number): void {\n this.player.seekTo(time);\n }\n\n selectCanvas(indexOrId: number | string, options?: { time?: number; autoplay?: boolean }): AVCanvas | undefined {\n return this.player.selectCanvas(indexOrId, options);\n }\n\n #readConfigAttribute(): void {\n const attribute = this.getAttribute('config');\n if (!attribute) return;\n try {\n this.#config = JSON.parse(attribute) as AVPlayerConfig;\n this.player.configure(this.#config);\n } catch (cause) {\n const error = cause instanceof Error ? cause : new Error(String(cause));\n this.dispatchEvent(new CustomEvent('av-error', { detail: { error }, bubbles: true, composed: true }));\n }\n }\n\n #panels(ui: AVStrings): Array<[Panel, string]> {\n const panels = this.player.config.panels;\n return ([\n panels.transcript ? ['transcript', ui.transcript] : undefined,\n panels.chapters ? ['chapters', ui.chapters] : undefined,\n panels.annotations ? ['annotations', ui.annotations] : undefined,\n panels.metadata ? ['metadata', ui.metadata] : undefined,\n panels.supplementalFiles ? ['files', ui.files] : undefined,\n ] as const).filter((value): value is [Panel, string] => Boolean(value));\n }\n\n /* Rebuilt for a new Manifest or configuration; the components inside follow the controller from there. */\n #render(): void {\n const manifest = this.player.manifest;\n const ui = resolveStrings(this.player.config.strings);\n const panels = this.#panels(ui);\n if (!panels.some(([id]) => id === this.#activePanel) && this.#activePanel !== 'none') this.#activePanel = panels[0]?.[0] ?? 'none';\n const showPanels = this.#activePanel !== 'none' && panels.length > 0;\n const hiddenHeading = '<span slot=\"heading\" hidden></span>';\n const tag = tags();\n const captions = this.player.config.audioArt.captions;\n this.#root.innerHTML = `<style>${styles}</style><div class=\"shell\">\n <slot name=\"header\">\n ${manifest ? `<header class=\"header\"><h2>${escapeHtml(manifest.label)}</h2>${manifest.summary ? `<p>${escapeHtml(manifest.summary)}</p>` : ''}</header>` : ''}\n </slot>\n <slot name=\"before-player\"></slot>\n <${tag.player} class=\"embedded\" data-component>${captions ? `<${tag.captions} class=\"embedded\" data-component slot=\"stage\" audio-only ${captions === 'line' ? 'line' : ''}></${tag.captions}>` : ''}</${tag.player}>\n <slot name=\"after-player\"></slot>\n ${manifest ? '<p class=\"position\" aria-live=\"off\"></p>' : ''}\n ${manifest && showPanels ? `\n <div class=\"tabs\" role=\"tablist\" aria-label=\"${escapeHtml(ui.mediaInformation)}\">\n ${panels.map(([id, text]) => `<button class=\"tab\" type=\"button\" role=\"tab\" id=\"tab-${id}\" aria-controls=\"panel-${id}\" data-panel=\"${id}\" aria-selected=\"${this.#activePanel === id}\">${escapeHtml(text)}</button>`).join('')}\n </div>\n <div class=\"details\">\n ${panels.map(([id]) => {\n const attributes = `id=\"panel-${id}\" role=\"tabpanel\" aria-labelledby=\"tab-${id}\" data-panel-body=\"${id}\" ${this.#activePanel === id ? '' : 'hidden'}`;\n switch (id) {\n case 'transcript':\n case 'chapters':\n case 'annotations': return `<${tag[id]} class=\"embedded\" data-component ${attributes}>${hiddenHeading}</${tag[id]}>`;\n default: return `<div ${attributes}></div>`;\n }\n }).join('')}\n </div>` : ''}\n </div>`;\n const player = this.#root.querySelector<MangoAVPlayerElement>(tag.player);\n if (player) player.forwardEvents = false;\n this.#root.querySelectorAll<AVControllerElement>('[data-component]').forEach((element) => { element.controller = this.player; });\n this.#root.querySelectorAll<HTMLElement>('[data-panel]').forEach((tab) => tab.addEventListener('click', () => this.#selectPanel(tab.dataset.panel as Panel)));\n this.#root.querySelector('.tabs')?.addEventListener('keydown', (event) => this.#moveTab(event as KeyboardEvent));\n this.#renderDetails();\n this.#renderPosition();\n }\n\n #selectPanel(panel: Panel): void {\n this.#activePanel = panel;\n this.#root.querySelectorAll<HTMLElement>('[data-panel]').forEach((tab) => tab.setAttribute('aria-selected', String(tab.dataset.panel === panel)));\n this.#root.querySelectorAll<HTMLElement>('[data-panel-body]').forEach((body) => { body.hidden = body.dataset.panelBody !== panel; });\n }\n\n #moveTab(event: KeyboardEvent): void {\n if (event.key !== 'ArrowLeft' && event.key !== 'ArrowRight') return;\n const tabs = [...this.#root.querySelectorAll<HTMLElement>('[data-panel]')];\n const index = tabs.findIndex((tab) => tab.dataset.panel === this.#activePanel);\n const next = tabs[(index + (event.key === 'ArrowRight' ? 1 : -1) + tabs.length) % tabs.length];\n if (!next) return;\n this.#selectPanel(next.dataset.panel as Panel);\n next.focus();\n }\n\n #renderDetails(): void {\n const manifest = this.player.manifest;\n const canvas = this.player.canvas;\n const ui = resolveStrings(this.player.config.strings);\n const metadata = this.#root.querySelector<HTMLElement>('[data-panel-body=metadata]');\n const files = this.#root.querySelector<HTMLElement>('[data-panel-body=files]');\n if (metadata) metadata.innerHTML = manifest && canvas ? metadataPanelHtml(manifest, canvas, ui) : '';\n if (files) files.innerHTML = manifest && canvas ? filesPanelHtml(manifest, canvas, ui) : '';\n }\n\n #renderPosition(): void {\n const position = this.#root.querySelector<HTMLElement>('.position');\n if (!position) return;\n const ui = resolveStrings(this.player.config.strings);\n const state = this.player.state;\n position.innerHTML = `${escapeHtml(ui.position)} <strong>${formatTime(state.currentTime)}</strong> <span>/ ${formatTime(state.duration)}</span>`;\n }\n}\n\nexport const defineMangoAVMedia = (tagName = 'mango-av-media'): typeof MangoAVMediaElement => {\n defineMangoAVPlayer();\n defineMangoAVChapters();\n defineMangoAVTranscript();\n defineMangoAVAnnotations();\n defineMangoAVCaptions();\n if (typeof customElements === 'undefined') return MangoAVMediaElement;\n if (!customElements.get(tagName)) customElements.define(tagName, MangoAVMediaElement);\n return MangoAVMediaElement;\n};\n"],"mappings":"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","names":[]}
@@ -0,0 +1,29 @@
1
+ import {
2
+ loadTranscript
3
+ } from "./chunk-LMZIFVHN.js";
4
+
5
+ // src/components/transcript-cues.ts
6
+ var loads = /* @__PURE__ */ new WeakMap();
7
+ var loadCues = (controller, resource) => {
8
+ const config = controller.config;
9
+ const options = {};
10
+ if (config.fetch) options.fetch = config.fetch;
11
+ if (config.requestInit) options.requestInit = config.requestInit;
12
+ if (config.transcript.showNotes !== void 0) options.showNotes = config.transcript.showNotes;
13
+ if (config.transcript.showMetadata !== void 0) options.showMetadata = config.transcript.showMetadata;
14
+ const key = `${Boolean(options.showNotes)}:${Boolean(options.showMetadata)}`;
15
+ let byOptions = loads.get(resource);
16
+ if (!byOptions) loads.set(resource, byOptions = /* @__PURE__ */ new Map());
17
+ let load = byOptions.get(key);
18
+ if (!load) {
19
+ load = loadTranscript(resource, options);
20
+ byOptions.set(key, load);
21
+ load.catch(() => byOptions?.delete(key));
22
+ }
23
+ return load;
24
+ };
25
+
26
+ export {
27
+ loadCues
28
+ };
29
+ //# sourceMappingURL=chunk-YVI7J43Q.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/transcript-cues.ts"],"sourcesContent":["import type { AVPlayerController } from '../controller';\nimport { loadTranscript } from '../transcript';\nimport type { TranscriptCue, TranscriptResource } from '../types';\n\n/*\n * The transcript panel and the captions often show the same transcript. They\n * share one load per resource and parse settings; a new Manifest brings new\n * resource objects, so nothing stale is kept.\n */\nconst loads = new WeakMap<TranscriptResource, Map<string, Promise<TranscriptCue[]>>>();\n\nexport const loadCues = (controller: AVPlayerController, resource: TranscriptResource): Promise<TranscriptCue[]> => {\n const config = controller.config;\n const options: Parameters<typeof loadTranscript>[1] = {};\n if (config.fetch) options.fetch = config.fetch;\n if (config.requestInit) options.requestInit = config.requestInit;\n if (config.transcript.showNotes !== undefined) options.showNotes = config.transcript.showNotes;\n if (config.transcript.showMetadata !== undefined) options.showMetadata = config.transcript.showMetadata;\n const key = `${Boolean(options.showNotes)}:${Boolean(options.showMetadata)}`;\n let byOptions = loads.get(resource);\n if (!byOptions) loads.set(resource, byOptions = new Map());\n let load = byOptions.get(key);\n if (!load) {\n load = loadTranscript(resource, options);\n byOptions.set(key, load);\n // A failed load is tried again next time.\n load.catch(() => byOptions?.delete(key));\n }\n return load;\n};\n"],"mappings":";;;;;AASA,IAAM,QAAQ,oBAAI,QAAmE;AAE9E,IAAM,WAAW,CAAC,YAAgC,aAA2D;AAClH,QAAM,SAAS,WAAW;AAC1B,QAAM,UAAgD,CAAC;AACvD,MAAI,OAAO,MAAO,SAAQ,QAAQ,OAAO;AACzC,MAAI,OAAO,YAAa,SAAQ,cAAc,OAAO;AACrD,MAAI,OAAO,WAAW,cAAc,OAAW,SAAQ,YAAY,OAAO,WAAW;AACrF,MAAI,OAAO,WAAW,iBAAiB,OAAW,SAAQ,eAAe,OAAO,WAAW;AAC3F,QAAM,MAAM,GAAG,QAAQ,QAAQ,SAAS,CAAC,IAAI,QAAQ,QAAQ,YAAY,CAAC;AAC1E,MAAI,YAAY,MAAM,IAAI,QAAQ;AAClC,MAAI,CAAC,UAAW,OAAM,IAAI,UAAU,YAAY,oBAAI,IAAI,CAAC;AACzD,MAAI,OAAO,UAAU,IAAI,GAAG;AAC5B,MAAI,CAAC,MAAM;AACT,WAAO,eAAe,UAAU,OAAO;AACvC,cAAU,IAAI,KAAK,IAAI;AAEvB,SAAK,MAAM,MAAM,WAAW,OAAO,GAAG,CAAC;AAAA,EACzC;AACA,SAAO;AACT;","names":[]}
@@ -0,0 +1,152 @@
1
+ import {
2
+ markActiveRow,
3
+ playFromInteraction,
4
+ scrollRowToTop
5
+ } from "./chunk-TQNEVWUH.js";
6
+ import {
7
+ loadCues
8
+ } from "./chunk-YVI7J43Q.js";
9
+ import {
10
+ AVControllerElement,
11
+ componentTheme,
12
+ panelStyles
13
+ } from "./chunk-JXZR5DL2.js";
14
+ import {
15
+ escapeHtml,
16
+ formatTime,
17
+ resolveStrings
18
+ } from "./chunk-53ZN4Y4L.js";
19
+
20
+ // src/components/mango-av-transcript.ts
21
+ var styles = `${componentTheme}${panelStyles}`;
22
+ var highlight = (text, query) => {
23
+ const escaped = escapeHtml(text);
24
+ if (!query) return escaped;
25
+ const safeQuery = escapeHtml(query).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
26
+ return escaped.replace(new RegExp(`(${safeQuery})`, "gi"), "<mark>$1</mark>");
27
+ };
28
+ var cuesHtml = (cues, query, ui) => {
29
+ const visible = query ? cues.filter((cue) => cue.text.toLowerCase().includes(query.toLowerCase())) : cues;
30
+ if (visible.length === 0) return `<li class="empty">${escapeHtml(ui.noTranscript)}</li>`;
31
+ return visible.map((cue) => `<li data-cue-id="${escapeHtml(cue.id)}" ${cue.start !== void 0 ? `data-start="${cue.start}" data-end="${cue.end ?? ""}"` : ""}>
32
+ <button type="button" class="row" ${cue.start === void 0 ? "disabled" : ""}>
33
+ <time>${cue.start !== void 0 ? formatTime(cue.start) : ""}</time>
34
+ <span class="text">${cue.speaker ? `<span class="speaker">${escapeHtml(cue.speaker)}</span>` : ""}<span>${highlight(cue.text, query)}</span></span>
35
+ </button>
36
+ </li>`).join("");
37
+ };
38
+ var MangoAVTranscriptElement = class extends AVControllerElement {
39
+ #root;
40
+ #cues = [];
41
+ #loading = false;
42
+ #transcriptIndex = 0;
43
+ #query = "";
44
+ #loadToken = 0;
45
+ #activeRow;
46
+ constructor() {
47
+ super();
48
+ this.#root = this.attachShadow({ mode: "open" });
49
+ }
50
+ controllerChanged() {
51
+ this.#reset();
52
+ }
53
+ handleControllerEvent(type, _event) {
54
+ if (type === "av-ready" || type === "av-canvaschange") this.#reset();
55
+ else if (type === "av-configchange") this.#render();
56
+ else if (type === "av-timeupdate" || type === "av-seek" || type === "av-statechange") this.#sync();
57
+ }
58
+ #reset() {
59
+ this.#cues = [];
60
+ this.#transcriptIndex = 0;
61
+ this.#render();
62
+ void this.#loadTranscript();
63
+ }
64
+ #render() {
65
+ const canvas = this.controller.canvas;
66
+ const transcripts = canvas?.transcripts ?? [];
67
+ const resource = transcripts[this.#transcriptIndex];
68
+ const ui = resolveStrings(this.controller.config.strings);
69
+ const config = this.controller.config.transcript;
70
+ this.#root.innerHTML = `<style>${styles}</style><section class="panel" aria-label="${escapeHtml(ui.transcript)}">
71
+ <slot name="heading"><h2>${escapeHtml(ui.transcript)}</h2></slot>
72
+ ${!canvas ? `<p class="empty">${escapeHtml(ui.noManifest)}</p>` : transcripts.length === 0 ? `<p class="empty">${escapeHtml(ui.noTranscript)}</p>` : `
73
+ <div class="tools">
74
+ ${transcripts.length > 1 ? `<label class="field">${escapeHtml(ui.transcript)}<select data-action="transcript">${transcripts.map((item, index) => `<option value="${index}" ${index === this.#transcriptIndex ? "selected" : ""}>${escapeHtml(item.label)}</option>`).join("")}</select></label>` : ""}
75
+ ${config.search ? `<label class="field">${escapeHtml(ui.searchTranscript)}<input type="search" data-action="search" value="${escapeHtml(this.#query)}"></label>` : ""}
76
+ ${config.download && resource?.src ? `<a href="${escapeHtml(resource.src)}" download>${escapeHtml(ui.download)}</a>` : ""}
77
+ </div>
78
+ <ol class="list"></ol>
79
+ `}
80
+ </section>`;
81
+ this.#renderList();
82
+ this.#bind();
83
+ }
84
+ #renderList() {
85
+ const list = this.#root.querySelector("ol.list");
86
+ if (!list) return;
87
+ const ui = resolveStrings(this.controller.config.strings);
88
+ list.innerHTML = this.#loading && this.#cues.length === 0 ? `<li class="empty">${escapeHtml(ui.loadingTranscript)}</li>` : cuesHtml(this.#cues, this.#query.trim(), ui);
89
+ list.querySelectorAll("[data-start] > button").forEach((button) => button.addEventListener("click", () => {
90
+ const time = Number(button.parentElement?.dataset.start);
91
+ if (!Number.isFinite(time)) return;
92
+ this.dispatchEvent(new CustomEvent("av-transcriptcueselect", { detail: { time }, bubbles: true, composed: true }));
93
+ this.controller.seekTo(time);
94
+ playFromInteraction(this.controller, this);
95
+ }));
96
+ this.#activeRow = void 0;
97
+ this.#sync();
98
+ }
99
+ #bind() {
100
+ this.#root.querySelector("[data-action=transcript]")?.addEventListener("change", (event) => {
101
+ this.#transcriptIndex = Number(event.target.value);
102
+ this.#cues = [];
103
+ this.#render();
104
+ void this.#loadTranscript();
105
+ });
106
+ this.#root.querySelector("[data-action=search]")?.addEventListener("input", (event) => {
107
+ this.#query = event.target.value;
108
+ this.#renderList();
109
+ this.dispatchEvent(new CustomEvent("av-transcriptsearch", { detail: { query: this.#query }, bubbles: true, composed: true }));
110
+ });
111
+ }
112
+ async #loadTranscript() {
113
+ const resource = this.controller.canvas?.transcripts[this.#transcriptIndex];
114
+ if (!resource) return;
115
+ const token = ++this.#loadToken;
116
+ this.#loading = true;
117
+ this.#renderList();
118
+ try {
119
+ const cues = await loadCues(this.controller, resource);
120
+ if (token !== this.#loadToken) return;
121
+ this.#cues = cues;
122
+ } catch (cause) {
123
+ if (token !== this.#loadToken) return;
124
+ const error = cause instanceof Error ? cause : new Error(String(cause));
125
+ this.dispatchEvent(new CustomEvent("av-error", { detail: { error }, bubbles: true, composed: true }));
126
+ this.#cues = [];
127
+ }
128
+ this.#loading = false;
129
+ this.#renderList();
130
+ }
131
+ /* While playing, keep the passage being heard at the top of the list. Paused, leave it where the user scrolled. */
132
+ #sync() {
133
+ const active = markActiveRow(this.#root, this.controller.state.currentTime);
134
+ if (active === this.#activeRow) return;
135
+ this.#activeRow = active;
136
+ if (active && this.controller.config.transcript.autoScroll && this.controller.state.status === "playing") scrollRowToTop(active);
137
+ }
138
+ };
139
+ var defineMangoAVTranscript = (tagName = "mango-av-transcript") => {
140
+ if (typeof customElements === "undefined") return MangoAVTranscriptElement;
141
+ if (!MangoAVTranscriptElement.registeredTag && !customElements.get(tagName)) {
142
+ customElements.define(tagName, MangoAVTranscriptElement);
143
+ MangoAVTranscriptElement.registeredTag = tagName;
144
+ }
145
+ return MangoAVTranscriptElement;
146
+ };
147
+
148
+ export {
149
+ MangoAVTranscriptElement,
150
+ defineMangoAVTranscript
151
+ };
152
+ //# sourceMappingURL=chunk-Z2OF3RKT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/mango-av-transcript.ts"],"sourcesContent":["import { AVControllerElement } from './controller-element';\nimport { componentTheme, panelStyles } from './component-styles';\nimport { playFromInteraction } from './playback';\nimport { markActiveRow, scrollRowToTop } from './timed-rows';\nimport { loadCues } from './transcript-cues';\nimport type { AVPlayerEventMap, AVStrings, TranscriptCue } from '../types';\nimport { escapeHtml, formatTime, resolveStrings } from '../utils';\n\nconst styles = `${componentTheme}${panelStyles}`;\n\nconst highlight = (text: string, query: string): string => {\n const escaped = escapeHtml(text);\n if (!query) return escaped;\n const safeQuery = escapeHtml(query).replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n return escaped.replace(new RegExp(`(${safeQuery})`, 'gi'), '<mark>$1</mark>');\n};\n\nconst cuesHtml = (cues: TranscriptCue[], query: string, ui: AVStrings): string => {\n const visible = query ? cues.filter((cue) => cue.text.toLowerCase().includes(query.toLowerCase())) : cues;\n if (visible.length === 0) return `<li class=\"empty\">${escapeHtml(ui.noTranscript)}</li>`;\n return visible.map((cue) => `<li data-cue-id=\"${escapeHtml(cue.id)}\" ${cue.start !== undefined ? `data-start=\"${cue.start}\" data-end=\"${cue.end ?? ''}\"` : ''}>\n <button type=\"button\" class=\"row\" ${cue.start === undefined ? 'disabled' : ''}>\n <time>${cue.start !== undefined ? formatTime(cue.start) : ''}</time>\n <span class=\"text\">${cue.speaker ? `<span class=\"speaker\">${escapeHtml(cue.speaker)}</span>` : ''}<span>${highlight(cue.text, query)}</span></span>\n </button>\n </li>`).join('');\n};\n\nexport class MangoAVTranscriptElement extends AVControllerElement {\n #root: ShadowRoot;\n #cues: TranscriptCue[] = [];\n #loading = false;\n #transcriptIndex = 0;\n #query = '';\n #loadToken = 0;\n #activeRow: HTMLElement | undefined;\n\n constructor() {\n super();\n this.#root = this.attachShadow({ mode: 'open' });\n }\n\n protected controllerChanged(): void {\n this.#reset();\n }\n\n protected handleControllerEvent<K extends keyof AVPlayerEventMap>(type: K, _event: CustomEvent<AVPlayerEventMap[K]>): void {\n if (type === 'av-ready' || type === 'av-canvaschange') this.#reset();\n else if (type === 'av-configchange') this.#render();\n else if (type === 'av-timeupdate' || type === 'av-seek' || type === 'av-statechange') this.#sync();\n }\n\n #reset(): void {\n this.#cues = [];\n this.#transcriptIndex = 0;\n this.#render();\n void this.#loadTranscript();\n }\n\n #render(): void {\n const canvas = this.controller.canvas;\n const transcripts = canvas?.transcripts ?? [];\n const resource = transcripts[this.#transcriptIndex];\n const ui = resolveStrings(this.controller.config.strings);\n const config = this.controller.config.transcript;\n this.#root.innerHTML = `<style>${styles}</style><section class=\"panel\" aria-label=\"${escapeHtml(ui.transcript)}\">\n <slot name=\"heading\"><h2>${escapeHtml(ui.transcript)}</h2></slot>\n ${!canvas ? `<p class=\"empty\">${escapeHtml(ui.noManifest)}</p>` : transcripts.length === 0 ? `<p class=\"empty\">${escapeHtml(ui.noTranscript)}</p>` : `\n <div class=\"tools\">\n ${transcripts.length > 1 ? `<label class=\"field\">${escapeHtml(ui.transcript)}<select data-action=\"transcript\">${transcripts.map((item, index) => `<option value=\"${index}\" ${index === this.#transcriptIndex ? 'selected' : ''}>${escapeHtml(item.label)}</option>`).join('')}</select></label>` : ''}\n ${config.search ? `<label class=\"field\">${escapeHtml(ui.searchTranscript)}<input type=\"search\" data-action=\"search\" value=\"${escapeHtml(this.#query)}\"></label>` : ''}\n ${config.download && resource?.src ? `<a href=\"${escapeHtml(resource.src)}\" download>${escapeHtml(ui.download)}</a>` : ''}\n </div>\n <ol class=\"list\"></ol>\n `}\n </section>`;\n this.#renderList();\n this.#bind();\n }\n\n #renderList(): void {\n const list = this.#root.querySelector<HTMLElement>('ol.list');\n if (!list) return;\n const ui = resolveStrings(this.controller.config.strings);\n list.innerHTML = this.#loading && this.#cues.length === 0\n ? `<li class=\"empty\">${escapeHtml(ui.loadingTranscript)}</li>`\n : cuesHtml(this.#cues, this.#query.trim(), ui);\n list.querySelectorAll<HTMLElement>('[data-start] > button').forEach((button) => button.addEventListener('click', () => {\n const time = Number(button.parentElement?.dataset.start);\n if (!Number.isFinite(time)) return;\n this.dispatchEvent(new CustomEvent('av-transcriptcueselect', { detail: { time }, bubbles: true, composed: true }));\n this.controller.seekTo(time);\n playFromInteraction(this.controller, this);\n }));\n this.#activeRow = undefined;\n this.#sync();\n }\n\n #bind(): void {\n this.#root.querySelector<HTMLSelectElement>('[data-action=transcript]')?.addEventListener('change', (event) => {\n this.#transcriptIndex = Number((event.target as HTMLSelectElement).value);\n this.#cues = [];\n this.#render();\n void this.#loadTranscript();\n });\n this.#root.querySelector<HTMLInputElement>('[data-action=search]')?.addEventListener('input', (event) => {\n this.#query = (event.target as HTMLInputElement).value;\n this.#renderList();\n this.dispatchEvent(new CustomEvent('av-transcriptsearch', { detail: { query: this.#query }, bubbles: true, composed: true }));\n });\n }\n\n async #loadTranscript(): Promise<void> {\n const resource = this.controller.canvas?.transcripts[this.#transcriptIndex];\n if (!resource) return;\n const token = ++this.#loadToken;\n this.#loading = true;\n this.#renderList();\n try {\n const cues = await loadCues(this.controller, resource);\n if (token !== this.#loadToken) return;\n this.#cues = cues;\n } catch (cause) {\n if (token !== this.#loadToken) return;\n const error = cause instanceof Error ? cause : new Error(String(cause));\n this.dispatchEvent(new CustomEvent('av-error', { detail: { error }, bubbles: true, composed: true }));\n this.#cues = [];\n }\n this.#loading = false;\n this.#renderList();\n }\n\n /* While playing, keep the passage being heard at the top of the list. Paused, leave it where the user scrolled. */\n #sync(): void {\n const active = markActiveRow(this.#root, this.controller.state.currentTime);\n if (active === this.#activeRow) return;\n this.#activeRow = active;\n if (active && this.controller.config.transcript.autoScroll && this.controller.state.status === 'playing') scrollRowToTop(active);\n }\n}\n\nexport const defineMangoAVTranscript = (tagName = 'mango-av-transcript'): typeof MangoAVTranscriptElement => {\n if (typeof customElements === 'undefined') return MangoAVTranscriptElement;\n if (!MangoAVTranscriptElement.registeredTag && !customElements.get(tagName)) {\n customElements.define(tagName, MangoAVTranscriptElement);\n MangoAVTranscriptElement.registeredTag = tagName;\n }\n return MangoAVTranscriptElement;\n};\n"],"mappings":"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","names":[]}