Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
aiden2480 committed May 30, 2022
2 parents 526875c + 65fa3e7 commit 39ed170
Show file tree
Hide file tree
Showing 5 changed files with 230 additions and 16 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,5 +98,5 @@ $ curl -L http://kanjithing-backend.chocolatejade42.repl.co/kanji/車
- [ ] Fix overlap interaction with especially long word descriptions (同 kanji)
- [ ] Use a RapidAPI key in the application to fetch data (Replit downtime)
- [ ] Unspaghettify everything
- [ ] Instead of shifting each character over one, convert it to binary instead
- [x] Instead of shifting each character over one, convert it to binary instead
- Ticks off the "syllabus components" requirement
216 changes: 216 additions & 0 deletions js/binary.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
/**
* Encodes a string to binary. This repeatedly calls
* the helper function convertCharToBinary
*
* @param {String} string The string to be converted
* @returns Binary encoded string
*/
export function encodeStringToBinary(string) {
var encoded = string.match(/./gu).map(char => {
return convertCharToBinary(char);
});

// Add a space after every 8th character
return encoded.join(" ");
}

/**
* Decodes a binary string back to a regular string
*
* @param {BinaryEncodedString} binary A string encoded in binary
* @returns The decoded string as an object
*/
export function decodeBinaryToString(binary) {
var input = binary.split(" ");
var output = [];

// Construct a 2D array of bytes, where each array
// holds each byte required for a character
input.forEach(byte => {
if (byte.startsWith(1) && !byte.startsWith(11)) {
output.at(-1).push(byte);
} else {
output.push([ byte ]);
}
});

// Concatenate each byte back to a string
output = output.map(item => {
const binary2string = x => String.fromCharCode(convertBinaryToDec(x));

// Treat the special case of one byte
if (item.length == 1) return binary2string(item[0]);

// Remove the prestring that tells us how many bytes are coming
var decoded = item[0].slice(item.length);

// Remove the prepadding of 10 from every byte afterwards and append
item.slice(1).forEach(item => {
decoded += item.slice(2);
});

return binary2string(decoded);
}).join("");

return output;
}

/**
* Converts binary text to hexadecimal
*
* @param {BinaryEncodedString} binary The string to be encoded to hex
* @returns The hexadecimal representation of the binary string
*/
export function encodeBinaryToHex(binary) {
var input = binary.replaceAll(" ", "").match(/.{4}/g);
var conv = {
10: "A", 11: "B", 12: "C",
13: "D", 14: "E", 15: "F",
}

var output = input.map(nibble => {
var decimal = convertBinaryToDec(nibble);
var char = conv[decimal] || decimal;

return char;
});

return output.join("");
}

/**
* Decodes a hexadecimal string back to its binary representation
*
* @param {HexEncodedString} hex The string to be decoded to binary
* @returns Decoded binary string, seperated with a space every 8 chars
*/
export function decodeHexToBinary(hex) {
var conv = {
"A": 10, "B": 11, "C": 12,
"D": 13, "E": 14, "F": 15,
}

var output = Array.from(hex).map(item => {
var char = conv[item] || item;

return convertDecToBinary(char).padStart(4, "0");
});

return output.join("").match(/.{8}/g).join(" ");
}

/**
* Converts a single character to its binary
* equivalent
*
* @param {Char} char A single UTF8 character
* @returns The binary representation of that char
*/
function convertCharToBinary(char) {
var code = char.codePointAt(); // Lookup the UTF8 code
var bits = convertDecToBinary(code);

// Normally, we would take out any left padded
// zeros, but they don't occur in the way I have
// written the convertNumToBinary algorithm
var numOfBytes = calcNumOfBytes(bits.length);

// If it's just one byte, we're going to handle it
// as a special case here because it's much easier
if (numOfBytes == 1) {
return bits.padStart(8, "0");
}

// Generate our 2D array, which contains x number of bytes.
// Each byte is an array with length 8
var conv = [];

// Prepad first byte with a one for every byte used for
// the character, and then a zero
conv.push([
...Array(numOfBytes).fill(1), // A one for every byte
0, // And then a zero
...Array(8 - (numOfBytes + 1)).fill(null) // The rest are null
]);

// For each remaining byte, prepad each byte with 10
for (let i = 0; i < numOfBytes - 1; i++) {
conv.push([1, 0, ...Array(6).fill(null)]);
}

// It's easier to now flatten this back into a 1D array
// to paste in the bits
conv = conv.flat();

// Find index of last null, paste in a bit, and then
// remove that bit from the to-be-pasted bits
while (bits.length > 0) {
var index = conv.lastIndexOf(null);
conv[index] = parseInt(bits.at(-1));

bits = bits.slice(0, -1);
}

// Replace any remaining spaces with zeros and return
while (conv.indexOf(null) !== -1) {
conv[conv.indexOf(null)] = 0;
}

return conv.join("");
}

