336 lines
13 KiB
HTML
336 lines
13 KiB
HTML
<!doctype html>
|
|
<html>
|
|
<head>
|
|
<style>
|
|
:not(:defined) {
|
|
visibility: hidden;
|
|
}
|
|
|
|
body {
|
|
font: 12px var(--sl-font-mono);
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
}
|
|
|
|
sl-input {
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
sl-button {
|
|
margin-top: 10px;
|
|
}
|
|
|
|
sl-alert {
|
|
margin-top: 20px;
|
|
}
|
|
|
|
sl-tree {
|
|
margin-top: 20px;
|
|
}
|
|
|
|
sl-icon[name="type"] {
|
|
color: var(--sl-color-danger-500);
|
|
}
|
|
|
|
sl-icon[name="square"] {
|
|
color: var(--sl-color-warning-500);
|
|
cursor: pointer;
|
|
}
|
|
|
|
sl-icon[name="check-square"] {
|
|
color: var(--sl-color-success-500);
|
|
}
|
|
|
|
sl-icon[name="check-square-fill"] {
|
|
color: var(--sl-color-success-500);
|
|
}
|
|
</style>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMAAAADACAYAAABS3GwHAAAAAXNSR0IArs4c6QAAAAlwSFlzAAAuIwAALiMBeKU/dgAAA7VpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDYuMC4wIj4KICAgPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iPgogICAgICAgICA8ZXhpZjpQaXhlbFlEaW1lbnNpb24+MTkyPC9leGlmOlBpeGVsWURpbWVuc2lvbj4KICAgICAgICAgPGV4aWY6UGl4ZWxYRGltZW5zaW9uPjE5MjwvZXhpZjpQaXhlbFhEaW1lbnNpb24+CiAgICAgICAgIDx4bXA6Q3JlYXRvclRvb2w+UGl4ZWxtYXRvciBQcm8gMy42LjEzPC94bXA6Q3JlYXRvclRvb2w+CiAgICAgICAgIDx4bXA6Q3JlYXRlRGF0ZT4yMDI0LTEyLTA0VDE2OjMwOjE5LTA4OjAwPC94bXA6Q3JlYXRlRGF0ZT4KICAgICAgICAgPHhtcDpNZXRhZGF0YURhdGU+MjAyNC0xMi0wNFQxNjozMToxNy0wODowMDwveG1wOk1ldGFkYXRhRGF0ZT4KICAgICAgICAgPHRpZmY6WFJlc29sdXRpb24+MzAwMDAwMC8xMDAwMDwvdGlmZjpYUmVzb2x1dGlvbj4KICAgICAgICAgPHRpZmY6UmVzb2x1dGlvblVuaXQ+MjwvdGlmZjpSZXNvbHV0aW9uVW5pdD4KICAgICAgICAgPHRpZmY6WVJlc29sdXRpb24+MzAwMDAwMC8x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" />
|
|
<link
|
|
rel="stylesheet"
|
|
media="(prefers-color-scheme:light)"
|
|
href="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.18.0/cdn/themes/light.css"
|
|
/>
|
|
<link
|
|
rel="stylesheet"
|
|
media="(prefers-color-scheme:dark)"
|
|
href="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.18.0/cdn/themes/dark.css"
|
|
onload="document.documentElement.classList.add('sl-theme-dark');"
|
|
/>
|
|
<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.18.0/cdn/shoelace-autoloader.js"></script>
|
|
<script>
|
|
function setInputIcon(val, icon) {
|
|
if (val.length > 0) {
|
|
icon.setAttribute('name', 'square');
|
|
} else {
|
|
icon.setAttribute('name', 'type');
|
|
}
|
|
}
|
|
|
|
function setInputIcons() {
|
|
setInputIcon(
|
|
document.getElementById('short').value,
|
|
document.getElementById('short-icon'),
|
|
);
|
|
|
|
setInputIcon(
|
|
document.getElementById('long').value,
|
|
document.getElementById('long-icon'),
|
|
);
|
|
}
|
|
|
|
function clearAlerts() {
|
|
document.getElementById('err').hide();
|
|
}
|
|
|
|
function error(err1, err2) {
|
|
clearAlerts();
|
|
|
|
document.getElementById('err1').innerText = err1;
|
|
document.getElementById('err2').innerText = err2;
|
|
document.getElementById('err').show();
|
|
}
|
|
|
|
async function setFromInputs() {
|
|
const short = document.getElementById('short').value;
|
|
const long = document.getElementById('long').value;
|
|
|
|
if (long == '') {
|
|
error('Unable to set', 'Long URL is required');
|
|
return;
|
|
}
|
|
|
|
document.getElementById('short-icon').setAttribute('name', 'check-square-fill');
|
|
document.getElementById('long-icon').setAttribute('name', 'check-square-fill');
|
|
|
|
await set(short, long);
|
|
}
|
|
|
|
async function set(short, long) {
|
|
if (short != '') {
|
|
setShortItem(short, null, 'check-square-fill');
|
|
}
|
|
|
|
const oldShort = document.getElementById('short').value;
|
|
const oldLong = document.getElementById('long').value;
|
|
|
|
let resp;
|
|
|
|
try {
|
|
resp = await fetch('./', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
short: short,
|
|
long: long,
|
|
}),
|
|
});
|
|
} catch (err) {
|
|
console.log(err);
|
|
setTimeout(async () => await set(short, long), 5000);
|
|
return;
|
|
}
|
|
|
|
if (resp.status !== 200) {
|
|
error('Failed to set', (await resp.json()).message);
|
|
return;
|
|
}
|
|
|
|
const data = await resp.json();
|
|
const newShort = data.short;
|
|
const newURL = data.url;
|
|
|
|
setShortItem(newShort, newURL, 'check-square');
|
|
|
|
// Only set the icons if we were actually setting from these inputs
|
|
if (document.getElementById('short').value == short && document.getElementById('long').value == long) {
|
|
document.getElementById('short-icon').setAttribute('name', 'check-square');
|
|
document.getElementById('long-icon').setAttribute('name', 'check-square');
|
|
}
|
|
|
|
// Only set the clipboard if the user didn't change the inputs
|
|
if (document.getElementById('short').value == oldShort && document.getElementById('long').value == oldLong) {
|
|
try {
|
|
await navigator.clipboard.writeText(newURL);
|
|
} catch (err) {
|
|
console.log(err);
|
|
}
|
|
}
|
|
|
|
const shorts = [];
|
|
for (const elem of document.getElementById('tree').children) {
|
|
const icon = elem.getElementsByTagName('sl-icon')[0];
|
|
if (icon.getAttribute('name') == 'check-square-fill' ||
|
|
icon.getAttribute('name') == 'check-square') {
|
|
shorts.push(elem.textContent);
|
|
}
|
|
}
|
|
|
|
try {
|
|
resp = await fetch('./', {
|
|
method: 'QUERY',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
shorts: shorts,
|
|
}),
|
|
});
|
|
} catch (err) {
|
|
console.log(err);
|
|
return;
|
|
}
|
|
|
|
for (const short of (await resp.json()).shorts) {
|
|
appendShortItem(short, long);
|
|
}
|
|
}
|
|
|
|
function setShortItem(short, url, icon) {
|
|
const tree = document.getElementById('tree');
|
|
|
|
for (const item of tree.children) {
|
|
if (item.textContent == short) {
|
|
tree.removeChild(item);
|
|
}
|
|
}
|
|
|
|
const item = document.createElement('sl-tree-item');
|
|
item.appendChild(document.createElement('sl-icon')).setAttribute('name', icon);
|
|
item.appendChild(document.createTextNode(short));
|
|
|
|
if (url != null) {
|
|
item.addEventListener('click', () => {
|
|
navigator.clipboard.writeText(url);
|
|
});
|
|
}
|
|
|
|
tree.insertBefore(item, tree.firstChild);
|
|
}
|
|
|
|
function appendShortItem(short, long) {
|
|
const tree = document.getElementById('tree');
|
|
|
|
for (const item of tree.children) {
|
|
if (item.textContent == short) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
const item = document.createElement('sl-tree-item');
|
|
item.appendChild(document.createElement('sl-icon')).setAttribute('name', 'square');
|
|
item.appendChild(document.createTextNode(short));
|
|
item.addEventListener('click', () => {
|
|
set(short, long);
|
|
});
|
|
|
|
tree.appendChild(item);
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', async () => {
|
|
await Promise.all([
|
|
customElements.whenDefined('sl-input'),
|
|
customElements.whenDefined('sl-icon'),
|
|
customElements.whenDefined('sl-button'),
|
|
customElements.whenDefined('sl-alert'),
|
|
customElements.whenDefined('sl-tree'),
|
|
]);
|
|
|
|
let shortPaste = false;
|
|
|
|
document.getElementById('short').addEventListener('sl-input', async () => {
|
|
clearAlerts();
|
|
setInputIcons();
|
|
|
|
if (shortPaste) {
|
|
shortPaste = false;
|
|
await setFromInputs();
|
|
}
|
|
});
|
|
|
|
document.getElementById('short').addEventListener('keydown', async (e) => {
|
|
if (e.key === 'Enter') {
|
|
await setFromInputs();
|
|
}
|
|
});
|
|
|
|
document.getElementById('short').addEventListener('paste', async () => {
|
|
if (document.getElementById('long').value != '') {
|
|
shortPaste = true;
|
|
}
|
|
});
|
|
|
|
document.getElementById('short-icon').addEventListener('click', async () => {
|
|
await setFromInputs();
|
|
});
|
|
|
|
let longPaste = false;
|
|
|
|
document.getElementById('long').addEventListener('sl-input', async () => {
|
|
clearAlerts();
|
|
setInputIcons();
|
|
document.getElementById('tree').replaceChildren();
|
|
|
|
if (longPaste) {
|
|
longPaste = false;
|
|
await setFromInputs();
|
|
}
|
|
});
|
|
|
|
document.getElementById('long').addEventListener('keydown', async (e) => {
|
|
if (e.key === 'Enter') {
|
|
await setFromInputs();
|
|
}
|
|
});
|
|
|
|
document.getElementById('long').addEventListener('paste', () => {
|
|
if (document.getElementById('short').value != '') {
|
|
longPaste = true;
|
|
}
|
|
});
|
|
|
|
document.getElementById('long-icon').addEventListener('click', async () => {
|
|
await setFromInputs();
|
|
});
|
|
|
|
|
|
document.getElementById('set').addEventListener('click', async () => {
|
|
await setFromInputs();
|
|
});
|
|
|
|
|
|
document.getElementById('long').focus();
|
|
setInputIcons();
|
|
});
|
|
</script>
|
|
</head>
|
|
<body>
|
|
<div id="container" style="width: min(500px, calc(100vw - 10px))">
|
|
<sl-input id="short" value="{{ .path }}" label="{{ .host }}/">
|
|
<sl-icon id="short-icon" name="type" slot="suffix"></sl-icon>
|
|
</sl-input>
|
|
|
|
<sl-input id="long" value="{{ .long }}" label="⟶" type="url">
|
|
<sl-icon id="long-icon" name="type" slot="suffix"></sl-icon>
|
|
</sl-input>
|
|
|
|
<div style="text-align: center; margin-top: 10px;">
|
|
<sl-button variant="primary" id="set">Set</sl-button>
|
|
</div>
|
|
|
|
<sl-alert id="err" variant="danger">
|
|
<sl-icon slot="icon" name="exclamation-octagon"></sl-icon>
|
|
<strong id="err1"></strong><br />
|
|
<span id="err2"></span>
|
|
</sl-alert>
|
|
|
|
<sl-tree id="tree">
|
|
</sl-tree>
|
|
</div>
|
|
</body>
|
|
</html>
|