@mango-iiif/av 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +205 -0
- package/dist/annotations.d.ts +12 -0
- package/dist/annotations.js +15 -0
- package/dist/annotations.js.map +1 -0
- package/dist/chapters.d.ts +12 -0
- package/dist/chapters.js +14 -0
- package/dist/chapters.js.map +1 -0
- package/dist/chunk-3LLEY7MA.js +126 -0
- package/dist/chunk-3LLEY7MA.js.map +1 -0
- package/dist/chunk-CDLYT3KB.js +17 -0
- package/dist/chunk-CDLYT3KB.js.map +1 -0
- package/dist/chunk-GAVG7CPU.js +1 -0
- package/dist/chunk-GAVG7CPU.js.map +1 -0
- package/dist/chunk-GX7UU6V4.js +547 -0
- package/dist/chunk-GX7UU6V4.js.map +1 -0
- package/dist/chunk-IFTSCRCU.js +83 -0
- package/dist/chunk-IFTSCRCU.js.map +1 -0
- package/dist/chunk-JXUHKKIX.js +94 -0
- package/dist/chunk-JXUHKKIX.js.map +1 -0
- package/dist/chunk-ML77YRJL.js +221 -0
- package/dist/chunk-ML77YRJL.js.map +1 -0
- package/dist/chunk-NTHMZZYJ.js +38 -0
- package/dist/chunk-NTHMZZYJ.js.map +1 -0
- package/dist/chunk-PZ6GV6ZJ.js +163 -0
- package/dist/chunk-PZ6GV6ZJ.js.map +1 -0
- package/dist/chunk-R4OUWZ3R.js +1038 -0
- package/dist/chunk-R4OUWZ3R.js.map +1 -0
- package/dist/chunk-YH6DS7J6.js +74 -0
- package/dist/chunk-YH6DS7J6.js.map +1 -0
- package/dist/controller-DLy38ais.d.ts +356 -0
- package/dist/controller-element-BOKM9v2S.d.ts +15 -0
- package/dist/core.d.ts +28 -0
- package/dist/core.js +28 -0
- package/dist/core.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.js +70 -0
- package/dist/index.js.map +1 -0
- package/dist/media.d.ts +28 -0
- package/dist/media.js +16 -0
- package/dist/media.js.map +1 -0
- package/dist/player.d.ts +27 -0
- package/dist/player.js +15 -0
- package/dist/player.js.map +1 -0
- package/dist/transcript-element.d.ts +12 -0
- package/dist/transcript-element.js +16 -0
- package/dist/transcript-element.js.map +1 -0
- package/docs/API.md +264 -0
- package/docs/FEATURE_PARITY.md +43 -0
- package/package.json +94 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// src/components/media-chrome.ts
|
|
2
|
+
var registration;
|
|
3
|
+
var languageRegistrations = /* @__PURE__ */ new Map();
|
|
4
|
+
var loadLanguage = (language) => {
|
|
5
|
+
const normalized = language.toLowerCase();
|
|
6
|
+
const code = normalized === "zh-cn" || normalized === "zh-tw" ? normalized : normalized.split("-")[0] ?? normalized;
|
|
7
|
+
if (languageRegistrations.has(code)) return languageRegistrations.get(code);
|
|
8
|
+
const pending = (() => {
|
|
9
|
+
switch (code) {
|
|
10
|
+
case "de":
|
|
11
|
+
return import("media-chrome/lang/de");
|
|
12
|
+
case "es":
|
|
13
|
+
return import("media-chrome/lang/es");
|
|
14
|
+
case "fr":
|
|
15
|
+
return import("media-chrome/lang/fr");
|
|
16
|
+
case "pt":
|
|
17
|
+
return import("media-chrome/lang/pt");
|
|
18
|
+
case "zh-cn":
|
|
19
|
+
return import("media-chrome/lang/zh-CN");
|
|
20
|
+
case "zh-tw":
|
|
21
|
+
return import("media-chrome/lang/zh-TW");
|
|
22
|
+
default:
|
|
23
|
+
return void 0;
|
|
24
|
+
}
|
|
25
|
+
})();
|
|
26
|
+
if (pending) languageRegistrations.set(code, pending);
|
|
27
|
+
return pending;
|
|
28
|
+
};
|
|
29
|
+
var ensureMediaChrome = async (language) => {
|
|
30
|
+
if (language) await loadLanguage(language);
|
|
31
|
+
registration ??= import("media-chrome");
|
|
32
|
+
await registration;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export {
|
|
36
|
+
ensureMediaChrome
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=chunk-NTHMZZYJ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/media-chrome.ts"],"sourcesContent":["let registration: Promise<unknown> | undefined;\nconst languageRegistrations = new Map<string, Promise<unknown>>();\n\nconst loadLanguage = (language: string): Promise<unknown> | undefined => {\n const normalized = language.toLowerCase();\n const code = normalized === 'zh-cn' || normalized === 'zh-tw'\n ? normalized\n : normalized.split('-')[0] ?? normalized;\n if (languageRegistrations.has(code)) return languageRegistrations.get(code);\n const pending = (() => {\n switch (code) {\n case 'de': return import('media-chrome/lang/de');\n case 'es': return import('media-chrome/lang/es');\n case 'fr': return import('media-chrome/lang/fr');\n case 'pt': return import('media-chrome/lang/pt');\n case 'zh-cn': return import('media-chrome/lang/zh-CN');\n case 'zh-tw': return import('media-chrome/lang/zh-TW');\n default: return undefined;\n }\n })();\n if (pending) languageRegistrations.set(code, pending);\n return pending;\n};\n\nexport const ensureMediaChrome = async (language?: string): Promise<void> => {\n if (language) await loadLanguage(language);\n registration ??= import('media-chrome');\n await registration;\n};\n"],"mappings":";AAAA,IAAI;AACJ,IAAM,wBAAwB,oBAAI,IAA8B;AAEhE,IAAM,eAAe,CAAC,aAAmD;AACvE,QAAM,aAAa,SAAS,YAAY;AACxC,QAAM,OAAO,eAAe,WAAW,eAAe,UAClD,aACA,WAAW,MAAM,GAAG,EAAE,CAAC,KAAK;AAChC,MAAI,sBAAsB,IAAI,IAAI,EAAG,QAAO,sBAAsB,IAAI,IAAI;AAC1E,QAAM,WAAW,MAAM;AACrB,YAAQ,MAAM;AAAA,MACZ,KAAK;AAAM,eAAO,OAAO,sBAAsB;AAAA,MAC/C,KAAK;AAAM,eAAO,OAAO,sBAAsB;AAAA,MAC/C,KAAK;AAAM,eAAO,OAAO,sBAAsB;AAAA,MAC/C,KAAK;AAAM,eAAO,OAAO,sBAAsB;AAAA,MAC/C,KAAK;AAAS,eAAO,OAAO,yBAAyB;AAAA,MACrD,KAAK;AAAS,eAAO,OAAO,yBAAyB;AAAA,MACrD;AAAS,eAAO;AAAA,IAClB;AAAA,EACF,GAAG;AACH,MAAI,QAAS,uBAAsB,IAAI,MAAM,OAAO;AACpD,SAAO;AACT;AAEO,IAAM,oBAAoB,OAAO,aAAqC;AAC3E,MAAI,SAAU,OAAM,aAAa,QAAQ;AACzC,mBAAiB,OAAO,cAAc;AACtC,QAAM;AACR;","names":[]}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import {
|
|
2
|
+
playFromInteraction
|
|
3
|
+
} from "./chunk-CDLYT3KB.js";
|
|
4
|
+
import {
|
|
5
|
+
loadTranscript
|
|
6
|
+
} from "./chunk-3LLEY7MA.js";
|
|
7
|
+
import {
|
|
8
|
+
AVControllerElement,
|
|
9
|
+
componentTheme
|
|
10
|
+
} from "./chunk-JXUHKKIX.js";
|
|
11
|
+
import {
|
|
12
|
+
escapeHtml,
|
|
13
|
+
formatTime,
|
|
14
|
+
resolveStrings
|
|
15
|
+
} from "./chunk-R4OUWZ3R.js";
|
|
16
|
+
|
|
17
|
+
// src/components/mango-av-transcript.ts
|
|
18
|
+
var styles = `${componentTheme}
|
|
19
|
+
.transcript { background: var(--mango-av-background); border: 1px solid var(--mango-av-border); border-radius: var(--mango-av-radius); padding: .75rem; }
|
|
20
|
+
h2 { font-size: 1rem; margin: 0 0 .5rem; }
|
|
21
|
+
.tools { display: flex; flex-wrap: wrap; gap: .45rem; margin-bottom: .75rem; }
|
|
22
|
+
.tools input, .tools select { border: 1px solid var(--mango-av-border); border-radius: .35rem; min-width: 0; padding: .45rem; }
|
|
23
|
+
.tools input { flex: 1; }
|
|
24
|
+
.tools a { align-content: center; color: var(--mango-av-accent); }
|
|
25
|
+
ol { list-style: none; margin: 0; max-height: var(--mango-av-transcript-height, 34rem); overflow: auto; padding: 0; }
|
|
26
|
+
button { background: transparent; border: 0; border-radius: .35rem; color: inherit; display: grid; gap: .2rem; padding: .5rem; text-align: left; width: 100%; }
|
|
27
|
+
button:hover { background: var(--mango-av-surface); }
|
|
28
|
+
button.active { background: color-mix(in srgb, var(--mango-av-accent) 14%, transparent); box-shadow: inset 3px 0 var(--mango-av-accent); }
|
|
29
|
+
mark { background: #ffe770; }
|
|
30
|
+
.speaker { font-weight: 700; }
|
|
31
|
+
`;
|
|
32
|
+
var highlight = (text, query) => {
|
|
33
|
+
const escaped = escapeHtml(text);
|
|
34
|
+
if (!query) return escaped;
|
|
35
|
+
const safeQuery = query.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
36
|
+
return escaped.replace(new RegExp(`(${safeQuery})`, "gi"), "<mark>$1</mark>");
|
|
37
|
+
};
|
|
38
|
+
var MangoAVTranscriptElement = class extends AVControllerElement {
|
|
39
|
+
#root;
|
|
40
|
+
#cues = [];
|
|
41
|
+
#transcriptIndex = 0;
|
|
42
|
+
#query = "";
|
|
43
|
+
#loadToken = 0;
|
|
44
|
+
constructor() {
|
|
45
|
+
super();
|
|
46
|
+
this.#root = this.attachShadow({ mode: "open" });
|
|
47
|
+
}
|
|
48
|
+
controllerChanged() {
|
|
49
|
+
this.#cues = [];
|
|
50
|
+
this.#transcriptIndex = 0;
|
|
51
|
+
this.#render();
|
|
52
|
+
void this.#loadTranscript();
|
|
53
|
+
}
|
|
54
|
+
handleControllerEvent(type, _event) {
|
|
55
|
+
if (type === "av-ready" || type === "av-canvaschange") {
|
|
56
|
+
this.#cues = [];
|
|
57
|
+
this.#transcriptIndex = 0;
|
|
58
|
+
this.#render();
|
|
59
|
+
void this.#loadTranscript();
|
|
60
|
+
} else if (type === "av-configchange") {
|
|
61
|
+
this.#render();
|
|
62
|
+
} else if (type === "av-timeupdate" || type === "av-seek" || type === "av-statechange") {
|
|
63
|
+
this.#sync();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
#render() {
|
|
67
|
+
const canvas = this.controller.canvas;
|
|
68
|
+
const transcripts = canvas?.transcripts ?? [];
|
|
69
|
+
const resource = transcripts[this.#transcriptIndex];
|
|
70
|
+
const query = this.#query.trim();
|
|
71
|
+
const ui = resolveStrings(this.controller.config.strings);
|
|
72
|
+
const cues = query ? this.#cues.filter((cue) => cue.text.toLowerCase().includes(query.toLowerCase())) : this.#cues;
|
|
73
|
+
this.#root.innerHTML = `<style>${styles}</style><section class="transcript" aria-label="${escapeHtml(ui.transcript)}">
|
|
74
|
+
<slot name="heading"><h2>${escapeHtml(ui.transcript)}</h2></slot>
|
|
75
|
+
${!canvas ? `<p class="empty">${escapeHtml(ui.noManifest)}</p>` : transcripts.length === 0 ? `<p class="empty">${escapeHtml(ui.noTranscript)}</p>` : `
|
|
76
|
+
<div class="tools">
|
|
77
|
+
${transcripts.length > 1 ? `<select data-action="transcript" aria-label="${escapeHtml(ui.transcript)}">${transcripts.map((item, index) => `<option value="${index}" ${index === this.#transcriptIndex ? "selected" : ""}>${escapeHtml(item.label)}</option>`).join("")}</select>` : ""}
|
|
78
|
+
${this.controller.config.transcript.search ? `<input type="search" data-action="search" value="${escapeHtml(this.#query)}" placeholder="${escapeHtml(ui.searchTranscript)}" aria-label="${escapeHtml(ui.searchTranscript)}">` : ""}
|
|
79
|
+
${this.controller.config.transcript.download && resource?.src ? `<a href="${escapeHtml(resource.src)}" download>${escapeHtml(ui.download)}</a>` : ""}
|
|
80
|
+
</div>
|
|
81
|
+
${this.#cues.length === 0 ? `<p class="empty">${escapeHtml(ui.loadingTranscript)}</p>` : `<ol>${cues.map((cue) => `<li data-cue-id="${escapeHtml(cue.id)}" ${cue.start !== void 0 ? `data-start="${cue.start}" data-end="${cue.end ?? cue.start}"` : ""}>
|
|
82
|
+
<button type="button" ${cue.start === void 0 ? "disabled" : ""}>
|
|
83
|
+
${cue.start !== void 0 ? `<span class="time">${formatTime(cue.start)}</span>` : ""}
|
|
84
|
+
${cue.speaker ? `<span class="speaker">${escapeHtml(cue.speaker)}</span>` : ""}
|
|
85
|
+
<span>${highlight(cue.text, query)}</span>
|
|
86
|
+
</button>
|
|
87
|
+
</li>`).join("")}</ol>`}
|
|
88
|
+
`}
|
|
89
|
+
</section>`;
|
|
90
|
+
this.#bind();
|
|
91
|
+
this.#sync();
|
|
92
|
+
}
|
|
93
|
+
#bind() {
|
|
94
|
+
this.#root.querySelector("[data-action=transcript]")?.addEventListener("change", (event) => {
|
|
95
|
+
this.#transcriptIndex = Number(event.target.value);
|
|
96
|
+
this.#cues = [];
|
|
97
|
+
this.#render();
|
|
98
|
+
void this.#loadTranscript();
|
|
99
|
+
});
|
|
100
|
+
this.#root.querySelector("[data-action=search]")?.addEventListener("input", (event) => {
|
|
101
|
+
const input = event.target;
|
|
102
|
+
const selection = input.selectionStart;
|
|
103
|
+
this.#query = input.value;
|
|
104
|
+
this.#render();
|
|
105
|
+
const next = this.#root.querySelector("[data-action=search]");
|
|
106
|
+
next?.focus();
|
|
107
|
+
if (selection !== null) next?.setSelectionRange(selection, selection);
|
|
108
|
+
this.dispatchEvent(new CustomEvent("av-transcriptsearch", { detail: { query: this.#query }, bubbles: true, composed: true }));
|
|
109
|
+
});
|
|
110
|
+
this.#root.querySelectorAll("[data-start] button").forEach((button) => button.addEventListener("click", () => {
|
|
111
|
+
const time = Number(button.parentElement?.dataset.start);
|
|
112
|
+
if (!Number.isFinite(time)) return;
|
|
113
|
+
this.dispatchEvent(new CustomEvent("av-transcriptcueselect", { detail: { time }, bubbles: true, composed: true }));
|
|
114
|
+
this.controller.seekTo(time);
|
|
115
|
+
playFromInteraction(this.controller, this);
|
|
116
|
+
}));
|
|
117
|
+
}
|
|
118
|
+
async #loadTranscript() {
|
|
119
|
+
const resource = this.controller.canvas?.transcripts[this.#transcriptIndex];
|
|
120
|
+
if (!resource) return;
|
|
121
|
+
const token = ++this.#loadToken;
|
|
122
|
+
const options = {};
|
|
123
|
+
if (this.controller.config.fetch) options.fetch = this.controller.config.fetch;
|
|
124
|
+
if (this.controller.config.requestInit) options.requestInit = this.controller.config.requestInit;
|
|
125
|
+
if (this.controller.config.transcript.showNotes !== void 0) options.showNotes = this.controller.config.transcript.showNotes;
|
|
126
|
+
if (this.controller.config.transcript.showMetadata !== void 0) options.showMetadata = this.controller.config.transcript.showMetadata;
|
|
127
|
+
try {
|
|
128
|
+
const cues = await loadTranscript(resource, options);
|
|
129
|
+
if (token !== this.#loadToken) return;
|
|
130
|
+
this.#cues = cues;
|
|
131
|
+
this.#render();
|
|
132
|
+
} catch (cause) {
|
|
133
|
+
if (token !== this.#loadToken) return;
|
|
134
|
+
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
135
|
+
this.dispatchEvent(new CustomEvent("av-error", { detail: { error }, bubbles: true, composed: true }));
|
|
136
|
+
this.#cues = [];
|
|
137
|
+
this.#render();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
#sync() {
|
|
141
|
+
const time = this.controller.state.currentTime;
|
|
142
|
+
let active;
|
|
143
|
+
this.#root.querySelectorAll("[data-start][data-end]").forEach((row) => {
|
|
144
|
+
const start = Number(row.dataset.start);
|
|
145
|
+
const end = Number(row.dataset.end);
|
|
146
|
+
const isActive = Number.isFinite(start) && time >= start && (!Number.isFinite(end) || time < end);
|
|
147
|
+
row.querySelector("button")?.classList.toggle("active", isActive);
|
|
148
|
+
if (isActive) active = row;
|
|
149
|
+
});
|
|
150
|
+
if (active && this.controller.config.transcript.autoScroll) active.scrollIntoView({ block: "nearest", behavior: "smooth" });
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
var defineMangoAVTranscript = (tagName = "mango-av-transcript") => {
|
|
154
|
+
if (typeof customElements === "undefined") return MangoAVTranscriptElement;
|
|
155
|
+
if (!customElements.get(tagName)) customElements.define(tagName, MangoAVTranscriptElement);
|
|
156
|
+
return MangoAVTranscriptElement;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
export {
|
|
160
|
+
MangoAVTranscriptElement,
|
|
161
|
+
defineMangoAVTranscript
|
|
162
|
+
};
|
|
163
|
+
//# sourceMappingURL=chunk-PZ6GV6ZJ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/mango-av-transcript.ts"],"sourcesContent":["import { AVControllerElement } from './controller-element';\nimport { componentTheme } from './component-styles';\nimport { loadTranscript } from '../transcript';\nimport { playFromInteraction } from './playback';\nimport type { AVPlayerEventMap, TranscriptCue } from '../types';\nimport { escapeHtml, formatTime, resolveStrings } from '../utils';\n\nconst styles = `${componentTheme}\n .transcript { background: var(--mango-av-background); border: 1px solid var(--mango-av-border); border-radius: var(--mango-av-radius); padding: .75rem; }\n h2 { font-size: 1rem; margin: 0 0 .5rem; }\n .tools { display: flex; flex-wrap: wrap; gap: .45rem; margin-bottom: .75rem; }\n .tools input, .tools select { border: 1px solid var(--mango-av-border); border-radius: .35rem; min-width: 0; padding: .45rem; }\n .tools input { flex: 1; }\n .tools a { align-content: center; color: var(--mango-av-accent); }\n ol { list-style: none; margin: 0; max-height: var(--mango-av-transcript-height, 34rem); overflow: auto; padding: 0; }\n button { background: transparent; border: 0; border-radius: .35rem; color: inherit; display: grid; gap: .2rem; padding: .5rem; text-align: left; width: 100%; }\n button:hover { background: var(--mango-av-surface); }\n button.active { background: color-mix(in srgb, var(--mango-av-accent) 14%, transparent); box-shadow: inset 3px 0 var(--mango-av-accent); }\n mark { background: #ffe770; }\n .speaker { font-weight: 700; }\n`;\n\nconst highlight = (text: string, query: string): string => {\n const escaped = escapeHtml(text);\n if (!query) return escaped;\n const safeQuery = query.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n return escaped.replace(new RegExp(`(${safeQuery})`, 'gi'), '<mark>$1</mark>');\n};\n\nexport class MangoAVTranscriptElement extends AVControllerElement {\n #root: ShadowRoot;\n #cues: TranscriptCue[] = [];\n #transcriptIndex = 0;\n #query = '';\n #loadToken = 0;\n\n constructor() {\n super();\n this.#root = this.attachShadow({ mode: 'open' });\n }\n\n protected controllerChanged(): void {\n this.#cues = [];\n this.#transcriptIndex = 0;\n this.#render();\n void this.#loadTranscript();\n }\n\n protected handleControllerEvent<K extends keyof AVPlayerEventMap>(type: K, _event: CustomEvent<AVPlayerEventMap[K]>): void {\n if (type === 'av-ready' || type === 'av-canvaschange') {\n this.#cues = [];\n this.#transcriptIndex = 0;\n this.#render();\n void this.#loadTranscript();\n } else if (type === 'av-configchange') {\n this.#render();\n } else if (type === 'av-timeupdate' || type === 'av-seek' || type === 'av-statechange') {\n this.#sync();\n }\n }\n\n #render(): void {\n const canvas = this.controller.canvas;\n const transcripts = canvas?.transcripts ?? [];\n const resource = transcripts[this.#transcriptIndex];\n const query = this.#query.trim();\n const ui = resolveStrings(this.controller.config.strings);\n const cues = query ? this.#cues.filter((cue) => cue.text.toLowerCase().includes(query.toLowerCase())) : this.#cues;\n this.#root.innerHTML = `<style>${styles}</style><section class=\"transcript\" 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 ? `<select data-action=\"transcript\" aria-label=\"${escapeHtml(ui.transcript)}\">${transcripts.map((item, index) => `<option value=\"${index}\" ${index === this.#transcriptIndex ? 'selected' : ''}>${escapeHtml(item.label)}</option>`).join('')}</select>` : ''}\n ${this.controller.config.transcript.search ? `<input type=\"search\" data-action=\"search\" value=\"${escapeHtml(this.#query)}\" placeholder=\"${escapeHtml(ui.searchTranscript)}\" aria-label=\"${escapeHtml(ui.searchTranscript)}\">` : ''}\n ${this.controller.config.transcript.download && resource?.src ? `<a href=\"${escapeHtml(resource.src)}\" download>${escapeHtml(ui.download)}</a>` : ''}\n </div>\n ${this.#cues.length === 0 ? `<p class=\"empty\">${escapeHtml(ui.loadingTranscript)}</p>` : `<ol>${cues.map((cue) => `<li data-cue-id=\"${escapeHtml(cue.id)}\" ${cue.start !== undefined ? `data-start=\"${cue.start}\" data-end=\"${cue.end ?? cue.start}\"` : ''}>\n <button type=\"button\" ${cue.start === undefined ? 'disabled' : ''}>\n ${cue.start !== undefined ? `<span class=\"time\">${formatTime(cue.start)}</span>` : ''}\n ${cue.speaker ? `<span class=\"speaker\">${escapeHtml(cue.speaker)}</span>` : ''}\n <span>${highlight(cue.text, query)}</span>\n </button>\n </li>`).join('')}</ol>`}\n `}\n </section>`;\n this.#bind();\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 const input = event.target as HTMLInputElement;\n const selection = input.selectionStart;\n this.#query = input.value;\n this.#render();\n const next = this.#root.querySelector<HTMLInputElement>('[data-action=search]');\n next?.focus();\n if (selection !== null) next?.setSelectionRange(selection, selection);\n this.dispatchEvent(new CustomEvent('av-transcriptsearch', { detail: { query: this.#query }, bubbles: true, composed: true }));\n });\n this.#root.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 }\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 const options: Parameters<typeof loadTranscript>[1] = {};\n if (this.controller.config.fetch) options.fetch = this.controller.config.fetch;\n if (this.controller.config.requestInit) options.requestInit = this.controller.config.requestInit;\n if (this.controller.config.transcript.showNotes !== undefined) options.showNotes = this.controller.config.transcript.showNotes;\n if (this.controller.config.transcript.showMetadata !== undefined) options.showMetadata = this.controller.config.transcript.showMetadata;\n try {\n const cues = await loadTranscript(resource, options);\n if (token !== this.#loadToken) return;\n this.#cues = cues;\n this.#render();\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 this.#render();\n }\n }\n\n #sync(): void {\n const time = this.controller.state.currentTime;\n let active: HTMLElement | undefined;\n this.#root.querySelectorAll<HTMLElement>('[data-start][data-end]').forEach((row) => {\n const start = Number(row.dataset.start);\n const end = Number(row.dataset.end);\n const isActive = Number.isFinite(start) && time >= start && (!Number.isFinite(end) || time < end);\n row.querySelector('button')?.classList.toggle('active', isActive);\n if (isActive) active = row;\n });\n if (active && this.controller.config.transcript.autoScroll) active.scrollIntoView({ block: 'nearest', behavior: 'smooth' });\n }\n}\n\nexport const defineMangoAVTranscript = (tagName = 'mango-av-transcript'): typeof MangoAVTranscriptElement => {\n if (typeof customElements === 'undefined') return MangoAVTranscriptElement;\n if (!customElements.get(tagName)) customElements.define(tagName, MangoAVTranscriptElement);\n return MangoAVTranscriptElement;\n};\n"],"mappings":"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","names":[]}
|