import { flattenObj } from "./utils.mjs" export class Track { constructor(table, idx, fields) { this.table = table this.idx = idx this.fields = flattenObj(fields) } makeRow() { const tr = document.createElement("tr") tr.dataset.i = this.idx this.table.fields.forEach(field => { const td = tr.insertCell(-1) const input = this.makeInput(field, this.fields[field.key], this.idx) td.appendChild(input) }) return tr } makeInput(field, value) { let input = document.createElement("input") let getValue = () => input.value switch (field.type) { case "num": input.type = "number" input.value = value getValue = () => +input.value break case "str": input.type = "text" input.value = value break case "bool": input.type = "checkbox" input.checked = value getValue = () => input.checked const hotone = this.table.CONSTRAINTS[field.key]?.type == "hotone" if (hotone) { if (value) { if (field.key in this.table.hotones) { alert(`Error in metadata file: field ${field.name} is hotone but multiple tracks are enabled`) } this.table.hotones[field.key] = input } input.addEventListener("click", e => { if (!input.checked) { e.preventDefault() } else { if (field.key in this.table.hotones) { this.table.hotones[field.key].checked = false this.table.hotones[field.key].dispatchEvent(new Event("change")) } this.table.hotones[field.key] = input } }) } break case "sel": input = document.createElement("select") const options = this.table.OPTIONS[field.key] options.forEach(option => { const opt = document.createElement("option") opt.innerText = option opt.value = option input.appendChild(opt) }) input.value = value default: break } input.dataset.key = field.key if (this.table.CONSTRAINTS[field.key]?.type === "readonly") { input.disabled = true } input.addEventListener("change", () => { this.editValue(field.key, getValue()) }) return input } editValue(key, value) { this.fields[key] = value this.table.editTrack(this.idx, key, value) } } export default class TracksTable { OPTIONS = { "language": ["fre", "eng"] } CONSTRAINTS = { "flags/default": { type: "hotone" }, "index": { type: "readonly" }, "channels": { type: "readonly" } } /** * @param {import('./editor.mjs').default} editor The parent editor * @param {string} type The type of tracks. One of `['audio', 'subtitle']` * @param {string} tableId The id of the table element * @param {string} dataKey The key of the tracks list inside of the data object */ constructor(editor, type, tableId, dataKey) { this.editor = editor this.type = type this.table = document.getElementById(tableId) this.headers = this.table.querySelector("thead tr") this.body = this.table.querySelector("tbody") this.fields = [] this.tracks = [] this.dataKey = dataKey this.hotones = {} } loadTracks(tracks) { this.tracks = tracks.map((t, i) => new Track(this, i, t)) this.clear() if (tracks.length === 0) { return } this.detectFields() this.addHeaders() this.tracks.forEach(track => { this.body.appendChild(track.makeRow()) }) } clear() { this.headers.innerHTML = "" this.body.innerHTML = "" this.fields = [] } detectFields() { Object.entries(this.tracks[0].fields).forEach(([key, value]) => { let type = { boolean: "bool", number: "num" }[typeof value] ?? "str" if (key === "language") { type = "sel" } const name = key.split("/").slice(-1)[0] this.fields.push({name, type, key}) }) } addHeaders() { this.fields.forEach(field => { const th = document.createElement("th") th.innerText = field.name this.headers.appendChild(th) }) } editTrack(trackIdx, fieldKey, fieldValue) { this.editor.editTrack(this.dataKey, trackIdx, fieldKey, fieldValue) } }