www.spaceplanner.app

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

ui.js (6068B)


      1 import * as api from "/lib/api.js"
      2 import * as etc from "/lib/etc.js"
      3 
      4 export function input(name, memo, options) {
      5 	if (!name) {
      6 		throw new Error("No name provided")
      7 	}
      8 
      9 	let e = document.createElement("input")
     10 	e.name = name
     11 	e.placeholder = name
     12 	e.setAttribute("title", memo)
     13 
     14 	if (!options) {
     15 		options = {}
     16 	}
     17 	if (options.attributes) {
     18 		for (let i in options.attributes) {
     19 			console.debug("Input", name, i, options.attributes[i])
     20 			e.setAttribute(i, options.attributes[i])
     21 		}
     22 	}
     23 	if (options.handlers) {
     24 		for (let i in options.handlers) {
     25 			e.addEventListener(i, options.handlers[i], false)
     26 		}
     27 	}
     28 
     29 	return e
     30 }
     31 
     32 export function button(name, memo, icon, options) {
     33 	let button = input(name, memo, options)
     34 	let attrs
     35 	if (icon == null) {
     36 		attrs = {
     37 			type: "button",
     38 			value: name
     39 		}
     40 	} else {
     41 		attrs = {
     42 			alt: name,
     43 			type: "image",
     44 			class: "icon",
     45 			src: "/icons/" + icon + "-outline.svg"
     46 		}
     47 	}
     48 	for (let i in attrs) {
     49 		console.debug("Button", name, i, attrs[i])
     50 		button.setAttribute(i, attrs[i])
     51 	}
     52 
     53 	return button
     54 }
     55 
     56 export function toggle(a, b, options) {
     57 	if (!options) {
     58 		options = {}
     59 	}
     60 	if (options.swap) {
     61 		let t = a
     62 		a = b
     63 		b = t
     64 	}
     65 	if (options.init) {
     66 		// Should this be run like in the event listener, handling .then methods?
     67 		b.func()
     68 	}
     69 	toggle_setup_button(a, b)
     70 	toggle_setup_button(b, a)
     71 	return a.button
     72 }
     73 
     74 function toggle_setup_button(a, b) {
     75 	a.button.addEventListener("click", function() {
     76 		let swap = function() { a.button.replaceWith(b.button) }
     77 		let r = a.func()
     78 		if (r && typeof r.then == "function") {
     79 			r.then(swap)
     80 		} else {
     81 			swap()
     82 		}
     83 	}, false)
     84 }
     85 
     86 export function login(options) {
     87 	options = options ?? {}
     88 
     89 	let form = document.createElement("form")
     90 	form.classList.add("credentials")
     91 
     92 	let h = form.appendChild(document.createElement("h1"))
     93 	h.append(document.createTextNode("Login"))
     94 
     95 	if (!options.user) {
     96 		let aside = form.appendChild(document.createElement("aside"))
     97 		aside.append(document.createTextNode("Don't have an account? "))
     98 		let a = aside.appendChild(document.createElement("a"))
     99 		a.href = "/register"
    100 		a.append(document.createTextNode("Signup"))
    101 		aside.append(document.createTextNode(" now!"))
    102 	}
    103 
    104 	let label = form.appendChild(document.createElement("label"))
    105 	label.appendChild(document.createTextNode("Username:"))
    106 	label.setAttribute("for", "username")
    107 	let u = form.appendChild(usernameInput())
    108 	if (options.user) {
    109 		u.value = options.user
    110 		if (options.forceUser) {
    111 			u.setAttribute("disabled", true)
    112 		}
    113 	}
    114 
    115 	label = form.appendChild(document.createElement("label"))
    116 	label.appendChild(document.createTextNode("Password:"))
    117 	label.setAttribute("for", "password")
    118 	form.appendChild(passwordInput())
    119 
    120 	let button = form.appendChild(document.createElement("input"))
    121 	button.setAttribute("type", "submit")
    122 	button.setAttribute("value", "Login")
    123 
    124 	form.addEventListener("submit", function(event) {
    125 		event.preventDefault()
    126 		api.login(username.value, password.value)
    127 			.then(function() {
    128 				form.remove()
    129 				if (options.callback != null) {
    130 					options.callback()
    131 				}
    132 			})
    133 			.catch(function(err) {
    134 				etc.error(err + ": Unable to login", form)
    135 			})
    136 	})
    137 
    138 	return form
    139 }
    140 
    141 export function usernameInput() {
    142 	let username = document.createElement("input")
    143 	username.id = "username"
    144 	username.setAttribute("autocomplete", "username")
    145 	username.setAttribute("name", "username")
    146 	username.setAttribute("minlength", 3)
    147 	username.setAttribute("maxlength", 32)
    148 	username.setAttribute("pattern", "^[^@]*$")
    149 	username.setAttribute("autocapitalize", "none")
    150 	username.setAttribute("spellcheck", "false")
    151 	username.setAttribute("autocorrect", "off")
    152 	username.addEventListener("change", function(ev) {
    153 		let v = ev.target.validity
    154 		if (v.tooShort || v.tooLong) {
    155 			ev.target.setCustomValidity("Usernames must be between 3-32 characters long.")
    156 		} else if (v.patternMismatch) {
    157 			ev.target.setCustomValidity("Usernames cannot contain the @ sign")
    158 		} else {
    159 			ev.target.setCustomValidity("")
    160 			return
    161 		}
    162 
    163 		ev.target.reportValidity()
    164 	})
    165 
    166 	return username
    167 }
    168 
    169 export function passwordInput(options) {
    170 	options = options ?? {}
    171 	let password = document.createElement("input")
    172 
    173 	password.id = "password"
    174 	password.setAttribute("autocomplete", options.new ? "new-password" : "current-password")
    175 	password.setAttribute("type", "password")
    176 	password.setAttribute("name", "password")
    177 	password.setAttribute("minlength", 8)
    178 	password.setAttribute("maxlength", 72)
    179 	password.addEventListener("change", function(ev) {
    180 		let v = ev.target.validity
    181 		if (v.tooShort || v.tooLong) {
    182 			ev.target.setCustomValidity("Passwords must be between 8-72 characters long.")
    183 		} else {
    184 			ev.target.setCustomValidity("")
    185 			return
    186 		}
    187 
    188 		ev.target.reportValidity()
    189 	})
    190 
    191 	return password
    192 }
    193 
    194 export function prettyName(name, options) {
    195 	options = options ?? {}
    196 	options.separator = options.separator ?? /[-_]/
    197 	options.title = options.title ?? true
    198 
    199 	let words = name.split(options.separator)
    200 	for (let i in words) {
    201 		words[i] = capitalize(words[i])
    202 		if (!options.title) {
    203 			break
    204 		}
    205 	}
    206 
    207 	return words.join(" ")
    208 }
    209 
    210 export function capitalize(word) {
    211 	return word.charAt(0).toUpperCase() + word.substr(1)
    212 }
    213 
    214 export function warning(content) {
    215 	let warning = document.createElement("span")
    216 	warning.classList.add("warning")
    217 
    218 	let icon = warning.appendChild(
    219 		document.createElement("img")
    220 	)
    221 	icon.classList.add("icon")
    222 	icon.setAttribute("src", "/icons/warning-outline.svg")
    223 
    224 	if (typeof content === "string") {
    225 		let s = content
    226 		content = document.createElement("p")
    227 		content.appendChild(
    228 			document.createTextNode(s)
    229 		)
    230 	}
    231 
    232 	// appendChild can make sure it's correct
    233 	warning.appendChild(content)
    234 	content.classList.add("content")
    235 
    236 	return warning
    237 }
    238 
    239 export function wait(content) {
    240 	let wait = document.getElementById("wait")
    241 	if (!content) {
    242 		wait.remove()
    243 	}
    244 
    245 	if (!wait) {
    246 		wait = document.body.appendChild(document.createElement("div"))
    247 		wait.id = "wait"
    248 	}
    249 
    250 	if (typeof content === "string") {
    251 		content = document.createTextNode(content)
    252 	}
    253 	wait.replaceChildren(content)
    254 }