refactor: changed structure for containerization
This commit is contained in:
248
src/public/static/js/tracks_table.mjs
Normal file
248
src/public/static/js/tracks_table.mjs
Normal file
@ -0,0 +1,248 @@
|
||||
import { findLanguage, flattenObj, getLanguageOptions } from "./utils.mjs"
|
||||
|
||||
export class Track {
|
||||
constructor(table, idx, fields) {
|
||||
/** @type {TracksTable} */
|
||||
this.table = table
|
||||
|
||||
/** @type {number} */
|
||||
this.idx = idx
|
||||
|
||||
/** @type {object} */
|
||||
this.fields = flattenObj(fields)
|
||||
|
||||
this.row = null
|
||||
}
|
||||
|
||||
makeRow() {
|
||||
this.row = document.createElement("tr")
|
||||
this.row.dataset.i = this.idx
|
||||
this.table.fields.forEach(field => {
|
||||
const td = this.row.insertCell(-1)
|
||||
const input = this.makeInput(field, this.fields[field.key])
|
||||
td.appendChild(input)
|
||||
|
||||
if (field.key === "name") {
|
||||
const btn = document.getElementById("improve-btn").cloneNode(true)
|
||||
btn.id = null
|
||||
btn.classList.remove("template")
|
||||
btn.addEventListener("click", () => {
|
||||
this.improveName()
|
||||
btn.classList.add("clicked")
|
||||
setTimeout(() => btn.classList.remove("clicked"), 1000)
|
||||
})
|
||||
td.appendChild(btn)
|
||||
}
|
||||
})
|
||||
return this.row
|
||||
}
|
||||
|
||||
makeInput(field, value, listeners=true) {
|
||||
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"
|
||||
|
||||
getValue = () => input.checked
|
||||
const onehot = this.table.CONSTRAINTS[field.key]?.type == "onehot"
|
||||
|
||||
if (listeners && onehot) {
|
||||
if (value) {
|
||||
if (field.key in this.table.onehots) {
|
||||
this.table.editor.notify(
|
||||
`Error in metadata file: field '${field.name}' is onehot but multiple tracks are enabled. Only the first one will be enabled`,
|
||||
"error",
|
||||
20000
|
||||
)
|
||||
value = false
|
||||
} else {
|
||||
this.table.onehots[field.key] = input
|
||||
}
|
||||
}
|
||||
input.addEventListener("click", e => {
|
||||
if (!input.checked) {
|
||||
e.preventDefault()
|
||||
} else {
|
||||
if (field.key in this.table.onehots) {
|
||||
this.table.onehots[field.key].checked = false
|
||||
this.table.onehots[field.key].dispatchEvent(new Event("change"))
|
||||
}
|
||||
this.table.onehots[field.key] = input
|
||||
}
|
||||
})
|
||||
}
|
||||
input.checked = value
|
||||
break
|
||||
|
||||
case "sel":
|
||||
input = document.createElement("select")
|
||||
let options = this.table.OPTIONS[field.key]
|
||||
if (typeof options === "function") {
|
||||
options = options()
|
||||
}
|
||||
options.forEach(option => {
|
||||
const opt = document.createElement("option")
|
||||
opt.innerText = option.display
|
||||
opt.value = option.value
|
||||
input.appendChild(opt)
|
||||
})
|
||||
|
||||
if (field.key === "language") {
|
||||
const lang = findLanguage(value)
|
||||
if (lang === null) {
|
||||
this.table.editor.notify(
|
||||
`Unknown language '${value}' for ${this.table.type} track ${this.idx}`,
|
||||
"error",
|
||||
20000
|
||||
)
|
||||
} else if (lang !== value) {
|
||||
this.table.editor.notify(
|
||||
`Language of ${this.table.type} track ${this.idx} was corrected (${value} -> ${lang})`,
|
||||
"warning"
|
||||
)
|
||||
value = lang
|
||||
this.editValue(field.key, value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
input.value = value
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
input.name = field.key + "[]"
|
||||
input.dataset.key = field.key
|
||||
if (this.table.CONSTRAINTS[field.key]?.type === "readonly") {
|
||||
input.disabled = true
|
||||
}
|
||||
if (listeners) {
|
||||
input.addEventListener("change", () => {
|
||||
this.editValue(field.key, getValue())
|
||||
})
|
||||
}
|
||||
return input
|
||||
}
|
||||
|
||||
editValue(key, value) {
|
||||
this.fields[key] = value
|
||||
this.table.editTrack(this.idx, key, value)
|
||||
|
||||
const input = this.row.querySelector(`[data-key='${key}']`)
|
||||
if (input) {
|
||||
const fieldType = this.table.getFieldProps(key).type
|
||||
switch (fieldType) {
|
||||
case "bool":
|
||||
input.checked = value
|
||||
break
|
||||
default:
|
||||
input.value = value
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
improveName() {
|
||||
this.table.editor.integrityMgr.improveName(this)
|
||||
}
|
||||
}
|
||||
|
||||
export default class TracksTable {
|
||||
OPTIONS = {
|
||||
"language": getLanguageOptions
|
||||
}
|
||||
CONSTRAINTS = {
|
||||
"flags/default": {
|
||||
type: "onehot"
|
||||
},
|
||||
"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.dataKey = dataKey
|
||||
|
||||
this.tracks = []
|
||||
this.fields = []
|
||||
this.onehots = {}
|
||||
}
|
||||
|
||||
getFieldProps(key) {
|
||||
return this.fields.find(f => f.key == key)
|
||||
}
|
||||
|
||||
loadTracks(tracks) {
|
||||
this.clear()
|
||||
this.tracks = tracks.map((t, i) => new Track(this, i, t))
|
||||
if (tracks.length === 0) {
|
||||
return
|
||||
}
|
||||
this.detectFields()
|
||||
this.addHeaders()
|
||||
this.tracks.forEach(track => {
|
||||
this.body.appendChild(track.makeRow())
|
||||
})
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.tracks = []
|
||||
this.fields = []
|
||||
this.onehots = {}
|
||||
this.headers.innerHTML = ""
|
||||
this.body.innerHTML = ""
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user