portal

Landing page for your homelab
git clone https://git.woozle.org/neale/portal.git

portal / web
Neale Pickett  ·  2023-03-11

portal.mjs

  1import * as Stat from "./stat.mjs"
  2
  3const Millisecond = 1
  4const Second = 1000 * Millisecond
  5const Minute = 60 * Second
  6
  7class StatApp {
  8  constructor(parent) {
  9    this.parent= parent
 10    this.stat = new Stat.Stat()
 11    this.chart = new Stat.PieChart(parent, this.stat)
 12
 13    setInterval(()=>this.update(), 2 * Second)
 14    setTimeout(()=>this.update(), 500 * Millisecond)
 15    this.update()
 16  }
 17
 18  async update() {
 19    // Use non-standard checkVisibility method to avoid a pointless update
 20    if (this.parent.checkVisibility && !this.parent.checkVisibility()) {
 21      return
 22    }
 23    await this.stat.update()
 24    this.chart.update()
 25  }
 26}
 27
 28let frames = {}
 29function activate(event, element) {
 30  event.preventDefault()
 31
 32  let parent = element.parentElement
 33  for (let e of parent.getElementsByClassName("active")) {
 34    e.classList.remove("active")
 35  }
 36  element.classList.add("active")
 37  let href = element.href
 38  let app = document.querySelector("#app")
 39
 40  let frame = frames[href]
 41  if (frame) {
 42    frame.dispatchEvent(new Event("load"))
 43  } else {
 44    frame = app.appendChild(document.createElement("iframe"))
 45    frame.addEventListener("load", e => frameLoaded(frame))
 46    frame.src = href
 47    frames[href] = frame
 48  }
 49
 50  for (let fhref in frames) {
 51    let f = frames[fhref]
 52    if (fhref == href) {
 53      f.classList.remove("hidden")
 54    } else {
 55      f.classList.add("hidden")
 56    }
 57  }
 58}
 59
 60
 61function frameLoaded(frame) {
 62  let doc = frame.contentDocument
 63  if (doc.title.length > 0) {
 64    document.title = doc.title
 65  }
 66  let icon = document.querySelector("link[rel~='icon']")
 67  let dicon = doc.querySelector("link[rel~='icon']")
 68  if (dicon) {
 69    icon.href = dicon.href
 70  } else {
 71    icon.href = defaultIcon
 72  }
 73}
 74
 75let defaultIcon = null
 76async function init() {
 77  let doc = document.querySelector("iframe").contentDocument
 78
 79  defaultIcon = document.querySelector("link[rel~='icon']").href
 80
 81  for (let l of document.head.querySelectorAll("style")) {
 82    doc.head.appendChild(l.cloneNode(true))
 83  }
 84  for (let l of document.head.querySelectorAll("link[rel='stylesheet']")) {
 85    doc.head.appendChild(l.cloneNode())
 86  }
 87  for (let f of document.querySelectorAll("#app iframe")) {
 88    frames[f.src] = f
 89  }
 90
 91
 92  let icons = doc.body.appendChild(doc.createElement("section"))
 93  icons.classList.add("icons")
 94
 95  let nav = document.querySelector("nav")
 96  let resp = await fetch("portal.json")
 97  let obj = await resp.json()
 98  for (let app of obj) {
 99    let hlink = null
100    if (app.target != "_blank") {
101      hlink = nav.appendChild(document.createElement("a"))
102      hlink.href = app.href
103      hlink.textContent = app.title
104      if (app.target) {
105        hlink.target = app.target
106      } else {
107        hlink.addEventListener("click", event => activate(event, hlink))
108      }
109    }
110
111    let dlink = icons.appendChild(doc.createElement("a"))
112    dlink.href = app.href
113    if (app.target) {
114      dlink.target = app.target
115    } else {
116      dlink.addEventListener("click", event => activate(event, hlink))
117    }
118    if (app.icon) {
119      let icon = dlink.appendChild(doc.createElement("img"))
120      icon.src = app.icon
121      icon.alt = app.title
122      icon.title = app.title
123      icon.style.objectFit = "cover"
124    } else if (app.app) {
125      if (app.app == "stat") {
126        new StatApp(dlink)
127      }
128    } else {
129      let text = dlink.appendChild(doc.createElement("div"))
130      text.textContent = app.title
131    }
132  }
133}
134
135if (document.readyState === "loading") {
136  document.addEventListener("DOMContentLoaded", init)
137} else {
138  init()
139}