www.spaceplanner.app

Web client to the spaceplanner API
git clone git://jacobedwards.org/www.spaceplanner.app
Log | Files | Refs

commit 30098218a60d82870cce5b8b674d1b713446a9c1
parent 3d0501dfff01387d08502f1f610b1154415cb20f
Author: Jacob R. Edwards <jacob@jacobedwards.org>
Date:   Tue,  6 Aug 2024 15:09:12 -0700

Use Javascript modules

Nothing besides by which name functions are called, and in some
cases where they are defined, should have been changed.

Diffstat:
Mfiles/floorplans/index.html | 5+----
Mfiles/floorplans/main.js | 50+++++++++++++++++++++++++++-----------------------
Mfiles/lib/api.js | 69+++++++++++++++++++++++++++++++++++----------------------------------
Dfiles/lib/bar.js | 38--------------------------------------
Mfiles/lib/etc.js | 51++++++++++++++++++++++++++++++++++++++++++++++-----
Mfiles/login/index.html | 4+---
Mfiles/login/main.js | 13++++++++-----
Mfiles/logout/index.html | 4+---
Mfiles/logout/main.js | 7+++++--
Mfiles/register/index.html | 4+---
Mfiles/register/main.js | 13++++++++-----
Mfiles/settings/index.html | 5+----
Mfiles/settings/main.js | 65++++++++++++++++++++++++++++++++++-------------------------------
13 files changed, 168 insertions(+), 160 deletions(-)

