Allow toggling of separator of ASCII

This commit is contained in:
Florine W. Dekker 2022-11-23 15:24:19 +01:00
parent 7ec5171adc
commit 567b364a59
Signed by: FWDekker
GPG Key ID: D3DCFAA8A4560BE0
3 changed files with 104 additions and 30 deletions

View File

@ -1,7 +1,6 @@
#inputs { #inputs {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
align-items: start;
justify-content: left; justify-content: left;
/*noinspection CssUnresolvedCustomProperty*/ /*noinspection CssUnresolvedCustomProperty*/
gap: var(--spacing); gap: var(--spacing);
@ -9,10 +8,14 @@
#inputs article { #inputs article {
margin: 0; margin: 0;
/* Make `textarea`s expand automatically */
display: flex;
flex-direction: column;
} }
#inputs textarea { #inputs textarea {
display: block; display: block;
width: 25em; width: 25em;
height: 5em; height: 100%;
} }

View File

@ -14,6 +14,7 @@
<meta name="fwd:footer:target" content="#footer" /> <meta name="fwd:footer:target" content="#footer" />
<meta name="fwd:footer:vcs-url" content="https://git.fwdekker.com/tools/converter/" /> <meta name="fwd:footer:vcs-url" content="https://git.fwdekker.com/tools/converter/" />
<meta name="fwd:footer:version" content="v%%VERSION_NUMBER%%" /> <meta name="fwd:footer:version" content="v%%VERSION_NUMBER%%" />
<meta name="fwd:validation:load-forms" />
<title>Converter | FWDekker</title> <title>Converter | FWDekker</title>

View File

