Melies/editor/public/static/js/tracks_table.mjs

173 lines
5.2 KiB
JavaScript

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)
}
}