document.addEventListener("DOMContentLoaded", async () => { "use strict"; const secure = location.protocol === "https:"; const $ = (s,c) => (c||document).querySelector(s); function $$(x,y,z,a){a=(z||document).querySelectorAll(x);if(typeof y=="function")[].forEach.call(a,y);return a} function m(a,b,c){c=document;b=c.createElement(b||"p");b.innerHTML=a.trim();for(a=c.createDocumentFragment();c=b.firstChild;)a.appendChild(c);return a.firstChild} function debounce(fn, delay) { let timeout = null; return (...args) => { if (timeout) clearTimeout(timeout); timeout = setTimeout(() => { timeout = null; fn.apply(null, args); }, delay||500); } } //sock.init(`ws${secure?"s":""}://${location.host}/ws`); //sock.on("hello", e => { // console.log("hello", e); // sock.send("world", {foo:"bar"}); //}); //if ("serviceWorker" in navigator) { // navigator.serviceWorker.register("sw.js") // .then(() => console.log("Service worker registered")); //} function drawNote(note) { const el = m(`

${note.id}

`); $("textarea", el).addEventListener("input", debounce(() => { saveNote(note.id, $("textarea", el).value); }, 500)); $("#notes").appendChild(el); } async function getNote(id) { const res = await fetch(`${id}`); const note = await res.json(); console.log(note); drawNote(note); } async function getList() { const res = await fetch("/list"); const list = await res.json(); console.log(list); for (let i=0;i { const res = await fetch("/login", { method: "POST", headers: {"Content-Type": "application/json"}, body: JSON.stringify({ username: $("#username").value, password: $("#password").value, keepSession: $("#keep-session").checked, }) }); // TODO check return code const json = await res.json(); console.log(json); // TODO "Must Change Password" flow getUserData(); getList(); }); $("#change-password").addEventListener("click", async () => { const res = await fetch("/change-password", { method: "POST", headers: {"Content-Type": "application/json"}, body: JSON.stringify({ username: $("#username").value, password: $("#password").value, newPassword: $("#new-password").value, keepSession: $("#keep-session").checked, }) }); const json = await res.json(); console.log(json); getUserData(); }); $("#change-username").addEventListener("click", async () => { const res = await fetch("/change-username", { method: "POST", headers: {"Content-Type": "application/json"}, body: JSON.stringify({ newUsername: $("#new-username").value, password: $("#password").value, }) }); const json = await res.json(); console.log(json); getUserData(); }); $("#logout").addEventListener("click", async () => { const res = await fetch("/logout", { method: "POST", }); //const json = await res.json(); //console.log(json); console.log("Logged out"); $("#uid").value = ""; }); $("#logout-everywhere").addEventListener("click", async () => { const res = await fetch("/deauth-all", { method: "POST", }); console.log("Logged out everywhere"); $("#uid").value = ""; }); try { getUserData(); getList(); getUserSessions(); } catch(e) {} // TODO make sure to save unsynced delta even if token has expired, then sync when logged in // TODO Cookie consent on signup; "only necessary for maintaining user session and cached data" // TODO "Review sessions" popup client-side when relogin required on a token which shouldn't have expired yet // TODO prevent data: and javascript: links? // Also embedding of external content? // encodeURI *AND* encode for attribute (quote marks etc) // https://github.com/cure53/DOMPurify // // elem.textContent = dangerVariable; !!!!!!!!! // elem.insertAdjacentText(dangerVariable); // elem.className = dangerVariable; // elem.setAttribute(safeName, dangerVariable); // formfield.value = dangerVariable; // document.createTextNode(dangerVariable); // document.createElement(dangerVariable); // elem.innerHTML = DOMPurify.sanitize(dangerVar); // https://github.com/cure53/DOMPurify/blob/main/src/attrs.js // https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html });