diff options
author | elioat <{ID}+{username}@users.noreply.github.com> | 2024-06-10 11:00:02 -0400 |
---|---|---|
committer | elioat <{ID}+{username}@users.noreply.github.com> | 2024-06-10 11:00:02 -0400 |
commit | 1bd51b5e9773d203c6be6020d5449973d6e25a3f (patch) | |
tree | 544e72d0fe528188e37349baa18f0eb7177071c8 /html | |
parent | df761f249300a4ea7f641bb698b68a144020d11d (diff) | |
download | tour-1bd51b5e9773d203c6be6020d5449973d6e25a3f.tar.gz |
*
Diffstat (limited to 'html')
-rw-r--r-- | html/yon.html | 208 |
1 files changed, 131 insertions, 77 deletions
diff --git a/html/yon.html b/html/yon.html index 98b9ba3..ee609dd 100644 --- a/html/yon.html +++ b/html/yon.html @@ -1,9 +1,6 @@ <!DOCTYPE html> <html lang="en"> <!-- - -Forked by eli_oat circa 2024 - MIT License Copyright (c) m15o @@ -31,12 +28,7 @@ SOFTWARE. <title>yon</title> </head> <style> - - html, body { - font-size: 1.25em; - } - - html, body, .column, textarea, table, td { + html, body, #app, .column, textarea, table, td { height: 100%; } @@ -93,7 +85,6 @@ SOFTWARE. } textarea { - font-size: 1.75em; box-sizing: border-box; padding: 5px; margin: 0; @@ -182,32 +173,13 @@ SOFTWARE. #settings.visible { display: table-row; } + + .dirty .save { + background-color: aquamarine; + } </style> <body> -<datalist id="files"></datalist> -<table> - <tr> - <td colspan="2" class="menu" style="height: 0;"> - <span class="new">new</span><span class="log">log</span><span class="ls">ls</span><span - class="ref">ref</span><span class="mv">mv</span><span class="del">del</span><span class="settings">settings</span> - </td> - </tr> - <tr id="settings"> - <td colspan="2" style="height: 0;"> - <span class="export">export</span> <span class="reset">reset</span> <input type="file" id="import" value=""> - </td> - </tr> - <tr> - <td class="col"> - <div class="active column"></div> - <div class="resize"></div> - </td> - <td> - <div class="column"></div> - </td> - </tr> -</table> -<input list="files" spellcheck="false" id="search" autocomplete="off"/> +<div id="app"></div> <script> //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Helpers @@ -311,14 +283,56 @@ SOFTWARE. } } + function basename(path) { + const n = path.split('/').pop(); + return n.split('.').shift(); + } + + function download(data, name, mime) { + const link = document.createElement('a'); + link.download = name; + link.href = window.URL.createObjectURL(new Blob([data], {type: mime})); + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // storage + + let store = {}; + + function storeSet(k, v) { + if (v === storeGet(k, v)) return; + $('#app').classList.add('dirty'); + store[k] = v; + } + + function storeGet(k) { + return store[k] || ''; + } + + function storeDel(k) { + delete store[k]; + } + + function storeKeys() { + return Object.keys(store); + } + + function storeClear() { + store = {}; + } + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Core function save() { let name = $name().value; let content = $content().value; + $panel().dataset['name'] = name; if (!name || name[0] === '+') return; - localStorage.setItem(name, content); + storeSet(name, content); refresh(); } @@ -331,8 +345,8 @@ SOFTWARE. hist().forward = []; } - function ls() { - return Object.keys(localStorage).map(k => '[[' + k + ']]').join('\n'); + function ls(prefix) { + return storeKeys().filter(k => !prefix || k.startsWith(prefix)).map(k => '[[' + k + ']]').join('\n'); } function load(name, noHist) { @@ -342,12 +356,12 @@ SOFTWARE. return; } !noHist && updateHistory(name) - if (name === '+ls') { - $content().value = ls(); + if (name.startsWith('+ls')) { + $content().value = ls(name.split(':')[1]); } else if (name.startsWith('+ref')) { $content().value = ref(name.split(':')[1]); } else { - $content().value = localStorage.getItem(name); + $content().value = storeGet(name); } $name().value = name; $panel().dataset['name'] = name; @@ -358,7 +372,7 @@ SOFTWARE. function refresh() { const $elt = $('#files'); $elt.innerHTML = ''; - Object.keys(localStorage).forEach(k => $elt.appendChild(makeElt('option', [], {}, null, k))); + storeKeys().forEach(k => $elt.appendChild(makeElt('option', [], {}, null, k))); } let paneID = 0; @@ -385,7 +399,7 @@ SOFTWARE. makeElt('span', ['fold', 'action'], {}, '-'), makeElt('span', ['max', 'action'], {}, '+'), makeElt('span', ['move', 'action'], {}, '~'), - makeElt('span', ['close', 'action'], {}, '×') + makeElt('span', ['close', 'action'], {}, 'x') ); const id = paneID++; const section = document.createElement('section'); @@ -402,8 +416,8 @@ SOFTWARE. } function ref(str) { - return Object.keys(localStorage).map(k => { - const v = localStorage.getItem(k); + return storeKeys().map(k => { + const v = storeGet(k); const m = v.split('\n').filter(l => l.includes('[[' + str + ']]')); if (!m.length) return null; return ['[[' + k + ']]', '----------', ...m, ''].join('\n'); @@ -411,9 +425,9 @@ SOFTWARE. } function mv(before, after) { - return Object.keys(localStorage).forEach(k => { - const v = localStorage.getItem(k); - localStorage.setItem(k, v.replaceAll('[[' + before + ']]', '[[' + after + ']]')) + return storeKeys().forEach(k => { + const v = storeGet(k); + storeSet(k, v.replaceAll('[[' + before + ']]', '[[' + after + ']]')) }) } @@ -474,8 +488,7 @@ SOFTWARE. function menuReset() { if (confirm('delete everything?')) { - localStorage.clear(); - location.reload(); + storeClear(); } } @@ -487,7 +500,7 @@ SOFTWARE. let prev = $panel().dataset['name']; mv(prev, $name().value); save(); - localStorage.removeItem(prev); + storeDel(prev); $prevPanel = $panel(); $$('section').forEach($pane => { setActive($pane); @@ -497,20 +510,26 @@ SOFTWARE. refresh(); } + function quine() { + const regex = /let store = (.*)/; + return ['<!DOCTYPE html>', + document.documentElement.outerHTML.replace(regex, "let store = " + JSON.stringify(store) + .replaceAll('</' + 'script', "' + '</' + 'script' + '") + ';')].join('\n'); + } + + function menuSave() { + $('#app').classList.remove('dirty'); + download(quine(), basename(window.location.href) + '.html', 'text/html'); + } + function menuExport() { - const blob = new Blob([JSON.stringify(localStorage)], {type: 'application/json'}); - const link = document.createElement('a'); - link.download = 'yon-export.json'; - link.href = window.URL.createObjectURL(blob); - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); + download(JSON.stringify(store), basename(window.location.href) + '.json', 'text/json'); } function menuDel() { let name = $name().value; if (name && confirm('delete ' + name + '?')) { - localStorage.removeItem(name); + storeDel(name); refresh(); deletePane($panel()); } @@ -568,6 +587,50 @@ SOFTWARE. let $prevPanel; let $resize; + function handleSearch(e) { + e.preventDefault(); + e.stopPropagation(); + if (isChordNavBlank(e)) { + navBlank(t$(e).value); + closeSearch(); + } else if (isChordNav(e)) { + if (!$content()) createPane(); + closeSearch(); + load(t$(e).value); + } else { + if (!$content()) return; + closeSearch(); + insert($content(), "[[" + t$(e).value + "]]"); + } + } + + document.getElementById('app').innerHTML = ` + <datalist id="files"></datalist> +<table> + <tr> + <td colspan="2" class="menu" style="height: 0;"> + <span class="new">new</span><span class="log">log</span><span class="ls">ls</span><span + class="ref">ref</span><span class="mv">mv</span><span class="del">del</span><span class="save">save</span><span class="settings">import</span><span class="export">export</span><span class="reset">reset</span> + </td> + </tr> + <tr id="settings"> + <td colspan="2" class="menu" style="height: 0;"> + <input type="file" id="import" value=""> + </td> + </tr> + <tr> + <td class="col"> + <div class="active column"></div> + <div class="resize"></div> + </td> + <td> + <div class="column"></div> + </td> + </tr> +</table> +<input list="files" spellcheck="false" id="search" autocomplete="off"/> + `; + document.addEventListener('mousedown', function (e) { if (hasCls(t$(e), 'resize')) { $resize = closest$(e, 'td'); @@ -596,11 +659,12 @@ SOFTWARE. case 'log': menuLog(); return; case 'ls': menuLs(); return; case 'reset': menuReset(); return; - case 'export': menuExport(); return; + case 'save': menuSave(); return; case 'ref': menuRef(); return; case 'mv': menuMv(); return; case 'del': menuDel(); return; case 'settings': menuSettings(); return; + case 'export': menuExport(); return; case 'close': paneClose(e); return; case 'fold': paneFold(e); return; case 'max': paneMax(e); return; @@ -621,22 +685,12 @@ SOFTWARE. } }); - function handleSearch(e) { - e.preventDefault(); - e.stopPropagation(); - if (isChordNavBlank(e)) { - navBlank(t$(e).value); - closeSearch(); - } else if (isChordNav(e)) { - if (!$content()) createPane(); - closeSearch(); - load(t$(e).value); - } else { - if (!$content()) return; - closeSearch(); - insert($content(), "[[" + t$(e).value + "]]"); + window.addEventListener('beforeunload', e => { + if ($('.dirty')) { + e.preventDefault(); + e.returnValue = true; } - } + }) document.addEventListener('keydown', function (e) { if (e.key === 'Enter') { @@ -660,7 +714,7 @@ SOFTWARE. const r = new FileReader(); r.onload = e => { let data = JSON.parse('' + e.target.result); - Object.keys(data).forEach(k => localStorage.setItem(k, data[k])); + Object.keys(data).forEach(k => storeSet(k, data[k])); } r.readAsText(file); refresh(); @@ -668,7 +722,7 @@ SOFTWARE. refresh(); createPane(); - load(); + load(location.hash && location.hash.substring(1)); </script> </body> -</html> +</html> \ No newline at end of file |