From 9ab322751a732d8cbc1ddf4f2ecf5022d7242baa Mon Sep 17 00:00:00 2001 From: Marijn Besseling Date: Sun, 7 Sep 2025 20:56:09 +0200 Subject: WIP migration --- .gitignore | 6 + .run/Publish Blog to custom server.run.xml | 6 + Blog.sln | 16 ++ Blog.sln.DotSettings.user | 3 + Blog/Blog.csproj | 9 + Blog/Components/App.razor | 61 ++++ Blog/Components/Layout/MainLayout.razor | 3 + Blog/Components/Layout/MainLayout.razor.css | 20 ++ Blog/Components/Pages/Calc.razor | 56 ++++ Blog/Components/Pages/Calc.razor.js | 140 +++++++++ Blog/Components/Pages/Concat.razor | 138 +++++++++ Blog/Components/Pages/Concat.razor.js | 261 +++++++++++++++++ Blog/Components/Pages/Error.razor | 37 +++ Blog/Components/Pages/Home.razor | 10 + Blog/Components/Pages/Letterflixd.razor | 30 ++ Blog/Components/Pages/Letterflixd.razor.js | 99 +++++++ Blog/Components/Pages/NotFound.razor | 4 + Blog/Components/Pages/Note.razor | 8 + Blog/Components/Pages/Note.razor.js | 33 +++ Blog/Components/Routes.razor | 6 + Blog/Components/_Imports.razor | 11 + Blog/Components/_Shared/PageScript.razor | 7 + Blog/Program.cs | 28 ++ Blog/Properties/launchSettings.json | 23 ++ Blog/appsettings.Development.json | 8 + Blog/appsettings.json | 9 + 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 ++++++++++++++++++++++++++++ 30 files changed, 1966 insertions(+) create mode 100644 .gitignore create mode 100644 .run/Publish Blog to custom server.run.xml create mode 100644 Blog.sln create mode 100644 Blog.sln.DotSettings.user create mode 100644 Blog/Blog.csproj create mode 100644 Blog/Components/App.razor create mode 100644 Blog/Components/Layout/MainLayout.razor create mode 100644 Blog/Components/Layout/MainLayout.razor.css create mode 100644 Blog/Components/Pages/Calc.razor create mode 100644 Blog/Components/Pages/Calc.razor.js create mode 100644 Blog/Components/Pages/Concat.razor create mode 100644 Blog/Components/Pages/Concat.razor.js create mode 100644 Blog/Components/Pages/Error.razor create mode 100644 Blog/Components/Pages/Home.razor create mode 100644 Blog/Components/Pages/Letterflixd.razor create mode 100644 Blog/Components/Pages/Letterflixd.razor.js create mode 100644 Blog/Components/Pages/NotFound.razor create mode 100644 Blog/Components/Pages/Note.razor create mode 100644 Blog/Components/Pages/Note.razor.js create mode 100644 Blog/Components/Routes.razor create mode 100644 Blog/Components/_Imports.razor create mode 100644 Blog/Components/_Shared/PageScript.razor create mode 100644 Blog/Program.cs create mode 100644 Blog/Properties/launchSettings.json create mode 100644 Blog/appsettings.Development.json create mode 100644 Blog/appsettings.json 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 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f400df3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +bin/ +obj/ +/packages/ +riderModule.iml +/_ReSharper.Caches/ +.idea/ \ No newline at end of file diff --git a/.run/Publish Blog to custom server.run.xml b/.run/Publish Blog to custom server.run.xml new file mode 100644 index 0000000..aed35c3 --- /dev/null +++ b/.run/Publish Blog to custom server.run.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Blog.sln b/Blog.sln new file mode 100644 index 0000000..66ac13d --- /dev/null +++ b/Blog.sln @@ -0,0 +1,16 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Blog", "Blog\Blog.csproj", "{59F49D4F-D18D-4CE9-9A86-01FF2ED3B919}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {59F49D4F-D18D-4CE9-9A86-01FF2ED3B919}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {59F49D4F-D18D-4CE9-9A86-01FF2ED3B919}.Debug|Any CPU.Build.0 = Debug|Any CPU + {59F49D4F-D18D-4CE9-9A86-01FF2ED3B919}.Release|Any CPU.ActiveCfg = Release|Any CPU + {59F49D4F-D18D-4CE9-9A86-01FF2ED3B919}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/Blog.sln.DotSettings.user b/Blog.sln.DotSettings.user new file mode 100644 index 0000000..8d45cc9 --- /dev/null +++ b/Blog.sln.DotSettings.user @@ -0,0 +1,3 @@ + + ForceIncluded + ForceIncluded \ No newline at end of file diff --git a/Blog/Blog.csproj b/Blog/Blog.csproj new file mode 100644 index 0000000..3cb3a89 --- /dev/null +++ b/Blog/Blog.csproj @@ -0,0 +1,9 @@ + + + + net10.0 + enable + enable + + + diff --git a/Blog/Components/App.razor b/Blog/Components/App.razor new file mode 100644 index 0000000..7c8b8ce --- /dev/null +++ b/Blog/Components/App.razor @@ -0,0 +1,61 @@ + + + + + + + + + + + + + +
+ +

