|
@@ -1,32 +1,321 @@
|
|
|
import './ActionButton.js' // To define action-button
|
|
import './ActionButton.js' // To define action-button
|
|
|
|
|
|
|
|
-export function marshalActionButtonsJsonToHtml (json) {
|
|
|
|
|
|
|
+export function marshalDashboardComponentsJsonToHtml (json) {
|
|
|
|
|
+ marshalActionsJsonToHtml(json)
|
|
|
|
|
+ marshalDashboardStructureToHtml(json)
|
|
|
|
|
+
|
|
|
|
|
+ window.changeDirectory = changeDirectory
|
|
|
|
|
+ window.showSection = showSection
|
|
|
|
|
+
|
|
|
|
|
+ changeDirectory(null)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function marshalActionsJsonToHtml (json) {
|
|
|
const currentIterationTimestamp = Date.now()
|
|
const currentIterationTimestamp = Date.now()
|
|
|
|
|
|
|
|
|
|
+ window.actionButtons = {}
|
|
|
|
|
+
|
|
|
for (const jsonButton of json.actions) {
|
|
for (const jsonButton of json.actions) {
|
|
|
- let htmlButton = document.querySelector('#execution-' + jsonButton.id)
|
|
|
|
|
|
|
+ let htmlButton = window.actionButtons[jsonButton.id]
|
|
|
|
|
|
|
|
- if (htmlButton == null) {
|
|
|
|
|
|
|
+ if (typeof htmlButton === 'undefined') {
|
|
|
htmlButton = document.createElement('action-button')
|
|
htmlButton = document.createElement('action-button')
|
|
|
htmlButton.constructFromJson(jsonButton)
|
|
htmlButton.constructFromJson(jsonButton)
|
|
|
|
|
|
|
|
- document.getElementById('root-group').appendChild(htmlButton)
|
|
|
|
|
- } else {
|
|
|
|
|
- htmlButton.updateFromJson(jsonButton)
|
|
|
|
|
- htmlButton.updateDom()
|
|
|
|
|
|
|
+ window.actionButtons[jsonButton.title] = htmlButton
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ htmlButton.updateFromJson(jsonButton)
|
|
|
htmlButton.updateIterationTimestamp = currentIterationTimestamp
|
|
htmlButton.updateIterationTimestamp = currentIterationTimestamp
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Remove existing, but stale buttons (that were not updated in this round)
|
|
// Remove existing, but stale buttons (that were not updated in this round)
|
|
|
- for (const existingButton of document.querySelector('#contentActions').querySelectorAll('action-button')) {
|
|
|
|
|
|
|
+ for (const existingButton of document.querySelectorAll('action-button')) {
|
|
|
if (existingButton.updateIterationTimestamp !== currentIterationTimestamp) {
|
|
if (existingButton.updateIterationTimestamp !== currentIterationTimestamp) {
|
|
|
existingButton.remove()
|
|
existingButton.remove()
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+function showSection (title) {
|
|
|
|
|
+ for (const section of document.querySelectorAll('section')) {
|
|
|
|
|
+ if (section.title === title) {
|
|
|
|
|
+ section.style.display = 'block'
|
|
|
|
|
+ } else {
|
|
|
|
|
+ section.style.display = 'none'
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ for (const otherName of ['Actions', 'Logs']) {
|
|
|
|
|
+ document.getElementById('show' + otherName).classList.remove('activeSection')
|
|
|
|
|
+ document.getElementById('content' + otherName).hidden = true
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // document.getElementById('show' + name).classList.add('activeSection')
|
|
|
|
|
+ // document.getElementById('content' + name).hidden = false
|
|
|
|
|
+
|
|
|
|
|
+ document.getElementById('hide-sidebar-checkbox').checked = true
|
|
|
|
|
+
|
|
|
|
|
+ changeDirectory(null)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+export function setupSectionNavigation (style) {
|
|
|
|
|
+ const nav = document.querySelector('nav')
|
|
|
|
|
+
|
|
|
|
|
+ if (style === 'sidebar') {
|
|
|
|
|
+ nav.classList += 'sidebar'
|
|
|
|
|
+
|
|
|
|
|
+ document.body.classList += 'has-sidebar'
|
|
|
|
|
+ } else {
|
|
|
|
|
+ nav.classList += 'topbar'
|
|
|
|
|
+
|
|
|
|
|
+ document.body.classList += 'has-topbar'
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ nav.hidden = false
|
|
|
|
|
+
|
|
|
|
|
+ document.getElementById('showActions').onclick = () => { showSection('Actions') }
|
|
|
|
|
+ document.getElementById('showLogs').onclick = () => { showSection('Logs') }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function marshalDashboardStructureToHtml (json) {
|
|
|
|
|
+ const nav = document.getElementById('navigation-links')
|
|
|
|
|
+
|
|
|
|
|
+ for (const dashboard of json.dashboards) {
|
|
|
|
|
+ const oldsection = document.querySelector('section[title="' + dashboard.title + '"]')
|
|
|
|
|
+
|
|
|
|
|
+ if (oldsection != null) {
|
|
|
|
|
+ oldsection.remove()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const section = document.createElement('section')
|
|
|
|
|
+ section.title = dashboard.title
|
|
|
|
|
+
|
|
|
|
|
+ const def = createFieldset('default', section)
|
|
|
|
|
+ section.appendChild(def)
|
|
|
|
|
+
|
|
|
|
|
+ document.getElementsByTagName('main')[0].appendChild(section)
|
|
|
|
|
+ marshalContainerContents(dashboard, section, def, dashboard.title)
|
|
|
|
|
+
|
|
|
|
|
+ const oldLi = nav.querySelector('li[title="' + dashboard.title + '"]')
|
|
|
|
|
+
|
|
|
|
|
+ if (oldLi != null) {
|
|
|
|
|
+ oldLi.remove()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const navigationA = document.createElement('a')
|
|
|
|
|
+ navigationA.title = dashboard.title
|
|
|
|
|
+ navigationA.innerText = dashboard.title
|
|
|
|
|
+ navigationA.onclick = () => {
|
|
|
|
|
+ showSection(dashboard.title)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const navigationLi = document.createElement('li')
|
|
|
|
|
+ navigationLi.appendChild(navigationA)
|
|
|
|
|
+ navigationLi.title = dashboard.title
|
|
|
|
|
+
|
|
|
|
|
+ document.getElementById('navigation-links').appendChild(navigationLi)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (json.dashboards.length === 0) {
|
|
|
|
|
+ showSection('Actions')
|
|
|
|
|
+ } else {
|
|
|
|
|
+ showSection(json.dashboards[0].title)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const rootGroup = document.querySelector('#root-group')
|
|
|
|
|
+
|
|
|
|
|
+ let hasRootActions = false
|
|
|
|
|
+
|
|
|
|
|
+ for (const btn of Object.values(window.actionButtons)) {
|
|
|
|
|
+ if (btn.parentElement === null) {
|
|
|
|
|
+ rootGroup.appendChild(btn)
|
|
|
|
|
+ hasRootActions = true
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!hasRootActions) {
|
|
|
|
|
+ nav.querySelector('li[title="Actions"]').style.display = 'none'
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function marshalLink (item, fieldset) {
|
|
|
|
|
+ let btn = window.actionButtons[item.link]
|
|
|
|
|
+
|
|
|
|
|
+ if (typeof btn === 'undefined') {
|
|
|
|
|
+ btn = document.createElement('button')
|
|
|
|
|
+ btn.innerText = 'Action not found: ' + item.link
|
|
|
|
|
+ btn.classList.add('error')
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ fieldset.appendChild(btn)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function marshalContainerContents (json, section, fieldset, parentDashboard) {
|
|
|
|
|
+ for (const item of json.contents) {
|
|
|
|
|
+ switch (item.type) {
|
|
|
|
|
+ case 'fieldset':
|
|
|
|
|
+ marshalFieldset(item, section, parentDashboard)
|
|
|
|
|
+ break
|
|
|
|
|
+ case 'directory':
|
|
|
|
|
+ marshalDirectoryButton(item, fieldset)
|
|
|
|
|
+ marshalDirectory(item, section)
|
|
|
|
|
+ break
|
|
|
|
|
+ case 'link':
|
|
|
|
|
+ marshalLink(item, fieldset)
|
|
|
|
|
+ break
|
|
|
|
|
+ default:
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function createFieldset (title, parentDashboard) {
|
|
|
|
|
+ const legend = document.createElement('legend')
|
|
|
|
|
+ legend.innerText = title
|
|
|
|
|
+
|
|
|
|
|
+ const fs = document.createElement('fieldset')
|
|
|
|
|
+ fs.title = title
|
|
|
|
|
+ fs.appendChild(legend)
|
|
|
|
|
+
|
|
|
|
|
+ if (typeof parentDashboard === 'undefined') {
|
|
|
|
|
+ fs.setAttribute('parent-dashboard', '')
|
|
|
|
|
+ } else {
|
|
|
|
|
+ fs.setAttribute('parent-dashboard', parentDashboard)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return fs
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function marshalFieldset (item, section, parentDashboard) {
|
|
|
|
|
+ const fs = createFieldset(item.title, parentDashboard)
|
|
|
|
|
+
|
|
|
|
|
+ marshalContainerContents(item, section, fs)
|
|
|
|
|
+
|
|
|
|
|
+ section.appendChild(fs)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function changeDirectory (selected) {
|
|
|
|
|
+ if (selected === '') {
|
|
|
|
|
+ selected = null
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (selected === null) {
|
|
|
|
|
+ window.directoryNavigation = []
|
|
|
|
|
+ } else if (selected === '..') {
|
|
|
|
|
+ window.directoryNavigation.pop()
|
|
|
|
|
+
|
|
|
|
|
+ if (window.directoryNavigation.length > 0) {
|
|
|
|
|
+ selected = window.directoryNavigation[window.directoryNavigation.length - 1]
|
|
|
|
|
+ } else {
|
|
|
|
|
+ selected = null
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // If the selected item is already in the nav list, pop elements until we get
|
|
|
|
|
+ // "back" to the existing nav item
|
|
|
|
|
+ while (window.directoryNavigation.includes(selected)) {
|
|
|
|
|
+ window.directoryNavigation.pop()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ window.directoryNavigation.push(selected)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ for (const fieldset of document.querySelectorAll('fieldset')) {
|
|
|
|
|
+ if (selected === null) {
|
|
|
|
|
+ if ((fieldset.id === 'root-group' || fieldset.getAttribute('parent-dashboard') !== '') && fieldset.children.length > 1) {
|
|
|
|
|
+ fieldset.style.display = 'grid'
|
|
|
|
|
+ } else {
|
|
|
|
|
+ fieldset.style.display = 'none'
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ if (fieldset.title === selected) {
|
|
|
|
|
+ fieldset.style.display = 'grid'
|
|
|
|
|
+ } else {
|
|
|
|
|
+ fieldset.style.display = 'none'
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const title = document.querySelector('h1')
|
|
|
|
|
+ title.innerHTML = ''
|
|
|
|
|
+
|
|
|
|
|
+ const rootLink = createDirectoryBreadcrumb('OliveTin', null)
|
|
|
|
|
+ title.appendChild(rootLink)
|
|
|
|
|
+
|
|
|
|
|
+ for (const dir of window.directoryNavigation) {
|
|
|
|
|
+ const sep = document.createElement('span')
|
|
|
|
|
+ sep.innerHTML = ' » '
|
|
|
|
|
+ title.append(sep)
|
|
|
|
|
+
|
|
|
|
|
+ if (dir === selected) {
|
|
|
|
|
+ title.append(selected)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ title.appendChild(createDirectoryBreadcrumb(dir))
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ document.title = title.innerText
|
|
|
|
|
+
|
|
|
|
|
+ if (selected === null) {
|
|
|
|
|
+ window.location.hash = null
|
|
|
|
|
+
|
|
|
|
|
+ window.history.pushState({ dir: null }, null, '#')
|
|
|
|
|
+ } else {
|
|
|
|
|
+ window.location.hash = selected
|
|
|
|
|
+
|
|
|
|
|
+ window.history.pushState({ dir: selected }, null, '#' + selected)
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function createDirectoryBreadcrumb (title, link) {
|
|
|
|
|
+ const a = document.createElement('a')
|
|
|
|
|
+ a.innerText = title
|
|
|
|
|
+ a.title = title
|
|
|
|
|
+
|
|
|
|
|
+ if (typeof link === 'undefined') {
|
|
|
|
|
+ link = title
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (link === null) {
|
|
|
|
|
+ a.href = '#'
|
|
|
|
|
+ } else {
|
|
|
|
|
+ a.href = '#' + link
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ a.onclick = () => {
|
|
|
|
|
+ changeDirectory(link)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return a
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function marshalDirectoryButton (item, fieldset) {
|
|
|
|
|
+ const directoryButton = document.createElement('button')
|
|
|
|
|
+ directoryButton.innerHTML = '<span class = "icon">📁</span> ' + item.title
|
|
|
|
|
+ directoryButton.onclick = () => {
|
|
|
|
|
+ changeDirectory(item.title)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ fieldset.appendChild(directoryButton)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function marshalDirectory (item, section) {
|
|
|
|
|
+ const fs = createFieldset(item.title)
|
|
|
|
|
+ fs.style.display = 'none'
|
|
|
|
|
+
|
|
|
|
|
+ const directoryBackButton = document.createElement('button')
|
|
|
|
|
+ directoryBackButton.innerHTML = '«'
|
|
|
|
|
+ directoryBackButton.title = 'Go back one directory'
|
|
|
|
|
+ directoryBackButton.onclick = () => {
|
|
|
|
|
+ changeDirectory('..')
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ fs.appendChild(directoryBackButton)
|
|
|
|
|
+
|
|
|
|
|
+ marshalContainerContents(item, section, fs)
|
|
|
|
|
+
|
|
|
|
|
+ section.appendChild(fs)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
export function marshalLogsJsonToHtml (json) {
|
|
export function marshalLogsJsonToHtml (json) {
|
|
|
for (const logEntry of json.logs) {
|
|
for (const logEntry of json.logs) {
|
|
|
const tpl = document.getElementById('tplLogRow')
|
|
const tpl = document.getElementById('tplLogRow')
|
|
@@ -69,3 +358,10 @@ export function marshalLogsJsonToHtml (json) {
|
|
|
document.querySelector('#logTableBody').prepend(row)
|
|
document.querySelector('#logTableBody').prepend(row)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+window.addEventListener('popstate', (e) => {
|
|
|
|
|
+ e.preventDefault()
|
|
|
|
|
+ if (e.state != null && typeof e.state.dir !== 'undefined') {
|
|
|
|
|
+ changeDirectory(e.state.dir)
|
|
|
|
|
+ }
|
|
|
|
|
+})
|