From 01f2cfcb6dcbcec552a02d841b915f88651676cb Mon Sep 17 00:00:00 2001 From: Lukas SP Date: Mon, 24 Aug 2020 18:00:04 +0200 Subject: [PATCH] Refactor frontend JavaScript --- web/index.html | 4 +- web/js/api.js | 35 ++++++++++++++++++ web/js/autoload.js | 85 ++++++++++++++++++++++-------------------- web/js/buttons.js | 92 +++++++++++++++++++++++++++++----------------- web/js/rest.js | 56 ---------------------------- 5 files changed, 141 insertions(+), 131 deletions(-) create mode 100644 web/js/api.js delete mode 100644 web/js/rest.js diff --git a/web/index.html b/web/index.html index 3f9a720..20af6f6 100644 --- a/web/index.html +++ b/web/index.html @@ -63,8 +63,6 @@ - - - + \ No newline at end of file diff --git a/web/js/api.js b/web/js/api.js new file mode 100644 index 0000000..4bc4014 --- /dev/null +++ b/web/js/api.js @@ -0,0 +1,35 @@ +// getAPIInformation returns the API information +export async function getAPIInformation() { + return await fetch(location.protocol + "//" + location.host + "/api/v1/info"); +} + +// getPaste retrieves a paste +export async function getPaste(id) { + return await fetch(location.protocol + "//" + location.host + "/api/v1/pastes/" + id); +} + +// createPaste creates a new paste +export async function createPaste(content) { + return await fetch(location.protocol + "//" + location.host + "/api/v1/pastes", { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + content + }) + }); +} + +// deletePaste deletes a paste +export async function deletePaste(id, deletionToken) { + return await fetch(location.protocol + "//" + location.host + "/api/v1/pastes/" + id, { + method: 'DELETE', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + deletionToken + }) + }); +} \ No newline at end of file diff --git a/web/js/autoload.js b/web/js/autoload.js index 2faa64c..873aec3 100644 --- a/web/js/autoload.js +++ b/web/js/autoload.js @@ -1,53 +1,60 @@ -// Load the API information -loadAPIInfo(); +// Import the used modules +import * as api from "./api.js"; +import * as buttons from "./buttons.js"; -// Set up the keybinds -setupKeybinds(); +// Set up the buttons +buttons.setupButtons(); +buttons.setupKeybinds(); + +// Load the API information +async function loadAPIInformation() { + const response = await api.getAPIInformation(); + if (response.ok) { + const data = await response.json(); + document.getElementById("version").innerText = data.version; + } +} +loadAPIInformation(); // Try to load a paste if one exists -let PASTE_ID = ""; -function loadPaste() { - let split = location.pathname.split("."); - let pasteID = split[0]; - let language = split[1]; - getPaste(pasteID, function(success, data) { - // Return if no paste was found - if (!success) { +export let PASTE_ID; +async function loadPaste() { + if (location.pathname != "/") { + // Define the paste ID and language + const split = location.pathname.replace("/", "").split("."); + const pasteID = split[0]; + const language = split[1]; + + // Retrieve the paste from the API and redirect the user to the main page if it could not be found + const response = await api.getPaste(pasteID); + if (!response.ok) { location.replace(location.protocol + "//" + location.host); return; - }; - - // Enable and disable the corresponding buttons + } + const data = await response.json(); + + // Adjust the button states document.getElementById("btn_save").setAttribute("disabled", true); document.getElementById("btn_delete").removeAttribute("disabled"); document.getElementById("btn_copy").removeAttribute("disabled"); - - // Set the paste content to the DOM and display the line numbers + + // Set the paste content to the DOM document.getElementById("code").innerHTML = language ? hljs.highlight(language, data.content).value : hljs.highlightAuto(data.content).value; - for (i = 1; i <= data.content.split(/\n/).length; i++) { - document.getElementById("linenos").innerHTML += "" + i + ""; - } - + + // Display the line numbers + const lineNumbersElement = document.getElementById("linenos"); + data.content.split(/\n/).forEach(function(_currentValue, index) { + lineNumbersElement.innerHTML += "" + (index + 1) + ""; + }); + // Set the PASTE_ID variable PASTE_ID = pasteID; - }); + } else { + const input = document.getElementById("input"); + input.classList.remove("hidden"); + input.focus(); + } } -if (location.pathname != "/") { - loadPaste(); -} else { - const element = document.getElementById("input"); - element.classList.remove("hidden"); - element.focus(); -} - -// Define a function to copy text to the clipboard -function copyToClipboard(text) { - const element = document.createElement("textarea"); - element.value = text; - document.body.appendChild(element); - element.select(); - document.execCommand("copy"); - document.body.removeChild(element); -} \ No newline at end of file +loadPaste(); \ No newline at end of file diff --git a/web/js/buttons.js b/web/js/buttons.js index bc9c1f2..4aaf54b 100644 --- a/web/js/buttons.js +++ b/web/js/buttons.js @@ -1,8 +1,14 @@ -// setupKeybinds initializes the keybinds for the buttons -function setupKeybinds() { - window.onkeydown = function(event) { - if (!event.ctrlKey) return; +// Import the used modules +import * as api from "./api.js"; +import * as autoload from "./autoload.js"; +// setupKeybinds initializes the keybinds for the buttons +export function setupKeybinds() { + window.onkeydown = function(event) { + // Return if the CTRL key was not pressed + if (!event.ctrlKey) return; + + // Define the DOM element of the pressed button let element = null; switch (event.keyCode) { case 81: { @@ -22,61 +28,81 @@ function setupKeybinds() { break; } } - + + // Call the onClick function of the button if (element) { if (element.hasAttribute("disabled")) return; event.preventDefault(); - element.onclick(); + element.click(); } } } -// Define the behavior of the 'new' button -document.getElementById("btn_new").onclick = function() { - location.replace(location.protocol + "//" + location.host); -} +// setupButtons configures the click listeners of the buttons +export function setupButtons() { + // Define the behavior of the 'new' button + document.getElementById("btn_new").addEventListener("click", function() { + location.replace(location.protocol + "//" + location.host); + }); -// Define the behavior of the 'save' button -document.getElementById("btn_save").onclick = function() { - // Return if the text area is empty - if (!document.getElementById("input").value) return; + // Define the behavior of the 'save' button + document.getElementById("btn_save").addEventListener("click", async function() { + // Return if the text area is empty + const input = document.getElementById("input"); + if (!input.value) return; - // Create the paste - createPaste(document.getElementById("input").value, function(success, data) { - // Notify the user about an error if one occurs - if (!success) { + // Create the paste + const response = await api.createPaste(input.value); + if (!response.ok) { alert("Error:\n\n" + data); return; } + const data = await response.json(); // Redirect the user to the paste page let address = location.protocol + "//" + location.host + "/" + data.id; if (data.suggestedSyntaxType) address += "." + data.suggestedSyntaxType; - copyToClipboard(data.deletionToken); location.replace(address); + + // TODO: Find a solution to display the deletion token }); -} -// Define the behavior of the 'delete' button -document.getElementById("btn_delete").onclick = function() { - // Ask the user for the deletion token - let deletionToken = window.prompt("Deletion Token:"); - if (!deletionToken) return; + // Define the behavior of the 'delete' button + document.getElementById("btn_delete").addEventListener("click", async function() { + // Ask the user for the deletion token + const deletionToken = window.prompt("Deletion Token:"); + if (!deletionToken) return; - // Delete the paste - deletePaste(PASTE_ID, deletionToken, function(success, data) { - // Notify the user about an error if one occurs - if (!success) { + // Delete the paste + const response = await api.deletePaste(autoload.PASTE_ID, deletionToken); + const data = await response.text(); + if (!response.ok) { alert("Error:\n\n" + data); return; } - // Redirect the user to the default page + // Redirect the user to the main page location.replace(location.protocol + "//" + location.host); }); + + // Define the behavior of the 'copy' button + document.getElementById("btn_copy").addEventListener("click", async function() { + // Ask for the clipboard permissions + askClipboardPermissions(); + + // Copy the code + await navigator.clipboard.writeText(document.getElementById("code").innerText); + }); } -// Define the behavior of the 'copy' button -document.getElementById("btn_copy").onclick = function() { - copyToClipboard(document.getElementById("code").innerText); +// askClipboardPermissions asks the user for the clipboard permissions +async function askClipboardPermissions() { + try { + const state = await navigator.permissions.query({ + name: "clipbaord-write" + }); + return state === "granted"; + } catch (error) { + return false; + } } \ No newline at end of file diff --git a/web/js/rest.js b/web/js/rest.js deleted file mode 100644 index f43ded4..0000000 --- a/web/js/rest.js +++ /dev/null @@ -1,56 +0,0 @@ -// loadAPIInfo loads and displays the API information -function loadAPIInfo() { - fetch(location.protocol + "//" + location.host + "/api/v1/info") - .then(response => response.json()) - .then(data => document.getElementById("version").innerText = data.version); -} - -// getPaste retrieves a paste -function getPaste(id, callback) { - fetch(location.protocol + "//" + location.host + "/api/v1/pastes/" + id) - .then(response => { - if (response.status != 200) { - response.text().then(data => callback(false, data)); - return; - } - response.json().then(data => callback(true, data)); - }); -} - -// createPaste creates a new paste -function createPaste(content, callback) { - fetch(location.protocol + "//" + location.host + "/api/v1/pastes", { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - content: content - }) - }).then(response => { - if (response.status != 200) { - response.text().then(data => callback(false, data)); - return; - } - response.json().then(data => callback(true, data)); - }); -} - -// deletePaste deletes a paste -function deletePaste(id, deletionToken, callback) { - fetch(location.protocol + "//" + location.host + "/api/v1/pastes/" + id, { - method: 'DELETE', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - deletionToken: deletionToken - }) - }).then(response => { - if (response.status != 200) { - response.text().then(data => callback(false, data)); - return; - } - response.text().then(data => callback(true, data)); - }); -} \ No newline at end of file