From 9e1ecbbe1f7623daf102e3c438f08cfb9f5d1dc9 Mon Sep 17 00:00:00 2001 From: Dave Smith-Hayes Date: Tue, 8 Oct 2024 08:42:15 -0400 Subject: [PATCH] Hey the chat works now a bit. --- chat-app/frontend/components/ChatApp.js | 14 - chat-app/frontend/components/Login.js | 37 - chat-app/frontend/components/MessageBox.js | 7 - chat-app/frontend/index.js | 66 +- chat-app/frontend/login.js | 14 + chat-app/frontend/session.js | 31 - chat-app/frontend/state.js | 2 + chat-app/package.json | 2 +- chat-app/src/chat-room.ts | 6 +- chat-app/src/index.ts | 21 +- chat-app/static/js/index.js | 1775 ++++++++++++++++++++ 11 files changed, 1860 insertions(+), 115 deletions(-) delete mode 100644 chat-app/frontend/components/ChatApp.js delete mode 100644 chat-app/frontend/components/Login.js delete mode 100644 chat-app/frontend/components/MessageBox.js create mode 100644 chat-app/frontend/login.js delete mode 100644 chat-app/frontend/session.js create mode 100644 chat-app/frontend/state.js create mode 100644 chat-app/static/js/index.js diff --git a/chat-app/frontend/components/ChatApp.js b/chat-app/frontend/components/ChatApp.js deleted file mode 100644 index 4cbed58..0000000 --- a/chat-app/frontend/components/ChatApp.js +++ /dev/null @@ -1,14 +0,0 @@ -import m from "mithril"; -import { MessageBox } from "./MessageBox"; -import { LoginForm } from "./Login"; - -export function createChatApp({ ws, session }) { - return { - view: function () { - return m("div", { class: "container" }, [ - m("h1", "A Stupid Chat Application"), - m(LoginForm) - ]); - } - }; -} diff --git a/chat-app/frontend/components/Login.js b/chat-app/frontend/components/Login.js deleted file mode 100644 index f657e0e..0000000 --- a/chat-app/frontend/components/Login.js +++ /dev/null @@ -1,37 +0,0 @@ -import m from "mithril"; -import Cookies from "js-cookie"; - -export function LoginForm() { - let loggedIn = false; - - async function login(e) { - e.preventDefault(); - - const formData = new FormData(e.target); - - const res = await m.request({ - method: "POST", - url: "/login", - body: formData - }); - - if (res.success) { - Cookies.set('logged-in', true); - } else { - Cookies.remove('logged-in'); - } - } - - return { - oninit: function() { - loggedIn = !!Cookies.get('logged-in'); - }, - view: function() { - return m("form", { action: "/login", method: "post", onsubmit: login }, [ - m("label", { for: "username" }, "Username"), - m("input", { type: "text", name: "username" }), - m("input", { type: "submit", value: "Login" }) - ]); - } - } -} diff --git a/chat-app/frontend/components/MessageBox.js b/chat-app/frontend/components/MessageBox.js deleted file mode 100644 index 40bb59e..0000000 --- a/chat-app/frontend/components/MessageBox.js +++ /dev/null @@ -1,7 +0,0 @@ -import m from "mithril"; - -export const MessageBox = { - view: function () { - return m("div", { id: "message-box" }, "Do Something"); - } -} diff --git a/chat-app/frontend/index.js b/chat-app/frontend/index.js index 62e94fe..5b34281 100644 --- a/chat-app/frontend/index.js +++ b/chat-app/frontend/index.js @@ -1,10 +1,62 @@ import m from "mithril"; -import { createChatApp } from "./components/ChatApp"; import { createWebSocket } from "./websocket"; -import { Sessions } from "./session"; -// open the WS to the server -const ws = createWebSocket(); -const session = new Sessions(); -const ChatApp = createChatApp({ ws, session }); -m.mount(document.getElementById("app"), ChatApp); +// on new message event, call m.redraw(); +const messages = []; +const socket = createWebSocket(); + +socket.onmessage = function (event) { + if (event.type === "message") { + const message = JSON.parse(event.data); + messages.push(message); + m.redraw(); + } +} + +function newMessageText(name, message) { + return `${name}: ${message}`; +} + +const MessageBox = { + view: function () { + return m(".messages", messages.map(function(message) { + return m(".message", newMessageText(message.chatter, message.message)); + })); + } +}; + +function sendMessage(e) { + e.preventDefault(); + const form = new FormData(e.target); + + const message = {}; + + for (const [key, value] of form.entries()) { + message[key] = value; + } + + socket.send(JSON.stringify(message)); +} + +const MessageInput = { + view: function () { + return m("div", [ + m("form", { method: "post", onsubmit: sendMessage }, [ + m("input", { name: "chatter", placeholder: "Name" }), + m("input", { name: "message", placeholder: "Message" }), + m("input", { type: "submit", value: "Send" }) + ]) + ]); + } +}; + +const Chat = { + view: function(vnode) { + return m("div", [ + m(MessageBox), + m(MessageInput) + ]) + } +}; + +m.mount(document.getElementById("app"), Chat); diff --git a/chat-app/frontend/login.js b/chat-app/frontend/login.js new file mode 100644 index 0000000..77ada13 --- /dev/null +++ b/chat-app/frontend/login.js @@ -0,0 +1,14 @@ +import m from 'mithril'; + +// login to the server +function login(e) { + +} + +export const Login = { + return m("form", { action: "/login", method: "post", onsubmit: login }, [ + m("label", { for: "username" }, "Username"), + m("input", { type: "text", name: "username" }), + m("input", { type: "submit", value: "Login" }) + ]); +}; diff --git a/chat-app/frontend/session.js b/chat-app/frontend/session.js deleted file mode 100644 index 6a35d38..0000000 --- a/chat-app/frontend/session.js +++ /dev/null @@ -1,31 +0,0 @@ -export class Sessions extends EventManager { - constructor() { - this.users = []; - } - - userExists(user) { - return !!this.findUser(user); - } - - findUser(user) { - return this.users.find(u => u.username === user.username); - } - - addUser(user) { - if (!this.userExists(user)) { - this.users.push(user); - this.emit('user-logged-in', user); - } - } - - clearUser(user) { - if (this.userExists(user)) { - const index = this.users.indexOf(user); - - if (index > -1) { - this.users.splice(index - 1, 1); - this.emit('user-removed', user); - } - } - } -} diff --git a/chat-app/frontend/state.js b/chat-app/frontend/state.js new file mode 100644 index 0000000..556f1d3 --- /dev/null +++ b/chat-app/frontend/state.js @@ -0,0 +1,2 @@ +const state = {}; + diff --git a/chat-app/package.json b/chat-app/package.json index b24a8ea..b3cc4d4 100644 --- a/chat-app/package.json +++ b/chat-app/package.json @@ -2,7 +2,7 @@ "name": "chat-app", "scripts": { "dev": "bun run --hot src/index.ts", - "build": "rm -r static/js && bun build.ts" + "build": "rm -r static/js; bun build.ts" }, "dependencies": { "@types/mithril": "^2.2.7", diff --git a/chat-app/src/chat-room.ts b/chat-app/src/chat-room.ts index 35903de..f3e41cd 100644 --- a/chat-app/src/chat-room.ts +++ b/chat-app/src/chat-room.ts @@ -8,8 +8,8 @@ export const chatters = new Array(); export type Message = { message: string, - timestamp: Date - chatter: Chatter + timestamp: Date, + chatter: string, }; export function createMessageString(message: Message): string { @@ -21,7 +21,7 @@ export class ChatRoom extends EventEmitter { public constructor() { super(); - this.message = new Array(); + this.messages = new Array(); } public getMessages(): Message[] { diff --git a/chat-app/src/index.ts b/chat-app/src/index.ts index d10e734..2ecd59b 100644 --- a/chat-app/src/index.ts +++ b/chat-app/src/index.ts @@ -4,12 +4,10 @@ import type { ServerWebSocket } from "hono"; import { chatRoom, createMessageString, - type Chatter, type Message } from "./chat-room.ts"; const app = new Hono(); -const users: string[] = []; // Set up the chat socket const { upgradeWebSocket, websocket } = createBunWebSocket(); @@ -19,22 +17,15 @@ app.get("/chat-service", upgradeWebSocket((c) => { chatRoom.addListener('message-added', function (e) { ws.send(JSON.stringify({ message: e.message.message, - chatter: e.message.chatter + chatter: e.message.chatter, + timestamp: e.message.timestamp })); }); }, onMessage(event, ws) { - const data = JSON.parse(event.data); - - if (!chatters.find(c => c.name == data.chatter.name)) { - chatters.push(data.chatter); - } - - const message: Message = { - chatter: data.chatter, - message: data.message, - timestamp: Date.now() as string - }; + const { data } = event; + const message = JSON.parse(data); + message.timestamp = Date.now() as string; chatRoom.addMessage(message); }, @@ -42,7 +33,7 @@ app.get("/chat-service", upgradeWebSocket((c) => { console.log("Connection closed."); }, onError(event) { - console.log(event); + console.error(event); } } })); diff --git a/chat-app/static/js/index.js b/chat-app/static/js/index.js new file mode 100644 index 0000000..97643c3 --- /dev/null +++ b/chat-app/static/js/index.js @@ -0,0 +1,1775 @@ +var __create = Object.create; +var __getProtoOf = Object.getPrototypeOf; +var __defProp = Object.defineProperty; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __toESM = (mod, isNodeMode, target) => { + target = mod != null ? __create(__getProtoOf(mod)) : {}; + const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target; + for (let key of __getOwnPropNames(mod)) + if (!__hasOwnProp.call(to, key)) + __defProp(to, key, { + get: () => mod[key], + enumerable: true + }); + return to; +}; +var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports); + +// node_modules/mithril/render/vnode.js +var require_vnode = __commonJS((exports, module) => { + function Vnode(tag, key, attrs, children, text, dom) { + return { tag, key, attrs, children, text, dom, domSize: undefined, state: undefined, events: undefined, instance: undefined }; + } + Vnode.normalize = function(node) { + if (Array.isArray(node)) + return Vnode("[", undefined, undefined, Vnode.normalizeChildren(node), undefined, undefined); + if (node == null || typeof node === "boolean") + return null; + if (typeof node === "object") + return node; + return Vnode("#", undefined, undefined, String(node), undefined, undefined); + }; + Vnode.normalizeChildren = function(input) { + var children = []; + if (input.length) { + var isKeyed = input[0] != null && input[0].key != null; + for (var i = 1;i < input.length; i++) { + if ((input[i] != null && input[i].key != null) !== isKeyed) { + throw new TypeError(isKeyed && (input[i] != null || typeof input[i] === "boolean") ? "In fragments, vnodes must either all have keys or none have keys. You may wish to consider using an explicit keyed empty fragment, m.fragment({key: ...}), instead of a hole." : "In fragments, vnodes must either all have keys or none have keys."); + } + } + for (var i = 0;i < input.length; i++) { + children[i] = Vnode.normalize(input[i]); + } + } + return children; + }; + module.exports = Vnode; +}); + +// node_modules/mithril/render/hyperscriptVnode.js +var require_hyperscriptVnode = __commonJS((exports, module) => { + var Vnode = require_vnode(); + module.exports = function() { + var attrs = arguments[this], start = this + 1, children; + if (attrs == null) { + attrs = {}; + } else if (typeof attrs !== "object" || attrs.tag != null || Array.isArray(attrs)) { + attrs = {}; + start = this; + } + if (arguments.length === start + 1) { + children = arguments[start]; + if (!Array.isArray(children)) + children = [children]; + } else { + children = []; + while (start < arguments.length) + children.push(arguments[start++]); + } + return Vnode("", attrs.key, attrs, children); + }; +}); + +// node_modules/mithril/util/hasOwn.js +var require_hasOwn = __commonJS((exports, module) => { + module.exports = {}.hasOwnProperty; +}); + +// node_modules/mithril/render/hyperscript.js +var require_hyperscript = __commonJS((exports, module) => { + function isEmpty(object) { + for (var key in object) + if (hasOwn.call(object, key)) + return false; + return true; + } + function compileSelector(selector) { + var match, tag = "div", classes = [], attrs = {}; + while (match = selectorParser.exec(selector)) { + var type = match[1], value = match[2]; + if (type === "" && value !== "") + tag = value; + else if (type === "#") + attrs.id = value; + else if (type === ".") + classes.push(value); + else if (match[3][0] === "[") { + var attrValue = match[6]; + if (attrValue) + attrValue = attrValue.replace(/\\(["'])/g, "$1").replace(/\\\\/g, "\\"); + if (match[4] === "class") + classes.push(attrValue); + else + attrs[match[4]] = attrValue === "" ? attrValue : attrValue || true; + } + } + if (classes.length > 0) + attrs.className = classes.join(" "); + return selectorCache[selector] = { tag, attrs }; + } + function execSelector(state, vnode) { + var attrs = vnode.attrs; + var hasClass = hasOwn.call(attrs, "class"); + var className = hasClass ? attrs.class : attrs.className; + vnode.tag = state.tag; + if (!isEmpty(state.attrs)) { + var newAttrs = {}; + for (var key in attrs) { + if (hasOwn.call(attrs, key)) + newAttrs[key] = attrs[key]; + } + attrs = newAttrs; + } + for (var key in state.attrs) { + if (hasOwn.call(state.attrs, key) && key !== "className" && !hasOwn.call(attrs, key)) { + attrs[key] = state.attrs[key]; + } + } + if (className != null || state.attrs.className != null) + attrs.className = className != null ? state.attrs.className != null ? String(state.attrs.className) + " " + String(className) : className : state.attrs.className != null ? state.attrs.className : null; + if (hasClass) + attrs.class = null; + vnode.attrs = attrs; + return vnode; + } + function hyperscript(selector) { + if (selector == null || typeof selector !== "string" && typeof selector !== "function" && typeof selector.view !== "function") { + throw Error("The selector must be either a string or a component."); + } + var vnode = hyperscriptVnode.apply(1, arguments); + if (typeof selector === "string") { + vnode.children = Vnode.normalizeChildren(vnode.children); + if (selector !== "[") + return execSelector(selectorCache[selector] || compileSelector(selector), vnode); + } + vnode.tag = selector; + return vnode; + } + var Vnode = require_vnode(); + var hyperscriptVnode = require_hyperscriptVnode(); + var hasOwn = require_hasOwn(); + var selectorParser = /(?:(^|#|\.)([^#\.\[\]]+))|(\[(.+?)(?:\s*=\s*("|'|)((?:\\["'\]]|.)*?)\5)?\])/g; + var selectorCache = {}; + module.exports = hyperscript; +}); + +// node_modules/mithril/render/trust.js +var require_trust = __commonJS((exports, module) => { + var Vnode = require_vnode(); + module.exports = function(html) { + if (html == null) + html = ""; + return Vnode("<", undefined, undefined, html, undefined, undefined); + }; +}); + +// node_modules/mithril/render/fragment.js +var require_fragment = __commonJS((exports, module) => { + var Vnode = require_vnode(); + var hyperscriptVnode = require_hyperscriptVnode(); + module.exports = function() { + var vnode = hyperscriptVnode.apply(0, arguments); + vnode.tag = "["; + vnode.children = Vnode.normalizeChildren(vnode.children); + return vnode; + }; +}); + +// node_modules/mithril/hyperscript.js +var require_hyperscript2 = __commonJS((exports, module) => { + var hyperscript = require_hyperscript(); + hyperscript.trust = require_trust(); + hyperscript.fragment = require_fragment(); + module.exports = hyperscript; +}); + +// node_modules/mithril/render/domFor.js +var require_domFor = __commonJS((exports, module) => { + function* domFor({ dom, domSize }, { generation } = {}) { + if (dom != null) + do { + const { nextSibling } = dom; + if (delayedRemoval.get(dom) === generation) { + yield dom; + domSize--; + } + dom = nextSibling; + } while (domSize); + } + var delayedRemoval = new WeakMap; + module.exports = { + delayedRemoval, + domFor + }; +}); + +// node_modules/mithril/render/render.js +var require_render = __commonJS((exports, module) => { + var Vnode = require_vnode(); + var df = require_domFor(); + var delayedRemoval = df.delayedRemoval; + var domFor = df.domFor; + module.exports = function() { + var nameSpace = { + svg: "http://www.w3.org/2000/svg", + math: "http://www.w3.org/1998/Math/MathML" + }; + var currentRedraw; + var currentRender; + function getDocument(dom) { + return dom.ownerDocument; + } + function getNameSpace(vnode) { + return vnode.attrs && vnode.attrs.xmlns || nameSpace[vnode.tag]; + } + function checkState(vnode, original) { + if (vnode.state !== original) + throw new Error("'vnode.state' must not be modified."); + } + function callHook(vnode) { + var original = vnode.state; + try { + return this.apply(original, arguments); + } finally { + checkState(vnode, original); + } + } + function activeElement(dom) { + try { + return getDocument(dom).activeElement; + } catch (e) { + return null; + } + } + function createNodes(parent, vnodes, start, end, hooks, nextSibling, ns) { + for (var i = start;i < end; i++) { + var vnode = vnodes[i]; + if (vnode != null) { + createNode(parent, vnode, hooks, ns, nextSibling); + } + } + } + function createNode(parent, vnode, hooks, ns, nextSibling) { + var tag = vnode.tag; + if (typeof tag === "string") { + vnode.state = {}; + if (vnode.attrs != null) + initLifecycle(vnode.attrs, vnode, hooks); + switch (tag) { + case "#": + createText(parent, vnode, nextSibling); + break; + case "<": + createHTML(parent, vnode, ns, nextSibling); + break; + case "[": + createFragment(parent, vnode, hooks, ns, nextSibling); + break; + default: + createElement(parent, vnode, hooks, ns, nextSibling); + } + } else + createComponent(parent, vnode, hooks, ns, nextSibling); + } + function createText(parent, vnode, nextSibling) { + vnode.dom = getDocument(parent).createTextNode(vnode.children); + insertDOM(parent, vnode.dom, nextSibling); + } + var possibleParents = { caption: "table", thead: "table", tbody: "table", tfoot: "table", tr: "tbody", th: "tr", td: "tr", colgroup: "table", col: "colgroup" }; + function createHTML(parent, vnode, ns, nextSibling) { + var match = vnode.children.match(/^\s*?<(\w+)/im) || []; + var temp = getDocument(parent).createElement(possibleParents[match[1]] || "div"); + if (ns === "http://www.w3.org/2000/svg") { + temp.innerHTML = "" + vnode.children + ""; + temp = temp.firstChild; + } else { + temp.innerHTML = vnode.children; + } + vnode.dom = temp.firstChild; + vnode.domSize = temp.childNodes.length; + var fragment = getDocument(parent).createDocumentFragment(); + var child; + while (child = temp.firstChild) { + fragment.appendChild(child); + } + insertDOM(parent, fragment, nextSibling); + } + function createFragment(parent, vnode, hooks, ns, nextSibling) { + var fragment = getDocument(parent).createDocumentFragment(); + if (vnode.children != null) { + var children = vnode.children; + createNodes(fragment, children, 0, children.length, hooks, null, ns); + } + vnode.dom = fragment.firstChild; + vnode.domSize = fragment.childNodes.length; + insertDOM(parent, fragment, nextSibling); + } + function createElement(parent, vnode, hooks, ns, nextSibling) { + var tag = vnode.tag; + var attrs = vnode.attrs; + var is = attrs && attrs.is; + ns = getNameSpace(vnode) || ns; + var element = ns ? is ? getDocument(parent).createElementNS(ns, tag, { is }) : getDocument(parent).createElementNS(ns, tag) : is ? getDocument(parent).createElement(tag, { is }) : getDocument(parent).createElement(tag); + vnode.dom = element; + if (attrs != null) { + setAttrs(vnode, attrs, ns); + } + insertDOM(parent, element, nextSibling); + if (!maybeSetContentEditable(vnode)) { + if (vnode.children != null) { + var children = vnode.children; + createNodes(element, children, 0, children.length, hooks, null, ns); + if (vnode.tag === "select" && attrs != null) + setLateSelectAttrs(vnode, attrs); + } + } + } + function initComponent(vnode, hooks) { + var sentinel; + if (typeof vnode.tag.view === "function") { + vnode.state = Object.create(vnode.tag); + sentinel = vnode.state.view; + if (sentinel.$$reentrantLock$$ != null) + return; + sentinel.$$reentrantLock$$ = true; + } else { + vnode.state = undefined; + sentinel = vnode.tag; + if (sentinel.$$reentrantLock$$ != null) + return; + sentinel.$$reentrantLock$$ = true; + vnode.state = vnode.tag.prototype != null && typeof vnode.tag.prototype.view === "function" ? new vnode.tag(vnode) : vnode.tag(vnode); + } + initLifecycle(vnode.state, vnode, hooks); + if (vnode.attrs != null) + initLifecycle(vnode.attrs, vnode, hooks); + vnode.instance = Vnode.normalize(callHook.call(vnode.state.view, vnode)); + if (vnode.instance === vnode) + throw Error("A view cannot return the vnode it received as argument"); + sentinel.$$reentrantLock$$ = null; + } + function createComponent(parent, vnode, hooks, ns, nextSibling) { + initComponent(vnode, hooks); + if (vnode.instance != null) { + createNode(parent, vnode.instance, hooks, ns, nextSibling); + vnode.dom = vnode.instance.dom; + vnode.domSize = vnode.dom != null ? vnode.instance.domSize : 0; + } else { + vnode.domSize = 0; + } + } + function updateNodes(parent, old, vnodes, hooks, nextSibling, ns) { + if (old === vnodes || old == null && vnodes == null) + return; + else if (old == null || old.length === 0) + createNodes(parent, vnodes, 0, vnodes.length, hooks, nextSibling, ns); + else if (vnodes == null || vnodes.length === 0) + removeNodes(parent, old, 0, old.length); + else { + var isOldKeyed = old[0] != null && old[0].key != null; + var isKeyed = vnodes[0] != null && vnodes[0].key != null; + var start = 0, oldStart = 0; + if (!isOldKeyed) + while (oldStart < old.length && old[oldStart] == null) + oldStart++; + if (!isKeyed) + while (start < vnodes.length && vnodes[start] == null) + start++; + if (isOldKeyed !== isKeyed) { + removeNodes(parent, old, oldStart, old.length); + createNodes(parent, vnodes, start, vnodes.length, hooks, nextSibling, ns); + } else if (!isKeyed) { + var commonLength = old.length < vnodes.length ? old.length : vnodes.length; + start = start < oldStart ? start : oldStart; + for (;start < commonLength; start++) { + o = old[start]; + v = vnodes[start]; + if (o === v || o == null && v == null) + continue; + else if (o == null) + createNode(parent, v, hooks, ns, getNextSibling(old, start + 1, nextSibling)); + else if (v == null) + removeNode(parent, o); + else + updateNode(parent, o, v, hooks, getNextSibling(old, start + 1, nextSibling), ns); + } + if (old.length > commonLength) + removeNodes(parent, old, start, old.length); + if (vnodes.length > commonLength) + createNodes(parent, vnodes, start, vnodes.length, hooks, nextSibling, ns); + } else { + var oldEnd = old.length - 1, end = vnodes.length - 1, map, o, v, oe, ve, topSibling; + while (oldEnd >= oldStart && end >= start) { + oe = old[oldEnd]; + ve = vnodes[end]; + if (oe.key !== ve.key) + break; + if (oe !== ve) + updateNode(parent, oe, ve, hooks, nextSibling, ns); + if (ve.dom != null) + nextSibling = ve.dom; + oldEnd--, end--; + } + while (oldEnd >= oldStart && end >= start) { + o = old[oldStart]; + v = vnodes[start]; + if (o.key !== v.key) + break; + oldStart++, start++; + if (o !== v) + updateNode(parent, o, v, hooks, getNextSibling(old, oldStart, nextSibling), ns); + } + while (oldEnd >= oldStart && end >= start) { + if (start === end) + break; + if (o.key !== ve.key || oe.key !== v.key) + break; + topSibling = getNextSibling(old, oldStart, nextSibling); + moveDOM(parent, oe, topSibling); + if (oe !== v) + updateNode(parent, oe, v, hooks, topSibling, ns); + if (++start <= --end) + moveDOM(parent, o, nextSibling); + if (o !== ve) + updateNode(parent, o, ve, hooks, nextSibling, ns); + if (ve.dom != null) + nextSibling = ve.dom; + oldStart++; + oldEnd--; + oe = old[oldEnd]; + ve = vnodes[end]; + o = old[oldStart]; + v = vnodes[start]; + } + while (oldEnd >= oldStart && end >= start) { + if (oe.key !== ve.key) + break; + if (oe !== ve) + updateNode(parent, oe, ve, hooks, nextSibling, ns); + if (ve.dom != null) + nextSibling = ve.dom; + oldEnd--, end--; + oe = old[oldEnd]; + ve = vnodes[end]; + } + if (start > end) + removeNodes(parent, old, oldStart, oldEnd + 1); + else if (oldStart > oldEnd) + createNodes(parent, vnodes, start, end + 1, hooks, nextSibling, ns); + else { + var originalNextSibling = nextSibling, vnodesLength = end - start + 1, oldIndices = new Array(vnodesLength), li = 0, i = 0, pos = 2147483647, matched = 0, map, lisIndices; + for (i = 0;i < vnodesLength; i++) + oldIndices[i] = -1; + for (i = end;i >= start; i--) { + if (map == null) + map = getKeyMap(old, oldStart, oldEnd + 1); + ve = vnodes[i]; + var oldIndex = map[ve.key]; + if (oldIndex != null) { + pos = oldIndex < pos ? oldIndex : -1; + oldIndices[i - start] = oldIndex; + oe = old[oldIndex]; + old[oldIndex] = null; + if (oe !== ve) + updateNode(parent, oe, ve, hooks, nextSibling, ns); + if (ve.dom != null) + nextSibling = ve.dom; + matched++; + } + } + nextSibling = originalNextSibling; + if (matched !== oldEnd - oldStart + 1) + removeNodes(parent, old, oldStart, oldEnd + 1); + if (matched === 0) + createNodes(parent, vnodes, start, end + 1, hooks, nextSibling, ns); + else { + if (pos === -1) { + lisIndices = makeLisIndices(oldIndices); + li = lisIndices.length - 1; + for (i = end;i >= start; i--) { + v = vnodes[i]; + if (oldIndices[i - start] === -1) + createNode(parent, v, hooks, ns, nextSibling); + else { + if (lisIndices[li] === i - start) + li--; + else + moveDOM(parent, v, nextSibling); + } + if (v.dom != null) + nextSibling = vnodes[i].dom; + } + } else { + for (i = end;i >= start; i--) { + v = vnodes[i]; + if (oldIndices[i - start] === -1) + createNode(parent, v, hooks, ns, nextSibling); + if (v.dom != null) + nextSibling = vnodes[i].dom; + } + } + } + } + } + } + } + function updateNode(parent, old, vnode, hooks, nextSibling, ns) { + var oldTag = old.tag, tag = vnode.tag; + if (oldTag === tag) { + vnode.state = old.state; + vnode.events = old.events; + if (shouldNotUpdate(vnode, old)) + return; + if (typeof oldTag === "string") { + if (vnode.attrs != null) { + updateLifecycle(vnode.attrs, vnode, hooks); + } + switch (oldTag) { + case "#": + updateText(old, vnode); + break; + case "<": + updateHTML(parent, old, vnode, ns, nextSibling); + break; + case "[": + updateFragment(parent, old, vnode, hooks, nextSibling, ns); + break; + default: + updateElement(old, vnode, hooks, ns); + } + } else + updateComponent(parent, old, vnode, hooks, nextSibling, ns); + } else { + removeNode(parent, old); + createNode(parent, vnode, hooks, ns, nextSibling); + } + } + function updateText(old, vnode) { + if (old.children.toString() !== vnode.children.toString()) { + old.dom.nodeValue = vnode.children; + } + vnode.dom = old.dom; + } + function updateHTML(parent, old, vnode, ns, nextSibling) { + if (old.children !== vnode.children) { + removeDOM(parent, old, undefined); + createHTML(parent, vnode, ns, nextSibling); + } else { + vnode.dom = old.dom; + vnode.domSize = old.domSize; + } + } + function updateFragment(parent, old, vnode, hooks, nextSibling, ns) { + updateNodes(parent, old.children, vnode.children, hooks, nextSibling, ns); + var domSize = 0, children = vnode.children; + vnode.dom = null; + if (children != null) { + for (var i = 0;i < children.length; i++) { + var child = children[i]; + if (child != null && child.dom != null) { + if (vnode.dom == null) + vnode.dom = child.dom; + domSize += child.domSize || 1; + } + } + if (domSize !== 1) + vnode.domSize = domSize; + } + } + function updateElement(old, vnode, hooks, ns) { + var element = vnode.dom = old.dom; + ns = getNameSpace(vnode) || ns; + updateAttrs(vnode, old.attrs, vnode.attrs, ns); + if (!maybeSetContentEditable(vnode)) { + updateNodes(element, old.children, vnode.children, hooks, null, ns); + } + } + function updateComponent(parent, old, vnode, hooks, nextSibling, ns) { + vnode.instance = Vnode.normalize(callHook.call(vnode.state.view, vnode)); + if (vnode.instance === vnode) + throw Error("A view cannot return the vnode it received as argument"); + updateLifecycle(vnode.state, vnode, hooks); + if (vnode.attrs != null) + updateLifecycle(vnode.attrs, vnode, hooks); + if (vnode.instance != null) { + if (old.instance == null) + createNode(parent, vnode.instance, hooks, ns, nextSibling); + else + updateNode(parent, old.instance, vnode.instance, hooks, nextSibling, ns); + vnode.dom = vnode.instance.dom; + vnode.domSize = vnode.instance.domSize; + } else if (old.instance != null) { + removeNode(parent, old.instance); + vnode.dom = undefined; + vnode.domSize = 0; + } else { + vnode.dom = old.dom; + vnode.domSize = old.domSize; + } + } + function getKeyMap(vnodes, start, end) { + var map = Object.create(null); + for (;start < end; start++) { + var vnode = vnodes[start]; + if (vnode != null) { + var key = vnode.key; + if (key != null) + map[key] = start; + } + } + return map; + } + var lisTemp = []; + function makeLisIndices(a) { + var result = [0]; + var u = 0, v = 0, i = 0; + var il = lisTemp.length = a.length; + for (var i = 0;i < il; i++) + lisTemp[i] = a[i]; + for (var i = 0;i < il; ++i) { + if (a[i] === -1) + continue; + var j = result[result.length - 1]; + if (a[j] < a[i]) { + lisTemp[i] = j; + result.push(i); + continue; + } + u = 0; + v = result.length - 1; + while (u < v) { + var c = (u >>> 1) + (v >>> 1) + (u & v & 1); + if (a[result[c]] < a[i]) { + u = c + 1; + } else { + v = c; + } + } + if (a[i] < a[result[u]]) { + if (u > 0) + lisTemp[i] = result[u - 1]; + result[u] = i; + } + } + u = result.length; + v = result[u - 1]; + while (u-- > 0) { + result[u] = v; + v = lisTemp[v]; + } + lisTemp.length = 0; + return result; + } + function getNextSibling(vnodes, i, nextSibling) { + for (;i < vnodes.length; i++) { + if (vnodes[i] != null && vnodes[i].dom != null) + return vnodes[i].dom; + } + return nextSibling; + } + function moveDOM(parent, vnode, nextSibling) { + if (vnode.dom != null) { + var target; + if (vnode.domSize == null) { + target = vnode.dom; + } else { + target = getDocument(parent).createDocumentFragment(); + for (var dom of domFor(vnode)) + target.appendChild(dom); + } + insertDOM(parent, target, nextSibling); + } + } + function insertDOM(parent, dom, nextSibling) { + if (nextSibling != null) + parent.insertBefore(dom, nextSibling); + else + parent.appendChild(dom); + } + function maybeSetContentEditable(vnode) { + if (vnode.attrs == null || vnode.attrs.contenteditable == null && vnode.attrs.contentEditable == null) + return false; + var children = vnode.children; + if (children != null && children.length === 1 && children[0].tag === "<") { + var content = children[0].children; + if (vnode.dom.innerHTML !== content) + vnode.dom.innerHTML = content; + } else if (children != null && children.length !== 0) + throw new Error("Child node of a contenteditable must be trusted."); + return true; + } + function removeNodes(parent, vnodes, start, end) { + for (var i = start;i < end; i++) { + var vnode = vnodes[i]; + if (vnode != null) + removeNode(parent, vnode); + } + } + function removeNode(parent, vnode) { + var mask = 0; + var original = vnode.state; + var stateResult, attrsResult; + if (typeof vnode.tag !== "string" && typeof vnode.state.onbeforeremove === "function") { + var result = callHook.call(vnode.state.onbeforeremove, vnode); + if (result != null && typeof result.then === "function") { + mask = 1; + stateResult = result; + } + } + if (vnode.attrs && typeof vnode.attrs.onbeforeremove === "function") { + var result = callHook.call(vnode.attrs.onbeforeremove, vnode); + if (result != null && typeof result.then === "function") { + mask |= 2; + attrsResult = result; + } + } + checkState(vnode, original); + var generation; + if (!mask) { + onremove(vnode); + removeDOM(parent, vnode, generation); + } else { + generation = currentRender; + for (var dom of domFor(vnode)) + delayedRemoval.set(dom, generation); + if (stateResult != null) { + stateResult.finally(function() { + if (mask & 1) { + mask &= 2; + if (!mask) { + checkState(vnode, original); + onremove(vnode); + removeDOM(parent, vnode, generation); + } + } + }); + } + if (attrsResult != null) { + attrsResult.finally(function() { + if (mask & 2) { + mask &= 1; + if (!mask) { + checkState(vnode, original); + onremove(vnode); + removeDOM(parent, vnode, generation); + } + } + }); + } + } + } + function removeDOM(parent, vnode, generation) { + if (vnode.dom == null) + return; + if (vnode.domSize == null) { + if (delayedRemoval.get(vnode.dom) === generation) + parent.removeChild(vnode.dom); + } else { + for (var dom of domFor(vnode, { generation })) + parent.removeChild(dom); + } + } + function onremove(vnode) { + if (typeof vnode.tag !== "string" && typeof vnode.state.onremove === "function") + callHook.call(vnode.state.onremove, vnode); + if (vnode.attrs && typeof vnode.attrs.onremove === "function") + callHook.call(vnode.attrs.onremove, vnode); + if (typeof vnode.tag !== "string") { + if (vnode.instance != null) + onremove(vnode.instance); + } else { + var children = vnode.children; + if (Array.isArray(children)) { + for (var i = 0;i < children.length; i++) { + var child = children[i]; + if (child != null) + onremove(child); + } + } + } + } + function setAttrs(vnode, attrs, ns) { + if (vnode.tag === "input" && attrs.type != null) + vnode.dom.setAttribute("type", attrs.type); + var isFileInput = attrs != null && vnode.tag === "input" && attrs.type === "file"; + for (var key in attrs) { + setAttr(vnode, key, null, attrs[key], ns, isFileInput); + } + } + function setAttr(vnode, key, old, value, ns, isFileInput) { + if (key === "key" || key === "is" || value == null || isLifecycleMethod(key) || old === value && !isFormAttribute(vnode, key) && typeof value !== "object" || key === "type" && vnode.tag === "input") + return; + if (key[0] === "o" && key[1] === "n") + return updateEvent(vnode, key, value); + if (key.slice(0, 6) === "xlink:") + vnode.dom.setAttributeNS("http://www.w3.org/1999/xlink", key.slice(6), value); + else if (key === "style") + updateStyle(vnode.dom, old, value); + else if (hasPropertyKey(vnode, key, ns)) { + if (key === "value") { + if ((vnode.tag === "input" || vnode.tag === "textarea") && vnode.dom.value === "" + value && (isFileInput || vnode.dom === activeElement(vnode.dom))) + return; + if (vnode.tag === "select" && old !== null && vnode.dom.value === "" + value) + return; + if (vnode.tag === "option" && old !== null && vnode.dom.value === "" + value) + return; + if (isFileInput && "" + value !== "") { + console.error("`value` is read-only on file inputs!"); + return; + } + } + vnode.dom[key] = value; + } else { + if (typeof value === "boolean") { + if (value) + vnode.dom.setAttribute(key, ""); + else + vnode.dom.removeAttribute(key); + } else + vnode.dom.setAttribute(key === "className" ? "class" : key, value); + } + } + function removeAttr(vnode, key, old, ns) { + if (key === "key" || key === "is" || old == null || isLifecycleMethod(key)) + return; + if (key[0] === "o" && key[1] === "n") + updateEvent(vnode, key, undefined); + else if (key === "style") + updateStyle(vnode.dom, old, null); + else if (hasPropertyKey(vnode, key, ns) && key !== "className" && key !== "title" && !(key === "value" && (vnode.tag === "option" || vnode.tag === "select" && vnode.dom.selectedIndex === -1 && vnode.dom === activeElement(vnode.dom))) && !(vnode.tag === "input" && key === "type")) { + vnode.dom[key] = null; + } else { + var nsLastIndex = key.indexOf(":"); + if (nsLastIndex !== -1) + key = key.slice(nsLastIndex + 1); + if (old !== false) + vnode.dom.removeAttribute(key === "className" ? "class" : key); + } + } + function setLateSelectAttrs(vnode, attrs) { + if ("value" in attrs) { + if (attrs.value === null) { + if (vnode.dom.selectedIndex !== -1) + vnode.dom.value = null; + } else { + var normalized = "" + attrs.value; + if (vnode.dom.value !== normalized || vnode.dom.selectedIndex === -1) { + vnode.dom.value = normalized; + } + } + } + if ("selectedIndex" in attrs) + setAttr(vnode, "selectedIndex", null, attrs.selectedIndex, undefined); + } + function updateAttrs(vnode, old, attrs, ns) { + if (old && old === attrs) { + console.warn("Don't reuse attrs object, use new object for every redraw, this will throw in next major"); + } + if (attrs != null) { + if (vnode.tag === "input" && attrs.type != null) + vnode.dom.setAttribute("type", attrs.type); + var isFileInput = vnode.tag === "input" && attrs.type === "file"; + for (var key in attrs) { + setAttr(vnode, key, old && old[key], attrs[key], ns, isFileInput); + } + } + var val; + if (old != null) { + for (var key in old) { + if ((val = old[key]) != null && (attrs == null || attrs[key] == null)) { + removeAttr(vnode, key, val, ns); + } + } + } + } + function isFormAttribute(vnode, attr) { + return attr === "value" || attr === "checked" || attr === "selectedIndex" || attr === "selected" && vnode.dom === activeElement(vnode.dom) || vnode.tag === "option" && vnode.dom.parentNode === activeElement(vnode.dom); + } + function isLifecycleMethod(attr) { + return attr === "oninit" || attr === "oncreate" || attr === "onupdate" || attr === "onremove" || attr === "onbeforeremove" || attr === "onbeforeupdate"; + } + function hasPropertyKey(vnode, key, ns) { + return ns === undefined && (vnode.tag.indexOf("-") > -1 || vnode.attrs != null && vnode.attrs.is || key !== "href" && key !== "list" && key !== "form" && key !== "width" && key !== "height") && key in vnode.dom; + } + var uppercaseRegex = /[A-Z]/g; + function toLowerCase(capital) { + return "-" + capital.toLowerCase(); + } + function normalizeKey(key) { + return key[0] === "-" && key[1] === "-" ? key : key === "cssFloat" ? "float" : key.replace(uppercaseRegex, toLowerCase); + } + function updateStyle(element, old, style) { + if (old === style) { + } else if (style == null) { + element.style = ""; + } else if (typeof style !== "object") { + element.style = style; + } else if (old == null || typeof old !== "object") { + element.style.cssText = ""; + for (var key in style) { + var value = style[key]; + if (value != null) + element.style.setProperty(normalizeKey(key), String(value)); + } + } else { + for (var key in style) { + var value = style[key]; + if (value != null && (value = String(value)) !== String(old[key])) { + element.style.setProperty(normalizeKey(key), value); + } + } + for (var key in old) { + if (old[key] != null && style[key] == null) { + element.style.removeProperty(normalizeKey(key)); + } + } + } + } + function EventDict() { + this._ = currentRedraw; + } + EventDict.prototype = Object.create(null); + EventDict.prototype.handleEvent = function(ev) { + var handler = this["on" + ev.type]; + var result; + if (typeof handler === "function") + result = handler.call(ev.currentTarget, ev); + else if (typeof handler.handleEvent === "function") + handler.handleEvent(ev); + if (this._ && ev.redraw !== false) + (0, this._)(); + if (result === false) { + ev.preventDefault(); + ev.stopPropagation(); + } + }; + function updateEvent(vnode, key, value) { + if (vnode.events != null) { + vnode.events._ = currentRedraw; + if (vnode.events[key] === value) + return; + if (value != null && (typeof value === "function" || typeof value === "object")) { + if (vnode.events[key] == null) + vnode.dom.addEventListener(key.slice(2), vnode.events, false); + vnode.events[key] = value; + } else { + if (vnode.events[key] != null) + vnode.dom.removeEventListener(key.slice(2), vnode.events, false); + vnode.events[key] = undefined; + } + } else if (value != null && (typeof value === "function" || typeof value === "object")) { + vnode.events = new EventDict; + vnode.dom.addEventListener(key.slice(2), vnode.events, false); + vnode.events[key] = value; + } + } + function initLifecycle(source, vnode, hooks) { + if (typeof source.oninit === "function") + callHook.call(source.oninit, vnode); + if (typeof source.oncreate === "function") + hooks.push(callHook.bind(source.oncreate, vnode)); + } + function updateLifecycle(source, vnode, hooks) { + if (typeof source.onupdate === "function") + hooks.push(callHook.bind(source.onupdate, vnode)); + } + function shouldNotUpdate(vnode, old) { + do { + if (vnode.attrs != null && typeof vnode.attrs.onbeforeupdate === "function") { + var force = callHook.call(vnode.attrs.onbeforeupdate, vnode, old); + if (force !== undefined && !force) + break; + } + if (typeof vnode.tag !== "string" && typeof vnode.state.onbeforeupdate === "function") { + var force = callHook.call(vnode.state.onbeforeupdate, vnode, old); + if (force !== undefined && !force) + break; + } + return false; + } while (false); + vnode.dom = old.dom; + vnode.domSize = old.domSize; + vnode.instance = old.instance; + vnode.attrs = old.attrs; + vnode.children = old.children; + vnode.text = old.text; + return true; + } + var currentDOM; + return function(dom, vnodes, redraw) { + if (!dom) + throw new TypeError("DOM element being rendered to does not exist."); + if (currentDOM != null && dom.contains(currentDOM)) { + throw new TypeError("Node is currently being rendered to and thus is locked."); + } + var prevRedraw = currentRedraw; + var prevDOM = currentDOM; + var hooks = []; + var active = activeElement(dom); + var namespace = dom.namespaceURI; + currentDOM = dom; + currentRedraw = typeof redraw === "function" ? redraw : undefined; + currentRender = {}; + try { + if (dom.vnodes == null) + dom.textContent = ""; + vnodes = Vnode.normalizeChildren(Array.isArray(vnodes) ? vnodes : [vnodes]); + updateNodes(dom, dom.vnodes, vnodes, hooks, null, namespace === "http://www.w3.org/1999/xhtml" ? undefined : namespace); + dom.vnodes = vnodes; + if (active != null && activeElement(dom) !== active && typeof active.focus === "function") + active.focus(); + for (var i = 0;i < hooks.length; i++) + hooks[i](); + } finally { + currentRedraw = prevRedraw; + currentDOM = prevDOM; + } + }; + }; +}); + +// node_modules/mithril/render.js +var require_render2 = __commonJS((exports, module) => { + module.exports = require_render()(typeof window !== "undefined" ? window : null); +}); + +// node_modules/mithril/api/mount-redraw.js +var require_mount_redraw = __commonJS((exports, module) => { + var Vnode = require_vnode(); + module.exports = function(render, schedule, console2) { + var subscriptions = []; + var pending = false; + var offset = -1; + function sync() { + for (offset = 0;offset < subscriptions.length; offset += 2) { + try { + render(subscriptions[offset], Vnode(subscriptions[offset + 1]), redraw); + } catch (e) { + console2.error(e); + } + } + offset = -1; + } + function redraw() { + if (!pending) { + pending = true; + schedule(function() { + pending = false; + sync(); + }); + } + } + redraw.sync = sync; + function mount(root, component) { + if (component != null && component.view == null && typeof component !== "function") { + throw new TypeError("m.mount expects a component, not a vnode."); + } + var index = subscriptions.indexOf(root); + if (index >= 0) { + subscriptions.splice(index, 2); + if (index <= offset) + offset -= 2; + render(root, []); + } + if (component != null) { + subscriptions.push(root, component); + render(root, Vnode(component), redraw); + } + } + return { mount, redraw }; + }; +}); + +// node_modules/mithril/mount-redraw.js +var require_mount_redraw2 = __commonJS((exports, module) => { + var render = require_render2(); + module.exports = require_mount_redraw()(render, typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : null, typeof console !== "undefined" ? console : null); +}); + +// node_modules/mithril/querystring/build.js +var require_build = __commonJS((exports, module) => { + module.exports = function(object) { + if (Object.prototype.toString.call(object) !== "[object Object]") + return ""; + var args = []; + for (var key in object) { + destructure(key, object[key]); + } + return args.join("&"); + function destructure(key2, value) { + if (Array.isArray(value)) { + for (var i = 0;i < value.length; i++) { + destructure(key2 + "[" + i + "]", value[i]); + } + } else if (Object.prototype.toString.call(value) === "[object Object]") { + for (var i in value) { + destructure(key2 + "[" + i + "]", value[i]); + } + } else + args.push(encodeURIComponent(key2) + (value != null && value !== "" ? "=" + encodeURIComponent(value) : "")); + } + }; +}); + +// node_modules/mithril/util/assign.js +var require_assign = __commonJS((exports, module) => { + var hasOwn = require_hasOwn(); + module.exports = Object.assign || function(target, source) { + for (var key in source) { + if (hasOwn.call(source, key)) + target[key] = source[key]; + } + }; +}); + +// node_modules/mithril/pathname/build.js +var require_build2 = __commonJS((exports, module) => { + var buildQueryString = require_build(); + var assign = require_assign(); + module.exports = function(template, params) { + if (/:([^\/\.-]+)(\.{3})?:/.test(template)) { + throw new SyntaxError("Template parameter names must be separated by either a '/', '-', or '.'."); + } + if (params == null) + return template; + var queryIndex = template.indexOf("?"); + var hashIndex = template.indexOf("#"); + var queryEnd = hashIndex < 0 ? template.length : hashIndex; + var pathEnd = queryIndex < 0 ? queryEnd : queryIndex; + var path = template.slice(0, pathEnd); + var query = {}; + assign(query, params); + var resolved = path.replace(/:([^\/\.-]+)(\.{3})?/g, function(m, key, variadic) { + delete query[key]; + if (params[key] == null) + return m; + return variadic ? params[key] : encodeURIComponent(String(params[key])); + }); + var newQueryIndex = resolved.indexOf("?"); + var newHashIndex = resolved.indexOf("#"); + var newQueryEnd = newHashIndex < 0 ? resolved.length : newHashIndex; + var newPathEnd = newQueryIndex < 0 ? newQueryEnd : newQueryIndex; + var result = resolved.slice(0, newPathEnd); + if (queryIndex >= 0) + result += template.slice(queryIndex, queryEnd); + if (newQueryIndex >= 0) + result += (queryIndex < 0 ? "?" : "&") + resolved.slice(newQueryIndex, newQueryEnd); + var querystring = buildQueryString(query); + if (querystring) + result += (queryIndex < 0 && newQueryIndex < 0 ? "?" : "&") + querystring; + if (hashIndex >= 0) + result += template.slice(hashIndex); + if (newHashIndex >= 0) + result += (hashIndex < 0 ? "" : "&") + resolved.slice(newHashIndex); + return result; + }; +}); + +// node_modules/mithril/request/request.js +var require_request = __commonJS((exports, module) => { + var buildPathname = require_build2(); + var hasOwn = require_hasOwn(); + module.exports = function($window, oncompletion) { + function PromiseProxy(executor) { + return new Promise(executor); + } + function makeRequest(url, args) { + return new Promise(function(resolve, reject) { + url = buildPathname(url, args.params); + var method = args.method != null ? args.method.toUpperCase() : "GET"; + var body = args.body; + var assumeJSON = (args.serialize == null || args.serialize === JSON.serialize) && !(body instanceof $window.FormData || body instanceof $window.URLSearchParams); + var responseType = args.responseType || (typeof args.extract === "function" ? "" : "json"); + var xhr = new $window.XMLHttpRequest, aborted = false, isTimeout = false; + var original = xhr, replacedAbort; + var abort = xhr.abort; + xhr.abort = function() { + aborted = true; + abort.call(this); + }; + xhr.open(method, url, args.async !== false, typeof args.user === "string" ? args.user : undefined, typeof args.password === "string" ? args.password : undefined); + if (assumeJSON && body != null && !hasHeader(args, "content-type")) { + xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8"); + } + if (typeof args.deserialize !== "function" && !hasHeader(args, "accept")) { + xhr.setRequestHeader("Accept", "application/json, text/*"); + } + if (args.withCredentials) + xhr.withCredentials = args.withCredentials; + if (args.timeout) + xhr.timeout = args.timeout; + xhr.responseType = responseType; + for (var key in args.headers) { + if (hasOwn.call(args.headers, key)) { + xhr.setRequestHeader(key, args.headers[key]); + } + } + xhr.onreadystatechange = function(ev) { + if (aborted) + return; + if (ev.target.readyState === 4) { + try { + var success = ev.target.status >= 200 && ev.target.status < 300 || ev.target.status === 304 || /^file:\/\//i.test(url); + var response = ev.target.response, message; + if (responseType === "json") { + if (!ev.target.responseType && typeof args.extract !== "function") { + try { + response = JSON.parse(ev.target.responseText); + } catch (e) { + response = null; + } + } + } else if (!responseType || responseType === "text") { + if (response == null) + response = ev.target.responseText; + } + if (typeof args.extract === "function") { + response = args.extract(ev.target, args); + success = true; + } else if (typeof args.deserialize === "function") { + response = args.deserialize(response); + } + if (success) { + if (typeof args.type === "function") { + if (Array.isArray(response)) { + for (var i = 0;i < response.length; i++) { + response[i] = new args.type(response[i]); + } + } else + response = new args.type(response); + } + resolve(response); + } else { + var completeErrorResponse = function() { + try { + message = ev.target.responseText; + } catch (e) { + message = response; + } + var error = new Error(message); + error.code = ev.target.status; + error.response = response; + reject(error); + }; + if (xhr.status === 0) { + setTimeout(function() { + if (isTimeout) + return; + completeErrorResponse(); + }); + } else + completeErrorResponse(); + } + } catch (e) { + reject(e); + } + } + }; + xhr.ontimeout = function(ev) { + isTimeout = true; + var error = new Error("Request timed out"); + error.code = ev.target.status; + reject(error); + }; + if (typeof args.config === "function") { + xhr = args.config(xhr, args, url) || xhr; + if (xhr !== original) { + replacedAbort = xhr.abort; + xhr.abort = function() { + aborted = true; + replacedAbort.call(this); + }; + } + } + if (body == null) + xhr.send(); + else if (typeof args.serialize === "function") + xhr.send(args.serialize(body)); + else if (body instanceof $window.FormData || body instanceof $window.URLSearchParams) + xhr.send(body); + else + xhr.send(JSON.stringify(body)); + }); + } + PromiseProxy.prototype = Promise.prototype; + PromiseProxy.__proto__ = Promise; + function hasHeader(args, name) { + for (var key in args.headers) { + if (hasOwn.call(args.headers, key) && key.toLowerCase() === name) + return true; + } + return false; + } + return { + request: function(url, args) { + if (typeof url !== "string") { + args = url; + url = url.url; + } else if (args == null) + args = {}; + var promise = makeRequest(url, args); + if (args.background === true) + return promise; + var count = 0; + function complete() { + if (--count === 0 && typeof oncompletion === "function") + oncompletion(); + } + return wrap(promise); + function wrap(promise2) { + var then = promise2.then; + promise2.constructor = PromiseProxy; + promise2.then = function() { + count++; + var next = then.apply(promise2, arguments); + next.then(complete, function(e) { + complete(); + if (count === 0) + throw e; + }); + return wrap(next); + }; + return promise2; + } + } + }; + }; +}); + +// node_modules/mithril/request.js +var require_request2 = __commonJS((exports, module) => { + var mountRedraw = require_mount_redraw2(); + module.exports = require_request()(typeof window !== "undefined" ? window : null, mountRedraw.redraw); +}); + +// node_modules/mithril/querystring/parse.js +var require_parse = __commonJS((exports, module) => { + function decodeURIComponentSave(str) { + try { + return decodeURIComponent(str); + } catch (err) { + return str; + } + } + module.exports = function(string) { + if (string === "" || string == null) + return {}; + if (string.charAt(0) === "?") + string = string.slice(1); + var entries = string.split("&"), counters = {}, data = {}; + for (var i = 0;i < entries.length; i++) { + var entry = entries[i].split("="); + var key = decodeURIComponentSave(entry[0]); + var value = entry.length === 2 ? decodeURIComponentSave(entry[1]) : ""; + if (value === "true") + value = true; + else if (value === "false") + value = false; + var levels = key.split(/\]\[?|\[/); + var cursor = data; + if (key.indexOf("[") > -1) + levels.pop(); + for (var j = 0;j < levels.length; j++) { + var level = levels[j], nextLevel = levels[j + 1]; + var isNumber = nextLevel == "" || !isNaN(parseInt(nextLevel, 10)); + if (level === "") { + var key = levels.slice(0, j).join(); + if (counters[key] == null) { + counters[key] = Array.isArray(cursor) ? cursor.length : 0; + } + level = counters[key]++; + } else if (level === "__proto__") + break; + if (j === levels.length - 1) + cursor[level] = value; + else { + var desc = Object.getOwnPropertyDescriptor(cursor, level); + if (desc != null) + desc = desc.value; + if (desc == null) + cursor[level] = desc = isNumber ? [] : {}; + cursor = desc; + } + } + } + return data; + }; +}); + +// node_modules/mithril/pathname/parse.js +var require_parse2 = __commonJS((exports, module) => { + var parseQueryString = require_parse(); + module.exports = function(url) { + var queryIndex = url.indexOf("?"); + var hashIndex = url.indexOf("#"); + var queryEnd = hashIndex < 0 ? url.length : hashIndex; + var pathEnd = queryIndex < 0 ? queryEnd : queryIndex; + var path = url.slice(0, pathEnd).replace(/\/{2,}/g, "/"); + if (!path) + path = "/"; + else { + if (path[0] !== "/") + path = "/" + path; + } + return { + path, + params: queryIndex < 0 ? {} : parseQueryString(url.slice(queryIndex + 1, queryEnd)) + }; + }; +}); + +// node_modules/mithril/pathname/compileTemplate.js +var require_compileTemplate = __commonJS((exports, module) => { + var parsePathname = require_parse2(); + module.exports = function(template) { + var templateData = parsePathname(template); + var templateKeys = Object.keys(templateData.params); + var keys = []; + var regexp = new RegExp("^" + templateData.path.replace(/:([^\/.-]+)(\.{3}|\.(?!\.)|-)?|[\\^$*+.()|\[\]{}]/g, function(m, key, extra) { + if (key == null) + return "\\" + m; + keys.push({ k: key, r: extra === "..." }); + if (extra === "...") + return "(.*)"; + if (extra === ".") + return "([^/]+)\\."; + return "([^/]+)" + (extra || ""); + }) + "$"); + return function(data) { + for (var i = 0;i < templateKeys.length; i++) { + if (templateData.params[templateKeys[i]] !== data.params[templateKeys[i]]) + return false; + } + if (!keys.length) + return regexp.test(data.path); + var values = regexp.exec(data.path); + if (values == null) + return false; + for (var i = 0;i < keys.length; i++) { + data.params[keys[i].k] = keys[i].r ? values[i + 1] : decodeURIComponent(values[i + 1]); + } + return true; + }; + }; +}); + +// node_modules/mithril/util/censor.js +var require_censor = __commonJS((exports, module) => { + var hasOwn = require_hasOwn(); + var magic = new RegExp("^(?:key|oninit|oncreate|onbeforeupdate|onupdate|onbeforeremove|onremove)$"); + module.exports = function(attrs, extras) { + var result = {}; + if (extras != null) { + for (var key in attrs) { + if (hasOwn.call(attrs, key) && !magic.test(key) && extras.indexOf(key) < 0) { + result[key] = attrs[key]; + } + } + } else { + for (var key in attrs) { + if (hasOwn.call(attrs, key) && !magic.test(key)) { + result[key] = attrs[key]; + } + } + } + return result; + }; +}); + +// node_modules/mithril/api/router.js +var require_router = __commonJS((exports, module) => { + function decodeURIComponentSave(component) { + try { + return decodeURIComponent(component); + } catch (e) { + return component; + } + } + var Vnode = require_vnode(); + var m = require_hyperscript(); + var buildPathname = require_build2(); + var parsePathname = require_parse2(); + var compileTemplate = require_compileTemplate(); + var assign = require_assign(); + var censor = require_censor(); + var sentinel = {}; + module.exports = function($window, mountRedraw) { + var callAsync = $window == null ? null : typeof $window.setImmediate === "function" ? $window.setImmediate : $window.setTimeout; + var p = Promise.resolve(); + var scheduled = false; + var ready = false; + var state = 0; + var compiled, fallbackRoute; + var currentResolver = sentinel, component, attrs, currentPath, lastUpdate; + var RouterRoot = { + onbeforeupdate: function() { + state = state ? 2 : 1; + return !(!state || sentinel === currentResolver); + }, + onremove: function() { + $window.removeEventListener("popstate", fireAsync, false); + $window.removeEventListener("hashchange", resolveRoute, false); + }, + view: function() { + if (!state || sentinel === currentResolver) + return; + var vnode = [Vnode(component, attrs.key, attrs)]; + if (currentResolver) + vnode = currentResolver.render(vnode[0]); + return vnode; + } + }; + var SKIP = route.SKIP = {}; + function resolveRoute() { + scheduled = false; + var prefix = $window.location.hash; + if (route.prefix[0] !== "#") { + prefix = $window.location.search + prefix; + if (route.prefix[0] !== "?") { + prefix = $window.location.pathname + prefix; + if (prefix[0] !== "/") + prefix = "/" + prefix; + } + } + var path = prefix.concat().replace(/(?:%[a-f89][a-f0-9])+/gim, decodeURIComponentSave).slice(route.prefix.length); + var data = parsePathname(path); + assign(data.params, $window.history.state); + function reject(e) { + console.error(e); + setPath(fallbackRoute, null, { replace: true }); + } + loop(0); + function loop(i) { + for (;i < compiled.length; i++) { + if (compiled[i].check(data)) { + var payload = compiled[i].component; + var matchedRoute = compiled[i].route; + var localComp = payload; + var update = lastUpdate = function(comp) { + if (update !== lastUpdate) + return; + if (comp === SKIP) + return loop(i + 1); + component = comp != null && (typeof comp.view === "function" || typeof comp === "function") ? comp : "div"; + attrs = data.params, currentPath = path, lastUpdate = null; + currentResolver = payload.render ? payload : null; + if (state === 2) + mountRedraw.redraw(); + else { + state = 2; + mountRedraw.redraw.sync(); + } + }; + if (payload.view || typeof payload === "function") { + payload = {}; + update(localComp); + } else if (payload.onmatch) { + p.then(function() { + return payload.onmatch(data.params, path, matchedRoute); + }).then(update, path === fallbackRoute ? null : reject); + } else + update("div"); + return; + } + } + if (path === fallbackRoute) { + throw new Error("Could not resolve default route " + fallbackRoute + "."); + } + setPath(fallbackRoute, null, { replace: true }); + } + } + function fireAsync() { + if (!scheduled) { + scheduled = true; + callAsync(resolveRoute); + } + } + function setPath(path, data, options) { + path = buildPathname(path, data); + if (ready) { + fireAsync(); + var state2 = options ? options.state : null; + var title = options ? options.title : null; + if (options && options.replace) + $window.history.replaceState(state2, title, route.prefix + path); + else + $window.history.pushState(state2, title, route.prefix + path); + } else { + $window.location.href = route.prefix + path; + } + } + function route(root, defaultRoute, routes) { + if (!root) + throw new TypeError("DOM element being rendered to does not exist."); + compiled = Object.keys(routes).map(function(route2) { + if (route2[0] !== "/") + throw new SyntaxError("Routes must start with a '/'."); + if (/:([^\/\.-]+)(\.{3})?:/.test(route2)) { + throw new SyntaxError("Route parameter names must be separated with either '/', '.', or '-'."); + } + return { + route: route2, + component: routes[route2], + check: compileTemplate(route2) + }; + }); + fallbackRoute = defaultRoute; + if (defaultRoute != null) { + var defaultData = parsePathname(defaultRoute); + if (!compiled.some(function(i) { + return i.check(defaultData); + })) { + throw new ReferenceError("Default route doesn't match any known routes."); + } + } + if (typeof $window.history.pushState === "function") { + $window.addEventListener("popstate", fireAsync, false); + } else if (route.prefix[0] === "#") { + $window.addEventListener("hashchange", resolveRoute, false); + } + ready = true; + mountRedraw.mount(root, RouterRoot); + resolveRoute(); + } + route.set = function(path, data, options) { + if (lastUpdate != null) { + options = options || {}; + options.replace = true; + } + lastUpdate = null; + setPath(path, data, options); + }; + route.get = function() { + return currentPath; + }; + route.prefix = "#!"; + route.Link = { + view: function(vnode) { + var child = m(vnode.attrs.selector || "a", censor(vnode.attrs, ["options", "params", "selector", "onclick"]), vnode.children); + var options, onclick, href; + if (child.attrs.disabled = Boolean(child.attrs.disabled)) { + child.attrs.href = null; + child.attrs["aria-disabled"] = "true"; + } else { + options = vnode.attrs.options; + onclick = vnode.attrs.onclick; + href = buildPathname(child.attrs.href, vnode.attrs.params); + child.attrs.href = route.prefix + href; + child.attrs.onclick = function(e) { + var result; + if (typeof onclick === "function") { + result = onclick.call(e.currentTarget, e); + } else if (onclick == null || typeof onclick !== "object") { + } else if (typeof onclick.handleEvent === "function") { + onclick.handleEvent(e); + } + if (result !== false && !e.defaultPrevented && (e.button === 0 || e.which === 0 || e.which === 1) && (!e.currentTarget.target || e.currentTarget.target === "_self") && !e.ctrlKey && !e.metaKey && !e.shiftKey && !e.altKey) { + e.preventDefault(); + e.redraw = false; + route.set(href, null, options); + } + }; + } + return child; + } + }; + route.param = function(key) { + return attrs && key != null ? attrs[key] : attrs; + }; + return route; + }; +}); + +// node_modules/mithril/route.js +var require_route = __commonJS((exports, module) => { + var mountRedraw = require_mount_redraw2(); + module.exports = require_router()(typeof window !== "undefined" ? window : null, mountRedraw); +}); + +// node_modules/mithril/index.js +var require_mithril = __commonJS((exports, module) => { + var hyperscript = require_hyperscript2(); + var request = require_request2(); + var mountRedraw = require_mount_redraw2(); + var domFor = require_domFor(); + var m = function m() { + return hyperscript.apply(this, arguments); + }; + m.m = hyperscript; + m.trust = hyperscript.trust; + m.fragment = hyperscript.fragment; + m.Fragment = "["; + m.mount = mountRedraw.mount; + m.route = require_route(); + m.render = require_render2(); + m.redraw = mountRedraw.redraw; + m.request = request.request; + m.parseQueryString = require_parse(); + m.buildQueryString = require_build(); + m.parsePathname = require_parse2(); + m.buildPathname = require_build2(); + m.vnode = require_vnode(); + m.censor = require_censor(); + m.domFor = domFor.domFor; + module.exports = m; +}); + +// frontend/index.js +var import_mithril = __toESM(require_mithril(), 1); + +// frontend/websocket.js +function createWebSocket() { + return new WebSocket("ws://localhost:3000/chat-service"); +} + +// frontend/index.js +function newMessageText(name, message) { + return `${name}: ${message}`; +} +function sendMessage(e) { + e.preventDefault(); + const form = new FormData(e.target); + const message = {}; + for (const [key, value] of form.entries()) { + message[key] = value; + } + socket.send(JSON.stringify(message)); +} +var messages = []; +var socket = createWebSocket(); +socket.onmessage = function(event) { + if (event.type === "message") { + const message = JSON.parse(event.data); + messages.push(message); + import_mithril.default.redraw(); + } +}; +var MessageBox = { + view: function() { + return import_mithril.default(".messages", messages.map(function(message) { + return import_mithril.default(".message", newMessageText(message.chatter, message.message)); + })); + } +}; +var MessageInput = { + view: function() { + return import_mithril.default("div", [ + import_mithril.default("form", { method: "post", onsubmit: sendMessage }, [ + import_mithril.default("input", { name: "chatter", placeholder: "Name" }), + import_mithril.default("input", { name: "message", placeholder: "Message" }), + import_mithril.default("input", { type: "submit", value: "Send" }) + ]) + ]); + } +}; +var Chat = { + view: function(vnode) { + return import_mithril.default("div", [ + import_mithril.default(MessageBox), + import_mithril.default(MessageInput) + ]); + } +}; +import_mithril.default.mount(document.getElementById("app"), Chat);