class MainMenu { DocumentID:string = "main-menu" HTMLTag:string = "LI" ImagePath:string = "img/" createIMG(src):any { var element = document.createElement("IMG") element.setAttribute("src", this.ImagePath + src) return element } createValue(value):any { var element = document.createElement("P") element.textContent = value return element } } class MainMenuItem extends MainMenu { menuKey:string value:string imgSrc:string headline:string id:string tableHeader:string[] constructor(menuKey:string, value:string, image:string, headline:string) { super() this.menuKey = menuKey this.value = value this.imgSrc = image this.headline = headline } createItem():void { var item = document.createElement("LI") item.addEventListener("click", function() { openThisMenu(item) }) item.setAttribute("id", this.id) item.setAttribute("data-menu", this.menuKey) var img = this.createIMG(this.imgSrc) var value = this.createValue(this.value) item.appendChild(img) item.appendChild(value) var doc = document.getElementById(this.DocumentID) doc.appendChild(item) switch(this.menuKey) { case "playlist": this.tableHeader = ["Playlist", "Tuner", "Last Update", "Availability %", "Type", "Streams", "group-title %", "tvg-id %", "Unique ID %"] break case "xmltv": this.tableHeader = ["Guide", "Last Update", "Availability %", "Channels", "Programs"] break case "filter": this.tableHeader = ["Filter Name", "Filter Type", "Filter"] break case "users": this.tableHeader = ["Username", "Password", "WEB", "PMS", "M3U", "XML", "API"] break case "mapping": this.tableHeader = ["BULK", "Ch. No.", "Logo", "Channel Name", "Playlist", "Group Title", "XMLTV File", "XMLTV ID", "Edit"] break } //console.log(this.menuKey, this.tableHeader); } } var SHELL_LAYOUT_READY:boolean = false function setLayoutMenuState(open:boolean) { if (document.body == null) { return } if (open == true) { document.body.classList.add("menu-open") } else { document.body.classList.remove("menu-open") } } function toggleLayoutMenu() { if (document.body == null) { return } var isOpen:boolean = document.body.classList.contains("menu-open") setLayoutMenuState(!isOpen) } function closeLayoutMenuIfMobile() { if (window.innerWidth <= 900) { setLayoutMenuState(false) } } function setActiveMenu(menuID:string) { ACTIVE_MENU_ID = menuID.toString() var menu = document.getElementById("main-menu") if (menu == null) { return } var items = menu.getElementsByTagName("LI") for (var i = 0; i < items.length; i++) { items[i].classList.remove("menu-active") } var activeItem = document.getElementById(ACTIVE_MENU_ID) var activeMenuKey:string = "" if (activeItem != null) { activeItem.classList.add("menu-active") var menuKeyValue = activeItem.getAttribute("data-menu") if (menuKeyValue != null) { activeMenuKey = menuKeyValue } } if (document.body != null) { if (activeMenuKey.length > 0) { document.body.setAttribute("data-active-menu", activeMenuKey) } else { document.body.removeAttribute("data-active-menu") } if (activeMenuKey == "log") { document.body.classList.add("menu-log-focus") } else { document.body.classList.remove("menu-log-focus") } } } function renderStatusCards() { var wrapper = document.getElementById("status-cards") if (wrapper == null || SERVER.hasOwnProperty("clientInfo") == false) { return } var info = SERVER["clientInfo"] var errors:number = parseInt(info["errors"], 10) var warnings:number = parseInt(info["warnings"], 10) var cards:any[] = [ {label: "Streams", value: info["streams"], tone: "ok"}, {label: "EPG Source", value: info["epgSource"], tone: "neutral"}, {label: "XEPG Channels", value: info["xepg"], tone: "ok"}, {label: "Errors", value: info["errors"], tone: errors > 0 ? "error" : "ok"}, {label: "Warnings", value: info["warnings"], tone: warnings > 0 ? "warn" : "ok"}, {label: "DVR", value: info["DVR"], tone: "neutral"}, ] wrapper.innerHTML = "" cards.forEach(card => { var box = document.createElement("DIV") box.className = "status-card status-card-" + card.tone var label = document.createElement("P") label.className = "status-card-label" label.innerText = card.label var value = document.createElement("P") value.className = "status-card-value" if (card.value == undefined || card.value == "") { value.innerText = "-" } else { value.innerText = card.value } box.appendChild(label) box.appendChild(value) wrapper.appendChild(box) }); } function initShellLayout() { if (SHELL_LAYOUT_READY == true) { return } var toggle = document.getElementById("menu-toggle") if (toggle != null) { toggle.onclick = function() { toggleLayoutMenu() } } var overlay = document.getElementById("layout-overlay") if (overlay != null) { overlay.onclick = function() { setLayoutMenuState(false) } } document.addEventListener("keydown", function(event) { if (event.key == "Escape") { setLayoutMenuState(false) showElement("popup", false) return } if (event.key == "/") { var target = event.target as HTMLElement var onInput = target.tagName == "INPUT" || target.tagName == "TEXTAREA" || target.tagName == "SELECT" if (onInput == true) { return } var search = document.getElementById("searchMapping") if (search != null) { event.preventDefault(); (search as HTMLInputElement).focus() } } }); setConnectionState("idle") SHELL_LAYOUT_READY = true } function shouldPollLogs():boolean { if (document.hidden == true) { return false } if (document.getElementById("content_log") == null) { return false } if (ACTIVE_MENU_ID.length < 1) { return false } var activeItem = document.getElementById(ACTIVE_MENU_ID) if (activeItem == null) { return false } return activeItem.getAttribute("data-menu") == "log" } function PageReady() { initShellLayout() var server:Server = new Server("getServerConfig") server.request(new Object()) var bootstrapAttempts:number = 0 var maxBootstrapAttempts:number = 5 var bootstrapTimer:number = window.setInterval(function() { if (SERVER.hasOwnProperty("clientInfo") == true) { window.clearInterval(bootstrapTimer) return } if (WS.hasQueued("getServerConfig")) { return } bootstrapAttempts++ var retryServer:Server = new Server("getServerConfig") retryServer.request(new Object()) if (bootstrapAttempts >= maxBootstrapAttempts) { window.clearInterval(bootstrapTimer) } }, 3000) window.addEventListener("resize", function(){ if (window.innerWidth > 900) { setLayoutMenuState(false) } calculateWrapperHeight(); }, true); setInterval(function(){ if (shouldPollLogs() == false) { return } updateLog() }, 10000); return } function isClientInfoHttpURL(value:string):boolean { return /^https?:\/\//i.test(value) } function setClientInfoValue(key:string, value:any) { var element = document.getElementById(key) if (element == null) { return } var textValue = "" if (value != undefined && value != null) { textValue = String(value) } if ((key == "m3u-url" || key == "xepg-url") && isClientInfoHttpURL(textValue)) { element.innerHTML = "" var anchor = document.createElement("a") anchor.href = textValue anchor.target = "_blank" anchor.rel = "noopener noreferrer" anchor.textContent = textValue element.appendChild(anchor) return } element.textContent = textValue } // menuItemVisible reports whether a menu entry applies to the current server state. function menuItemVisible(item:MainMenuItem):boolean { switch (item.menuKey) { case "users": case "logout": return SERVER["settings"]["authentication.web"] == true case "mapping": case "xmltv": return SERVER["clientInfo"]["epgSource"] == "XEPG" default: return true } } // renderedMenuKeys lists the menu entries currently in the document, in order. function renderedMenuKeys(menu:HTMLElement):string[] { var keys:string[] = [] var items = menu.getElementsByTagName("LI") for (var i = 0; i < items.length; i++) { keys.push(items[i].getAttribute("data-menu")) } return keys } function createLayout() { // Client Info var obj = SERVER["clientInfo"] var keys = getObjKeys(obj); for (var i = 0; i < keys.length; i++) { setClientInfoValue(keys[i], obj[keys[i]]) } renderStatusCards() var menu = document.getElementById("main-menu") if (menu == null) { return } // Menu: rebuilt only when the set of visible entries changes, so that // keyboard focus inside the list survives routine server responses. var visibleKeys:string[] = [] for (let i = 0; i < menuItems.length; i++) { menuItems[i].id = i if (menuItemVisible(menuItems[i])) { visibleKeys.push(menuItems[i].menuKey) } } if (visibleKeys.join(",") != renderedMenuKeys(menu).join(",")) { menu.innerHTML = "" for (let i = 0; i < menuItems.length; i++) { if (menuItemVisible(menuItems[i])) { menuItems[i].createItem() } } } if (ACTIVE_MENU_ID.length > 0 && document.getElementById(ACTIVE_MENU_ID)) { setActiveMenu(ACTIVE_MENU_ID) } var content = document.getElementById("content") if (ACTIVE_MENU_ID.length == 0 && content != null) { if (content.innerHTML.replace(/\\s/g, "").length == 0) { var firstItem = menu.getElementsByTagName("LI")[0] as HTMLElement if (firstItem != undefined) { firstItem.click() } } } return }