feat: HTTP interface fully functional
This commit is contained in:
parent
b3f73fde50
commit
6c7b06f021
|
@ -6,6 +6,7 @@
|
||||||
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
|
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
|
||||||
<title>Lonk</title>
|
<title>Lonk</title>
|
||||||
<meta name='viewport' content='width=device-width, initial-scale=1'>
|
<meta name='viewport' content='width=device-width, initial-scale=1'>
|
||||||
|
<link rel='stylesheet' type='text/css' media='screen' href='reset.min.css'>
|
||||||
<link rel='stylesheet' type='text/css' media='screen' href='main.css'>
|
<link rel='stylesheet' type='text/css' media='screen' href='main.css'>
|
||||||
<script src="main.js"></script>
|
<script src="main.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
@ -19,12 +20,12 @@
|
||||||
be browsing the web with Javascript blocked, then
|
be browsing the web with Javascript blocked, then
|
||||||
just do a POST request to /shorten with the URL you
|
just do a POST request to /shorten with the URL you
|
||||||
wish to shorten Base64-encoded as the body. -->
|
wish to shorten Base64-encoded as the body. -->
|
||||||
<form id="form">
|
<form id="form" novalidate>
|
||||||
<input type="url" name="url" id="url" required>
|
<input type="url" name="url" id="url" autocomplete="off" required autofocus>
|
||||||
<input type="submit" hidden />
|
<input type="submit" hidden />
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<p id="info"></p>
|
<p id="info" state="hidden"></p>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
|
@ -0,0 +1,56 @@
|
||||||
|
body {
|
||||||
|
background-color: #0b0b0e;
|
||||||
|
|
||||||
|
width: 100vw;
|
||||||
|
min-height: 100vh;
|
||||||
|
height: 1px;
|
||||||
|
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding-top: 5vh;
|
||||||
|
padding-bottom: 5vh;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column nowrap;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
#form {
|
||||||
|
width: 95%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#form > input {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
background-color: transparent;
|
||||||
|
color: #ffab3e;
|
||||||
|
text-align: center;
|
||||||
|
font-family: monospace;
|
||||||
|
|
||||||
|
border: none;
|
||||||
|
border-bottom: #15151b;
|
||||||
|
}
|
||||||
|
|
||||||
|
#shortened {
|
||||||
|
font-size: calc(min(20pt, 10vw));
|
||||||
|
font-family: monospace;
|
||||||
|
color: #385d22;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#info {
|
||||||
|
font-family: monospace;
|
||||||
|
color: rgb(91, 91, 91);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#info[state="hidden"] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
#info[state~="hidden"] {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#info[state="error"] {
|
||||||
|
color: rgb(142, 49, 33);
|
||||||
|
}
|
|
@ -2,19 +2,21 @@
|
||||||
|
|
||||||
(() => {
|
(() => {
|
||||||
document.addEventListener('DOMContentLoaded', (_) => {
|
document.addEventListener('DOMContentLoaded', (_) => {
|
||||||
const waiting = false;
|
let waiting = false;
|
||||||
let xhr;
|
let xhr;
|
||||||
|
|
||||||
|
const shortened = document.getElementById('shortened');
|
||||||
|
const info = document.getElementById('info');
|
||||||
const field = document.getElementById('url');
|
const field = document.getElementById('url');
|
||||||
|
|
||||||
field.addEventListener('change', () => {
|
|
||||||
if (waiting && xhr != null) {
|
|
||||||
xhr.abort();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const form = document.getElementById('form');
|
const form = document.getElementById('form');
|
||||||
|
|
||||||
|
// Select the full link with one click
|
||||||
|
shortened.onclick = () => {
|
||||||
|
this.focus();
|
||||||
|
this.select();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set up the actual submission
|
||||||
form.addEventListener('submit', (event) => {
|
form.addEventListener('submit', (event) => {
|
||||||
// We need to create a different body, so prevent the default submission.
|
// We need to create a different body, so prevent the default submission.
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
@ -25,7 +27,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the URL the user is trying to shorten.
|
// Get the URL the user is trying to shorten.
|
||||||
const url = document.getElementById('url').value;
|
let url = document.getElementById('url').value;
|
||||||
|
|
||||||
|
// If it doesn't have a protocol, assume https.
|
||||||
|
if (!/^\w+:\/\/.*$/.test(url)) {
|
||||||
|
url = 'https://' + url;
|
||||||
|
}
|
||||||
|
|
||||||
// Encode the URL as Base64.
|
// Encode the URL as Base64.
|
||||||
const encoded = btoa(encodeURIComponent(url).replace(/%([0-9A-F]{2})/g, (match, p1) =>
|
const encoded = btoa(encodeURIComponent(url).replace(/%([0-9A-F]{2})/g, (match, p1) =>
|
||||||
|
@ -38,18 +45,33 @@
|
||||||
xhr.overrideMimeType('text/plain');
|
xhr.overrideMimeType('text/plain');
|
||||||
xhr.send(encoded);
|
xhr.send(encoded);
|
||||||
xhr.onload = () => {
|
xhr.onload = () => {
|
||||||
|
waiting = false;
|
||||||
switch (xhr.status) {
|
switch (xhr.status) {
|
||||||
case 200:
|
case 200:
|
||||||
let slug = xhr.response;
|
let slug = xhr.response;
|
||||||
console.log(slug);
|
const short_link = `${window.location.href}l/${slug}`;
|
||||||
break;
|
|
||||||
|
// Display to the user
|
||||||
|
|
||||||
|
shortened.innerText = short_link;
|
||||||
|
|
||||||
|
info.setAttribute('state', 'info');
|
||||||
|
info.innerText = 'Link successfully shortened and copied to your clipboard.';
|
||||||
|
|
||||||
|
// Copy the link to the clipboard
|
||||||
|
|
||||||
|
navigator.clipboard.writeText(short_link);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
console.log(xhr.response);
|
info.setAttribute('state', 'error');
|
||||||
break;
|
info.innerText = xhr.response;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
xhr.onerror = () => {
|
xhr.onerror = () => {
|
||||||
// TODO
|
waiting = false;
|
||||||
|
info.setAttribute('state', 'error');
|
||||||
|
info.innerText = "Failed to shorten the URL. Please try again.";
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video {margin: 0;padding: 0;border: 0;font-size: 100%;font: inherit;vertical-align: baseline;}:focus {outline: 0;}article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section {display: block;}body {line-height: 1;}ol, ul {list-style: none;}blockquote, q {quotes: none;}blockquote:before, blockquote:after, q:before, q:after {content: '';content: none;}table {border-collapse: collapse;border-spacing: 0;}input[type=search]::-webkit-search-cancel-button, input[type=search]::-webkit-search-decoration, input[type=search]::-webkit-search-results-button, input[type=search]::-webkit-search-results-decoration {-webkit-appearance: none;-moz-appearance: none;}input[type=search] {-webkit-appearance: none;-moz-appearance: none;-webkit-box-sizing: content-box;-moz-box-sizing: content-box;box-sizing: content-box;}textarea {overflow: auto;vertical-align: top;resize: vertical;}audio, canvas, video {display: inline-block;*display: inline;*zoom: 1;max-width: 100%;}audio:not([controls]) {display: none;height: 0;}[hidden] {display: none;}html {font-size: 100%;-webkit-text-size-adjust: 100%;-ms-text-size-adjust: 100%;}a:focus {outline: thin dotted;}a:active, a:hover {outline: 0;}img {border: 0;-ms-interpolation-mode: bicubic;}figure {margin: 0;}form {margin: 0;}fieldset {border: 1px solid #c0c0c0;margin: 0 2px;padding: 0.35em 0.625em 0.75em;}legend {border: 0;padding: 0;white-space: normal;*margin-left: -7px;}button, input, select, textarea {font-size: 100%;margin: 0;vertical-align: baseline;*vertical-align: middle;}button, input {line-height: normal;}button, select {text-transform: none;}button, html input[type="button"], input[type="reset"], input[type="submit"] {-webkit-appearance: button;cursor: pointer;*overflow: visible;}button[disabled], html input[disabled] {cursor: default;}input[type="checkbox"], input[type="radio"] {box-sizing: border-box;padding: 0;*height: 13px;*width: 13px;}input[type="search"] {-webkit-appearance: textfield;-moz-box-sizing: content-box;-webkit-box-sizing: content-box;box-sizing: content-box;}input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration {-webkit-appearance: none;}button::-moz-focus-inner, input::-moz-focus-inner {border: 0;padding: 0;}textarea {overflow: auto;vertical-align: top;}table {border-collapse: collapse;border-spacing: 0;}html, button, input, select, textarea {color: #222;}::-moz-selection {background: #b3d4fc;text-shadow: none;}::selection {background: #b3d4fc;text-shadow: none;}img {vertical-align: middle;}fieldset {border: 0;margin: 0;padding: 0;}textarea {resize: vertical;}.chromeframe {margin: 0.2em 0;background: #ccc;color: #000;padding: 0.2em 0;}
|
Loading…
Reference in New Issue