Support alternative b64 characters

Coincidentally fixes #2 and fixes #7.
This commit is contained in:
Florine W. Dekker 2020-04-05 13:38:38 +02:00
parent 353937e3a1
commit 0e148a48ef
Signed by: FWDekker
GPG Key ID: B1B567AF58D6EE0F
1 changed files with 56 additions and 16 deletions

View File

@ -55,7 +55,7 @@
<a href="https://git.fwdekker.com/FWDekker/converter/src/branch/master/LICENSE">MIT License</a>.
Source code available on <a href="https://git.fwdekker.com/FWDekker/converter/">git</a>.
<div style="float: right;">v1.4.4</div>
<div style="float: right;">v1.4.5</div>
</section>
</footer>
</main>
@ -66,11 +66,40 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/big-integer/1.6.44/BigInteger.min.js"
integrity="sha256-es+ex6Oj344uak+VnCPyaHY2nzQkqhr7ByWVQgdjATA=" crossorigin="anonymous"></script>
<script>
const stringReplaceAt =
(str, index, replacement) => str.substr(0, index) + replacement + str.substr(index + replacement.length);
/**
* Replaces the character at the given index with the given replacement.
*
* @param str the string to replace in
* @param index the index in the given string to replace at
* @param replacement the replacement to insert into the string
* @returns {string} the input string with one character replaced
*/
const stringReplaceAt = (str, index, replacement) =>
str.substr(0, index) + replacement + str.substr(index + replacement.length);
const stringReplaceAll =
(str, target, replacement) => str.split(target).join(replacement);
/**
* Replaces all instances of the target with the replacement.
*
* @param str the string to replace in
* @param target the character to replace
* @param replacement the replacement to insert into the string
* @returns {string} the input string with all instances of the target replaced
*/
const stringReplaceAll = (str, target, replacement) =>
str.split(target).join(replacement);
/**
* Runs `stringReplaceAll` for each character in `targets` and `replacements`.
*
* @param str the string to replace in
* @param targets the characters to replace
* @param replacements the replacements to insert into the string; each character here corresponds to a character
* in the targets string
* @returns {string} the input string with all instances of the targets replaced
*/
const stringReplaceAlls = (str, targets, replacements) =>
Array.from(targets).reduce((output, target, index) =>
stringReplaceAll(output, target, replacements[index]), str);
class NumeralSystem {
@ -117,7 +146,7 @@
return;
this.textarea.value = this.numeralSystem.filterBaseString(this.textarea.value);
updateAllInputs(this.numeralSystem.baseToDecimal(this.textarea.value));
updateAllInputs(this, this.numeralSystem.baseToDecimal(this.textarea.value));
};
}
@ -133,6 +162,17 @@
}
class Base64NumeralSystem extends NumeralSystem {
// TODO Convert static methods to static properties once supported by Firefox
static defaultAlphabet() {
return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
}
/**
* Constructs a new base 64 numeral system.
*
* @param alphabet the 64 characters to encode numbers with, and the padding character at the end
*/
constructor(alphabet) {
super(64, alphabet, true);
}
@ -148,11 +188,14 @@
.map(pair => String.fromCharCode(parseInt(pair.join(""), 16)))
.join("");
return btoa(b64);
return stringReplaceAlls(btoa(b64), Base64NumeralSystem.defaultAlphabet(), this.alphabet);
}
baseToDecimal(baseString) {
const hex = Array.from(atob(baseString))
if (baseString.length % 4 === 1) throw new Error("Invalid input string length.");
const normalBaseString = stringReplaceAlls(baseString, this.alphabet, Base64NumeralSystem.defaultAlphabet());
const hex = Array.from(atob(normalBaseString))
.map(char => char.charCodeAt(0).toString(16).padStart(2, "0")).join("");
return bigInt(hex, 16);
}
@ -160,17 +203,13 @@
class Base64NumeralSystemInput extends NumeralSystemInput {
// TODO Convert static methods to static properties once supported by Firefox
static defaultAlphabet() {
return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
}
static dropdownOptions() {
return {"Standard": ['+', '/'], "Filename": ['-', '_'], "IMAP": ['+', ',']};
}
constructor(name) {
super(name, new Base64NumeralSystem(Base64NumeralSystemInput.defaultAlphabet()));
super(name, new Base64NumeralSystem(Base64NumeralSystem.defaultAlphabet()));
this.dropdownLabel = document.createElement("label");
this.dropdownLabel.setAttribute("for", `${this.name}Dropdown`);
@ -236,8 +275,9 @@
),
];
const updateAllInputs = newValue => {
const updateAllInputs = (source, newValue) => {
for (const input of inputs)
if (input !== source)
input.update(newValue);
};
@ -248,7 +288,7 @@
for (const input of inputs)
input.addToParent(inputParent);
updateAllInputs(bigInt(42));
updateAllInputs(undefined, bigInt(42));
inputs[0].textarea.focus();
});
</script>