diff --git a/files/floorplans/index.html b/files/floorplans/index.html @@ -3,10 +3,7 @@ <title>Spaceplanner - Floorplans</title> <link rel="stylesheet" type="text/css" href="/css/main.css"> <link rel="stylesheet" type="text/css" href="./main.css"> - <script src="/lib/api.js" async></script> - <script src="/lib/bar.js" async></script> - <script src="/lib/etc.js" async></script> - <script src="./main.js" async></script> + <script type="module" src="./main.js"></script> </head> <html> <h1>Floorplans</h1> diff --git a/files/floorplans/main.js b/files/floorplans/main.js @@ -1,7 +1,11 @@ +import * as api from "/lib/api.js" +import * as etc from "/lib/etc.js" + function init() { - authorize() - show_bar() - display_button = document.getElementById("display_method") + etc.authorize() + etc.bar() + + let display_button = document.getElementById("display_method") if (!display_button) { throw new Error("Expected #display_method") } @@ -9,7 +13,7 @@ function init() { display_button.addEventListener("click", toggle_display_method_func(display_button), false) - api_fetch("GET", "floorplans/" + localStorage.getItem("username")) + api.fetch("GET", "floorplans/" + localStorage.getItem("username")) .then(show_floorplans) } @@ -20,16 +24,16 @@ function toggle_display_method_func(button) { } function set_display_method(button, method) { - floorplans = document.getElementById("floorplans") + let floorplans = document.getElementById("floorplans") if (!floorplans) { throw new Error("expected #floorplans") } if (method === "list") { floorplans.removeAttribute("class") - other = "grid" + var other = "grid" } else if (method === "grid") { floorplans.setAttribute("class", "grid") - other = "list" + var other = "list" } else { throw new Error("Invalid method") } @@ -40,29 +44,29 @@ function set_display_method(button, method) { function edit_floorplan_func(item, floorplan) { return function() { - set_error("Edit not implemented", item) + etc.error("Edit not implemented", item) } } function delete_floorplan_func(item, floorplan) { return function() { - api_fetch("DELETE", "floorplans/" + floorplan.user + "/" + floorplan.name) + api.fetch("DELETE", "floorplans/" + floorplan.user + "/" + floorplan.name) .then(function() { item.parentElement.remove() }) .catch(function(err) { - set_error("Unable to delete floorplan: " + err, item) + etc.error("Unable to delete floorplan: " + err, item) }) } } function create_floorplan(floorplan) { - root = document.createElement("div") + let root = document.createElement("div") root.setAttribute("class", "floorplan") - aside = document.createElement("aside") + let aside = document.createElement("aside") - button = document.createElement("input") + let button = document.createElement("input") button.addEventListener("click", edit_floorplan_func(root, floorplan), false) button.type = "image" button.src = "/icons/create-outline.svg" @@ -82,19 +86,19 @@ function create_floorplan(floorplan) { root.append(aside) - header = document.createElement("header") - heading = document.createElement("h2") + let header = document.createElement("header") + let heading = document.createElement("h2") header.append(heading) - link = document.createElement("a") + let link = document.createElement("a") heading.append(link) if (floorplan.synopsis) { - synopsis = document.createElement("span") + let synopsis = document.createElement("span") synopsis.setAttribute("class", "synopsis") synopsis.appendChild(document.createTextNode(floorplan.synopsis)) header.append(synopsis) } if (floorplan.address) { - address = document.createElement("address") + let address = document.createElement("address") address.appendChild(document.createTextNode(floorplan.address)) header.append(address) } @@ -104,7 +108,7 @@ function create_floorplan(floorplan) { root.append(header) if (floorplan.user != localStorage.getItem("username")) { - footer = document.createElement("footer") + let footer = document.createElement("footer") // TODO: Link to user page, when it exists footer.append(document.createTextNode("By " + floorplan.user)) root.append(footer) @@ -114,16 +118,16 @@ function create_floorplan(floorplan) { } function show_floorplans(floorplans) { - list = document.getElementById("floorplans") + let list = document.getElementById("floorplans") if (!list) { throw new Error("expected #floorplans") } - for (i in floorplans) { - item = document.createElement("li") + for (let i in floorplans) { + let item = document.createElement("li") item.append(create_floorplan(floorplans[i])) list.append(item) } } -window.onload = handle_wrap(init) +window.onload = etc.handle_wrap(init) diff --git a/files/lib/api.js b/files/lib/api.js @@ -1,8 +1,8 @@ -let api_proto = "http" -let api_host = "api.spaceplanner.app" -let api_version = "v0" +const proto = "http" +const host = "api.spaceplanner.app" +const version = "v0" -function api_verify_response(response) { +function verify_response(response) { let type = response.headers.get("Content-Type") if (type != "application/json; charset=utf-8") { return Promise.reject(new Error("API returned unacceptable format: " + type)) @@ -11,11 +11,11 @@ function api_verify_response(response) { } } -function api_parse_response(response) { +function parse_response(response) { return response.json() } -function api_status(response) { +function status(response) { // response.code is from appleboy's golang JWT LoginHandler // May figure out how to change in the future if (response.code >= 200 || response.code < 300) { @@ -32,55 +32,56 @@ function api_status(response) { } function api_fetch(method, endpoint, body) { - params = { "method": method, "headers": { "Content-Type": "application/json" } }; - - let token = api_token() - if (api_authorized_duration(token) > 0) { - params["headers"]["Authorization"] = "Bearer " + token + let params = { "method": method, "headers": { "Content-Type": "application/json" } }; + let t = token() + if (authorized_duration(t) > 0) { + params["headers"]["Authorization"] = "Bearer " + t } if (body) { params["body"] = JSON.stringify(body) } - return fetch(api_proto + "://" + api_host + "/" + api_version + "/" + endpoint, params) - .then(api_verify_response) - .then(api_parse_response) - .then(api_status) + return fetch(proto + "://" + host + "/" + version + "/" + endpoint, params) + .then(verify_response) + .then(parse_response) + .then(status) } -function api_refresh_token() { +export { api_fetch as fetch } + +export function refresh_token() { api_fetch("GET", "tokens/refresh") .then(function(resp) { - api_update_token(resp.token) + update_token(resp.token) }) } -function api_update_token(token) { - console.log("api_update_token(" + token + ")") - if (!token) { +export function update_token(t) { + console.log("update_token(" + t + ")") + if (!t) { localStorage.removeItem("token") localStorage.removeItem("username") } else { - localStorage.setItem("token", token) - localStorage.setItem("username", api_token_payload(token)["id"]) + localStorage.setItem("token", t) + localStorage.setItem("username", token_payload(t)["id"]) } } -function api_token() { +export function token() { let t = localStorage.getItem("token") - console.log("api_token() > " + t) + console.debug("token() > " + t) return t } -function api_token_payload(token) { - if (!token) { - token = api_token() - if (!token) { - return token +export function token_payload(t) { + if (!t) { + t = token() + if (!t) { + return t } } - let a = token.split('.') + let a = t.split('.') if (a.length != 3) { throw new Error("Invalid token") } @@ -89,8 +90,8 @@ function api_token_payload(token) { // Returns seconds until authorization expires, or negative the // number of seconds it has been expired. -function api_authorized_duration(token) { - let payload = api_token_payload(token) +export function authorized_duration(t) { + let payload = token_payload(t) if (!payload) { return -1 } @@ -98,6 +99,6 @@ function api_authorized_duration(token) { return payload["exp"] - (Date.now() / 1000) } -function api_logged_in() { - return api_authorized_duration() > 0 +export function logged_in() { + return authorized_duration() > 0 } diff --git a/files/lib/bar.js b/files/lib/bar.js @@ -1,38 +0,0 @@ -function link(name, href) { - let a = document.createElement("a") - a.href = href - a.appendChild(document.createTextNode(name)) - return a -} - -function additem(list, element) { - let i = document.createElement("li") - i.appendChild(element) - list.append(i) -} - -function show_bar(on) { - if (!on) { - on = document.querySelector("body") - } - - let nav = document.createElement("nav") - let left = document.createElement("ul") - nav.appendChild(left) - let right = document.createElement("ul") - nav.appendChild(right) - - if (!api_logged_in()) { - additem(right, link("Login", "/login")) - } else { - let jwt_payload = api_token_payload() - let li = document.createElement("li") - li.appendChild(document.createTextNode("Welcome ")) - li.appendChild(link(jwt_payload["id"], "/settings")) - left.appendChild(li) - additem(right, link("Floorplans", "/floorplans")) - additem(right, link("Logout", "/logout")) - } - - on.prepend(nav) -} diff --git a/files/lib/etc.js b/files/lib/etc.js @@ -1,5 +1,46 @@ -function authorize() { - if (api_authorized_duration() <= 0) { +import * as api from "/lib/api.js" + +function link(name, href) { + let a = document.createElement("a") + a.href = href + a.appendChild(document.createTextNode(name)) + return a +} + +function additem(list, element) { + let i = document.createElement("li") + i.appendChild(element) + list.append(i) +} + +export function bar(on) { + if (!on) { + on = document.querySelector("body") + } + + let nav = document.createElement("nav") + let left = document.createElement("ul") + nav.appendChild(left) + let right = document.createElement("ul") + nav.appendChild(right) + + if (!api.logged_in()) { + additem(right, link("Login", "/login")) + } else { + let jwt_payload = api.token_payload() + let li = document.createElement("li") + li.appendChild(document.createTextNode("Welcome ")) + li.appendChild(link(jwt_payload["id"], "/settings")) + left.appendChild(li) + additem(right, link("Floorplans", "/floorplans")) + additem(right, link("Logout", "/logout")) + } + + on.prepend(nav) +} + +export function authorize() { + if (api.authorized_duration() <= 0) { // Maybe add a parameter which has /login redirect // back to the page that was trying to be accessed window.location.href = "/login" @@ -12,7 +53,7 @@ function delete_element_func(element) { } } -function set_error(message, on) { +export function error(message, on) { if (!on) { on = document.body } @@ -40,13 +81,13 @@ function set_error(message, on) { } } -function handle_wrap(func, on) { +export function handle_wrap(func, on) { return function() { try { func() } catch(err) { - set_error("There was an issue with the page: " + err, on) + error("There was an issue with the page: " + err, on) } } } diff --git a/files/login/index.html b/files/login/index.html @@ -2,9 +2,7 @@ <head> <title>Spaceplanner Login</title> <link rel="stylesheet" type="text/css" href="/css/main.css"> - <script src="/lib/api.js" async></script> - <script src="/lib/etc.js" async></script> - <script src="./main.js" async></script> + <script type="module" src="./main.js"></script> </head> <html> <h1>Login</h1> diff --git a/files/login/main.js b/files/login/main.js @@ -1,19 +1,22 @@ +import * as api from "/lib/api.js" +import * as etc from "/lib/etc.js" + let default_page = "/floorplans" function handle_token(resp) { - api_update_token(resp.token) + api.update_token(resp.token) window.location.href = default_page } function login(username, password, err_callback) { - api_fetch("POST", "tokens", { "username": username, "password": password }) + api.fetch("POST", "tokens", { "username": username, "password": password }) .then(handle_token) .catch(err_callback) return false; } function init() { - if (api_authorized_duration() > 0) { + if (api.authorized_duration() > 0) { window.location.href = default_page } @@ -30,9 +33,9 @@ function init() { login_form.onsubmit = function () { return login( username_input.value, password_input.value, - function (error) { return set_error(error, login_form) } + function (error) { return etc.error(error, login_form) } ); }; } -window.onload = handle_wrap(init) +window.onload = etc.handle_wrap(init) diff --git a/files/logout/index.html b/files/logout/index.html @@ -2,9 +2,7 @@ <head> <title>Spaceplanner Logout</title> <link rel="stylesheet" type="text/css" href="/css/main.css"> - <script src="/lib/api.js" async></script> - <script src="/lib/etc.js" async></script> - <script src="./main.js" async></script> + <script type="module" src="./main.js"></script> </head> <html> <h1>Logout</h1> diff --git a/files/logout/main.js b/files/logout/main.js @@ -1,8 +1,11 @@ let default_page = "/" +import * as api from "/lib/api.js" +import * as etc from "/lib/etc.js" + function init() { - api_update_token(null) + api.update_token(null) window.location.href = default_page } -window.onload = handle_wrap(init) +window.onload = etc.handle_wrap(init) diff --git a/files/register/index.html b/files/register/index.html @@ -2,9 +2,7 @@ <head> <title>Spaceplanner - Register</title> <link rel="stylesheet" type="text/css" href="/css/main.css"> - <script src="/lib/api.js" async></script> - <script src="/lib/etc.js" async></script> - <script src="./main.js" async></script> + <script src="./main.js"></script> </head> <html> <h1>Create New Account</h1> diff --git a/files/register/main.js b/files/register/main.js @@ -1,18 +1,21 @@ -default_page = "/floorplans" +import * as api from "/lib/api.js" +import * as etc from "/lib/etc.js" + +let default_page = "/floorplans" function handle_creation(resp) { window.location.href = "/login" } function register(username, password, err_callback) { - api_fetch("POST", "users", { "username": username, "password": password }) + api.fetch("POST", "users", { "username": username, "password": password }) .then(handle_creation) .catch(err_callback) return false; } function init() { - if (api_authorized_duration() > 0) { + if (api.authorized_duration() > 0) { // Maybe don't do this? window.location.href = default_page } @@ -30,9 +33,9 @@ function init() { form.onsubmit = function () { return register( username_input.value, password_input.value, - function (error) { return set_error(error, form) } + function (error) { return etc.error(error, form) } ); }; } -window.onload = handle_wrap(init) +window.onload = etc.handle_wrap(init) diff --git a/files/settings/index.html b/files/settings/index.html @@ -2,10 +2,7 @@ <head> <title>Spaceplanner - Settings</title> <link rel="stylesheet" type="text/css" href="/css/main.css"> - <script src="/lib/api.js" async></script> - <script src="/lib/bar.js" async></script> - <script src="/lib/etc.js" async></script> - <script src="./main.js" async></script> + <script type="module" src="./main.js"></script> </head> <html> <h1>Settings</h1> diff --git a/files/settings/main.js b/files/settings/main.js @@ -1,44 +1,47 @@ +import * as api from "/lib/api.js" +import * as etc from "/lib/etc.js" + function init() { - authorize() - show_bar() + etc.authorize() + etc.bar() main() } function main() { - errfunc = function(err) { set_error("Unable to get settings: " + err, document.querySelector("#settings")) } - api_fetch("GET", "settings") + let errfunc = function(err) { etc.error("Unable to get settings: " + err, document.querySelector("#settings")) } + api.fetch("GET", "settings") .then(function(params) { - api_fetch("GET", "users/" + localStorage.getItem("username") + "/settings") + api.fetch("GET", "users/" + localStorage.getItem("username") + "/settings") .then(function(current) { show_settings(current, params) }) }) .catch(errfunc) - profile = document.createElement("h2") + let profile = document.createElement("h2") profile.appendChild(document.createTextNode("Profile")) document.body.append(profile) - del = delete_form() + let del = delete_form() del.onsubmit = delete_user document.body.append(del) } function delete_form() { - form = document.createElement("form") + let form = document.createElement("form") form.id = "delete_user_form" - label = document.createElement("label") + let label = document.createElement("label") label.setAttribute("for", "delete_user_confirm") label.appendChild(document.createTextNode("Confirm ")) form.append(label) - check = document.createElement("input") + let check = document.createElement("input") check.id = "delete_user_confirm" check.type = "checkbox" check.setAttribute("required", true) form.append(check) - submit = document.createElement("input") + let submit = document.createElement("input") submit.type = "submit" submit.value = "Delete User" form.append(submit) @@ -49,31 +52,31 @@ function delete_form() { } function delete_user() { - api_fetch("DELETE", "users/" + localStorage.getItem("username")) + api.fetch("DELETE", "users/" + localStorage.getItem("username")) .then(function() { - api_update_token(null) + api.update_token(null) document.location.href = "/" }) - .catch(function(err) { set_error("Unable to delete user: " + err, document.getElementById("#delete_form")) }) + .catch(function(err) { etc.error("Unable to delete user: " + err, document.getElementById("#delete_form")) }) return false } function show_settings(current, params) { - form = document.createElement("form") + let form = document.createElement("form") form.id = "settings" - list = document.createElement("ul") + let list = document.createElement("ul") form.append(list) for (name in params) { - id = name + "_setting" - item = document.createElement("li") + let id = name + "_setting" + let item = document.createElement("li") - label = document.createElement("label") + let label = document.createElement("label") label.setAttribute("for", id) label.appendChild(document.createTextNode(name[0].toUpperCase() + name.substring(1) + " ")) item.append(label) - input = create_input(name, params[name], current[name]) + let input = create_input(name, params[name], current[name]) input.id = id item.append(input) @@ -88,37 +91,37 @@ function show_settings(current, params) { list.append(item) } - submit = document.createElement("input") + let submit = document.createElement("input") submit.value = "Update" submit.type = "submit" form.append(submit) form.onsubmit = function () { return update_settings(current, params) } - current_form = document.querySelector("#settings") + let current_form = document.querySelector("#settings") current_form.replaceWith(form) } function update_settings(current, params) { - settings = Array.from(document.querySelectorAll("form#settings > ul > li > input")) - patch = [] - for (name in settings) { - newvalue = settings[name].value + let settings = Array.from(document.querySelectorAll("form#settings > ul > li > input")) + let patch = [] + for (let name in settings) { + let newvalue = settings[name].value if (params.default && newvalue == params.default) continue - oldvalue = current[name] + let oldvalue = current[name] if (oldvalue && newvalue == oldvalue) continue patch.push({ op: "add", path: settings[name].name, value: newvalue }) } - api_fetch("PATCH", "users/" + localStorage.getItem("username") + "/settings", patch) - .catch(function(err) { set_error("Unable to update settings: " + err) }) + api.fetch("PATCH", "users/" + localStorage.getItem("username") + "/settings", patch) + .catch(function(err) { etc.error("Unable to update settings: " + err) }) return false } function create_input(name, setting, current_value) { - input = document.createElement("input") + let input = document.createElement("input") if (setting.type == "string") { input.type = "text" } else { @@ -133,4 +136,4 @@ function create_input(name, setting, current_value) { return input } -window.onload = handle_wrap(init) +window.onload = etc.handle_wrap(init)