From a0d37e3566742219ea2607f3d62ecfbdf4add625 Mon Sep 17 00:00:00 2001 From: Jonatan Nilsson Date: Wed, 11 May 2022 11:09:31 +0000 Subject: [PATCH] Dev --- .npmrc | 1 + dev.mjs | 8 + index.mjs | 6 + package.json | 34 + public/assets/Inter.var.woff2 | Bin 0 -> 84724 bytes public/assets/app.css | 154 +++ public/assets/app.js | 91 ++ public/assets/favicon.ico | Bin 0 -> 22382 bytes public/assets/mithril.js | 1842 +++++++++++++++++++++++++++++++++ public/index.html | 21 + server/serve.mjs | 34 + server/server.mjs | 94 ++ 12 files changed, 2285 insertions(+) create mode 100644 .npmrc create mode 100644 dev.mjs create mode 100644 index.mjs create mode 100644 package.json create mode 100644 public/assets/Inter.var.woff2 create mode 100644 public/assets/app.css create mode 100644 public/assets/app.js create mode 100644 public/assets/favicon.ico create mode 100644 public/assets/mithril.js create mode 100644 public/index.html create mode 100644 server/serve.mjs create mode 100644 server/server.mjs diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..9cf9495 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +package-lock=false \ No newline at end of file diff --git a/dev.mjs b/dev.mjs new file mode 100644 index 0000000..648f8c1 --- /dev/null +++ b/dev.mjs @@ -0,0 +1,8 @@ +import { ServiceCore } from 'service-core' +import * as index from './index.mjs' + +var core = new ServiceCore('ffmpeg_service', import.meta.url, 4040, '') + +core.init(index).then(function() { + return core.run() +}) \ No newline at end of file diff --git a/index.mjs b/index.mjs new file mode 100644 index 0000000..f5328d6 --- /dev/null +++ b/index.mjs @@ -0,0 +1,6 @@ +export function start(http, port, ctx) { + return import('./server/server.mjs') + .then(function(server) { + return server.run(http, port, ctx) + }) +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..82fd424 --- /dev/null +++ b/package.json @@ -0,0 +1,34 @@ +{ + "name": "ffmpeg-service", + "version": "1.0.0", + "description": "FFmpeg streaming service", + "main": "index.js", + "scripts": { + "dev:server": "node dev.mjs | bunyan", + "dev:server:watch": "npm-watch dev:server", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "watch": { + "dev:server": { + "patterns": [ + "server/*" + ], + "extensions": "mjs", + "quiet": true, + "inherit": true + } + }, + "repository": { + "type": "git", + "url": "https://git.nfp.is/TheThing/ffmpeg-service.git" + }, + "author": "Jonatan Nilsson", + "license": "WTFPL", + "devDependencies": { + "service-core": "^3.0.0-beta.17" + }, + "dependencies": { + "flaska": "^1.2.3", + "ws": "^8.6.0" + } +} diff --git a/public/assets/Inter.var.woff2 b/public/assets/Inter.var.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..be21b422bf1cbfc97cfc966f28051abac7887df0 GIT binary patch literal 84724 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 slS`~+fs%^PhstIbHk?#KRjv~Kn;0O{1hW&i*H literal 0 HcmV?d00001 diff --git a/public/assets/app.css b/public/assets/app.css new file mode 100644 index 0000000..5f75ad0 --- /dev/null +++ b/public/assets/app.css @@ -0,0 +1,154 @@ +:root { + --bg: #282828; + --foreground: white; + --header-bg: linear-gradient(#303030, #181818); + --header-fg: #555; + --header-title-fg: white; + --nav-above-border-color: #000000; + --nav-bg: linear-gradient(#262626, #181818); + --nav-fg: #666; + --nav-active-fg: #eb6e00; + --nav-top-border-color: #303030; + font-size: 12px; +} + +@font-face { + font-family: 'Inter'; + font-weight: 100 900; + font-display: swap; + font-style: oblique 0deg 10deg; + src: url("Inter.var.woff2?v=3.19") format("woff2"); +} + +html { + box-sizing: border-box; +} + +*, *:before, *:after { + box-sizing: inherit; +} + +body { + font-family: Inter, sans-serif; + font-feature-settings: "zero", "calt", "ccmp", "kern"; + background: var(--bg); + color: var(--foreground); + font-size: 12px; +} + +body, h1, h2, h3, h4, h5, h6, p, ol, ul { + margin: 0; + padding: 0; + font-weight: 300; +} + +.filler { + flex: 2 1 auto; +} + +.container { + min-height: 100vh; + display: flex; + flex-direction: column; +} + +[hidden] { display: none !important; } + +header { + border-bottom: 1px solid black; + background: var(--header-bg); + display: flex; + align-items: center; + color: var(--header-fg); +} + +header h1 { + padding: 1.5rem; + font-weight: 400; + align-self: stretch; + color: white; +} + +header span { + font-size: 1.2rem; +} + +header .status { + background-color: HSL(0, 0%, 80%); + border-radius: 100px; + box-shadow: rgba(255, 255, 255, .25) 0px 15px 11px -9px inset, rgba(0, 0, 0, .25) 0px -12px 11px -9px inset, rgba(0, 0, 0, .65) 0 4px 20px; + padding: 0.6rem 2rem; + text-align: center; + text-decoration: none; + border: 0; + font-size: 1rem; + user-select: none; + margin: 0 1rem 0 1rem; +} + +header .status.red { + background-color: #ff3232; + box-shadow: rgba(255, 255, 255, .25) 0px 15px 11px -9px inset, rgba(0, 0, 0, .25) 0px -12px 11px -9px inset, rgba(0, 0, 0, .65) 0 4px 20px, rgba(255, 50, 50, .5) 0 0 15px; + color: black; +} + +header .status.green { + background-color: #10ef07; + box-shadow: rgba(255, 255, 255, .65) 0px 15px 11px -9px inset, rgba(0, 100, 0, .4) 0px -12px 11px -9px inset, rgba(0, 0, 0, .65) 0 4px 20px, rgba(0, 255, 0, .5) 0 0 15px; + color: black; +} + +main { + flex: 2 1 auto; +} + +nav { + display: flex; + justify-content: center; + background: var(--nav-bg); + border-top: 1px solid var(--nav-top-border-color); + position: relative; + padding-bottom: 0.25rem; +} + +nav a, +nav a:visited { + width: 100px; + padding: 0.5rem 0.5rem 0.5rem; + color: var(--nav-fg); + text-align: center; + display: flex; + flex-direction: column; + align-items: center; + cursor: pointer; + font-size: 1.1rem; + height: 5rem; + justify-content: center; + border-radius: 3px; +} + +nav a svg { + height: 2rem; + margin-bottom: 0.5rem; + fill: var(--nav-fg); +} + +nav a.active { + color: var(--nav-active-fg); + background: #181818; + border: 1px solid #141414; + box-shadow: rgba(0, 0, 0, .35) 0px 5px 16px -6px inset; +} +nav a.active svg { + fill: var(--nav-active-fg); +} + +nav::after { + content: ''; + position: absolute; + top: -2px; + left: 0; + right: 0; + width: 100%; + border-top: 1px solid var(--nav-above-border-color); +} diff --git a/public/assets/app.js b/public/assets/app.js new file mode 100644 index 0000000..1022927 --- /dev/null +++ b/public/assets/app.js @@ -0,0 +1,91 @@ +class WSClient +{ + constructor() { + // Create WebSocket connection. + const socket = new WebSocket('ws://localhost:4040') + + // Connection opened + socket.addEventListener('open', function (event) { + console.log('Sending hello server') + socket.send(JSON.stringify({ msg: 'Hello Server!' })) + }) + + // Listen for messages + socket.addEventListener('message', function (event) { + console.log('Message from server', event.data) + }) + } +} + +const client = new WSClient() + +class Header { + view(vnode) { + return [ + m('h1', 'Fíladelfíu streymi'), + m('div.status.green', { hidden: true }, 'No errors'), + m('div.filler'), + m('span', 'Status:'), + m('div.status', 'Not live'), + ] + } +} + +class Nav { + view(vnode) { + let path = m.route.get() + return [ + m(m.route.Link, { + class: path === '/' ? 'active' : '', + href: '/', + }, [ + m('svg', { + viewBox: '0 0 512 512' + }, m('path', { + d: 'M32 400C32 426.5 53.49 448 80 448H496C504.8 448 512 455.2 512 464C512 472.8 504.8 480 496 480H80C35.82 480 0 444.2 0 400V48C0 39.16 7.164 32 16 32C24.84 32 32 39.16 32 48V400zM331.3 299.3C325.1 305.6 314.9 305.6 308.7 299.3L223.1 214.6L123.3 315.3C117.1 321.6 106.9 321.6 100.7 315.3C94.44 309.1 94.44 298.9 100.7 292.7L212.7 180.7C218.9 174.4 229.1 174.4 235.3 180.7L320 265.4L452.7 132.7C458.9 126.4 469.1 126.4 475.3 132.7C481.6 138.9 481.6 149.1 475.3 155.3L331.3 299.3z' + })), + m('span', 'Status') + ]), + m(m.route.Link, { + class: path === '/settings' ? 'active' : '', + href: '/settings', + }, [ + m('svg', { + viewBox: '0 0 512 512' + }, m('path', { + d: 'M0 416C0 407.2 7.164 400 16 400H81.6C89.01 363.5 121.3 336 160 336C198.7 336 230.1 363.5 238.4 400H496C504.8 400 512 407.2 512 416C512 424.8 504.8 432 496 432H238.4C230.1 468.5 198.7 496 160 496C121.3 496 89.01 468.5 81.6 432H16C7.164 432 0 424.8 0 416V416zM208 416C208 389.5 186.5 368 160 368C133.5 368 112 389.5 112 416C112 442.5 133.5 464 160 464C186.5 464 208 442.5 208 416zM352 176C390.7 176 422.1 203.5 430.4 240H496C504.8 240 512 247.2 512 256C512 264.8 504.8 272 496 272H430.4C422.1 308.5 390.7 336 352 336C313.3 336 281 308.5 273.6 272H16C7.164 272 0 264.8 0 256C0 247.2 7.164 240 16 240H273.6C281 203.5 313.3 176 352 176zM400 256C400 229.5 378.5 208 352 208C325.5 208 304 229.5 304 256C304 282.5 325.5 304 352 304C378.5 304 400 282.5 400 256zM496 80C504.8 80 512 87.16 512 96C512 104.8 504.8 112 496 112H270.4C262.1 148.5 230.7 176 192 176C153.3 176 121 148.5 113.6 112H16C7.164 112 0 104.8 0 96C0 87.16 7.164 80 16 80H113.6C121 43.48 153.3 16 192 16C230.7 16 262.1 43.48 270.4 80H496zM144 96C144 122.5 165.5 144 192 144C218.5 144 240 122.5 240 96C240 69.49 218.5 48 192 48C165.5 48 144 69.49 144 96z' + })), + m('span', 'Settings') + ]), + ] + } +} + +class Status { + view(vnode) { + return [ + m('span', 'Hello world') + ] + } +} + +class Settings { + view(vnode) { + return [ + m('span', 'Settings') + ] + } +} + +const rootHeader = document.getElementById('header') +const rootNav = document.getElementById('nav') +const rootMain = document.getElementById('main') + +const allRoutes = { + '/': Status, + '/settings': Settings, +} + +m.mount(rootHeader, Header) +m.mount(rootNav, Nav) +m.route(rootMain, '/', allRoutes) diff --git a/public/assets/favicon.ico b/public/assets/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..7823b8b6ac6e20cae31b6795602b6a06ca00d780 GIT binary patch literal 22382 zcmeHPX>b%p6dp?a?;rp3lPav@6cH3x!9YQwKnRx*As0lHLnYi01dEbW5kUom;sFQ< zC>jq6h#XoFZU{%X_PUJ-Fd!mW_!~+nd~kxNwL}4s#o39)7`JXuY0=R^t@h0 zX`wu(Jn@9WVJl^9OGW9dC`zkV#`OkHFXA+Z!?E58vVvMmh)0@;9&@ zZ5_~#?$t}$)0WXaXn#g$I?v^)9-`&+A{x_|X!Ba%_3u`BZx!aNG-*hX26Zy(o01uf z)cf8zmDVis(VB&RO7CFYNBQyT8f{tYqqhcXl>Z8y+FazlKBBv(*KxVrP1$K7y$(*N z5zlKhyqgxOQX;IQ7X~f2e4&PtU*YYcI{rQ6u#MZ(ZflrtRN& zxjov2oj+Drq_-#aVaiV!q|whCy?XsC=LcxP+ri2|wpCvr*NHSz`8&1JT++^dh|_M)xH{!kGq zijYLHi2MjK&$ME3uwZ_&e>MX)1GO1QZ%b`>zA9mzz%e~iS-J+q43(^&+1A0cCACAttj-@(tlv*gQzr7P7X!=2nBvQO^$<` z=~&7*gpP$`-(T2w6f7gy`{oO$Dm)o1@>LoWVou*d5{vwrpJSX66tp3mI zL;7x!==MVEw*>O*VQ-Mu{xQ9HGUklOcqaFfg zaStKJ#;GGl`NdOJeIC>2vfD0|!%)2WN3N?8;-S$!>EPhbbXts2gpHslZk?FRhIFAL zVf|taj9adu+nW6{9pO45O|Cnvvot%6p5nP|FJD)&)a!k(9_66xPkv8qQ`;i6Q)~uO zhXJbN*U${qp@y(e;b19bvG*yM1p8+*U^8GdU^8GdU^8GdU^8GdU^AdIkdZ-;@t#TU zfVQ-g{R_%Dez7@fgyc=Fg-o ze#SoZS=!I|Bj0{fj0=_bE7?DY%JJ=q=La>#@JH=W^M3KQ5zkY}!1h#~DmIdfrwUFO4;2_8D={u@^FXVRy|!LM_{&xHry9@9HgMROYqvG7mK(CFML zcjSAc{LNAJf8g)Kv~pgwpTO3yy!y9HvtNZ3fB3$A^00?S_o(g1V3mK>d_O^_+y<*S zzNN;~9}7Q!`zDvmnUMVcLv_@%aPzuLFE=Y!jY>D;dF?sF*8+OhEwA~`Z_X!Q!~Y|Z zb~&9+_OY$;U6AV#@kH>m&kQY`tk#l6lT~fm%)r_A#|JK({a-2se)g9Ux~#&*Qvwkz z@cm_JnPFY&KqB~uJVWru((!|1;Qw`V^mAl|NA&-rh+psk zA2Z-#f3cty2Bg719(myZw0yQ-kAsh4>~~aG8Kfrze$k&xr>m7=o6>8>v}1j>j=7}x z0kQuGi8Ap2GUt`AlLi%rCFShDC(hf*bz8;F?Ut__ z6aOXlp+wuiUEx>Zqdcf1T|xVbmiYAaShhf1{AV6*{IhaHk$z)8U@zRT_WS0dnlo%N z4=d*xHZl7Fheq|#j+vj^WXAu_FpV(3qkqMDHyJzPIRA!sP2^eqK|r>x^?k}@#{cDq zhMkAC-|Xir^YGgi!xn)5yOe$o`;?4d12@Sy{!8~8?>oPQ&q_+zWSlgAe2pz2eB(vl z6Qv%XF*f}Fd%srHX%#l~`iJwbI9GwTFMKy*7e?Wh+i`){G34F!Mf5y@F@f}*+r6G8 zQv)aX9m8k!{`=;D*zf+BS7d$WxWJFS74yA#*C>NF%J$ESxyHPPxe>nh@#@&{sj*I6 zI2*lA2>hr&mcX`*$3d(%OdM?Ne-&>s&h^I1YrcmvSl_URChlR~hh4Ltaf!58;iMtf z`qwS@>w90ABTb^+;WGiuuLk~4A9WMX@|f2T`8t;VB)%v1Yj4U}t>Q_HF_AH(pZ^>5 zG+na7jVQf0a zG|B^JV7GrZ12zLT12zLT12zLT12zLT12zMx!$4jCAJ9 0) attrs.className = classes.join(" ") + return selectorCache[selector] = {tag: tag, attrs: attrs} +} +function execSelector(state, vnode) { + var attrs = vnode.attrs + var children = Vnode.normalizeChildren(vnode.children) + var hasClass = hasOwn.call(attrs, "class") + var className = hasClass ? attrs.class : attrs.className + vnode.tag = state.tag + vnode.attrs = null + vnode.children = undefined + if (!isEmpty(state.attrs) && !isEmpty(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 + for (var key in attrs) { + if (hasOwn.call(attrs, key) && key !== "key") { + vnode.attrs = attrs + break + } + } + if (Array.isArray(children) && children.length === 1 && children[0] != null && children[0].tag === "#") { + vnode.text = children[0].children + } else { + vnode.children = children + } + 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 +} +hyperscript.trust = function(html) { + if (html == null) html = "" + return Vnode("<", undefined, undefined, html, undefined, undefined) +} +hyperscript.fragment = function() { + var vnode2 = hyperscriptVnode.apply(0, arguments) + vnode2.tag = "[" + vnode2.children = Vnode.normalizeChildren(vnode2.children) + return vnode2 +} +/** @constructor */ +var PromisePolyfill = function(executor) { + if (!(this instanceof PromisePolyfill)) throw new Error("Promise must be called with `new`") + if (typeof executor !== "function") throw new TypeError("executor must be a function") + var self = this, resolvers = [], rejectors = [], resolveCurrent = handler(resolvers, true), rejectCurrent = handler(rejectors, false) + var instance = self._instance = {resolvers: resolvers, rejectors: rejectors} + var callAsync = typeof setImmediate === "function" ? setImmediate : setTimeout + function handler(list, shouldAbsorb) { + return function execute(value) { + var then + try { + if (shouldAbsorb && value != null && (typeof value === "object" || typeof value === "function") && typeof (then = value.then) === "function") { + if (value === self) throw new TypeError("Promise can't be resolved w/ itself") + executeOnce(then.bind(value)) + } + else { + callAsync(function() { + if (!shouldAbsorb && list.length === 0) console.error("Possible unhandled promise rejection:", value) + for (var i = 0; i < list.length; i++) list[i](value) + resolvers.length = 0, rejectors.length = 0 + instance.state = shouldAbsorb + instance.retry = function() {execute(value)} + }) + } + } + catch (e) { + rejectCurrent(e) + } + } + } + function executeOnce(then) { + var runs = 0 + function run(fn) { + return function(value) { + if (runs++ > 0) return + fn(value) + } + } + var onerror = run(rejectCurrent) + try {then(run(resolveCurrent), onerror)} catch (e) {onerror(e)} + } + executeOnce(executor) +} +PromisePolyfill.prototype.then = function(onFulfilled, onRejection) { + var self = this, instance = self._instance + function handle(callback, list, next, state) { + list.push(function(value) { + if (typeof callback !== "function") next(value) + else try {resolveNext(callback(value))} catch (e) {if (rejectNext) rejectNext(e)} + }) + if (typeof instance.retry === "function" && state === instance.state) instance.retry() + } + var resolveNext, rejectNext + var promise = new PromisePolyfill(function(resolve, reject) {resolveNext = resolve, rejectNext = reject}) + handle(onFulfilled, instance.resolvers, resolveNext, true), handle(onRejection, instance.rejectors, rejectNext, false) + return promise +} +PromisePolyfill.prototype.catch = function(onRejection) { + return this.then(null, onRejection) +} +PromisePolyfill.prototype.finally = function(callback) { + return this.then( + function(value) { + return PromisePolyfill.resolve(callback()).then(function() { + return value + }) + }, + function(reason) { + return PromisePolyfill.resolve(callback()).then(function() { + return PromisePolyfill.reject(reason); + }) + } + ) +} +PromisePolyfill.resolve = function(value) { + if (value instanceof PromisePolyfill) return value + return new PromisePolyfill(function(resolve) {resolve(value)}) +} +PromisePolyfill.reject = function(value) { + return new PromisePolyfill(function(resolve, reject) {reject(value)}) +} +PromisePolyfill.all = function(list) { + return new PromisePolyfill(function(resolve, reject) { + var total = list.length, count = 0, values = [] + if (list.length === 0) resolve([]) + else for (var i = 0; i < list.length; i++) { + (function(i) { + function consume(value) { + count++ + values[i] = value + if (count === total) resolve(values) + } + if (list[i] != null && (typeof list[i] === "object" || typeof list[i] === "function") && typeof list[i].then === "function") { + list[i].then(consume, reject) + } + else consume(list[i]) + })(i) + } + }) +} +PromisePolyfill.race = function(list) { + return new PromisePolyfill(function(resolve, reject) { + for (var i = 0; i < list.length; i++) { + list[i].then(resolve, reject) + } + }) +} +if (typeof window !== "undefined") { + if (typeof window.Promise === "undefined") { + window.Promise = PromisePolyfill + } else if (!window.Promise.prototype.finally) { + window.Promise.prototype.finally = PromisePolyfill.prototype.finally + } + var PromisePolyfill = window.Promise +} else if (typeof global !== "undefined") { + if (typeof global.Promise === "undefined") { + global.Promise = PromisePolyfill + } else if (!global.Promise.prototype.finally) { + global.Promise.prototype.finally = PromisePolyfill.prototype.finally + } + var PromisePolyfill = global.Promise +} else { +} +var _12 = function($window) { + var $doc = $window && $window.document + var currentRedraw + var nameSpace = { + svg: "http://www.w3.org/2000/svg", + math: "http://www.w3.org/1998/Math/MathML" + } + function getNameSpace(vnode3) { + return vnode3.attrs && vnode3.attrs.xmlns || nameSpace[vnode3.tag] + } + //sanity check to discourage people from doing `vnode3.state = ...` + function checkState(vnode3, original) { + if (vnode3.state !== original) throw new Error("`vnode.state` must not be modified") + } + //Note: the hook is passed as the `this` argument to allow proxying the + //arguments without requiring a full array allocation to do so. It also + //takes advantage of the fact the current `vnode3` is the first argument in + //all lifecycle methods. + function callHook(vnode3) { + var original = vnode3.state + try { + return this.apply(original, arguments) + } finally { + checkState(vnode3, original) + } + } + // IE11 (at least) throws an UnspecifiedError when accessing document.activeElement when + // inside an iframe. Catch and swallow this error, and heavy-handidly return null. + function activeElement() { + try { + return $doc.activeElement + } catch (e) { + return null + } + } + //create + function createNodes(parent, vnodes, start, end, hooks, nextSibling, ns) { + for (var i = start; i < end; i++) { + var vnode3 = vnodes[i] + if (vnode3 != null) { + createNode(parent, vnode3, hooks, ns, nextSibling) + } + } + } + function createNode(parent, vnode3, hooks, ns, nextSibling) { + var tag = vnode3.tag + if (typeof tag === "string") { + vnode3.state = {} + if (vnode3.attrs != null) initLifecycle(vnode3.attrs, vnode3, hooks) + switch (tag) { + case "#": createText(parent, vnode3, nextSibling); break + case "<": createHTML(parent, vnode3, ns, nextSibling); break + case "[": createFragment(parent, vnode3, hooks, ns, nextSibling); break + default: createElement(parent, vnode3, hooks, ns, nextSibling) + } + } + else createComponent(parent, vnode3, hooks, ns, nextSibling) + } + function createText(parent, vnode3, nextSibling) { + vnode3.dom = $doc.createTextNode(vnode3.children) + insertNode(parent, vnode3.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, vnode3, ns, nextSibling) { + var match0 = vnode3.children.match(/^\s*?<(\w+)/im) || [] + // not using the proper parent makes the child element(s) vanish. + // var div = document.createElement("div") + // div.innerHTML = "ij" + // console.log(div.innerHTML) + // --> "ij", no in sight. + var temp = $doc.createElement(possibleParents[match0[1]] || "div") + if (ns === "http://www.w3.org/2000/svg") { + temp.innerHTML = "" + vnode3.children + "" + temp = temp.firstChild + } else { + temp.innerHTML = vnode3.children + } + vnode3.dom = temp.firstChild + vnode3.domSize = temp.childNodes.length + // Capture nodes to remove, so we don't confuse them. + vnode3.instance = [] + var fragment = $doc.createDocumentFragment() + var child + while (child = temp.firstChild) { + vnode3.instance.push(child) + fragment.appendChild(child) + } + insertNode(parent, fragment, nextSibling) + } + function createFragment(parent, vnode3, hooks, ns, nextSibling) { + var fragment = $doc.createDocumentFragment() + if (vnode3.children != null) { + var children3 = vnode3.children + createNodes(fragment, children3, 0, children3.length, hooks, null, ns) + } + vnode3.dom = fragment.firstChild + vnode3.domSize = fragment.childNodes.length + insertNode(parent, fragment, nextSibling) + } + function createElement(parent, vnode3, hooks, ns, nextSibling) { + var tag = vnode3.tag + var attrs2 = vnode3.attrs + var is = attrs2 && attrs2.is + ns = getNameSpace(vnode3) || ns + var element = ns ? + is ? $doc.createElementNS(ns, tag, {is: is}) : $doc.createElementNS(ns, tag) : + is ? $doc.createElement(tag, {is: is}) : $doc.createElement(tag) + vnode3.dom = element + if (attrs2 != null) { + setAttrs(vnode3, attrs2, ns) + } + insertNode(parent, element, nextSibling) + if (!maybeSetContentEditable(vnode3)) { + if (vnode3.text != null) { + if (vnode3.text !== "") element.textContent = vnode3.text + else vnode3.children = [Vnode("#", undefined, undefined, vnode3.text, undefined, undefined)] + } + if (vnode3.children != null) { + var children3 = vnode3.children + createNodes(element, children3, 0, children3.length, hooks, null, ns) + if (vnode3.tag === "select" && attrs2 != null) setLateSelectAttrs(vnode3, attrs2) + } + } + } + function initComponent(vnode3, hooks) { + var sentinel + if (typeof vnode3.tag.view === "function") { + vnode3.state = Object.create(vnode3.tag) + sentinel = vnode3.state.view + if (sentinel.$$reentrantLock$$ != null) return + sentinel.$$reentrantLock$$ = true + } else { + vnode3.state = void 0 + sentinel = vnode3.tag + if (sentinel.$$reentrantLock$$ != null) return + sentinel.$$reentrantLock$$ = true + vnode3.state = (vnode3.tag.prototype != null && typeof vnode3.tag.prototype.view === "function") ? new vnode3.tag(vnode3) : vnode3.tag(vnode3) + } + initLifecycle(vnode3.state, vnode3, hooks) + if (vnode3.attrs != null) initLifecycle(vnode3.attrs, vnode3, hooks) + vnode3.instance = Vnode.normalize(callHook.call(vnode3.state.view, vnode3)) + if (vnode3.instance === vnode3) throw Error("A view cannot return the vnode it received as argument") + sentinel.$$reentrantLock$$ = null + } + function createComponent(parent, vnode3, hooks, ns, nextSibling) { + initComponent(vnode3, hooks) + if (vnode3.instance != null) { + createNode(parent, vnode3.instance, hooks, ns, nextSibling) + vnode3.dom = vnode3.instance.dom + vnode3.domSize = vnode3.dom != null ? vnode3.instance.domSize : 0 + } + else { + vnode3.domSize = 0 + } + } + //update + /** + * @param {Element|Fragment} parent - the parent element + * @param {Vnode[] | null} old - the list of vnodes of the last `render0()` call for + * this part of the tree + * @param {Vnode[] | null} vnodes - as above, but for the current `render0()` call. + * @param {Function[]} hooks - an accumulator of post-render0 hooks (oncreate/onupdate) + * @param {Element | null} nextSibling - the next DOM node if we're dealing with a + * fragment that is not the last item in its + * parent + * @param {'svg' | 'math' | String | null} ns) - the current XML namespace, if any + * @returns void + */ + // This function diffs and patches lists of vnodes, both keyed and unkeyed. + // + // We will: + // + // 1. describe its general structure + // 2. focus on the diff algorithm optimizations + // 3. discuss DOM node operations. + // ## Overview: + // + // The updateNodes() function: + // - deals with trivial cases + // - determines whether the lists are keyed or unkeyed based on the first non-null node + // of each list. + // - diffs them and patches the DOM if needed (that's the brunt of the code) + // - manages the leftovers: after diffing, are there: + // - old nodes left to remove? + // - new nodes to insert? + // deal with them! + // + // The lists are only iterated over once, with an exception for the nodes in `old` that + // are visited in the fourth part of the diff and in the `removeNodes` loop. + // ## Diffing + // + // Reading https://github.com/localvoid/ivi/blob/ddc09d06abaef45248e6133f7040d00d3c6be853/packages/ivi/src/vdom/implementation.ts#L617-L837 + // may be good for context on longest increasing subsequence-based logic for moving nodes. + // + // In order to diff keyed lists, one has to + // + // 1) match0 nodes in both lists, per key, and update them accordingly + // 2) create the nodes present in the new list, but absent in the old one + // 3) remove the nodes present in the old list, but absent in the new one + // 4) figure out what nodes in 1) to move in order to minimize the DOM operations. + // + // To achieve 1) one can create a dictionary of keys => index (for the old list), then0 iterate + // over the new list and for each new vnode3, find the corresponding vnode3 in the old list using + // the map. + // 2) is achieved in the same step: if a new node has no corresponding entry in the map, it is new + // and must be created. + // For the removals, we actually remove the nodes that have been updated from the old list. + // The nodes that remain in that list after 1) and 2) have been performed can be safely removed. + // The fourth step is a bit more complex and relies on the longest increasing subsequence (LIS) + // algorithm. + // + // the longest increasing subsequence is the list of nodes that can remain in place. Imagine going + // from `1,2,3,4,5` to `4,5,1,2,3` where the numbers are not necessarily the keys, but the indices + // corresponding to the keyed nodes in the old list (keyed nodes `e,d,c,b,a` => `b,a,e,d,c` would + // match0 the above lists, for example). + // + // In there are two increasing subsequences: `4,5` and `1,2,3`, the latter being the longest. We + // can update those nodes without moving them, and only call `insertNode` on `4` and `5`. + // + // @localvoid adapted the algo to also support node deletions and insertions (the `lis` is actually + // the longest increasing subsequence *of old nodes still present in the new list*). + // + // It is a general algorithm that is fireproof in all circumstances, but it requires the allocation + // and the construction of a `key => oldIndex` map, and three arrays (one with `newIndex => oldIndex`, + // the `LIS` and a temporary one to create the LIS). + // + // So we cheat where we can: if the tails of the lists are identical, they are guaranteed to be part of + // the LIS and can be updated without moving them. + // + // If two nodes are swapped, they are guaranteed not to be part of the LIS, and must be moved (with + // the exception of the last node if the list is fully reversed). + // + // ## Finding the next sibling. + // + // `updateNode()` and `createNode()` expect a nextSibling parameter to perform DOM operations. + // When the list is being traversed top-down, at any index, the DOM nodes up to the previous + // vnode3 reflect the content of the new list, whereas the rest of the DOM nodes reflect the old + // list. The next sibling must be looked for in the old list using `getNextSibling(... oldStart + 1 ...)`. + // + // In the other scenarios (swaps, upwards traversal, map-based diff), + // the new vnodes list is traversed upwards. The DOM nodes at the bottom of the list reflect the + // bottom part of the new vnodes list, and we can use the `v.dom` value of the previous node + // as the next sibling (cached in the `nextSibling` variable). + // ## DOM node moves + // + // In most scenarios `updateNode()` and `createNode()` perform the DOM operations. However, + // this is not the case if the node moved (second and fourth part of the diff algo). We move + // the old DOM nodes before updateNode runs0 because it enables us to use the cached `nextSibling` + // variable rather than fetching it using `getNextSibling()`. + // + // The fourth part of the diff currently inserts nodes unconditionally, leading to issues + // like #1791 and #1999. We need to be smarter about those situations where adjascent old + // nodes remain together in the new list in a way that isn't covered by parts one and + // three of the diff algo. + 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 isKeyed0 = vnodes[0] != null && vnodes[0].key != null + var start = 0, oldStart = 0 + if (!isOldKeyed) while (oldStart < old.length && old[oldStart] == null) oldStart++ + if (!isKeyed0) while (start < vnodes.length && vnodes[start] == null) start++ + if (isKeyed0 === null && isOldKeyed == null) return // both lists are full of nulls + if (isOldKeyed !== isKeyed0) { + removeNodes(parent, old, oldStart, old.length) + createNodes(parent, vnodes, start, vnodes.length, hooks, nextSibling, ns) + } else if (!isKeyed0) { + // Don't index past the end of either list (causes deopts). + var commonLength = old.length < vnodes.length ? old.length : vnodes.length + // Rewind if necessary to the first non-null index on either side. + // We could alternatively either explicitly create or remove nodes when `start !== oldStart` + // but that would be optimizing for sparse lists which are more rare than dense ones. + 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 { + // keyed diff + var oldEnd = old.length - 1, end = vnodes.length - 1, map, o, v, oe, ve, topSibling + // bottom-up + 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-- + } + // top-down + 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) + } + // swaps and list reversals + while (oldEnd >= oldStart && end >= start) { + if (start === end) break + if (o.key !== ve.key || oe.key !== v.key) break + topSibling = getNextSibling(old, oldStart, nextSibling) + moveNodes(parent, oe, topSibling) + if (oe !== v) updateNode(parent, oe, v, hooks, topSibling, ns) + if (++start <= --end) moveNodes(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] + } + // bottom up once again + 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 { + // inspired by ivi https://github.com/ivijs/ivi/ by Boris Kaul + 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 // becomes -1 if nodes were re-ordered + 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) { + // the indices of the indices of the items that are part of the + // longest increasing subsequence in the oldIndices list + 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 moveNodes(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, vnode3, hooks, nextSibling, ns) { + var oldTag = old.tag, tag = vnode3.tag + if (oldTag === tag) { + vnode3.state = old.state + vnode3.events = old.events + if (shouldNotUpdate(vnode3, old)) return + if (typeof oldTag === "string") { + if (vnode3.attrs != null) { + updateLifecycle(vnode3.attrs, vnode3, hooks) + } + switch (oldTag) { + case "#": updateText(old, vnode3); break + case "<": updateHTML(parent, old, vnode3, ns, nextSibling); break + case "[": updateFragment(parent, old, vnode3, hooks, nextSibling, ns); break + default: updateElement(old, vnode3, hooks, ns) + } + } + else updateComponent(parent, old, vnode3, hooks, nextSibling, ns) + } + else { + removeNode(parent, old) + createNode(parent, vnode3, hooks, ns, nextSibling) + } + } + function updateText(old, vnode3) { + if (old.children.toString() !== vnode3.children.toString()) { + old.dom.nodeValue = vnode3.children + } + vnode3.dom = old.dom + } + function updateHTML(parent, old, vnode3, ns, nextSibling) { + if (old.children !== vnode3.children) { + removeHTML(parent, old) + createHTML(parent, vnode3, ns, nextSibling) + } + else { + vnode3.dom = old.dom + vnode3.domSize = old.domSize + vnode3.instance = old.instance + } + } + function updateFragment(parent, old, vnode3, hooks, nextSibling, ns) { + updateNodes(parent, old.children, vnode3.children, hooks, nextSibling, ns) + var domSize = 0, children3 = vnode3.children + vnode3.dom = null + if (children3 != null) { + for (var i = 0; i < children3.length; i++) { + var child = children3[i] + if (child != null && child.dom != null) { + if (vnode3.dom == null) vnode3.dom = child.dom + domSize += child.domSize || 1 + } + } + if (domSize !== 1) vnode3.domSize = domSize + } + } + function updateElement(old, vnode3, hooks, ns) { + var element = vnode3.dom = old.dom + ns = getNameSpace(vnode3) || ns + if (vnode3.tag === "textarea") { + if (vnode3.attrs == null) vnode3.attrs = {} + if (vnode3.text != null) { + vnode3.attrs.value = vnode3.text //FIXME handle0 multiple children3 + vnode3.text = undefined + } + } + updateAttrs(vnode3, old.attrs, vnode3.attrs, ns) + if (!maybeSetContentEditable(vnode3)) { + if (old.text != null && vnode3.text != null && vnode3.text !== "") { + if (old.text.toString() !== vnode3.text.toString()) old.dom.firstChild.nodeValue = vnode3.text + } + else { + if (old.text != null) old.children = [Vnode("#", undefined, undefined, old.text, undefined, old.dom.firstChild)] + if (vnode3.text != null) vnode3.children = [Vnode("#", undefined, undefined, vnode3.text, undefined, undefined)] + updateNodes(element, old.children, vnode3.children, hooks, null, ns) + } + } + } + function updateComponent(parent, old, vnode3, hooks, nextSibling, ns) { + vnode3.instance = Vnode.normalize(callHook.call(vnode3.state.view, vnode3)) + if (vnode3.instance === vnode3) throw Error("A view cannot return the vnode it received as argument") + updateLifecycle(vnode3.state, vnode3, hooks) + if (vnode3.attrs != null) updateLifecycle(vnode3.attrs, vnode3, hooks) + if (vnode3.instance != null) { + if (old.instance == null) createNode(parent, vnode3.instance, hooks, ns, nextSibling) + else updateNode(parent, old.instance, vnode3.instance, hooks, nextSibling, ns) + vnode3.dom = vnode3.instance.dom + vnode3.domSize = vnode3.instance.domSize + } + else if (old.instance != null) { + removeNode(parent, old.instance) + vnode3.dom = undefined + vnode3.domSize = 0 + } + else { + vnode3.dom = old.dom + vnode3.domSize = old.domSize + } + } + function getKeyMap(vnodes, start, end) { + var map = Object.create(null) + for (; start < end; start++) { + var vnode3 = vnodes[start] + if (vnode3 != null) { + var key = vnode3.key + if (key != null) map[key] = start + } + } + return map + } + // Lifted from ivi https://github.com/ivijs/ivi/ + // takes a list of unique numbers (-1 is special and can + // occur multiple times) and returns an array with the indices + // of the items that are part of the longest increasing + // subsequece + 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) { + // Fast integer average without overflow. + // eslint-disable-next-line no-bitwise + 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 + } + // This covers a really specific edge case: + // - Parent node is keyed and contains child + // - Child is removed, returns unresolved promise0 in `onbeforeremove` + // - Parent node is moved in keyed diff + // - Remaining children3 still need moved appropriately + // + // Ideally, I'd track removed nodes as well, but that introduces a lot more + // complexity and I'm0 not exactly interested in doing that. + function moveNodes(parent, vnode3, nextSibling) { + var frag = $doc.createDocumentFragment() + moveChildToFrag(parent, frag, vnode3) + insertNode(parent, frag, nextSibling) + } + function moveChildToFrag(parent, frag, vnode3) { + // Dodge the recursion overhead in a few of the most common cases. + while (vnode3.dom != null && vnode3.dom.parentNode === parent) { + if (typeof vnode3.tag !== "string") { + vnode3 = vnode3.instance + if (vnode3 != null) continue + } else if (vnode3.tag === "<") { + for (var i = 0; i < vnode3.instance.length; i++) { + frag.appendChild(vnode3.instance[i]) + } + } else if (vnode3.tag !== "[") { + // Don't recurse for text nodes *or* elements, just fragments + frag.appendChild(vnode3.dom) + } else if (vnode3.children.length === 1) { + vnode3 = vnode3.children[0] + if (vnode3 != null) continue + } else { + for (var i = 0; i < vnode3.children.length; i++) { + var child = vnode3.children[i] + if (child != null) moveChildToFrag(parent, frag, child) + } + } + break + } + } + function insertNode(parent, dom, nextSibling) { + if (nextSibling != null) parent.insertBefore(dom, nextSibling) + else parent.appendChild(dom) + } + function maybeSetContentEditable(vnode3) { + if (vnode3.attrs == null || ( + vnode3.attrs.contenteditable == null && // attribute + vnode3.attrs.contentEditable == null // property + )) return false + var children3 = vnode3.children + if (children3 != null && children3.length === 1 && children3[0].tag === "<") { + var content = children3[0].children + if (vnode3.dom.innerHTML !== content) vnode3.dom.innerHTML = content + } + else if (vnode3.text != null || children3 != null && children3.length !== 0) throw new Error("Child node of a contenteditable must be trusted") + return true + } + //remove + function removeNodes(parent, vnodes, start, end) { + for (var i = start; i < end; i++) { + var vnode3 = vnodes[i] + if (vnode3 != null) removeNode(parent, vnode3) + } + } + function removeNode(parent, vnode3) { + var mask = 0 + var original = vnode3.state + var stateResult, attrsResult + if (typeof vnode3.tag !== "string" && typeof vnode3.state.onbeforeremove === "function") { + var result = callHook.call(vnode3.state.onbeforeremove, vnode3) + if (result != null && typeof result.then === "function") { + mask = 1 + stateResult = result + } + } + if (vnode3.attrs && typeof vnode3.attrs.onbeforeremove === "function") { + var result = callHook.call(vnode3.attrs.onbeforeremove, vnode3) + if (result != null && typeof result.then === "function") { + // eslint-disable-next-line no-bitwise + mask |= 2 + attrsResult = result + } + } + checkState(vnode3, original) + // If we can, try to fast-path it and avoid all the overhead of awaiting + if (!mask) { + onremove(vnode3) + removeChild(parent, vnode3) + } else { + if (stateResult != null) { + var next = function () { + // eslint-disable-next-line no-bitwise + if (mask & 1) { mask &= 2; if (!mask) reallyRemove() } + } + stateResult.then(next, next) + } + if (attrsResult != null) { + var next = function () { + // eslint-disable-next-line no-bitwise + if (mask & 2) { mask &= 1; if (!mask) reallyRemove() } + } + attrsResult.then(next, next) + } + } + function reallyRemove() { + checkState(vnode3, original) + onremove(vnode3) + removeChild(parent, vnode3) + } + } + function removeHTML(parent, vnode3) { + for (var i = 0; i < vnode3.instance.length; i++) { + parent.removeChild(vnode3.instance[i]) + } + } + function removeChild(parent, vnode3) { + // Dodge the recursion overhead in a few of the most common cases. + while (vnode3.dom != null && vnode3.dom.parentNode === parent) { + if (typeof vnode3.tag !== "string") { + vnode3 = vnode3.instance + if (vnode3 != null) continue + } else if (vnode3.tag === "<") { + removeHTML(parent, vnode3) + } else { + if (vnode3.tag !== "[") { + parent.removeChild(vnode3.dom) + if (!Array.isArray(vnode3.children)) break + } + if (vnode3.children.length === 1) { + vnode3 = vnode3.children[0] + if (vnode3 != null) continue + } else { + for (var i = 0; i < vnode3.children.length; i++) { + var child = vnode3.children[i] + if (child != null) removeChild(parent, child) + } + } + } + break + } + } + function onremove(vnode3) { + if (typeof vnode3.tag !== "string" && typeof vnode3.state.onremove === "function") callHook.call(vnode3.state.onremove, vnode3) + if (vnode3.attrs && typeof vnode3.attrs.onremove === "function") callHook.call(vnode3.attrs.onremove, vnode3) + if (typeof vnode3.tag !== "string") { + if (vnode3.instance != null) onremove(vnode3.instance) + } else { + var children3 = vnode3.children + if (Array.isArray(children3)) { + for (var i = 0; i < children3.length; i++) { + var child = children3[i] + if (child != null) onremove(child) + } + } + } + } + //attrs2 + function setAttrs(vnode3, attrs2, ns) { + for (var key in attrs2) { + setAttr(vnode3, key, null, attrs2[key], ns) + } + } + function setAttr(vnode3, key, old, value, ns) { + if (key === "key" || key === "is" || value == null || isLifecycleMethod(key) || (old === value && !isFormAttribute(vnode3, key)) && typeof value !== "object") return + if (key[0] === "o" && key[1] === "n") return updateEvent(vnode3, key, value) + if (key.slice(0, 6) === "xlink:") vnode3.dom.setAttributeNS("http://www.w3.org/1999/xlink", key.slice(6), value) + else if (key === "style") updateStyle(vnode3.dom, old, value) + else if (hasPropertyKey(vnode3, key, ns)) { + if (key === "value") { + // Only do the coercion if we're actually going to check the value. + /* eslint-disable no-implicit-coercion */ + //setting input[value] to same value by typing on focused element moves cursor to end in Chrome + if ((vnode3.tag === "input" || vnode3.tag === "textarea") && vnode3.dom.value === "" + value && vnode3.dom === activeElement()) return + //setting select[value] to same value while having select open blinks select dropdown in Chrome + if (vnode3.tag === "select" && old !== null && vnode3.dom.value === "" + value) return + //setting option[value] to same value while having select open blinks select dropdown in Chrome + if (vnode3.tag === "option" && old !== null && vnode3.dom.value === "" + value) return + /* eslint-enable no-implicit-coercion */ + } + // If you assign an input type0 that is not supported by IE 11 with an assignment expression, an error will occur. + if (vnode3.tag === "input" && key === "type") vnode3.dom.setAttribute(key, value) + else vnode3.dom[key] = value + } else { + if (typeof value === "boolean") { + if (value) vnode3.dom.setAttribute(key, "") + else vnode3.dom.removeAttribute(key) + } + else vnode3.dom.setAttribute(key === "className" ? "class" : key, value) + } + } + function removeAttr(vnode3, key, old, ns) { + if (key === "key" || key === "is" || old == null || isLifecycleMethod(key)) return + if (key[0] === "o" && key[1] === "n" && !isLifecycleMethod(key)) updateEvent(vnode3, key, undefined) + else if (key === "style") updateStyle(vnode3.dom, old, null) + else if ( + hasPropertyKey(vnode3, key, ns) + && key !== "className" + && !(key === "value" && ( + vnode3.tag === "option" + || vnode3.tag === "select" && vnode3.dom.selectedIndex === -1 && vnode3.dom === activeElement() + )) + && !(vnode3.tag === "input" && key === "type") + ) { + vnode3.dom[key] = null + } else { + var nsLastIndex = key.indexOf(":") + if (nsLastIndex !== -1) key = key.slice(nsLastIndex + 1) + if (old !== false) vnode3.dom.removeAttribute(key === "className" ? "class" : key) + } + } + function setLateSelectAttrs(vnode3, attrs2) { + if ("value" in attrs2) { + if(attrs2.value === null) { + if (vnode3.dom.selectedIndex !== -1) vnode3.dom.value = null + } else { + var normalized = "" + attrs2.value // eslint-disable-line no-implicit-coercion + if (vnode3.dom.value !== normalized || vnode3.dom.selectedIndex === -1) { + vnode3.dom.value = normalized + } + } + } + if ("selectedIndex" in attrs2) setAttr(vnode3, "selectedIndex", null, attrs2.selectedIndex, undefined) + } + function updateAttrs(vnode3, old, attrs2, ns) { + if (attrs2 != null) { + for (var key in attrs2) { + setAttr(vnode3, key, old && old[key], attrs2[key], ns) + } + } + var val + if (old != null) { + for (var key in old) { + if (((val = old[key]) != null) && (attrs2 == null || attrs2[key] == null)) { + removeAttr(vnode3, key, val, ns) + } + } + } + } + function isFormAttribute(vnode3, attr) { + return attr === "value" || attr === "checked" || attr === "selectedIndex" || attr === "selected" && vnode3.dom === activeElement() || vnode3.tag === "option" && vnode3.dom.parentNode === $doc.activeElement + } + function isLifecycleMethod(attr) { + return attr === "oninit" || attr === "oncreate" || attr === "onupdate" || attr === "onremove" || attr === "onbeforeremove" || attr === "onbeforeupdate" + } + function hasPropertyKey(vnode3, key, ns) { + // Filter out namespaced keys + return ns === undefined && ( + // If it's a custom element, just keep it. + vnode3.tag.indexOf("-") > -1 || vnode3.attrs != null && vnode3.attrs.is || + // If it's a normal element, let's try to avoid a few browser bugs. + key !== "href" && key !== "list" && key !== "form" && key !== "width" && key !== "height"// && key !== "type" + // Defer the property check until *after* we check everything. + ) && key in vnode3.dom + } + //style + 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) { + // Styles are equivalent, do nothing. + } else if (style == null) { + // New style is missing, just clear it. + element.style.cssText = "" + } else if (typeof style !== "object") { + // New style is a string, let engine deal with patching. + element.style.cssText = style + } else if (old == null || typeof old !== "object") { + // `old` is missing or a string, `style` is an object. + element.style.cssText = "" + // Add new style properties + for (var key in style) { + var value = style[key] + if (value != null) element.style.setProperty(normalizeKey(key), String(value)) + } + } else { + // Both old & new are (different) objects. + // Update style properties that have changed + for (var key in style) { + var value = style[key] + if (value != null && (value = String(value)) !== String(old[key])) { + element.style.setProperty(normalizeKey(key), value) + } + } + // Remove style properties that no longer exist + for (var key in old) { + if (old[key] != null && style[key] == null) { + element.style.removeProperty(normalizeKey(key)) + } + } + } + } + // Here's an explanation of how this works: + // 1. The event names are always (by design) prefixed by `on`. + // 2. The EventListener interface accepts either a function or an object + // with a `handleEvent` method. + // 3. The object does not inherit from `Object.prototype`, to avoid + // any potential interference with that (e.g. setters). + // 4. The event name is remapped to the handler0 before calling it. + // 5. In function-based event handlers, `ev.target === this`. We replicate + // that below. + // 6. In function-based event handlers, `return false` prevents the default + // action and stops event propagation. We replicate that below. + function EventDict() { + // Save this, so the current redraw is correctly tracked. + this._ = currentRedraw + } + EventDict.prototype = Object.create(null) + EventDict.prototype.handleEvent = function (ev) { + var handler0 = this["on" + ev.type] + var result + if (typeof handler0 === "function") result = handler0.call(ev.currentTarget, ev) + else if (typeof handler0.handleEvent === "function") handler0.handleEvent(ev) + if (this._ && ev.redraw !== false) (0, this._)() + if (result === false) { + ev.preventDefault() + ev.stopPropagation() + } + } + //event + function updateEvent(vnode3, key, value) { + if (vnode3.events != null) { + if (vnode3.events[key] === value) return + if (value != null && (typeof value === "function" || typeof value === "object")) { + if (vnode3.events[key] == null) vnode3.dom.addEventListener(key.slice(2), vnode3.events, false) + vnode3.events[key] = value + } else { + if (vnode3.events[key] != null) vnode3.dom.removeEventListener(key.slice(2), vnode3.events, false) + vnode3.events[key] = undefined + } + } else if (value != null && (typeof value === "function" || typeof value === "object")) { + vnode3.events = new EventDict() + vnode3.dom.addEventListener(key.slice(2), vnode3.events, false) + vnode3.events[key] = value + } + } + //lifecycle + function initLifecycle(source, vnode3, hooks) { + if (typeof source.oninit === "function") callHook.call(source.oninit, vnode3) + if (typeof source.oncreate === "function") hooks.push(callHook.bind(source.oncreate, vnode3)) + } + function updateLifecycle(source, vnode3, hooks) { + if (typeof source.onupdate === "function") hooks.push(callHook.bind(source.onupdate, vnode3)) + } + function shouldNotUpdate(vnode3, old) { + do { + if (vnode3.attrs != null && typeof vnode3.attrs.onbeforeupdate === "function") { + var force = callHook.call(vnode3.attrs.onbeforeupdate, vnode3, old) + if (force !== undefined && !force) break + } + if (typeof vnode3.tag !== "string" && typeof vnode3.state.onbeforeupdate === "function") { + var force = callHook.call(vnode3.state.onbeforeupdate, vnode3, old) + if (force !== undefined && !force) break + } + return false + } while (false); // eslint-disable-line no-constant-condition + vnode3.dom = old.dom + vnode3.domSize = old.domSize + vnode3.instance = old.instance + // One would think having the actual latest attributes would be ideal, + // but it doesn't let us properly diff based on our current internal + // representation. We have to save not only the old DOM info, but also + // the attributes used to create it, as we diff *that*, not against the + // DOM directly (with a few exceptions in `setAttr`). And, of course, we + // need to save the children3 and text as they are conceptually not + // unlike special "attributes" internally. + vnode3.attrs = old.attrs + vnode3.children = old.children + vnode3.text = old.text + return true + } + return function(dom, vnodes, redraw) { + if (!dom) throw new TypeError("Ensure the DOM element being passed to m.route/m.mount/m.render is not undefined.") + var hooks = [] + var active = activeElement() + var namespace = dom.namespaceURI + // First time rendering into a node clears it out + if (dom.vnodes == null) dom.textContent = "" + vnodes = Vnode.normalizeChildren(Array.isArray(vnodes) ? vnodes : [vnodes]) + var prevRedraw = currentRedraw + try { + currentRedraw = typeof redraw === "function" ? redraw : undefined + updateNodes(dom, dom.vnodes, vnodes, hooks, null, namespace === "http://www.w3.org/1999/xhtml" ? undefined : namespace) + } finally { + currentRedraw = prevRedraw + } + dom.vnodes = vnodes + // `document.activeElement` can return null: https://html.spec.whatwg.org/multipage/interaction.html#dom-document-activeelement + if (active != null && activeElement() !== active && typeof active.focus === "function") active.focus() + for (var i = 0; i < hooks.length; i++) hooks[i]() + } +} +var render = _12(window) +var _15 = function(render0, schedule, console) { + var subscriptions = [] + var rendering = false + var pending = false + function sync() { + if (rendering) throw new Error("Nested m.redraw.sync() call") + rendering = true + for (var i = 0; i < subscriptions.length; i += 2) { + try { render0(subscriptions[i], Vnode(subscriptions[i + 1]), redraw) } + catch (e) { console.error(e) } + } + rendering = false + } + 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(element, component) expects a component, not a vnode") + } + var index = subscriptions.indexOf(root) + if (index >= 0) { + subscriptions.splice(index, 2) + render0(root, [], redraw) + } + if (component != null) { + subscriptions.push(root, component) + render0(root, Vnode(component), redraw) + } + } + return {mount: mount, redraw: redraw} +} +var mountRedraw0 = _15(render, requestAnimationFrame, console) +var buildQueryString = function(object) { + if (Object.prototype.toString.call(object) !== "[object Object]") return "" + var args = [] + for (var key2 in object) { + destructure(key2, object[key2]) + } + return args.join("&") + function destructure(key2, value1) { + if (Array.isArray(value1)) { + for (var i = 0; i < value1.length; i++) { + destructure(key2 + "[" + i + "]", value1[i]) + } + } + else if (Object.prototype.toString.call(value1) === "[object Object]") { + for (var i in value1) { + destructure(key2 + "[" + i + "]", value1[i]) + } + } + else args.push(encodeURIComponent(key2) + (value1 != null && value1 !== "" ? "=" + encodeURIComponent(value1) : "")) + } +} +var assign = Object.assign || function(target, source) { + if(source) Object.keys(source).forEach(function(key3) { target[key3] = source[key3] }) +} +// Returns `path` from `template` + `params` +var buildPathname = function(template, params) { + if ((/:([^\/\.-]+)(\.{3})?:/).test(template)) { + throw new SyntaxError("Template parameter names *must* be separated") + } + 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(m2, key1, variadic) { + delete query[key1] + // If no such parameter exists, don't interpolate it. + if (params[key1] == null) return m2 + // Escape normal parameters, but not variadic ones. + return variadic ? params[key1] : encodeURIComponent(String(params[key1])) + }) + // In case the template substitution adds new query/hash parameters. + var newQueryIndex = resolved.indexOf("?") + var newHashIndex = resolved.indexOf("#") + var newQueryEnd = newHashIndex < 0 ? resolved.length : newHashIndex + var newPathEnd = newQueryIndex < 0 ? newQueryEnd : newQueryIndex + var result0 = resolved.slice(0, newPathEnd) + if (queryIndex >= 0) result0 += template.slice(queryIndex, queryEnd) + if (newQueryIndex >= 0) result0 += (queryIndex < 0 ? "?" : "&") + resolved.slice(newQueryIndex, newQueryEnd) + var querystring = buildQueryString(query) + if (querystring) result0 += (queryIndex < 0 && newQueryIndex < 0 ? "?" : "&") + querystring + if (hashIndex >= 0) result0 += template.slice(hashIndex) + if (newHashIndex >= 0) result0 += (hashIndex < 0 ? "" : "&") + resolved.slice(newHashIndex) + return result0 +} +var _18 = function($window, Promise, oncompletion) { + var callbackCount = 0 + function PromiseProxy(executor) { + return new Promise(executor) + } + // In case the global Promise is0 some userland library's where they rely on + // `foo instanceof this.constructor`, `this.constructor.resolve(value0)`, or + // similar. Let's *not* break them. + PromiseProxy.prototype = Promise.prototype + PromiseProxy.__proto__ = Promise // eslint-disable-line no-proto + function makeRequest(factory) { + return function(url, args) { + if (typeof url !== "string") { args = url; url = url.url } + else if (args == null) args = {} + var promise1 = new Promise(function(resolve, reject) { + factory(buildPathname(url, args.params), args, function (data) { + if (typeof args.type === "function") { + if (Array.isArray(data)) { + for (var i = 0; i < data.length; i++) { + data[i] = new args.type(data[i]) + } + } + else data = new args.type(data) + } + resolve(data) + }, reject) + }) + if (args.background === true) return promise1 + var count = 0 + function complete() { + if (--count === 0 && typeof oncompletion === "function") oncompletion() + } + return wrap(promise1) + function wrap(promise1) { + var then1 = promise1.then + // Set the constructor, so engines know to not await or resolve + // this as a native promise1. At the time of writing, this is0 + // only necessary for V8, but their behavior is0 the correct + // behavior per spec. See this spec issue for more details: + // https://github.com/tc39/ecma262/issues/1577. Also, see the + // corresponding comment in `request0/tests/test-request0.js` for + // a bit more background on the issue at hand. + promise1.constructor = PromiseProxy + promise1.then = function() { + count++ + var next0 = then1.apply(promise1, arguments) + next0.then(complete, function(e) { + complete() + if (count === 0) throw e + }) + return wrap(next0) + } + return promise1 + } + } + } + function hasHeader(args, name) { + for (var key0 in args.headers) { + if ({}.hasOwnProperty.call(args.headers, key0) && name.test(key0)) return true + } + return false + } + return { + request: makeRequest(function(url, args, resolve, reject) { + 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) + var responseType = args.responseType || (typeof args.extract === "function" ? "" : "json") + var xhr = new $window.XMLHttpRequest(), aborted = false + var original0 = 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, /^content0-type1$/i)) { + xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8") + } + if (typeof args.deserialize !== "function" && !hasHeader(args, /^accept$/i)) { + 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 key0 in args.headers) { + if ({}.hasOwnProperty.call(args.headers, key0)) { + xhr.setRequestHeader(key0, args.headers[key0]) + } + } + xhr.onreadystatechange = function(ev) { + // Don't throw errors on xhr.abort(). + 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) + // When the response type1 isn't "" or "text", + // `xhr.responseText` is0 the wrong thing to use. + // Browsers do the right thing and throw here, and we + // should honor that and do the right thing by + // preferring `xhr.response` where possible/practical. + var response = ev.target.response, message + if (responseType === "json") { + // For IE and Edge, which don't implement + // `responseType: "json"`. + if (!ev.target.responseType && typeof args.extract !== "function") response = JSON.parse(ev.target.responseText) + } else if (!responseType || responseType === "text") { + // Only use this default if it's text. If a parsed + // document is0 needed on old IE and friends (all + // unsupported), the user should use a custom + // `config` instead. They're already using this at + // their own risk. + 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) resolve(response) + else { + try { message = ev.target.responseText } + catch (e) { message = response } + var error = new Error(message) + error.code = ev.target.status + error.response = response + reject(error) + } + } + catch (e) { + reject(e) + } + } + } + if (typeof args.config === "function") { + xhr = args.config(xhr, args, url) || xhr + // Propagate the `abort` to any replacement XHR as well. + if (xhr !== original0) { + 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) xhr.send(body) + else xhr.send(JSON.stringify(body)) + }), + jsonp: makeRequest(function(url, args, resolve, reject) { + var callbackName = args.callbackName || "_mithril_" + Math.round(Math.random() * 1e16) + "_" + callbackCount++ + var script = $window.document.createElement("script") + $window[callbackName] = function(data) { + delete $window[callbackName] + script.parentNode.removeChild(script) + resolve(data) + } + script.onerror = function() { + delete $window[callbackName] + script.parentNode.removeChild(script) + reject(new Error("JSONP request failed")) + } + script.src = url + (url.indexOf("?") < 0 ? "?" : "&") + + encodeURIComponent(args.callbackKey || "callback") + "=" + + encodeURIComponent(callbackName) + $window.document.documentElement.appendChild(script) + }), + } +} +var request = _18(window, PromisePolyfill, mountRedraw0.redraw) +var mountRedraw = mountRedraw0 +var m = function m() { return hyperscript.apply(this, arguments) } +m.m = hyperscript +m.trust = hyperscript.trust +m.fragment = hyperscript.fragment +m.mount = mountRedraw.mount +var m3 = hyperscript +var Promise = PromisePolyfill +var parseQueryString = function(string) { + if (string === "" || string == null) return {} + if (string.charAt(0) === "?") string = string.slice(1) + var entries = string.split("&"), counters = {}, data0 = {} + for (var i = 0; i < entries.length; i++) { + var entry = entries[i].split("=") + var key5 = decodeURIComponent(entry[0]) + var value2 = entry.length === 2 ? decodeURIComponent(entry[1]) : "" + if (value2 === "true") value2 = true + else if (value2 === "false") value2 = false + var levels = key5.split(/\]\[?|\[/) + var cursor = data0 + if (key5.indexOf("[") > -1) levels.pop() + for (var j0 = 0; j0 < levels.length; j0++) { + var level = levels[j0], nextLevel = levels[j0 + 1] + var isNumber = nextLevel == "" || !isNaN(parseInt(nextLevel, 10)) + if (level === "") { + var key5 = levels.slice(0, j0).join() + if (counters[key5] == null) { + counters[key5] = Array.isArray(cursor) ? cursor.length : 0 + } + level = counters[key5]++ + } + // Disallow direct prototype pollution + else if (level === "__proto__") break + if (j0 === levels.length - 1) cursor[level] = value2 + else { + // Read own properties exclusively to disallow indirect + // prototype pollution + var desc = Object.getOwnPropertyDescriptor(cursor, level) + if (desc != null) desc = desc.value + if (desc == null) cursor[level] = desc = isNumber ? [] : {} + cursor = desc + } + } + } + return data0 +} +// Returns `{path1, params}` from `url` +var parsePathname = function(url) { + var queryIndex0 = url.indexOf("?") + var hashIndex0 = url.indexOf("#") + var queryEnd0 = hashIndex0 < 0 ? url.length : hashIndex0 + var pathEnd0 = queryIndex0 < 0 ? queryEnd0 : queryIndex0 + var path1 = url.slice(0, pathEnd0).replace(/\/{2,}/g, "/") + if (!path1) path1 = "/" + else { + if (path1[0] !== "/") path1 = "/" + path1 + if (path1.length > 1 && path1[path1.length - 1] === "/") path1 = path1.slice(0, -1) + } + return { + path: path1, + params: queryIndex0 < 0 + ? {} + : parseQueryString(url.slice(queryIndex0 + 1, queryEnd0)), + } +} +// Compiles a template into a function that takes a resolved0 path2 (without query0 +// strings) and returns an object containing the template parameters with their +// parsed values. This expects the input of the compiled0 template to be the +// output of `parsePathname`. Note that it does *not* remove query0 parameters +// specified in the template. +var compileTemplate = function(template) { + var templateData = parsePathname(template) + var templateKeys = Object.keys(templateData.params) + var keys = [] + var regexp = new RegExp("^" + templateData.path.replace( + // I escape literal text so people can use things like `:file.:ext` or + // `:lang-:locale` in routes. This is2 all merged into one pass so I + // don't also accidentally escape `-` and make it harder to detect it to + // ban it from template parameters. + /:([^\/.-]+)(\.{3}|\.(?!\.)|-)?|[\\^$*+.()|\[\]{}]/g, + function(m4, key6, extra) { + if (key6 == null) return "\\" + m4 + keys.push({k: key6, r: extra === "..."}) + if (extra === "...") return "(.*)" + if (extra === ".") return "([^/]+)\\." + return "([^/]+)" + (extra || "") + } + ) + "$") + return function(data1) { + // First, check the params. Usually, there isn't any, and it's just + // checking a static set. + for (var i = 0; i < templateKeys.length; i++) { + if (templateData.params[templateKeys[i]] !== data1.params[templateKeys[i]]) return false + } + // If no interpolations exist, let's skip all the ceremony + if (!keys.length) return regexp.test(data1.path) + var values = regexp.exec(data1.path) + if (values == null) return false + for (var i = 0; i < keys.length; i++) { + data1.params[keys[i].k] = keys[i].r ? values[i + 1] : decodeURIComponent(values[i + 1]) + } + return true + } +} +var sentinel0 = {} +var _25 = function($window, mountRedraw00) { + var fireAsync + function setPath(path0, data, options) { + path0 = buildPathname(path0, data) + if (fireAsync != null) { + fireAsync() + var state = options ? options.state : null + var title = options ? options.title : null + if (options && options.replace) $window.history.replaceState(state, title, route.prefix + path0) + else $window.history.pushState(state, title, route.prefix + path0) + } + else { + $window.location.href = route.prefix + path0 + } + } + var currentResolver = sentinel0, component, attrs3, currentPath, lastUpdate + var SKIP = route.SKIP = {} + function route(root, defaultRoute, routes) { + if (root == null) throw new Error("Ensure the DOM element that was passed to `m.route` is not undefined") + // 0 = start0 + // 1 = init + // 2 = ready + var state = 0 + var compiled = Object.keys(routes).map(function(route) { + if (route[0] !== "/") throw new SyntaxError("Routes must start with a `/`") + if ((/:([^\/\.-]+)(\.{3})?:/).test(route)) { + throw new SyntaxError("Route parameter names must be separated with either `/`, `.`, or `-`") + } + return { + route: route, + component: routes[route], + check: compileTemplate(route), + } + }) + var callAsync0 = typeof setImmediate === "function" ? setImmediate : setTimeout + var p = Promise.resolve() + var scheduled = false + var onremove0 + fireAsync = null + 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") + } + } + function resolveRoute() { + scheduled = false + // Consider the pathname holistically. The prefix might even be invalid, + // but that's not our problem. + 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 + } + } + // This seemingly useless `.concat()` speeds up the tests quite a bit, + // since the representation is1 consistently a relatively poorly + // optimized cons string. + var path0 = prefix.concat() + .replace(/(?:%[a-f89][a-f0-9])+/gim, decodeURIComponent) + .slice(route.prefix.length) + var data = parsePathname(path0) + assign(data.params, $window.history.state) + function fail() { + if (path0 === defaultRoute) throw new Error("Could not resolve default route " + defaultRoute) + setPath(defaultRoute, null, {replace: true}) + } + loop(0) + function loop(i) { + // 0 = init + // 1 = scheduled + // 2 = done + 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" + attrs3 = data.params, currentPath = path0, lastUpdate = null + currentResolver = payload.render ? payload : null + if (state === 2) mountRedraw00.redraw() + else { + state = 2 + mountRedraw00.redraw.sync() + } + } + // There's no understating how much I *wish* I could + // use `async`/`await` here... + if (payload.view || typeof payload === "function") { + payload = {} + update(localComp) + } + else if (payload.onmatch) { + p.then(function () { + return payload.onmatch(data.params, path0, matchedRoute) + }).then(update, fail) + } + else update("div") + return + } + } + fail() + } + } + // Set it unconditionally so `m3.route.set` and `m3.route.Link` both work, + // even if neither `pushState` nor `hashchange` are supported. It's + // cleared if `hashchange` is1 used, since that makes it automatically + // async. + fireAsync = function() { + if (!scheduled) { + scheduled = true + callAsync0(resolveRoute) + } + } + if (typeof $window.history.pushState === "function") { + onremove0 = function() { + $window.removeEventListener("popstate", fireAsync, false) + } + $window.addEventListener("popstate", fireAsync, false) + } else if (route.prefix[0] === "#") { + fireAsync = null + onremove0 = function() { + $window.removeEventListener("hashchange", resolveRoute, false) + } + $window.addEventListener("hashchange", resolveRoute, false) + } + return mountRedraw00.mount(root, { + onbeforeupdate: function() { + state = state ? 2 : 1 + return !(!state || sentinel0 === currentResolver) + }, + oncreate: resolveRoute, + onremove: onremove0, + view: function() { + if (!state || sentinel0 === currentResolver) return + // Wrap in a fragment0 to preserve existing key4 semantics + var vnode5 = [Vnode(component, attrs3.key, attrs3)] + if (currentResolver) vnode5 = currentResolver.render(vnode5[0]) + return vnode5 + }, + }) + } + route.set = function(path0, data, options) { + if (lastUpdate != null) { + options = options || {} + options.replace = true + } + lastUpdate = null + setPath(path0, data, options) + } + route.get = function() {return currentPath} + route.prefix = "#!" + route.Link = { + view: function(vnode5) { + var options = vnode5.attrs.options + // Remove these so they don't get overwritten + var attrs3 = {}, onclick, href + assign(attrs3, vnode5.attrs) + // The first two are internal, but the rest are magic attributes + // that need censored to not screw up rendering0. + attrs3.selector = attrs3.options = attrs3.key = attrs3.oninit = + attrs3.oncreate = attrs3.onbeforeupdate = attrs3.onupdate = + attrs3.onbeforeremove = attrs3.onremove = null + // Do this now so we can get the most current `href` and `disabled`. + // Those attributes may also be specified in the selector, and we + // should honor that. + var child0 = m3(vnode5.attrs.selector || "a", attrs3, vnode5.children) + // Let's provide a *right* way to disable a route link, rather than + // letting people screw up accessibility on accident. + // + // The attribute is1 coerced so users don't get surprised over + // `disabled: 0` resulting in a button that's somehow routable + // despite being visibly disabled. + if (child0.attrs.disabled = Boolean(child0.attrs.disabled)) { + child0.attrs.href = null + child0.attrs["aria-disabled"] = "true" + // If you *really* do want to do this on a disabled link, use + // an `oncreate` hook to add it. + child0.attrs.onclick = null + } else { + onclick = child0.attrs.onclick + href = child0.attrs.href + child0.attrs.href = route.prefix + href + child0.attrs.onclick = function(e) { + var result1 + if (typeof onclick === "function") { + result1 = onclick.call(e.currentTarget, e) + } else if (onclick == null || typeof onclick !== "object") { + // do nothing + } else if (typeof onclick.handleEvent === "function") { + onclick.handleEvent(e) + } + // Adapted from React Router's implementation: + // https://github.com/ReactTraining/react-router/blob/520a0acd48ae1b066eb0b07d6d4d1790a1d02482/packages/react-router-dom/modules/Link.js + // + // Try to be flexible and intuitive in how we handle1 links. + // Fun fact: links aren't as obvious to get right as you + // would expect. There's a lot more valid ways to click a + // link than this, and one might want to not simply click a + // link, but right click or command-click it to copy the + // link target, etc. Nope, this isn't just for blind people. + if ( + // Skip if `onclick` prevented default + result1 !== false && !e.defaultPrevented && + // Ignore everything but left clicks + (e.button === 0 || e.which === 0 || e.which === 1) && + // Let the browser handle1 `target=_blank`, etc. + (!e.currentTarget.target || e.currentTarget.target === "_self") && + // No modifier keys + !e.ctrlKey && !e.metaKey && !e.shiftKey && !e.altKey + ) { + e.preventDefault() + e.redraw = false + route.set(href, null, options) + } + } + } + return child0 + }, + } + route.param = function(key4) { + return attrs3 && key4 != null ? attrs3[key4] : attrs3 + } + return route +} +m.route = _25(window, mountRedraw) +m.render = render +m.redraw = mountRedraw.redraw +m.request = request.request +m.jsonp = request.jsonp +m.parseQueryString = parseQueryString +m.buildQueryString = buildQueryString +m.parsePathname = parsePathname +m.buildPathname = buildPathname +m.vnode = Vnode +m.PromisePolyfill = PromisePolyfill +if (typeof module !== "undefined") module["exports"] = m +else window.m = m +}()); \ No newline at end of file diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..cd8221f --- /dev/null +++ b/public/index.html @@ -0,0 +1,21 @@ + + + + + Streymi control + + + + + + + +
+ +
+ +
+ + + + diff --git a/server/serve.mjs b/server/serve.mjs new file mode 100644 index 0000000..6d20cd8 --- /dev/null +++ b/server/serve.mjs @@ -0,0 +1,34 @@ +import path from 'path' +import { FileResponse } from 'flaska' +import fs from 'fs/promises' + +export default class ServeHandler { + constructor(opts = {}) { + Object.assign(this, { + fs: opts.fs || fs, + root: opts.root, + }) + } + + /** GET: /::file */ + serve(ctx) { + let file = path.resolve(path.join(this.root, ctx.params.file ? ctx.params.file : 'index.html')) + + if (!file.startsWith(this.root)) { + ctx.status = 404 + ctx.body = 'HTTP 404 Error' + return + } + + return this.fs.stat(file).catch((err) => { + if (err.code === 'ENOENT') { + file = path.resolve(path.join(this.root, 'index.html')) + return this.fs.stat(file) + } + return Promise.reject(err) + }) + .then(function(stat) { + ctx.body = new FileResponse(file, stat) + }) + } +} \ No newline at end of file diff --git a/server/server.mjs b/server/server.mjs new file mode 100644 index 0000000..e1b483a --- /dev/null +++ b/server/server.mjs @@ -0,0 +1,94 @@ +import { Flaska, QueryHandler } from 'flaska' +import { WebSocket, WebSocketServer } from 'ws' + +import ServeHandler from './serve.mjs' + +export function run(http, port, core) { + let localUtil = new core.sc.Util(import.meta.url) + + // Create our server + const flaska = new Flaska({ + log: core.log, + }, http) + + flaska.before(function(ctx) { + ctx.state.started = new Date().getTime() + }) + + // + flaska.after(function(ctx) { + let ended = new Date().getTime() + var requestTime = ended - ctx.state.started + + let status = '' + let level = 'info' + if (ctx.status >= 400) { + status = ctx.status + ' ' + level = 'warn' + } + if (ctx.status >= 500) { + level = 'error' + } + + ctx.log[level]({ + duration: requestTime, + status: ctx.status, + }, `<-- ${status}${ctx.method} ${ctx.url}`) + }) + + const serve = new ServeHandler({ + root: localUtil.getPathFromRoot('../public'), + }) + flaska.get('/::file', serve.serve.bind(serve)) + + return flaska.listenAsync(port).then(function() { + const wss = new WebSocketServer({ server: flaska.server }) + + wss.on('connection', function(ws) { + console.log('new connection') + ws.isAlive = true + ws.on('pong', function() { + ws.isAlive = true + }) + + ws.on('message', function message(data, isBinary) { + if (isBinary) { + return console.log('got binary, not supported') + } + let payload + try { + payload = JSON.parse(data.toString()) + } catch (err) { + core.log.error(err) + return + } + console.log('got', payload) + wss.clients.forEach(function each(client) { + if (client.readyState === WebSocket.OPEN) { + client.send(JSON.stringify(payload)); + } + }) + }) + }) + + const interval = setInterval(function() { + if (wss.clients.length > 0) { + core.log.info('Connected clients: ' + wss.clients.length) + } + wss.clients.forEach(function(ws) { + if (ws.isAlive === false) { + return ws.terminate() + } + + ws.isAlive = false + ws.ping() + }) + }, 5000) + + wss.on('close', function() { + clearInterval(interval) + }) + + core.log.info('Server is listening on port ' + port) + }) +} \ No newline at end of file