/**
* Converts a decimal number to binary by continuously
* dividing it by two and flooring the remainder.
*
* @param {Number} num A decimal number
* @returns Its binary equivalent
*/
function convertDecToBinary(num) {
var concat = [];

while (num > 0) {
concat.push(num % 2);
num = Math.floor(num / 2);
}

return concat.reverse().join("");
}

/**
* Converts a binary encoded decimal back to a decimal
*
* @param {BinaryEncodedString} bin A decimal integer in binary
* @returns Integer
*/
function convertBinaryToDec(bin) {
var nums = bin.split("").map((item, index) => {
let power = bin.length - index - 1;

return parseInt(item) * 2 ** power;
});

return nums.reduce((a, b) => a + b);
}

/**
* Calculates how many bytes are required to represent the
* number of bits inputted. Support only added for up to four
* bytes as necessary for UTF8/unicode encoding
*
* @param {Number} numOfBits The number of bits in the string
* @returns The number of bytes required to store those bits
*/
function calcNumOfBytes(numOfBits) {
var opts = [7, 11, 16, 21];
var indx = opts.find(x => numOfBits <= x);
var byte = opts.indexOf(indx) + 1;

// | Ben's formula | bits <= 7 | 1 byte |
// |---------------|------------|---------|
// | 2 x 5 + 1 | bits <= 11 | 2 bytes |
// | 3 x 5 + 1 | bits <= 16 | 3 bytes |
// | 4 x 5 + 1 | bits <= 21 | 4 bytes |

return byte;
}
24 changes: 12 additions & 12 deletions js/settings.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
import {
encodeStringToBinary, decodeBinaryToString,
encodeBinaryToHex, decodeHexToBinary,
} from "./binary.js";

const KANJI_REGEX = /^[\u4E00-\u9FAF]+$/;
generateSettingsPage();

Expand Down Expand Up @@ -241,17 +246,19 @@ window.addEventListener("beforeunload", event => {

document.getElementById("export").addEventListener("click", async () => {
var sets = (await chrome.storage.local.get("customsets")).customsets;
var encoded = transform(JSON.stringify(sets), 1);
var encoded = encodeBinaryToHex(encodeStringToBinary(JSON.stringify(sets)));

prompt("Your exported configuration: (Right click to copy)", encoded);
navigator.clipboard.writeText(encoded);
alert("Your exported configuration has been copied to your clipboard in hex format");
});

document.getElementById("import").addEventListener("click", async () => {
try {
var encoded = prompt("Paste in your configuration string below");
var decoded = JSON.parse(transform(encoded, -1));
var decoded = JSON.parse(decodeBinaryToString(decodeHexToBinary(encoded)));
} catch (error) {
return alert("Invalid configuration string. Couldn't import configuration");
alert("Invalid configuration string. Couldn't import configuration");
return console.error("Error while attempting to parse import string: " + encoded + "\n\n" + error);
}

await chrome.storage.local.set({ customsets: decoded });
Expand All @@ -271,11 +278,4 @@ document.getElementById("showsettings").addEventListener("change", async (event)
document.getElementById("videoslider").addEventListener("change", async (event) => {
var videoSpeed = event.target.value / 100;
chrome.storage.local.set({ videoSpeed });
})

// Helper functions
function transform(text, num=1) {
return text.split("").map(char => {
return String.fromCharCode(char.charCodeAt(0) + num);
}).join("");
}
});
2 changes: 0 additions & 2 deletions js/utilities.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
export const KANJI_REGEX = /^[\u4E00-\u9FAF]+$/;

function getLastFrameOfVideo(url) {
// Take in a video URL and get the last frame from that video.
// Used to compare video to canvas drawing via Rembrandt.
Expand Down
2 changes: 1 addition & 1 deletion settings.html
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,6 @@ <h2>Other configuration</h2>
</table>
</div>

<script src="js/settings.js"></script>
<script src="js/settings.js" type="module"></script>
</body>
</html>

0 comments on commit 39ed170

Please sign in to comment.