2019-10-22 18:34:26 +00:00
|
|
|
<!DOCTYPE html>
|
|
|
|
<html lang="en">
|
|
|
|
<head>
|
2019-10-25 03:26:19 +00:00
|
|
|
<meta charset="utf-8" />
|
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
2020-01-15 23:17:12 +00:00
|
|
|
<link
|
|
|
|
rel="stylesheet"
|
|
|
|
href="https://cdnjs.cloudflare.com/ajax/libs/spectre.css/0.5.8/spectre.min.css"
|
|
|
|
integrity="sha256-J24PZiunX9uL1Sdmbe6YT9kNuV5lfVxj3A6Kij5UP6k="
|
|
|
|
crossorigin="anonymous"
|
|
|
|
/>
|
|
|
|
<link
|
|
|
|
rel="stylesheet"
|
|
|
|
href="https://cdnjs.cloudflare.com/ajax/libs/spectre.css/0.5.8/spectre-icons.min.css"
|
|
|
|
integrity="sha256-LxdDS9G94ArUz2UYVPo5FhSeD4owwcBFAQv2Nl1dNUU="
|
|
|
|
crossorigin="anonymous"
|
|
|
|
/>
|
2019-10-25 03:26:19 +00:00
|
|
|
<style>
|
2020-01-15 23:17:12 +00:00
|
|
|
div[id$="-form"]:not(.active) {
|
|
|
|
display: none;
|
|
|
|
}
|
2019-10-25 03:26:19 +00:00
|
|
|
</style>
|
2019-10-22 18:34:26 +00:00
|
|
|
<title>filite</title>
|
|
|
|
</head>
|
|
|
|
<body>
|
2020-01-15 23:17:12 +00:00
|
|
|
<nav class="container mb-2 pb-2">
|
|
|
|
<div class="columns">
|
|
|
|
<div
|
|
|
|
class="column col-sm-12 col-md-10 col-lg-8 col-6 col-mx-auto"
|
|
|
|
>
|
|
|
|
<ul class="tab tab-block">
|
|
|
|
<li id="files-tab" class="tab-item">
|
|
|
|
<a href="#">Files</a>
|
|
|
|
</li>
|
|
|
|
<li id="links-tab" class="tab-item active">
|
|
|
|
<a href="#">Links</a>
|
|
|
|
</li>
|
|
|
|
<li id="texts-tab" class="tab-item">
|
|
|
|
<a href="#">Texts</a>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
|
|
</div>
|
2019-10-25 03:26:19 +00:00
|
|
|
</div>
|
|
|
|
</nav>
|
2020-01-15 23:17:12 +00:00
|
|
|
<main class="container mt-2 pt-2">
|
|
|
|
<div class="columns">
|
|
|
|
<div
|
|
|
|
id="files-form"
|
|
|
|
class="column col-sm-12 col-md-10 col-lg-8 col-6 col-mx-auto"
|
|
|
|
>
|
|
|
|
<div class="form-group">
|
|
|
|
<label class="form-label" for="files-url">URL</label>
|
|
|
|
<div class="input-group">
|
|
|
|
<span class="input-group-addon">/f/</span>
|
|
|
|
<input
|
|
|
|
id="files-url"
|
|
|
|
class="form-input"
|
|
|
|
type="text"
|
|
|
|
placeholder="a1b2c3"
|
|
|
|
required
|
|
|
|
/>
|
|
|
|
<button
|
|
|
|
class="btn btn-primary input-group-btn"
|
|
|
|
id="files-submit"
|
|
|
|
disabled
|
|
|
|
>
|
|
|
|
<i class="icon icon-upload"></i>
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
<p class="form-input-hint">Press space to randomize</p>
|
|
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
|
|
<label class="form-label" for="files-file">File</label>
|
|
|
|
<input
|
|
|
|
class="form-input"
|
|
|
|
id="files-file"
|
|
|
|
type="file"
|
|
|
|
required
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div
|
|
|
|
id="links-form"
|
|
|
|
class="column col-sm-12 col-md-10 col-lg-8 col-6 col-mx-auto active"
|
|
|
|
>
|
|
|
|
<div class="form-group">
|
|
|
|
<label class="form-label" for="links-url">URL</label>
|
|
|
|
<div class="input-group">
|
|
|
|
<span class="input-group-addon">/l/</span>
|
|
|
|
<input
|
|
|
|
id="links-url"
|
|
|
|
class="form-input"
|
|
|
|
type="text"
|
|
|
|
placeholder="a1b2c3"
|
|
|
|
required
|
|
|
|
/>
|
|
|
|
<button
|
|
|
|
class="btn btn-primary input-group-btn"
|
|
|
|
id="links-submit"
|
|
|
|
disabled
|
|
|
|
>
|
|
|
|
<i class="icon icon-upload"></i>
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
<p class="form-input-hint">Press space to randomize</p>
|
|
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
|
|
<label class="form-label" for="links-forward"
|
|
|
|
>Forward</label
|
|
|
|
>
|
|
|
|
<input
|
|
|
|
id="links-forward"
|
|
|
|
class="form-input"
|
|
|
|
type="url"
|
|
|
|
placeholder="http://example.com/"
|
|
|
|
required
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div
|
|
|
|
id="texts-form"
|
|
|
|
class="column col-sm-12 col-md-10 col-lg-8 col-6 col-mx-auto"
|
|
|
|
>
|
|
|
|
<div class="form-group">
|
|
|
|
<label class="form-label" for="texts-url">URL</label>
|
|
|
|
<div class="input-group">
|
|
|
|
<span class="input-group-addon">/t/</span>
|
|
|
|
<input
|
|
|
|
id="texts-url"
|
|
|
|
class="form-input"
|
|
|
|
type="text"
|
|
|
|
placeholder="a1b2c3"
|
|
|
|
required
|
|
|
|
/>
|
|
|
|
<button
|
|
|
|
class="btn btn-primary input-group-btn"
|
|
|
|
id="texts-submit"
|
|
|
|
disabled
|
|
|
|
>
|
|
|
|
<i class="icon icon-upload"></i>
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
<p class="form-input-hint">Press space to randomize</p>
|
|
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
|
|
<label class="form-label" for="texts-contents"
|
|
|
|
>Contents</label
|
|
|
|
>
|
|
|
|
<textarea
|
|
|
|
id="texts-contents"
|
|
|
|
class="form-input"
|
|
|
|
placeholder="Hello, World!"
|
|
|
|
required
|
|
|
|
></textarea>
|
|
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
|
|
<label class="form-switch">
|
|
|
|
<input id="texts-highlight" type="checkbox" />
|
|
|
|
<i class="form-icon"></i> Syntax highlighting
|
|
|
|
</label>
|
|
|
|
</div>
|
2019-10-25 03:26:19 +00:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</main>
|
2020-01-15 23:17:12 +00:00
|
|
|
<div id="modal" class="modal">
|
|
|
|
<a id="modal-bg" href="#" class="modal-overlay"></a>
|
|
|
|
<div class="modal-container">
|
|
|
|
<div class="modal-header">
|
|
|
|
<div class="modal-title h6">Success</div>
|
|
|
|
</div>
|
|
|
|
<div class="modal-body">
|
|
|
|
<div class="content">
|
|
|
|
<div class="form-group">
|
|
|
|
<div class="has-icon-right">
|
|
|
|
<input
|
|
|
|
id="modal-input"
|
|
|
|
type="url"
|
|
|
|
class="form-input"
|
|
|
|
/>
|
|
|
|
<i class="form-icon icon icon-copy"></i>
|
|
|
|
</div>
|
|
|
|
<p class="form-input-hint" id="modal-hint">
|
|
|
|
Click to copy to clipboard
|
|
|
|
</p>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
2019-10-25 03:26:19 +00:00
|
|
|
<script>
|
2020-01-15 23:17:12 +00:00
|
|
|
const tabs = {
|
|
|
|
files: [
|
|
|
|
document.querySelector("#files-tab"),
|
|
|
|
document.querySelector("#files-form"),
|
|
|
|
],
|
|
|
|
links: [
|
|
|
|
document.querySelector("#links-tab"),
|
|
|
|
document.querySelector("#links-form"),
|
|
|
|
],
|
|
|
|
texts: [
|
|
|
|
document.querySelector("#texts-tab"),
|
|
|
|
document.querySelector("#texts-form"),
|
|
|
|
],
|
|
|
|
};
|
|
|
|
|
|
|
|
const inputs = {
|
|
|
|
files: [
|
|
|
|
document.querySelector("#files-url"),
|
|
|
|
document.querySelector("#files-file"),
|
|
|
|
document.querySelector("#files-submit"),
|
|
|
|
],
|
|
|
|
links: [
|
|
|
|
document.querySelector("#links-url"),
|
|
|
|
document.querySelector("#links-forward"),
|
|
|
|
document.querySelector("#links-submit"),
|
|
|
|
],
|
|
|
|
texts: [
|
|
|
|
document.querySelector("#texts-url"),
|
|
|
|
document.querySelector("#texts-contents"),
|
|
|
|
document.querySelector("#texts-highlight"),
|
|
|
|
document.querySelector("#texts-submit"),
|
|
|
|
],
|
|
|
|
};
|
|
|
|
|
|
|
|
const used = {
|
|
|
|
files: [],
|
|
|
|
links: [],
|
|
|
|
texts: [],
|
|
|
|
};
|
|
|
|
|
|
|
|
let baseUrl = `${location.protocol}//${location.host}${location.pathname}`;
|
|
|
|
if (!baseUrl.endsWith("/")) {
|
|
|
|
baseUrl += "/";
|
|
|
|
}
|
|
|
|
|
|
|
|
const modal = {
|
|
|
|
self: document.querySelector("#modal"),
|
|
|
|
input: document.querySelector("#modal-input"),
|
|
|
|
bg: document.querySelector("#modal-bg"),
|
|
|
|
hint: document.querySelector("#modal-hint"),
|
|
|
|
};
|
|
|
|
const openModal = (text) => {
|
|
|
|
modal.input.value = text;
|
|
|
|
modal.hint.innerText = "Click to copy to clipboard";
|
|
|
|
modal.self.classList.add("active");
|
|
|
|
};
|
|
|
|
const closeModal = () => {
|
|
|
|
modal.hint.innerText = "Copied to clipboard";
|
|
|
|
setTimeout(() => {
|
|
|
|
modal.self.classList.remove("active");
|
|
|
|
modal.input.value = "";
|
|
|
|
}, 1000);
|
|
|
|
};
|
|
|
|
modal.input.onclick = (e) => {
|
|
|
|
e.preventDefault();
|
|
|
|
modal.input.select();
|
|
|
|
document.execCommand("copy");
|
|
|
|
closeModal();
|
|
|
|
};
|
|
|
|
modal.bg.onclick = closeModal;
|
|
|
|
|
|
|
|
const fetchUsed = () => {
|
|
|
|
fetch(`${baseUrl}f`)
|
|
|
|
.then((response) => response.json())
|
|
|
|
.then((json) => (used.files = json));
|
|
|
|
fetch(`${baseUrl}l`)
|
|
|
|
.then((response) => response.json())
|
|
|
|
.then((json) => (used.links = json));
|
|
|
|
fetch(`${baseUrl}t`)
|
|
|
|
.then((response) => response.json())
|
|
|
|
.then((json) => (used.texts = json));
|
|
|
|
};
|
|
|
|
fetchUsed();
|
|
|
|
|
|
|
|
const randomUrl = () => {
|
|
|
|
return Math.floor(Math.random() * 2147483647).toString(36);
|
|
|
|
};
|
|
|
|
|
|
|
|
for (const group in tabs) {
|
|
|
|
tabs[group][0].onclick = () => {
|
|
|
|
const active = document.querySelectorAll(".active");
|
|
|
|
for (const el of active) {
|
|
|
|
el.classList.remove("active");
|
|
|
|
}
|
|
|
|
for (const el of tabs[group]) {
|
|
|
|
el.classList.add("active");
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const group in inputs) {
|
|
|
|
const submitButton = inputs[group][inputs[group].length - 1];
|
|
|
|
|
|
|
|
const urlInput = inputs[group][0];
|
|
|
|
urlInput.addEventListener("input", (e) => {
|
|
|
|
if (urlInput.value[urlInput.value.length - 1] === " ") {
|
|
|
|
urlInput.value = randomUrl();
|
|
|
|
checkValidity();
|
|
|
|
e.preventDefault();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
urlInput.value = urlInput.value
|
|
|
|
.replace(/[^0-9A-Za-z]/g, "")
|
|
|
|
.toLowerCase();
|
|
|
|
if (parseInt(urlInput.value, 36) > 2147483647) {
|
|
|
|
urlInput.setCustomValidity(
|
|
|
|
"Base 36 integer below or equal to zik0zj"
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
urlInput.setCustomValidity("");
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
const checkValidity = () => {
|
|
|
|
if (
|
|
|
|
used[group].some(
|
|
|
|
(x) => x.id === parseInt(urlInput.value, 36)
|
|
|
|
)
|
|
|
|
) {
|
|
|
|
urlInput.classList.add("conflict");
|
|
|
|
} else {
|
|
|
|
urlInput.classList.remove("conflict");
|
|
|
|
}
|
|
|
|
submitButton.disabled = inputs[group].some(
|
|
|
|
(input) =>
|
|
|
|
input.validity != undefined && !input.validity.valid
|
|
|
|
);
|
|
|
|
};
|
|
|
|
checkValidity();
|
|
|
|
|
|
|
|
for (const input of inputs[group].filter(
|
|
|
|
(input) =>
|
|
|
|
input instanceof HTMLInputElement ||
|
|
|
|
input instanceof HTMLTextAreaElement
|
|
|
|
)) {
|
|
|
|
input.addEventListener("input", () => checkValidity());
|
|
|
|
input.addEventListener("change", () => checkValidity());
|
|
|
|
}
|
|
|
|
|
|
|
|
const clearInputs = () => {
|
|
|
|
for (const input of inputs[group].filter(
|
|
|
|
(input) =>
|
|
|
|
input instanceof HTMLInputElement ||
|
|
|
|
input instanceof HTMLTextAreaElement
|
|
|
|
)) {
|
|
|
|
input.value = "";
|
|
|
|
}
|
|
|
|
submitButton.disabled = true;
|
|
|
|
};
|
|
|
|
|
|
|
|
if (group === "files") {
|
|
|
|
submitButton.addEventListener("click", () => {
|
|
|
|
const filesFileInput = inputs.files[1];
|
|
|
|
const file = filesFileInput.files[0];
|
|
|
|
|
|
|
|
if (!file) {
|
|
|
|
alert(new Error("No file selected"));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-01-16 03:34:55 +00:00
|
|
|
const fd = new FormData();
|
|
|
|
fd.append("file", file);
|
|
|
|
const id = urlInput.value;
|
|
|
|
const url = `${baseUrl}f/${id}`;
|
2020-01-15 23:17:12 +00:00
|
|
|
|
2020-01-16 03:34:55 +00:00
|
|
|
let status;
|
|
|
|
fetch(url, {
|
|
|
|
method: "PUT",
|
|
|
|
body: fd,
|
|
|
|
})
|
|
|
|
.then((response) => {
|
|
|
|
status = response.status;
|
|
|
|
return response.text();
|
|
|
|
})
|
|
|
|
.then((text) => {
|
|
|
|
if (status !== 201) {
|
|
|
|
throw new Error(text);
|
|
|
|
} else {
|
|
|
|
openModal(url);
|
|
|
|
clearInputs();
|
|
|
|
fetchUsed();
|
|
|
|
}
|
2020-01-15 23:17:12 +00:00
|
|
|
})
|
2020-01-16 03:34:55 +00:00
|
|
|
.catch((error) => alert(error));
|
2020-01-15 23:17:12 +00:00
|
|
|
});
|
|
|
|
} else if (group === "links") {
|
|
|
|
submitButton.addEventListener("click", () => {
|
|
|
|
const id = urlInput.value;
|
|
|
|
const forward = inputs.links[1].value;
|
|
|
|
|
|
|
|
const url = `${baseUrl}l/${id}`;
|
|
|
|
let status;
|
|
|
|
fetch(url, {
|
|
|
|
method: "PUT",
|
|
|
|
body: JSON.stringify({ forward }),
|
|
|
|
})
|
|
|
|
.then((response) => {
|
|
|
|
status = response.status;
|
|
|
|
return response.text();
|
|
|
|
})
|
|
|
|
.then((text) => {
|
|
|
|
if (status !== 201) {
|
|
|
|
throw new Error(text);
|
|
|
|
} else {
|
|
|
|
openModal(url);
|
|
|
|
clearInputs();
|
|
|
|
fetchUsed();
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.catch((error) => alert(error));
|
|
|
|
});
|
|
|
|
} else if (group === "texts") {
|
|
|
|
submitButton.addEventListener("click", () => {
|
|
|
|
const id = urlInput.value;
|
|
|
|
const contents = inputs.texts[1].value;
|
|
|
|
const highlight = inputs.texts[2].checked;
|
|
|
|
|
|
|
|
const url = `${baseUrl}t/${id}`;
|
|
|
|
let status;
|
|
|
|
fetch(url, {
|
|
|
|
method: "PUT",
|
|
|
|
body: JSON.stringify({ contents, highlight }),
|
|
|
|
})
|
|
|
|
.then((response) => {
|
|
|
|
status = response.status;
|
|
|
|
return response.text();
|
|
|
|
})
|
|
|
|
.then((text) => {
|
|
|
|
if (status !== 201) {
|
|
|
|
throw new Error(text);
|
|
|
|
} else {
|
|
|
|
openModal(url);
|
|
|
|
clearInputs();
|
|
|
|
fetchUsed();
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.catch((error) => alert(error));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2019-10-25 03:26:19 +00:00
|
|
|
</script>
|
2019-10-22 18:34:26 +00:00
|
|
|
</body>
|
|
|
|
</html>
|