.bes.is

+
+ + + + + + + + + + \ No newline at end of file diff --git a/Blog/Components/Layout/MainLayout.razor b/Blog/Components/Layout/MainLayout.razor new file mode 100644 index 0000000..e1a9a75 --- /dev/null +++ b/Blog/Components/Layout/MainLayout.razor @@ -0,0 +1,3 @@ +@inherits LayoutComponentBase + +@Body diff --git a/Blog/Components/Layout/MainLayout.razor.css b/Blog/Components/Layout/MainLayout.razor.css new file mode 100644 index 0000000..60cec92 --- /dev/null +++ b/Blog/Components/Layout/MainLayout.razor.css @@ -0,0 +1,20 @@ +#blazor-error-ui { + color-scheme: light only; + background: lightyellow; + bottom: 0; + box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); + box-sizing: border-box; + display: none; + left: 0; + padding: 0.6rem 1.25rem 0.7rem 1.25rem; + position: fixed; + width: 100%; + z-index: 1000; +} + + #blazor-error-ui .dismiss { + cursor: pointer; + position: absolute; + right: 0.75rem; + top: 0.5rem; + } diff --git a/Blog/Components/Pages/Calc.razor b/Blog/Components/Pages/Calc.razor new file mode 100644 index 0000000..942d9a9 --- /dev/null +++ b/Blog/Components/Pages/Calc.razor @@ -0,0 +1,56 @@ +@page "/Calc" +Calculator + + +
+

A rpn calculator

+
+ + +
+
+
+
+ +
+ Available stack operations +
+ dup +

Duplicates the top value on the stack

+
+ [1, 2, 3] + dup +
+
+ [1, 1, 2, 3] + +
+
+ +
+ drop +

Drops the top value on the stack

+
+ [1, 2, 3] + drop +
+
+ [2, 3] + +
+
+ +
+ swap +

Swaps the top two values on the stack

