/* Copyright 2020 Cyanic This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. */ function toggleView(el) { [...el.parentNode.children].forEach(child => { if (child === el) child.classList.remove('hidden') else child.classList.add('hidden') }) } function contextMenu(x, y, items) { if (document.getElementById('ContextMenu')) return let cm = document.createElement('div') cm.id = 'ContextMenu' for (item of items) { let a = document.createElement('a') a.textContent = item.text a.onclick = item.onclick a.oncontextmenu = e => { a.onclick(e) e.preventDefault() } cm.appendChild(a) } document.body.appendChild(cm) const hide = e => { window.removeEventListener('click', hide, true) window.removeEventListener('contextmenu', hide, true) document.body.removeChild(cm) } window.addEventListener('click', hide, true) window.addEventListener('contextmenu', hide, true) if (x + cm.clientWidth > window.innerWidth) cm.style.right = window.innerWidth - x else cm.style.left = x if (y + cm.clientHeight > window.innerHeight) cm.style.bottom = window.innerHeight - y else cm.style.top = y } function runApp(app) { return fetch('/runapp?app=' + app) } function editApp(appNode) { let app = appNode.dataset.app fetch('/getapp?app=' + app) .then(r => r.text()) .then(text => { const editapp = document.getElementById('editapp') toggleView(editapp) editapp.innerHTML = '' let dark = window.matchMedia( '(prefers-color-scheme: dark)').matches let cm = CodeMirror(editapp, { value: text, mode: "yaml", theme: dark ? 'seti' : 'default' }) let save = document.createElement('button') save.textContent = 'Save' editapp.appendChild(save) let saverun = document.createElement('button') saverun.textContent = 'Save and run' editapp.appendChild(saverun) let cancel = document.createElement('button') cancel.textContent = 'Cancel' cancel.onclick = () => toggleView(apps) cancel.style.marginLeft = '4em' editapp.appendChild(cancel) let error = document.createElement('pre') editapp.appendChild(error) let savecb = () => ( fetch('/setapp?app=' + app, { method: 'POST', body: cm.getValue(), }) .then(r => r.json()) .then(json => { if (json['error']) throw Error(json['error']) const appChildSelector = child => { const appSelector = `.app[data-app="${app}"]` return document.querySelector(appSelector + child) } const nameNode = appChildSelector(' .name') const iconNode = appChildSelector('> img') app = appNode.dataset.app = json.app nameNode.textContent = appNode.dataset.name = json.name iconNode.src = json.favicon || '/static/dwas-app.svg' return json.app }) ) save.onclick = () => { error.textContent = '' savecb() .catch(e => { error.textContent = 'Error: ' + e }) } saverun.onclick = () => { error.textContent = '' savecb() .then(app => runApp(app)) .catch(e => { error.textContent = 'Error: ' + e }) } }) .catch(e => console.error(e)) } function createApp(data) { let app = document.createElement('div') app.className = 'app' app.dataset.app = data.app app.dataset.name = data.name app.onclick = () => { runApp(app.dataset.app) } let icon = document.createElement('img') icon.src = data.favicon || '/static/dwas-app.svg' icon.onload = () => { if (icon.naturalHeight < 64) icon.style.imageRendering = 'crisp-edges' } app.appendChild(icon) let name = document.createElement('div') name.className = 'name' name.textContent = data.name app.appendChild(name) app.addEventListener('contextmenu', e => { let items = [{ text: 'edit', onclick: () => editApp(app), }, { text: 'add to desktop', onclick: () => { fetch('/addtodesktop?app=' + app.dataset.app) .catch(e => console.error(e)) }, }, { text: 'delete', onclick: () => { let name = app.dataset.name if (confirm(`Are you sure you want to delete '${name}'?`)) fetch('/deleteapp?app=' + app.dataset.app) .then(() => { apps.removeChild(app) }) .catch(e => console.error(e)) }, }] contextMenu(e.clientX, e.clientY, items) e.preventDefault() }, false) return app } const apps = document.getElementById('apps') fetch('/apps') .then(r => r.json()) .then(applist => applist.forEach(data => { let app = createApp(data) apps.appendChild(app) })) .catch(e => console.error(e)) apps.addEventListener('contextmenu', e => { let items = [{ text: 'new app', onclick: () => { fetch('/createapp') .then(r => r.json()) .then(data => { let app = createApp(data) apps.appendChild(app) }) .catch(e => console.error(e)) }, }, { text: 'reload', onclick: () => {location.reload()}, }] contextMenu(e.clientX, e.clientY, items) e.preventDefault() })