From 9ab322751a732d8cbc1ddf4f2ecf5022d7242baa Mon Sep 17 00:00:00 2001 From: Marijn Besseling Date: Sun, 7 Sep 2025 20:56:09 +0200 Subject: WIP migration --- Blog/wwwroot/Blog.lib.module.js | 83 ++++++++ Blog/wwwroot/app.css | 280 ++++++++++++++++++++++++++ Blog/wwwroot/common.module.js | 146 ++++++++++++++ Blog/wwwroot/lz-string.module.js | 425 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 934 insertions(+) create mode 100644 Blog/wwwroot/Blog.lib.module.js create mode 100644 Blog/wwwroot/app.css create mode 100644 Blog/wwwroot/common.module.js create mode 100644 Blog/wwwroot/lz-string.module.js (limited to 'Blog/wwwroot') diff --git a/Blog/wwwroot/Blog.lib.module.js b/Blog/wwwroot/Blog.lib.module.js new file mode 100644 index 0000000..c1a5870 --- /dev/null +++ b/Blog/wwwroot/Blog.lib.module.js @@ -0,0 +1,83 @@ +const pageScriptInfoBySrc = new Map(); + +function registerPageScriptElement(src) { + if (!src) { + throw new Error('Must provide a non-empty value for the "src" attribute.'); + } + + let pageScriptInfo = pageScriptInfoBySrc.get(src); + + if (pageScriptInfo) { + pageScriptInfo.referenceCount++; + } else { + pageScriptInfo = { referenceCount: 1, module: null }; + pageScriptInfoBySrc.set(src, pageScriptInfo); + initializePageScriptModule(src, pageScriptInfo); + } +} + +function unregisterPageScriptElement(src) { + if (!src) { + return; + } + + const pageScriptInfo = pageScriptInfoBySrc.get(src); + + if (!pageScriptInfo) { + return; + } + + pageScriptInfo.referenceCount--; +} + +async function initializePageScriptModule(src, pageScriptInfo) { + if (src.startsWith("./")) { + src = new URL(src.substr(2), document.baseURI).toString(); + } + + const module = await import(src); + + if (pageScriptInfo.referenceCount <= 0) { + return; + } + + pageScriptInfo.module = module; + module.onLoad?.(); + module.onUpdate?.(); +} + +function onEnhancedLoad() { + for (const [src, { module, referenceCount }] of pageScriptInfoBySrc) { + if (referenceCount <= 0) { + module?.onDispose?.(); + pageScriptInfoBySrc.delete(src); + } + } + + for (const { module } of pageScriptInfoBySrc.values()) { + module?.onUpdate?.(); + } +} + +export function afterWebStarted(blazor) { + console.log("afterWebStarted"); + customElements.define('page-script', class extends HTMLElement { + static observedAttributes = ['src']; + + attributeChangedCallback(name, oldValue, newValue) { + if (name !== 'src') { + return; + } + + this.src = newValue; + unregisterPageScriptElement(oldValue); + registerPageScriptElement(newValue); + } + + disconnectedCallback() { + unregisterPageScriptElement(this.src); + } + }); + + blazor.addEventListener('enhancedload', onEnhancedLoad); +} \ No newline at end of file diff --git a/Blog/wwwroot/app.css b/Blog/wwwroot/app.css new file mode 100644 index 0000000..0065fd6 --- /dev/null +++ b/Blog/wwwroot/app.css @@ -0,0 +1,280 @@ +h1:focus { + outline: none; +} + +.valid.modified:not([type=checkbox]) { + outline: 1px solid #26b050; +} + +.invalid { + outline: 1px solid #e50000; +} + +.validation-message { + color: #e50000; +} + +.blazor-error-boundary { + background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; + padding: 1rem 1rem 1rem 3.7rem; + color: white; +} + + .blazor-error-boundary::after { + content: "An error has occurred." + } + +.darker-border-checkbox.form-check-input { + border-color: #929292; +} + +.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder { + color: var(--bs-secondary-color); + text-align: end; +} + +.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder { + text-align: start; +} + +* { + box-sizing: border-box; + font-size: calc(1rem + 0.5vw); + font-family: monospace; +} + +:root { + --black: #050505; + --white: #fafafa; + --error: #da0000; + --info: #669aba; + + --ratio: 1.5; + --s0: 1rem; + --s1: calc(var(--s0) * var(--ratio)); +} + +@media (prefers-color-scheme: dark) { + :root { + --background: var(--black); + --color: var(--white); + } +} + +@media (prefers-color-scheme: light) { + :root { + --background: var(--white); + --color: var(--black); + } +} + +body { + background-color: var(--background); + color: var(--color); +} + +.center { + box-sizing: content-box; + max-inline-size: 60ch; + margin-inline: auto; + padding-inline-start: var(--s1); + padding-inline-end: var(--s1); +} + +#logo { + height: 5em; + display: inline-block; +} + +header > h1 { + color: var(--header); + display: inline-block; +} + +li:has(a:focus)::marker { + content: "==> "; +} + +li:has(a:hover)::marker { + content: " >"; +} + +li:has(a:hover:focus)::marker { + content: "==>"; +} + +li::marker { + content: "> "; +} + +a { + color: var(--color); +} + +a:empty::before { + content: attr(href); +} + +input[type="text"] { + width: 80%; + display: inline-block; + background-color: var(--background); + color: var(--color); + border-color: var(--color); + border-radius: 0.5em; +} + +input[type="text"] { + width: 100%; + display: inline-block; + margin-inline: 0 1em; + font-family: monospace; + white-space: pre-wrap; + background-color: var(--background); + color: var(--color); + border-color: var(--border); + border-radius: 0.5em; +} + +label + input { + margin-block-end: 1em; +} + +label { + display: block; +} + +textarea { + width: 100%; + display: block; + margin-inline: 0 1em; + font-family: monospace; + white-space: pre-wrap; + background-color: var(--background); + color: var(--color); + border-color: var(--border); + border-radius: 0.5em; +} + +pre { + font-family: monospace; + white-space: pre-line; +} + +button, +input[type="button"], +input[type="submit"], +input[type="file"]::file-selector-button { + min-width: 20%; + display: inline-block; + color: var(--color); + background-color: var(--background); + border: 1px solid var(--color); + border-radius: 0.5em; + padding: 0.1em 1em; + cursor: pointer; + font-family: monospace; +} + + +.flex-row { + display: flex; + gap: 1em; +} + +.flex-spread { + display: flex; + gap: 1em; + justify-content: space-between; +} + +.history { + filter: opacity(30%); +} + +div + .history { + margin-block-start: 2em; +} + +main { + border-block-start: 2px dotted var(--color); + border-block-end: 2px dotted var(--color); + padding-block: 1em; +} + +.docs { + margin: 2em 0; + border: 2px dashed var(--color); + padding: 0.5em 1em; + border-radius: 0.5em; +} + +.error::before { + content: "ERR: "; + color: var(--error); +} + +.info::before { + content: "INF: "; + color: var(--info); +} + +label:has(> #debug:not(:checked)) + #log .debug { + display: none; +} + +.debug::before { + content: "DBG: "; + color: var(--info); +} + +.row + .row { + margin-block-start: 1em; +} + +.noselect { + user-select: none; + -moz-user-select: -moz-none; + -webkit-user-select: none; + -ms-user-select: none; +} + +img { + margin-inline: auto; + margin-block: 1em; +} + +footer > ul { + columns: 2; +} + +legend::before { + content: "( "; +} + +legend::after { + content: " )"; +} + +summary { + cursor: help; +} + +summary::marker { + content: "> "; +} + +details[open] summary::marker { + content: "x "; +} + +input[type="checkbox"] { + appearance: none; +} +input[type="checkbox"]:checked::after { + content: "[x]" +} + +input[type="checkbox"]::after { + content: "[ ]" +} \ No newline at end of file diff --git a/Blog/wwwroot/common.module.js b/Blog/wwwroot/common.module.js new file mode 100644 index 0000000..6007790 --- /dev/null +++ b/Blog/wwwroot/common.module.js @@ -0,0 +1,146 @@ +function getById(id) { + let element = document.getElementById(id); + if (element) { + return element; + } else { + throw `Element with id '${id}' not found.`; + } +} + +/** + * @param {string} href + * @returns {HTMLAnchorElement} + */ +function a(href, pageName) { + const aElem = document.createElement("a"); + aElem.href = href; + aElem.text = pageName; + return aElem; +} + +function div(...children) { + const divElem = document.createElement("div"); + divElem.replaceChildren(...children); + return divElem; +} + +function span(text) { + const spanElem = document.createElement("span"); + spanElem.textContent = text; + return spanElem; +} + +function h(elementTag, ...children) { + const elem = document.createElement(elementTag); + elem.replaceChildren(...children); + return elem; +} + +/** @param {HTMLElement} element */ +function addClass(element, newClass) { + element.classList.add(newClass); + return element; +} + +function writeError(error) { + writeLog(error, "error"); +} + +function writeInfo(msg) { + writeLog(msg, "info"); +} + +function writeDebug(msg) { + writeLog(msg, "debug"); +} + +function writeLog(msg, className) { + if (typeof msg === "string" || msg instanceof String) { + log.appendChild(div(addClass(span(msg), className))); + } else { + log.appendChild(div(addClass(msg, className))); + } +} + +function resetLog() { + log.replaceChildren(); +} + +const log = getById("log"); + +// https://stackoverflow.com/questions/75988682/debounce-in-javascript +// https://www.joshwcomeau.com/snippets/javascript/debounce/ +function debounce(callback, wait) { + let timeoutId = null; + return (...args) => { + window.clearTimeout(timeoutId); + timeoutId = window.setTimeout(() => { + callback(...args); + }, wait); + }; +} + +const stylesheet = new CSSStyleSheet(); +stylesheet.replaceSync(` + a { + color: var(--color); + } + nav > ul { + list-style-type: none; + padding-inline-start: 0; + } + + nav > ul > li { + display: inline-block; + } + + nav > ul > li > a::after { + display: inline-block; + color: var(--color); + content: ">"; + padding: 0 1em; + } +`); + +customElements.define("mb-nav", + class MBNav extends HTMLElement { + constructor() { + super(); + } + + get pagename() { + return this.getAttribute("pagename"); + } + + set pagename(value) { + this.setAttribute("pagename", value); + } + + connectedCallback() { + const shadow = this.attachShadow({ mode: "open" }); + shadow.adoptedStyleSheets = [stylesheet]; + + shadow.appendChild( + h("nav", + h("ul", + h("li", a("/", "MB.bes.is")), + h("li", span(this.pagename)), + ) + ) + ) + } + }); + +export { + getById, + a, + div, + span, + h, + addClass, + writeError, + writeInfo, + writeDebug, + resetLog, + debounce, +}; diff --git a/Blog/wwwroot/lz-string.module.js b/Blog/wwwroot/lz-string.module.js new file mode 100644 index 0000000..6167fd2 --- /dev/null +++ b/Blog/wwwroot/lz-string.module.js @@ -0,0 +1,425 @@ +// Copyright (c) 2013 Pieroxy +// This work is free. You can redistribute it and/or modify it +// under the terms of the WTFPL, Version 2 +// For more information see LICENSE.txt or http://www.wtfpl.net/ +// +// For more information, the home page: +// http://pieroxy.net/blog/pages/lz-string/testing.html +// +// LZ-based compression algorithm, version 1.4.5 +var LzStringModule = (function() { + +// private property + var f = String.fromCharCode; + var keyStrUriSafe = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$"; + var baseReverseDic = {}; + + function getBaseValue(alphabet, character) { + if (!baseReverseDic[alphabet]) { + baseReverseDic[alphabet] = {}; + for (var i=0 ; i> 1; + } + } else { + value = 1; + for (i=0 ; i> 1; + } + } + context_enlargeIn--; + if (context_enlargeIn == 0) { + context_enlargeIn = Math.pow(2, context_numBits); + context_numBits++; + } + delete context_dictionaryToCreate[context_w]; + } else { + value = context_dictionary[context_w]; + for (i=0 ; i> 1; + } + + + } + context_enlargeIn--; + if (context_enlargeIn == 0) { + context_enlargeIn = Math.pow(2, context_numBits); + context_numBits++; + } + // Add wc to the dictionary. + context_dictionary[context_wc] = context_dictSize++; + context_w = String(context_c); + } + } + + // Output the code for w. + if (context_w !== "") { + if (Object.prototype.hasOwnProperty.call(context_dictionaryToCreate,context_w)) { + if (context_w.charCodeAt(0)<256) { + for (i=0 ; i> 1; + } + } else { + value = 1; + for (i=0 ; i> 1; + } + } + context_enlargeIn--; + if (context_enlargeIn == 0) { + context_enlargeIn = Math.pow(2, context_numBits); + context_numBits++; + } + delete context_dictionaryToCreate[context_w]; + } else { + value = context_dictionary[context_w]; + for (i=0 ; i> 1; + } + + + } + context_enlargeIn--; + if (context_enlargeIn == 0) { + context_enlargeIn = Math.pow(2, context_numBits); + context_numBits++; + } + } + + // Mark the end of the stream + value = 2; + for (i=0 ; i> 1; + } + + // Flush the last char + while (true) { + context_data_val = (context_data_val << 1); + if (context_data_position == bitsPerChar-1) { + context_data.push(getCharFromInt(context_data_val)); + break; + } + else context_data_position++; + } + return context_data.join(''); + }, + + _decompress: function (length, resetValue, getNextValue) { + var dictionary = [], + next, + enlargeIn = 4, + dictSize = 4, + numBits = 3, + entry = "", + result = [], + i, + w, + bits, resb, maxpower, power, + c, + data = {val:getNextValue(0), position:resetValue, index:1}; + + for (i = 0; i < 3; i += 1) { + dictionary[i] = i; + } + + bits = 0; + maxpower = Math.pow(2,2); + power=1; + while (power!=maxpower) { + resb = data.val & data.position; + data.position >>= 1; + if (data.position == 0) { + data.position = resetValue; + data.val = getNextValue(data.index++); + } + bits |= (resb>0 ? 1 : 0) * power; + power <<= 1; + } + + switch (next = bits) { + case 0: + bits = 0; + maxpower = Math.pow(2,8); + power=1; + while (power!=maxpower) { + resb = data.val & data.position; + data.position >>= 1; + if (data.position == 0) { + data.position = resetValue; + data.val = getNextValue(data.index++); + } + bits |= (resb>0 ? 1 : 0) * power; + power <<= 1; + } + c = f(bits); + break; + case 1: + bits = 0; + maxpower = Math.pow(2,16); + power=1; + while (power!=maxpower) { + resb = data.val & data.position; + data.position >>= 1; + if (data.position == 0) { + data.position = resetValue; + data.val = getNextValue(data.index++); + } + bits |= (resb>0 ? 1 : 0) * power; + power <<= 1; + } + c = f(bits); + break; + case 2: + return ""; + } + dictionary[3] = c; + w = c; + result.push(c); + while (true) { + if (data.index > length) { + return ""; + } + + bits = 0; + maxpower = Math.pow(2,numBits); + power=1; + while (power!=maxpower) { + resb = data.val & data.position; + data.position >>= 1; + if (data.position == 0) { + data.position = resetValue; + data.val = getNextValue(data.index++); + } + bits |= (resb>0 ? 1 : 0) * power; + power <<= 1; + } + + switch (c = bits) { + case 0: + bits = 0; + maxpower = Math.pow(2,8); + power=1; + while (power!=maxpower) { + resb = data.val & data.position; + data.position >>= 1; + if (data.position == 0) { + data.position = resetValue; + data.val = getNextValue(data.index++); + } + bits |= (resb>0 ? 1 : 0) * power; + power <<= 1; + } + + dictionary[dictSize++] = f(bits); + c = dictSize-1; + enlargeIn--; + break; + case 1: + bits = 0; + maxpower = Math.pow(2,16); + power=1; + while (power!=maxpower) { + resb = data.val & data.position; + data.position >>= 1; + if (data.position == 0) { + data.position = resetValue; + data.val = getNextValue(data.index++); + } + bits |= (resb>0 ? 1 : 0) * power; + power <<= 1; + } + dictionary[dictSize++] = f(bits); + c = dictSize-1; + enlargeIn--; + break; + case 2: + return result.join(''); + } + + if (enlargeIn == 0) { + enlargeIn = Math.pow(2, numBits); + numBits++; + } + + if (dictionary[c]) { + entry = dictionary[c]; + } else { + if (c === dictSize) { + entry = w + w.charAt(0); + } else { + return null; + } + } + result.push(entry); + + // Add w+entry[0] to the dictionary. + dictionary[dictSize++] = w + entry.charAt(0); + enlargeIn--; + + w = entry; + + if (enlargeIn == 0) { + enlargeIn = Math.pow(2, numBits); + numBits++; + } + + } + } + }; + return LZString; +})(); + + +export default LzStringModule; \ No newline at end of file -- cgit v1.2.3