+
+ [1, 2, 3] + swap +
+
+ [2, 1, 3] + +
+
+
+
\ No newline at end of file diff --git a/Blog/Components/Pages/Calc.razor.js b/Blog/Components/Pages/Calc.razor.js new file mode 100644 index 0000000..d18634e --- /dev/null +++ b/Blog/Components/Pages/Calc.razor.js @@ -0,0 +1,140 @@ +import { getById, div, span, h, writeError } from "/common.module.js" + +export function onLoad() { + console.log('Loaded'); + const form = getById("form"); + const input = getById("input"); + const log = getById("log"); + + form.addEventListener("submit", submitForm); + + const urlParams = new URLSearchParams(window.location.search); + const queryInput = urlParams.get('in'); + + if (input.value.length === 0) { + input.value = queryInput; + } +} + +export function onUpdate() { + console.log('Updated'); +} + +export function onDispose() { + console.log('Disposed'); +} + +/** @param {SubmitEvent} event */ +function submitForm(event) { + console.log(input.value); + resetLog(); + try { + const stack = evalString(input.value); + console.log(stack); + + const path = window.location.pathname; + const params = new URLSearchParams(window.location.search); + const hash = window.location.hash; + + params.set("in", input.value); + window.history.replaceState({}, '', `${path}?${params.toString()}${hash}`); + } + catch (error) { + writeError(error); + } + event.preventDefault(); +} + +/** @param {string} input */ +function evalString(input) { + let words = input.trim().split(' ').filter(i => i); + return evalWords([], words); +} + +function effect2(f) { + return (stack) => { + if (stack.length <= 1) throw "stack underflow"; + let [x, y, ...rest] = stack; + return [f(y, x), ...rest]; + } +} + +const plus = effect2((a, b) => a + b); +const subtract = effect2((a, b) => a - b); +const multiply = effect2((a, b) => a * b); +const divide = effect2((a, b) => a / b); + + +function evalWords(stack, words) { + writeLog(stack, words); + if (words.length === 0) return stack; + + let [word, ...rest] = words; + return evalWords(evalWord(word, stack), rest); +} + +function evalWord(word, stack, rest) { + switch (word) { + case "+": return plus(stack); + case "-": return subtract(stack); + case "*": return multiply(stack); + case "/": return divide(stack); + case "dup": return dup(stack); + case "drop": return drop(stack); + case "swap": return swap(stack); + default: return parse(word, stack); + } +} + +function dup(stack) { + let [x, ...rest] = stack; + return [x, x, ...rest]; +} + +function drop(stack) { + let [_, ...rest] = stack; + return rest; +} + +function swap(stack) { + let [x, y, ...rest] = stack; + return [y, x, ...rest]; +} + +function parse(word, stack) { + let num = Number(word); + if (isNaN(num)) { + throw `word '${word}' not recognised`; + } + return [num, ...stack]; +} + +function writeLog(stack, words) { + let log_left = span(`[${stack.join(', ')}]`); + let log_right = span(words.join(' ')); + + if (words.length === 0) { + log_left.textContent += " <==" + } + + let log_row = div(log_left, log_right); + log_row.className = "flex-spread"; + + log.appendChild(log_row); +} + +function resetLog() { + if (!log.hasChildNodes()) return; + + let summary = h("summary"); + summary.textContent = log.firstChild.lastChild.textContent; + + let old_log = log.cloneNode(true); + old_log.id = ''; + + let details = h("details", summary, old_log); + details.className = "history" + + log.insertAdjacentElement("afterend", details); + log.replaceChildren(); //remove children, clear log +} \ No newline at end of file diff --git a/Blog/Components/Pages/Concat.razor b/Blog/Components/Pages/Concat.razor new file mode 100644 index 0000000..856d4e0 --- /dev/null +++ b/Blog/Components/Pages/Concat.razor @@ -0,0 +1,138 @@ +@page "/Concat" +Concatenator + +
+

A "concatenative language"

+
+ + +
+
+
+
+ +
+ Predefined operations +
+ dup +

Duplicates the top value on the stack

+
+ [1, 2, 3] + dup +
+
+ [1, 1, 2, 3] + +
+
+ +
+ drop +

Drops the top value on the stack

+
+ [1, 2, 3] + drop +
+
+ [2, 3] + +
+
+ +
+ swap +

Swaps the top two values on the stack

+
+ [1, 2, 3] + swap +
+
+ [2, 1, 3] + +
+
+ +
+ skip +

Skips forward in the program by the amount on the top of the stack. + If the value on the top of the stack is 0 or negative, + the value is dropped and the program does not skip ahead. +

+
+ [1, 2, 3] + skip drop dup +
+
+ [2, 3] + dup +
+
+ [2, 2, 3] + +
+ Or +
+ [-1, 2, 3] + skip drop dup +
+
+ [2, 3] + drop dup +
+
+ [3] + dup +
+
+ [3, 3] + +
+
+ +
+ : word ... ; +

Creates a new definition.

+

Example: ": double 2 * ;" defines a new word called double. Any subsequent mention of double will + replace the word with its definition.

+
+ [] + : double 2 * ; 3 double +
+
+ [] + 3 double +
+
+ [3] + double +
+
+ [3] + 2 * +
+
+ [2, 3] + * +
+
+ [6] <== + +
+
+
+
+ Example programs +
+ Factorial + + Replace program + +
+                : counter   dup 2 swap - ;
+                : !   dup 1 - counter skip ! * ;
+                7 !
+            
+
+
+
\ No newline at end of file diff --git a/Blog/Components/Pages/Concat.razor.js b/Blog/Components/Pages/Concat.razor.js new file mode 100644 index 0000000..bd476e9 --- /dev/null +++ b/Blog/Components/Pages/Concat.razor.js @@ -0,0 +1,261 @@ +import { getById, div, span, h, writeError } from "/common.module.js"; +import lzString from "/lz-string.module.js"; + +let definitions = {}; +let log = undefined; +let input = undefined; + +export function onLoad() { + console.log('Loaded'); + const form = getById("form"); + input = getById("input"); + log = getById("log"); + + form.onsubmit = async (event) => { + event.preventDefault(); + await submitForm(); + }; + + const urlParams = new URLSearchParams(window.location.search); + const queryInput = urlParams.get("in"); + + if (input.value.length === 0) { + input.value = lzString.decompressFromEncodedURIComponent(queryInput); + } +} + +export function onUpdate() { + const urlParams = new URLSearchParams(window.location.search); + const queryInput = urlParams.get("in"); + + if (input.value.length === 0) { + input.value = lzString.decompressFromEncodedURIComponent(queryInput); + } +} + +async function submitForm() { + console.log(input.value); + resetLog(); + try { + definitions = {}; + const stack = await evalString(input.value); + console.log(stack); + + const path = window.location.pathname; + const params = new URLSearchParams(window.location.search); + const hash = window.location.hash; + + params.set("in", lzString.compressToEncodedURIComponent(input.value)); + window.history.replaceState( + {}, + "", + `${path}?${params.toString()}${hash}`, + ); + } catch (error) { + writeError(error); + console.log(error); + } +} + +function splitWords(input) { + return input + .trim() + .split(/\s+/) + .filter((i) => i); +} + +/** @param {string} inputString */ +function evalString(inputString) { + let words = splitWords(inputString); + return evalWords(words); +} + +function effect2(f) { + return (stack) => { + if (stack.length <= 1) throw "stack underflow, need 2 numbers"; + let [x, y, ...rest] = stack; + return [f(y, x), ...rest]; + }; +} + +const plus = effect2((a, b) => a + b); +const subtract = effect2((a, b) => a - b); +const multiply = effect2((a, b) => a * b); +const divide = effect2((a, b) => a / b); + +async function evalWords(inputWords) { + let words = inputWords; + let stack = []; + + while (words.length > 0) { + await writeLog(stack, words); + if (words.length === 0) return stack; + + let [word, ...rest] = words; + [stack, words] = evalWord(word, stack, rest); + + await new Promise((r) => setTimeout(r, 100)); + } + await writeLog(stack, words); + return stack; +} + +function evalWord(word, stack, rest) { + switch (word) { + case "+": + return [plus(stack), rest]; + case "-": + return [subtract(stack), rest]; + case "*": + return [multiply(stack), rest]; + case "/": + return [divide(stack), rest]; + case "dup": + return [dup(stack), rest]; + case "drop": + return [drop(stack), rest]; + case "swap": + return [swap(stack), rest]; + case "skip": + return skip(stack, rest); + case ",,": + return unquote(stack, rest); + case ":": + return [stack, define(rest)]; + default: + return parse(word, stack, rest); + } +} + +function unquote(stack, rest) { + let [quote, ...restStack] = stack; + if (typeof quote === "string" || quote instanceof String) { + return [restStack, [...splitWords(quote), ...rest]]; + } else { + throw "not a string, only strings are unquoteable"; + } +} + +function skip(stack, words) { + if (stack.length === 0) throw "stack underflow, dont know how much to skip"; + let [amount, ...restStack] = stack; + + if (amount > words.length) + throw `program underflow, cant skip ${amount} words`; + if (amount <= 0) return [stack.slice(1), words]; // no skipping on <= 0 + + let restWords = words.slice(amount); + + return [restStack, restWords]; +} + +function define(words) { + if (words.length < 2) throw "missing definition after ':'"; + let [ident, ...rest] = words; + let index = 0; + let definition = []; + + for (; index < rest.length; index++) { + const word = rest[index]; + if (word === ";") { + break; + } else if (rest.length - 1 === index) { + throw "expected ';', found end of program"; + } + definition.push(word); + } + + definitions[ident] = definition; + + return rest.slice(index + 1); +} + +function dup(stack) { + if (stack.length === 0) throw "stack underflow, nothing to duplicate"; + let [x, ...rest] = stack; + return [x, x, ...rest]; +} + +function drop(stack) { + if (stack.length === 0) throw "stack underflow, nothing to drop"; + let [_, ...rest] = stack; + return rest; +} + +function swap(stack) { + if (stack.length < 2) throw "stack underflow, not enough to swap"; + let [x, y, ...rest] = stack; + return [y, x, ...rest]; +} + +function parse(word, stack, rest) { + if (word.startsWith('"')) { + return parseString(word, stack, rest); + } + + if (word in definitions) { + return [stack, [...definitions[word], ...rest]]; + } + + let num = Number(word); + if (isNaN(num)) { + throw `word '${word}' not recognised`; + } + + return [[num, ...stack], rest]; +} + +function parseString(word, stack, rest) { + if (word.length > 1 && word.endsWith('"')) { + return [[word.slice(1, -1), ...stack], rest]; + } + + let string = word.slice(1); + let index = 0; + + for (; index < rest.length; index++) { + const word = rest[index]; + if (word.endsWith('"')) { + string = string.concat(" ", word.slice(0, -1)); + break; + } else if (rest.length - 1 === index) { + throw "expected word ending with '\"', found end of program"; + } + string = string.concat(" ", word); + } + + return [[string, ...stack], rest.slice(index + 1)]; +} + +function writeLog(stack, words) { + return new Promise((resolve, _reject) => { + let log_left = span(`[${stack.join(", ")}]`); + let log_right = span(words.join(" ")); + + if (words.length === 0) { + log_left.textContent += " <=="; + } + + let log_row = div(log_left, log_right); + log_row.className = "flex-spread"; + + log.appendChild(log_row); + resolve(); + }); +} + +function resetLog() { + if (!log.hasChildNodes()) return; + + let summary = h("summary"); + summary.textContent = log.firstChild.lastChild.textContent; + + let old_log = log.cloneNode(true); + old_log.id = ""; + + let details = h("details", summary, old_log); + details.className = "history"; + + log.insertAdjacentElement("afterend", details); + log.replaceChildren(); //remove children, clear log +} diff --git a/Blog/Components/Pages/Error.razor b/Blog/Components/Pages/Error.razor new file mode 100644 index 0000000..6567978 --- /dev/null +++ b/Blog/Components/Pages/Error.razor @@ -0,0 +1,37 @@ +@page "/Error" +@using System.Diagnostics +@using Microsoft.AspNetCore.Http + +Error + +

Error.

+

An error occurred while processing your request.

+ +@if (ShowRequestId) +{ +

+ Request ID: @RequestId +

+} + +

Development Mode

+

+ Swapping to Development environment will display more detailed information about the error that occurred. +

+

+ The Development environment shouldn't be enabled for deployed applications. + It can result in displaying sensitive information from exceptions to end users. + For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development + and restarting the app. +

+ +@code{ + [CascadingParameter] private HttpContext? HttpContext { get; set; } + + private string? RequestId { get; set; } + private bool ShowRequestId => !string.IsNullOrEmpty(RequestId); + + protected override void OnInitialized() => + RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier; + +} \ No newline at end of file diff --git a/Blog/Components/Pages/Home.razor b/Blog/Components/Pages/Home.razor new file mode 100644 index 0000000..be924d5 --- /dev/null +++ b/Blog/Components/Pages/Home.razor @@ -0,0 +1,10 @@ +@page "/" + +Home + +
+

A couple of JS/HTML experiments.

+
+
+
+
\ No newline at end of file diff --git a/Blog/Components/Pages/Letterflixd.razor b/Blog/Components/Pages/Letterflixd.razor new file mode 100644 index 0000000..b6a088a --- /dev/null +++ b/Blog/Components/Pages/Letterflixd.razor @@ -0,0 +1,30 @@ +@page "/Letterflixd" +Netflix to Letterboxd converter + +
+

Convert Netflix viewing history to a Letterboxd import file.

+

Download your viewing history from Netflix at https://www.netflix.com/settings/viewed/

+

Import the resulting Letterboxd file at https://letterboxd.com/import/

+
+
+
+ +
+
+ +
+
+
+
+ +
+
+
\ No newline at end of file diff --git a/Blog/Components/Pages/Letterflixd.razor.js b/Blog/Components/Pages/Letterflixd.razor.js new file mode 100644 index 0000000..a3903e2 --- /dev/null +++ b/Blog/Components/Pages/Letterflixd.razor.js @@ -0,0 +1,99 @@ +import { getById, resetLog, writeError, writeInfo, writeDebug, a, span, div, addClass } from "/common.module.js" + +let netflix_file_input = undefined; + +export function onLoad() { + + const form = getById("form"); + netflix_file_input = getById("netflix-file"); + + form.addEventListener("submit", submitForm); +} + + +/** @param {SubmitEvent} event */ +function submitForm(event) { + event.preventDefault(); + event.stopPropagation(); + + resetLog(); + const files = netflix_file_input.files; + if (files.length !== 1) { + writeError("First select your viewing history file."); + return; + } + + const netflix_file = files[0]; + + writeInfo("Checking metadata..."); + + if (!netflix_file.type.match('text/csv')) { + let detected_filetype = ""; + if (netflix_file.type) { + detected_filetype = ", detected filetype: " + netflix_file.type; + } + writeError("Select a CSV file" + detected_filetype); + return; + } + + handleFile(netflix_file); +} + +/** @param {File} file */ +function handleFile(file) { + writeInfo("Reading file..."); + file.text() + .then(convertText); +} + +const viewingHistoryPattern = /"(.*)","(\d{1,2})\/(\d{1,2})\/(\d+)"/; + +/** @param {string} data */ +function convertText(data) { + writeInfo("Checking header..."); + const [header, ...lines] = data.split(/\r?\n|\r|\n/g); + if (header !== "Title,Date") { + writeError("Invalid Netflix viewing history file, expected header \"Title,Date\""); + return; + } + writeInfo("History count: " + lines.length); + let letterboxd_output = "Title,WatchedDate,Rewatch\n"; + let watched_movies = new Set(); + + for (let line of lines) { + if (!line) continue; + if (line.includes(": Season") + || line.includes(": Limited Series:") + || line.includes(": Part") + || line.includes(": Chapter ") + || line.includes(": Volume ") + || line.includes(": Series ") + || line.includes(": Book ")) { + writeDebug("Skipping show episode: " + line); + continue; + } + if (line.startsWith(": ")) { + writeDebug("Skipping empty title entry") + } + const [_, title, day, month, year] = viewingHistoryPattern.exec(line); + if (title && day && month && year) { + const rewatch = watched_movies.has(title); + letterboxd_output += `"${title}",${year}-${month}-${day},${rewatch}\n` + if (rewatch) { + writeDebug("Rewatch of: " + title); + } + watched_movies.add(title); + } + else { + writeError("could not parse line: " + line); + } + } + + let letterboxd_import = new Blob([letterboxd_output], { type: 'text/csv' }); + + const filename = "netflix-letterboxd-import.csv" + let download_button = a(window.URL.createObjectURL(letterboxd_import), filename) + let download_text = span("Download import file ==> ") + download_button.download = filename; + log.appendChild(div(addClass(download_text, "info"), download_button)); +} \ No newline at end of file diff --git a/Blog/Components/Pages/NotFound.razor b/Blog/Components/Pages/NotFound.razor new file mode 100644 index 0000000..5278494 --- /dev/null +++ b/Blog/Components/Pages/NotFound.razor @@ -0,0 +1,4 @@ +@page "/not-found" + +

Not Found

+

Sorry, the content you are looking for does not exist.

\ No newline at end of file diff --git a/Blog/Components/Pages/Note.razor b/Blog/Components/Pages/Note.razor new file mode 100644 index 0000000..531adcf --- /dev/null +++ b/Blog/Components/Pages/Note.razor @@ -0,0 +1,8 @@ +@page "/Note" +Note + + +
+ +
+
\ No newline at end of file diff --git a/Blog/Components/Pages/Note.razor.js b/Blog/Components/Pages/Note.razor.js new file mode 100644 index 0000000..3d47d23 --- /dev/null +++ b/Blog/Components/Pages/Note.razor.js @@ -0,0 +1,33 @@ +import { getById, debounce, writeError, resetLog } from "/common.module.js"; +import lzString from "/lz-string.module.js"; + +let input = undefined; +export function onLoad() { + input = getById("input"); + input.addEventListener("input", debounce(() => { + if (input.value === '') { + window.location.hash = '' + } + else { + window.location.hash = '#' + lzString.compressToEncodedURIComponent(input.value); + } + resetLog(); + }, 10)) + + window.addEventListener('hashchange', loadState); + loadState(); +} + +export function onUpdate() { + loadState(); +} + +function loadState() { + if (window.location.hash !== '') { + input.value = lzString.decompressFromEncodedURIComponent(window.location.hash.substring(1)); + if (input.value === '') { + //Hash but no content? + writeError("Failed to load note from url.") + } + } +} \ No newline at end of file diff --git a/Blog/Components/Routes.razor b/Blog/Components/Routes.razor new file mode 100644 index 0000000..9661f49 --- /dev/null +++ b/Blog/Components/Routes.razor @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Blog/Components/_Imports.razor b/Blog/Components/_Imports.razor new file mode 100644 index 0000000..0483514 --- /dev/null +++ b/Blog/Components/_Imports.razor @@ -0,0 +1,11 @@ +@using System.Net.Http +@using System.Net.Http.Json +@using Microsoft.AspNetCore.Components.Forms +@using Microsoft.AspNetCore.Components.Routing +@using Microsoft.AspNetCore.Components.Web +@using static Microsoft.AspNetCore.Components.Web.RenderMode +@using Microsoft.AspNetCore.Components.Web.Virtualization +@using Microsoft.JSInterop +@using Blog +@using Blog.Components +@using Blog.Components._Shared \ No newline at end of file diff --git a/Blog/Components/_Shared/PageScript.razor b/Blog/Components/_Shared/PageScript.razor new file mode 100644 index 0000000..5776866 --- /dev/null +++ b/Blog/Components/_Shared/PageScript.razor @@ -0,0 +1,7 @@ + + +@code { + [Parameter] + [EditorRequired] + public string Src { get; set; } = null!; +} \ No newline at end of file diff --git a/Blog/Program.cs b/Blog/Program.cs new file mode 100644 index 0000000..0d38e90 --- /dev/null +++ b/Blog/Program.cs @@ -0,0 +1,28 @@ +using Blog.Components; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. +builder.Services.AddRazorComponents(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (!app.Environment.IsDevelopment()) +{ + app.UseExceptionHandler("/Error", createScopeForErrors: true); + // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. + app.UseHsts(); +} + +app.UseHttpsRedirection(); + +app.UseAntiforgery(); + +app.MapStaticAssets(); +app.MapRazorComponents(); + +app.Run(); \ No newline at end of file diff --git a/Blog/Properties/launchSettings.json b/Blog/Properties/launchSettings.json new file mode 100644 index 0000000..da8485d --- /dev/null +++ b/Blog/Properties/launchSettings.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:5296", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:7014;http://localhost:5296", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } + } diff --git a/Blog/appsettings.Development.json b/Blog/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/Blog/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/Blog/appsettings.json b/Blog/appsettings.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/Blog/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} 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