From bdb8c02d5a05259f5c4dd41117e7f4e32b1681a7 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Fri, 21 Oct 2022 18:29:52 +0100 Subject: [PATCH] Input and Output encodings are now saved per tab --- src/web/App.mjs | 42 ++++++++++++++++++--------- src/web/Manager.mjs | 17 +++++++++++ src/web/utils/statusBar.mjs | 2 +- src/web/waiters/ControlsWaiter.mjs | 4 +-- src/web/waiters/HighlighterWaiter.mjs | 4 +-- src/web/waiters/InputWaiter.mjs | 13 +++++---- src/web/waiters/OutputWaiter.mjs | 28 +++++++++++++----- src/web/waiters/WorkerWaiter.mjs | 2 +- src/web/workers/InputWorker.mjs | 8 ++++- 9 files changed, 87 insertions(+), 33 deletions(-) diff --git a/src/web/App.mjs b/src/web/App.mjs index e71ad07f..2ea55708 100755 --- a/src/web/App.mjs +++ b/src/web/App.mjs @@ -11,6 +11,7 @@ import HTMLCategory from "./HTMLCategory.mjs"; import HTMLOperation from "./HTMLOperation.mjs"; import Split from "split.js"; import moment from "moment-timezone"; +import cptable from "codepage"; /** @@ -41,6 +42,10 @@ class App { this.autoBakePause = false; this.progress = 0; this.ingId = 0; + + this.appLoaded = false; + this.workerLoaded = false; + this.waitersLoaded = false; } @@ -59,11 +64,10 @@ class App { this.manager.output.saveBombe(); this.adjustComponentSizes(); this.setCompileMessage(); + this.uriParams = this.getURIParams(); log.debug("App loaded"); this.appLoaded = true; - - this.loadURIParams(); this.loaded(); } @@ -76,9 +80,12 @@ class App { loaded() { // Check that both the app and the worker have loaded successfully, and that // we haven't already loaded before attempting to remove the loading screen. - if (!this.workerLoaded || !this.appLoaded || + if (!this.workerLoaded || !this.appLoaded || !this.waitersLoaded || !document.getElementById("loader-wrapper")) return; + // Load state from URI + this.loadURIParams(this.uriParams); + // Trigger CSS animations to remove preloader document.body.classList.add("loaded"); @@ -454,11 +461,12 @@ class App { * If character encodings are present, sets them appropriately. * If theme is present, uses the theme. * + * @param {Object} params * @fires Manager#statechange */ - loadURIParams() { + loadURIParams(params=this.getURIParams()) { this.autoBakePause = true; - this.uriParams = this.getURIParams(); + this.uriParams = params; // Read in recipe from URI params if (this.uriParams.recipe) { @@ -483,15 +491,8 @@ class App { search.dispatchEvent(new Event("search")); } - // Read in input data from URI params - if (this.uriParams.input) { - try { - const inputData = fromBase64(this.uriParams.input); - this.setInput(inputData); - } catch (err) {} - } - // Input Character Encoding + // Must be set before the input is loaded if (this.uriParams.ienc) { this.manager.input.chrEncChange(parseInt(this.uriParams.ienc, 10)); } @@ -501,6 +502,21 @@ class App { this.manager.output.chrEncChange(parseInt(this.uriParams.oenc, 10)); } + // Read in input data from URI params + if (this.uriParams.input) { + try { + let inputVal; + const inputChrEnc = this.manager.input.getChrEnc(); + const inputData = fromBase64(this.uriParams.input); + if (inputChrEnc > 0) { + inputVal= cptable.utils.decode(inputChrEnc, inputData); + } else { + inputVal = Utils.arrayBufferToStr(inputData); + } + this.setInput(inputVal); + } catch (err) {} + } + // Read in theme from URI params if (this.uriParams.theme) { this.manager.options.changeTheme(Utils.escapeHtml(this.uriParams.theme)); diff --git a/src/web/Manager.mjs b/src/web/Manager.mjs index 730d6e2e..319110f4 100755 --- a/src/web/Manager.mjs +++ b/src/web/Manager.mjs @@ -93,6 +93,23 @@ class Manager { this.bindings.updateKeybList(); this.background.registerChefWorker(); this.seasonal.load(); + + this.confirmWaitersLoaded(); + } + + /** + * Confirms that all Waiters have loaded correctly. + */ + confirmWaitersLoaded() { + if (this.tabs.getActiveTab("input") >= 0 && + this.tabs.getActiveTab("output") >= 0) { + log.debug("Waiters loaded"); + this.app.waitersLoaded = true; + this.app.loaded(); + } else { + // Not loaded yet, try again soon + setTimeout(this.confirmWaitersLoaded.bind(this), 10); + } } diff --git a/src/web/utils/statusBar.mjs b/src/web/utils/statusBar.mjs index 97c6cff1..02c61de0 100644 --- a/src/web/utils/statusBar.mjs +++ b/src/web/utils/statusBar.mjs @@ -222,7 +222,7 @@ class StatusBarPanel { /** - * Gets the current character encoding of the document + * Sets the current character encoding of the document */ updateCharEnc() { const chrEncVal = this.chrEncGetter(); diff --git a/src/web/waiters/ControlsWaiter.mjs b/src/web/waiters/ControlsWaiter.mjs index f0a87d6f..fe2e9f9f 100755 --- a/src/web/waiters/ControlsWaiter.mjs +++ b/src/web/waiters/ControlsWaiter.mjs @@ -138,8 +138,8 @@ class ControlsWaiter { } } - const inputChrEnc = this.manager.input.inputChrEnc; - const outputChrEnc = this.manager.output.outputChrEnc; + const inputChrEnc = this.manager.input.getChrEnc(); + const outputChrEnc = this.manager.output.getChrEnc(); const params = [ includeRecipe ? ["recipe", recipeStr] : undefined, diff --git a/src/web/waiters/HighlighterWaiter.mjs b/src/web/waiters/HighlighterWaiter.mjs index 998454ed..9275d7aa 100755 --- a/src/web/waiters/HighlighterWaiter.mjs +++ b/src/web/waiters/HighlighterWaiter.mjs @@ -51,8 +51,8 @@ class HighlighterWaiter { const selectionRanges = e.state.selection.ranges; // Adjust offsets based on the width of the character set - const inputCharacterWidth = chrEncWidth(this.manager.input.inputChrEnc); - const outputCharacterWidth = chrEncWidth(this.manager.output.outputChrEnc); + const inputCharacterWidth = chrEncWidth(this.manager.input.getChrEnc()); + const outputCharacterWidth = chrEncWidth(this.manager.output.getChrEnc()); let ratio = 1; if (inputCharacterWidth !== outputCharacterWidth && inputCharacterWidth !== 0 && outputCharacterWidth !== 0) { diff --git a/src/web/waiters/InputWaiter.mjs b/src/web/waiters/InputWaiter.mjs index f6f900c5..a9a86b75 100644 --- a/src/web/waiters/InputWaiter.mjs +++ b/src/web/waiters/InputWaiter.mjs @@ -469,6 +469,7 @@ class InputWaiter { * @param {string} file.type * @param {string} status * @param {number} progress + * @param {number} encoding * @param {boolean} [silent=false] - If false, fires the manager statechange event */ async set(inputNum, inputData, silent=false) { @@ -476,13 +477,14 @@ class InputWaiter { const activeTab = this.manager.tabs.getActiveTab("input"); if (inputNum !== activeTab) return; + this.inputChrEnc = inputData.encoding; + if (inputData.file) { this.setFile(inputNum, inputData); } else { this.clearFile(inputNum); } - // TODO Per-tab encodings? let inputVal; if (this.inputChrEnc > 0) { inputVal = cptable.utils.decode(this.inputChrEnc, new Uint8Array(inputData.buffer)); @@ -609,8 +611,8 @@ class InputWaiter { // If value is a string, interpret it using the specified character encoding if (typeof value === "string") { stringSample = value.slice(0, 4096); - if (this.inputChrEnc > 0) { - buffer = cptable.utils.encode(this.inputChrEnc, value); + if (this.getChrEnc() > 0) { + buffer = cptable.utils.encode(this.getChrEnc(), value); buffer = new Uint8Array(buffer).buffer; } else { buffer = Utils.strToArrayBuffer(value); @@ -631,7 +633,8 @@ class InputWaiter { data: { inputNum: inputNum, buffer: buffer, - stringSample: stringSample + stringSample: stringSample, + encoding: this.getChrEnc() } }, transferable); } @@ -924,7 +927,7 @@ class InputWaiter { * @param {number} inputNum - The inputNum of the tab to change to * @param {boolean} [changeOutput=false] - If true, also changes the output */ - changeTab(inputNum, changeOutput) { + changeTab(inputNum, changeOutput=false) { if (this.manager.tabs.getTabItem(inputNum, "input") !== null) { this.manager.tabs.changeTab(inputNum, "input"); this.inputWorker.postMessage({ diff --git a/src/web/waiters/OutputWaiter.mjs b/src/web/waiters/OutputWaiter.mjs index 90088c42..57eede3c 100755 --- a/src/web/waiters/OutputWaiter.mjs +++ b/src/web/waiters/OutputWaiter.mjs @@ -51,7 +51,6 @@ class OutputWaiter { }; // Hold a copy of the currently displayed output so that we don't have to update it unnecessarily this.currentOutputCache = null; - this.outputChrEnc = 0; this.initEditor(); this.outputs = {}; @@ -146,7 +145,14 @@ class OutputWaiter { */ chrEncChange(chrEncVal) { if (typeof chrEncVal !== "number") return; - this.outputChrEnc = chrEncVal; + + const currentTabNum = this.manager.tabs.getActiveTab("output"); + if (currentTabNum >= 0) { + this.outputs[currentTabNum].encoding = chrEncVal; + } else { + throw new Error("Cannot change output chrEnc to " + chrEncVal); + } + // Reset the output, forcing it to re-decode the data with the new character encoding this.setOutput(this.currentOutputCache, true); // Update the URL manually since we aren't firing a statechange event @@ -154,11 +160,15 @@ class OutputWaiter { } /** - * Getter for the input character encoding + * Getter for the output character encoding * @returns {number} */ getChrEnc() { - return this.outputChrEnc; + const currentTabNum = this.manager.tabs.getActiveTab("output"); + if (currentTabNum < 0) { + return 0; + } + return this.outputs[currentTabNum].encoding; } /** @@ -195,11 +205,12 @@ class OutputWaiter { // If data is an ArrayBuffer, convert to a string in the correct character encoding if (data instanceof ArrayBuffer) { - if (this.outputChrEnc === 0) { + const encoding = this.getChrEnc(); + if (encoding === 0) { data = Utils.arrayBufferToStr(data); } else { try { - data = cptable.utils.decode(this.outputChrEnc, new Uint8Array(data)); + data = cptable.utils.decode(encoding, new Uint8Array(data)); } catch (err) { data = err; } @@ -324,7 +335,8 @@ class OutputWaiter { error: null, status: "inactive", bakeId: -1, - progress: false + progress: false, + encoding: 0 }; this.outputs[inputNum] = newOutput; @@ -851,7 +863,7 @@ class OutputWaiter { if (!this.manager.tabs.getTabItem(inputNum, "output") && numTabs < this.maxTabs) { // Create a new tab element - const newTab = this.manager.tabs.reateTabElement(inputNum, changeTab, "output"); + const newTab = this.manager.tabs.createTabElement(inputNum, changeTab, "output"); tabsWrapper.appendChild(newTab); } else if (numTabs === this.maxTabs) { // Can't create a new tab diff --git a/src/web/waiters/WorkerWaiter.mjs b/src/web/waiters/WorkerWaiter.mjs index 0857912e..518c31c1 100644 --- a/src/web/waiters/WorkerWaiter.mjs +++ b/src/web/waiters/WorkerWaiter.mjs @@ -217,7 +217,7 @@ class WorkerWaiter { break; case "workerLoaded": this.app.workerLoaded = true; - log.debug("ChefWorker loaded."); + log.debug("ChefWorker loaded"); if (!this.loaded) { this.app.loaded(); this.loaded = true; diff --git a/src/web/workers/InputWorker.mjs b/src/web/workers/InputWorker.mjs index e1c75de9..aecc7a17 100644 --- a/src/web/workers/InputWorker.mjs +++ b/src/web/workers/InputWorker.mjs @@ -30,6 +30,7 @@ self.pendingFiles = []; * @property {string} file.type * @property {string} status * @property {number} progress + * @property {number} encoding */ self.inputs = {}; self.loaderWorkers = []; @@ -512,6 +513,7 @@ self.updateInputProgress = function(inputData) { * @param {object} inputData * @param {number} inputData.inputNum - The input that's having its value updated * @param {ArrayBuffer} inputData.buffer - The new value of the input as a buffer + * @param {number} [inputData.encoding] - The character encoding of the input data * @param {string} [inputData.stringSample] - A sample of the value as a string (truncated to 4096 chars) */ self.updateInputValue = function(inputData) { @@ -522,6 +524,9 @@ self.updateInputValue = function(inputData) { throw new Error(`No input with ID ${inputNum} exists`); self.inputs[inputNum].buffer = inputData.buffer; + if ("encoding" in inputData) { + self.inputs[inputNum].encoding = inputData.encoding; + } if (!("stringSample" in inputData)) { inputData.stringSample = Utils.arrayBufferToStr(inputData.buffer.slice(0, 4096)); } @@ -756,7 +761,8 @@ self.addInput = function( stringSample: "", file: null, status: "pending", - progress: 0 + progress: 0, + encoding: 0 }; switch (type) {