continuous-integration/drone/push Build is passing
TypeScript:
- menu_ts.ts split into menu.ts, content.ts, popup.ts and xmltv.ts; the
other files drop their _ts suffix. Bundle order fixed in ts/tsconfig.json.
- All 94 string event handlers (setAttribute("onclick", "javascript: ..."))
replaced with addEventListener closures. changeButtonAction, which
rewrote an onclick attribute from select values, is gone.
- Settings rows are generated from one SETTINGS_FIELDS table instead of
~20 copy-pasted blocks; rendered HTML is byte-identical to before.
saveSettings now serialises password inputs, so a changed Plex token is
actually sent (the server treats the mask as unchanged).
- createLayout rebuilds the menu list only when the set of visible items
changes, so focus in the menu survives a refresh.
- announce()/alertUser(): alerts are mirrored into an aria-live region.
- Filter popup offers a third type, Regular Expression (regex-filter), and
the filter table labels it. Playlist popups get a Buffer select
(default / none / xTeVe / FFmpeg / VLC) saved as the playlist's
"buffer" parameter; the tuner field stays editable when the
playlist's own buffer is active.
Tests: tests/ holds 27 jsdom tests (Node's built-in runner, jsdom pinned)
that load the built bundle with a fixture server payload: menu visibility
rules, mapping table renders names as text, settings panel fields, popup
flows, sorting, bulk select, layout refresh keeps nodes, live region,
regex option, buffer select. npm test runs in the Drone webui-check step.
README-DEV documents the layout and the test workflow.
1172 lines
35 KiB
TypeScript
1172 lines
35 KiB
TypeScript
class PopupWindow {
|
|
DocumentID:string = "popup-custom"
|
|
InteractionID:string = "interaction"
|
|
doc = document.getElementById(this.DocumentID)
|
|
|
|
createTitle(title:string):any {
|
|
var td = document.createElement("TD")
|
|
td.className = "left"
|
|
td.innerHTML = title + ":"
|
|
return td
|
|
}
|
|
|
|
createContent(element):any {
|
|
var td = document.createElement("TD")
|
|
td.appendChild(element)
|
|
return td
|
|
}
|
|
|
|
createInteraction():any {
|
|
var div = document.createElement("div")
|
|
div.setAttribute("id", "popup-interaction")
|
|
div.className = "interaction"
|
|
this.doc.appendChild(div)
|
|
}
|
|
}
|
|
|
|
class PopupContent extends PopupWindow{
|
|
|
|
table = document.createElement("TABLE")
|
|
|
|
createHeadline(headline):void {
|
|
this.doc.innerHTML = ""
|
|
var element = document.createElement("H3")
|
|
element.innerHTML = headline.toUpperCase()
|
|
this.doc.appendChild(element)
|
|
|
|
// Tabelle erstellen
|
|
this.table = document.createElement("TABLE")
|
|
this.doc.appendChild(this.table)
|
|
}
|
|
|
|
appendRow(title:string, element:any):void {
|
|
var tr = document.createElement("TR")
|
|
|
|
// Bezeichnung
|
|
if (title.length != 0) {
|
|
tr.appendChild(this.createTitle(title))
|
|
}
|
|
|
|
|
|
// Content
|
|
tr.appendChild(this.createContent(element))
|
|
this.table.appendChild(tr)
|
|
}
|
|
|
|
|
|
createInput(type:string, name:string, value:string):any {
|
|
|
|
var input = document.createElement("INPUT")
|
|
if (value == undefined) {
|
|
value = ""
|
|
}
|
|
|
|
input.setAttribute("type", type)
|
|
input.setAttribute("name", name)
|
|
input.setAttribute("value", value)
|
|
return input
|
|
}
|
|
|
|
createCheckbox(name:string):any {
|
|
var input = document.createElement("INPUT")
|
|
|
|
input.setAttribute("type", "checkbox")
|
|
input.setAttribute("name", name)
|
|
return input
|
|
}
|
|
|
|
createSelect(text:string[], values:string[], set:string, dbKey:string):any {
|
|
var select = document.createElement("SELECT")
|
|
select.setAttribute("name", dbKey)
|
|
for (let i = 0; i < text.length; i++) {
|
|
var option = document.createElement("OPTION")
|
|
option.setAttribute("value", values[i])
|
|
option.innerText = text[i]
|
|
select.appendChild(option)
|
|
}
|
|
if(set != "") {
|
|
(select as HTMLSelectElement).value = set
|
|
}
|
|
|
|
if (set == undefined) {
|
|
(select as HTMLSelectElement).value = values[0]
|
|
}
|
|
|
|
return select
|
|
}
|
|
|
|
selectOption(select:any, value:string):any {
|
|
//select.selectedOptions = value
|
|
var s:HTMLSelectElement = (select as HTMLSelectElement)
|
|
s.options[s.selectedIndex].value = value
|
|
return select
|
|
}
|
|
|
|
// isHTML must only be true for static text written in the source; never for data.
|
|
description(value:string, isHTML:boolean = false):any {
|
|
var tr = document.createElement("TR")
|
|
var td = document.createElement("TD")
|
|
var span = document.createElement("PRE")
|
|
|
|
if (isHTML) {
|
|
span.innerHTML = value
|
|
} else {
|
|
span.textContent = value
|
|
}
|
|
|
|
tr.appendChild(td)
|
|
|
|
tr.appendChild(this.createContent(span))
|
|
|
|
this.table.appendChild(tr)
|
|
}
|
|
|
|
// Interaktion
|
|
addInteraction(element:any) {
|
|
var interaction = document.getElementById("popup-interaction")
|
|
interaction.appendChild(element)
|
|
}
|
|
}
|
|
|
|
// Buffer choices a playlist can make; "default" means the global setting.
|
|
const PLAYLIST_BUFFER_OPTIONS = [
|
|
{ text: "Default (global setting)", value: "default" },
|
|
{ text: "No buffer (pass the URL through)", value: "-" },
|
|
{ text: "xTeVe", value: "xteve" },
|
|
{ text: "FFmpeg", value: "ffmpeg" },
|
|
{ text: "VLC", value: "vlc" }
|
|
]
|
|
|
|
function createBufferSelect(content:PopupContent, data:object):HTMLSelectElement {
|
|
var text:string[] = PLAYLIST_BUFFER_OPTIONS.map(function(o) { return o.text })
|
|
var values:string[] = PLAYLIST_BUFFER_OPTIONS.map(function(o) { return o.value })
|
|
var current:string = data["buffer"]
|
|
if (values.indexOf(current) == -1) {
|
|
current = "default"
|
|
}
|
|
return content.createSelect(text, values, current, "buffer")
|
|
}
|
|
|
|
// playlistBufferActive: is a buffer in use for this playlist (its own choice,
|
|
// or the global setting when it has none)? Decides whether the tuner count
|
|
// can be edited.
|
|
function playlistBufferActive(data:object):boolean {
|
|
var own:string = data["buffer"]
|
|
if (own != undefined && own != "default" && own != "") {
|
|
return own != "-"
|
|
}
|
|
return SERVER["settings"]["buffer"] != "-"
|
|
}
|
|
|
|
function openPopUp(dataType, element?) {
|
|
|
|
var data:object = new Object();
|
|
var id:any
|
|
switch (element) {
|
|
case undefined:
|
|
|
|
switch (dataType) {
|
|
case "group-title":
|
|
if (id == undefined) {
|
|
id = -1
|
|
}
|
|
data = getLocalData("filter", id)
|
|
data["type"] = "group-title"
|
|
break;
|
|
|
|
case "custom-filter":
|
|
if (id == undefined) {
|
|
id = -1
|
|
}
|
|
data = getLocalData("filter", id)
|
|
data["type"] = "custom-filter"
|
|
break;
|
|
|
|
case "regex-filter":
|
|
if (id == undefined) {
|
|
id = -1
|
|
}
|
|
data = getLocalData("filter", id)
|
|
data["type"] = "regex-filter"
|
|
break;
|
|
|
|
default:
|
|
data["id.provider"] = "-"
|
|
data["type"] = dataType
|
|
id = "-"
|
|
break;
|
|
}
|
|
|
|
break
|
|
|
|
default:
|
|
id = element.id
|
|
data = getLocalData(dataType, id)
|
|
break;
|
|
}
|
|
|
|
var content:PopupContent = new PopupContent()
|
|
|
|
switch (dataType) {
|
|
case "playlist":
|
|
content.createHeadline("Playlist type")
|
|
// Type
|
|
var text:string[] = ["M3U", "HDHomeRun"]
|
|
var values:string[] = ["m3u", "hdhr"]
|
|
var select = content.createSelect(text, values, "", "type")
|
|
select.setAttribute("id", "type")
|
|
content.appendRow("Type", select)
|
|
const typeSelect:HTMLSelectElement = select
|
|
|
|
// Interaktion
|
|
content.createInteraction()
|
|
// Abbrechen
|
|
var input = content.createInput("button", "cancel", "Cancel")
|
|
input.addEventListener("click", function() { showElement("popup", false) })
|
|
content.addInteraction(input)
|
|
|
|
// Weiter
|
|
var input = content.createInput("button", "next", "Next")
|
|
input.addEventListener("click", function() { openPopUp(typeSelect.value) })
|
|
input.setAttribute("id", 'next')
|
|
content.addInteraction(input)
|
|
break
|
|
|
|
case "m3u":
|
|
content.createHeadline(dataType)
|
|
// Name
|
|
var dbKey:string = "name"
|
|
var input = content.createInput("text", dbKey, data[dbKey])
|
|
input.setAttribute("placeholder", "Playlist name")
|
|
content.appendRow("Name", input)
|
|
|
|
// Beschreibung
|
|
var dbKey:string = "description"
|
|
var input = content.createInput("text", dbKey, data[dbKey])
|
|
input.setAttribute("placeholder", "Description")
|
|
content.appendRow("Description", input)
|
|
|
|
// URL
|
|
var dbKey:string = "file.source"
|
|
var input = content.createInput("text", dbKey, data[dbKey])
|
|
input.setAttribute("placeholder", "File path or URL of the M3U")
|
|
content.appendRow("M3U File", input)
|
|
|
|
// Buffer (per playlist override of the global setting)
|
|
var bufferSelect = createBufferSelect(content, data)
|
|
content.appendRow("Buffer", bufferSelect)
|
|
|
|
// Tuner
|
|
if (playlistBufferActive(data)) {
|
|
var text:string[] = new Array()
|
|
var values:string[] = new Array()
|
|
|
|
for (var i = 1; i <= 100; i++) {
|
|
text.push(i.toString())
|
|
values.push(i.toString())
|
|
}
|
|
|
|
var dbKey:string = "tuner"
|
|
var select = content.createSelect(text, values, data[dbKey], dbKey)
|
|
content.appendRow("Tuner / Streams", select)
|
|
} else {
|
|
var dbKey:string = "tuner"
|
|
if (data[dbKey] == undefined) {
|
|
data[dbKey] = 1
|
|
}
|
|
var input = content.createInput("text", dbKey, data[dbKey])
|
|
input.setAttribute("readonly", "true")
|
|
input.className = "notAvailable"
|
|
content.appendRow("Tuner / Streams", input)
|
|
}
|
|
|
|
content.description("Number of parallel connections that can be established to the provider. <br>Only available with activated buffer.<br>New settings will only be applied after quitting all streams.", true)
|
|
|
|
// Interaktion
|
|
content.createInteraction()
|
|
// Löschen
|
|
if (data["id.provider"]!= "-") {
|
|
var input = content.createInput("button", "delete", "Delete")
|
|
input.className = "delete"
|
|
input.addEventListener("click", function() { savePopupData("m3u", String(id), true, 0) })
|
|
content.addInteraction(input)
|
|
} else {
|
|
var input = content.createInput("button", "back", "Back")
|
|
input.addEventListener("click", function() { openPopUp("playlist") })
|
|
content.addInteraction(input)
|
|
}
|
|
|
|
// Abbrechen
|
|
var input = content.createInput("button", "cancel", "Cancel")
|
|
input.addEventListener("click", function() { showElement("popup", false) })
|
|
content.addInteraction(input)
|
|
|
|
// Aktualisieren
|
|
if (data["id.provider"]!= "-") {
|
|
var input = content.createInput("button", "update", "Update")
|
|
input.addEventListener("click", function() { savePopupData("m3u", String(id), false, 1) })
|
|
content.addInteraction(input)
|
|
}
|
|
|
|
// Speichern
|
|
var input = content.createInput("button", "save", "Save")
|
|
input.addEventListener("click", function() { savePopupData("m3u", String(id), false, 0) })
|
|
content.addInteraction(input)
|
|
break
|
|
|
|
case "hdhr":
|
|
content.createHeadline(dataType)
|
|
// Name
|
|
var dbKey:string = "name"
|
|
var input = content.createInput("text", dbKey, data[dbKey])
|
|
input.setAttribute("placeholder", "Playlist name")
|
|
content.appendRow("Name", input)
|
|
|
|
// Beschreibung
|
|
var dbKey:string = "description"
|
|
var input = content.createInput("text", dbKey, data[dbKey])
|
|
input.setAttribute("placeholder", "Description")
|
|
content.appendRow("Description", input)
|
|
|
|
// URL
|
|
var dbKey:string = "file.source"
|
|
var input = content.createInput("text", dbKey, data[dbKey])
|
|
input.setAttribute("placeholder", "IP address and port (192.168.1.10:5004)")
|
|
content.appendRow("HDHomeRun IP", input)
|
|
|
|
// Buffer (per playlist override of the global setting)
|
|
var bufferSelect = createBufferSelect(content, data)
|
|
content.appendRow("Buffer", bufferSelect)
|
|
|
|
// Tuner
|
|
if (playlistBufferActive(data)) {
|
|
var text:string[] = new Array()
|
|
var values:string[] = new Array()
|
|
|
|
for (var i = 1; i <= 100; i++) {
|
|
text.push(i.toString())
|
|
values.push(i.toString())
|
|
}
|
|
|
|
var dbKey:string = "tuner"
|
|
var select = content.createSelect(text, values, data[dbKey], dbKey)
|
|
content.appendRow("Tuner / Streams", select)
|
|
} else {
|
|
var dbKey:string = "tuner"
|
|
if (data[dbKey] == undefined) {
|
|
data[dbKey] = 1
|
|
}
|
|
var input = content.createInput("text", dbKey, data[dbKey])
|
|
input.setAttribute("readonly", "true")
|
|
input.className = "notAvailable"
|
|
content.appendRow("Tuner / Streams", input)
|
|
}
|
|
|
|
content.description("Number of parallel connections that can be established to the provider. <br>Only available with activated buffer.<br>New settings will only be applied after quitting all streams.", true)
|
|
|
|
// Interaktion
|
|
content.createInteraction()
|
|
// Löschen
|
|
if (data["id.provider"]!= "-") {
|
|
var input = content.createInput("button", "delete", "Delete")
|
|
input.addEventListener("click", function() { savePopupData("hdhr", String(id), true, 0) })
|
|
input.className = "delete"
|
|
content.addInteraction(input)
|
|
} else {
|
|
var input = content.createInput("button", "back", "Back")
|
|
input.addEventListener("click", function() { openPopUp("playlist") })
|
|
content.addInteraction(input)
|
|
}
|
|
|
|
// Abbrechen
|
|
var input = content.createInput("button", "cancel", "Cancel")
|
|
input.addEventListener("click", function() { showElement("popup", false) })
|
|
content.addInteraction(input)
|
|
|
|
// Aktualisieren
|
|
if (data["id.provider"]!= "-") {
|
|
var input = content.createInput("button", "update", "Update")
|
|
input.addEventListener("click", function() { savePopupData("hdhr", String(id), false, 1) })
|
|
content.addInteraction(input)
|
|
}
|
|
|
|
// Speichern
|
|
var input = content.createInput("button", "save", "Save")
|
|
input.addEventListener("click", function() { savePopupData("hdhr", String(id), false, 0) })
|
|
content.addInteraction(input)
|
|
break
|
|
|
|
case "filter":
|
|
content.createHeadline(dataType)
|
|
|
|
// Type
|
|
var dbKey:string = "type"
|
|
var text:string[] = ["M3U: " + "Group Title", "xTeVe: " + "Custom Filter", "xTeVe: " + "Regular Expression"]
|
|
var values:string[] = ["group-title", "custom-filter", "regex-filter"]
|
|
var select = content.createSelect(text, values, "group-title", dbKey)
|
|
select.setAttribute("id", id)
|
|
content.appendRow("Type", select)
|
|
const filterTypeSelect:HTMLSelectElement = select
|
|
|
|
// Interaktion
|
|
content.createInteraction()
|
|
// Abbrechen
|
|
var input = content.createInput("button", "cancel", "Cancel")
|
|
input.addEventListener("click", function() { showElement("popup", false) })
|
|
content.addInteraction(input)
|
|
|
|
// Weiter
|
|
var input = content.createInput("button", "next", "Next")
|
|
input.addEventListener("click", function() { openPopUp(filterTypeSelect.value) })
|
|
input.setAttribute("id", 'next')
|
|
content.addInteraction(input)
|
|
break
|
|
|
|
case "custom-filter":
|
|
case "group-title":
|
|
case "regex-filter":
|
|
|
|
switch (dataType) {
|
|
case "custom-filter":
|
|
content.createHeadline("Custom")
|
|
break;
|
|
|
|
case "group-title":
|
|
content.createHeadline("Group")
|
|
break;
|
|
|
|
case "regex-filter":
|
|
content.createHeadline("Regular Expression")
|
|
break;
|
|
}
|
|
|
|
// Name
|
|
var dbKey:string = "name"
|
|
var input = content.createInput("text", dbKey, data[dbKey])
|
|
input.setAttribute("placeholder", "Filter name")
|
|
content.appendRow("Filter Name", input)
|
|
|
|
// Beschreibung
|
|
var dbKey:string = "description"
|
|
var input = content.createInput("text", dbKey, data[dbKey])
|
|
input.setAttribute("placeholder", "Description")
|
|
content.appendRow("Description", input)
|
|
|
|
// Typ
|
|
var dbKey:string = "type"
|
|
var input = content.createInput("hidden", dbKey, data[dbKey])
|
|
content.appendRow("", input)
|
|
|
|
var filterType = data[dbKey]
|
|
|
|
switch (filterType) {
|
|
|
|
case "custom-filter":
|
|
// Groß- Kleinschreibung beachten
|
|
var dbKey:string = "caseSensitive"
|
|
var input = content.createCheckbox(dbKey)
|
|
input.checked = data[dbKey]
|
|
content.appendRow("Case Sensitive", input)
|
|
|
|
// Filterregel (Benutzerdefiniert)
|
|
var dbKey:string = "filter"
|
|
var input = content.createInput("text", dbKey, data[dbKey])
|
|
input.setAttribute("placeholder", "Sport {HD} !{ES,IT}")
|
|
content.appendRow("Filter Rule", input)
|
|
|
|
break;
|
|
|
|
case "regex-filter":
|
|
// Groß- Kleinschreibung beachten
|
|
var dbKey:string = "caseSensitive"
|
|
var input = content.createCheckbox(dbKey)
|
|
input.checked = data[dbKey]
|
|
content.appendRow("Case Sensitive", input)
|
|
|
|
// Regulärer Ausdruck
|
|
var dbKey:string = "filter"
|
|
var input = content.createInput("text", dbKey, data[dbKey])
|
|
input.setAttribute("placeholder", "^(BBC|ITV) \\d+$")
|
|
content.appendRow("Regular Expression", input)
|
|
content.description("Go regular expression, tried against the channel name, then the group title, then the raw M3U attributes. Case-insensitive unless Case Sensitive is checked. Invalid patterns are rejected on save.")
|
|
|
|
break;
|
|
|
|
case "group-title":
|
|
//alert(dbKey + " " + filterType)
|
|
// Filter basierend auf den Gruppen in der M3U
|
|
var dbKey:string = "filter"
|
|
var groupsM3U = getLocalData("m3uGroups", "")
|
|
var text:string[] = groupsM3U["text"]
|
|
var values:string[] = groupsM3U["value"]
|
|
|
|
var select = content.createSelect(text, values, data[dbKey], dbKey)
|
|
markChangedOnChange(select)
|
|
content.appendRow("Group Title", select)
|
|
content.description("Select a M3U group. (Counter)<br>Changing the group title in the M3U invalidates the filter.", true)
|
|
|
|
// Groß- Kleinschreibung beachten
|
|
var dbKey:string = "caseSensitive"
|
|
var input = content.createCheckbox(dbKey)
|
|
input.checked = data[dbKey]
|
|
content.appendRow("Case Sensitive", input)
|
|
|
|
|
|
var dbKey:string = "include"
|
|
var input = content.createInput("text", dbKey, data[dbKey])
|
|
input.setAttribute("placeholder", "FHD,UHD")
|
|
|
|
content.appendRow("Include", input)
|
|
content.description("Channel name must include.<br>(Comma separated) Comma means or", true)
|
|
|
|
var dbKey:string = "exclude"
|
|
var input = content.createInput("text", dbKey, data[dbKey])
|
|
input.setAttribute("placeholder", "ES,IT")
|
|
content.appendRow("Exclude", input)
|
|
content.description("Channel name must not contain.<br>(Comma separated) Comma means or", true)
|
|
|
|
break
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Interaktion
|
|
content.createInteraction()
|
|
|
|
// Löschen
|
|
var input = content.createInput("button", "delete", "Delete")
|
|
input.addEventListener("click", function() { savePopupData("filter", String(id), true, 0) })
|
|
input.className = "delete"
|
|
content.addInteraction(input)
|
|
|
|
// Abbrechen
|
|
var input = content.createInput("button", "cancel", "Cancel")
|
|
input.addEventListener("click", function() { showElement("popup", false) })
|
|
content.addInteraction(input)
|
|
|
|
// Speichern
|
|
var input = content.createInput("button", "save", "Save")
|
|
input.addEventListener("click", function() { savePopupData("filter", String(id), false, 0) })
|
|
content.addInteraction(input)
|
|
|
|
break
|
|
|
|
case "xmltv":
|
|
content.createHeadline(dataType)
|
|
// Name
|
|
var dbKey:string = "name"
|
|
var input = content.createInput("text", dbKey, data[dbKey])
|
|
input.setAttribute("placeholder", "Guide name")
|
|
content.appendRow("Name", input)
|
|
|
|
// Beschreibung
|
|
var dbKey:string = "description"
|
|
var input = content.createInput("text", dbKey, data[dbKey])
|
|
input.setAttribute("placeholder", "Description")
|
|
content.appendRow("Description", input)
|
|
|
|
// URL
|
|
var dbKey:string = "file.source"
|
|
var input = content.createInput("text", dbKey, data[dbKey])
|
|
input.setAttribute("placeholder", "File path or URL of the XMLTV")
|
|
content.appendRow("XMLTV File", input)
|
|
|
|
// Interaktion
|
|
content.createInteraction()
|
|
// Löschen
|
|
if (data["id.provider"]!= "-") {
|
|
var input = content.createInput("button", "delete", "Delete")
|
|
input.addEventListener("click", function() { savePopupData("xmltv", String(id), true, 0) })
|
|
input.className = "delete"
|
|
content.addInteraction(input)
|
|
}
|
|
|
|
// Abbrechen
|
|
var input = content.createInput("button", "cancel", "Cancel")
|
|
input.addEventListener("click", function() { showElement("popup", false) })
|
|
content.addInteraction(input)
|
|
|
|
// Aktualisieren
|
|
if (data["id.provider"]!= "-") {
|
|
var input = content.createInput("button", "update", "Update")
|
|
input.addEventListener("click", function() { savePopupData("xmltv", String(id), false, 1) })
|
|
content.addInteraction(input)
|
|
}
|
|
|
|
// Speichern
|
|
var input = content.createInput("button", "save", "Save")
|
|
input.addEventListener("click", function() { savePopupData("xmltv", String(id), false, 0) })
|
|
content.addInteraction(input)
|
|
break
|
|
|
|
case "users":
|
|
content.createHeadline("Users")
|
|
// Benutzername
|
|
var dbKey:string = "username"
|
|
var input = content.createInput("text", dbKey, data[dbKey])
|
|
input.setAttribute("placeholder", "Username")
|
|
content.appendRow("Username", input)
|
|
|
|
// Neues Passwort
|
|
var dbKey:string = "password"
|
|
var input = content.createInput("password", dbKey, "")
|
|
input.setAttribute("placeholder", "Password")
|
|
content.appendRow("Password", input)
|
|
|
|
// Bestätigung
|
|
var dbKey:string = "confirm"
|
|
var input = content.createInput("password", dbKey, "")
|
|
input.setAttribute("placeholder", "Password confirm")
|
|
content.appendRow("Confirm", input)
|
|
|
|
// Berechtigung WEB
|
|
var dbKey:string = "authentication.web"
|
|
var input = content.createCheckbox(dbKey)
|
|
input.checked = data[dbKey]
|
|
if (data["defaultUser"] == true) {
|
|
input.addEventListener("click", function(event) { event.preventDefault() })
|
|
}
|
|
content.appendRow("Web Access", input)
|
|
|
|
// Berechtigung PMS
|
|
var dbKey:string = "authentication.pms"
|
|
var input = content.createCheckbox(dbKey)
|
|
input.checked = data[dbKey]
|
|
content.appendRow("PMS Access", input)
|
|
|
|
// Berechtigung M3U
|
|
var dbKey:string = "authentication.m3u"
|
|
var input = content.createCheckbox(dbKey)
|
|
input.checked = data[dbKey]
|
|
content.appendRow("M3U Access", input)
|
|
|
|
// Berechtigung XML
|
|
var dbKey:string = "authentication.xml"
|
|
var input = content.createCheckbox(dbKey)
|
|
input.checked = data[dbKey]
|
|
content.appendRow("XML Access", input)
|
|
|
|
// Berechtigung API
|
|
var dbKey:string = "authentication.api"
|
|
var input = content.createCheckbox(dbKey)
|
|
input.checked = data[dbKey]
|
|
content.appendRow("API Access", input)
|
|
|
|
// Interaktion
|
|
content.createInteraction()
|
|
|
|
// Löschen
|
|
if (data["defaultUser"]!= true && id != "-") {
|
|
var input = content.createInput("button", "delete", "Delete")
|
|
input.className = "delete"
|
|
input.addEventListener("click", function() { savePopupData(dataType, String(id), true, 0) })
|
|
content.addInteraction(input)
|
|
}
|
|
|
|
// Abbrechen
|
|
var input = content.createInput("button", "cancel", "Cancel")
|
|
input.addEventListener("click", function() { showElement("popup", false) })
|
|
content.addInteraction(input)
|
|
|
|
// Speichern
|
|
var input = content.createInput("button", "save", "Save")
|
|
input.addEventListener("click", function() { savePopupData(dataType, String(id), false, 0) })
|
|
content.addInteraction(input)
|
|
|
|
break
|
|
|
|
case "mapping":
|
|
content.createHeadline("Mapping")
|
|
// Aktiv
|
|
var dbKey:string = "x-active"
|
|
var input = content.createCheckbox(dbKey)
|
|
input.checked = data[dbKey]
|
|
input.id = "active"
|
|
//markChangedOnChange(input)
|
|
input.addEventListener("change", function() { toggleChannelStatus(id) })
|
|
content.appendRow("Active", input)
|
|
|
|
// Kanalname
|
|
var dbKey:string = "x-name"
|
|
var input = content.createInput("text", dbKey, data[dbKey])
|
|
markChangedOnChange(input)
|
|
if (BULK_EDIT == true) {
|
|
input.style.border = "solid 1px red"
|
|
input.setAttribute("readonly", "true")
|
|
}
|
|
content.appendRow("Channel Name", input)
|
|
|
|
content.description(data["name"])
|
|
|
|
// Beschreibung
|
|
var dbKey:string = "x-description"
|
|
var input = content.createInput("text", dbKey, data[dbKey])
|
|
input.setAttribute("placeholder", "Used by the Dummy as an XML description")
|
|
markChangedOnChange(input)
|
|
content.appendRow("Channel Description", input)
|
|
|
|
// Aktualisierung des Kanalnamens
|
|
if (data.hasOwnProperty("_uuid.key")) {
|
|
if (data["_uuid.key"] != "") {
|
|
var dbKey:string = "x-update-channel-name"
|
|
var input = content.createCheckbox(dbKey)
|
|
markChangedOnChange(input)
|
|
input.checked = data[dbKey]
|
|
content.appendRow("Update Channel Name", input)
|
|
}
|
|
}
|
|
|
|
// Logo URL (Kanal)
|
|
var dbKey:string = "tvg-logo"
|
|
var input = content.createInput("text", dbKey, data[dbKey])
|
|
markChangedOnChange(input)
|
|
input.setAttribute("id", "channel-icon")
|
|
content.appendRow("Logo URL", input)
|
|
|
|
// Aktualisierung des Kanallogos
|
|
var dbKey:string = "x-update-channel-icon"
|
|
var input = content.createCheckbox(dbKey)
|
|
input.checked = data[dbKey]
|
|
input.setAttribute("id", "update-icon")
|
|
markChangedOnChange(input, function() { changeChannelLogo(id) })
|
|
content.appendRow("Update Channel Logo", input)
|
|
|
|
// Erweitern der EPG Kategorie
|
|
var dbKey:string = "x-category"
|
|
var text:string[] = ["-", "Kids (Emby only)", "News", "Movie", "Series", "Sports"]
|
|
var values:string[] = ["", "Kids", "News", "Movie", "Series", "Sports"]
|
|
var select = content.createSelect(text, values, data[dbKey], dbKey)
|
|
markChangedOnChange(select)
|
|
content.appendRow("EPG Category", select)
|
|
|
|
// M3U Gruppentitel
|
|
var dbKey:string = "x-group-title"
|
|
var input = content.createInput("text", dbKey, data[dbKey])
|
|
markChangedOnChange(input)
|
|
content.appendRow("Group Title (xteve.m3u)", input)
|
|
|
|
if (data["group-title"] != undefined) {
|
|
content.description(data["group-title"])
|
|
}
|
|
|
|
// XMLTV Datei
|
|
var dbKey:string = "x-xmltv-file"
|
|
var xmlFile = data[dbKey]
|
|
var xmltv:XMLTVFile = new XMLTVFile()
|
|
var select = xmltv.getFiles(data[dbKey])
|
|
select.setAttribute("name", dbKey)
|
|
select.setAttribute("id", "popup-xmltv")
|
|
markChangedOnChange(select, function(element) { setXmltvChannel(id, element) })
|
|
content.appendRow("XMLTV File", select)
|
|
var file = data[dbKey]
|
|
|
|
// XMLTV Mapping
|
|
var dbKey:string = "x-mapping"
|
|
var xmltv:XMLTVFile = new XMLTVFile()
|
|
var select = xmltv.getPrograms(file, data[dbKey])
|
|
select.setAttribute("name", dbKey)
|
|
select.setAttribute("id", "popup-mapping")
|
|
markChangedOnChange(select, function(element) { checkXmltvChannel(id, element, xmlFile) })
|
|
|
|
sortSelect(select)
|
|
content.appendRow("XMLTV Channel", select)
|
|
|
|
// Interaktion
|
|
content.createInteraction()
|
|
|
|
// Logo hochladen
|
|
var input = content.createInput("button", "cancel", "Upload Logo")
|
|
input.addEventListener("click", function() { uploadLogo() })
|
|
content.addInteraction(input)
|
|
|
|
// Abbrechen
|
|
var input = content.createInput("button", "cancel", "Cancel")
|
|
input.addEventListener("click", function() { showElement("popup", false) })
|
|
content.addInteraction(input)
|
|
|
|
// Fertig
|
|
var ids:string[] = new Array()
|
|
ids = getAllSelectedChannels()
|
|
if (ids.length == 0) {
|
|
ids.push(id)
|
|
}
|
|
|
|
var input = content.createInput("button", "save", "Done")
|
|
input.addEventListener("click", function() { donePopupData(dataType, ids.join(",")) })
|
|
content.addInteraction(input)
|
|
break
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
showPopUpElement('popup-custom');
|
|
}
|
|
|
|
function savePopupData(dataType:string, id:string, remove:Boolean, option:number) {
|
|
|
|
if (dataType == "mapping") {
|
|
|
|
var data = new Object()
|
|
console.log("Save mapping data")
|
|
|
|
cmd = "saveEpgMapping"
|
|
data["epgMapping"] = SERVER["xepg"]["epgMapping"]
|
|
|
|
console.log("SEND TO SERVER");
|
|
|
|
var server:Server = new Server(cmd)
|
|
server.request(data)
|
|
|
|
delete UNDO["epgMapping"]
|
|
|
|
return
|
|
}
|
|
|
|
console.log("Save popup data")
|
|
var div = document.getElementById("popup-custom")
|
|
|
|
var inputs = div.getElementsByTagName("TABLE")[0].getElementsByTagName("INPUT");
|
|
var selects = div.getElementsByTagName("TABLE")[0].getElementsByTagName("SELECT");
|
|
|
|
var input = new Object();
|
|
var confirmMsg: string
|
|
|
|
for (let i = 0; i < selects.length; i++) {
|
|
|
|
var name:string
|
|
name = (selects[i] as HTMLSelectElement).name
|
|
var value = (selects[i] as HTMLSelectElement).value
|
|
|
|
switch (name) {
|
|
case "tuner":
|
|
input[name] = parseInt(value)
|
|
break;
|
|
|
|
default:
|
|
input[name] = value
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
for (let i = 0; i < inputs.length; i++) {
|
|
|
|
switch ((inputs[i] as HTMLInputElement).type) {
|
|
|
|
case "checkbox":
|
|
name = (inputs[i] as HTMLInputElement).name
|
|
input[name] = (inputs[i] as HTMLInputElement).checked
|
|
break
|
|
|
|
case "text":
|
|
case "hidden":
|
|
case "password":
|
|
|
|
name = (inputs[i] as HTMLInputElement).name
|
|
|
|
switch (name) {
|
|
case "tuner":
|
|
input[name] = parseInt((inputs[i] as HTMLInputElement).value)
|
|
break;
|
|
|
|
default:
|
|
input[name] = (inputs[i] as HTMLInputElement).value
|
|
break;
|
|
}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var data = new Object()
|
|
|
|
var cmd:string
|
|
|
|
if (remove == true) {
|
|
input["delete"] = true
|
|
}
|
|
|
|
switch (dataType) {
|
|
case "users":
|
|
|
|
confirmMsg = "Delete this user?"
|
|
if (id == "-") {
|
|
cmd = "saveNewUser"
|
|
data["userData"] = input
|
|
} else {
|
|
cmd = "saveUserData"
|
|
var d = new Object()
|
|
d[id] = input
|
|
data["userData"] = d
|
|
}
|
|
|
|
break;
|
|
|
|
case "m3u":
|
|
|
|
confirmMsg = "Delete this playlist?"
|
|
switch (option) {
|
|
// Popup: Save
|
|
case 0:
|
|
cmd = "saveFilesM3U"
|
|
break
|
|
|
|
// Popup: Update
|
|
case 1:
|
|
cmd = "updateFileM3U"
|
|
break
|
|
|
|
}
|
|
|
|
data["files"] = new Object
|
|
data["files"][dataType] = new Object
|
|
data["files"][dataType][id] = input
|
|
|
|
break
|
|
|
|
case "hdhr":
|
|
|
|
confirmMsg = "Delete this HDHomeRun tuner?"
|
|
switch (option) {
|
|
// Popup: Save
|
|
case 0:
|
|
cmd = "saveFilesHDHR"
|
|
break
|
|
|
|
// Popup: Update
|
|
case 1:
|
|
cmd = "updateFileHDHR"
|
|
break
|
|
|
|
}
|
|
|
|
data["files"] = new Object
|
|
data["files"][dataType] = new Object
|
|
data["files"][dataType][id] = input
|
|
|
|
break
|
|
|
|
case "xmltv":
|
|
|
|
confirmMsg = "Delete this XMLTV file?"
|
|
switch (option) {
|
|
// Popup: Save
|
|
case 0:
|
|
cmd = "saveFilesXMLTV"
|
|
break
|
|
|
|
// Popup: Update
|
|
case 1:
|
|
cmd = "updateFileXMLTV"
|
|
break
|
|
|
|
}
|
|
|
|
data["files"] = new Object
|
|
data["files"][dataType] = new Object
|
|
data["files"][dataType][id] = input
|
|
|
|
break
|
|
|
|
case "filter":
|
|
|
|
confirmMsg = "Delete this filter?"
|
|
cmd = "saveFilter"
|
|
data["filter"] = new Object
|
|
data["filter"][id] = input
|
|
break
|
|
|
|
default:
|
|
console.log(dataType, id);
|
|
return
|
|
break;
|
|
|
|
}
|
|
|
|
if (remove == true) {
|
|
|
|
if (!confirm(confirmMsg)) {
|
|
showElement("popup", false)
|
|
return
|
|
}
|
|
|
|
}
|
|
|
|
console.log("SEND TO SERVER");
|
|
|
|
console.log(data);
|
|
|
|
var server:Server = new Server(cmd)
|
|
server.request(data)
|
|
|
|
}
|
|
|
|
function donePopupData(dataType:string, idsStr:string) {
|
|
|
|
var ids:string[] = idsStr.split(',');
|
|
var div = document.getElementById("popup-custom")
|
|
var inputs = div.getElementsByClassName("changed")
|
|
|
|
ids.forEach(id => {
|
|
var input = new Object();
|
|
input = SERVER["xepg"]["epgMapping"][id]
|
|
|
|
console.log(input);
|
|
|
|
for (let i = 0; i < inputs.length; i++) {
|
|
|
|
var name:string
|
|
var value:any
|
|
|
|
switch (inputs[i].tagName) {
|
|
|
|
case "INPUT":
|
|
switch ((inputs[i] as HTMLInputElement).type) {
|
|
case "checkbox":
|
|
name = (inputs[i] as HTMLInputElement).name
|
|
value = (inputs[i] as HTMLInputElement).checked
|
|
input[name] = value
|
|
break
|
|
|
|
case "text":
|
|
name = (inputs[i] as HTMLInputElement).name
|
|
value = (inputs[i] as HTMLInputElement).value
|
|
input[name] = value
|
|
break
|
|
|
|
}
|
|
|
|
break
|
|
|
|
case "SELECT":
|
|
name = (inputs[i] as HTMLSelectElement).name
|
|
value = (inputs[i] as HTMLSelectElement).value
|
|
input[name] = value
|
|
break
|
|
|
|
}
|
|
|
|
switch (name) {
|
|
|
|
|
|
case "tvg-logo":
|
|
//(document.getElementById(id).childNodes[2].firstChild as HTMLElement).setAttribute("src", value)
|
|
break
|
|
|
|
case "x-name":
|
|
(document.getElementById(id).childNodes[3].firstChild as HTMLElement).textContent = value
|
|
break
|
|
|
|
case "x-category":
|
|
(document.getElementById(id).childNodes[3].firstChild as HTMLElement).className = value
|
|
break
|
|
|
|
case "x-group-title":
|
|
(document.getElementById(id).childNodes[5].firstChild as HTMLElement).textContent = value
|
|
break
|
|
|
|
case "x-xmltv-file":
|
|
if (value != "xTeVe Dummy" && value != "-") {
|
|
value = getValueFromProviderFile(value, "xmltv", "name")
|
|
}
|
|
|
|
if (value == "-") {
|
|
input["x-active"] = false
|
|
}
|
|
|
|
(document.getElementById(id).childNodes[6].firstChild as HTMLElement).textContent = value
|
|
break
|
|
|
|
case "x-mapping":
|
|
if (value == "-") {
|
|
input["x-active"] = false
|
|
}
|
|
|
|
(document.getElementById(id).childNodes[7].firstChild as HTMLElement).textContent = value
|
|
|
|
break
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
createSearchObj()
|
|
searchInMapping()
|
|
|
|
}
|
|
|
|
if (input["x-active"] == false) {
|
|
document.getElementById(id).className = "notActiveEPG"
|
|
} else {
|
|
document.getElementById(id).className = "activeEPG"
|
|
}
|
|
|
|
console.log(input["tvg-logo"]);
|
|
(document.getElementById(id).childNodes[2].firstChild as HTMLElement).setAttribute("src", input["tvg-logo"])
|
|
|
|
|
|
});
|
|
|
|
showElement("popup", false);
|
|
|
|
return
|
|
}
|
|
|
|
function showPreview(element:boolean) {
|
|
|
|
var div = document.getElementById("myStreamsBox")
|
|
switch (element) {
|
|
|
|
case false:
|
|
div.className = "notVisible"
|
|
return
|
|
break;
|
|
}
|
|
|
|
var streams:string[] = ["activeStreams", "inactiveStreams"]
|
|
|
|
streams.forEach(preview => {
|
|
|
|
var table = document.getElementById(preview)
|
|
table.innerHTML = ""
|
|
var obj:string[] = SERVER["data"]["StreamPreviewUI"][preview]
|
|
|
|
obj.forEach(channel => {
|
|
|
|
var tr = document.createElement("TR")
|
|
var tdKey = document.createElement("TD")
|
|
var tdVal = document.createElement("TD")
|
|
|
|
tdKey.className = "tdKey"
|
|
tdVal.className = "tdVal"
|
|
|
|
switch (preview) {
|
|
case "activeStreams":
|
|
tdKey.innerText = "Channel: (+)"
|
|
break;
|
|
|
|
case "inactiveStreams":
|
|
tdKey.innerText = "Channel: (-)"
|
|
break;
|
|
}
|
|
|
|
tdVal.innerText = channel
|
|
tr.appendChild(tdKey)
|
|
tr.appendChild(tdVal)
|
|
|
|
table.appendChild(tr)
|
|
|
|
});
|
|
|
|
});
|
|
|
|
showElement("loading", false)
|
|
div.className = "visible"
|
|
|
|
return
|
|
}
|