@ -1,5 +1,7 @@
// noinspection JSUnresolvedVariable // noinspection JSUnresolvedVariable
const {$, doAfterLoad, stringToHtml} = window.fwdekker; const {$, doAfterLoad, stringToHtml} = window.fwdekker;
// noinspection JSUnresolvedVariable
const {clearInputValidity, showInputInvalid} = window.fwdekker.validation;
import bigInt from "big-integer"; import bigInt from "big-integer";
@ -77,7 +79,7 @@ class NumeralSystem {
* @returns {string[]} the representations of `decimalNumbers` in this system * @returns {string[]} the representations of `decimalNumbers` in this system
*/ */
decimalsToBases(decimalNumbers) { decimalsToBases(decimalNumbers) {
return decimalNumbers.map((it) => this.decimalToBase(it)); return decimalNumbers.map(it => this.decimalToBase(it));
} }
/** /**
@ -97,7 +99,7 @@ class NumeralSystem {
* @returns {bigInt[]} the decimal representations of `baseStrings` * @returns {bigInt[]} the decimal representations of `baseStrings`
*/ */
basesToDecimals(baseStrings) { basesToDecimals(baseStrings) {
return baseStrings.map((it) => this.baseToDecimal(it)); return baseStrings.map(it => this.baseToDecimal(it));
} }
/** /**
@ -130,22 +132,24 @@ class NumeralSystemInput {
constructor(name, numeralSystem) { constructor(name, numeralSystem) {
this.name = name; this.name = name;
this.numeralSystem = numeralSystem; this.numeralSystem = numeralSystem;
const base = this.numeralSystem.base;
this.label = stringToHtml(`<label for="b${base}-input">${this.name} <small>(base ${base})</small></label>`);
this.textarea = stringToHtml(`<textarea id="b${base}-input" class="number-input"></textarea>`);
this.textarea.oninput = () => {
if (this.textarea.value == null || this.textarea.value === "")
return;
this.textarea.value = this.numeralSystem.filterBaseString(this.textarea.value);
updateAllInputs(this, this.numeralSystem.basesToDecimals(this.textarea.value.split(",")));
};
this.wrapper = document.createElement("article"); this.wrapper = document.createElement("article");
this.wrapper.appendChild(this.label); const base = this.numeralSystem.base;
const label = stringToHtml(`<label for="b${base}-input">${this.name} <small>(base ${base})</small></label>`);
this.wrapper.appendChild(label);
this.textarea = stringToHtml(`<textarea id="b${base}-input" class="number-input"></textarea>`);
this.textarea.addEventListener("input", () => {
if (this.textarea.value === "") return;
this.textarea.value = this.numeralSystem.filterBaseString(this.textarea.value);
updateAllInputs(this, this.numeralSystem.basesToDecimals(this.getValues()));
});
this.wrapper.appendChild(this.textarea); this.wrapper.appendChild(this.textarea);
const hint = stringToHtml(`<small id="b${base}-input-hint" data-hint-for="b${base}-input"></small>`);
this.wrapper.appendChild(hint);
} }
@ -158,12 +162,21 @@ class NumeralSystemInput {
parent.appendChild(this.wrapper); parent.appendChild(this.wrapper);
} }
/**
* Returns the input's values.
*
* @returns {string[]} the input's values expressed in the associated numeral system
*/
getValues() {
return this.textarea.value.split(",");
}
/** /**
* Updates the input's value to contain this input's numeral system's representation of `decimalNumbers`. * Updates the input's value to contain this input's numeral system's representation of `decimalNumbers`.
* *
* @param decimalNumbers {bigInt[]} the decimal numbers to represent in the input * @param decimalNumbers {bigInt[]} the decimal numbers to represent in the input
*/ */
update(decimalNumbers) { setValues(decimalNumbers) {
this.textarea.value = this.numeralSystem.decimalsToBases(decimalNumbers).join(","); this.textarea.value = this.numeralSystem.decimalsToBases(decimalNumbers).join(",");
} }
} }
@ -248,21 +261,20 @@ class Base64NumeralSystemInput extends NumeralSystemInput {
constructor(name) { constructor(name) {
super(name, new Base64NumeralSystem(Base64NumeralSystem.defaultAlphabet())); super(name, new Base64NumeralSystem(Base64NumeralSystem.defaultAlphabet()));
this.dropdown = stringToHtml(`<select id="${this.numeralSystem.base}-dropdown"></select>`); const dropdown = stringToHtml(`<select id="${this.numeralSystem.base}-dropdown"></select>`);
this.dropdown.onchange = () => { dropdown.addEventListener("change", () => {
const selectedOption = Base64NumeralSystemInput.dropdownOptions()[this.dropdown.value]; const selectedOption = Base64NumeralSystemInput.dropdownOptions()[dropdown.value];
this.setLastDigits(selectedOption[0], selectedOption[1]); this.setLastDigits(selectedOption[0], selectedOption[1]);
}; });
this.wrapper.appendChild(dropdown);
Object Object
.keys(Base64NumeralSystemInput.dropdownOptions()) .keys(Base64NumeralSystemInput.dropdownOptions())
.forEach(key => { .map(key => {
const text = key + ": " + Base64NumeralSystemInput.dropdownOptions()[key].join(""); const text = key + ": " + Base64NumeralSystemInput.dropdownOptions()[key].join("");
const option = stringToHtml(`<option value="${key}">${text}</option>`); return stringToHtml(`<option value="${key}">${text}</option>`);
this.dropdown.appendChild(option); })
}); .forEach(option => dropdown.appendChild(option));
this.wrapper.appendChild(this.dropdown);
} }
@ -282,6 +294,64 @@ class Base64NumeralSystemInput extends NumeralSystemInput {
} }
/**
* An input field that contains a value in some numeral system, and can optionally interpret the separator symbol
* literally as part of its alphabet.
*
* Useful for numeral systems with separators in their alphabet. Lets the user decide whether to interpret those values
* as literals or as separators.
*/
class NumeralSystemInputWithToggleableSeparator extends NumeralSystemInput {
/**
* Constructs a new numeral system input, including HTML elements.
*
* @param name {string} the human-readable name of the system
* @param numeralSystem {NumeralSystem} the numeral system in which values in this input are expressed
*/
constructor(name, numeralSystem) {
if (!numeralSystem.alphabet.includes(","))
console.warn("Toggleable separator input incorrectly used on numeral system without comma in alphabet.");
super(name, numeralSystem);
this.textarea.addEventListener("input", () => clearInputValidity(this.textarea));
const id = `b${this.numeralSystem.base}-separator-toggle`;
const checkboxContainer = document.createElement("div");
this.wrapper.appendChild(checkboxContainer);
this.separatorCheckbox = stringToHtml(`<input type="checkbox" role="switch" id="${id}" checked />`);
this.separatorCheckbox.addEventListener("change", () => this.textarea.dispatchEvent(new InputEvent("input")));
checkboxContainer.appendChild(this.separatorCheckbox);
const label = stringToHtml(`<label for="${id}">Comma separates values</label>`);
checkboxContainer.appendChild(label);
}
getValues() {
if (this.separatorCheckbox.checked)
return super.getValues();
else
return [this.textarea.value];
}
setValues(decimalNumbers) {
clearInputValidity(this.textarea);
const baseStrings = this.numeralSystem.decimalsToBases(decimalNumbers);
if (baseStrings.length === 1) {
this.separatorCheckbox.checked = !baseStrings[0].includes(",");
this.textarea.value = baseStrings[0];
} else {
if (baseStrings.some(it => it.includes(",")))
showInputInvalid(this.textarea, "Contains a mixture of literal commas and separator commas.", false);
this.textarea.value = baseStrings.join(",");
}
}
}
/** /**
* All the inputs to display. * All the inputs to display.
* *
@ -294,7 +364,7 @@ const inputs = [
new NumeralSystemInput("Duodecimal", new NumeralSystem(12, "0123456789ab", false)), new NumeralSystemInput("Duodecimal", new NumeralSystem(12, "0123456789ab", false)),
new NumeralSystemInput("Hexadecimal", new NumeralSystem(16, "0123456789abcdef", false)), new NumeralSystemInput("Hexadecimal", new NumeralSystem(16, "0123456789abcdef", false)),
new Base64NumeralSystemInput("Base64"), new Base64NumeralSystemInput("Base64"),
new NumeralSystemInput( new NumeralSystemInputWithToggleableSeparator(
"ASCII", "ASCII",
new NumeralSystem( new NumeralSystem(
256, 256,
@ -313,7 +383,7 @@ const inputs = [
function updateAllInputs(source, newDecimalValues) { function updateAllInputs(source, newDecimalValues) {
for (const input of inputs) for (const input of inputs)
if (input !== source) if (input !== source)
input.update(newDecimalValues); input.setValues(newDecimalValues);
} }