diff --git a/backup/.htaccess b/backup/.htaccess
new file mode 100644
index 0000000..50726d0
--- /dev/null
+++ b/backup/.htaccess
@@ -0,0 +1,3 @@
+RewriteEngine on
+RewriteCond %{REQUEST_URI} \.bin$ [NC]
+RewriteRule ^(.*\.bin)$ /busanzeiger-webinterface/src/show.php [L,QSA]
diff --git a/backup/active.txt b/backup/active.txt
new file mode 100644
index 0000000..7c5c8de
--- /dev/null
+++ b/backup/active.txt
@@ -0,0 +1 @@
+Schildkroete.bin
\ No newline at end of file
diff --git a/backup/alphabet-extended.js b/backup/alphabet-extended.js
new file mode 100644
index 0000000..ccad0cc
--- /dev/null
+++ b/backup/alphabet-extended.js
@@ -0,0 +1,5097 @@
+const FONT_5x7 = {
+ "0": [
+ " ### ",
+ "# ##",
+ "# # #",
+ "## #",
+ "# #",
+ " ### ",
+ " "
+ ],
+ "1": [
+ "# ",
+ "# ",
+ "# ",
+ "# ",
+ "# ",
+ "# ",
+ " "
+ ],
+ "2": [
+ " ### ",
+ "# #",
+ " # ",
+ " # ",
+ " # ",
+ "#####",
+ " "
+ ],
+ "3": [
+ "#### ",
+ " #",
+ " ####",
+ " #",
+ " #",
+ "#### ",
+ " "
+ ],
+ "4": [
+ "# # ",
+ "# # ",
+ "# # ",
+ "#### ",
+ " # ",
+ " # ",
+ " "
+ ],
+ "5": [
+ "#### ",
+ "# ",
+ "#### ",
+ " # ",
+ " # ",
+ "#### ",
+ " "
+ ],
+ "6": [
+ "# ",
+ "# ",
+ "#### ",
+ "# # ",
+ "# # ",
+ "#### ",
+ " "
+ ],
+ "7": [
+ "#### ",
+ " # ",
+ " # ",
+ " # ",
+ " # ",
+ " # ",
+ " "
+ ],
+ "8": [
+ " ### ",
+ "# #",
+ " ### ",
+ "# #",
+ "# #",
+ " ### ",
+ " "
+ ],
+ "9": [
+ "#### ",
+ "# # ",
+ "#### ",
+ " # ",
+ " # ",
+ " # ",
+ " "
+ ],
+ " ": [
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ // Original punctuation
+ "!": [
+ "# ",
+ "# ",
+ "# ",
+ "# ",
+ " ",
+ "# ",
+ " "
+ ],
+ "\"": [
+ "# # ",
+ "# # ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "#": [
+ " # # ",
+ "#####",
+ " # # ",
+ "#####",
+ " # # ",
+ " ",
+ " "
+ ],
+ "$": [
+ " # ",
+ " ####",
+ "# ",
+ " ### ",
+ " #",
+ "#### ",
+ " # "
+ ],
+ "%": [
+ "## #",
+ "## # ",
+ " # ",
+ " # ",
+ "# ##",
+ "# ##",
+ " "
+ ],
+ "&": [
+ " ## ",
+ "# # ",
+ " ## ",
+ "# ## ",
+ "# # ",
+ " ## #",
+ " "
+ ],
+ "'": [
+ " # ",
+ " # ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "(": [
+ " # ",
+ " # ",
+ " # ",
+ " # ",
+ " # ",
+ " # ",
+ " "
+ ],
+ ")": [
+ " # ",
+ " # ",
+ " # ",
+ " # ",
+ " # ",
+ " # ",
+ " "
+ ],
+ "*": [
+ " ",
+ " # # ",
+ " # ",
+ " # # ",
+ " ",
+ " ",
+ " "
+ ],
+ "+": [
+ " ",
+ " ",
+ " # ",
+ " ### ",
+ " # ",
+ " ",
+ " "
+ ],
+ ",": [
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " # ",
+ "# "
+ ],
+ "-": [
+ " ",
+ " ",
+ " ",
+ " ### ",
+ " ",
+ " ",
+ " "
+ ],
+ ".": [
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ "# ",
+ " "
+ ],
+ "/": [
+ " ",
+ " #",
+ " # ",
+ " # ",
+ " # ",
+ "# ",
+ " "
+ ],
+ ":": [
+ " ",
+ " # ",
+ " ",
+ " ",
+ " # ",
+ " ",
+ " "
+ ],
+ ";": [
+ " ",
+ " ",
+ " # ",
+ " ",
+ " ",
+ " # ",
+ "# "
+ ],
+ "<": [
+ " ",
+ " # ",
+ " # ",
+ " # ",
+ " # ",
+ " # ",
+ " "
+ ],
+ "=": [
+ " ",
+ " ",
+ " ### ",
+ " ",
+ " ### ",
+ " ",
+ " "
+ ],
+ ">": [
+ " ",
+ " # ",
+ " # ",
+ " # ",
+ " # ",
+ " # ",
+ " "
+ ],
+ "?": [
+ " ## ",
+ "# # ",
+ " # ",
+ " # ",
+ " ",
+ " # ",
+ " "
+ ],
+ "@": [
+ " ### ",
+ "# #",
+ "# ###",
+ "# # #",
+ "# ###",
+ "# ",
+ " ####"
+ ],
+ "A": [
+ " # ",
+ " # # ",
+ "# #",
+ "#####",
+ "# #",
+ "# #",
+ " "
+ ],
+ "B": [
+ "#### ",
+ "# #",
+ "#### ",
+ "# #",
+ "# #",
+ "#### ",
+ " "
+ ],
+ "C": [
+ " ####",
+ "# ",
+ "# ",
+ "# ",
+ "# ",
+ " ####",
+ " "
+ ],
+ "D": [
+ "#### ",
+ "# #",
+ "# #",
+ "# #",
+ "# #",
+ "#### ",
+ " "
+ ],
+ "E": [
+ "#### ",
+ "# ",
+ "### ",
+ "# ",
+ "# ",
+ "#### ",
+ " "
+ ],
+ "F": [
+ "#### ",
+ "# ",
+ "# ",
+ "### ",
+ "# ",
+ "# ",
+ " "
+ ],
+ "G": [
+ " ### ",
+ "# ",
+ "# ",
+ "# ###",
+ "# #",
+ " ####",
+ " "
+ ],
+ "H": [
+ "# #",
+ "# #",
+ "# #",
+ "#####",
+ "# #",
+ "# #",
+ " "
+ ],
+ "I": [
+ "#",
+ "#",
+ "#",
+ "#",
+ "#",
+ "#"
+ ],
+ "J": [
+ " ### ",
+ " # ",
+ " # ",
+ " # ",
+ "# # ",
+ "#### ",
+ " "
+ ],
+ "K": [
+ "# # ",
+ "# # ",
+ "## ",
+ "# # ",
+ "# # ",
+ "# # ",
+ " "
+ ],
+ "L": [
+ "# ",
+ "# ",
+ "# ",
+ "# ",
+ "# ",
+ "#### ",
+ " "
+ ],
+ "M": [
+ "# #",
+ "## ##",
+ "# # #",
+ "# #",
+ "# #",
+ "# #",
+ " "
+ ],
+ "N": [
+ "# #",
+ "## #",
+ "# # #",
+ "# # #",
+ "# ##",
+ "# #",
+ " "
+ ],
+ "O": [
+ " ### ",
+ "# #",
+ "# #",
+ "# #",
+ "# #",
+ " ### ",
+ " "
+ ],
+ "P": [
+ "#### ",
+ "# #",
+ "# #",
+ "#### ",
+ "# ",
+ "# ",
+ " "
+ ],
+ "Q": [
+ " ## ",
+ "# # ",
+ "# # ",
+ "# # ",
+ "# ## ",
+ " ####",
+ " "
+ ],
+ "R": [
+ "#### ",
+ "# #",
+ "# #",
+ "#### ",
+ "# #",
+ "# #",
+ " "
+ ],
+ "S": [
+ " ####",
+ "# ",
+ " ### ",
+ " #",
+ " #",
+ "#### ",
+ " "
+ ],
+ "T": [
+ "#####",
+ " # ",
+ " # ",
+ " # ",
+ " # ",
+ " # ",
+ " "
+ ],
+ "U": [
+ "# # ",
+ "# # ",
+ "# # ",
+ "# # ",
+ "# # ",
+ " ## ",
+ " "
+ ],
+ "V": [
+ "# #",
+ "# #",
+ "# #",
+ " # # ",
+ " # # ",
+ " # ",
+ " "
+ ],
+ "W": [
+ "# #",
+ "# #",
+ "# #",
+ "# #",
+ "# # #",
+ " # # ",
+ " "
+ ],
+ "X": [
+ "# #",
+ " # # ",
+ " # ",
+ " # # ",
+ "# #",
+ "# #",
+ " "
+ ],
+ "Y": [
+ "# #",
+ " # # ",
+ " # ",
+ " # ",
+ " # ",
+ " # ",
+ " "
+ ],
+ "Z": [
+ "#####",
+ " #",
+ " ## ",
+ " # ",
+ "# ",
+ "#####",
+ " "
+ ],
+ "[": [
+ " ### ",
+ " # ",
+ " # ",
+ " # ",
+ " # ",
+ " ### ",
+ " "
+ ],
+ "\\": [
+ " ",
+ "# ",
+ " # ",
+ " # ",
+ " # ",
+ " #",
+ " "
+ ],
+ "]": [
+ " ### ",
+ " # ",
+ " # ",
+ " # ",
+ " # ",
+ " ### ",
+ " "
+ ],
+ "^": [
+ " # ",
+ " # # ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "_": [
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ### ",
+ " "
+ ],
+ "`": [
+ " # ",
+ " # ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ // Lowercase letters
+ "a": [
+ " ",
+ " ",
+ " ### ",
+ "# #",
+ "#####",
+ "# #",
+ " "
+ ],
+ "b": [
+ "# ",
+ "# ",
+ "#### ",
+ "# #",
+ "# #",
+ "#### ",
+ " "
+ ],
+ "c": [
+ " ",
+ " ",
+ " ### ",
+ "# ",
+ "# ",
+ " ### ",
+ " "
+ ],
+ "d": [
+ " #",
+ " #",
+ " ####",
+ "# #",
+ "# #",
+ " ####",
+ " "
+ ],
+ "e": [
+ " ",
+ " ",
+ "#### ",
+ "### ",
+ "# ",
+ "#### ",
+ " "
+ ],
+ "f": [
+ " ##",
+ " # ",
+ " ### ",
+ " # ",
+ " # ",
+ " # ",
+ " "
+ ],
+ "g": [
+ " ",
+ " ",
+ " ####",
+ "# #",
+ " ####",
+ " #",
+ " ### "
+ ],
+ "h": [
+ "# ",
+ "# ",
+ "#### ",
+ "# #",
+ "# #",
+ "# #",
+ " "
+ ],
+ "i": [
+ " # ",
+ " ",
+ "## ",
+ " # ",
+ " # ",
+ "### ",
+ " "
+ ],
+ "j": [
+ " # ",
+ " ",
+ " ## ",
+ " # ",
+ " # ",
+ "# # ",
+ " ## "
+ ],
+ "k": [
+ "# ",
+ "# ",
+ "# # ",
+ "# # ",
+ "## ",
+ "# ## ",
+ " "
+ ],
+ "l": [
+ "## ",
+ " # ",
+ " # ",
+ " # ",
+ " # ",
+ "### ",
+ " "
+ ],
+ "m": [
+ " ",
+ " ",
+ "## # ",
+ "# # #",
+ "# # #",
+ "# #",
+ " "
+ ],
+ "n": [
+ " ",
+ " ",
+ "#### ",
+ "# #",
+ "# #",
+ "# #",
+ " "
+ ],
+ "o": [
+ " ",
+ " ",
+ " ### ",
+ "# #",
+ "# #",
+ " ### ",
+ " "
+ ],
+ "p": [
+ " ",
+ " ",
+ "#### ",
+ "# #",
+ "#### ",
+ "# ",
+ "# "
+ ],
+ "q": [
+ " ",
+ " ",
+ " ####",
+ "# #",
+ " ####",
+ " #",
+ " #"
+ ],
+ "r": [
+ " ",
+ " ",
+ "# ## ",
+ "## ",
+ "# ",
+ "# ",
+ " "
+ ],
+ "s": [
+ " ",
+ " ",
+ " ####",
+ "#### ",
+ " #",
+ "#### ",
+ " "
+ ],
+ "t": [
+ " # ",
+ " # ",
+ "### ",
+ " # ",
+ " # ",
+ " ## ",
+ " "
+ ],
+ "u": [
+ " ",
+ " ",
+ "# #",
+ "# #",
+ "# #",
+ " ####",
+ " "
+ ],
+ "v": [
+ " ",
+ " ",
+ "# #",
+ "# #",
+ " # # ",
+ " # ",
+ " "
+ ],
+ "w": [
+ " ",
+ " ",
+ "# #",
+ "# # #",
+ "# # #",
+ " # # ",
+ " "
+ ],
+ "x": [
+ " ",
+ " ",
+ "# #",
+ " # # ",
+ " # ",
+ " # # ",
+ " "
+ ],
+ "y": [
+ " ",
+ " ",
+ "# #",
+ "# #",
+ " ####",
+ " #",
+ " ### "
+ ],
+ "z": [
+ " ",
+ " ",
+ "#####",
+ " # ",
+ " # ",
+ "#####",
+ " "
+ ],
+ "{": [
+ " ## ",
+ " # ",
+ "## ",
+ " # ",
+ " # ",
+ " ## ",
+ " "
+ ],
+ "|": [
+ " # ",
+ " # ",
+ " # ",
+ " # ",
+ " # ",
+ " # ",
+ " "
+ ],
+ "~": [
+ " ",
+ " ",
+ " # #",
+ "# ## ",
+ " ",
+ " ",
+ " "
+ ],
+ // Latin-1 Supplement (U+00A0 to U+00FF)
+ "¡": [
+ " # ",
+ " ",
+ " # ",
+ " # ",
+ " # ",
+ " # ",
+ " "
+ ],
+ "¢": [
+ " # ",
+ " ### ",
+ "# ",
+ "# ",
+ " ### ",
+ " # ",
+ " "
+ ],
+ "£": [
+ " ## ",
+ " # ",
+ "#### ",
+ " # ",
+ " # ",
+ "#####",
+ " "
+ ],
+ "¤": [
+ " ",
+ "# #",
+ " ### ",
+ " # # ",
+ " ### ",
+ "# #",
+ " "
+ ],
+ "¥": [
+ "# #",
+ " # # ",
+ "#####",
+ " # ",
+ "#####",
+ " # ",
+ " "
+ ],
+ "¦": [
+ " # ",
+ " # ",
+ " ",
+ " # ",
+ " # ",
+ " # ",
+ " "
+ ],
+ "§": [
+ " ### ",
+ "# ",
+ " ## ",
+ "# ## ",
+ " ## ",
+ " #",
+ " ### "
+ ],
+ "¨": [
+ " # # ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "©": [
+ " ### ",
+ "# #",
+ "# # #",
+ "# # #",
+ "# #",
+ " ### ",
+ " "
+ ],
+ "ª": [
+ " ## ",
+ " # ",
+ " ## ",
+ " ",
+ "#### ",
+ " ",
+ " "
+ ],
+ "«": [
+ " ",
+ " ",
+ " # # ",
+ "# #",
+ " # # ",
+ " ",
+ " "
+ ],
+ "¬": [
+ " ",
+ " ",
+ "#####",
+ " #",
+ " #",
+ " ",
+ " "
+ ],
+ "": [
+ " ",
+ " ",
+ " ",
+ " ### ",
+ " ",
+ " ",
+ " "
+ ],
+ "®": [
+ " ### ",
+ "# #",
+ "# # #",
+ "## ##",
+ "# #",
+ " ### ",
+ " "
+ ],
+ "¯": [
+ "#####",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "°": [
+ " ## ",
+ "# # ",
+ " ## ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "±": [
+ " ",
+ " # ",
+ "#####",
+ " # ",
+ "#####",
+ " ",
+ " "
+ ],
+ "²": [
+ " ## ",
+ " # ",
+ " # ",
+ "### ",
+ " ",
+ " ",
+ " "
+ ],
+ "³": [
+ "## ",
+ " # ",
+ "## ",
+ " # ",
+ " ",
+ " ",
+ " "
+ ],
+ "´": [
+ " # ",
+ " # ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "µ": [
+ " ",
+ " ",
+ "# #",
+ "# #",
+ "#### ",
+ "# ",
+ "# "
+ ],
+ "¶": [
+ " ####",
+ "#### ",
+ "#### ",
+ " # # ",
+ " # # ",
+ " # # ",
+ " "
+ ],
+ "·": [
+ " ",
+ " ",
+ " # ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "¸": [
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " # ",
+ " # "
+ ],
+ "¹": [
+ " # ",
+ "## ",
+ " # ",
+ "### ",
+ " ",
+ " ",
+ " "
+ ],
+ "º": [
+ " ## ",
+ "# # ",
+ " ## ",
+ " ",
+ "#### ",
+ " ",
+ " "
+ ],
+ "»": [
+ " ",
+ " ",
+ "# #",
+ " # # ",
+ "# #",
+ " ",
+ " "
+ ],
+ "¼": [
+ "# #",
+ "# # ",
+ " # ",
+ " # # ",
+ "# ##",
+ " # #",
+ " "
+ ],
+ "½": [
+ "# #",
+ "# # ",
+ " ## ",
+ " # #",
+ "# # #",
+ " ###",
+ " "
+ ],
+ "¾": [
+ "## #",
+ " # # ",
+ "## # ",
+ " ## #",
+ "# ##",
+ " # #",
+ " "
+ ],
+ "¿": [
+ " # ",
+ " ",
+ " # ",
+ " # ",
+ "# # ",
+ " ## ",
+ " "
+ ],
+ "À": [
+ " # ",
+ " # ",
+ " # # ",
+ "# #",
+ "#####",
+ "# #",
+ " "
+ ],
+ "Á": [
+ " # ",
+ " # ",
+ " # # ",
+ "# #",
+ "#####",
+ "# #",
+ " "
+ ],
+ "Â": [
+ " # ",
+ " # # ",
+ "# #",
+ "#####",
+ "# #",
+ "# #",
+ " "
+ ],
+ "Ã": [
+ " # # ",
+ " # ",
+ " # # ",
+ "# #",
+ "#####",
+ "# #",
+ " "
+ ],
+ // Umlauts - Uppercase
+ "Ä": [
+ " # # ",
+ " # ",
+ " # # ",
+ "# #",
+ "#####",
+ "# #",
+ " "
+ ],
+ "Å": [
+ " # ",
+ " # # ",
+ " # ",
+ " # # ",
+ "#####",
+ "# #",
+ " "
+ ],
+ "Æ": [
+ " ####",
+ "# # ",
+ "# ###",
+ "####",
+ "# # ",
+ "# ###",
+ " "
+ ],
+ "Ç": [
+ " ####",
+ "# ",
+ "# ",
+ "# ",
+ " ####",
+ " # ",
+ " # "
+ ],
+ "È": [
+ " # ",
+ "#### ",
+ "# ",
+ "### ",
+ "# ",
+ "#### ",
+ " "
+ ],
+ "É": [
+ " # ",
+ "#### ",
+ "# ",
+ "### ",
+ "# ",
+ "#### ",
+ " "
+ ],
+ "Ê": [
+ " # ",
+ " # # ",
+ "#### ",
+ "### ",
+ "# ",
+ "#### ",
+ " "
+ ],
+ "Ë": [
+ " # # ",
+ "#### ",
+ "# ",
+ "### ",
+ "# ",
+ "#### ",
+ " "
+ ],
+ "Ì": [
+ " # ",
+ " # ",
+ " # ",
+ " # ",
+ " # ",
+ " # ",
+ " "
+ ],
+ "Í": [
+ " # ",
+ " # ",
+ " # ",
+ " # ",
+ " # ",
+ " # ",
+ " "
+ ],
+ "Î": [
+ " # ",
+ " # # ",
+ " # ",
+ " # ",
+ " # ",
+ " # ",
+ " "
+ ],
+ "Ï": [
+ " # # ",
+ " # ",
+ " # ",
+ " # ",
+ " # ",
+ " # ",
+ " "
+ ],
+ "Ð": [
+ " ### ",
+ " # #",
+ "#### ",
+ " # #",
+ " # #",
+ " ### ",
+ " "
+ ],
+ "Ñ": [
+ " # # ",
+ "# #",
+ "## #",
+ "# # #",
+ "# ##",
+ "# #",
+ " "
+ ],
+ "Ò": [
+ " # ",
+ " ### ",
+ "# #",
+ "# #",
+ "# #",
+ " ### ",
+ " "
+ ],
+ "Ó": [
+ " # ",
+ " ### ",
+ "# #",
+ "# #",
+ "# #",
+ " ### ",
+ " "
+ ],
+ "Ô": [
+ " # ",
+ " # # ",
+ "# #",
+ "# #",
+ "# #",
+ " ### ",
+ " "
+ ],
+ "Õ": [
+ " # # ",
+ " ### ",
+ "# #",
+ "# #",
+ "# #",
+ " ### ",
+ " "
+ ],
+ "Ö": [
+ " # # ",
+ " ### ",
+ "# #",
+ "# #",
+ "# #",
+ " ### ",
+ " "
+ ],
+ "×": [
+ " ",
+ "# #",
+ " # # ",
+ " # ",
+ " # # ",
+ "# #",
+ " "
+ ],
+ "Ø": [
+ " ### ",
+ "# ##",
+ "# # #",
+ "# # #",
+ "## #",
+ " ### ",
+ " "
+ ],
+ "Ù": [
+ " # ",
+ "# #",
+ "# #",
+ "# #",
+ "# #",
+ " ### ",
+ " "
+ ],
+ "Ú": [
+ " # ",
+ "# #",
+ "# #",
+ "# #",
+ "# #",
+ " ### ",
+ " "
+ ],
+ "Û": [
+ " # ",
+ " # # ",
+ "# #",
+ "# #",
+ "# #",
+ " ### ",
+ " "
+ ],
+ "Ü": [
+ " # # ",
+ "# #",
+ "# #",
+ "# #",
+ "# #",
+ " ### ",
+ " "
+ ],
+ "Ý": [
+ " # ",
+ "# #",
+ " # # ",
+ " # ",
+ " # ",
+ " # ",
+ " "
+ ],
+ "Þ": [
+ "# ",
+ "#### ",
+ "# #",
+ "#### ",
+ "# ",
+ "# ",
+ " "
+ ],
+ "ß": [
+ " ### ",
+ "# #",
+ "#### ",
+ "# #",
+ "# #",
+ "## # ",
+ " "
+ ],
+ "à": [
+ " # ",
+ " ",
+ " ### ",
+ "# #",
+ "#####",
+ "# #",
+ " "
+ ],
+ "á": [
+ " # ",
+ " ",
+ " ### ",
+ "# #",
+ "#####",
+ "# #",
+ " "
+ ],
+ "â": [
+ " # ",
+ " # # ",
+ " ### ",
+ "# #",
+ "#####",
+ "# #",
+ " "
+ ],
+ "ã": [
+ " # # ",
+ " # ",
+ " ### ",
+ "# #",
+ "#####",
+ "# #",
+ " "
+ ],
+ // Umlauts - Lowercase
+ "ä": [
+ " # # ",
+ " ",
+ " ### ",
+ "# #",
+ "#####",
+ "# #",
+ " "
+ ],
+ "å": [
+ " # ",
+ " ### ",
+ " ",
+ " ### ",
+ "# #",
+ "#####",
+ "# #",
+ " "
+ ],
+ "æ": [
+ " ",
+ " ",
+ "#### ",
+ " # #",
+ " ####",
+ "# # #",
+ " "
+ ],
+ "ç": [
+ " ",
+ " ",
+ " ### ",
+ "# ",
+ " ### ",
+ " # ",
+ " # "
+ ],
+ "è": [
+ " # ",
+ " ",
+ "#### ",
+ "### ",
+ "# ",
+ "#### ",
+ " "
+ ],
+ "é": [
+ " # ",
+ " ",
+ "#### ",
+ "### ",
+ "# ",
+ "#### ",
+ " "
+ ],
+ "ê": [
+ " # ",
+ " # # ",
+ "#### ",
+ "### ",
+ "# ",
+ "#### ",
+ " "
+ ],
+ "ë": [
+ " # # ",
+ " ",
+ "#### ",
+ "### ",
+ "# ",
+ "#### ",
+ " "
+ ],
+ "ì": [
+ " # ",
+ " ",
+ "## ",
+ " # ",
+ " # ",
+ "### ",
+ " "
+ ],
+ "í": [
+ " # ",
+ " ",
+ "## ",
+ " # ",
+ " # ",
+ "### ",
+ " "
+ ],
+ "î": [
+ " # ",
+ " # # ",
+ "## ",
+ " # ",
+ " # ",
+ "### ",
+ " "
+ ],
+ "ï": [
+ " # # ",
+ " ",
+ "## ",
+ " # ",
+ " # ",
+ "### ",
+ " "
+ ],
+ "ð": [
+ " ## ",
+ " # # ",
+ " # ",
+ " ####",
+ "# #",
+ " ### ",
+ " "
+ ],
+ "ñ": [
+ " # # ",
+ " ",
+ "#### ",
+ "# #",
+ "# #",
+ "# #",
+ " "
+ ],
+ "ò": [
+ " # ",
+ " ",
+ " ### ",
+ "# #",
+ "# #",
+ " ### ",
+ " "
+ ],
+ "ó": [
+ " # ",
+ " ",
+ " ### ",
+ "# #",
+ "# #",
+ " ### ",
+ " "
+ ],
+ "ô": [
+ " # ",
+ " # # ",
+ " ### ",
+ "# #",
+ "# #",
+ " ### ",
+ " "
+ ],
+ "õ": [
+ " # # ",
+ " ",
+ " ### ",
+ "# #",
+ "# #",
+ " ### ",
+ " "
+ ],
+ "ö": [
+ " # # ",
+ " ",
+ " ### ",
+ "# #",
+ "# #",
+ " ### ",
+ " "
+ ],
+ "÷": [
+ " ",
+ " # ",
+ "#####",
+ " ",
+ " # ",
+ " ",
+ " "
+ ],
+ "ø": [
+ " ",
+ " ",
+ " ####",
+ "# # #",
+ "# # #",
+ "#### ",
+ " "
+ ],
+ "ù": [
+ " # ",
+ " ",
+ "# #",
+ "# #",
+ "# #",
+ " ####",
+ " "
+ ],
+ "ú": [
+ " # ",
+ " ",
+ "# #",
+ "# #",
+ "# #",
+ " ####",
+ " "
+ ],
+ "û": [
+ " # ",
+ " # # ",
+ "# #",
+ "# #",
+ "# #",
+ " ####",
+ " "
+ ],
+ "ü": [
+ " # # ",
+ " ",
+ "# #",
+ "# #",
+ "# #",
+ " ####",
+ " "
+ ],
+ "ý": [
+ " # ",
+ " ",
+ "# #",
+ "# #",
+ " ####",
+ " #",
+ " ### "
+ ],
+ "þ": [
+ "# ",
+ "# ",
+ "#### ",
+ "# #",
+ "#### ",
+ "# ",
+ "# "
+ ],
+ "ÿ": [
+ " # # ",
+ " ",
+ "# #",
+ "# #",
+ " ####",
+ " #",
+ " ### "
+ ],
+ "ı": [
+ " ",
+ " ",
+ " # ",
+ " # ",
+ " # ",
+ " # ",
+ " "
+ ],
+ "Ĺ": [
+ " # ",
+ "# ",
+ "# ",
+ "# ",
+ "# ",
+ "#### ",
+ " "
+ ],
+ "ƒ": [
+ " ## ",
+ " # ",
+ "### ",
+ " # ",
+ " # ",
+ " # ",
+ "# "
+ ],
+ "Ɵ": [
+ " ### ",
+ "# #",
+ "#####",
+ "# #",
+ "# #",
+ " ### ",
+ " "
+ ],
+ "ƪ": [
+ " ## ",
+ "# # ",
+ " ### ",
+ " # ",
+ " ",
+ " ",
+ " "
+ ],
+ "Ʒ": [
+ "#### ",
+ " # ",
+ " ## ",
+ " # ",
+ "#### ",
+ " ",
+ " "
+ ],
+ "Ƹ": [
+ "#### ",
+ " #",
+ " ## ",
+ " #",
+ "#### ",
+ " ",
+ " "
+ ],
+ "ǝ": [
+ " ",
+ " ",
+ " ### ",
+ "#####",
+ "# #",
+ " ### ",
+ " "
+ ],
+ "ɐ": [
+ " ",
+ " ",
+ " # # ",
+ "#### ",
+ " # ",
+ " ### ",
+ " "
+ ],
+ "ɔ": [
+ " ",
+ " ",
+ " ####",
+ "# ",
+ " ####",
+ " ",
+ " "
+ ],
+ "ɛ": [
+ " ",
+ " ### ",
+ "# ",
+ " ## ",
+ " ### ",
+ " ",
+ " "
+ ],
+ "ɟ": [
+ " ## ",
+ " # ",
+ "### ",
+ " # ",
+ " # ",
+ "# ",
+ " "
+ ],
+ "ɪ": [
+ " ",
+ "#####",
+ " # ",
+ " # ",
+ "#####",
+ " ",
+ " "
+ ],
+ "ɯ": [
+ " ",
+ " ",
+ "# #",
+ "# # #",
+ "# # #",
+ " # ##",
+ " "
+ ],
+ "ɴ": [
+ " ",
+ "# #",
+ "## #",
+ "# # #",
+ "# ##",
+ " ",
+ " "
+ ],
+ "ʀ": [
+ " ",
+ "#### ",
+ "# #",
+ "#### ",
+ "# # ",
+ "# # ",
+ " "
+ ],
+ "ʇ": [
+ " ",
+ " ## ",
+ " # ",
+ " # ",
+ " ###",
+ " # ",
+ " # "
+ ],
+ "ʋ": [
+ " ",
+ " ",
+ "# #",
+ "# #",
+ " # # ",
+ " # ",
+ "# "
+ ],
+ "ʔ": [
+ " # ",
+ " ### ",
+ " # ",
+ " ",
+ " # ",
+ " ",
+ " "
+ ],
+ "ʕ": [
+ " ",
+ " ## ",
+ " # ",
+ " # ",
+ " ## ",
+ " ",
+ " "
+ ],
+ "ʖ": [
+ " ",
+ " ## ",
+ "# ",
+ "# # ",
+ " ## ",
+ " ",
+ " "
+ ],
+ "ʘ": [
+ " ",
+ " ### ",
+ "# # #",
+ "# # #",
+ " ### ",
+ " ",
+ " "
+ ],
+ "ʙ": [
+ " ",
+ "#### ",
+ "# #",
+ "#### ",
+ "# #",
+ "#### ",
+ " "
+ ],
+ "ʞ": [
+ " ",
+ " # #",
+ " # # ",
+ " ## ",
+ " # # ",
+ " # #",
+ " "
+ ],
+ "ʟ": [
+ " ",
+ "# ",
+ "# ",
+ "# ",
+ "#### ",
+ " ",
+ " "
+ ],
+ "ʸ": [
+ "# # ",
+ " ## ",
+ " # ",
+ " # ",
+ " ",
+ " ",
+ " "
+ ],
+ "ˆ": [
+ " # ",
+ " # # ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "ˊ": [
+ " # ",
+ " # ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "ˋ": [
+ " # ",
+ " # ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "˘": [
+ " # # ",
+ " # ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "ˡ": [
+ "# ",
+ "# ",
+ "# ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "˵": [
+ " ### ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "˶": [
+ " ### ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "̀": [
+ " # ",
+ " # ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "́": [
+ " # ",
+ " # ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "̄": [
+ "#####",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "̅": [
+ "#####",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "̆": [
+ " # # ",
+ " # ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "̑": [
+ "# # #",
+ " # # ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "̫": [
+ " ",
+ " ",
+ " ",
+ " ### ",
+ " ",
+ " ",
+ " "
+ ],
+ "̯": [
+ " ",
+ " ",
+ " ",
+ " ### ",
+ " ",
+ " ",
+ " "
+ ],
+ "̲": [
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ "#####",
+ " "
+ ],
+ "̵": [
+ " ",
+ " ",
+ " # ",
+ " ### ",
+ " # ",
+ " ",
+ " "
+ ],
+ "̿": [
+ "#####",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "͆": [
+ " ### ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "͇": [
+ " ",
+ " ",
+ " ",
+ " ",
+ "#####",
+ " ",
+ " "
+ ],
+ "͜": [
+ " ",
+ " ",
+ " ",
+ " # # ",
+ " # ",
+ " ",
+ " "
+ ],
+ "͝": [
+ " # # ",
+ " # ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "͟": [
+ " ",
+ " ",
+ "#####",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "͠": [
+ " # #",
+ " ## ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "͡": [
+ " # ",
+ " # # ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "Δ": [
+ " # ",
+ " # # ",
+ " # # ",
+ "# #",
+ "# #",
+ "#####",
+ " "
+ ],
+ "Λ": [
+ " # ",
+ " # # ",
+ " # # ",
+ "# #",
+ "# #",
+ "# #",
+ " "
+ ],
+ "Σ": [
+ "#####",
+ "# ",
+ " ## ",
+ " ## ",
+ "# ",
+ "#####",
+ " "
+ ],
+ "Ω": [
+ " ### ",
+ "# #",
+ "# #",
+ "# #",
+ " # # ",
+ "## ##",
+ " "
+ ],
+ "α": [
+ " ",
+ " ",
+ " ### ",
+ "# # ",
+ "# ## ",
+ " # # ",
+ " "
+ ],
+ "β": [
+ " ## ",
+ "# # ",
+ "### ",
+ "# # ",
+ "# # ",
+ "### ",
+ "# "
+ ],
+ "ε": [
+ " ",
+ " ",
+ " ####",
+ "# ",
+ " ### ",
+ " ####",
+ " "
+ ],
+ "η": [
+ " ",
+ "# #",
+ "# #",
+ "#####",
+ "# #",
+ " #",
+ " "
+ ],
+ "ι": [
+ " ",
+ " # ",
+ " # ",
+ " # ",
+ " # ",
+ " ",
+ " "
+ ],
+ "λ": [
+ " # ",
+ " # ",
+ " # # ",
+ "# # ",
+ "# # ",
+ " ",
+ " "
+ ],
+ "ο": [
+ " ",
+ " ### ",
+ "# #",
+ " ### ",
+ " ",
+ " ",
+ " "
+ ],
+ "π": [
+ " ",
+ " ",
+ "#####",
+ " # # ",
+ " # # ",
+ " # # ",
+ " "
+ ],
+ "ς": [
+ " ",
+ " ####",
+ "# ",
+ " ### ",
+ " #",
+ " ## ",
+ " "
+ ],
+ "τ": [
+ " ",
+ " ",
+ "#####",
+ " # ",
+ " # ",
+ " # ",
+ " "
+ ],
+ "ψ": [
+ " ",
+ "# # #",
+ "# # #",
+ " ### ",
+ " # ",
+ " # ",
+ " "
+ ],
+ "ω": [
+ " ",
+ " ",
+ "# #",
+ "# #",
+ "# # #",
+ " # # ",
+ " "
+ ],
+ "Д": [
+ " ### ",
+ " # # ",
+ " # # ",
+ " # # ",
+ "#####",
+ "# #",
+ " "
+ ],
+ "И": [
+ "# #",
+ "# ##",
+ "# # #",
+ "## #",
+ "# #",
+ "# #",
+ " "
+ ],
+ "П": [
+ "#####",
+ "# #",
+ "# #",
+ "# #",
+ "# #",
+ "# #",
+ " "
+ ],
+ "Ф": [
+ " # ",
+ " ### ",
+ "# #",
+ "# #",
+ " ### ",
+ " # ",
+ " "
+ ],
+ "Я": [
+ " ####",
+ "# #",
+ " ####",
+ " # #",
+ "# #",
+ "# #",
+ " "
+ ],
+ "в": [
+ " ",
+ "### ",
+ "# # ",
+ "### ",
+ "# # ",
+ "### ",
+ " "
+ ],
+ "д": [
+ " ",
+ " ### ",
+ " # # ",
+ "#####",
+ " #",
+ " ",
+ " "
+ ],
+ "з": [
+ " ",
+ " ### ",
+ " # ",
+ " ## ",
+ " # ",
+ " ### ",
+ " "
+ ],
+ "м": [
+ " ",
+ "# #",
+ "## ##",
+ "# # #",
+ "# #",
+ "# #",
+ " "
+ ],
+ "у": [
+ " ",
+ "# #",
+ "# #",
+ " ####",
+ " #",
+ " ### ",
+ " "
+ ],
+ "я": [
+ " ",
+ " ####",
+ "# #",
+ " ####",
+ " # #",
+ "# #",
+ " "
+ ],
+ "є": [
+ " ",
+ " ### ",
+ "### ",
+ " ### ",
+ "### ",
+ " ### ",
+ " "
+ ],
+ "҂": [
+ "# #",
+ " # # ",
+ " # ",
+ " # # ",
+ "# #",
+ " ",
+ " "
+ ],
+ "Ӝ": [
+ " # # ",
+ "# # #",
+ " ### ",
+ " # ",
+ " ### ",
+ "# # #",
+ " "
+ ],
+ "ԅ": [
+ " ",
+ " ## ",
+ "# # ",
+ " ### ",
+ " ",
+ " ",
+ " "
+ ],
+ "ן": [
+ "# ",
+ "# ",
+ "# ",
+ "# ",
+ "# ",
+ "# ",
+ " "
+ ],
+ "סּ": [
+ " ### ",
+ "# ",
+ " ### ",
+ " # ",
+ " ",
+ " ",
+ " "
+ ],
+ "ױ": [
+ "# #",
+ "# #",
+ "# #",
+ " # # ",
+ " # ",
+ " ",
+ " "
+ ],
+ "ײ": [
+ "# # ",
+ "# # ",
+ "# # ",
+ " ## #",
+ " ",
+ " ",
+ " "
+ ],
+ "ل": [
+ " # ",
+ " # ",
+ " # ",
+ " # ",
+ " ## ",
+ " ",
+ " "
+ ],
+ "و": [
+ " ",
+ " ## ",
+ "# # ",
+ " ## ",
+ " ",
+ " ",
+ " "
+ ],
+ "٩": [
+ " ### ",
+ "# # ",
+ " ### ",
+ " #",
+ " ",
+ " ",
+ " "
+ ],
+ "ਊ": [
+ " ## ",
+ "# # ",
+ " ## ",
+ " # ",
+ " ",
+ " ",
+ " "
+ ],
+ "੭": [
+ " ### ",
+ " # ",
+ " # ",
+ " # ",
+ " ",
+ " ",
+ " "
+ ],
+ "ଘ": [
+ " ### ",
+ "# #",
+ " ### ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "୧": [
+ " # ",
+ "## ",
+ " # ",
+ " # ",
+ " ",
+ " ",
+ " "
+ ],
+ "୨": [
+ " ## ",
+ " # ",
+ " ## ",
+ "# ",
+ " ",
+ " ",
+ " "
+ ],
+ "థ": [
+ " ### ",
+ "# #",
+ " ### ",
+ " # #",
+ " ",
+ " ",
+ " "
+ ],
+ "౪": [
+ " # # ",
+ " # # ",
+ "#### ",
+ " # ",
+ " ",
+ " ",
+ " "
+ ],
+ "ಠ": [
+ " ### ",
+ "# #",
+ "# #",
+ " ### ",
+ " # ",
+ " ",
+ " "
+ ],
+ "ಡ": [
+ " ### ",
+ "# #",
+ " ### ",
+ " # ",
+ " ",
+ " ",
+ " "
+ ],
+ "ಥ": [
+ " # # ",
+ " ### ",
+ "# #",
+ " ### ",
+ " ",
+ " ",
+ " "
+ ],
+ "ರ": [
+ " ## ",
+ " # ",
+ " ### ",
+ " # ",
+ " ",
+ " ",
+ " "
+ ],
+ "ೃ": [
+ " ## ",
+ " # ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "ง": [
+ " ## ",
+ " # ",
+ " # ",
+ " # ",
+ " ## ",
+ " ",
+ " "
+ ],
+ "๏": [
+ " ### ",
+ "# #",
+ "# #",
+ "# #",
+ " ### ",
+ " ",
+ " "
+ ],
+ "๑": [
+ "# ",
+ "# ",
+ "# ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "ຈ": [
+ " ## ",
+ " # ",
+ " ### ",
+ " # ",
+ " ",
+ " ",
+ " "
+ ],
+ "ຶ": [
+ " # # ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "༎": [
+ " # # ",
+ "# #",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "༼": [
+ " ### ",
+ " # ",
+ "# ",
+ " # ",
+ " ### ",
+ " ",
+ " "
+ ],
+ "༽": [
+ " ### ",
+ " # ",
+ " #",
+ " # ",
+ " ### ",
+ " ",
+ " "
+ ],
+ "ლ": [
+ " ",
+ " # ",
+ " # # ",
+ "# # ",
+ " # ",
+ " # ",
+ " "
+ ],
+ "ღ": [
+ " # # ",
+ "## ##",
+ " ### ",
+ " # ",
+ " ",
+ " ",
+ " "
+ ],
+ "ᐛ": [
+ " ",
+ " # # ",
+ " # ",
+ " # # ",
+ "# #",
+ " ",
+ " "
+ ],
+ "ᕕ": [
+ " # ",
+ "# # ",
+ " # ",
+ " # ",
+ " # ",
+ " ",
+ " "
+ ],
+ "ᕗ": [
+ " # ",
+ " # #",
+ " # ",
+ " # ",
+ " # ",
+ " ",
+ " "
+ ],
+ "ᕙ": [
+ " # ",
+ "# ## ",
+ " # #",
+ " # ",
+ " # ",
+ " ",
+ " "
+ ],
+ "ᕤ": [
+ " # ",
+ " ## #",
+ "# # ",
+ " # ",
+ " # ",
+ " ",
+ " "
+ ],
+ "ᕥ": [
+ " # ",
+ " # # ",
+ " # ",
+ " # # ",
+ "# #",
+ " ",
+ " "
+ ],
+ "ᕦ": [
+ " # ",
+ "## # ",
+ " # # ",
+ " # #",
+ " # ",
+ " ",
+ " "
+ ],
+ "ᴀ": [
+ " ",
+ " ### ",
+ "# #",
+ "#####",
+ "# #",
+ " ",
+ " "
+ ],
+ "ᴇ": [
+ " ",
+ "#### ",
+ "### ",
+ "#### ",
+ " ",
+ " ",
+ " "
+ ],
+ "ᴏ": [
+ " ",
+ " ### ",
+ "# #",
+ " ### ",
+ " ",
+ " ",
+ " "
+ ],
+ "ᴖ": [
+ " ",
+ " ### ",
+ "# #",
+ " ### ",
+ " ",
+ " ",
+ " "
+ ],
+ "ᴗ": [
+ " ",
+ " ",
+ "# #",
+ " ### ",
+ " ",
+ " ",
+ " "
+ ],
+ "ᴛ": [
+ " ",
+ "#####",
+ " # ",
+ " # ",
+ " ",
+ " ",
+ " "
+ ],
+ "ᴥ": [
+ " ",
+ " # # ",
+ "## ##",
+ " ### ",
+ " ",
+ " ",
+ " "
+ ],
+ "ᵉ": [
+ " ## ",
+ "# # ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "ᵒ": [
+ " ## ",
+ "# # ",
+ " ## ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "ᵕ": [
+ " ",
+ " # # ",
+ " # ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "ᵘ": [
+ "# # ",
+ " ## ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "ᵛ": [
+ "# # ",
+ " # ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "ᶜ": [
+ " ## ",
+ "# ",
+ " ## ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "ᶤ": [
+ "# ",
+ "# ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "ᶰ": [
+ " ",
+ "# # ",
+ "# # ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "᷄": [
+ " # ",
+ " # ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "᷅": [
+ " # ",
+ " # ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ // General Punctuation (U+2000 to U+206F)
+ "‐": [
+ " ",
+ " ",
+ " ",
+ " ### ",
+ " ",
+ " ",
+ " "
+ ],
+ "‑": [
+ " ",
+ " ",
+ " ",
+ " ### ",
+ " ",
+ " ",
+ " "
+ ],
+ "‒": [
+ " ",
+ " ",
+ " ",
+ " ### ",
+ " ",
+ " ",
+ " "
+ ],
+ "–": [
+ " ",
+ " ",
+ " ",
+ "#####",
+ " ",
+ " ",
+ " "
+ ],
+ "—": [
+ " ",
+ " ",
+ " ",
+ "#####",
+ " ",
+ " ",
+ " "
+ ],
+ "―": [
+ " ",
+ " ",
+ " ",
+ "#####",
+ " ",
+ " ",
+ " "
+ ],
+ "‖": [
+ " # # ",
+ " # # ",
+ " # # ",
+ " # # ",
+ " # # ",
+ " # # ",
+ " "
+ ],
+ "‗": [
+ " ",
+ " ",
+ " ",
+ " ",
+ "#####",
+ "#####",
+ " "
+ ],
+ "‘": [
+ " # ",
+ " # ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "’": [
+ " # ",
+ " # ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "‚": [
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " # ",
+ " # "
+ ],
+ "‛": [
+ " # ",
+ " # ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "“": [
+ " # # ",
+ "# #",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "”": [
+ " # # ",
+ "# #",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "„": [
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " # # ",
+ "# #"
+ ],
+ "‟": [
+ " # # ",
+ "# #",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "†": [
+ " # ",
+ " # ",
+ "#####",
+ " # ",
+ " # ",
+ " # ",
+ " "
+ ],
+ "‡": [
+ " # ",
+ "#####",
+ " # ",
+ "#####",
+ " # ",
+ " ",
+ " "
+ ],
+ "•": [
+ " ",
+ " ",
+ " ### ",
+ " ### ",
+ " ### ",
+ " ",
+ " "
+ ],
+ "‣": [
+ " ",
+ " ",
+ " # ",
+ " ### ",
+ "#####",
+ " ",
+ " "
+ ],
+ "․": [
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ "# ",
+ " "
+ ],
+ "‥": [
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ "# #",
+ " "
+ ],
+ "…": [
+ " ",
+ " ",
+ " ",
+ " ",
+ "# # #",
+ " ",
+ " "
+ ],
+ "‧": [
+ " ",
+ " ",
+ " # ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "‰": [
+ "# #",
+ "# # #",
+ " # ",
+ " # # ",
+ "# #",
+ "# #",
+ " "
+ ],
+ "′": [
+ " # ",
+ "# ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "″": [
+ "# # ",
+ "# # ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "‴": [
+ "# # #",
+ "# # #",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "‵": [
+ " # ",
+ " #",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "‶": [
+ "# # ",
+ "# # ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "‸": [
+ " ",
+ " ",
+ " # # ",
+ " # ",
+ " ",
+ " ",
+ " "
+ ],
+ "‹": [
+ " ",
+ " ",
+ " # ",
+ " # ",
+ " # ",
+ " ",
+ " "
+ ],
+ "›": [
+ " ",
+ " ",
+ " # ",
+ " # ",
+ " # ",
+ " ",
+ " "
+ ],
+ "※": [
+ " # ",
+ "# # #",
+ " ### ",
+ "# # #",
+ " # ",
+ " ",
+ " "
+ ],
+ "‼": [
+ "# # ",
+ "# # ",
+ "# # ",
+ "# # ",
+ " ",
+ "# # ",
+ " "
+ ],
+ "‽": [
+ " ## ",
+ "# # ",
+ " ## ",
+ " # ",
+ " # ",
+ " ",
+ " "
+ ],
+ "‾": [
+ "#####",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "‿": [
+ " ",
+ " ",
+ " ",
+ " ",
+ " # # ",
+ " # ",
+ " "
+ ],
+ "⁀": [
+ " ",
+ " # # ",
+ "# #",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "⁄": [
+ " ",
+ " #",
+ " # ",
+ " # ",
+ " # ",
+ "# ",
+ " "
+ ],
+ "⁇": [
+ " # # ",
+ "# # #",
+ " ## ",
+ " ## ",
+ " ",
+ " # # ",
+ " "
+ ],
+ "⁈": [
+ " ## ",
+ "# # ",
+ " # ",
+ " # ",
+ " ",
+ " # #",
+ " "
+ ],
+ "⁉": [
+ " # # ",
+ " # # ",
+ " # # ",
+ " # ",
+ " ",
+ " # ",
+ " "
+ ],
+ "⁊": [
+ " ",
+ " #",
+ " ####",
+ "# ",
+ " ### ",
+ " ",
+ " "
+ ],
+ "⁋": [
+ "#### ",
+ " ####",
+ " ####",
+ " # # ",
+ " # # ",
+ " # # ",
+ " "
+ ],
+ "⁌": [
+ " ",
+ " ",
+ "#### ",
+ " ### ",
+ "#### ",
+ " ",
+ " "
+ ],
+ "⁍": [
+ " ",
+ " ",
+ " ####",
+ " ### ",
+ " ####",
+ " ",
+ " "
+ ],
+ "⁎": [
+ " ",
+ " ",
+ " ",
+ " ",
+ " # # ",
+ " # ",
+ " # # "
+ ],
+ "⁏": [
+ " ",
+ "# ",
+ " # ",
+ " ",
+ " ",
+ " # ",
+ " "
+ ],
+ "⁐": [
+ " ",
+ " ",
+ " # # ",
+ " # ",
+ " # # ",
+ " ",
+ " "
+ ],
+ "⁑": [
+ " # # ",
+ " # ",
+ " # # ",
+ " ",
+ " # # ",
+ " # ",
+ " # # "
+ ],
+ "⁒": [
+ " ",
+ " # ",
+ " ",
+ "#####",
+ " ",
+ " # ",
+ " "
+ ],
+ "⁓": [
+ " ",
+ " ",
+ " # #",
+ " ## ",
+ "# # ",
+ " ",
+ " "
+ ],
+ "⁔": [
+ " ",
+ " ",
+ " ",
+ " ",
+ "# #",
+ " # # ",
+ " "
+ ],
+ "⁕": [
+ " ### ",
+ "# # #",
+ " ### ",
+ "# # #",
+ " ### ",
+ " ",
+ " "
+ ],
+ "⁖": [
+ " ",
+ " # ",
+ " # ",
+ " # ",
+ " ",
+ " ",
+ " "
+ ],
+ "⁗": [
+ "# # #",
+ "# # #",
+ "# ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "⁘": [
+ " ",
+ "# # #",
+ " ",
+ " # ",
+ " ",
+ " ",
+ " "
+ ],
+ "⁙": [
+ " ",
+ "# # #",
+ " # ",
+ "# # #",
+ " ",
+ " ",
+ " "
+ ],
+ "⁚": [
+ " ",
+ " # # ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "⁛": [
+ "# # #",
+ " ",
+ " # ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "⁜": [
+ " # ",
+ "# #",
+ " # ",
+ "# #",
+ " # ",
+ " ",
+ " "
+ ],
+ "⁝": [
+ " # ",
+ " ",
+ " # ",
+ " ",
+ " # ",
+ " ",
+ " "
+ ],
+ "⁞": [
+ " # ",
+ " # ",
+ " # ",
+ " # ",
+ " ",
+ " ",
+ " "
+ ],
+ "⁻": [
+ " ",
+ " ### ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "€": [
+ " ## ",
+ " # ",
+ "#### ",
+ " # ",
+ "#### ",
+ " ## ",
+ " "
+ ],
+ "₿": [
+ " # ",
+ " ####",
+ "# # ",
+ " ### ",
+ " # #",
+ "#### ",
+ " # "
+ ],
+ "™": [
+ "#####",
+ " # ",
+ " # ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "⅓": [
+ "# # ",
+ " # ",
+ " # # ",
+ " # # ",
+ "# # ",
+ " ",
+ " "
+ ],
+ "⅔": [
+ "## # ",
+ " ## ",
+ " # ",
+ " # # ",
+ "# # ",
+ " ",
+ " "
+ ],
+ "⅛": [
+ "# #",
+ " ## ",
+ " # #",
+ "# # ",
+ " ###",
+ " ",
+ " "
+ ],
+ "⅜": [
+ "## #",
+ " # # ",
+ "## # ",
+ " ## #",
+ "# ##",
+ " ",
+ " "
+ ],
+ "⅝": [
+ "### ",
+ " # #",
+ "### ",
+ " ###",
+ " #",
+ " ",
+ " "
+ ],
+ "⅞": [
+ "### #",
+ " ## ",
+ " # ",
+ " # #",
+ "# ##",
+ " ",
+ " "
+ ],
+ "←": [
+ " ",
+ " # ",
+ " # ",
+ "#####",
+ " # ",
+ " # ",
+ " "
+ ],
+ "↑": [
+ " # ",
+ " ### ",
+ "# # #",
+ " # ",
+ " # ",
+ " # ",
+ " "
+ ],
+ "→": [
+ " ",
+ " # ",
+ " # ",
+ "#####",
+ " # ",
+ " # ",
+ " "
+ ],
+ "↓": [
+ " # ",
+ " # ",
+ " # ",
+ "# # #",
+ " ### ",
+ " # ",
+ " "
+ ],
+ "↼": [
+ " ",
+ " ",
+ " # ",
+ "#####",
+ " ",
+ " ",
+ " "
+ ],
+ "⇀": [
+ " ",
+ " ",
+ " # ",
+ "#####",
+ " ",
+ " ",
+ " "
+ ],
+ "∀": [
+ " # ",
+ " # # ",
+ "#####",
+ "# #",
+ " ",
+ " ",
+ " "
+ ],
+ "∇": [
+ "#####",
+ " ### ",
+ " # ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "∑": [
+ "#####",
+ " # ",
+ " # ",
+ " # ",
+ "# ",
+ "#####",
+ " "
+ ],
+ "√": [
+ " #",
+ " #",
+ "# #",
+ " # #",
+ " # #",
+ " # ",
+ " "
+ ],
+ "∞": [
+ " ",
+ " ",
+ " # # ",
+ "# # #",
+ " # # ",
+ " ",
+ " "
+ ],
+ "∠": [
+ " ",
+ " #",
+ " # ",
+ " # ",
+ "#####",
+ " ",
+ " "
+ ],
+ "∩": [
+ " ### ",
+ "# #",
+ "# #",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "∫": [
+ " ## ",
+ " # ",
+ " # ",
+ " # ",
+ " # ",
+ "## ",
+ " "
+ ],
+ "∵": [
+ " ",
+ " # ",
+ "# # #",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "≖": [
+ " # # ",
+ "#####",
+ " ",
+ "#####",
+ " ",
+ " ",
+ " "
+ ],
+ "≡": [
+ " ",
+ "#####",
+ " ",
+ "#####",
+ " ",
+ "#####",
+ " "
+ ],
+ "≦": [
+ " # ",
+ " # ",
+ " # ",
+ "#####",
+ "#####",
+ " ",
+ " "
+ ],
+ "≧": [
+ " # ",
+ " # ",
+ " # ",
+ "#####",
+ "#####",
+ " ",
+ " "
+ ],
+ "⊂": [
+ " ## ",
+ " # ",
+ " # ",
+ " # ",
+ " ## ",
+ " ",
+ " "
+ ],
+ "⊃": [
+ " ## ",
+ " # ",
+ " # ",
+ " # ",
+ " ## ",
+ " ",
+ " "
+ ],
+ "⊙": [
+ " ### ",
+ "# # #",
+ "# # #",
+ " ### ",
+ " ",
+ " ",
+ " "
+ ],
+ "⊹": [
+ "# #",
+ " # # ",
+ " # ",
+ " # # ",
+ "# #",
+ " ",
+ " "
+ ],
+ "⌂": [
+ " # ",
+ " ### ",
+ "#####",
+ "# # #",
+ "# #",
+ " ",
+ " "
+ ],
+ "⌐": [
+ " ",
+ "#####",
+ "# ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "⌒": [
+ " # ",
+ " # # ",
+ "# #",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "⌓": [
+ "# #",
+ " # # ",
+ " # ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "⌘": [
+ " # ",
+ "# # #",
+ " ### ",
+ " # # ",
+ " # ",
+ " ",
+ " "
+ ],
+ "⌣": [
+ " ",
+ " ",
+ "# #",
+ " # # ",
+ " # ",
+ " ",
+ " "
+ ],
+ "⌥": [
+ " ### ",
+ " # ",
+ " # # ",
+ "# #",
+ " ",
+ " ",
+ " "
+ ],
+ "─": [
+ " ",
+ " ",
+ " ",
+ "#####",
+ " ",
+ " ",
+ " "
+ ],
+ "━": [
+ " ",
+ " ",
+ " ",
+ "#####",
+ "#####",
+ " ",
+ " "
+ ],
+ "│": [
+ " # ",
+ " # ",
+ " # ",
+ " # ",
+ " # ",
+ " # ",
+ " "
+ ],
+ "┌": [
+ " ",
+ " ",
+ " ",
+ " ###",
+ " # ",
+ " # ",
+ " "
+ ],
+ "┐": [
+ " ",
+ " ",
+ " ",
+ "### ",
+ " # ",
+ " # ",
+ " "
+ ],
+ "└": [
+ " # ",
+ " # ",
+ " ###",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "┘": [
+ " # ",
+ " # ",
+ "### ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "├": [
+ " # ",
+ " # ",
+ " ###",
+ " ###",
+ " # ",
+ " # ",
+ " "
+ ],
+ "┣": [
+ " # ",
+ " ###",
+ " # ",
+ " ###",
+ " # ",
+ " # ",
+ " "
+ ],
+ "┤": [
+ " # ",
+ " # ",
+ "### ",
+ "### ",
+ " # ",
+ " # ",
+ " "
+ ],
+ "┬": [
+ " ",
+ " ",
+ "#####",
+ " # ",
+ " # ",
+ " # ",
+ " "
+ ],
+ "┴": [
+ " # ",
+ " # ",
+ " # ",
+ "#####",
+ " ",
+ " ",
+ " "
+ ],
+ "┻": [
+ " # ",
+ " # ",
+ " # ",
+ "#####",
+ "#####",
+ " ",
+ " "
+ ],
+ "═": [
+ " ",
+ " ",
+ "#####",
+ " ",
+ "#####",
+ " ",
+ " "
+ ],
+ "╔": [
+ " ",
+ " ",
+ " ###",
+ " # ",
+ " ###",
+ " # ",
+ " "
+ ],
+ "╗": [
+ " ",
+ " ",
+ "### ",
+ " # ",
+ "### ",
+ " # ",
+ " "
+ ],
+ "╚": [
+ " # ",
+ " ###",
+ " # ",
+ " ###",
+ " ",
+ " ",
+ " "
+ ],
+ "╝": [
+ " # ",
+ "### ",
+ " # ",
+ "### ",
+ " ",
+ " ",
+ " "
+ ],
+ "╤": [
+ " ",
+ "#####",
+ " # ",
+ " # ",
+ " # ",
+ " ",
+ " "
+ ],
+ "╥": [
+ " ",
+ "#####",
+ " # ",
+ "#####",
+ " # ",
+ " ",
+ " "
+ ],
+ "╦": [
+ " ",
+ "#####",
+ " # ",
+ "#####",
+ " # ",
+ " ",
+ " "
+ ],
+ "╭": [
+ " ",
+ " ",
+ " ##",
+ " # ",
+ " # ",
+ " # ",
+ " "
+ ],
+ "╮": [
+ " ",
+ " ",
+ "## ",
+ " # ",
+ " # ",
+ " # ",
+ " "
+ ],
+ "╯": [
+ " # ",
+ " # ",
+ "## ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "╰": [
+ " # ",
+ " # ",
+ " ##",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "╹": [
+ " # ",
+ " # ",
+ " # ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "▀": [
+ "#####",
+ "#####",
+ "#####",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "▇": [
+ "#####",
+ "#####",
+ "#####",
+ "#####",
+ "#####",
+ " ",
+ " "
+ ],
+ "█": [
+ "#####",
+ "#####",
+ "#####",
+ "#####",
+ "#####",
+ "#####",
+ " "
+ ],
+ "▒": [
+ "# # #",
+ " # # ",
+ "# # #",
+ " # # ",
+ "# # #",
+ " # # ",
+ " "
+ ],
+ "■": [
+ " ",
+ " ### ",
+ " ### ",
+ " ### ",
+ " ",
+ " ",
+ " "
+ ],
+ "□": [
+ " ",
+ " ### ",
+ " # # ",
+ " ### ",
+ " ",
+ " ",
+ " "
+ ],
+ "▲": [
+ " ",
+ " # ",
+ " ### ",
+ "#####",
+ " ",
+ " ",
+ " "
+ ],
+ "△": [
+ " ",
+ " # ",
+ " # # ",
+ "# #",
+ "#####",
+ " ",
+ " "
+ ],
+ "▽": [
+ " ",
+ "#####",
+ " ### ",
+ " # ",
+ " ",
+ " ",
+ " "
+ ],
+ "◉": [
+ " ### ",
+ "## ##",
+ "## ##",
+ "## ##",
+ " ### ",
+ " ",
+ " "
+ ],
+ "◔": [
+ " ",
+ " ### ",
+ "### #",
+ " # # ",
+ " ### ",
+ " ",
+ " "
+ ],
+ "◕": [
+ " ",
+ " ### ",
+ " ### ",
+ " # # ",
+ " ### ",
+ " ",
+ " "
+ ],
+ "◞": [
+ " ",
+ " # ",
+ " # ",
+ " # ",
+ " ",
+ " ",
+ " "
+ ],
+ "◟": [
+ " ",
+ " # ",
+ " # ",
+ " # ",
+ " ",
+ " ",
+ " "
+ ],
+ "◠": [
+ " ",
+ " # ",
+ " # # ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "◡": [
+ " ",
+ " ",
+ " ",
+ " # # ",
+ " # ",
+ " ",
+ " "
+ ],
+ "☀": [
+ "# # #",
+ " ### ",
+ "## ##",
+ " ### ",
+ "# # #",
+ " ",
+ " "
+ ],
+ "☁": [
+ " ",
+ " # # ",
+ "#####",
+ "#####",
+ " ",
+ " ",
+ " "
+ ],
+ "☃": [
+ " # ",
+ " ### ",
+ " ### ",
+ "#####",
+ " # # ",
+ " ",
+ " "
+ ],
+ "★": [
+ " # ",
+ " ### ",
+ "#####",
+ " # # ",
+ "# #",
+ " ",
+ " "
+ ],
+ "☆": [
+ " # ",
+ " # # ",
+ "# #",
+ " # # ",
+ "# #",
+ " ",
+ " "
+ ],
+ "☉": [
+ "# # #",
+ " ### ",
+ "## ##",
+ " ### ",
+ "# # #",
+ " ",
+ " "
+ ],
+ "☜": [
+ " ",
+ " ## ",
+ " # ",
+ "#####",
+ " # ",
+ " ## ",
+ " "
+ ],
+ "☝": [
+ " # ",
+ " ### ",
+ " # # ",
+ " # ",
+ " # ",
+ " ",
+ " "
+ ],
+ "☞": [
+ " ",
+ " ## ",
+ " # ",
+ "#####",
+ " # ",
+ " ## ",
+ " "
+ ],
+ "☠": [
+ " ### ",
+ "# # #",
+ " ### ",
+ " # ",
+ " # # ",
+ " ",
+ " "
+ ],
+ "☢": [
+ " # ",
+ " # # ",
+ "# # #",
+ " ### ",
+ " # ",
+ " ",
+ " "
+ ],
+ "☭": [
+ " ## ",
+ " ### ",
+ "# # ",
+ " # # ",
+ " ## ",
+ " ",
+ " "
+ ],
+ "☯": [
+ " ### ",
+ "## ##",
+ "# # #",
+ "## ##",
+ " ### ",
+ " ",
+ " "
+ ],
+ "☺": [
+ " ### ",
+ "# #",
+ "# # #",
+ "# #",
+ " ### ",
+ " ",
+ " "
+ ],
+ "♠": [
+ " # ",
+ " ### ",
+ "#####",
+ "# # #",
+ " # ",
+ " ",
+ " "
+ ],
+ "♡": [
+ " # # ",
+ "# # #",
+ "# #",
+ " # # ",
+ " # ",
+ " ",
+ " "
+ ],
+ "♣": [
+ " # ",
+ " ### ",
+ "## ##",
+ " ### ",
+ " # ",
+ " ",
+ " "
+ ],
+ "♥": [
+ " # # ",
+ "## ##",
+ "## ##",
+ " ### ",
+ " # ",
+ " ",
+ " "
+ ],
+ "♦": [
+ " # ",
+ " ### ",
+ "#####",
+ " ### ",
+ " # ",
+ " ",
+ " "
+ ],
+ "♪": [
+ " # ",
+ " ## ",
+ " # ",
+ " # ",
+ " ## ",
+ " ## ",
+ " "
+ ],
+ "♫": [
+ " ###",
+ " # # ",
+ " # # ",
+ " # # ",
+ "## ##",
+ "## ##",
+ " "
+ ],
+ "♬": [
+ " ###",
+ " ####",
+ " # # ",
+ " # # ",
+ "## ##",
+ " ",
+ " "
+ ],
+ "♭": [
+ "# ",
+ "# ",
+ "### ",
+ "# # ",
+ "### ",
+ " ",
+ " "
+ ],
+ "♮": [
+ " # # ",
+ " ### ",
+ "### #",
+ " # # ",
+ " ",
+ " ",
+ " "
+ ],
+ "♯": [
+ " # # ",
+ "#####",
+ " # # ",
+ "#####",
+ " # # ",
+ " ",
+ " "
+ ],
+ "⚀": [
+ " ### ",
+ "# #",
+ "# # #",
+ "# #",
+ " ### ",
+ " ",
+ " "
+ ],
+ "⚁": [
+ " ### ",
+ "# ##",
+ "# #",
+ "## #",
+ " ### ",
+ " ",
+ " "
+ ],
+ "⚂": [
+ " ### ",
+ "# # #",
+ "# #",
+ "# # #",
+ " ### ",
+ " ",
+ " "
+ ],
+ "⚃": [
+ " ### ",
+ "# # #",
+ "# #",
+ "## ##",
+ " ### ",
+ " ",
+ " "
+ ],
+ "⚄": [
+ " ### ",
+ "## ##",
+ "# #",
+ "## ##",
+ " ### ",
+ " ",
+ " "
+ ],
+ "⚅": [
+ " ### ",
+ "## ##",
+ "# # #",
+ "## ##",
+ " ### ",
+ " ",
+ " "
+ ],
+ "⚆": [
+ " ### ",
+ "#####",
+ "## ##",
+ "#####",
+ " ### ",
+ " ",
+ " "
+ ],
+ "✉": [
+ "#####",
+ "## ##",
+ "# # #",
+ "# #",
+ "#####",
+ " ",
+ " "
+ ],
+ "✌": [
+ " # # ",
+ " # # ",
+ " # # ",
+ " # ",
+ " ",
+ " ",
+ " "
+ ],
+ "✔": [
+ " ",
+ " #",
+ " ##",
+ "# ## ",
+ " ## ",
+ " ",
+ " "
+ ],
+ "✧": [
+ " # ",
+ " # # ",
+ "# #",
+ " # # ",
+ " # ",
+ " ",
+ " "
+ ],
+ "✿": [
+ " ",
+ " # # ",
+ "# # #",
+ " # # ",
+ " ",
+ " ",
+ " "
+ ],
+ "❤": [
+ " ## #",
+ "#### ",
+ "#### ",
+ " ### ",
+ " # ",
+ " ",
+ " "
+ ],
+ "⤏": [
+ " ",
+ " ##",
+ "#####",
+ " ##",
+ " ",
+ " ",
+ " "
+ ],
+ "⤜": [
+ " ",
+ "## ",
+ "#####",
+ "## ",
+ " ",
+ " ",
+ " "
+ ],
+ "⩽": [
+ " # ",
+ " # ",
+ " # ",
+ "#####",
+ "#####",
+ " ",
+ " "
+ ],
+ "⩾": [
+ " # ",
+ " # ",
+ " # ",
+ "#####",
+ "#####",
+ " ",
+ " "
+ ],
+ "ⱺ": [
+ " ",
+ " ### ",
+ "# #",
+ "#####",
+ " ### ",
+ " ",
+ " "
+ ],
+ "⸑": [
+ "# #",
+ " # # ",
+ " # ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "⸫": [
+ " # ",
+ " ",
+ " # ",
+ " ",
+ " # ",
+ " ",
+ " "
+ ],
+ "。": [
+ " ",
+ " ",
+ " ",
+ " ",
+ " ## ",
+ " ## ",
+ " "
+ ],
+ "」": [
+ " ###",
+ " # ",
+ " # ",
+ " # ",
+ " ###",
+ " ",
+ " "
+ ],
+ "っ": [
+ " ",
+ " ## ",
+ " # ",
+ " ## ",
+ " ",
+ " ",
+ " "
+ ],
+ "つ": [
+ " ",
+ " ###",
+ " # ",
+ " ###",
+ " ",
+ " ",
+ " "
+ ],
+ "づ": [
+ " # # ",
+ " ###",
+ " # ",
+ " ###",
+ " ",
+ " ",
+ " "
+ ],
+ "゜": [
+ " ## ",
+ "# # ",
+ " ## ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "ェ": [
+ " ## ",
+ "#### ",
+ " ## ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "コ": [
+ "### ",
+ " ",
+ "### ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "シ": [
+ " ",
+ "# ",
+ " # ",
+ " # ",
+ " # ",
+ " ",
+ " "
+ ],
+ "ツ": [
+ " ",
+ " # #",
+ " # # ",
+ " # ",
+ " ",
+ " ",
+ " "
+ ],
+ "ノ": [
+ " ",
+ " # ",
+ " # ",
+ " # ",
+ "# ",
+ " ",
+ " "
+ ],
+ "ヮ": [
+ " ",
+ " ###",
+ " # # ",
+ " # ",
+ " ",
+ " ",
+ " "
+ ],
+ "ヽ": [
+ " # ",
+ " # ",
+ "# ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "ㅂ": [
+ " ### ",
+ "# #",
+ " ### ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "ㆆ": [
+ " ### ",
+ "# #",
+ "# #",
+ " ### ",
+ " ",
+ " ",
+ " "
+ ],
+ "㇁": [
+ " ",
+ " # ",
+ " # ",
+ " # ",
+ " ",
+ " ",
+ " "
+ ],
+ "人": [
+ " # ",
+ " # # ",
+ "# #",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "卌": [
+ " # # ",
+ "#####",
+ " # # ",
+ "#####",
+ " ",
+ " ",
+ " "
+ ],
+ "屮": [
+ " # ",
+ " # ",
+ " ### ",
+ "# #",
+ " ",
+ " ",
+ " "
+ ],
+ "彡": [
+ "# # ",
+ " # # ",
+ " ## ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "由": [
+ " ### ",
+ " # # ",
+ " ### ",
+ " # # ",
+ " ### ",
+ " ",
+ " "
+ ],
+ "皿": [
+ "#####",
+ "# #",
+ "#####",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "益": [
+ "#####",
+ " ### ",
+ "#####",
+ "# # #",
+ " ",
+ " ",
+ " "
+ ],
+ "ꇴ": [
+ " ## ",
+ " # ",
+ " ## ",
+ " # #",
+ " ",
+ " ",
+ " "
+ ],
+ "꒡": [
+ " # ",
+ " # # ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "꒰": [
+ " ##",
+ " # ",
+ " # ",
+ " # ",
+ " ##",
+ " ",
+ " "
+ ],
+ "꒱": [
+ "## ",
+ " # ",
+ " # ",
+ " # ",
+ "## ",
+ " ",
+ " "
+ ],
+ "눈": [
+ " # # ",
+ "## ##",
+ " ### ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "︵": [
+ " ",
+ " # ",
+ " # # ",
+ "# #",
+ " ",
+ " ",
+ " "
+ ],
+ "︶": [
+ "# #",
+ " # # ",
+ " # ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "︹": [
+ " ### ",
+ " # ",
+ "# ",
+ " # ",
+ " ### ",
+ " ",
+ " "
+ ],
+ "︻": [
+ " ### ",
+ " # ",
+ " # ",
+ " # ",
+ " ### ",
+ " ",
+ " "
+ ],
+ "︿": [
+ " # ",
+ " # # ",
+ "# #",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "﹃": [
+ "## ",
+ " # ",
+ " # ",
+ " # ",
+ " ",
+ " ",
+ " "
+ ],
+ "﹏": [
+ " ",
+ " ",
+ " ",
+ " ",
+ " # # ",
+ "# #",
+ " "
+ ],
+ ")": [
+ " # ",
+ " # ",
+ " # ",
+ " # ",
+ " # ",
+ " ",
+ " "
+ ],
+ "-": [
+ " ",
+ " ",
+ "#####",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "/": [
+ " ",
+ " #",
+ " # ",
+ " # ",
+ " # ",
+ "# ",
+ " "
+ ],
+ "\": [
+ " ",
+ "# ",
+ " # ",
+ " # ",
+ " # ",
+ " #",
+ " "
+ ],
+ "^": [
+ " # ",
+ " # # ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "_": [
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ "#####",
+ " "
+ ],
+ "`": [
+ " # ",
+ " # ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "|": [
+ " # ",
+ " # ",
+ " # ",
+ " # ",
+ " # ",
+ " ",
+ " "
+ ],
+ "。": [
+ " ",
+ " ",
+ " ",
+ " ",
+ " # ",
+ " ",
+ " "
+ ],
+ "・": [
+ " ",
+ " ",
+ " # ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "ェ": [
+ " ## ",
+ "### ",
+ " ## ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ "ノ": [
+ " ",
+ " # ",
+ " # ",
+ " # ",
+ "# ",
+ " ",
+ " "
+ ],
+ "ミ": [
+ "# ",
+ " # ",
+ " # ",
+ " # ",
+ " ",
+ " ",
+ " "
+ ],
+ "゚": [
+ " ## ",
+ "# # ",
+ " ## ",
+ " ",
+ " ",
+ " ",
+ " "
+ ],
+ " ̄": [
+ "#####",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ]
+};
+
+if (typeof module !== 'undefined' && module.exports) {
+ module.exports = { FONT_5x7 };
+}
diff --git a/backup/app.js b/backup/app.js
new file mode 100644
index 0000000..f303f5c
--- /dev/null
+++ b/backup/app.js
@@ -0,0 +1,1756 @@
+let DISPLAY_WIDTH = 120;
+const DISPLAY_HEIGHT = 60;
+const BRIGHTNESS_THRESHOLD = 127;
+const BORDER_BLACK_THRESHOLD = 64;
+const BORDER_WHITE_THRESHOLD = 191;
+
+document.documentElement.style.setProperty('--col-count', DISPLAY_WIDTH);
+
+ const upload = document.getElementById('upload');
+ const dropzone = document.getElementById('dropzone');
+ const matrix = document.getElementById('matrix');
+ const exportBtn = document.getElementById('export');
+ const invertBtn = document.getElementById('invert');
+ const ditherBtn = document.getElementById('dither');
+ const drawBtn = document.getElementById('draw');
+ const eraseBtn = document.getElementById('erase');
+ const resetBtn = document.getElementById('reset');
+ const brushSizeSelect = document.getElementById('brushSize');
+ const animationDurationInput = document.getElementById('animationDuration');
+ const revertDurationBtn = document.getElementById('revertDuration');
+ const textInputArea = document.getElementById('textInputArea');
+ const modeRadios = document.querySelectorAll('input[name="mode"]');
+
+ let currentMode = "image";
+ let drawMode = false;
+ let eraseMode = false;
+ let brushSize = 1;
+ let currentImage = null;
+ let currentFile = null;
+ let invertImageColors = false;
+ let useDithering = false;
+ let hasManualEdits = false;
+ let isDurationManuallyLocked = false; // Track if user manually adjusted duration
+
+ // GIF-specific state
+ let isAnimatedGif = false;
+ let gifFrames = [];
+ let gifFrameEdits = [];
+ let currentFrameIndex = 0;
+ let gifReader = null;
+ let isPlaying = false;
+ let animationTimer = null;
+
+ // Animation creation state
+ let isAnimationCreationMode = false;
+ let animationSourceFiles = []; // Store original files for multi-image animation
+
+ // --- Helper functions ---
+ // Calculate default duration from frame count (0.2s per frame)
+ const DEFAULT_FRAME_DELAY = 0.2; // seconds per frame
+
+ function calculateDurationFromFrames(frameCount) {
+ if (frameCount === 0) return 3.0; // default for empty
+ return Math.round(frameCount * DEFAULT_FRAME_DELAY * 10) / 10; // round to 0.1s
+ }
+
+ function updateAnimationDuration() {
+ if (isDurationManuallyLocked) return; // Don't update if manually locked
+
+ const frameCount = gifFrames.length;
+ const newDuration = calculateDurationFromFrames(frameCount);
+ animationDurationInput.value = newDuration.toFixed(1);
+ }
+
+ // Handle drawing/erasing at specific grid coordinates with brush size
+ function toggleDotAtCoordinates(x, y) {
+ const radius = brushSize / 2;
+
+ // Calculate the bounding box for the brush
+ const minX = Math.floor(x - radius);
+ const maxX = Math.floor(x + radius);
+ const minY = Math.floor(y - radius);
+ const maxY = Math.floor(y + radius);
+
+ // Iterate through all dots in the bounding box
+ for (let cy = minY; cy <= maxY; cy++) {
+ for (let cx = minX; cx <= maxX; cx++) {
+ // Skip if outside display bounds
+ if (cx < 0 || cx >= DISPLAY_WIDTH || cy < 0 || cy >= DISPLAY_HEIGHT) continue;
+
+ // Calculate distance from center
+ const distance = Math.sqrt((cx - x) * (cx - x) + (cy - y) * (cy - y));
+
+ // Only activate dots within the circular radius
+ if (distance <= radius) {
+ const dot = document.querySelector(`.dot[data-x="${cx}"][data-y="${cy}"]`);
+ if (!dot) continue;
+
+ if (drawMode && !dot.classList.contains('on')) {
+ dot.classList.add('on');
+ hasManualEdits = true;
+ trackDotEdit(cx, cy, 'add');
+ }
+ if (eraseMode && dot.classList.contains('on')) {
+ dot.classList.remove('on');
+ hasManualEdits = true;
+ trackDotEdit(cx, cy, 'remove');
+ }
+ }
+ }
+ }
+ }
+
+ // Calculate which dot is closest to the click position
+ function getDotCoordinatesFromEvent(e) {
+ const rect = matrix.getBoundingClientRect();
+ const clickX = e.clientX - rect.left;
+ const clickY = e.clientY - rect.top;
+
+ // Each dot is 10px + 1px gap = 11px total
+ const dotSize = 11;
+
+ // Calculate grid coordinates
+ const gridX = Math.floor(clickX / dotSize);
+ const gridY = Math.floor(clickY / dotSize);
+
+ return { x: gridX, y: gridY };
+ }
+
+ // Matrix-level event handlers for drawing
+ let isDrawing = false;
+
+ function handleMatrixMouseDown(e) {
+ if (!drawMode && !eraseMode) return;
+
+ isDrawing = true;
+ const coords = getDotCoordinatesFromEvent(e);
+ toggleDotAtCoordinates(coords.x, coords.y);
+ e.preventDefault();
+ }
+
+ function handleMatrixMouseMove(e) {
+ if (!isDrawing) return;
+ if (!drawMode && !eraseMode) return;
+
+ const coords = getDotCoordinatesFromEvent(e);
+ toggleDotAtCoordinates(coords.x, coords.y);
+ e.preventDefault();
+ }
+
+ function handleMatrixMouseUp(e) {
+ isDrawing = false;
+ }
+
+ function handleMatrixMouseLeave(e) {
+ isDrawing = false;
+ }
+
+ function addDotInteraction(dot) {
+ // Individual dot interactions are now handled at the matrix level
+ // This function is kept for compatibility but does nothing
+ }
+
+ function setButtonActive(button, active) {
+ button.classList.toggle('active', active);
+ }
+
+ function toggleDrawEraseButtons(activeDraw) {
+ setButtonActive(drawBtn, activeDraw);
+ setButtonActive(eraseBtn, !activeDraw);
+ drawMode = activeDraw;
+ eraseMode = !activeDraw;
+ }
+
+ // Helper function to track dot edits for GIF frames
+ function trackDotEdit(x, y, action) {
+ if (!isAnimatedGif) return;
+
+ const key = `${x},${y}`;
+ if (action === 'add') {
+ gifFrameEdits[currentFrameIndex].addedDots.add(key);
+ gifFrameEdits[currentFrameIndex].removedDots.delete(key);
+ } else if (action === 'remove') {
+ gifFrameEdits[currentFrameIndex].removedDots.add(key);
+ gifFrameEdits[currentFrameIndex].addedDots.delete(key);
+ }
+ }
+
+ // Helper function to create a frame object
+ function createFrameObject(sourceFile, delay = 20) {
+ return {
+ imageData: null,
+ delay: delay,
+ processed: false,
+ sourceFile: sourceFile
+ };
+ }
+
+ // Helper function to create a frame edits object
+ function createFrameEditsObject() {
+ return {
+ addedDots: new Set(),
+ removedDots: new Set()
+ };
+ }
+
+ // Helper function to apply a single dot edit to the DOM
+ function applyDotEditToDOM(key, action) {
+ const [x, y] = key.split(',').map(Number);
+ const dot = document.querySelector(`.dot[data-x="${x}"][data-y="${y}"]`);
+
+ if (!dot) return;
+
+ if (action === 'add' && !dot.classList.contains('on')) {
+ dot.classList.add('on');
+ } else if (action === 'remove' && dot.classList.contains('on')) {
+ dot.classList.remove('on');
+ }
+ }
+
+ function applyDithering(data, width, height) {
+ for (let y = 0; y < height; y++) {
+ for (let x = 0; x < width; x++) {
+ const i = (y * width + x) * 4;
+ const oldGray = (data.data[i] + data.data[i+1] + data.data[i+2]) / 3;
+ const newGray = oldGray > BRIGHTNESS_THRESHOLD ? 255 : 0;
+ const error = oldGray - newGray;
+
+ const val = invertImageColors ? 255 - newGray : newGray;
+ data.data[i] = data.data[i+1] = data.data[i+2] = val;
+
+ // Distribute error to neighboring pixels
+ if (x + 1 < width) {
+ const ri = (y * width + (x + 1)) * 4;
+ data.data[ri] = data.data[ri] + error * 7/16;
+ data.data[ri+1] = data.data[ri+1] + error * 7/16;
+ data.data[ri+2] = data.data[ri+2] + error * 7/16;
+ }
+ if (y + 1 < height) {
+ if (x - 1 >= 0) {
+ const bli = ((y+1) * width + (x - 1)) * 4;
+ data.data[bli] = data.data[bli] + error * 3/16;
+ data.data[bli+1] = data.data[bli+1] + error * 3/16;
+ data.data[bli+2] = data.data[bli+2] + error * 3/16;
+ }
+ const bi = ((y+1) * width + x) * 4;
+ data.data[bi] = data.data[bi] + error * 5/16;
+ data.data[bi+1] = data.data[bi+1] + error * 5/16;
+ data.data[bi+2] = data.data[bi+2] + error * 5/16;
+
+ if (x + 1 < width) {
+ const bri = ((y+1) * width + (x + 1)) * 4;
+ data.data[bri] = data.data[bri] + error * 1/16;
+ data.data[bri+1] = data.data[bri+1] + error * 1/16;
+ data.data[bri+2] = data.data[bri+2] + error * 1/16;
+ }
+ }
+ }
+ }
+ }
+
+ function invertCurrentMatrix() {
+ const dots = document.querySelectorAll('.dot');
+ dots.forEach(dot => {
+ dot.classList.toggle('on');
+ });
+ }
+
+ async function generateJSONExport() {
+ if (currentMode !== "image") {
+ // Text mode not supported by this function
+ return null;
+ }
+
+ if (isAnimatedGif) {
+ // Export all GIF frames as array of arrays
+ const allFrames = [];
+
+ // First, ensure all frames are processed
+ for (let i = 0; i < gifFrames.length; i++) {
+ try {
+ // Use appropriate processing based on whether it's a GIF or multi-image animation
+ if (animationSourceFiles && animationSourceFiles.length > 0) {
+ // Multi-image animation
+ await processStaticImageFrame(i);
+ } else {
+ // GIF animation
+ await processGifFrameAsync(i);
+ }
+ } catch (err) {
+ console.error(`Error processing frame ${i}:`, err);
+ }
+ }
+
+ // Now extract pixel data from all frames
+ for (let i = 0; i < gifFrames.length; i++) {
+ const frame = gifFrames[i];
+ const edits = gifFrameEdits[i];
+ const onDots = [];
+
+ if (frame.imageData) {
+ const data = frame.imageData.data;
+
+ // Extract "on" pixels from base frame
+ for (let y = 0; y < DISPLAY_HEIGHT; y++) {
+ for (let x = 0; x < DISPLAY_WIDTH; x++) {
+ const idx = (y * DISPLAY_WIDTH + x) * 4;
+ const brightness = (data[idx] + data[idx+1] + data[idx+2]) / 3;
+ const isOn = brightness > BRIGHTNESS_THRESHOLD;
+ const key = `${x},${y}`;
+
+ // Apply manual edits
+ let finalState = isOn;
+ if (edits.addedDots.has(key)) finalState = true;
+ if (edits.removedDots.has(key)) finalState = false;
+
+ if (finalState) {
+ onDots.push([x, y]);
+ }
+ }
+ }
+ }
+
+ allFrames.push(onDots);
+ }
+
+ return JSON.stringify(allFrames);
+ } else {
+ // Static image export
+ const onDots = Array.from(document.querySelectorAll('.dot.on')).map(el => [Number(el.dataset.x), Number(el.dataset.y)]);
+ return JSON.stringify(onDots);
+ }
+ }
+
+ // Helper function to check if there's user content
+ function hasUserContent() {
+ // Check if there's a loaded file or manual edits or any dots turned on
+ if (currentFile) return true;
+ if (hasManualEdits) return true;
+ if (gifFrames.length > 0) return true;
+ const onDots = document.querySelectorAll('.dot.on');
+ return onDots.length > 0;
+ }
+
+ // --- Mode toggle ---
+ modeRadios.forEach(r => {
+ r.addEventListener('change', (e) => {
+ const newMode = r.value;
+
+ // Check if switching from Image to Text mode with content
+ if (currentMode === "image" && newMode === "text" && hasUserContent()) {
+ if (!confirm('Switching to Text mode will clear all current content. Continue?')) {
+ // Revert the radio button selection
+ document.querySelector('input[name="mode"][value="image"]').checked = true;
+ e.preventDefault();
+ return;
+ }
+ }
+
+ stopAnimation();
+ currentMode = newMode;
+ drawMode = false;
+ eraseMode = false;
+ hasManualEdits = false;
+ setButtonActive(drawBtn, false);
+ setButtonActive(eraseBtn, false);
+
+ // Reset GIF state when switching modes
+ isAnimatedGif = false;
+ gifReader = null;
+ gifFrames = [];
+ gifFrameEdits = [];
+ animationSourceFiles = [];
+ currentFrameIndex = 0;
+ updateFrameControls();
+
+ if (currentMode === "image") {
+ dropzone.style.display = "block";
+ textInputArea.style.display = "none";
+ ditherBtn.style.display = "inline-block";
+ drawBtn.style.display = "inline-block";
+ eraseBtn.style.display = "inline-block";
+ brushSizeSelect.parentElement.style.display = "inline-block";
+ animationCreationCheckbox.disabled = false;
+ animationCreationCheckbox.parentElement.style.opacity = "1";
+ animationDurationInput.parentElement.style.display = "inline-block";
+ // Show revert button only if duration is locked
+ revertDurationBtn.style.display = isDurationManuallyLocked ? "inline-block" : "none";
+ } else {
+ dropzone.style.display = "none";
+ textInputArea.style.display = "block";
+ ditherBtn.style.display = "none";
+ drawBtn.style.display = "none";
+ eraseBtn.style.display = "none";
+ brushSizeSelect.parentElement.style.display = "none";
+ animationCreationCheckbox.disabled = true;
+ animationCreationCheckbox.parentElement.style.opacity = "0.5";
+ animationDurationInput.parentElement.style.display = "none";
+ revertDurationBtn.style.display = "none"; // Always hidden in text mode
+ currentFile = null; // Clear file reference in text mode
+ emptyCanvas();
+ }
+ });
+ });
+
+ // --- Animation Creation checkbox ---
+ const animationCreationCheckbox = document.getElementById('animationCreation');
+
+ // Initialize controls visibility for default mode (image)
+ drawBtn.style.display = "inline-block";
+ eraseBtn.style.display = "inline-block";
+ brushSizeSelect.parentElement.style.display = "inline-block";
+ animationCreationCheckbox.disabled = false;
+ animationCreationCheckbox.parentElement.style.opacity = "1";
+ animationDurationInput.parentElement.style.display = "inline-block";
+ revertDurationBtn.style.display = "none"; // Hidden by default (auto-update mode)
+
+ // Helper function to toggle animation creation mode
+ function setAnimationCreationMode(enabled) {
+ isAnimationCreationMode = enabled;
+ animationCreationCheckbox.checked = enabled;
+
+ if (enabled) {
+ upload.setAttribute('multiple', 'multiple');
+ } else {
+ upload.removeAttribute('multiple');
+ }
+
+ // Show/hide revert button based on animation mode and lock state
+ if (enabled && isDurationManuallyLocked) {
+ revertDurationBtn.style.display = "inline-block";
+ } else {
+ revertDurationBtn.style.display = "none";
+ }
+
+ updateFrameControls();
+ }
+
+ animationCreationCheckbox.addEventListener('change', () => {
+ setAnimationCreationMode(animationCreationCheckbox.checked);
+ });
+
+ function emptyCanvas() {
+ matrix.innerHTML = '';
+ matrix.style.setProperty('--col-count', DISPLAY_WIDTH);
+ for (let y = 0; y < DISPLAY_HEIGHT; y++) {
+ for (let x = 0; x < DISPLAY_WIDTH; x++) {
+ const dot = document.createElement('div');
+ dot.classList.add('dot');
+ dot.dataset.x = x;
+ dot.dataset.y = y;
+ addDotInteraction(dot);
+ matrix.appendChild(dot);
+ }
+ }
+ updateFrameControls();
+ }
+
+ async function loadImage(file) {
+ // Check if file is a GIF
+ if (await isGifFile(file)) {
+ await loadGif(file);
+ } else {
+ // Existing static image logic
+ currentFile = file;
+ hasManualEdits = false;
+ isAnimatedGif = false;
+ gifReader = null;
+ gifFrames = [];
+ gifFrameEdits = [];
+ animationSourceFiles = [];
+ updateFrameControls();
+
+ const img = new Image();
+ img.onload = () => {
+ currentImage = img;
+ processImageToBW(img, (bwCanvas) => {
+ const ctx = bwCanvas.getContext('2d');
+ const imgData = ctx.getImageData(0, 0, bwCanvas.width, bwCanvas.height);
+ displayMatrix(imgData);
+ });
+ };
+ img.src = URL.createObjectURL(file);
+ }
+ }
+
+ async function loadMultipleImages(files) {
+ currentFile = null;
+ gifReader = null;
+
+ // Expand GIF files into individual frames
+ const expandedFiles = [];
+ for (const file of files) {
+ if (await isGifFile(file)) {
+ // Extract all frames from GIF
+ const frameBlobs = await extractGifFramesAsBlobs(file);
+ expandedFiles.push(...frameBlobs);
+ } else {
+ // Add non-GIF files as-is
+ expandedFiles.push(file);
+ }
+ }
+
+ // New files to add (keep in upload order, don't sort)
+ const newFiles = expandedFiles;
+
+ // Check if we're inserting into existing animation or starting fresh
+ const isInserting = animationSourceFiles.length > 0;
+
+ if (isInserting) {
+ // Insert new frames after current frame
+ const insertPosition = currentFrameIndex + 1;
+
+ // Split existing arrays at insertion point
+ const beforeFrames = animationSourceFiles.slice(0, insertPosition);
+ const afterFrames = animationSourceFiles.slice(insertPosition);
+ const beforeGifFrames = gifFrames.slice(0, insertPosition);
+ const afterGifFrames = gifFrames.slice(insertPosition);
+ const beforeEdits = gifFrameEdits.slice(0, insertPosition);
+ const afterEdits = gifFrameEdits.slice(insertPosition);
+
+ // Rebuild source files array with insertion
+ animationSourceFiles = [...beforeFrames, ...newFiles, ...afterFrames];
+
+ // Initialize new frame objects
+ const newGifFrames = [];
+ const newGifFrameEdits = [];
+ for (let i = 0; i < newFiles.length; i++) {
+ newGifFrames.push(createFrameObject(newFiles[i]));
+ newGifFrameEdits.push(createFrameEditsObject());
+ }
+
+ // Rebuild frame arrays with insertion
+ gifFrames = [...beforeGifFrames, ...newGifFrames, ...afterGifFrames];
+ gifFrameEdits = [...beforeEdits, ...newGifFrameEdits, ...afterEdits];
+
+ // Move to first newly inserted frame
+ currentFrameIndex = insertPosition;
+
+ } else {
+ // Starting fresh - no existing animation
+ hasManualEdits = false;
+ animationSourceFiles = newFiles;
+
+ // Initialize frame storage
+ const numFrames = newFiles.length;
+ gifFrames = new Array(numFrames);
+ gifFrameEdits = new Array(numFrames);
+
+ for (let i = 0; i < numFrames; i++) {
+ gifFrames[i] = createFrameObject(newFiles[i]);
+ gifFrameEdits[i] = createFrameEditsObject();
+ }
+
+ currentFrameIndex = 0;
+ }
+
+ // Set animation state
+ isAnimatedGif = gifFrames.length > 1;
+
+ // Process and display current frame
+ await processStaticImageFrame(currentFrameIndex);
+
+ // Update UI
+ updateFrameControls();
+ updateFrameCounter();
+ updateAnimationDuration();
+ }
+
+ async function processStaticImageFrame(frameIndex) {
+ if (frameIndex >= animationSourceFiles.length) return;
+ if (!gifFrames[frameIndex]) return; // Safety check for undefined frame
+
+ const file = animationSourceFiles[frameIndex];
+
+ return new Promise((resolve, reject) => {
+ const img = new Image();
+ img.onload = () => {
+ processImageToBW(img, (bwCanvas) => {
+ const ctx = bwCanvas.getContext('2d');
+ const processedData = ctx.getImageData(0, 0, bwCanvas.width, bwCanvas.height);
+
+ // Safety check - frame might have been cleared during async operation
+ if (!gifFrames[frameIndex]) {
+ resolve();
+ return;
+ }
+
+ // Store processed frame
+ gifFrames[frameIndex].imageData = processedData;
+ gifFrames[frameIndex].processed = true;
+
+ // Display frame if it's the current one
+ if (frameIndex === currentFrameIndex) {
+ displayGifFrame(frameIndex);
+ }
+
+ resolve();
+ });
+ };
+ img.onerror = () => reject('Failed to load image');
+ img.src = URL.createObjectURL(file);
+ });
+ }
+
+ async function isGifFile(file) {
+ const arrayBuffer = await file.arrayBuffer();
+ const bytes = new Uint8Array(arrayBuffer);
+ // GIF89a magic: 0x47 0x49 0x46 0x38 0x39 0x61
+ // GIF87a magic: 0x47 0x49 0x46 0x38 0x37 0x61
+ return bytes.length >= 6 &&
+ bytes[0] === 0x47 && bytes[1] === 0x49 && bytes[2] === 0x46 &&
+ bytes[3] === 0x38 && (bytes[4] === 0x39 || bytes[4] === 0x37) &&
+ bytes[5] === 0x61;
+ }
+
+ async function loadGif(file) {
+ currentFile = file;
+ hasManualEdits = false;
+
+ const arrayBuffer = await file.arrayBuffer();
+ const bytes = new Uint8Array(arrayBuffer);
+
+ try {
+ gifReader = new GifReader(bytes);
+ const numFrames = gifReader.numFrames();
+
+ // Initialize frame storage
+ gifFrames = new Array(numFrames);
+ gifFrameEdits = new Array(numFrames);
+
+ for (let i = 0; i < numFrames; i++) {
+ const frameInfo = gifReader.frameInfo(i);
+ gifFrames[i] = {
+ imageData: null,
+ delay: frameInfo.delay,
+ processed: false
+ };
+ gifFrameEdits[i] = {
+ addedDots: new Set(),
+ removedDots: new Set()
+ };
+ }
+
+ isAnimatedGif = numFrames > 1;
+ currentFrameIndex = 0;
+
+ // Auto-switch to animation creation mode for animated GIFs
+ if (isAnimatedGif && !isAnimationCreationMode) {
+ setAnimationCreationMode(true);
+ }
+
+ // If in animation creation mode, extract frames and use multi-image approach
+ if (isAnimationCreationMode && isAnimatedGif) {
+ const frameBlobs = await extractGifFramesAsBlobs(file);
+ await loadMultipleImages(frameBlobs);
+ return; // Exit early, loadMultipleImages handles the rest
+ }
+
+ // Process and display first frame (for non-animation-creation mode)
+ processAndDisplayGifFrame(0);
+
+ // Update UI
+ updateFrameControls();
+ updateFrameCounter();
+ updateAnimationDuration();
+
+ } catch (err) {
+ alert('Error loading GIF: ' + err.message);
+ console.error(err);
+ }
+ }
+
+ // Helper function to extract all frames from a GIF as Blob objects
+ async function extractGifFramesAsBlobs(file) {
+ const arrayBuffer = await file.arrayBuffer();
+ const bytes = new Uint8Array(arrayBuffer);
+
+ try {
+ const reader = new GifReader(bytes);
+ const numFrames = reader.numFrames();
+ const frameBlobs = [];
+
+ for (let i = 0; i < numFrames; i++) {
+ // Decode frame from GIF to RGBA
+ const width = reader.width;
+ const height = reader.height;
+ const rgbaData = new Uint8Array(width * height * 4);
+ reader.decodeAndBlitFrameRGBA(i, rgbaData);
+
+ // Create canvas with decoded frame
+ const canvas = document.createElement('canvas');
+ canvas.width = width;
+ canvas.height = height;
+ const ctx = canvas.getContext('2d');
+ const imageData = ctx.createImageData(width, height);
+ imageData.data.set(rgbaData);
+ ctx.putImageData(imageData, 0, 0);
+
+ // Convert canvas to Blob
+ const blob = await new Promise(resolve => canvas.toBlob(resolve, 'image/png'));
+
+ // Create a synthetic file name
+ const originalName = file.name.replace(/\.[^/.]+$/, ''); // Remove extension
+ Object.defineProperty(blob, 'name', {
+ value: `${originalName}_frame${String(i + 1).padStart(3, '0')}.png`,
+ writable: false
+ });
+
+ frameBlobs.push(blob);
+ }
+
+ return frameBlobs;
+ } catch (err) {
+ console.error('Error extracting GIF frames:', err);
+ return [file]; // Fallback to original file if extraction fails
+ }
+ }
+
+ function processGifFrameAsync(frameIndex) {
+ return new Promise((resolve, reject) => {
+ if (!gifReader || frameIndex >= gifFrames.length || !gifFrames[frameIndex]) {
+ reject('Invalid frame index');
+ return;
+ }
+
+ // Check if frame already processed
+ if (gifFrames[frameIndex].processed) {
+ resolve();
+ return;
+ }
+
+ // Decode frame from GIF to RGBA
+ const width = gifReader.width;
+ const height = gifReader.height;
+ const rgbaData = new Uint8Array(width * height * 4);
+ gifReader.decodeAndBlitFrameRGBA(frameIndex, rgbaData);
+
+ // Create temporary canvas with decoded frame
+ const tempCanvas = document.createElement('canvas');
+ tempCanvas.width = width;
+ tempCanvas.height = height;
+ const tempCtx = tempCanvas.getContext('2d');
+ const tempImageData = tempCtx.createImageData(width, height);
+ tempImageData.data.set(rgbaData);
+ tempCtx.putImageData(tempImageData, 0, 0);
+
+ // Create Image object for processing
+ const img = new Image();
+ img.onload = () => {
+ processImageToBW(img, (bwCanvas) => {
+ const ctx = bwCanvas.getContext('2d');
+ const processedData = ctx.getImageData(0, 0, bwCanvas.width, bwCanvas.height);
+
+ // Safety check - frame might have been cleared during async operation
+ if (!gifFrames[frameIndex]) {
+ resolve();
+ return;
+ }
+
+ // Store processed frame
+ gifFrames[frameIndex].imageData = processedData;
+ gifFrames[frameIndex].processed = true;
+
+ resolve();
+ });
+ };
+ img.onerror = () => reject('Failed to load frame');
+ img.src = tempCanvas.toDataURL();
+ });
+ }
+
+ function processAndDisplayGifFrame(frameIndex) {
+ if (frameIndex >= gifFrames.length || !gifFrames[frameIndex]) return;
+
+ // Check if frame already processed with current settings
+ if (gifFrames[frameIndex].processed) {
+ displayGifFrame(frameIndex);
+ return;
+ }
+
+ // For multi-image animations (not GIFs)
+ if (animationSourceFiles && animationSourceFiles.length > 0) {
+ processStaticImageFrame(frameIndex).then(() => {
+ displayGifFrame(frameIndex);
+ }).catch(err => {
+ console.error('Error processing static image frame:', err);
+ });
+ return;
+ }
+
+ // For GIF frames
+ if (!gifReader) return;
+
+ // Use the async version but don't wait
+ processGifFrameAsync(frameIndex).then(() => {
+ displayGifFrame(frameIndex);
+ }).catch(err => {
+ console.error('Error processing GIF frame:', err);
+ });
+ }
+
+ function displayGifFrame(frameIndex) {
+ if (!gifFrames[frameIndex] || !gifFrames[frameIndex].imageData) return;
+
+ currentFrameIndex = frameIndex;
+ const imageData = gifFrames[frameIndex].imageData;
+
+ // Display base frame using existing function
+ displayMatrix(imageData);
+
+ // Apply manual edits for this frame
+ const edits = gifFrameEdits[frameIndex];
+ if (edits) {
+ edits.addedDots.forEach(key => applyDotEditToDOM(key, 'add'));
+ edits.removedDots.forEach(key => applyDotEditToDOM(key, 'remove'));
+ }
+
+ // Update frame counter UI
+ updateFrameCounter();
+ }
+
+ function navigateToFrame(direction) {
+ if (!isAnimatedGif || gifFrames.length === 0) return;
+
+ let newIndex = currentFrameIndex;
+ if (direction === 'next') {
+ newIndex = (currentFrameIndex + 1) % gifFrames.length;
+ } else if (direction === 'prev') {
+ newIndex = (currentFrameIndex - 1 + gifFrames.length) % gifFrames.length;
+ }
+
+ processAndDisplayGifFrame(newIndex);
+ }
+
+ async function deleteCurrentFrame() {
+ if (!isAnimatedGif || gifFrames.length <= 1) {
+ return;
+ }
+
+ // Only allow deletion for multi-image animations (not GIFs)
+ if (!animationSourceFiles || animationSourceFiles.length === 0) {
+ return;
+ }
+
+ stopAnimation();
+
+ // Remove frame from all arrays
+ animationSourceFiles.splice(currentFrameIndex, 1);
+ gifFrames.splice(currentFrameIndex, 1);
+ gifFrameEdits.splice(currentFrameIndex, 1);
+
+ // Adjust current frame index
+ if (currentFrameIndex >= gifFrames.length) {
+ currentFrameIndex = gifFrames.length - 1;
+ }
+
+ // Update animation state
+ isAnimatedGif = gifFrames.length > 1;
+
+ // Display the new current frame
+ if (gifFrames.length > 0) {
+ if (animationSourceFiles.length > 0) {
+ await processStaticImageFrame(currentFrameIndex);
+ } else {
+ processAndDisplayGifFrame(currentFrameIndex);
+ }
+ updateFrameControls();
+ updateFrameCounter();
+ updateAnimationDuration();
+ } else {
+ // No frames left, reset to empty canvas
+ isAnimatedGif = false;
+ emptyCanvas();
+ updateFrameControls();
+ updateFrameCounter();
+ updateAnimationDuration();
+ }
+ }
+
+ // Helper function to capture current canvas state as a Blob
+ async function captureCurrentCanvasAsBlob(name = 'captured_frame') {
+ const canvas = document.createElement('canvas');
+ canvas.width = DISPLAY_WIDTH;
+ canvas.height = DISPLAY_HEIGHT;
+ const ctx = canvas.getContext('2d');
+ const imageData = ctx.createImageData(DISPLAY_WIDTH, DISPLAY_HEIGHT);
+
+ // Read current state from DOM
+ for (let y = 0; y < DISPLAY_HEIGHT; y++) {
+ for (let x = 0; x < DISPLAY_WIDTH; x++) {
+ const dot = document.querySelector(`.dot[data-x="${x}"][data-y="${y}"]`);
+ const isOn = dot && dot.classList.contains('on');
+ const idx = (y * DISPLAY_WIDTH + x) * 4;
+ const val = isOn ? 255 : 0;
+ imageData.data[idx] = val; // R
+ imageData.data[idx + 1] = val; // G
+ imageData.data[idx + 2] = val; // B
+ imageData.data[idx + 3] = 255; // A
+ }
+ }
+
+ ctx.putImageData(imageData, 0, 0);
+
+ const blob = await new Promise(resolve => canvas.toBlob(resolve, 'image/png'));
+ Object.defineProperty(blob, 'name', {
+ value: `${name}_${Date.now()}.png`,
+ writable: false
+ });
+
+ return blob;
+ }
+
+ // Add an empty frame after the current one
+ async function addEmptyFrame() {
+ if (!isAnimationCreationMode) return;
+
+ stopAnimation();
+
+ // If no frames exist, first capture the current canvas state
+ if (gifFrames.length === 0) {
+ const currentBlob = await captureCurrentCanvasAsBlob('current_frame');
+
+ // Add current state as frame 0
+ animationSourceFiles.push(currentBlob);
+ gifFrames.push(createFrameObject(currentBlob));
+ gifFrameEdits.push(createFrameEditsObject());
+
+ // Set to frame 0
+ currentFrameIndex = 0;
+
+ // Process frame 0 (this preserves the current drawing)
+ await processStaticImageFrame(0);
+ }
+
+ // Now add the empty frame after the current frame
+ const insertPosition = currentFrameIndex + 1;
+
+ // Create a blank canvas as a Blob
+ const canvas = document.createElement('canvas');
+ canvas.width = DISPLAY_WIDTH;
+ canvas.height = DISPLAY_HEIGHT;
+ const ctx = canvas.getContext('2d');
+ ctx.fillStyle = 'black';
+ ctx.fillRect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT);
+
+ const blob = await new Promise(resolve => canvas.toBlob(resolve, 'image/png'));
+ Object.defineProperty(blob, 'name', {
+ value: `empty_frame_${Date.now()}.png`,
+ writable: false
+ });
+
+ // Insert into arrays
+ animationSourceFiles.splice(insertPosition, 0, blob);
+ gifFrames.splice(insertPosition, 0, createFrameObject(blob));
+ gifFrameEdits.splice(insertPosition, 0, createFrameEditsObject());
+
+ // Update state and switch to new frame
+ isAnimatedGif = gifFrames.length > 1;
+ currentFrameIndex = insertPosition;
+
+ // Process and display the new frame
+ await processStaticImageFrame(currentFrameIndex);
+ updateFrameControls();
+ updateFrameCounter();
+ updateAnimationDuration();
+ }
+
+ // Duplicate the current frame
+ async function duplicateCurrentFrame() {
+ if (!isAnimationCreationMode) return;
+
+ stopAnimation();
+
+ // If no frames exist, first capture the current canvas state
+ if (gifFrames.length === 0) {
+ const currentBlob = await captureCurrentCanvasAsBlob('current_frame');
+
+ // Add current state as frame 0
+ animationSourceFiles.push(currentBlob);
+ gifFrames.push(createFrameObject(currentBlob));
+ gifFrameEdits.push(createFrameEditsObject());
+
+ // Set to frame 0
+ currentFrameIndex = 0;
+
+ // Process frame 0
+ await processStaticImageFrame(0);
+ }
+
+ // Now duplicate the current frame
+ const insertPosition = currentFrameIndex + 1;
+
+ // Get current frame data
+ const currentFrame = gifFrames[currentFrameIndex];
+ if (!currentFrame) return; // Safety check
+ if (!currentFrame.imageData) {
+ await processStaticImageFrame(currentFrameIndex);
+ }
+
+ // Create a canvas with the current frame's content (including edits)
+ const canvas = document.createElement('canvas');
+ canvas.width = DISPLAY_WIDTH;
+ canvas.height = DISPLAY_HEIGHT;
+ const ctx = canvas.getContext('2d');
+
+ // Draw the base image
+ ctx.putImageData(gifFrames[currentFrameIndex].imageData, 0, 0);
+
+ // Apply edits to the canvas
+ const edits = gifFrameEdits[currentFrameIndex];
+ const imageData = ctx.getImageData(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT);
+
+ edits.addedDots.forEach(key => {
+ const [x, y] = key.split(',').map(Number);
+ const idx = (y * DISPLAY_WIDTH + x) * 4;
+ imageData.data[idx] = imageData.data[idx+1] = imageData.data[idx+2] = 255;
+ });
+
+ edits.removedDots.forEach(key => {
+ const [x, y] = key.split(',').map(Number);
+ const idx = (y * DISPLAY_WIDTH + x) * 4;
+ imageData.data[idx] = imageData.data[idx+1] = imageData.data[idx+2] = 0;
+ });
+
+ ctx.putImageData(imageData, 0, 0);
+
+ // Convert to Blob
+ const blob = await new Promise(resolve => canvas.toBlob(resolve, 'image/png'));
+ Object.defineProperty(blob, 'name', {
+ value: `duplicate_frame_${Date.now()}.png`,
+ writable: false
+ });
+
+ // Insert into arrays
+ animationSourceFiles.splice(insertPosition, 0, blob);
+ gifFrames.splice(insertPosition, 0, createFrameObject(blob));
+ gifFrameEdits.splice(insertPosition, 0, createFrameEditsObject());
+
+ // Update state and switch to new frame
+ isAnimatedGif = gifFrames.length > 1;
+ currentFrameIndex = insertPosition;
+
+ // Process and display the new frame
+ await processStaticImageFrame(currentFrameIndex);
+ updateFrameControls();
+ updateFrameCounter();
+ updateAnimationDuration();
+ }
+
+ function updateFrameControls() {
+ const frameControls = document.getElementById('frameControls');
+ const deleteBtn = document.getElementById('deleteFrame');
+ const addEmptyBtn = document.getElementById('addEmptyFrame');
+ const duplicateBtn = document.getElementById('duplicateFrame');
+ const prevBtn = document.getElementById('prevFrame');
+ const nextBtn = document.getElementById('nextFrame');
+ const playBtn = document.getElementById('playStop');
+
+ // Show controls if:
+ // 1. Multiple frames exist (GIF or multi-image animation), OR
+ // 2. Animation creation mode is active (even with 0 frames, so user can add first frame)
+ const shouldShowControls = (gifFrames.length > 1 && (isAnimatedGif || animationSourceFiles.length > 0)) ||
+ isAnimationCreationMode;
+
+ if (shouldShowControls) {
+ frameControls.style.display = 'flex';
+
+ // Show navigation buttons (prev, next, play) only when there are 2+ frames
+ if (gifFrames.length > 1) {
+ prevBtn.style.display = 'inline-block';
+ nextBtn.style.display = 'inline-block';
+ playBtn.style.display = 'inline-block';
+ } else {
+ prevBtn.style.display = 'none';
+ nextBtn.style.display = 'none';
+ playBtn.style.display = 'none';
+ }
+
+ // Show delete button when there are multiple frames and it's a multi-image animation
+ if (gifFrames.length > 1 && animationSourceFiles && animationSourceFiles.length > 0) {
+ deleteBtn.style.display = 'inline-block';
+ } else {
+ deleteBtn.style.display = 'none';
+ }
+
+ // Show add empty frame button when in animation creation mode (even with 0 frames)
+ if (isAnimationCreationMode) {
+ addEmptyBtn.style.display = 'inline-block';
+ } else {
+ addEmptyBtn.style.display = 'none';
+ }
+
+ // Show duplicate button when in animation creation mode (even with 0 frames)
+ if (isAnimationCreationMode) {
+ duplicateBtn.style.display = 'inline-block';
+ } else {
+ duplicateBtn.style.display = 'none';
+ }
+ } else {
+ frameControls.style.display = 'none';
+ deleteBtn.style.display = 'none';
+ addEmptyBtn.style.display = 'none';
+ duplicateBtn.style.display = 'none';
+ prevBtn.style.display = 'none';
+ nextBtn.style.display = 'none';
+ playBtn.style.display = 'none';
+ }
+ }
+
+ function updateFrameCounter() {
+ const counter = document.getElementById('frameCounter');
+ const shouldShowCounter = (gifFrames.length > 1 && (isAnimatedGif || animationSourceFiles.length > 0)) ||
+ (isAnimationCreationMode && gifFrames.length >= 1);
+
+ if (shouldShowCounter) {
+ counter.textContent = `Frame ${currentFrameIndex + 1} of ${gifFrames.length}`;
+ } else {
+ counter.textContent = '';
+ }
+ }
+
+ function startAnimation() {
+ if (!isAnimatedGif || isPlaying) return;
+
+ isPlaying = true;
+ const playStopBtn = document.getElementById('playStop');
+ playStopBtn.textContent = '⏸ Stop';
+ setButtonActive(playStopBtn, true);
+
+ animationTimer = setInterval(() => {
+ navigateToFrame('next');
+ }, 200); // 0.2 seconds
+ }
+
+ function stopAnimation() {
+ if (!isPlaying) return;
+
+ isPlaying = false;
+ const playStopBtn = document.getElementById('playStop');
+ playStopBtn.textContent = '▶ Play';
+ setButtonActive(playStopBtn, false);
+
+ if (animationTimer) {
+ clearInterval(animationTimer);
+ animationTimer = null;
+ }
+ }
+
+ function reprocessAllGifFrames() {
+ if (!isAnimatedGif || !gifReader) return;
+
+ // Mark all frames as unprocessed
+ gifFrames.forEach(frame => {
+ frame.processed = false;
+ frame.imageData = null;
+ });
+
+ // Reprocess current frame
+ processAndDisplayGifFrame(currentFrameIndex);
+ }
+
+ function detectBorderColor(img) {
+ const tmpCheck = document.createElement('canvas');
+ tmpCheck.width = img.width;
+ tmpCheck.height = img.height;
+ const ctxCheck = tmpCheck.getContext('2d');
+ ctxCheck.drawImage(img, 0, 0);
+ const checkData = ctxCheck.getImageData(0, 0, img.width, img.height);
+
+ let blackCount = 0;
+ let whiteCount = 0;
+ let totalBorderPixels = 0;
+
+ // Sample border pixels (top, bottom, left, right edges)
+ const sampleBorder = (x, y) => {
+ const i = (y * img.width + x) * 4;
+ const avg = (checkData.data[i] + checkData.data[i+1] + checkData.data[i+2]) / 3;
+ if (avg < BORDER_BLACK_THRESHOLD) blackCount++;
+ else if (avg > BORDER_WHITE_THRESHOLD) whiteCount++;
+ totalBorderPixels++;
+ };
+
+ // Sample top and bottom edges
+ for (let x = 0; x < img.width; x++) {
+ sampleBorder(x, 0);
+ sampleBorder(x, img.height - 1);
+ }
+ // Sample left and right edges (excluding corners already sampled)
+ for (let y = 1; y < img.height - 1; y++) {
+ sampleBorder(0, y);
+ sampleBorder(img.width - 1, y);
+ }
+
+ // Determine background color based on border analysis
+ if (blackCount / totalBorderPixels > 0.5) {
+ return 'black';
+ } else if (whiteCount / totalBorderPixels > 0.5) {
+ return 'white';
+ }
+ return 'black'; // default
+ }
+
+ function processImageToBW(img, callback) {
+ const tmp = document.createElement('canvas');
+ tmp.width = DISPLAY_WIDTH;
+ tmp.height = DISPLAY_HEIGHT;
+ const ctx = tmp.getContext('2d');
+
+ // Calculate aspect ratios
+ const imgAspect = img.width / img.height;
+ const displayAspect = DISPLAY_WIDTH / DISPLAY_HEIGHT;
+
+ let drawWidth, drawHeight, offsetX, offsetY;
+
+ if (imgAspect > displayAspect) {
+ // Image is wider - fit by width, center vertically
+ drawWidth = DISPLAY_WIDTH;
+ drawHeight = DISPLAY_WIDTH / imgAspect;
+ offsetX = 0;
+ offsetY = (DISPLAY_HEIGHT - drawHeight) / 2;
+ } else {
+ // Image is taller - fit by height, center horizontally
+ drawHeight = DISPLAY_HEIGHT;
+ drawWidth = DISPLAY_HEIGHT * imgAspect;
+ offsetX = (DISPLAY_WIDTH - drawWidth) / 2;
+ offsetY = 0;
+ }
+
+ // Detect and use border color for background
+ const bgColor = detectBorderColor(img);
+ ctx.fillStyle = bgColor;
+ ctx.fillRect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT);
+
+ ctx.drawImage(img, offsetX, offsetY, drawWidth, drawHeight);
+ const data = ctx.getImageData(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT);
+
+ if (useDithering) {
+ applyDithering(data, DISPLAY_WIDTH, DISPLAY_HEIGHT);
+ } else {
+ // Simple threshold
+ for (let i = 0; i < data.data.length; i += 4) {
+ const avg = (data.data[i] + data.data[i+1] + data.data[i+2]) / 3;
+ const val = invertImageColors ? 255 - (avg > BRIGHTNESS_THRESHOLD ? 255 : 0) : (avg > BRIGHTNESS_THRESHOLD ? 255 : 0);
+ data.data[i] = data.data[i+1] = data.data[i+2] = val;
+ }
+ }
+
+ ctx.putImageData(data, 0, 0);
+ callback(tmp);
+ }
+
+ function displayMatrix(imageData) {
+ matrix.innerHTML = '';
+ matrix.style.setProperty('--col-count', DISPLAY_WIDTH);
+ const d = imageData.data;
+ for (let y = 0; y < DISPLAY_HEIGHT; y++) {
+ for (let x = 0; x < DISPLAY_WIDTH; x++) {
+ const idx = (y * DISPLAY_WIDTH + x) * 4;
+ const brightness = (d[idx] + d[idx+1] + d[idx+2]) / 3;
+ const isOn = brightness > BRIGHTNESS_THRESHOLD;
+ const dot = document.createElement('div');
+ dot.classList.add('dot');
+ if (isOn) dot.classList.add('on');
+ dot.dataset.x = x;
+ dot.dataset.y = y;
+ addDotInteraction(dot);
+ matrix.appendChild(dot);
+ }
+ }
+ }
+
+ function renderCustomTextToMatrix(text, font, charWidth = 8, charHeight = 12, spacing = 1, lineSpacing = 2) {
+ const lines = text.split("\n");
+ const widestLine = Math.max(...lines.map(line => line.length));
+ const contentWidth = widestLine * (charWidth + spacing);
+
+ // expand display width
+ DISPLAY_WIDTH = Math.max(60, contentWidth + 2);
+ document.documentElement.style.setProperty('--col-count', DISPLAY_WIDTH);
+
+ const c = document.createElement("canvas");
+ c.width = DISPLAY_WIDTH;
+ c.height = DISPLAY_HEIGHT;
+ const ctx = c.getContext("2d");
+
+ ctx.fillStyle = "black";
+ ctx.fillRect(0, 0, c.width, c.height);
+ ctx.fillStyle = "white";
+ let currentXWriteIndex = 0;
+ let startY = 1;
+ for (let l = 0; l < lines.length; l++) {
+ const line = lines[l];
+
+ for (let i = 0; i < line.length; i++) {
+ let ch = line[i];
+ if(!font[ch]) {
+ ch = ch.toUpperCase();
+ }
+ const glyph = font[ch] || font[" "];
+ if (!glyph) continue;
+ for (let y = 0; y < glyph.length; y++) {
+ const row = glyph[y];
+ for (let x = 0; x < row.length; x++) {
+ if (row.charAt(x) === "#") {
+ ctx.fillRect(1 + currentXWriteIndex + x, startY + y, 1, 1);
+ }
+ }
+
+ }
+ currentXWriteIndex += glyph[0].length + spacing;
+ }
+ currentXWriteIndex = spacing;
+ startY += charHeight + lineSpacing;
+ }
+
+ return { imageData: ctx.getImageData(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT), contentWidth };
+ }
+
+ // Auto-update text preview with debouncing (1 second after last keystroke)
+ let textUpdateTimer = null;
+
+ function updateTextPreview() {
+ const text = document.getElementById("scrollText").value.trim();
+ if (!text) {
+ emptyCanvas();
+ document.getElementById('scrolls_output').value = '';
+ return;
+ }
+
+ currentFile = null; // Clear file reference for text content
+ hasManualEdits = false;
+ const { imageData, contentWidth } = renderCustomTextToMatrix(text, FONT_5x7, 5, 7, 1);
+ displayMatrix(imageData);
+
+ // also update scrolls_output for server
+ const speed = Number(document.getElementById('scrollSpeed').value);
+ const directionRight = document.getElementById('scrollRight').checked;
+ const wrap = document.getElementById('scrollWrap').checked;
+ const scrollObj = [{
+ x: 0, y: 0,
+ width: DISPLAY_WIDTH,
+ height: 8,
+ contentWidth,
+ speed, directionRight, wrap,
+ pixelsOn: []
+ }];
+ document.getElementById('scrolls_output').value = JSON.stringify(scrollObj);
+ }
+
+ document.getElementById("scrollText").addEventListener("input", () => {
+ // Clear previous timer
+ if (textUpdateTimer) {
+ clearTimeout(textUpdateTimer);
+ }
+
+ // Set new timer for 1 second
+ textUpdateTimer = setTimeout(() => {
+ updateTextPreview();
+ }, 1000);
+ });
+
+ upload.addEventListener('change', e => {
+ const files = e.target.files;
+ if (!files || files.length === 0) return;
+
+ // Auto-switch to animation creation mode if multiple files selected
+ if (files.length > 1 && !isAnimationCreationMode) {
+ setAnimationCreationMode(true);
+ }
+
+ if (isAnimationCreationMode) {
+ // Animation creation mode - treat as animation (even single file)
+ loadMultipleImages(files);
+ } else {
+ // Single file - load normally
+ loadImage(files[0]);
+ }
+ });
+ dropzone.addEventListener('click', () => upload.click());
+
+ // Drag-and-drop handlers
+ dropzone.addEventListener('dragover', (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ dropzone.style.borderColor = '#888';
+ });
+
+ dropzone.addEventListener('dragleave', (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ dropzone.style.borderColor = '#555';
+ });
+
+ dropzone.addEventListener('drop', (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ dropzone.style.borderColor = '#555';
+
+ const files = e.dataTransfer.files;
+ if (!files || files.length === 0) return;
+
+ // Filter to only image files
+ const imageFiles = Array.from(files).filter(f => f.type.startsWith('image/'));
+ if (imageFiles.length === 0) return;
+
+ // Auto-switch to animation creation mode if multiple files dropped
+ if (imageFiles.length > 1 && !isAnimationCreationMode) {
+ setAnimationCreationMode(true);
+ }
+
+ if (isAnimationCreationMode) {
+ // Animation creation mode - treat as animation (even single file)
+ loadMultipleImages(imageFiles);
+ } else {
+ // Single file - load normally
+ loadImage(imageFiles[0]);
+ }
+ });
+
+ // Matrix drawing event handlers
+ matrix.addEventListener('mousedown', handleMatrixMouseDown);
+ matrix.addEventListener('mousemove', handleMatrixMouseMove);
+ matrix.addEventListener('mouseup', handleMatrixMouseUp);
+ matrix.addEventListener('mouseleave', handleMatrixMouseLeave);
+
+ invertBtn.addEventListener('click', () => {
+ invertImageColors = !invertImageColors;
+ setButtonActive(invertBtn, invertImageColors);
+
+ if (isAnimatedGif) {
+ // For GIFs: reprocess all frames with new invert setting
+ if (hasManualEdits && !confirm('Toggling invert will reprocess all frames. Manual edits will be preserved. Continue?')) {
+ invertImageColors = !invertImageColors; // Revert
+ setButtonActive(invertBtn, invertImageColors);
+ return;
+ }
+ reprocessAllGifFrames();
+ } else {
+ // For static images: just invert current matrix (preserves edits)
+ invertCurrentMatrix();
+ }
+ });
+
+ ditherBtn.addEventListener('click', () => {
+ if (hasManualEdits && !confirm('Toggling dither will reload the image and your manual edits may be lost. Continue?')) {
+ return;
+ }
+ useDithering = !useDithering;
+ ditherBtn.textContent = useDithering ? 'Dither: ON' : 'Dither: OFF';
+ setButtonActive(ditherBtn, useDithering);
+
+ if (isAnimatedGif) {
+ // For multi-image animations or GIFs
+ if (animationSourceFiles && animationSourceFiles.length > 0) {
+ // Multi-image animation - reprocess all frames
+ gifFrames.forEach(frame => {
+ frame.processed = false;
+ frame.imageData = null;
+ });
+ processStaticImageFrame(currentFrameIndex);
+ } else {
+ // GIF animation - reprocess all frames
+ reprocessAllGifFrames();
+ }
+ } else if (currentFile) {
+ loadImage(currentFile);
+ hasManualEdits = false;
+ }
+ });
+
+ drawBtn.addEventListener('click', () => {
+ toggleDrawEraseButtons(true);
+ });
+
+ eraseBtn.addEventListener('click', () => {
+ toggleDrawEraseButtons(false);
+ });
+
+ brushSizeSelect.addEventListener('change', (e) => {
+ brushSize = parseInt(e.target.value, 10);
+ });
+
+ // Lock duration when manually changed
+ animationDurationInput.addEventListener('input', () => {
+ isDurationManuallyLocked = true;
+ revertDurationBtn.style.display = 'inline-block';
+ revertDurationBtn.style.fontWeight = 'bold';
+ revertDurationBtn.style.color = '#f60';
+ });
+
+ // Revert button to unlock and recalculate duration
+ revertDurationBtn.addEventListener('click', () => {
+ isDurationManuallyLocked = false;
+ revertDurationBtn.style.display = 'none';
+ revertDurationBtn.style.fontWeight = 'normal';
+ revertDurationBtn.style.color = '';
+ updateAnimationDuration();
+ });
+
+ resetBtn.addEventListener('click', () => {
+ stopAnimation();
+ drawMode = false;
+ eraseMode = false;
+ setButtonActive(drawBtn, false);
+ setButtonActive(eraseBtn, false);
+ currentFile = null; // Clear file reference on reset
+ hasManualEdits = false;
+
+ // Reset GIF state
+ isAnimatedGif = false;
+ gifReader = null;
+ gifFrames = [];
+ gifFrameEdits = [];
+ animationSourceFiles = [];
+ currentFrameIndex = 0;
+
+ // Reset duration lock and UI
+ isDurationManuallyLocked = false;
+ revertDurationBtn.style.display = 'none';
+ revertDurationBtn.style.fontWeight = 'normal';
+ revertDurationBtn.style.color = '';
+
+ updateFrameControls();
+ updateFrameCounter();
+ updateAnimationDuration();
+
+ emptyCanvas();
+ });
+
+ exportBtn.addEventListener('click', async () => {
+ if (currentMode === "image") {
+ const jsonExport = await generateJSONExport();
+ document.getElementById('array_output').value = jsonExport;
+ document.getElementById('scrolls_output').value = '';
+ } else {
+ const text = document.getElementById('scrollText').value.trim();
+ if (!text) return alert("Enter some text first!");
+ const speed = Number(document.getElementById('scrollSpeed').value);
+ const directionRight = document.getElementById('scrollRight').checked;
+ const wrap = document.getElementById('scrollWrap').checked;
+
+ const { contentWidth } = renderCustomTextToMatrix(text, FONT_5x7, 5, 7, 1);
+ const onDots = Array.from(document.querySelectorAll('.dot.on')).map(el => [Number(el.dataset.x), Number(el.dataset.y)]);
+ document.getElementById('array_output').value = JSON.stringify(onDots);
+
+ const scrollObj = [{
+ x: 0, y: 0,
+ width: DISPLAY_WIDTH,
+ height: 8,
+ contentWidth,
+ speed, directionRight, wrap,
+ pixelsOn: []
+ }];
+
+ document.getElementById('scrolls_output').value = JSON.stringify(scrollObj);
+ }
+ });
+
+ // Helper function to try frame reduction for binary export
+ // Calculate updateInterval from duration and frame count
+ // duration: total animation duration in seconds
+ // frameCount: number of frames
+ // returns: updateInterval in 10ms units (for binary format)
+ function calculateUpdateInterval(duration, frameCount) {
+ if (frameCount === 0) return 3; // default fallback
+ const frameDelay = duration / frameCount; // seconds per frame
+ const updateInterval = Math.round(frameDelay * 100); // convert to 10ms units
+ return Math.max(1, Math.min(255, updateInterval)); // clamp to 1-255 (1 byte range)
+ }
+
+ // Reduce frames to target count by keeping frames uniformly
+ // Maintains the same total animation duration
+ function reduceFramesToTarget(frames, targetFrameCount, duration) {
+ if (frames.length <= targetFrameCount) {
+ const updateInterval = calculateUpdateInterval(duration, frames.length);
+ return { frames, updateInterval, skipPattern: 1 };
+ }
+
+ // Calculate how many frames to skip between kept frames
+ const skipPattern = Math.floor(frames.length / targetFrameCount);
+ const reducedFrames = [];
+
+ // Keep every Nth frame
+ for (let i = 0; i < frames.length; i += skipPattern) {
+ reducedFrames.push(frames[i]);
+ if (reducedFrames.length >= targetFrameCount) break;
+ }
+
+ // Calculate new updateInterval to maintain same total duration
+ const updateInterval = calculateUpdateInterval(duration, reducedFrames.length);
+
+ return {
+ frames: reducedFrames,
+ updateInterval,
+ skipPattern
+ };
+ }
+
+ // Download Binary button - converts JSON export to binary format
+ document.getElementById('downloadBinary').addEventListener('click', async () => {
+ if (currentMode === "image") {
+ try {
+ const MAX_BINARY_SIZE = 16 * 1024; // 16KB flash limit
+
+ // Get JSON export using shared function
+ let jsonExport = await generateJSONExport();
+
+ // For single images, wrap in array for parseJSONFrames compatibility
+ if (!isAnimatedGif) {
+ const singleFrame = JSON.parse(jsonExport);
+ jsonExport = JSON.stringify([singleFrame]);
+ }
+
+ // Always use 120x60 for image mode (DISPLAY_WIDTH can change in text mode)
+ const imageWidth = 120;
+ const imageHeight = 60;
+
+ // Parse frames first to get actual count
+ let frames = JSON.parse(jsonExport);
+ const originalFrameCount = frames.length;
+
+ // Get animation duration from input field
+ const animationDuration = parseFloat(document.getElementById('animationDuration').value) || 3.0;
+
+ // Calculate updateInterval based on duration and frame count
+ const initialUpdateInterval = calculateUpdateInterval(animationDuration, originalFrameCount);
+
+ const binaryOptions = {
+ startX: 0,
+ startY: 0,
+ updateInterval: initialUpdateInterval,
+ width: imageWidth,
+ height: imageHeight,
+ maxFrames: originalFrameCount // Use actual frame count, not arbitrary limit
+ };
+
+ // Generate initial binary
+ let binaryData = jsonToBinary(jsonExport, binaryOptions);
+ const originalSize = binaryData.length;
+ const TARGET_FRAME_COUNT = 17;
+
+ // Check if we need to reduce frames due to size limit
+ if (binaryData.length > MAX_BINARY_SIZE && frames.length > 1) {
+ // Try reducing to target frame count (maintains duration)
+ const reduction = reduceFramesToTarget(frames, TARGET_FRAME_COUNT, animationDuration);
+
+ // Update options with new updateInterval
+ const reducedOptions = {
+ ...binaryOptions,
+ maxFrames: reduction.frames.length,
+ updateInterval: reduction.updateInterval
+ };
+
+ // Generate binary with reduced frames
+ const reducedBinary = jsonToBinary(JSON.stringify(reduction.frames), reducedOptions);
+
+ if (reducedBinary.length <= MAX_BINARY_SIZE) {
+ // Success - frames fit within size limit
+ const droppedCount = originalFrameCount - reduction.frames.length;
+ const originalFrameDelay = (animationDuration / originalFrameCount * 1000).toFixed(1);
+ const newFrameDelay = (animationDuration / reduction.frames.length * 1000).toFixed(1);
+ alert(
+ `Binary size reduction applied:\n\n` +
+ `Original: ${originalSize} bytes (${originalFrameCount} frames)\n` +
+ `Reduced: ${reducedBinary.length} bytes (${reduction.frames.length} frames)\n` +
+ `Maximum: ${MAX_BINARY_SIZE} bytes\n\n` +
+ `Kept every ${reduction.skipPattern}${reduction.skipPattern === 1 ? 'st' : reduction.skipPattern === 2 ? 'nd' : reduction.skipPattern === 3 ? 'rd' : 'th'} frame (${droppedCount} frames removed)\n` +
+ `Duration maintained: ${animationDuration}s\n` +
+ `Frame delay adjusted: ${originalFrameDelay}ms → ${newFrameDelay}ms\n` +
+ `Update interval: ${binaryOptions.updateInterval} → ${reduction.updateInterval}`
+ );
+ frames = reduction.frames;
+ binaryData = reducedBinary;
+ binaryOptions.updateInterval = reduction.updateInterval;
+ binaryOptions.maxFrames = reduction.frames.length;
+ } else {
+ // Even with reduction to target, still too large
+ alert(
+ `Binary file is too large to flash!\n\n` +
+ `Size: ${reducedBinary.length} bytes\n` +
+ `Maximum: ${MAX_BINARY_SIZE} bytes\n\n` +
+ `Even after reducing to ${TARGET_FRAME_COUNT} frames, the file exceeds the limit.\n` +
+ `Try reducing image complexity.`
+ );
+ return;
+ }
+ } else if (binaryData.length > MAX_BINARY_SIZE && frames.length === 1) {
+ // Single frame too large
+ alert(
+ `Binary file is too large to flash!\n\n` +
+ `Size: ${binaryData.length} bytes\n` +
+ `Maximum: ${MAX_BINARY_SIZE} bytes\n\n` +
+ `This is a single frame. Try reducing image complexity.`
+ );
+ return;
+ }
+
+ // Create blob and trigger download
+ const blob = new Blob([binaryData], { type: 'application/octet-stream' });
+ const url = URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = 'animation.bin';
+ document.body.appendChild(a);
+ a.click();
+ document.body.removeChild(a);
+ URL.revokeObjectURL(url);
+
+ console.log(`Binary file created: ${binaryData.length} bytes (${frames.length} frames)`);
+ } catch (err) {
+ alert('Error creating binary file: ' + err.message);
+ console.error(err);
+ }
+ } else {
+ alert('Binary export is only available in Image Mode.');
+ }
+ });
+
+ // Frame navigation event listeners
+ document.getElementById('prevFrame').addEventListener('click', () => {
+ stopAnimation();
+ navigateToFrame('prev');
+ });
+
+ document.getElementById('playStop').addEventListener('click', () => {
+ if (isPlaying) {
+ stopAnimation();
+ } else {
+ startAnimation();
+ }
+ });
+
+ document.getElementById('nextFrame').addEventListener('click', () => {
+ stopAnimation();
+ navigateToFrame('next');
+ });
+
+ document.getElementById('deleteFrame').addEventListener('click', () => {
+ deleteCurrentFrame();
+ });
+
+ document.getElementById('addEmptyFrame').addEventListener('click', () => {
+ addEmptyFrame();
+ });
+
+ document.getElementById('duplicateFrame').addEventListener('click', () => {
+ duplicateCurrentFrame();
+ });
+
+ // Keyboard shortcuts for frame navigation
+ document.addEventListener('keydown', (e) => {
+ if (!isAnimatedGif) return;
+
+ if (e.key === 'ArrowLeft' && !e.target.matches('input, textarea')) {
+ e.preventDefault();
+ stopAnimation();
+ navigateToFrame('prev');
+ } else if (e.key === 'ArrowRight' && !e.target.matches('input, textarea')) {
+ e.preventDefault();
+ stopAnimation();
+ navigateToFrame('next');
+ }
+ });
+
+ window.addEventListener('load', emptyCanvas);
diff --git a/backup/avj2305/Artikel1.bin b/backup/avj2305/Artikel1.bin
new file mode 100644
index 0000000..1104d29
Binary files /dev/null and b/backup/avj2305/Artikel1.bin differ
diff --git a/backup/avj2305/Artikel1.png b/backup/avj2305/Artikel1.png
new file mode 100644
index 0000000..9ebe401
Binary files /dev/null and b/backup/avj2305/Artikel1.png differ
diff --git a/backup/avj2305/Artikel21.bin b/backup/avj2305/Artikel21.bin
new file mode 100644
index 0000000..a52cd83
Binary files /dev/null and b/backup/avj2305/Artikel21.bin differ
diff --git a/backup/avj2305/Artikel21.png b/backup/avj2305/Artikel21.png
new file mode 100644
index 0000000..2e848e1
Binary files /dev/null and b/backup/avj2305/Artikel21.png differ
diff --git a/backup/avj2305/Frosch161.bin b/backup/avj2305/Frosch161.bin
new file mode 100644
index 0000000..9b4deed
Binary files /dev/null and b/backup/avj2305/Frosch161.bin differ
diff --git a/backup/avj2305/Frosch161.png b/backup/avj2305/Frosch161.png
new file mode 100644
index 0000000..9e27cf2
Binary files /dev/null and b/backup/avj2305/Frosch161.png differ
diff --git a/backup/avj2305/LaufSpruch1.bin b/backup/avj2305/LaufSpruch1.bin
new file mode 100644
index 0000000..6bb9af7
Binary files /dev/null and b/backup/avj2305/LaufSpruch1.bin differ
diff --git a/backup/avj2305/LaufSpruch1.png b/backup/avj2305/LaufSpruch1.png
new file mode 100644
index 0000000..6554ad4
Binary files /dev/null and b/backup/avj2305/LaufSpruch1.png differ
diff --git a/backup/avj2305/LaufSpruch2.bin b/backup/avj2305/LaufSpruch2.bin
new file mode 100644
index 0000000..48ee107
Binary files /dev/null and b/backup/avj2305/LaufSpruch2.bin differ
diff --git a/backup/avj2305/LaufSpruch2.png b/backup/avj2305/LaufSpruch2.png
new file mode 100644
index 0000000..36b9cc0
Binary files /dev/null and b/backup/avj2305/LaufSpruch2.png differ
diff --git a/backup/avj2305/LaufSpruch3.bin b/backup/avj2305/LaufSpruch3.bin
new file mode 100644
index 0000000..f57ad0c
Binary files /dev/null and b/backup/avj2305/LaufSpruch3.bin differ
diff --git a/backup/avj2305/LaufSpruch3.png b/backup/avj2305/LaufSpruch3.png
new file mode 100644
index 0000000..034b7eb
Binary files /dev/null and b/backup/avj2305/LaufSpruch3.png differ
diff --git a/backup/avj2305/MvAVJ.bin b/backup/avj2305/MvAVJ.bin
new file mode 100644
index 0000000..d08c64d
Binary files /dev/null and b/backup/avj2305/MvAVJ.bin differ
diff --git a/backup/avj2305/MvAVJ.png b/backup/avj2305/MvAVJ.png
new file mode 100644
index 0000000..2ace381
Binary files /dev/null and b/backup/avj2305/MvAVJ.png differ
diff --git a/backup/avj2305/NotWidersetzen.bin b/backup/avj2305/NotWidersetzen.bin
new file mode 100644
index 0000000..fd4389c
Binary files /dev/null and b/backup/avj2305/NotWidersetzen.bin differ
diff --git a/backup/avj2305/NotWidersetzen.png b/backup/avj2305/NotWidersetzen.png
new file mode 100644
index 0000000..4977a05
Binary files /dev/null and b/backup/avj2305/NotWidersetzen.png differ
diff --git a/backup/avj2305/Schildkroete.bin b/backup/avj2305/Schildkroete.bin
new file mode 100644
index 0000000..9cfb723
Binary files /dev/null and b/backup/avj2305/Schildkroete.bin differ
diff --git a/backup/avj2305/Schildkroete.png b/backup/avj2305/Schildkroete.png
new file mode 100644
index 0000000..eabec85
Binary files /dev/null and b/backup/avj2305/Schildkroete.png differ
diff --git a/backup/avj2305/Widersetzen.bin b/backup/avj2305/Widersetzen.bin
new file mode 100644
index 0000000..1230f33
Binary files /dev/null and b/backup/avj2305/Widersetzen.bin differ
diff --git a/backup/avj2305/Widersetzen.png b/backup/avj2305/Widersetzen.png
new file mode 100644
index 0000000..6609f00
Binary files /dev/null and b/backup/avj2305/Widersetzen.png differ
diff --git a/backup/avj2305/Widersetzen1.bin b/backup/avj2305/Widersetzen1.bin
new file mode 100644
index 0000000..af4d1dd
Binary files /dev/null and b/backup/avj2305/Widersetzen1.bin differ
diff --git a/backup/avj2305/Widersetzen1.png b/backup/avj2305/Widersetzen1.png
new file mode 100644
index 0000000..0dd9983
Binary files /dev/null and b/backup/avj2305/Widersetzen1.png differ
diff --git a/backup/avj2305/Zitat1.bin b/backup/avj2305/Zitat1.bin
new file mode 100644
index 0000000..2260c55
Binary files /dev/null and b/backup/avj2305/Zitat1.bin differ
diff --git a/backup/avj2305/Zitat1.png b/backup/avj2305/Zitat1.png
new file mode 100644
index 0000000..27d92ff
Binary files /dev/null and b/backup/avj2305/Zitat1.png differ
diff --git a/backup/avj2305/Zitat2.bin b/backup/avj2305/Zitat2.bin
new file mode 100644
index 0000000..f43ecbf
Binary files /dev/null and b/backup/avj2305/Zitat2.bin differ
diff --git a/backup/avj2305/Zitat2.png b/backup/avj2305/Zitat2.png
new file mode 100644
index 0000000..cdf0baa
Binary files /dev/null and b/backup/avj2305/Zitat2.png differ
diff --git a/backup/avj2305/Zitat3.bin b/backup/avj2305/Zitat3.bin
new file mode 100644
index 0000000..18808b3
Binary files /dev/null and b/backup/avj2305/Zitat3.bin differ
diff --git a/backup/avj2305/Zitat3.png b/backup/avj2305/Zitat3.png
new file mode 100644
index 0000000..a35eaa8
Binary files /dev/null and b/backup/avj2305/Zitat3.png differ
diff --git a/backup/binfmt.js b/backup/binfmt.js
new file mode 100644
index 0000000..bc07326
--- /dev/null
+++ b/backup/binfmt.js
@@ -0,0 +1,165 @@
+// Binary Framebuffer Encoder
+// Converts pixel data to binary format for bus display
+
+const MAGIC = [66, 78, 23, 238];
+const DrawEntryType = {
+ Image: 1,
+ Animation: 2,
+ HScroll: 3,
+ VScroll: 4,
+ Line: 5
+};
+
+function bitmapSizeBytes(width, height) {
+ return Math.floor((width * height + 7) / 8);
+}
+
+function setPixelInBitmap(x, y, memory, width, height, value) {
+ const index = y * width + x;
+ const byteIndex = Math.floor(index / 8);
+ const bitIndex = index % 8;
+ if (value) {
+ memory[byteIndex] |= 1 << bitIndex;
+ } else {
+ memory[byteIndex] &= ~(1 << bitIndex);
+ }
+}
+
+class BinaryFramebufferEncoder {
+ entries = [];
+
+ addImage(posX, posY, width, height, pixelData) {
+ const data = new Uint8Array(bitmapSizeBytes(width, height));
+ for (let y = 0; y < height; y++) {
+ for (let x = 0; x < width; x++) {
+ setPixelInBitmap(x, y, data, width, height, pixelData[y][x]);
+ }
+ }
+ this.entries.push({
+ type: DrawEntryType.Image,
+ header: { posX, posY },
+ width,
+ height,
+ data
+ });
+ }
+
+ addAnimation(posX, posY, width, height, frameCount, updateInterval, frames) {
+ const frameSize = bitmapSizeBytes(width, height);
+ const data = new Uint8Array(frameSize * frameCount);
+ for (let f = 0; f < frameCount; f++) {
+ const frameData = data.subarray(f * frameSize, (f + 1) * frameSize);
+ for (let y = 0; y < height; y++) {
+ for (let x = 0; x < width; x++) {
+ setPixelInBitmap(x, y, frameData, width, height, frames[f][y][x]);
+ }
+ }
+ }
+ this.entries.push({
+ type: DrawEntryType.Animation,
+ header: { posX, posY },
+ width,
+ height,
+ frameCount,
+ updateInterval,
+ data
+ });
+ }
+
+ encode() {
+ let totalSize = 5;
+ for (const entry of this.entries) {
+ totalSize += 3;
+ switch (entry.type) {
+ case DrawEntryType.Image:
+ totalSize += 2 + entry.data.length;
+ break;
+ case DrawEntryType.Animation:
+ totalSize += 4 + entry.data.length;
+ break;
+ }
+ }
+ const buffer = new Uint8Array(totalSize);
+ let pos = 0;
+ buffer.set(MAGIC, pos);
+ pos += 4;
+ buffer[pos++] = this.entries.length;
+ for (const entry of this.entries) {
+ buffer[pos++] = entry.type;
+ buffer[pos++] = entry.header.posX;
+ buffer[pos++] = entry.header.posY;
+ switch (entry.type) {
+ case DrawEntryType.Image:
+ buffer[pos++] = entry.width;
+ buffer[pos++] = entry.height;
+ buffer.set(entry.data, pos);
+ pos += entry.data.length;
+ break;
+ case DrawEntryType.Animation:
+ buffer[pos++] = entry.width;
+ buffer[pos++] = entry.height;
+ buffer[pos++] = entry.frameCount;
+ buffer[pos++] = entry.updateInterval;
+ buffer.set(entry.data, pos);
+ pos += entry.data.length;
+ break;
+ }
+ }
+ return buffer;
+ }
+}
+
+function parseJSONFrames(text, width = 120, height = 60, maxFrames = 50) {
+ const json = JSON.parse(text);
+ if (!Array.isArray(json)) {
+ throw new Error("JSON must be an array of frames");
+ }
+ const frames = [];
+ for (const frame of json) {
+ if (!Array.isArray(frame)) {
+ throw new Error("Each frame must be an array of coordinates");
+ }
+ const pixels = Array(height).fill(null).map(() => Array(width).fill(false));
+ for (const change of frame) {
+ const [x, y] = change;
+ if (y < pixels.length && x < pixels[y].length) {
+ pixels[y][x] = true;
+ }
+ }
+ if (frames.length >= maxFrames) continue;
+ frames.push(pixels);
+ }
+ return frames;
+}
+
+function jsonToBinary(jsonString, options = {}) {
+ const {
+ startX = 0,
+ startY = 0,
+ updateInterval = 3,
+ width = 120,
+ height = 60,
+ maxFrames = 50
+ } = options;
+ const encoder = new BinaryFramebufferEncoder();
+ const frames = parseJSONFrames(jsonString, width, height, maxFrames);
+ if (frames.length === 1) {
+ encoder.addImage(startX, startY, width, height, frames[0]);
+ } else {
+ encoder.addAnimation(startX, startY, width, height, frames.length, updateInterval, frames);
+ }
+ return encoder.encode();
+}
+
+// Helper function to drop every nth frame from a frame array
+function dropEveryNthFrame(frames, n) {
+ if (n <= 1 || frames.length <= 1) return frames;
+ return frames.filter((frame, index) => (index + 1) % n !== 0);
+}
+
+// Helper function to keep 1 frame, drop 2 frames in repeating pattern
+// Pattern: keep, drop, drop, keep, drop, drop, ...
+function keepOneDropTwo(frames) {
+ if (frames.length <= 1) return frames;
+ return frames.filter((frame, index) => index % 3 === 0);
+}
diff --git a/backup/editor/index.html b/backup/editor/index.html
new file mode 100644
index 0000000..c91f6e3
--- /dev/null
+++ b/backup/editor/index.html
@@ -0,0 +1,112 @@
+
+
+
+
+
+
+ MonoDisplay Editor
+
+
+
+
+
+
+
+
MonoDisplay
+
+
+
+
+
+
+
+
untitled
+
+
+
+
+
+
+
+
preview · 120 × 60
+
+
+
+
demos
+
+
+
+
+
+
+ sections
+
+
+
No sections yet.
Use Add section to get started.
+
+
+
+
+
+
+
+
+
+
+
diff --git a/backup/editor/public/mono-display.js b/backup/editor/public/mono-display.js
new file mode 100644
index 0000000..ea17394
--- /dev/null
+++ b/backup/editor/public/mono-display.js
@@ -0,0 +1,5302 @@
+var __create = Object.create;
+var __getProtoOf = Object.getPrototypeOf;
+var __defProp = Object.defineProperty;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+var __hasOwnProp = Object.prototype.hasOwnProperty;
+function __accessProp(key) {
+ return this[key];
+}
+var __toESMCache_node;
+var __toESMCache_esm;
+var __toESM = (mod, isNodeMode, target) => {
+ var canCache = mod != null && typeof mod === "object";
+ if (canCache) {
+ var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
+ var cached = cache.get(mod);
+ if (cached)
+ return cached;
+ }
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
+ for (let key of __getOwnPropNames(mod))
+ if (!__hasOwnProp.call(to, key))
+ __defProp(to, key, {
+ get: __accessProp.bind(mod, key),
+ enumerable: true
+ });
+ if (canCache)
+ cache.set(mod, to);
+ return to;
+};
+var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
+var __returnValue = (v) => v;
+function __exportSetter(name, newValue) {
+ this[name] = __returnValue.bind(null, newValue);
+}
+var __export = (target, all) => {
+ for (var name in all)
+ __defProp(target, name, {
+ get: all[name],
+ enumerable: true,
+ configurable: true,
+ set: __exportSetter.bind(all, name)
+ });
+};
+
+// node_modules/mithril/render/vnode.js
+var require_vnode = __commonJS((exports, module) => {
+ function Vnode(tag, key, attrs, children, text, dom) {
+ return { tag, key, attrs, children, text, dom, is: undefined, domSize: undefined, state: undefined, events: undefined, instance: undefined };
+ }
+ Vnode.normalize = function(node) {
+ if (Array.isArray(node))
+ return Vnode("[", undefined, undefined, Vnode.normalizeChildren(node), undefined, undefined);
+ if (node == null || typeof node === "boolean")
+ return null;
+ if (typeof node === "object")
+ return node;
+ return Vnode("#", undefined, undefined, String(node), undefined, undefined);
+ };
+ Vnode.normalizeChildren = function(input) {
+ var children = new Array(input.length);
+ var numKeyed = 0;
+ for (var i = 0;i < input.length; i++) {
+ children[i] = Vnode.normalize(input[i]);
+ if (children[i] !== null && children[i].key != null)
+ numKeyed++;
+ }
+ if (numKeyed !== 0 && numKeyed !== input.length) {
+ throw new TypeError(children.includes(null) ? "In fragments, vnodes must either all have keys or none have keys. You may wish to consider using an explicit keyed empty fragment, m.fragment({key: ...}), instead of a hole." : "In fragments, vnodes must either all have keys or none have keys.");
+ }
+ return children;
+ };
+ module.exports = Vnode;
+});
+
+// node_modules/mithril/render/hyperscriptVnode.js
+var require_hyperscriptVnode = __commonJS((exports, module) => {
+ var Vnode = require_vnode();
+ module.exports = function(attrs, children) {
+ if (attrs == null || typeof attrs === "object" && attrs.tag == null && !Array.isArray(attrs)) {
+ if (children.length === 1 && Array.isArray(children[0]))
+ children = children[0];
+ } else {
+ children = children.length === 0 && Array.isArray(attrs) ? attrs : [attrs, ...children];
+ attrs = undefined;
+ }
+ return Vnode("", attrs && attrs.key, attrs, children);
+ };
+});
+
+// node_modules/mithril/util/hasOwn.js
+var require_hasOwn = __commonJS((exports, module) => {
+ module.exports = {}.hasOwnProperty;
+});
+
+// node_modules/mithril/render/emptyAttrs.js
+var require_emptyAttrs = __commonJS((exports, module) => {
+ module.exports = {};
+});
+
+// node_modules/mithril/render/cachedAttrsIsStaticMap.js
+var require_cachedAttrsIsStaticMap = __commonJS((exports, module) => {
+ var emptyAttrs = require_emptyAttrs();
+ module.exports = new Map([[emptyAttrs, true]]);
+});
+
+// node_modules/mithril/render/hyperscript.js
+var require_hyperscript = __commonJS((exports, module) => {
+ var Vnode = require_vnode();
+ var hyperscriptVnode = require_hyperscriptVnode();
+ var hasOwn = require_hasOwn();
+ var emptyAttrs = require_emptyAttrs();
+ var cachedAttrsIsStaticMap = require_cachedAttrsIsStaticMap();
+ var selectorParser = /(?:(^|#|\.)([^#\.\[\]]+))|(\[(.+?)(?:\s*=\s*("|'|)((?:\\["'\]]|.)*?)\5)?\])/g;
+ var selectorCache = Object.create(null);
+ function isEmpty(object) {
+ for (var key in object)
+ if (hasOwn.call(object, key))
+ return false;
+ return true;
+ }
+ function isFormAttributeKey(key) {
+ return key === "value" || key === "checked" || key === "selectedIndex" || key === "selected";
+ }
+ function compileSelector(selector) {
+ var match, tag = "div", classes = [], attrs = {}, isStatic = true;
+ while (match = selectorParser.exec(selector)) {
+ var type = match[1], value = match[2];
+ if (type === "" && value !== "")
+ tag = value;
+ else if (type === "#")
+ attrs.id = value;
+ else if (type === ".")
+ classes.push(value);
+ else if (match[3][0] === "[") {
+ var attrValue = match[6];
+ if (attrValue)
+ attrValue = attrValue.replace(/\\(["'])/g, "$1").replace(/\\\\/g, "\\");
+ if (match[4] === "class")
+ classes.push(attrValue);
+ else {
+ attrs[match[4]] = attrValue === "" ? attrValue : attrValue || true;
+ if (isFormAttributeKey(match[4]))
+ isStatic = false;
+ }
+ }
+ }
+ if (classes.length > 0)
+ attrs.className = classes.join(" ");
+ if (isEmpty(attrs))
+ attrs = emptyAttrs;
+ else
+ cachedAttrsIsStaticMap.set(attrs, isStatic);
+ return selectorCache[selector] = { tag, attrs, is: attrs.is };
+ }
+ function execSelector(state, vnode) {
+ vnode.tag = state.tag;
+ var attrs = vnode.attrs;
+ if (attrs == null) {
+ vnode.attrs = state.attrs;
+ vnode.is = state.is;
+ return vnode;
+ }
+ if (hasOwn.call(attrs, "class")) {
+ if (attrs.class != null)
+ attrs.className = attrs.class;
+ attrs.class = null;
+ }
+ if (state.attrs !== emptyAttrs) {
+ var className = attrs.className;
+ attrs = Object.assign({}, state.attrs, attrs);
+ if (state.attrs.className != null)
+ attrs.className = className != null ? String(state.attrs.className) + " " + String(className) : state.attrs.className;
+ }
+ if (state.tag === "input" && hasOwn.call(attrs, "type")) {
+ attrs = Object.assign({ type: attrs.type }, attrs);
+ }
+ vnode.is = attrs.is;
+ vnode.attrs = attrs;
+ return vnode;
+ }
+ function hyperscript(selector, attrs, ...children) {
+ if (selector == null || typeof selector !== "string" && typeof selector !== "function" && typeof selector.view !== "function") {
+ throw Error("The selector must be either a string or a component.");
+ }
+ var vnode = hyperscriptVnode(attrs, children);
+ if (typeof selector === "string") {
+ vnode.children = Vnode.normalizeChildren(vnode.children);
+ if (selector !== "[")
+ return execSelector(selectorCache[selector] || compileSelector(selector), vnode);
+ }
+ if (vnode.attrs == null)
+ vnode.attrs = {};
+ vnode.tag = selector;
+ return vnode;
+ }
+ module.exports = hyperscript;
+});
+
+// node_modules/mithril/render/trust.js
+var require_trust = __commonJS((exports, module) => {
+ var Vnode = require_vnode();
+ module.exports = function(html) {
+ if (html == null)
+ html = "";
+ return Vnode("<", undefined, undefined, html, undefined, undefined);
+ };
+});
+
+// node_modules/mithril/render/fragment.js
+var require_fragment = __commonJS((exports, module) => {
+ var Vnode = require_vnode();
+ var hyperscriptVnode = require_hyperscriptVnode();
+ module.exports = function(attrs, ...children) {
+ var vnode = hyperscriptVnode(attrs, children);
+ if (vnode.attrs == null)
+ vnode.attrs = {};
+ vnode.tag = "[";
+ vnode.children = Vnode.normalizeChildren(vnode.children);
+ return vnode;
+ };
+});
+
+// node_modules/mithril/hyperscript.js
+var require_hyperscript2 = __commonJS((exports, module) => {
+ var hyperscript = require_hyperscript();
+ hyperscript.trust = require_trust();
+ hyperscript.fragment = require_fragment();
+ module.exports = hyperscript;
+});
+
+// node_modules/mithril/render/delayedRemoval.js
+var require_delayedRemoval = __commonJS((exports, module) => {
+ module.exports = new WeakMap;
+});
+
+// node_modules/mithril/render/domFor.js
+var require_domFor = __commonJS((exports, module) => {
+ var delayedRemoval = require_delayedRemoval();
+ function* domFor(vnode) {
+ var dom = vnode.dom;
+ var domSize = vnode.domSize;
+ var generation = delayedRemoval.get(dom);
+ if (dom != null)
+ do {
+ var nextSibling = dom.nextSibling;
+ if (delayedRemoval.get(dom) === generation) {
+ yield dom;
+ domSize--;
+ }
+ dom = nextSibling;
+ } while (domSize);
+ }
+ module.exports = domFor;
+});
+
+// node_modules/mithril/render/render.js
+var require_render = __commonJS((exports, module) => {
+ var Vnode = require_vnode();
+ var delayedRemoval = require_delayedRemoval();
+ var domFor = require_domFor();
+ var cachedAttrsIsStaticMap = require_cachedAttrsIsStaticMap();
+ module.exports = function() {
+ var nameSpace = {
+ svg: "http://www.w3.org/2000/svg",
+ math: "http://www.w3.org/1998/Math/MathML"
+ };
+ var currentRedraw;
+ var currentRender;
+ function getDocument(dom) {
+ return dom.ownerDocument;
+ }
+ function getNameSpace(vnode) {
+ return vnode.attrs && vnode.attrs.xmlns || nameSpace[vnode.tag];
+ }
+ function checkState(vnode, original) {
+ if (vnode.state !== original)
+ throw new Error("'vnode.state' must not be modified.");
+ }
+ function callHook(vnode) {
+ var original = vnode.state;
+ try {
+ return this.apply(original, arguments);
+ } finally {
+ checkState(vnode, original);
+ }
+ }
+ function activeElement(dom) {
+ try {
+ return getDocument(dom).activeElement;
+ } catch (e) {
+ return null;
+ }
+ }
+ function createNodes(parent, vnodes, start, end, hooks, nextSibling, ns) {
+ for (var i = start;i < end; i++) {
+ var vnode = vnodes[i];
+ if (vnode != null) {
+ createNode(parent, vnode, hooks, ns, nextSibling);
+ }
+ }
+ }
+ function createNode(parent, vnode, hooks, ns, nextSibling) {
+ var tag = vnode.tag;
+ if (typeof tag === "string") {
+ vnode.state = {};
+ if (vnode.attrs != null)
+ initLifecycle(vnode.attrs, vnode, hooks);
+ switch (tag) {
+ case "#":
+ createText(parent, vnode, nextSibling);
+ break;
+ case "<":
+ createHTML(parent, vnode, ns, nextSibling);
+ break;
+ case "[":
+ createFragment(parent, vnode, hooks, ns, nextSibling);
+ break;
+ default:
+ createElement(parent, vnode, hooks, ns, nextSibling);
+ }
+ } else
+ createComponent(parent, vnode, hooks, ns, nextSibling);
+ }
+ function createText(parent, vnode, nextSibling) {
+ vnode.dom = getDocument(parent).createTextNode(vnode.children);
+ insertDOM(parent, vnode.dom, nextSibling);
+ }
+ var possibleParents = { caption: "table", thead: "table", tbody: "table", tfoot: "table", tr: "tbody", th: "tr", td: "tr", colgroup: "table", col: "colgroup" };
+ function createHTML(parent, vnode, ns, nextSibling) {
+ var match = vnode.children.match(/^\s*?<(\w+)/im) || [];
+ var temp = getDocument(parent).createElement(possibleParents[match[1]] || "div");
+ if (ns === "http://www.w3.org/2000/svg") {
+ temp.innerHTML = '";
+ temp = temp.firstChild;
+ } else {
+ temp.innerHTML = vnode.children;
+ }
+ vnode.dom = temp.firstChild;
+ vnode.domSize = temp.childNodes.length;
+ var fragment = getDocument(parent).createDocumentFragment();
+ var child;
+ while (child = temp.firstChild) {
+ fragment.appendChild(child);
+ }
+ insertDOM(parent, fragment, nextSibling);
+ }
+ function createFragment(parent, vnode, hooks, ns, nextSibling) {
+ var fragment = getDocument(parent).createDocumentFragment();
+ if (vnode.children != null) {
+ var children = vnode.children;
+ createNodes(fragment, children, 0, children.length, hooks, null, ns);
+ }
+ vnode.dom = fragment.firstChild;
+ vnode.domSize = fragment.childNodes.length;
+ insertDOM(parent, fragment, nextSibling);
+ }
+ function createElement(parent, vnode, hooks, ns, nextSibling) {
+ var tag = vnode.tag;
+ var attrs = vnode.attrs;
+ var is = vnode.is;
+ ns = getNameSpace(vnode) || ns;
+ var element = ns ? is ? getDocument(parent).createElementNS(ns, tag, { is }) : getDocument(parent).createElementNS(ns, tag) : is ? getDocument(parent).createElement(tag, { is }) : getDocument(parent).createElement(tag);
+ vnode.dom = element;
+ if (attrs != null) {
+ setAttrs(vnode, attrs, ns);
+ }
+ insertDOM(parent, element, nextSibling);
+ if (!maybeSetContentEditable(vnode)) {
+ if (vnode.children != null) {
+ var children = vnode.children;
+ createNodes(element, children, 0, children.length, hooks, null, ns);
+ if (vnode.tag === "select" && attrs != null)
+ setLateSelectAttrs(vnode, attrs);
+ }
+ }
+ }
+ function initComponent(vnode, hooks) {
+ var sentinel;
+ if (typeof vnode.tag.view === "function") {
+ vnode.state = Object.create(vnode.tag);
+ sentinel = vnode.state.view;
+ if (sentinel.$$reentrantLock$$ != null)
+ return;
+ sentinel.$$reentrantLock$$ = true;
+ } else {
+ vnode.state = undefined;
+ sentinel = vnode.tag;
+ if (sentinel.$$reentrantLock$$ != null)
+ return;
+ sentinel.$$reentrantLock$$ = true;
+ vnode.state = vnode.tag.prototype != null && typeof vnode.tag.prototype.view === "function" ? new vnode.tag(vnode) : vnode.tag(vnode);
+ }
+ initLifecycle(vnode.state, vnode, hooks);
+ if (vnode.attrs != null)
+ initLifecycle(vnode.attrs, vnode, hooks);
+ vnode.instance = Vnode.normalize(callHook.call(vnode.state.view, vnode));
+ if (vnode.instance === vnode)
+ throw Error("A view cannot return the vnode it received as argument");
+ sentinel.$$reentrantLock$$ = null;
+ }
+ function createComponent(parent, vnode, hooks, ns, nextSibling) {
+ initComponent(vnode, hooks);
+ if (vnode.instance != null) {
+ createNode(parent, vnode.instance, hooks, ns, nextSibling);
+ vnode.dom = vnode.instance.dom;
+ vnode.domSize = vnode.instance.domSize;
+ } else {
+ vnode.domSize = 0;
+ }
+ }
+ function updateNodes(parent, old, vnodes, hooks, nextSibling, ns) {
+ if (old === vnodes || old == null && vnodes == null)
+ return;
+ else if (old == null || old.length === 0)
+ createNodes(parent, vnodes, 0, vnodes.length, hooks, nextSibling, ns);
+ else if (vnodes == null || vnodes.length === 0)
+ removeNodes(parent, old, 0, old.length);
+ else {
+ var isOldKeyed = old[0] != null && old[0].key != null;
+ var isKeyed = vnodes[0] != null && vnodes[0].key != null;
+ var start = 0, oldStart = 0;
+ if (!isOldKeyed)
+ while (oldStart < old.length && old[oldStart] == null)
+ oldStart++;
+ if (!isKeyed)
+ while (start < vnodes.length && vnodes[start] == null)
+ start++;
+ if (isOldKeyed !== isKeyed) {
+ removeNodes(parent, old, oldStart, old.length);
+ createNodes(parent, vnodes, start, vnodes.length, hooks, nextSibling, ns);
+ } else if (!isKeyed) {
+ var commonLength = old.length < vnodes.length ? old.length : vnodes.length;
+ start = start < oldStart ? start : oldStart;
+ for (;start < commonLength; start++) {
+ o = old[start];
+ v = vnodes[start];
+ if (o === v || o == null && v == null)
+ continue;
+ else if (o == null)
+ createNode(parent, v, hooks, ns, getNextSibling(old, start + 1, nextSibling));
+ else if (v == null)
+ removeNode(parent, o);
+ else
+ updateNode(parent, o, v, hooks, getNextSibling(old, start + 1, nextSibling), ns);
+ }
+ if (old.length > commonLength)
+ removeNodes(parent, old, start, old.length);
+ if (vnodes.length > commonLength)
+ createNodes(parent, vnodes, start, vnodes.length, hooks, nextSibling, ns);
+ } else {
+ var oldEnd = old.length - 1, end = vnodes.length - 1, map, o, v, oe, ve, topSibling;
+ while (oldEnd >= oldStart && end >= start) {
+ oe = old[oldEnd];
+ ve = vnodes[end];
+ if (oe.key !== ve.key)
+ break;
+ if (oe !== ve)
+ updateNode(parent, oe, ve, hooks, nextSibling, ns);
+ if (ve.dom != null)
+ nextSibling = ve.dom;
+ oldEnd--, end--;
+ }
+ while (oldEnd >= oldStart && end >= start) {
+ o = old[oldStart];
+ v = vnodes[start];
+ if (o.key !== v.key)
+ break;
+ oldStart++, start++;
+ if (o !== v)
+ updateNode(parent, o, v, hooks, getNextSibling(old, oldStart, nextSibling), ns);
+ }
+ while (oldEnd >= oldStart && end >= start) {
+ if (start === end)
+ break;
+ if (o.key !== ve.key || oe.key !== v.key)
+ break;
+ topSibling = getNextSibling(old, oldStart, nextSibling);
+ moveDOM(parent, oe, topSibling);
+ if (oe !== v)
+ updateNode(parent, oe, v, hooks, topSibling, ns);
+ if (++start <= --end)
+ moveDOM(parent, o, nextSibling);
+ if (o !== ve)
+ updateNode(parent, o, ve, hooks, nextSibling, ns);
+ if (ve.dom != null)
+ nextSibling = ve.dom;
+ oldStart++;
+ oldEnd--;
+ oe = old[oldEnd];
+ ve = vnodes[end];
+ o = old[oldStart];
+ v = vnodes[start];
+ }
+ while (oldEnd >= oldStart && end >= start) {
+ if (oe.key !== ve.key)
+ break;
+ if (oe !== ve)
+ updateNode(parent, oe, ve, hooks, nextSibling, ns);
+ if (ve.dom != null)
+ nextSibling = ve.dom;
+ oldEnd--, end--;
+ oe = old[oldEnd];
+ ve = vnodes[end];
+ }
+ if (start > end)
+ removeNodes(parent, old, oldStart, oldEnd + 1);
+ else if (oldStart > oldEnd)
+ createNodes(parent, vnodes, start, end + 1, hooks, nextSibling, ns);
+ else {
+ var originalNextSibling = nextSibling, vnodesLength = end - start + 1, oldIndices = new Array(vnodesLength), li = 0, i = 0, pos = 2147483647, matched = 0, map, lisIndices;
+ for (i = 0;i < vnodesLength; i++)
+ oldIndices[i] = -1;
+ for (i = end;i >= start; i--) {
+ if (map == null)
+ map = getKeyMap(old, oldStart, oldEnd + 1);
+ ve = vnodes[i];
+ var oldIndex = map[ve.key];
+ if (oldIndex != null) {
+ pos = oldIndex < pos ? oldIndex : -1;
+ oldIndices[i - start] = oldIndex;
+ oe = old[oldIndex];
+ old[oldIndex] = null;
+ if (oe !== ve)
+ updateNode(parent, oe, ve, hooks, nextSibling, ns);
+ if (ve.dom != null)
+ nextSibling = ve.dom;
+ matched++;
+ }
+ }
+ nextSibling = originalNextSibling;
+ if (matched !== oldEnd - oldStart + 1)
+ removeNodes(parent, old, oldStart, oldEnd + 1);
+ if (matched === 0)
+ createNodes(parent, vnodes, start, end + 1, hooks, nextSibling, ns);
+ else {
+ if (pos === -1) {
+ lisIndices = makeLisIndices(oldIndices);
+ li = lisIndices.length - 1;
+ for (i = end;i >= start; i--) {
+ v = vnodes[i];
+ if (oldIndices[i - start] === -1)
+ createNode(parent, v, hooks, ns, nextSibling);
+ else {
+ if (lisIndices[li] === i - start)
+ li--;
+ else
+ moveDOM(parent, v, nextSibling);
+ }
+ if (v.dom != null)
+ nextSibling = vnodes[i].dom;
+ }
+ } else {
+ for (i = end;i >= start; i--) {
+ v = vnodes[i];
+ if (oldIndices[i - start] === -1)
+ createNode(parent, v, hooks, ns, nextSibling);
+ if (v.dom != null)
+ nextSibling = vnodes[i].dom;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ function updateNode(parent, old, vnode, hooks, nextSibling, ns) {
+ var oldTag = old.tag, tag = vnode.tag;
+ if (oldTag === tag && old.is === vnode.is) {
+ vnode.state = old.state;
+ vnode.events = old.events;
+ if (shouldNotUpdate(vnode, old))
+ return;
+ if (typeof oldTag === "string") {
+ if (vnode.attrs != null) {
+ updateLifecycle(vnode.attrs, vnode, hooks);
+ }
+ switch (oldTag) {
+ case "#":
+ updateText(old, vnode);
+ break;
+ case "<":
+ updateHTML(parent, old, vnode, ns, nextSibling);
+ break;
+ case "[":
+ updateFragment(parent, old, vnode, hooks, nextSibling, ns);
+ break;
+ default:
+ updateElement(old, vnode, hooks, ns);
+ }
+ } else
+ updateComponent(parent, old, vnode, hooks, nextSibling, ns);
+ } else {
+ removeNode(parent, old);
+ createNode(parent, vnode, hooks, ns, nextSibling);
+ }
+ }
+ function updateText(old, vnode) {
+ if (old.children.toString() !== vnode.children.toString()) {
+ old.dom.nodeValue = vnode.children;
+ }
+ vnode.dom = old.dom;
+ }
+ function updateHTML(parent, old, vnode, ns, nextSibling) {
+ if (old.children !== vnode.children) {
+ removeDOM(parent, old);
+ createHTML(parent, vnode, ns, nextSibling);
+ } else {
+ vnode.dom = old.dom;
+ vnode.domSize = old.domSize;
+ }
+ }
+ function updateFragment(parent, old, vnode, hooks, nextSibling, ns) {
+ updateNodes(parent, old.children, vnode.children, hooks, nextSibling, ns);
+ var domSize = 0, children = vnode.children;
+ vnode.dom = null;
+ if (children != null) {
+ for (var i = 0;i < children.length; i++) {
+ var child = children[i];
+ if (child != null && child.dom != null) {
+ if (vnode.dom == null)
+ vnode.dom = child.dom;
+ domSize += child.domSize || 1;
+ }
+ }
+ }
+ vnode.domSize = domSize;
+ }
+ function updateElement(old, vnode, hooks, ns) {
+ var element = vnode.dom = old.dom;
+ ns = getNameSpace(vnode) || ns;
+ if (old.attrs != vnode.attrs || vnode.attrs != null && !cachedAttrsIsStaticMap.get(vnode.attrs)) {
+ updateAttrs(vnode, old.attrs, vnode.attrs, ns);
+ }
+ if (!maybeSetContentEditable(vnode)) {
+ updateNodes(element, old.children, vnode.children, hooks, null, ns);
+ }
+ }
+ function updateComponent(parent, old, vnode, hooks, nextSibling, ns) {
+ vnode.instance = Vnode.normalize(callHook.call(vnode.state.view, vnode));
+ if (vnode.instance === vnode)
+ throw Error("A view cannot return the vnode it received as argument");
+ updateLifecycle(vnode.state, vnode, hooks);
+ if (vnode.attrs != null)
+ updateLifecycle(vnode.attrs, vnode, hooks);
+ if (vnode.instance != null) {
+ if (old.instance == null)
+ createNode(parent, vnode.instance, hooks, ns, nextSibling);
+ else
+ updateNode(parent, old.instance, vnode.instance, hooks, nextSibling, ns);
+ vnode.dom = vnode.instance.dom;
+ vnode.domSize = vnode.instance.domSize;
+ } else {
+ if (old.instance != null)
+ removeNode(parent, old.instance);
+ vnode.domSize = 0;
+ }
+ }
+ function getKeyMap(vnodes, start, end) {
+ var map = Object.create(null);
+ for (;start < end; start++) {
+ var vnode = vnodes[start];
+ if (vnode != null) {
+ var key = vnode.key;
+ if (key != null)
+ map[key] = start;
+ }
+ }
+ return map;
+ }
+ var lisTemp = [];
+ function makeLisIndices(a) {
+ var result = [0];
+ var u = 0, v = 0, i = 0;
+ var il = lisTemp.length = a.length;
+ for (var i = 0;i < il; i++)
+ lisTemp[i] = a[i];
+ for (var i = 0;i < il; ++i) {
+ if (a[i] === -1)
+ continue;
+ var j = result[result.length - 1];
+ if (a[j] < a[i]) {
+ lisTemp[i] = j;
+ result.push(i);
+ continue;
+ }
+ u = 0;
+ v = result.length - 1;
+ while (u < v) {
+ var c = (u >>> 1) + (v >>> 1) + (u & v & 1);
+ if (a[result[c]] < a[i]) {
+ u = c + 1;
+ } else {
+ v = c;
+ }
+ }
+ if (a[i] < a[result[u]]) {
+ if (u > 0)
+ lisTemp[i] = result[u - 1];
+ result[u] = i;
+ }
+ }
+ u = result.length;
+ v = result[u - 1];
+ while (u-- > 0) {
+ result[u] = v;
+ v = lisTemp[v];
+ }
+ lisTemp.length = 0;
+ return result;
+ }
+ function getNextSibling(vnodes, i, nextSibling) {
+ for (;i < vnodes.length; i++) {
+ if (vnodes[i] != null && vnodes[i].dom != null)
+ return vnodes[i].dom;
+ }
+ return nextSibling;
+ }
+ function moveDOM(parent, vnode, nextSibling) {
+ if (vnode.dom != null) {
+ var target;
+ if (vnode.domSize == null || vnode.domSize === 1) {
+ target = vnode.dom;
+ } else {
+ target = getDocument(parent).createDocumentFragment();
+ for (var dom of domFor(vnode))
+ target.appendChild(dom);
+ }
+ insertDOM(parent, target, nextSibling);
+ }
+ }
+ function insertDOM(parent, dom, nextSibling) {
+ if (nextSibling != null)
+ parent.insertBefore(dom, nextSibling);
+ else
+ parent.appendChild(dom);
+ }
+ function maybeSetContentEditable(vnode) {
+ if (vnode.attrs == null || vnode.attrs.contenteditable == null && vnode.attrs.contentEditable == null)
+ return false;
+ var children = vnode.children;
+ if (children != null && children.length === 1 && children[0].tag === "<") {
+ var content = children[0].children;
+ if (vnode.dom.innerHTML !== content)
+ vnode.dom.innerHTML = content;
+ } else if (children != null && children.length !== 0)
+ throw new Error("Child node of a contenteditable must be trusted.");
+ return true;
+ }
+ function removeNodes(parent, vnodes, start, end) {
+ for (var i = start;i < end; i++) {
+ var vnode = vnodes[i];
+ if (vnode != null)
+ removeNode(parent, vnode);
+ }
+ }
+ function tryBlockRemove(parent, vnode, source, counter) {
+ var original = vnode.state;
+ var result = callHook.call(source.onbeforeremove, vnode);
+ if (result == null)
+ return;
+ var generation = currentRender;
+ for (var dom of domFor(vnode))
+ delayedRemoval.set(dom, generation);
+ counter.v++;
+ Promise.resolve(result).finally(function() {
+ checkState(vnode, original);
+ tryResumeRemove(parent, vnode, counter);
+ });
+ }
+ function tryResumeRemove(parent, vnode, counter) {
+ if (--counter.v === 0) {
+ onremove(vnode);
+ removeDOM(parent, vnode);
+ }
+ }
+ function removeNode(parent, vnode) {
+ var counter = { v: 1 };
+ if (typeof vnode.tag !== "string" && typeof vnode.state.onbeforeremove === "function")
+ tryBlockRemove(parent, vnode, vnode.state, counter);
+ if (vnode.attrs && typeof vnode.attrs.onbeforeremove === "function")
+ tryBlockRemove(parent, vnode, vnode.attrs, counter);
+ tryResumeRemove(parent, vnode, counter);
+ }
+ function removeDOM(parent, vnode) {
+ if (vnode.dom == null)
+ return;
+ if (vnode.domSize == null || vnode.domSize === 1) {
+ parent.removeChild(vnode.dom);
+ } else {
+ for (var dom of domFor(vnode))
+ parent.removeChild(dom);
+ }
+ }
+ function onremove(vnode) {
+ if (typeof vnode.tag !== "string" && typeof vnode.state.onremove === "function")
+ callHook.call(vnode.state.onremove, vnode);
+ if (vnode.attrs && typeof vnode.attrs.onremove === "function")
+ callHook.call(vnode.attrs.onremove, vnode);
+ if (typeof vnode.tag !== "string") {
+ if (vnode.instance != null)
+ onremove(vnode.instance);
+ } else {
+ if (vnode.events != null)
+ vnode.events._ = null;
+ var children = vnode.children;
+ if (Array.isArray(children)) {
+ for (var i = 0;i < children.length; i++) {
+ var child = children[i];
+ if (child != null)
+ onremove(child);
+ }
+ }
+ }
+ }
+ function setAttrs(vnode, attrs, ns) {
+ for (var key in attrs) {
+ setAttr(vnode, key, null, attrs[key], ns);
+ }
+ }
+ function setAttr(vnode, key, old, value, ns) {
+ if (key === "key" || value == null || isLifecycleMethod(key) || old === value && !isFormAttribute(vnode, key) && typeof value !== "object")
+ return;
+ if (key[0] === "o" && key[1] === "n")
+ return updateEvent(vnode, key, value);
+ if (key.slice(0, 6) === "xlink:")
+ vnode.dom.setAttributeNS("http://www.w3.org/1999/xlink", key.slice(6), value);
+ else if (key === "style")
+ updateStyle(vnode.dom, old, value);
+ else if (hasPropertyKey(vnode, key, ns)) {
+ if (key === "value") {
+ if ((vnode.tag === "input" || vnode.tag === "textarea") && vnode.dom.value === "" + value)
+ return;
+ if (vnode.tag === "select" && old !== null && vnode.dom.value === "" + value)
+ return;
+ if (vnode.tag === "option" && old !== null && vnode.dom.value === "" + value)
+ return;
+ if (vnode.tag === "input" && vnode.attrs.type === "file" && "" + value !== "") {
+ console.error("`value` is read-only on file inputs!");
+ return;
+ }
+ }
+ if (vnode.tag === "input" && key === "type")
+ vnode.dom.setAttribute(key, value);
+ else
+ vnode.dom[key] = value;
+ } else {
+ if (typeof value === "boolean") {
+ if (value)
+ vnode.dom.setAttribute(key, "");
+ else
+ vnode.dom.removeAttribute(key);
+ } else
+ vnode.dom.setAttribute(key === "className" ? "class" : key, value);
+ }
+ }
+ function removeAttr(vnode, key, old, ns) {
+ if (key === "key" || old == null || isLifecycleMethod(key))
+ return;
+ if (key[0] === "o" && key[1] === "n")
+ updateEvent(vnode, key, undefined);
+ else if (key === "style")
+ updateStyle(vnode.dom, old, null);
+ else if (hasPropertyKey(vnode, key, ns) && key !== "className" && key !== "title" && !(key === "value" && (vnode.tag === "option" || vnode.tag === "select" && vnode.dom.selectedIndex === -1 && vnode.dom === activeElement(vnode.dom))) && !(vnode.tag === "input" && key === "type")) {
+ vnode.dom[key] = null;
+ } else {
+ var nsLastIndex = key.indexOf(":");
+ if (nsLastIndex !== -1)
+ key = key.slice(nsLastIndex + 1);
+ if (old !== false)
+ vnode.dom.removeAttribute(key === "className" ? "class" : key);
+ }
+ }
+ function setLateSelectAttrs(vnode, attrs) {
+ if ("value" in attrs) {
+ if (attrs.value === null) {
+ if (vnode.dom.selectedIndex !== -1)
+ vnode.dom.value = null;
+ } else {
+ var normalized = "" + attrs.value;
+ if (vnode.dom.value !== normalized || vnode.dom.selectedIndex === -1) {
+ vnode.dom.value = normalized;
+ }
+ }
+ }
+ if ("selectedIndex" in attrs)
+ setAttr(vnode, "selectedIndex", null, attrs.selectedIndex, undefined);
+ }
+ function updateAttrs(vnode, old, attrs, ns) {
+ var val;
+ if (old != null) {
+ if (old === attrs && !cachedAttrsIsStaticMap.has(attrs)) {
+ console.warn("Don't reuse attrs object, use new object for every redraw, this will throw in next major");
+ }
+ for (var key in old) {
+ if ((val = old[key]) != null && (attrs == null || attrs[key] == null)) {
+ removeAttr(vnode, key, val, ns);
+ }
+ }
+ }
+ if (attrs != null) {
+ for (var key in attrs) {
+ setAttr(vnode, key, old && old[key], attrs[key], ns);
+ }
+ }
+ }
+ function isFormAttribute(vnode, attr) {
+ return attr === "value" || attr === "checked" || attr === "selectedIndex" || attr === "selected" && (vnode.dom === activeElement(vnode.dom) || vnode.tag === "option" && vnode.dom.parentNode === activeElement(vnode.dom));
+ }
+ function isLifecycleMethod(attr) {
+ return attr === "oninit" || attr === "oncreate" || attr === "onupdate" || attr === "onremove" || attr === "onbeforeremove" || attr === "onbeforeupdate";
+ }
+ function hasPropertyKey(vnode, key, ns) {
+ return ns === undefined && (vnode.tag.indexOf("-") > -1 || vnode.is || key !== "href" && key !== "list" && key !== "form" && key !== "width" && key !== "height") && key in vnode.dom;
+ }
+ function updateStyle(element, old, style) {
+ if (old === style) {} else if (style == null) {
+ element.style = "";
+ } else if (typeof style !== "object") {
+ element.style = style;
+ } else if (old == null || typeof old !== "object") {
+ element.style = "";
+ for (var key in style) {
+ var value = style[key];
+ if (value != null) {
+ if (key.includes("-"))
+ element.style.setProperty(key, String(value));
+ else
+ element.style[key] = String(value);
+ }
+ }
+ } else {
+ for (var key in old) {
+ if (old[key] != null && style[key] == null) {
+ if (key.includes("-"))
+ element.style.removeProperty(key);
+ else
+ element.style[key] = "";
+ }
+ }
+ for (var key in style) {
+ var value = style[key];
+ if (value != null && (value = String(value)) !== String(old[key])) {
+ if (key.includes("-"))
+ element.style.setProperty(key, value);
+ else
+ element.style[key] = value;
+ }
+ }
+ }
+ }
+ function EventDict() {
+ this._ = currentRedraw;
+ }
+ EventDict.prototype = Object.create(null);
+ EventDict.prototype.handleEvent = function(ev) {
+ var handler = this["on" + ev.type];
+ var result;
+ if (typeof handler === "function")
+ result = handler.call(ev.currentTarget, ev);
+ else if (typeof handler.handleEvent === "function")
+ handler.handleEvent(ev);
+ var self = this;
+ if (self._ != null) {
+ if (ev.redraw !== false)
+ (0, self._)();
+ if (result != null && typeof result.then === "function") {
+ Promise.resolve(result).then(function() {
+ if (self._ != null && ev.redraw !== false)
+ (0, self._)();
+ });
+ }
+ }
+ if (result === false) {
+ ev.preventDefault();
+ ev.stopPropagation();
+ }
+ };
+ function updateEvent(vnode, key, value) {
+ if (vnode.events != null) {
+ vnode.events._ = currentRedraw;
+ if (vnode.events[key] === value)
+ return;
+ if (value != null && (typeof value === "function" || typeof value === "object")) {
+ if (vnode.events[key] == null)
+ vnode.dom.addEventListener(key.slice(2), vnode.events, false);
+ vnode.events[key] = value;
+ } else {
+ if (vnode.events[key] != null)
+ vnode.dom.removeEventListener(key.slice(2), vnode.events, false);
+ vnode.events[key] = undefined;
+ }
+ } else if (value != null && (typeof value === "function" || typeof value === "object")) {
+ vnode.events = new EventDict;
+ vnode.dom.addEventListener(key.slice(2), vnode.events, false);
+ vnode.events[key] = value;
+ }
+ }
+ function initLifecycle(source, vnode, hooks) {
+ if (typeof source.oninit === "function")
+ callHook.call(source.oninit, vnode);
+ if (typeof source.oncreate === "function")
+ hooks.push(callHook.bind(source.oncreate, vnode));
+ }
+ function updateLifecycle(source, vnode, hooks) {
+ if (typeof source.onupdate === "function")
+ hooks.push(callHook.bind(source.onupdate, vnode));
+ }
+ function shouldNotUpdate(vnode, old) {
+ do {
+ if (vnode.attrs != null && typeof vnode.attrs.onbeforeupdate === "function") {
+ var force = callHook.call(vnode.attrs.onbeforeupdate, vnode, old);
+ if (force !== undefined && !force)
+ break;
+ }
+ if (typeof vnode.tag !== "string" && typeof vnode.state.onbeforeupdate === "function") {
+ var force = callHook.call(vnode.state.onbeforeupdate, vnode, old);
+ if (force !== undefined && !force)
+ break;
+ }
+ return false;
+ } while (false);
+ vnode.dom = old.dom;
+ vnode.domSize = old.domSize;
+ vnode.instance = old.instance;
+ vnode.attrs = old.attrs;
+ vnode.children = old.children;
+ vnode.text = old.text;
+ return true;
+ }
+ var currentDOM;
+ return function(dom, vnodes, redraw) {
+ if (!dom)
+ throw new TypeError("DOM element being rendered to does not exist.");
+ if (currentDOM != null && dom.contains(currentDOM)) {
+ throw new TypeError("Node is currently being rendered to and thus is locked.");
+ }
+ var prevRedraw = currentRedraw;
+ var prevDOM = currentDOM;
+ var hooks = [];
+ var active = activeElement(dom);
+ var namespace = dom.namespaceURI;
+ currentDOM = dom;
+ currentRedraw = typeof redraw === "function" ? redraw : undefined;
+ currentRender = {};
+ try {
+ if (dom.vnodes == null)
+ dom.textContent = "";
+ vnodes = Vnode.normalizeChildren(Array.isArray(vnodes) ? vnodes : [vnodes]);
+ updateNodes(dom, dom.vnodes, vnodes, hooks, null, namespace === "http://www.w3.org/1999/xhtml" ? undefined : namespace);
+ dom.vnodes = vnodes;
+ if (active != null && activeElement(dom) !== active && typeof active.focus === "function")
+ active.focus();
+ for (var i = 0;i < hooks.length; i++)
+ hooks[i]();
+ } finally {
+ currentRedraw = prevRedraw;
+ currentDOM = prevDOM;
+ }
+ };
+ };
+});
+
+// node_modules/mithril/render.js
+var require_render2 = __commonJS((exports, module) => {
+ module.exports = require_render()();
+});
+
+// node_modules/mithril/api/mount-redraw.js
+var require_mount_redraw = __commonJS((exports, module) => {
+ var Vnode = require_vnode();
+ module.exports = function(render, schedule, console2) {
+ var subscriptions = [];
+ var pending = false;
+ var offset = -1;
+ function sync() {
+ for (offset = 0;offset < subscriptions.length; offset += 2) {
+ try {
+ render(subscriptions[offset], Vnode(subscriptions[offset + 1]), redraw);
+ } catch (e) {
+ console2.error(e);
+ }
+ }
+ offset = -1;
+ }
+ function redraw() {
+ if (!pending) {
+ pending = true;
+ schedule(function() {
+ pending = false;
+ sync();
+ });
+ }
+ }
+ redraw.sync = sync;
+ function mount(root, component) {
+ if (component != null && component.view == null && typeof component !== "function") {
+ throw new TypeError("m.mount expects a component, not a vnode.");
+ }
+ var index = subscriptions.indexOf(root);
+ if (index >= 0) {
+ subscriptions.splice(index, 2);
+ if (index <= offset)
+ offset -= 2;
+ render(root, []);
+ }
+ if (component != null) {
+ subscriptions.push(root, component);
+ render(root, Vnode(component), redraw);
+ }
+ }
+ return { mount, redraw };
+ };
+});
+
+// node_modules/mithril/mount-redraw.js
+var require_mount_redraw2 = __commonJS((exports, module) => {
+ var render = require_render2();
+ module.exports = require_mount_redraw()(render, typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : null, typeof console !== "undefined" ? console : null);
+});
+
+// node_modules/mithril/querystring/build.js
+var require_build = __commonJS((exports, module) => {
+ module.exports = function(object) {
+ if (Object.prototype.toString.call(object) !== "[object Object]")
+ return "";
+ var args = [];
+ for (var key in object) {
+ destructure(key, object[key]);
+ }
+ return args.join("&");
+ function destructure(key2, value) {
+ if (Array.isArray(value)) {
+ for (var i = 0;i < value.length; i++) {
+ destructure(key2 + "[" + i + "]", value[i]);
+ }
+ } else if (Object.prototype.toString.call(value) === "[object Object]") {
+ for (var i in value) {
+ destructure(key2 + "[" + i + "]", value[i]);
+ }
+ } else
+ args.push(encodeURIComponent(key2) + (value != null && value !== "" ? "=" + encodeURIComponent(value) : ""));
+ }
+ };
+});
+
+// node_modules/mithril/pathname/build.js
+var require_build2 = __commonJS((exports, module) => {
+ var buildQueryString = require_build();
+ module.exports = function(template, params) {
+ if (/:([^\/\.-]+)(\.{3})?:/.test(template)) {
+ throw new SyntaxError("Template parameter names must be separated by either a '/', '-', or '.'.");
+ }
+ if (params == null)
+ return template;
+ var queryIndex = template.indexOf("?");
+ var hashIndex = template.indexOf("#");
+ var queryEnd = hashIndex < 0 ? template.length : hashIndex;
+ var pathEnd = queryIndex < 0 ? queryEnd : queryIndex;
+ var path = template.slice(0, pathEnd);
+ var query = {};
+ Object.assign(query, params);
+ var resolved = path.replace(/:([^\/\.-]+)(\.{3})?/g, function(m, key, variadic) {
+ delete query[key];
+ if (params[key] == null)
+ return m;
+ return variadic ? params[key] : encodeURIComponent(String(params[key]));
+ });
+ var newQueryIndex = resolved.indexOf("?");
+ var newHashIndex = resolved.indexOf("#");
+ var newQueryEnd = newHashIndex < 0 ? resolved.length : newHashIndex;
+ var newPathEnd = newQueryIndex < 0 ? newQueryEnd : newQueryIndex;
+ var result = resolved.slice(0, newPathEnd);
+ if (queryIndex >= 0)
+ result += template.slice(queryIndex, queryEnd);
+ if (newQueryIndex >= 0)
+ result += (queryIndex < 0 ? "?" : "&") + resolved.slice(newQueryIndex, newQueryEnd);
+ var querystring = buildQueryString(query);
+ if (querystring)
+ result += (queryIndex < 0 && newQueryIndex < 0 ? "?" : "&") + querystring;
+ if (hashIndex >= 0)
+ result += template.slice(hashIndex);
+ if (newHashIndex >= 0)
+ result += (hashIndex < 0 ? "" : "&") + resolved.slice(newHashIndex);
+ return result;
+ };
+});
+
+// node_modules/mithril/request/request.js
+var require_request = __commonJS((exports, module) => {
+ var buildPathname = require_build2();
+ var hasOwn = require_hasOwn();
+ module.exports = function($window, oncompletion) {
+ function PromiseProxy(executor) {
+ return new Promise(executor);
+ }
+ function makeRequest(url, args) {
+ return new Promise(function(resolve, reject) {
+ url = buildPathname(url, args.params);
+ var method = args.method != null ? args.method.toUpperCase() : "GET";
+ var body = args.body;
+ var assumeJSON = (args.serialize == null || args.serialize === JSON.serialize) && !(body instanceof $window.FormData || body instanceof $window.URLSearchParams);
+ var responseType = args.responseType || (typeof args.extract === "function" ? "" : "json");
+ var xhr = new $window.XMLHttpRequest, aborted = false, isTimeout = false;
+ var original = xhr, replacedAbort;
+ var abort = xhr.abort;
+ xhr.abort = function() {
+ aborted = true;
+ abort.call(this);
+ };
+ xhr.open(method, url, args.async !== false, typeof args.user === "string" ? args.user : undefined, typeof args.password === "string" ? args.password : undefined);
+ if (assumeJSON && body != null && !hasHeader(args, "content-type")) {
+ xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8");
+ }
+ if (typeof args.deserialize !== "function" && !hasHeader(args, "accept")) {
+ xhr.setRequestHeader("Accept", "application/json, text/*");
+ }
+ if (args.withCredentials)
+ xhr.withCredentials = args.withCredentials;
+ if (args.timeout)
+ xhr.timeout = args.timeout;
+ xhr.responseType = responseType;
+ for (var key in args.headers) {
+ if (hasOwn.call(args.headers, key)) {
+ xhr.setRequestHeader(key, args.headers[key]);
+ }
+ }
+ xhr.onreadystatechange = function(ev) {
+ if (aborted)
+ return;
+ if (ev.target.readyState === 4) {
+ try {
+ var success = ev.target.status >= 200 && ev.target.status < 300 || ev.target.status === 304 || /^file:\/\//i.test(url);
+ var response = ev.target.response, message;
+ if (responseType === "json") {
+ if (!ev.target.responseType && typeof args.extract !== "function") {
+ try {
+ response = JSON.parse(ev.target.responseText);
+ } catch (e) {
+ response = null;
+ }
+ }
+ } else if (!responseType || responseType === "text") {
+ if (response == null)
+ response = ev.target.responseText;
+ }
+ if (typeof args.extract === "function") {
+ response = args.extract(ev.target, args);
+ success = true;
+ } else if (typeof args.deserialize === "function") {
+ response = args.deserialize(response);
+ }
+ if (success) {
+ if (typeof args.type === "function") {
+ if (Array.isArray(response)) {
+ for (var i = 0;i < response.length; i++) {
+ response[i] = new args.type(response[i]);
+ }
+ } else
+ response = new args.type(response);
+ }
+ resolve(response);
+ } else {
+ var completeErrorResponse = function() {
+ try {
+ message = ev.target.responseText;
+ } catch (e) {
+ message = response;
+ }
+ var error = new Error(message);
+ error.code = ev.target.status;
+ error.response = response;
+ reject(error);
+ };
+ if (xhr.status === 0) {
+ setTimeout(function() {
+ if (isTimeout)
+ return;
+ completeErrorResponse();
+ });
+ } else
+ completeErrorResponse();
+ }
+ } catch (e) {
+ reject(e);
+ }
+ }
+ };
+ xhr.ontimeout = function(ev) {
+ isTimeout = true;
+ var error = new Error("Request timed out");
+ error.code = ev.target.status;
+ reject(error);
+ };
+ if (typeof args.config === "function") {
+ xhr = args.config(xhr, args, url) || xhr;
+ if (xhr !== original) {
+ replacedAbort = xhr.abort;
+ xhr.abort = function() {
+ aborted = true;
+ replacedAbort.call(this);
+ };
+ }
+ }
+ if (body == null)
+ xhr.send();
+ else if (typeof args.serialize === "function")
+ xhr.send(args.serialize(body));
+ else if (body instanceof $window.FormData || body instanceof $window.URLSearchParams)
+ xhr.send(body);
+ else
+ xhr.send(JSON.stringify(body));
+ });
+ }
+ PromiseProxy.prototype = Promise.prototype;
+ PromiseProxy.__proto__ = Promise;
+ function hasHeader(args, name) {
+ for (var key in args.headers) {
+ if (hasOwn.call(args.headers, key) && key.toLowerCase() === name)
+ return true;
+ }
+ return false;
+ }
+ return {
+ request: function(url, args) {
+ if (typeof url !== "string") {
+ args = url;
+ url = url.url;
+ } else if (args == null)
+ args = {};
+ var promise = makeRequest(url, args);
+ if (args.background === true)
+ return promise;
+ var count = 0;
+ function complete() {
+ if (--count === 0 && typeof oncompletion === "function")
+ oncompletion();
+ }
+ return wrap(promise);
+ function wrap(promise2) {
+ var then = promise2.then;
+ promise2.constructor = PromiseProxy;
+ promise2.then = function() {
+ count++;
+ var next = then.apply(promise2, arguments);
+ next.then(complete, function(e) {
+ complete();
+ if (count === 0)
+ throw e;
+ });
+ return wrap(next);
+ };
+ return promise2;
+ }
+ }
+ };
+ };
+});
+
+// node_modules/mithril/request.js
+var require_request2 = __commonJS((exports, module) => {
+ var mountRedraw = require_mount_redraw2();
+ module.exports = require_request()(typeof window !== "undefined" ? window : null, mountRedraw.redraw);
+});
+
+// node_modules/mithril/util/decodeURIComponentSafe.js
+var require_decodeURIComponentSafe = __commonJS((exports, module) => {
+ var validUtf8Encodings = /%(?:[0-7]|(?!c[01]|e0%[89]|ed%[ab]|f0%8|f4%[9ab])(?:c|d|(?:e|f[0-4]%[89ab])[\da-f]%[89ab])[\da-f]%[89ab])[\da-f]/gi;
+ module.exports = function(str) {
+ return String(str).replace(validUtf8Encodings, decodeURIComponent);
+ };
+});
+
+// node_modules/mithril/querystring/parse.js
+var require_parse = __commonJS((exports, module) => {
+ var decodeURIComponentSafe = require_decodeURIComponentSafe();
+ module.exports = function(string) {
+ if (string === "" || string == null)
+ return {};
+ if (string.charAt(0) === "?")
+ string = string.slice(1);
+ var entries = string.split("&"), counters = {}, data = {};
+ for (var i = 0;i < entries.length; i++) {
+ var entry = entries[i].split("=");
+ var key = decodeURIComponentSafe(entry[0]);
+ var value = entry.length === 2 ? decodeURIComponentSafe(entry[1]) : "";
+ if (value === "true")
+ value = true;
+ else if (value === "false")
+ value = false;
+ var levels = key.split(/\]\[?|\[/);
+ var cursor = data;
+ if (key.indexOf("[") > -1)
+ levels.pop();
+ for (var j = 0;j < levels.length; j++) {
+ var level = levels[j], nextLevel = levels[j + 1];
+ var isNumber = nextLevel == "" || !isNaN(parseInt(nextLevel, 10));
+ if (level === "") {
+ var key = levels.slice(0, j).join();
+ if (counters[key] == null) {
+ counters[key] = Array.isArray(cursor) ? cursor.length : 0;
+ }
+ level = counters[key]++;
+ } else if (level === "__proto__")
+ break;
+ if (j === levels.length - 1)
+ cursor[level] = value;
+ else {
+ var desc = Object.getOwnPropertyDescriptor(cursor, level);
+ if (desc != null)
+ desc = desc.value;
+ if (desc == null)
+ cursor[level] = desc = isNumber ? [] : {};
+ cursor = desc;
+ }
+ }
+ }
+ return data;
+ };
+});
+
+// node_modules/mithril/pathname/parse.js
+var require_parse2 = __commonJS((exports, module) => {
+ var parseQueryString = require_parse();
+ module.exports = function(url) {
+ var queryIndex = url.indexOf("?");
+ var hashIndex = url.indexOf("#");
+ var queryEnd = hashIndex < 0 ? url.length : hashIndex;
+ var pathEnd = queryIndex < 0 ? queryEnd : queryIndex;
+ var path = url.slice(0, pathEnd).replace(/\/{2,}/g, "/");
+ if (!path)
+ path = "/";
+ else {
+ if (path[0] !== "/")
+ path = "/" + path;
+ }
+ return {
+ path,
+ params: queryIndex < 0 ? {} : parseQueryString(url.slice(queryIndex + 1, queryEnd))
+ };
+ };
+});
+
+// node_modules/mithril/pathname/compileTemplate.js
+var require_compileTemplate = __commonJS((exports, module) => {
+ var parsePathname = require_parse2();
+ module.exports = function(template) {
+ var templateData = parsePathname(template);
+ var templateKeys = Object.keys(templateData.params);
+ var keys = [];
+ var regexp = new RegExp("^" + templateData.path.replace(/:([^\/.-]+)(\.{3}|\.(?!\.)|-)?|[\\^$*+.()|\[\]{}]/g, function(m, key, extra) {
+ if (key == null)
+ return "\\" + m;
+ keys.push({ k: key, r: extra === "..." });
+ if (extra === "...")
+ return "(.*)";
+ if (extra === ".")
+ return "([^/]+)\\.";
+ return "([^/]+)" + (extra || "");
+ }) + "\\/?$");
+ return function(data) {
+ for (var i = 0;i < templateKeys.length; i++) {
+ if (templateData.params[templateKeys[i]] !== data.params[templateKeys[i]])
+ return false;
+ }
+ if (!keys.length)
+ return regexp.test(data.path);
+ var values = regexp.exec(data.path);
+ if (values == null)
+ return false;
+ for (var i = 0;i < keys.length; i++) {
+ data.params[keys[i].k] = keys[i].r ? values[i + 1] : decodeURIComponent(values[i + 1]);
+ }
+ return true;
+ };
+ };
+});
+
+// node_modules/mithril/util/censor.js
+var require_censor = __commonJS((exports, module) => {
+ var hasOwn = require_hasOwn();
+ var magic = /^(?:key|oninit|oncreate|onbeforeupdate|onupdate|onbeforeremove|onremove)$/;
+ module.exports = function(attrs, extras) {
+ var result = {};
+ if (extras != null) {
+ for (var key in attrs) {
+ if (hasOwn.call(attrs, key) && !magic.test(key) && extras.indexOf(key) < 0) {
+ result[key] = attrs[key];
+ }
+ }
+ } else {
+ for (var key in attrs) {
+ if (hasOwn.call(attrs, key) && !magic.test(key)) {
+ result[key] = attrs[key];
+ }
+ }
+ }
+ return result;
+ };
+});
+
+// node_modules/mithril/api/router.js
+var require_router = __commonJS((exports, module) => {
+ var Vnode = require_vnode();
+ var hyperscript = require_hyperscript();
+ var decodeURIComponentSafe = require_decodeURIComponentSafe();
+ var buildPathname = require_build2();
+ var parsePathname = require_parse2();
+ var compileTemplate = require_compileTemplate();
+ var censor = require_censor();
+ module.exports = function($window, mountRedraw) {
+ var p = Promise.resolve();
+ var scheduled = false;
+ var ready = false;
+ var hasBeenResolved = false;
+ var dom, compiled, fallbackRoute;
+ var currentResolver, component, attrs, currentPath, lastUpdate;
+ var RouterRoot = {
+ onremove: function() {
+ ready = hasBeenResolved = false;
+ $window.removeEventListener("popstate", fireAsync, false);
+ },
+ view: function() {
+ var vnode = Vnode(component, attrs.key, attrs);
+ if (currentResolver)
+ return currentResolver.render(vnode);
+ return [vnode];
+ }
+ };
+ var SKIP = route.SKIP = {};
+ function resolveRoute() {
+ scheduled = false;
+ var prefix = $window.location.hash;
+ if (route.prefix[0] !== "#") {
+ prefix = $window.location.search + prefix;
+ if (route.prefix[0] !== "?") {
+ prefix = $window.location.pathname + prefix;
+ if (prefix[0] !== "/")
+ prefix = "/" + prefix;
+ }
+ }
+ var path = decodeURIComponentSafe(prefix).slice(route.prefix.length);
+ var data = parsePathname(path);
+ Object.assign(data.params, $window.history.state);
+ function reject(e) {
+ console.error(e);
+ route.set(fallbackRoute, null, { replace: true });
+ }
+ loop(0);
+ function loop(i) {
+ for (;i < compiled.length; i++) {
+ if (compiled[i].check(data)) {
+ var payload = compiled[i].component;
+ var matchedRoute = compiled[i].route;
+ var localComp = payload;
+ var update = lastUpdate = function(comp) {
+ if (update !== lastUpdate)
+ return;
+ if (comp === SKIP)
+ return loop(i + 1);
+ component = comp != null && (typeof comp.view === "function" || typeof comp === "function") ? comp : "div";
+ attrs = data.params, currentPath = path, lastUpdate = null;
+ currentResolver = payload.render ? payload : null;
+ if (hasBeenResolved)
+ mountRedraw.redraw();
+ else {
+ hasBeenResolved = true;
+ mountRedraw.mount(dom, RouterRoot);
+ }
+ };
+ if (payload.view || typeof payload === "function") {
+ payload = {};
+ update(localComp);
+ } else if (payload.onmatch) {
+ p.then(function() {
+ return payload.onmatch(data.params, path, matchedRoute);
+ }).then(update, path === fallbackRoute ? null : reject);
+ } else
+ update();
+ return;
+ }
+ }
+ if (path === fallbackRoute) {
+ throw new Error("Could not resolve default route " + fallbackRoute + ".");
+ }
+ route.set(fallbackRoute, null, { replace: true });
+ }
+ }
+ function fireAsync() {
+ if (!scheduled) {
+ scheduled = true;
+ setTimeout(resolveRoute);
+ }
+ }
+ function route(root, defaultRoute, routes) {
+ if (!root)
+ throw new TypeError("DOM element being rendered to does not exist.");
+ compiled = Object.keys(routes).map(function(route2) {
+ if (route2[0] !== "/")
+ throw new SyntaxError("Routes must start with a '/'.");
+ if (/:([^\/\.-]+)(\.{3})?:/.test(route2)) {
+ throw new SyntaxError("Route parameter names must be separated with either '/', '.', or '-'.");
+ }
+ return {
+ route: route2,
+ component: routes[route2],
+ check: compileTemplate(route2)
+ };
+ });
+ fallbackRoute = defaultRoute;
+ if (defaultRoute != null) {
+ var defaultData = parsePathname(defaultRoute);
+ if (!compiled.some(function(i) {
+ return i.check(defaultData);
+ })) {
+ throw new ReferenceError("Default route doesn't match any known routes.");
+ }
+ }
+ dom = root;
+ $window.addEventListener("popstate", fireAsync, false);
+ ready = true;
+ resolveRoute();
+ }
+ route.set = function(path, data, options) {
+ if (lastUpdate != null) {
+ options = options || {};
+ options.replace = true;
+ }
+ lastUpdate = null;
+ path = buildPathname(path, data);
+ if (ready) {
+ fireAsync();
+ var state = options ? options.state : null;
+ var title = options ? options.title : null;
+ if (options && options.replace)
+ $window.history.replaceState(state, title, route.prefix + path);
+ else
+ $window.history.pushState(state, title, route.prefix + path);
+ } else {
+ $window.location.href = route.prefix + path;
+ }
+ };
+ route.get = function() {
+ return currentPath;
+ };
+ route.prefix = "#!";
+ route.Link = {
+ view: function(vnode) {
+ var child = hyperscript(vnode.attrs.selector || "a", censor(vnode.attrs, ["options", "params", "selector", "onclick"]), vnode.children);
+ var options, onclick, href;
+ if (child.attrs.disabled = Boolean(child.attrs.disabled)) {
+ child.attrs.href = null;
+ child.attrs["aria-disabled"] = "true";
+ } else {
+ options = vnode.attrs.options;
+ onclick = vnode.attrs.onclick;
+ href = buildPathname(child.attrs.href, vnode.attrs.params);
+ child.attrs.href = route.prefix + href;
+ child.attrs.onclick = function(e) {
+ var result;
+ if (typeof onclick === "function") {
+ result = onclick.call(e.currentTarget, e);
+ } else if (onclick == null || typeof onclick !== "object") {} else if (typeof onclick.handleEvent === "function") {
+ onclick.handleEvent(e);
+ }
+ if (result !== false && !e.defaultPrevented && (e.button === 0 || e.which === 0 || e.which === 1) && (!e.currentTarget.target || e.currentTarget.target === "_self") && !e.ctrlKey && !e.metaKey && !e.shiftKey && !e.altKey) {
+ e.preventDefault();
+ e.redraw = false;
+ route.set(href, null, options);
+ }
+ };
+ }
+ return child;
+ }
+ };
+ route.param = function(key) {
+ return attrs && key != null ? attrs[key] : attrs;
+ };
+ return route;
+ };
+});
+
+// node_modules/mithril/route.js
+var require_route = __commonJS((exports, module) => {
+ var mountRedraw = require_mount_redraw2();
+ module.exports = require_router()(typeof window !== "undefined" ? window : null, mountRedraw);
+});
+
+// node_modules/mithril/index.js
+var require_mithril = __commonJS((exports, module) => {
+ var hyperscript = require_hyperscript2();
+ var mountRedraw = require_mount_redraw2();
+ var request = require_request2();
+ var router = require_route();
+ var m = function m2() {
+ return hyperscript.apply(this, arguments);
+ };
+ m.m = hyperscript;
+ m.trust = hyperscript.trust;
+ m.fragment = hyperscript.fragment;
+ m.Fragment = "[";
+ m.mount = mountRedraw.mount;
+ m.route = router;
+ m.render = require_render2();
+ m.redraw = mountRedraw.redraw;
+ m.request = request.request;
+ m.parseQueryString = require_parse();
+ m.buildQueryString = require_build();
+ m.parsePathname = require_parse2();
+ m.buildPathname = require_build2();
+ m.vnode = require_vnode();
+ m.censor = require_censor();
+ m.domFor = require_domFor();
+ module.exports = m;
+});
+
+// src/browser.ts
+var import_mithril = __toESM(require_mithril(), 1);
+
+// src/index.ts
+var exports_src = {};
+__export(exports_src, {
+ unpackPixels: () => unpackPixels,
+ pad32: () => pad32,
+ packedSize: () => packedSize,
+ packPixels: () => packPixels,
+ loadBinFile: () => loadBinFile,
+ isCustomFont: () => isCustomFont,
+ customFontOrdinal: () => customFontOrdinal,
+ buildBinBuffer: () => buildBinBuffer,
+ StringToSectionType: () => StringToSectionType,
+ StringToElementType: () => StringToElementType,
+ SectionTypeToString: () => SectionTypeToString,
+ SectionType: () => SectionType,
+ MonoDisplayRenderer: () => MonoDisplayRenderer,
+ MonoDisplayParser: () => MonoDisplayParser,
+ MonoDisplayFile: () => MonoDisplayFile,
+ MonoDisplayDriver: () => MonoDisplayDriver,
+ MONOFORMAT_MAGIC_HEADER: () => MONOFORMAT_MAGIC_HEADER,
+ FileVersion: () => FileVersion,
+ ElementTypeToString: () => ElementTypeToString,
+ ElementType: () => ElementType,
+ CUSTOM_FONT_BASE: () => CUSTOM_FONT_BASE,
+ BinaryReader: () => BinaryReader
+});
+
+// src/types.ts
+var FileVersion;
+((FileVersion2) => {
+ FileVersion2[FileVersion2["Illegal"] = 0] = "Illegal";
+ FileVersion2[FileVersion2["V1"] = 1] = "V1";
+})(FileVersion ||= {});
+var SectionType;
+((SectionType2) => {
+ SectionType2[SectionType2["ElementsAlways"] = 1] = "ElementsAlways";
+ SectionType2[SectionType2["ElementsTimespan"] = 2] = "ElementsTimespan";
+ SectionType2[SectionType2["CustomFont"] = 32] = "CustomFont";
+})(SectionType ||= {});
+var SectionTypeToString = Object.fromEntries(Object.keys(SectionType).filter((k) => isNaN(Number(k))).map((k) => [SectionType[k], k]));
+var StringToSectionType = Object.fromEntries(Object.keys(SectionType).filter((k) => isNaN(Number(k))).map((k) => [k, SectionType[k]]));
+var ElementType;
+((ElementType2) => {
+ ElementType2[ElementType2["Image2D"] = 1] = "Image2D";
+ ElementType2[ElementType2["Animation"] = 2] = "Animation";
+ ElementType2[ElementType2["HorizontalScroll"] = 3] = "HorizontalScroll";
+ ElementType2[ElementType2["VerticalScroll"] = 4] = "VerticalScroll";
+ ElementType2[ElementType2["Line"] = 5] = "Line";
+ ElementType2[ElementType2["ClippedText"] = 16] = "ClippedText";
+ ElementType2[ElementType2["HScrollText"] = 17] = "HScrollText";
+ ElementType2[ElementType2["VScrollText"] = 18] = "VScrollText";
+ ElementType2[ElementType2["CurrentTime"] = 32] = "CurrentTime";
+})(ElementType ||= {});
+var ElementTypeToString = Object.fromEntries(Object.keys(ElementType).filter((k) => isNaN(Number(k))).map((k) => [ElementType[k], k]));
+var StringToElementType = Object.fromEntries(Object.keys(ElementType).filter((k) => isNaN(Number(k))).map((k) => [k, ElementType[k]]));
+var CUSTOM_FONT_BASE = 32768;
+function isCustomFont(fontIndex) {
+ return fontIndex >= CUSTOM_FONT_BASE;
+}
+function customFontOrdinal(fontIndex) {
+ return fontIndex - CUSTOM_FONT_BASE;
+}
+// src/helper.ts
+function packPixels(pixels, width, height) {
+ const total = width * height;
+ const packed = new Uint8Array(total + 7 >> 3);
+ for (let i = 0;i < total; i++) {
+ if (pixels[i])
+ packed[i >> 3] = (packed[i >> 3] ?? 0) | 1 << (i & 7);
+ }
+ return packed;
+}
+function unpackPixels(packed, width, height) {
+ const total = width * height;
+ const pixels = new Uint8Array(total);
+ for (let i = 0;i < total; i++) {
+ pixels[i] = (packed[i >> 3] ?? 0) >> (i & 7) & 1;
+ }
+ return pixels;
+}
+function packedSize(width, height) {
+ return width * height + 7 >> 3;
+}
+function pad32(byteCount) {
+ return 4 - (byteCount & 3) & 3;
+}
+
+class BinaryReader {
+ view;
+ pos = 0;
+ constructor(buffer) {
+ this.view = buffer instanceof ArrayBuffer ? new DataView(buffer) : new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
+ }
+ get offset() {
+ return this.pos;
+ }
+ get byteLength() {
+ return this.view.byteLength;
+ }
+ get remaining() {
+ return this.view.byteLength - this.pos;
+ }
+ readByte() {
+ this.#need(1);
+ return this.view.getUint8(this.pos++);
+ }
+ readUint16() {
+ this.#need(2);
+ const v = this.view.getUint16(this.pos, true);
+ this.pos += 2;
+ return v;
+ }
+ readShort() {
+ return this.readUint16();
+ }
+ readInt16() {
+ this.#need(2);
+ const v = this.view.getInt16(this.pos, true);
+ this.pos += 2;
+ return v;
+ }
+ readUint24() {
+ this.#need(3);
+ const v = this.view.getUint8(this.pos) | this.view.getUint8(this.pos + 1) << 8 | this.view.getUint8(this.pos + 2) << 16;
+ this.pos += 3;
+ return v;
+ }
+ readUint32() {
+ this.#need(4);
+ const v = this.view.getUint32(this.pos, true);
+ this.pos += 4;
+ return v;
+ }
+ readUint64() {
+ this.#need(8);
+ const lo = BigInt(this.view.getUint32(this.pos, true));
+ const hi = BigInt(this.view.getUint32(this.pos + 4, true));
+ this.pos += 8;
+ return hi << 32n | lo;
+ }
+ readBytes(n) {
+ this.#need(n);
+ const s = new Uint8Array(this.view.buffer, this.pos, n);
+ this.pos += n;
+ return s;
+ }
+ readUtf8(n) {
+ return new TextDecoder().decode(this.readBytes(n));
+ }
+ seek(pos) {
+ if (pos < 0 || pos > this.view.byteLength) {
+ throw new RangeError(`seek(${pos}) out of [0, ${this.view.byteLength}]`);
+ }
+ this.pos = pos;
+ }
+ #need(n) {
+ if (this.remaining < n)
+ throw new RangeError(`BinaryReader: need ${n} byte(s) at offset ${this.pos}, ${this.remaining} remain`);
+ }
+}
+// src/parser.ts
+var MONOFORMAT_MAGIC_HEADER = 1663794863;
+
+class MonoDisplayParser {
+ parse(buffer) {
+ const r = new BinaryReader(buffer);
+ const magic = r.readUint32();
+ if (magic !== MONOFORMAT_MAGIC_HEADER)
+ throw new Error(`Bad magic 0x${magic.toString(16)} - expected 0x632B7EAF`);
+ const version = r.readUint32();
+ if (version === 0 || version > 1)
+ throw new Error(`Unsupported version ${version}`);
+ const numSections = r.readUint16();
+ r.readUint16();
+ const sections = [];
+ for (let i = 0;i < numSections; i++) {
+ const sectionType = r.readByte();
+ const sectionSize = r.readUint24();
+ const sectionStart = r.offset;
+ const dataSize = sectionSize - 4;
+ const sectionEnd = sectionStart + dataSize;
+ switch (sectionType) {
+ case 1 /* ElementsAlways */:
+ sections.push(this.#parseElementsSection(r, 1 /* ElementsAlways */, false, sectionEnd));
+ break;
+ case 2 /* ElementsTimespan */:
+ sections.push(this.#parseElementsSection(r, 2 /* ElementsTimespan */, true, sectionEnd));
+ break;
+ case 32 /* CustomFont */:
+ sections.push(this.#parseCustomFont(r, dataSize));
+ break;
+ default:
+ break;
+ }
+ r.seek(sectionStart + dataSize);
+ }
+ return { sections };
+ }
+ #parseFlags(raw) {
+ return {
+ drawFront: !!(raw & 1),
+ drawBack: !!(raw & 2),
+ clearBuffer: !!(raw & 4)
+ };
+ }
+ #parseElementsSection(r, type, hasTimestamp, sectionEnd) {
+ const flags = this.#parseFlags(r.readUint16());
+ const numElements = r.readUint16();
+ let startTimestamp = 0n, endTimestamp = 0n;
+ if (hasTimestamp) {
+ startTimestamp = r.readUint64();
+ endTimestamp = r.readUint64();
+ }
+ const elements = [];
+ for (let i = 0;i < numElements; i++) {
+ if (r.offset + 2 > sectionEnd)
+ break;
+ const el = this.#parseElement(r);
+ if (el === null)
+ break;
+ elements.push(el);
+ }
+ if (hasTimestamp) {
+ return { sectionType: 2 /* ElementsTimespan */, flags, startTimestamp, endTimestamp, elements };
+ }
+ return { sectionType: 1 /* ElementsAlways */, flags, elements };
+ }
+ #parseCustomFont(r, dataSize) {
+ const actualSize = r.readUint24();
+ r.readByte();
+ const fontData = new Uint8Array(r.readBytes(Math.min(actualSize, dataSize - 4)));
+ return { sectionType: 32 /* CustomFont */, fontData };
+ }
+ #parseElement(r) {
+ const elementStart = r.offset;
+ const type = r.readUint16();
+ switch (type) {
+ case 1 /* Image2D */:
+ return this.#parseImage2D(r, elementStart);
+ case 2 /* Animation */:
+ return this.#parseAnimation(r, elementStart);
+ case 3 /* HorizontalScroll */:
+ return this.#parseHScroll(r, elementStart);
+ case 4 /* VerticalScroll */:
+ return this.#parseVScroll(r, elementStart);
+ case 5 /* Line */:
+ return this.#parseLine(r);
+ case 16 /* ClippedText */:
+ return this.#parseClippedText(r, elementStart);
+ case 17 /* HScrollText */:
+ return this.#parseHScrollText(r, elementStart);
+ case 32 /* CurrentTime */:
+ return this.#parseCurrentTime(r);
+ default:
+ return null;
+ }
+ }
+ #alignTo4(start, current) {
+ const end = start + (current - start + 3 & ~3);
+ }
+ #parseImage2D(r, start) {
+ const xOffset = r.readUint16();
+ const yOffset = r.readUint16();
+ const width = r.readUint16();
+ const height = r.readUint16();
+ r.readUint16();
+ const packed = r.readBytes(packedSize(width, height));
+ const pixels = unpackPixels(new Uint8Array(packed), width, height);
+ const dataRead = 12 + packed.byteLength;
+ r.seek(start + dataRead + pad32(dataRead));
+ return { type: 1 /* Image2D */, xOffset, yOffset, image: { pixels, width, height } };
+ }
+ #parseAnimation(r, start) {
+ const xOffset = r.readUint16();
+ const yOffset = r.readUint16();
+ const width = r.readUint16();
+ const height = r.readUint16();
+ const numFrames = r.readUint16();
+ const updateInterval = r.readUint16();
+ r.readUint16();
+ const frameBytes = packedSize(width, height);
+ const frames = [];
+ for (let f = 0;f < numFrames; f++) {
+ const packed = r.readBytes(frameBytes);
+ frames.push({ pixels: unpackPixels(new Uint8Array(packed), width, height), width, height });
+ }
+ const dataRead = 16 + numFrames * frameBytes;
+ r.seek(start + dataRead + pad32(dataRead));
+ return { type: 2 /* Animation */, xOffset, yOffset, width, height, updateInterval, frames };
+ }
+ #parseScrollFlags(raw) {
+ return {
+ endless: !!(raw & 1),
+ invertDirection: !!(raw & 2),
+ padStart: !!(raw & 4),
+ padEnd: !!(raw & 8)
+ };
+ }
+ #parseHScroll(r, start) {
+ const xOffset = r.readUint16();
+ const yOffset = r.readUint16();
+ const width = r.readUint16();
+ const height = r.readUint16();
+ const contentWidth = r.readUint16();
+ const flagsByte = r.readByte();
+ const scrollSpeed = r.readByte();
+ r.readUint16();
+ const packed = r.readBytes(packedSize(contentWidth, height));
+ const pixels = unpackPixels(new Uint8Array(packed), contentWidth, height);
+ const dataRead = 16 + packed.byteLength;
+ r.seek(start + dataRead + pad32(dataRead));
+ return {
+ type: 3 /* HorizontalScroll */,
+ xOffset,
+ yOffset,
+ width,
+ height,
+ contentWidth,
+ scrollSpeed,
+ flags: this.#parseScrollFlags(flagsByte),
+ content: { pixels, width: contentWidth, height }
+ };
+ }
+ #parseVScroll(r, start) {
+ const xOffset = r.readUint16();
+ const yOffset = r.readUint16();
+ const width = r.readUint16();
+ const height = r.readUint16();
+ const contentHeight = r.readUint16();
+ const flagsByte = r.readByte();
+ const scrollSpeed = r.readByte();
+ r.readUint16();
+ const packed = r.readBytes(packedSize(width, contentHeight));
+ const pixels = unpackPixels(new Uint8Array(packed), width, contentHeight);
+ const dataRead = 16 + packed.byteLength;
+ r.seek(start + dataRead + pad32(dataRead));
+ return {
+ type: 4 /* VerticalScroll */,
+ xOffset,
+ yOffset,
+ width,
+ height,
+ contentHeight,
+ scrollSpeed,
+ flags: this.#parseScrollFlags(flagsByte),
+ content: { pixels, width, height: contentHeight }
+ };
+ }
+ #parseLine(r) {
+ const xOrigin = r.readUint16();
+ const yOrigin = r.readUint16();
+ const xTarget = r.readUint16();
+ const yTarget = r.readUint16();
+ const lineStyle = r.readByte();
+ const flagsByte = r.readByte();
+ return { type: 5 /* Line */, xOrigin, yOrigin, xTarget, yTarget, lineStyle, invertPixels: !!(flagsByte & 1) };
+ }
+ #parseClippedText(r, start) {
+ const xOffset = r.readUint16();
+ const yOffset = r.readUint16();
+ const width = r.readUint16();
+ const height = r.readUint16();
+ const fontIndex = r.readUint16();
+ const textLen = r.readUint16();
+ const text = r.readUtf8(textLen);
+ const dataRead = 14 + textLen;
+ r.seek(start + dataRead + pad32(dataRead));
+ return { type: 16 /* ClippedText */, xOffset, yOffset, width, height, fontIndex, text };
+ }
+ #parseCurrentTime(r) {
+ const xOffset = r.readUint16();
+ const yOffset = r.readUint16();
+ const width = r.readUint16();
+ const height = r.readUint16();
+ const fontIndex = r.readUint16();
+ const utcOffsetMinutes = r.readInt16();
+ const flagsByte = r.readByte();
+ r.readByte();
+ return {
+ type: 32 /* CurrentTime */,
+ xOffset,
+ yOffset,
+ width,
+ height,
+ fontIndex,
+ utcOffsetMinutes,
+ flags: {
+ clock12h: !!(flagsByte & 1),
+ showHours: !!(flagsByte & 2),
+ showMinutes: !!(flagsByte & 4),
+ showSeconds: !!(flagsByte & 8)
+ }
+ };
+ }
+ #parseHScrollText(r, start) {
+ const xOffset = r.readUint16();
+ const yOffset = r.readUint16();
+ const width = r.readUint16();
+ const height = r.readUint16();
+ const flagsByte = r.readByte();
+ const scrollSpeed = r.readByte();
+ const fontIndex = r.readUint16();
+ const textLen = r.readUint16();
+ const text = r.readUtf8(textLen);
+ const dataRead = 16 + textLen;
+ r.seek(start + dataRead + pad32(dataRead));
+ return {
+ type: 17 /* HScrollText */,
+ xOffset,
+ yOffset,
+ width,
+ height,
+ scrollSpeed,
+ fontIndex,
+ flags: this.#parseScrollFlags(flagsByte),
+ text
+ };
+ }
+}
+// src/font/fonts/u8g2_font_5x7_mf_u8g2font.ts
+var u8g2_font_5x7_mf_u8g2font = [191, 2, 3, 2, 3, 3, 1, 1, 4, 5, 7, 0, 255, 6, 255, 6, 0, 1, 42, 2, 114, 7, 90, 32, 5, 125, 125, 30, 33, 8, 125, 173, 176, 29, 202, 1, 34, 9, 125, 157, 82, 18, 37, 57, 3, 35, 11, 125, 237, 36, 25, 148, 202, 160, 148, 1, 36, 9, 125, 237, 165, 182, 37, 201, 12, 37, 9, 125, 141, 48, 202, 154, 194, 24, 38, 10, 125, 237, 44, 201, 42, 89, 18, 3, 39, 7, 125, 173, 176, 206, 2, 40, 8, 125, 173, 44, 108, 205, 1, 41, 9, 125, 157, 52, 108, 203, 17, 0, 42, 9, 125, 237, 36, 203, 182, 74, 12, 43, 9, 125, 253, 48, 26, 164, 48, 7, 44, 7, 125, 125, 22, 173, 6, 45, 8, 125, 125, 202, 144, 147, 0, 46, 7, 125, 125, 186, 166, 3, 47, 7, 125, 125, 36, 107, 39, 48, 12, 125, 173, 44, 137, 146, 40, 137, 146, 44, 7, 49, 8, 125, 173, 76, 108, 155, 1, 50, 9, 125, 29, 169, 152, 213, 134, 24, 51, 11, 125, 141, 33, 140, 212, 36, 138, 116, 0, 52, 11, 125, 173, 76, 74, 162, 33, 11, 115, 0, 53, 11, 125, 141, 33, 9, 215, 36, 138, 116, 0, 54, 10, 125, 29, 41, 156, 42, 81, 164, 3, 55, 11, 125, 141, 33, 204, 194, 44, 204, 17, 0, 56, 10, 125, 29, 169, 36, 85, 162, 72, 7, 57, 10, 125, 29, 169, 18, 69, 99, 164, 3, 58, 8, 125, 109, 77, 71, 52, 29, 59, 9, 125, 109, 77, 71, 180, 34, 0, 60, 8, 125, 125, 36, 171, 150, 1, 61, 9, 125, 125, 108, 136, 135, 28, 4, 62, 8, 125, 237, 180, 86, 71, 0, 63, 10, 125, 173, 44, 9, 179, 28, 202, 1, 64, 11, 125, 29, 169, 146, 40, 137, 146, 234, 0, 65, 12, 125, 29, 169, 18, 37, 67, 18, 37, 81, 12, 66, 10, 125, 141, 169, 50, 85, 162, 100, 7, 67, 9, 125, 29, 169, 18, 150, 34, 29, 68, 12, 125, 141, 169, 18, 37, 81, 18, 37, 59, 0, 69, 11, 125, 141, 33, 9, 167, 48, 28, 98, 0, 70, 10, 125, 141, 33, 9, 167, 176, 14, 1, 71, 11, 125, 29, 169, 18, 38, 74, 20, 205, 0, 72, 14, 125, 141, 40, 137, 146, 33, 137, 146, 40, 137, 98, 0, 73, 8, 125, 157, 45, 108, 155, 1, 74, 9, 125, 189, 176, 37, 138, 116, 0, 75, 11, 125, 141, 40, 41, 105, 90, 18, 149, 1, 76, 8, 125, 141, 176, 227, 16, 3, 77, 14, 125, 141, 40, 25, 146, 33, 137, 146, 40, 137, 98, 0, 78, 13, 125, 141, 40, 81, 18, 165, 162, 36, 74, 20, 3, 79, 12, 125, 29, 169, 18, 37, 81, 18, 69, 58, 0, 80, 11, 125, 141, 169, 18, 37, 83, 152, 67, 0, 81, 12, 125, 29, 169, 18, 37, 81, 162, 68, 106, 2, 82, 11, 125, 141, 169, 18, 37, 83, 18, 149, 1, 83, 9, 125, 29, 169, 53, 42, 233, 0, 84, 7, 125, 157, 45, 236, 14, 85, 13, 125, 141, 40, 137, 146, 40, 137, 146, 40, 210, 1, 86, 12, 125, 141, 40, 137, 146, 40, 137, 34, 77, 7, 87, 14, 125, 141, 40, 137, 146, 40, 25, 146, 33, 137, 98, 0, 88, 12, 125, 141, 40, 137, 34, 77, 170, 68, 49, 0, 89, 10, 125, 157, 82, 18, 37, 89, 88, 7, 90, 9, 125, 141, 33, 204, 26, 135, 24, 91, 8, 125, 157, 41, 108, 156, 1, 92, 7, 125, 221, 238, 32, 0, 93, 7, 125, 157, 177, 211, 12, 94, 8, 125, 173, 44, 201, 89, 1, 95, 7, 125, 125, 198, 33, 6, 96, 7, 125, 157, 52, 103, 6, 97, 11, 125, 125, 112, 137, 146, 68, 74, 98, 0, 98, 10, 125, 141, 48, 156, 42, 81, 178, 3, 99, 8, 125, 125, 80, 10, 83, 29, 100, 11, 125, 189, 48, 90, 162, 36, 138, 102, 0, 101, 9, 125, 125, 80, 74, 140, 58, 0, 102, 11, 125, 173, 44, 137, 178, 45, 204, 17, 0, 103, 10, 125, 125, 112, 137, 34, 41, 93, 0, 104, 11, 125, 141, 48, 156, 42, 81, 18, 197, 0, 105, 9, 125, 173, 28, 17, 107, 51, 0, 106, 10, 125, 189, 28, 10, 75, 73, 22, 1, 107, 10, 125, 141, 176, 18, 105, 73, 84, 6, 108, 7, 125, 29, 177, 219, 12, 109, 11, 125, 125, 44, 137, 134, 36, 74, 162, 24, 110, 10, 125, 125, 108, 170, 68, 73, 20, 3, 111, 9, 125, 125, 80, 170, 68, 145, 14, 112, 10, 125, 125, 108, 170, 68, 201, 20, 2, 113, 11, 125, 125, 112, 137, 146, 40, 26, 19, 0, 114, 9, 125, 125, 108, 170, 132, 57, 4, 115, 8, 125, 125, 112, 81, 149, 29, 116, 9, 125, 157, 48, 219, 194, 84, 6, 117, 11, 125, 125, 44, 74, 162, 36, 138, 102, 0, 118, 11, 125, 125, 48, 137, 146, 40, 201, 114, 0, 119, 11, 125, 125, 44, 74, 162, 100, 72, 134, 24, 120, 9, 125, 125, 44, 138, 52, 169, 12, 121, 10, 125, 125, 44, 74, 162, 74, 86, 3, 122, 9, 125, 125, 108, 200, 106, 67, 12, 123, 8, 125, 189, 154, 88, 141, 1, 124, 7, 125, 173, 176, 119, 0, 125, 10, 125, 157, 52, 212, 194, 44, 71, 0, 126, 7, 125, 157, 238, 204, 0, 160, 5, 125, 125, 30, 161, 8, 125, 173, 28, 10, 219, 1, 162, 10, 125, 253, 108, 41, 37, 217, 22, 1, 163, 9, 125, 125, 41, 219, 42, 50, 0, 164, 11, 125, 221, 44, 153, 146, 104, 201, 82, 0, 165, 10, 125, 157, 82, 146, 101, 91, 152, 3, 166, 8, 125, 253, 48, 135, 194, 28, 167, 11, 125, 45, 41, 212, 146, 76, 140, 36, 0, 168, 7, 125, 157, 58, 55, 0, 169, 12, 125, 157, 37, 83, 146, 73, 73, 180, 100, 1, 170, 9, 125, 29, 41, 201, 116, 22, 0, 171, 9, 125, 125, 48, 146, 218, 49, 0, 172, 9, 125, 125, 202, 16, 230, 32, 0, 173, 7, 125, 125, 210, 78, 2, 174, 11, 125, 157, 37, 27, 146, 105, 210, 146, 5, 175, 7, 125, 141, 33, 231, 6, 176, 9, 125, 173, 44, 201, 114, 22, 0, 177, 11, 125, 173, 48, 26, 164, 48, 26, 84, 0, 178, 9, 125, 29, 49, 11, 117, 26, 0, 179, 9, 125, 29, 77, 204, 116, 26, 0, 180, 7, 125, 173, 44, 231, 0, 181, 11, 125, 125, 44, 74, 162, 36, 74, 166, 16, 182, 13, 125, 157, 69, 73, 148, 40, 137, 146, 40, 137, 1, 183, 8, 125, 125, 80, 211, 105, 0, 184, 7, 125, 125, 230, 44, 3, 185, 9, 125, 173, 76, 204, 118, 18, 0, 186, 9, 125, 157, 44, 201, 114, 38, 0, 187, 9, 125, 125, 44, 42, 73, 57, 8, 188, 9, 125, 141, 176, 41, 147, 198, 4, 189, 9, 125, 141, 176, 69, 204, 66, 5, 190, 11, 125, 13, 77, 204, 148, 76, 26, 19, 0, 191, 10, 125, 173, 28, 202, 194, 36, 203, 1, 192, 12, 125, 29, 169, 18, 37, 67, 18, 37, 81, 12, 193, 12, 125, 29, 169, 18, 37, 67, 18, 37, 81, 12, 194, 12, 125, 29, 169, 18, 37, 67, 18, 37, 81, 12, 195, 12, 125, 29, 169, 18, 37, 67, 18, 37, 81, 12, 196, 12, 125, 141, 40, 146, 42, 67, 18, 37, 81, 12, 197, 12, 125, 29, 77, 170, 12, 73, 148, 68, 49, 0, 198, 12, 125, 157, 165, 148, 40, 83, 18, 37, 50, 0, 199, 10, 125, 29, 169, 18, 150, 34, 45, 3, 200, 11, 125, 141, 33, 9, 167, 48, 28, 98, 0, 201, 11, 125, 141, 33, 9, 167, 48, 28, 98, 0, 202, 11, 125, 141, 33, 9, 167, 48, 28, 98, 0, 203, 11, 125, 141, 33, 9, 167, 48, 28, 98, 0, 204, 8, 125, 157, 45, 108, 155, 1, 205, 8, 125, 157, 45, 108, 155, 1, 206, 8, 125, 157, 45, 108, 155, 1, 207, 8, 125, 157, 45, 108, 155, 1, 208, 12, 125, 141, 45, 73, 148, 40, 137, 146, 100, 7, 209, 13, 125, 141, 68, 137, 18, 165, 162, 36, 74, 20, 3, 210, 12, 125, 29, 169, 18, 37, 81, 18, 69, 58, 0, 211, 12, 125, 29, 169, 18, 37, 81, 18, 69, 58, 0, 212, 12, 125, 29, 169, 18, 37, 81, 18, 69, 58, 0, 213, 12, 125, 29, 169, 18, 37, 81, 18, 69, 58, 0, 214, 12, 125, 141, 40, 146, 42, 81, 18, 69, 58, 0, 215, 9, 125, 125, 44, 138, 52, 169, 12, 216, 12, 125, 157, 37, 81, 18, 75, 162, 36, 59, 0, 217, 13, 125, 141, 40, 137, 146, 40, 137, 146, 40, 210, 1, 218, 13, 125, 141, 40, 137, 146, 40, 137, 146, 40, 210, 1, 219, 13, 125, 141, 40, 137, 146, 40, 137, 146, 40, 210, 1, 220, 11, 125, 141, 40, 142, 146, 40, 137, 34, 29, 221, 10, 125, 157, 82, 18, 37, 89, 88, 7, 222, 10, 125, 141, 112, 170, 76, 97, 14, 1, 223, 10, 125, 29, 169, 82, 170, 68, 73, 29, 224, 11, 125, 157, 52, 91, 162, 36, 145, 146, 24, 225, 11, 125, 173, 44, 92, 162, 36, 145, 146, 24, 226, 12, 125, 173, 44, 137, 150, 40, 73, 164, 36, 6, 227, 11, 125, 157, 110, 75, 148, 36, 82, 18, 3, 228, 11, 125, 157, 250, 18, 37, 137, 148, 196, 0, 229, 11, 125, 29, 77, 91, 162, 36, 145, 146, 24, 230, 9, 125, 125, 112, 73, 148, 218, 12, 231, 9, 125, 125, 84, 10, 83, 45, 2, 232, 9, 125, 157, 52, 147, 18, 163, 14, 233, 9, 125, 173, 44, 148, 18, 163, 14, 234, 10, 125, 157, 44, 201, 164, 196, 168, 3, 235, 10, 125, 141, 36, 71, 164, 196, 168, 3, 236, 9, 125, 157, 52, 19, 107, 51, 0, 237, 9, 125, 173, 44, 20, 107, 51, 0, 238, 9, 125, 173, 44, 137, 196, 218, 12, 239, 8, 125, 157, 186, 88, 155, 1, 240, 10, 125, 157, 84, 146, 42, 81, 164, 3, 241, 10, 125, 157, 78, 83, 37, 74, 162, 24, 242, 10, 125, 157, 52, 147, 42, 81, 164, 3, 243, 10, 125, 173, 44, 148, 42, 81, 164, 3, 244, 10, 125, 29, 29, 145, 42, 81, 164, 3, 245, 10, 125, 157, 110, 82, 37, 138, 116, 0, 246, 9, 125, 157, 186, 84, 137, 34, 29, 247, 8, 125, 109, 125, 200, 117, 0, 248, 9, 125, 125, 112, 73, 44, 201, 14, 249, 11, 125, 157, 52, 170, 68, 73, 20, 205, 0, 250, 11, 125, 173, 172, 148, 68, 73, 20, 205, 0, 251, 11, 125, 29, 61, 74, 162, 36, 138, 102, 0, 252, 11, 125, 157, 114, 148, 68, 73, 20, 205, 0, 253, 10, 125, 173, 172, 148, 68, 149, 172, 6, 254, 10, 125, 221, 112, 170, 68, 201, 20, 2, 255, 10, 125, 157, 114, 148, 68, 149, 172, 6, 0, 0, 0, 4, 255, 255, 0, 0];
+
+// src/font/fonts/u8g2_font_5x7_tf.u8g2font.ts
+var u8g2_font_5x7_tf_u8g2font = [
+ 191,
+ 0,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 4,
+ 4,
+ 5,
+ 7,
+ 0,
+ 255,
+ 6,
+ 255,
+ 6,
+ 0,
+ 1,
+ 10,
+ 2,
+ 22,
+ 6,
+ 47,
+ 32,
+ 5,
+ 0,
+ 189,
+ 1,
+ 33,
+ 6,
+ 177,
+ 177,
+ 25,
+ 41,
+ 34,
+ 7,
+ 91,
+ 183,
+ 73,
+ 86,
+ 0,
+ 35,
+ 10,
+ 45,
+ 177,
+ 171,
+ 134,
+ 170,
+ 134,
+ 170,
+ 0,
+ 36,
+ 9,
+ 45,
+ 177,
+ 91,
+ 245,
+ 78,
+ 105,
+ 1,
+ 37,
+ 8,
+ 52,
+ 177,
+ 201,
+ 177,
+ 119,
+ 0,
+ 38,
+ 9,
+ 44,
+ 177,
+ 139,
+ 41,
+ 86,
+ 49,
+ 5,
+ 39,
+ 5,
+ 153,
+ 183,
+ 25,
+ 40,
+ 7,
+ 114,
+ 177,
+ 83,
+ 205,
+ 0,
+ 41,
+ 8,
+ 114,
+ 177,
+ 137,
+ 169,
+ 82,
+ 0,
+ 42,
+ 7,
+ 107,
+ 177,
+ 73,
+ 213,
+ 106,
+ 43,
+ 10,
+ 45,
+ 177,
+ 205,
+ 40,
+ 14,
+ 153,
+ 81,
+ 4,
+ 44,
+ 7,
+ 91,
+ 175,
+ 83,
+ 37,
+ 0,
+ 45,
+ 6,
+ 12,
+ 181,
+ 25,
+ 1,
+ 46,
+ 6,
+ 82,
+ 177,
+ 25,
+ 1,
+ 47,
+ 7,
+ 36,
+ 179,
+ 143,
+ 109,
+ 0,
+ 48,
+ 8,
+ 115,
+ 177,
+ 171,
+ 92,
+ 21,
+ 0,
+ 49,
+ 7,
+ 115,
+ 177,
+ 75,
+ 178,
+ 53,
+ 50,
+ 9,
+ 52,
+ 177,
+ 83,
+ 49,
+ 199,
+ 114,
+ 4,
+ 51,
+ 10,
+ 52,
+ 177,
+ 25,
+ 57,
+ 105,
+ 36,
+ 147,
+ 2,
+ 52,
+ 10,
+ 52,
+ 177,
+ 141,
+ 170,
+ 26,
+ 49,
+ 39,
+ 0,
+ 53,
+ 10,
+ 52,
+ 177,
+ 25,
+ 122,
+ 35,
+ 153,
+ 20,
+ 0,
+ 54,
+ 10,
+ 52,
+ 177,
+ 83,
+ 121,
+ 69,
+ 153,
+ 20,
+ 0,
+ 55,
+ 10,
+ 52,
+ 177,
+ 25,
+ 57,
+ 230,
+ 152,
+ 35,
+ 0,
+ 56,
+ 10,
+ 52,
+ 177,
+ 83,
+ 49,
+ 169,
+ 40,
+ 147,
+ 2,
+ 57,
+ 10,
+ 52,
+ 177,
+ 83,
+ 81,
+ 166,
+ 157,
+ 20,
+ 0,
+ 58,
+ 7,
+ 106,
+ 177,
+ 25,
+ 113,
+ 4,
+ 59,
+ 8,
+ 51,
+ 175,
+ 179,
+ 145,
+ 42,
+ 1,
+ 60,
+ 7,
+ 107,
+ 177,
+ 77,
+ 117,
+ 1,
+ 61,
+ 8,
+ 28,
+ 179,
+ 25,
+ 25,
+ 141,
+ 0,
+ 62,
+ 7,
+ 107,
+ 177,
+ 201,
+ 85,
+ 9,
+ 63,
+ 9,
+ 115,
+ 177,
+ 107,
+ 166,
+ 12,
+ 19,
+ 0,
+ 64,
+ 9,
+ 52,
+ 177,
+ 83,
+ 81,
+ 221,
+ 72,
+ 1,
+ 65,
+ 9,
+ 52,
+ 177,
+ 83,
+ 81,
+ 142,
+ 41,
+ 3,
+ 66,
+ 10,
+ 52,
+ 177,
+ 89,
+ 113,
+ 164,
+ 40,
+ 71,
+ 2,
+ 67,
+ 9,
+ 52,
+ 177,
+ 83,
+ 81,
+ 151,
+ 73,
+ 1,
+ 68,
+ 9,
+ 52,
+ 177,
+ 89,
+ 209,
+ 57,
+ 18,
+ 0,
+ 69,
+ 9,
+ 52,
+ 177,
+ 25,
+ 122,
+ 229,
+ 60,
+ 2,
+ 70,
+ 8,
+ 52,
+ 177,
+ 25,
+ 122,
+ 229,
+ 26,
+ 71,
+ 9,
+ 52,
+ 177,
+ 83,
+ 81,
+ 167,
+ 153,
+ 6,
+ 72,
+ 8,
+ 52,
+ 177,
+ 137,
+ 114,
+ 76,
+ 51,
+ 73,
+ 7,
+ 115,
+ 177,
+ 89,
+ 177,
+ 53,
+ 74,
+ 8,
+ 52,
+ 177,
+ 111,
+ 203,
+ 164,
+ 0,
+ 75,
+ 10,
+ 52,
+ 177,
+ 137,
+ 42,
+ 73,
+ 153,
+ 202,
+ 0,
+ 76,
+ 7,
+ 52,
+ 177,
+ 201,
+ 221,
+ 35,
+ 77,
+ 9,
+ 52,
+ 177,
+ 137,
+ 227,
+ 136,
+ 102,
+ 0,
+ 78,
+ 8,
+ 52,
+ 177,
+ 137,
+ 107,
+ 169,
+ 51,
+ 79,
+ 9,
+ 52,
+ 177,
+ 83,
+ 209,
+ 153,
+ 20,
+ 0,
+ 80,
+ 10,
+ 52,
+ 177,
+ 89,
+ 81,
+ 142,
+ 148,
+ 51,
+ 0,
+ 81,
+ 10,
+ 60,
+ 175,
+ 83,
+ 209,
+ 92,
+ 73,
+ 163,
+ 0,
+ 82,
+ 9,
+ 52,
+ 177,
+ 89,
+ 81,
+ 142,
+ 212,
+ 12,
+ 83,
+ 10,
+ 52,
+ 177,
+ 83,
+ 49,
+ 101,
+ 84,
+ 38,
+ 5,
+ 84,
+ 7,
+ 115,
+ 177,
+ 89,
+ 177,
+ 11,
+ 85,
+ 8,
+ 52,
+ 177,
+ 137,
+ 158,
+ 73,
+ 1,
+ 86,
+ 9,
+ 52,
+ 177,
+ 137,
+ 206,
+ 36,
+ 21,
+ 0,
+ 87,
+ 9,
+ 52,
+ 177,
+ 137,
+ 230,
+ 56,
+ 98,
+ 0,
+ 88,
+ 10,
+ 52,
+ 177,
+ 137,
+ 50,
+ 73,
+ 21,
+ 101,
+ 0,
+ 89,
+ 8,
+ 115,
+ 177,
+ 73,
+ 86,
+ 89,
+ 1,
+ 90,
+ 9,
+ 52,
+ 177,
+ 25,
+ 57,
+ 182,
+ 71,
+ 0,
+ 91,
+ 7,
+ 115,
+ 177,
+ 25,
+ 177,
+ 57,
+ 92,
+ 9,
+ 36,
+ 179,
+ 201,
+ 40,
+ 163,
+ 140,
+ 2,
+ 93,
+ 7,
+ 115,
+ 177,
+ 153,
+ 205,
+ 17,
+ 94,
+ 5,
+ 83,
+ 185,
+ 107,
+ 95,
+ 6,
+ 12,
+ 177,
+ 25,
+ 1,
+ 96,
+ 6,
+ 82,
+ 185,
+ 137,
+ 1,
+ 97,
+ 8,
+ 36,
+ 177,
+ 27,
+ 81,
+ 169,
+ 2,
+ 98,
+ 10,
+ 52,
+ 177,
+ 201,
+ 121,
+ 69,
+ 57,
+ 18,
+ 0,
+ 99,
+ 6,
+ 35,
+ 177,
+ 155,
+ 89,
+ 100,
+ 8,
+ 52,
+ 177,
+ 175,
+ 70,
+ 148,
+ 105,
+ 101,
+ 8,
+ 36,
+ 177,
+ 83,
+ 105,
+ 100,
+ 5,
+ 102,
+ 9,
+ 52,
+ 177,
+ 173,
+ 202,
+ 153,
+ 35,
+ 0,
+ 103,
+ 9,
+ 44,
+ 175,
+ 27,
+ 49,
+ 169,
+ 140,
+ 6,
+ 104,
+ 8,
+ 52,
+ 177,
+ 201,
+ 121,
+ 69,
+ 51,
+ 105,
+ 8,
+ 115,
+ 177,
+ 203,
+ 72,
+ 86,
+ 3,
+ 106,
+ 9,
+ 123,
+ 175,
+ 205,
+ 176,
+ 84,
+ 21,
+ 0,
+ 107,
+ 9,
+ 52,
+ 177,
+ 201,
+ 85,
+ 146,
+ 169,
+ 12,
+ 108,
+ 7,
+ 115,
+ 177,
+ 145,
+ 93,
+ 3,
+ 109,
+ 8,
+ 36,
+ 177,
+ 73,
+ 105,
+ 72,
+ 25,
+ 110,
+ 7,
+ 36,
+ 177,
+ 89,
+ 209,
+ 12,
+ 111,
+ 8,
+ 36,
+ 177,
+ 83,
+ 81,
+ 38,
+ 5,
+ 112,
+ 9,
+ 44,
+ 175,
+ 89,
+ 81,
+ 142,
+ 148,
+ 1,
+ 113,
+ 8,
+ 44,
+ 175,
+ 27,
+ 81,
+ 166,
+ 29,
+ 114,
+ 8,
+ 36,
+ 177,
+ 89,
+ 81,
+ 103,
+ 0,
+ 115,
+ 8,
+ 36,
+ 177,
+ 27,
+ 26,
+ 13,
+ 5,
+ 116,
+ 9,
+ 52,
+ 177,
+ 203,
+ 113,
+ 230,
+ 140,
+ 4,
+ 117,
+ 7,
+ 36,
+ 177,
+ 137,
+ 102,
+ 26,
+ 118,
+ 7,
+ 99,
+ 177,
+ 73,
+ 86,
+ 5,
+ 119,
+ 7,
+ 36,
+ 177,
+ 137,
+ 114,
+ 28,
+ 120,
+ 8,
+ 36,
+ 177,
+ 137,
+ 73,
+ 170,
+ 24,
+ 121,
+ 9,
+ 44,
+ 175,
+ 137,
+ 50,
+ 149,
+ 37,
+ 0,
+ 122,
+ 8,
+ 36,
+ 177,
+ 25,
+ 177,
+ 28,
+ 1,
+ 123,
+ 8,
+ 115,
+ 177,
+ 77,
+ 73,
+ 214,
+ 1,
+ 124,
+ 5,
+ 177,
+ 177,
+ 57,
+ 125,
+ 9,
+ 115,
+ 177,
+ 201,
+ 81,
+ 197,
+ 20,
+ 1,
+ 126,
+ 7,
+ 20,
+ 185,
+ 75,
+ 42,
+ 1,
+ 160,
+ 5,
+ 0,
+ 189,
+ 1,
+ 161,
+ 6,
+ 177,
+ 177,
+ 73,
+ 35,
+ 162,
+ 9,
+ 52,
+ 175,
+ 141,
+ 35,
+ 53,
+ 103,
+ 2,
+ 163,
+ 8,
+ 44,
+ 177,
+ 85,
+ 113,
+ 86,
+ 2,
+ 164,
+ 10,
+ 45,
+ 177,
+ 201,
+ 105,
+ 166,
+ 184,
+ 114,
+ 0,
+ 165,
+ 9,
+ 115,
+ 177,
+ 73,
+ 170,
+ 90,
+ 49,
+ 1,
+ 166,
+ 6,
+ 169,
+ 177,
+ 81,
+ 2,
+ 167,
+ 8,
+ 123,
+ 175,
+ 155,
+ 170,
+ 146,
+ 11,
+ 168,
+ 6,
+ 75,
+ 187,
+ 73,
+ 1,
+ 169,
+ 11,
+ 61,
+ 175,
+ 91,
+ 89,
+ 165,
+ 169,
+ 146,
+ 78,
+ 11,
+ 170,
+ 6,
+ 27,
+ 183,
+ 91,
+ 73,
+ 171,
+ 7,
+ 29,
+ 179,
+ 139,
+ 178,
+ 1,
+ 172,
+ 6,
+ 20,
+ 179,
+ 25,
+ 57,
+ 173,
+ 5,
+ 75,
+ 181,
+ 25,
+ 174,
+ 11,
+ 61,
+ 175,
+ 91,
+ 121,
+ 164,
+ 57,
+ 117,
+ 90,
+ 0,
+ 175,
+ 6,
+ 12,
+ 187,
+ 25,
+ 1,
+ 176,
+ 6,
+ 91,
+ 183,
+ 235,
+ 2,
+ 177,
+ 11,
+ 53,
+ 177,
+ 205,
+ 40,
+ 14,
+ 153,
+ 81,
+ 28,
+ 2,
+ 178,
+ 6,
+ 98,
+ 181,
+ 81,
+ 6,
+ 179,
+ 6,
+ 98,
+ 181,
+ 25,
+ 105,
+ 180,
+ 6,
+ 82,
+ 185,
+ 83,
+ 0,
+ 181,
+ 8,
+ 44,
+ 175,
+ 137,
+ 230,
+ 72,
+ 25,
+ 182,
+ 8,
+ 52,
+ 177,
+ 27,
+ 106,
+ 245,
+ 3,
+ 183,
+ 6,
+ 82,
+ 181,
+ 25,
+ 1,
+ 184,
+ 6,
+ 82,
+ 175,
+ 83,
+ 0,
+ 185,
+ 7,
+ 99,
+ 181,
+ 75,
+ 50,
+ 13,
+ 186,
+ 6,
+ 27,
+ 183,
+ 235,
+ 2,
+ 187,
+ 8,
+ 29,
+ 179,
+ 137,
+ 165,
+ 76,
+ 0,
+ 188,
+ 9,
+ 60,
+ 175,
+ 201,
+ 205,
+ 168,
+ 118,
+ 0,
+ 189,
+ 9,
+ 60,
+ 175,
+ 201,
+ 45,
+ 29,
+ 179,
+ 0,
+ 190,
+ 10,
+ 60,
+ 175,
+ 145,
+ 58,
+ 170,
+ 168,
+ 118,
+ 0,
+ 191,
+ 9,
+ 115,
+ 177,
+ 203,
+ 48,
+ 197,
+ 84,
+ 1,
+ 192,
+ 9,
+ 52,
+ 177,
+ 83,
+ 81,
+ 142,
+ 41,
+ 3,
+ 193,
+ 9,
+ 52,
+ 177,
+ 83,
+ 81,
+ 142,
+ 41,
+ 3,
+ 194,
+ 9,
+ 52,
+ 177,
+ 83,
+ 81,
+ 142,
+ 41,
+ 3,
+ 195,
+ 9,
+ 52,
+ 177,
+ 83,
+ 81,
+ 142,
+ 41,
+ 3,
+ 196,
+ 10,
+ 52,
+ 177,
+ 137,
+ 73,
+ 197,
+ 49,
+ 101,
+ 0,
+ 197,
+ 9,
+ 52,
+ 177,
+ 147,
+ 42,
+ 142,
+ 41,
+ 3,
+ 198,
+ 9,
+ 52,
+ 177,
+ 27,
+ 169,
+ 26,
+ 170,
+ 37,
+ 199,
+ 10,
+ 60,
+ 175,
+ 83,
+ 81,
+ 151,
+ 73,
+ 70,
+ 0,
+ 200,
+ 9,
+ 52,
+ 177,
+ 25,
+ 122,
+ 229,
+ 60,
+ 2,
+ 201,
+ 9,
+ 52,
+ 177,
+ 25,
+ 122,
+ 229,
+ 60,
+ 2,
+ 202,
+ 9,
+ 52,
+ 177,
+ 25,
+ 122,
+ 229,
+ 60,
+ 2,
+ 203,
+ 9,
+ 52,
+ 177,
+ 25,
+ 122,
+ 229,
+ 60,
+ 2,
+ 204,
+ 7,
+ 115,
+ 177,
+ 89,
+ 177,
+ 53,
+ 205,
+ 7,
+ 115,
+ 177,
+ 89,
+ 177,
+ 53,
+ 206,
+ 7,
+ 115,
+ 177,
+ 89,
+ 177,
+ 53,
+ 207,
+ 7,
+ 115,
+ 177,
+ 89,
+ 177,
+ 53,
+ 208,
+ 9,
+ 52,
+ 177,
+ 153,
+ 105,
+ 117,
+ 141,
+ 4,
+ 209,
+ 8,
+ 52,
+ 177,
+ 73,
+ 115,
+ 169,
+ 51,
+ 210,
+ 9,
+ 52,
+ 177,
+ 83,
+ 209,
+ 153,
+ 20,
+ 0,
+ 211,
+ 9,
+ 52,
+ 177,
+ 83,
+ 209,
+ 153,
+ 20,
+ 0,
+ 212,
+ 9,
+ 52,
+ 177,
+ 83,
+ 209,
+ 153,
+ 20,
+ 0,
+ 213,
+ 9,
+ 52,
+ 177,
+ 83,
+ 209,
+ 153,
+ 20,
+ 0,
+ 214,
+ 10,
+ 52,
+ 177,
+ 137,
+ 73,
+ 69,
+ 51,
+ 41,
+ 0,
+ 215,
+ 8,
+ 36,
+ 177,
+ 137,
+ 73,
+ 170,
+ 24,
+ 216,
+ 9,
+ 52,
+ 177,
+ 27,
+ 233,
+ 72,
+ 71,
+ 2,
+ 217,
+ 8,
+ 52,
+ 177,
+ 137,
+ 158,
+ 73,
+ 1,
+ 218,
+ 8,
+ 52,
+ 177,
+ 137,
+ 158,
+ 73,
+ 1,
+ 219,
+ 8,
+ 52,
+ 177,
+ 137,
+ 158,
+ 73,
+ 1,
+ 220,
+ 10,
+ 52,
+ 177,
+ 137,
+ 25,
+ 69,
+ 51,
+ 41,
+ 0,
+ 221,
+ 8,
+ 115,
+ 177,
+ 73,
+ 86,
+ 89,
+ 1,
+ 222,
+ 10,
+ 52,
+ 177,
+ 201,
+ 43,
+ 142,
+ 148,
+ 51,
+ 0,
+ 223,
+ 9,
+ 52,
+ 177,
+ 83,
+ 81,
+ 149,
+ 86,
+ 2,
+ 224,
+ 10,
+ 52,
+ 177,
+ 203,
+ 40,
+ 142,
+ 168,
+ 84,
+ 1,
+ 225,
+ 9,
+ 52,
+ 177,
+ 173,
+ 71,
+ 84,
+ 170,
+ 0,
+ 226,
+ 9,
+ 52,
+ 177,
+ 173,
+ 210,
+ 136,
+ 74,
+ 21,
+ 227,
+ 10,
+ 52,
+ 177,
+ 75,
+ 42,
+ 142,
+ 168,
+ 84,
+ 1,
+ 228,
+ 9,
+ 52,
+ 177,
+ 171,
+ 225,
+ 136,
+ 74,
+ 21,
+ 229,
+ 9,
+ 52,
+ 177,
+ 147,
+ 114,
+ 68,
+ 165,
+ 10,
+ 230,
+ 8,
+ 36,
+ 177,
+ 27,
+ 105,
+ 197,
+ 1,
+ 231,
+ 8,
+ 107,
+ 175,
+ 155,
+ 89,
+ 37,
+ 0,
+ 232,
+ 10,
+ 52,
+ 177,
+ 203,
+ 40,
+ 170,
+ 52,
+ 178,
+ 2,
+ 233,
+ 9,
+ 52,
+ 177,
+ 173,
+ 85,
+ 26,
+ 89,
+ 1,
+ 234,
+ 10,
+ 52,
+ 177,
+ 139,
+ 41,
+ 170,
+ 52,
+ 178,
+ 2,
+ 235,
+ 10,
+ 52,
+ 177,
+ 73,
+ 25,
+ 171,
+ 52,
+ 178,
+ 2,
+ 236,
+ 8,
+ 115,
+ 177,
+ 201,
+ 73,
+ 86,
+ 3,
+ 237,
+ 7,
+ 115,
+ 177,
+ 43,
+ 101,
+ 53,
+ 238,
+ 7,
+ 115,
+ 177,
+ 171,
+ 102,
+ 53,
+ 239,
+ 8,
+ 115,
+ 177,
+ 73,
+ 89,
+ 86,
+ 3,
+ 240,
+ 10,
+ 52,
+ 177,
+ 203,
+ 200,
+ 138,
+ 50,
+ 41,
+ 0,
+ 241,
+ 9,
+ 52,
+ 177,
+ 75,
+ 42,
+ 173,
+ 104,
+ 6,
+ 242,
+ 10,
+ 52,
+ 177,
+ 203,
+ 40,
+ 170,
+ 40,
+ 147,
+ 2,
+ 243,
+ 9,
+ 52,
+ 177,
+ 173,
+ 85,
+ 148,
+ 73,
+ 1,
+ 244,
+ 10,
+ 52,
+ 177,
+ 211,
+ 88,
+ 69,
+ 153,
+ 20,
+ 0,
+ 245,
+ 10,
+ 52,
+ 177,
+ 75,
+ 42,
+ 170,
+ 40,
+ 147,
+ 2,
+ 246,
+ 10,
+ 52,
+ 177,
+ 171,
+ 161,
+ 138,
+ 50,
+ 41,
+ 0,
+ 247,
+ 9,
+ 44,
+ 177,
+ 211,
+ 112,
+ 100,
+ 168,
+ 0,
+ 248,
+ 9,
+ 36,
+ 177,
+ 27,
+ 105,
+ 164,
+ 145,
+ 0,
+ 249,
+ 9,
+ 52,
+ 177,
+ 203,
+ 168,
+ 52,
+ 211,
+ 0,
+ 250,
+ 7,
+ 52,
+ 177,
+ 109,
+ 154,
+ 105,
+ 251,
+ 8,
+ 52,
+ 177,
+ 211,
+ 48,
+ 154,
+ 105,
+ 252,
+ 9,
+ 52,
+ 177,
+ 171,
+ 81,
+ 52,
+ 211,
+ 0,
+ 253,
+ 9,
+ 60,
+ 175,
+ 109,
+ 202,
+ 84,
+ 150,
+ 0,
+ 254,
+ 10,
+ 52,
+ 175,
+ 201,
+ 43,
+ 202,
+ 145,
+ 50,
+ 0,
+ 255,
+ 10,
+ 60,
+ 175,
+ 171,
+ 81,
+ 148,
+ 169,
+ 44,
+ 1,
+ 0,
+ 0,
+ 0,
+ 4,
+ 255,
+ 255,
+ 0,
+ 0
+];
+
+// src/font/fonts/u8g2_font_HelvetiPixel_tr_u8g2font.ts
+var u8g2_font_HelvetiPixel_tr_u8g2font = [95, 0, 3, 2, 4, 4, 2, 4, 5, 9, 12, 0, 253, 8, 254, 8, 254, 1, 83, 2, 187, 3, 231, 32, 5, 0, 98, 5, 33, 6, 129, 227, 196, 37, 34, 7, 51, 54, 69, 98, 9, 35, 16, 134, 226, 85, 146, 37, 201, 176, 212, 146, 104, 88, 106, 73, 4, 36, 14, 149, 162, 85, 182, 84, 148, 108, 75, 148, 202, 22, 1, 37, 18, 136, 163, 78, 22, 37, 81, 165, 24, 197, 81, 152, 148, 42, 81, 150, 0, 38, 15, 135, 34, 150, 24, 101, 81, 40, 38, 145, 166, 100, 210, 20, 39, 6, 49, 182, 196, 0, 40, 11, 163, 26, 85, 18, 37, 81, 91, 148, 5, 41, 12, 163, 26, 69, 22, 101, 81, 75, 148, 68, 0, 42, 8, 53, 178, 69, 82, 89, 26, 43, 10, 85, 166, 85, 24, 13, 82, 24, 1, 44, 7, 50, 218, 76, 162, 0, 45, 6, 19, 42, 197, 0, 46, 6, 17, 227, 68, 0, 47, 10, 147, 30, 85, 75, 84, 137, 74, 0, 48, 10, 133, 162, 205, 146, 249, 150, 44, 0, 49, 8, 131, 99, 85, 178, 68, 61, 50, 10, 133, 162, 205, 146, 133, 89, 199, 65, 51, 12, 133, 162, 205, 146, 133, 145, 26, 106, 201, 2, 52, 14, 134, 226, 101, 168, 37, 81, 37, 75, 134, 49, 77, 0, 53, 12, 133, 162, 197, 49, 28, 210, 80, 75, 22, 0, 54, 12, 133, 162, 205, 146, 137, 67, 146, 217, 146, 5, 55, 12, 133, 162, 197, 32, 102, 97, 22, 102, 97, 6, 56, 12, 133, 162, 205, 146, 105, 201, 146, 217, 146, 5, 57, 12, 133, 162, 205, 146, 217, 146, 33, 172, 44, 0, 58, 6, 81, 227, 68, 22, 59, 8, 114, 218, 76, 158, 40, 0, 60, 7, 86, 230, 165, 100, 54, 61, 8, 53, 170, 197, 160, 14, 2, 62, 8, 86, 230, 133, 108, 50, 2, 63, 11, 133, 162, 205, 146, 105, 141, 57, 20, 1, 64, 20, 136, 159, 214, 144, 133, 73, 180, 40, 81, 162, 68, 137, 52, 68, 105, 52, 40, 0, 65, 14, 135, 34, 94, 156, 38, 97, 146, 149, 6, 37, 85, 3, 66, 13, 133, 227, 197, 144, 100, 218, 160, 100, 182, 65, 1, 67, 13, 135, 34, 214, 16, 133, 114, 123, 24, 13, 9, 0, 68, 13, 134, 35, 198, 16, 101, 73, 232, 45, 25, 34, 0, 69, 11, 133, 227, 197, 49, 28, 146, 176, 56, 8, 70, 10, 133, 227, 197, 49, 28, 146, 176, 17, 71, 15, 135, 34, 214, 16, 133, 114, 109, 72, 147, 48, 26, 18, 0, 72, 10, 134, 35, 70, 104, 28, 6, 209, 49, 73, 6, 129, 227, 196, 65, 74, 9, 132, 98, 93, 111, 82, 162, 0, 75, 15, 134, 35, 70, 150, 68, 149, 76, 76, 178, 168, 150, 132, 1, 76, 8, 133, 227, 69, 216, 199, 65, 77, 16, 135, 99, 70, 186, 13, 217, 82, 81, 42, 82, 36, 69, 106, 0, 78, 12, 133, 227, 69, 54, 77, 74, 162, 36, 210, 45, 79, 12, 135, 34, 214, 86, 73, 93, 147, 44, 155, 0, 80, 12, 133, 227, 197, 144, 100, 182, 65, 9, 139, 0, 81, 12, 135, 34, 214, 86, 73, 93, 147, 72, 27, 4, 82, 13, 134, 35, 198, 160, 132, 198, 97, 137, 106, 73, 24, 83, 12, 133, 227, 205, 146, 169, 106, 170, 37, 11, 0, 84, 9, 133, 162, 197, 32, 133, 125, 2, 85, 10, 134, 35, 70, 232, 199, 100, 72, 0, 86, 15, 135, 34, 70, 170, 38, 89, 148, 85, 194, 36, 141, 51, 0, 87, 17, 137, 162, 70, 150, 105, 153, 86, 233, 148, 52, 37, 109, 197, 44, 2, 88, 14, 133, 227, 69, 150, 148, 146, 44, 204, 146, 40, 169, 5, 89, 12, 133, 162, 69, 166, 37, 165, 36, 11, 155, 0, 90, 9, 134, 226, 197, 53, 236, 117, 24, 91, 8, 162, 27, 197, 210, 47, 2, 92, 10, 147, 30, 69, 84, 139, 106, 81, 1, 93, 8, 162, 218, 132, 210, 47, 3, 94, 10, 85, 174, 85, 150, 68, 73, 77, 11, 95, 6, 22, 218, 197, 1, 96, 6, 34, 250, 68, 20, 97, 11, 85, 162, 205, 146, 37, 131, 150, 12, 1, 98, 12, 133, 227, 69, 88, 28, 146, 204, 54, 40, 0, 99, 10, 85, 162, 205, 146, 137, 89, 178, 0, 100, 10, 133, 162, 101, 203, 160, 217, 146, 33, 101, 10, 85, 162, 205, 146, 13, 67, 58, 4, 102, 10, 131, 34, 85, 18, 37, 75, 212, 2, 103, 12, 117, 154, 205, 160, 217, 146, 33, 76, 22, 0, 104, 10, 133, 227, 69, 88, 28, 146, 204, 45, 105, 7, 113, 227, 68, 50, 8, 106, 9, 146, 218, 76, 150, 116, 81, 0, 107, 11, 132, 163, 69, 214, 164, 36, 82, 82, 10, 108, 6, 129, 227, 196, 65, 109, 12, 87, 99, 198, 162, 68, 145, 20, 73, 145, 84, 110, 9, 85, 227, 197, 144, 100, 110, 1, 111, 9, 85, 162, 205, 146, 217, 146, 5, 112, 12, 117, 219, 197, 144, 100, 182, 65, 9, 67, 0, 113, 10, 117, 154, 205, 160, 217, 146, 33, 44, 114, 9, 84, 163, 69, 50, 68, 89, 13, 115, 9, 84, 98, 205, 16, 138, 67, 2, 116, 9, 115, 34, 77, 148, 44, 81, 45, 117, 9, 85, 227, 69, 230, 150, 12, 1, 118, 11, 85, 162, 69, 166, 37, 165, 36, 139, 0, 119, 13, 87, 34, 70, 20, 73, 145, 210, 45, 202, 18, 0, 120, 10, 85, 162, 69, 150, 212, 42, 181, 0, 121, 12, 117, 154, 69, 166, 37, 165, 36, 11, 35, 13, 122, 9, 85, 162, 197, 160, 181, 13, 2, 123, 11, 179, 22, 85, 18, 181, 100, 81, 91, 0, 124, 7, 161, 219, 196, 67, 0, 125, 11, 179, 22, 69, 22, 181, 37, 81, 75, 4, 126, 8, 36, 175, 77, 162, 36, 0, 0, 0, 0, 4, 255, 255, 0, 0];
+
+// src/font/fonts/u8g2_font_micropixel_tf_u8g2font.ts
+var u8g2_font_micropixel_tf_u8g2font = [187, 0, 2, 2, 3, 4, 2, 4, 5, 8, 12, 255, 253, 5, 254, 6, 255, 0, 245, 1, 245, 5, 253, 32, 5, 0, 177, 2, 33, 6, 41, 81, 178, 2, 34, 7, 19, 153, 146, 84, 0, 35, 10, 45, 209, 86, 13, 85, 13, 85, 1, 36, 10, 60, 175, 26, 71, 138, 122, 232, 4, 37, 6, 36, 177, 18, 123, 38, 10, 45, 209, 150, 83, 92, 50, 170, 0, 39, 5, 17, 91, 34, 40, 7, 58, 111, 166, 58, 3, 41, 8, 58, 111, 18, 83, 75, 1, 42, 6, 27, 153, 146, 58, 43, 7, 27, 147, 150, 86, 2, 44, 5, 17, 79, 34, 45, 5, 10, 117, 34, 46, 5, 9, 81, 18, 47, 7, 43, 145, 90, 149, 17, 48, 9, 44, 177, 166, 226, 152, 73, 1, 49, 6, 170, 145, 182, 26, 50, 8, 44, 177, 166, 98, 115, 4, 51, 9, 44, 177, 166, 98, 21, 147, 2, 52, 8, 44, 177, 18, 229, 208, 5, 53, 9, 44, 177, 50, 244, 70, 35, 1, 54, 9, 44, 177, 166, 242, 138, 73, 1, 55, 9, 44, 177, 50, 114, 172, 35, 0, 56, 10, 44, 177, 166, 98, 82, 49, 41, 0, 57, 9, 44, 177, 166, 98, 218, 73, 1, 58, 6, 41, 81, 146, 3, 59, 6, 49, 79, 146, 5, 60, 6, 26, 115, 38, 3, 61, 6, 27, 147, 178, 7, 62, 7, 26, 115, 18, 21, 0, 63, 9, 44, 177, 166, 98, 77, 37, 0, 64, 10, 45, 209, 182, 178, 26, 169, 90, 0, 65, 8, 44, 177, 166, 226, 152, 50, 66, 10, 44, 177, 178, 226, 72, 113, 36, 0, 67, 8, 44, 177, 54, 114, 141, 6, 68, 9, 44, 177, 178, 162, 57, 18, 0, 69, 8, 44, 177, 50, 244, 202, 35, 70, 8, 44, 177, 50, 244, 202, 25, 71, 8, 44, 177, 54, 114, 154, 105, 72, 8, 44, 177, 18, 229, 152, 50, 73, 7, 43, 145, 178, 98, 53, 74, 7, 43, 145, 38, 203, 5, 75, 9, 44, 177, 18, 85, 146, 169, 12, 76, 7, 43, 145, 18, 155, 3, 77, 9, 45, 209, 146, 215, 74, 90, 7, 78, 8, 44, 177, 18, 151, 154, 50, 79, 9, 44, 177, 166, 162, 153, 20, 0, 80, 9, 44, 177, 178, 226, 72, 57, 3, 81, 8, 44, 177, 166, 162, 165, 6, 82, 9, 44, 177, 178, 226, 72, 81, 6, 83, 10, 44, 177, 54, 50, 210, 104, 36, 0, 84, 7, 43, 145, 178, 98, 11, 85, 8, 44, 177, 18, 157, 73, 1, 86, 8, 44, 177, 18, 93, 49, 2, 87, 9, 45, 209, 146, 173, 164, 186, 0, 88, 9, 44, 177, 18, 101, 82, 81, 6, 89, 8, 43, 145, 146, 84, 89, 1, 90, 9, 44, 177, 50, 114, 82, 121, 4, 91, 7, 58, 111, 178, 186, 4, 92, 7, 43, 145, 18, 115, 25, 93, 7, 58, 111, 162, 186, 6, 94, 6, 19, 153, 214, 0, 95, 6, 12, 177, 50, 2, 96, 6, 18, 121, 18, 3, 97, 7, 27, 145, 182, 146, 0, 98, 8, 43, 145, 18, 163, 74, 11, 99, 6, 27, 145, 182, 5, 100, 7, 43, 145, 90, 173, 36, 101, 6, 26, 113, 178, 4, 102, 8, 43, 145, 154, 210, 138, 9, 103, 8, 43, 141, 182, 146, 92, 0, 104, 8, 43, 145, 18, 163, 74, 42, 105, 6, 41, 81, 146, 6, 106, 8, 186, 76, 150, 83, 165, 0, 107, 8, 35, 145, 146, 86, 82, 1, 108, 6, 41, 81, 50, 4, 109, 8, 29, 209, 50, 82, 165, 10, 110, 7, 27, 145, 162, 146, 10, 111, 7, 27, 145, 50, 210, 8, 112, 8, 43, 141, 162, 210, 138, 17, 113, 7, 43, 141, 182, 146, 44, 114, 7, 27, 145, 50, 146, 4, 115, 7, 27, 145, 166, 146, 2, 116, 7, 42, 113, 146, 86, 12, 117, 7, 27, 145, 146, 84, 18, 118, 7, 27, 145, 146, 84, 5, 119, 8, 29, 209, 146, 146, 234, 2, 120, 6, 27, 145, 146, 58, 121, 8, 43, 141, 146, 84, 146, 11, 122, 7, 27, 145, 178, 210, 0, 123, 9, 59, 143, 154, 98, 202, 49, 7, 124, 5, 49, 81, 114, 125, 10, 59, 143, 146, 99, 78, 49, 69, 0, 126, 7, 20, 181, 150, 84, 2, 160, 5, 0, 177, 2, 161, 6, 41, 81, 146, 6, 162, 9, 44, 175, 26, 71, 138, 51, 1, 163, 9, 44, 177, 170, 226, 72, 113, 4, 164, 10, 45, 209, 146, 211, 76, 113, 229, 0, 165, 9, 43, 145, 146, 84, 181, 18, 0, 166, 6, 49, 79, 34, 5, 167, 12, 60, 177, 54, 50, 82, 49, 105, 52, 18, 0, 168, 7, 21, 217, 162, 70, 18, 169, 10, 45, 209, 182, 178, 26, 57, 45, 0, 170, 7, 27, 153, 182, 146, 0, 171, 7, 29, 211, 22, 101, 3, 172, 6, 19, 147, 50, 3, 174, 9, 45, 209, 182, 226, 85, 45, 0, 175, 6, 12, 187, 50, 2, 176, 7, 27, 153, 50, 210, 8, 177, 8, 43, 145, 150, 86, 70, 3, 178, 7, 35, 155, 162, 215, 0, 179, 7, 35, 155, 34, 229, 2, 180, 6, 18, 121, 166, 0, 181, 8, 43, 141, 146, 212, 144, 17, 182, 11, 45, 209, 54, 86, 82, 49, 197, 148, 0, 183, 5, 9, 85, 18, 184, 7, 155, 106, 150, 23, 0, 185, 6, 162, 155, 162, 26, 186, 7, 27, 157, 50, 210, 8, 187, 8, 29, 211, 18, 75, 153, 0, 191, 9, 44, 173, 150, 169, 88, 41, 0, 192, 11, 68, 177, 150, 81, 198, 42, 142, 41, 3, 193, 10, 68, 177, 90, 83, 42, 142, 41, 3, 194, 11, 68, 177, 166, 98, 134, 42, 142, 41, 3, 195, 11, 68, 177, 150, 84, 198, 42, 142, 41, 3, 196, 10, 60, 177, 18, 51, 84, 113, 76, 25, 197, 10, 60, 177, 166, 98, 82, 113, 76, 25, 198, 12, 47, 17, 55, 70, 204, 99, 197, 28, 71, 0, 199, 11, 68, 171, 54, 114, 141, 102, 70, 49, 1, 200, 11, 68, 177, 150, 81, 198, 42, 142, 41, 3, 201, 10, 68, 177, 90, 227, 161, 87, 30, 1, 202, 11, 68, 177, 22, 83, 134, 67, 175, 60, 2, 203, 10, 60, 177, 146, 50, 28, 122, 229, 17, 204, 9, 67, 145, 146, 51, 90, 177, 26, 205, 9, 67, 145, 154, 50, 90, 177, 26, 206, 8, 67, 145, 214, 94, 177, 26, 207, 9, 59, 145, 146, 242, 138, 213, 0, 208, 10, 173, 176, 54, 227, 72, 101, 90, 0, 209, 11, 68, 177, 150, 84, 134, 113, 169, 41, 3, 210, 11, 68, 177, 150, 81, 198, 42, 154, 73, 1, 211, 10, 68, 177, 90, 83, 42, 154, 73, 1, 212, 11, 68, 177, 166, 98, 134, 42, 154, 73, 1, 213, 11, 68, 177, 150, 84, 198, 42, 154, 73, 1, 214, 10, 60, 177, 18, 51, 84, 209, 76, 10, 215, 6, 27, 147, 146, 58, 216, 10, 174, 208, 170, 99, 26, 43, 102, 9, 217, 11, 68, 177, 150, 81, 134, 209, 153, 20, 0, 218, 9, 68, 177, 90, 227, 232, 76, 10, 219, 11, 68, 177, 166, 98, 70, 209, 153, 20, 0, 220, 10, 60, 177, 18, 51, 138, 206, 164, 0, 221, 10, 67, 145, 154, 50, 74, 170, 172, 0, 222, 9, 44, 177, 146, 87, 28, 41, 3, 223, 9, 44, 177, 178, 162, 42, 85, 2, 224, 8, 51, 145, 146, 51, 92, 73, 225, 8, 51, 145, 154, 50, 92, 73, 226, 8, 51, 145, 214, 70, 43, 9, 227, 10, 52, 177, 150, 84, 198, 170, 84, 0, 228, 8, 43, 145, 146, 50, 90, 73, 229, 10, 59, 145, 50, 210, 200, 104, 37, 1, 230, 7, 28, 177, 54, 82, 28, 231, 8, 51, 139, 182, 85, 94, 0, 232, 7, 50, 113, 18, 227, 18, 233, 7, 50, 113, 166, 151, 0, 234, 8, 51, 145, 214, 86, 81, 1, 235, 8, 43, 145, 146, 178, 138, 10, 236, 7, 50, 113, 18, 115, 42, 237, 7, 50, 113, 166, 83, 5, 238, 8, 51, 145, 214, 70, 177, 2, 239, 9, 43, 145, 146, 50, 138, 21, 0, 240, 12, 60, 177, 146, 98, 149, 81, 26, 49, 41, 0, 241, 10, 52, 177, 150, 84, 134, 50, 181, 0, 242, 9, 51, 145, 146, 51, 26, 105, 4, 243, 9, 51, 145, 154, 50, 26, 105, 4, 244, 8, 51, 145, 214, 30, 105, 4, 245, 10, 52, 177, 150, 84, 134, 171, 90, 0, 246, 8, 43, 145, 146, 242, 72, 35, 247, 8, 43, 145, 150, 209, 70, 9, 248, 10, 173, 174, 142, 210, 76, 113, 101, 4, 249, 9, 51, 145, 146, 51, 74, 42, 9, 250, 9, 51, 145, 154, 50, 74, 42, 9, 251, 8, 51, 145, 214, 78, 42, 9, 252, 8, 43, 145, 146, 114, 82, 73, 253, 10, 67, 141, 154, 50, 74, 42, 201, 5, 254, 10, 67, 143, 18, 163, 74, 106, 197, 8, 255, 10, 59, 141, 146, 114, 82, 73, 46, 0, 0, 0, 0, 4, 255, 255, 0, 0];
+
+// src/font/fonts/u8g2_font_micropixel_tr_u8g2font.ts
+var u8g2_font_micropixel_tr_u8g2font = [95, 0, 2, 2, 3, 3, 2, 4, 4, 6, 9, 255, 254, 5, 254, 6, 255, 0, 239, 1, 234, 2, 195, 32, 5, 128, 216, 0, 33, 6, 169, 168, 172, 0, 34, 6, 147, 204, 36, 21, 35, 10, 173, 232, 85, 67, 85, 67, 85, 0, 36, 10, 188, 215, 198, 145, 162, 30, 58, 1, 37, 6, 164, 216, 196, 30, 38, 9, 173, 232, 229, 20, 151, 140, 42, 39, 5, 145, 173, 8, 40, 7, 186, 183, 169, 206, 0, 41, 8, 186, 183, 196, 212, 82, 0, 42, 6, 155, 204, 164, 14, 43, 7, 155, 201, 165, 149, 0, 44, 5, 145, 167, 8, 45, 5, 138, 186, 8, 46, 5, 137, 168, 4, 47, 7, 171, 200, 86, 101, 4, 48, 9, 172, 216, 169, 56, 102, 82, 0, 49, 6, 234, 200, 173, 6, 50, 8, 172, 216, 169, 216, 28, 1, 51, 9, 172, 216, 169, 88, 197, 164, 0, 52, 8, 172, 216, 68, 57, 116, 1, 53, 9, 172, 216, 12, 189, 209, 72, 0, 54, 9, 172, 216, 169, 188, 98, 82, 0, 55, 8, 172, 216, 140, 28, 235, 8, 56, 9, 172, 216, 169, 152, 84, 76, 10, 57, 9, 172, 216, 169, 152, 118, 82, 0, 58, 6, 169, 168, 228, 0, 59, 6, 177, 167, 100, 1, 60, 6, 154, 185, 201, 0, 61, 6, 155, 201, 236, 1, 62, 6, 154, 185, 68, 5, 63, 8, 172, 216, 169, 88, 83, 9, 64, 10, 173, 232, 173, 172, 70, 170, 22, 0, 65, 8, 172, 216, 169, 56, 166, 12, 66, 9, 172, 216, 172, 56, 82, 28, 9, 67, 8, 172, 216, 141, 92, 163, 1, 68, 8, 172, 216, 172, 104, 142, 4, 69, 8, 172, 216, 12, 189, 242, 8, 70, 8, 172, 216, 12, 189, 114, 6, 71, 8, 172, 216, 141, 156, 102, 26, 72, 8, 172, 216, 68, 57, 166, 12, 73, 7, 171, 200, 172, 88, 13, 74, 7, 171, 200, 201, 114, 1, 75, 9, 172, 216, 68, 149, 100, 42, 3, 76, 7, 171, 200, 196, 230, 0, 77, 9, 173, 232, 228, 181, 146, 214, 1, 78, 8, 172, 216, 196, 165, 166, 12, 79, 8, 172, 216, 169, 104, 38, 5, 80, 9, 172, 216, 172, 56, 82, 206, 0, 81, 8, 172, 216, 169, 104, 169, 1, 82, 9, 172, 216, 172, 56, 82, 148, 1, 83, 9, 172, 216, 141, 140, 52, 26, 9, 84, 7, 171, 200, 172, 216, 2, 85, 8, 172, 216, 68, 103, 82, 0, 86, 8, 172, 216, 68, 87, 140, 0, 87, 9, 173, 232, 100, 43, 169, 46, 0, 88, 9, 172, 216, 68, 153, 84, 148, 1, 89, 8, 171, 200, 36, 85, 86, 0, 90, 9, 172, 216, 140, 156, 84, 30, 1, 91, 7, 186, 183, 172, 46, 1, 92, 7, 171, 200, 196, 92, 6, 93, 7, 186, 183, 168, 174, 1, 94, 5, 147, 204, 53, 95, 6, 140, 216, 140, 0, 96, 6, 146, 188, 196, 0, 97, 6, 155, 200, 173, 36, 98, 8, 171, 200, 196, 168, 210, 2, 99, 6, 155, 200, 109, 1, 100, 7, 171, 200, 86, 43, 9, 101, 6, 154, 184, 44, 1, 102, 8, 171, 200, 166, 180, 98, 2, 103, 8, 171, 198, 173, 36, 23, 0, 104, 8, 171, 200, 196, 168, 146, 10, 105, 6, 169, 168, 164, 1, 106, 8, 122, 166, 229, 84, 41, 0, 107, 8, 163, 200, 164, 149, 84, 0, 108, 6, 169, 168, 12, 1, 109, 8, 157, 232, 140, 84, 169, 2, 110, 7, 155, 200, 168, 164, 2, 111, 7, 155, 200, 140, 52, 2, 112, 8, 171, 198, 168, 180, 98, 4, 113, 7, 171, 198, 173, 36, 11, 114, 7, 155, 200, 140, 36, 1, 115, 7, 155, 200, 169, 164, 0, 116, 7, 170, 184, 164, 21, 3, 117, 7, 155, 200, 36, 149, 4, 118, 7, 155, 200, 36, 85, 1, 119, 8, 157, 232, 164, 164, 186, 0, 120, 6, 155, 200, 164, 14, 121, 8, 171, 198, 36, 149, 228, 2, 122, 6, 155, 200, 172, 52, 123, 9, 187, 199, 166, 152, 114, 204, 1, 124, 5, 177, 168, 28, 125, 9, 187, 199, 228, 152, 83, 76, 17, 126, 7, 148, 218, 37, 149, 0, 0, 0, 0, 4, 255, 255, 0, 0];
+
+// src/font/fonts/u8g2_font_NokiaSmallPlain_tf_u8g2font.ts
+var u8g2_font_NokiaSmallPlain_tf_u8g2font = [148, 0, 2, 2, 3, 4, 2, 4, 5, 7, 8, 0, 255, 7, 255, 7, 255, 1, 34, 2, 43, 5, 72, 32, 5, 0, 113, 2, 33, 7, 57, 81, 50, 84, 0, 34, 7, 19, 155, 146, 84, 0, 35, 13, 61, 209, 86, 166, 52, 84, 53, 84, 153, 18, 0, 36, 10, 68, 175, 26, 71, 74, 243, 208, 9, 37, 10, 60, 177, 162, 86, 140, 178, 90, 2, 38, 12, 61, 209, 150, 83, 76, 185, 74, 50, 170, 0, 39, 5, 17, 91, 34, 40, 7, 66, 111, 166, 122, 6, 41, 8, 66, 111, 18, 83, 151, 2, 42, 10, 45, 211, 86, 199, 33, 115, 74, 0, 43, 10, 45, 211, 154, 81, 28, 50, 163, 8, 44, 6, 18, 111, 166, 0, 45, 6, 12, 183, 50, 2, 46, 5, 9, 81, 18, 47, 9, 59, 145, 90, 197, 42, 70, 0, 48, 9, 60, 177, 166, 162, 103, 82, 0, 49, 6, 186, 145, 182, 122, 50, 10, 60, 177, 178, 81, 78, 42, 231, 17, 51, 11, 60, 177, 178, 81, 78, 26, 229, 145, 0, 52, 9, 60, 177, 30, 85, 146, 67, 23, 53, 10, 60, 177, 178, 242, 70, 57, 143, 4, 54, 10, 60, 177, 166, 242, 138, 102, 82, 0, 55, 10, 60, 177, 50, 114, 204, 49, 151, 0, 56, 11, 60, 177, 166, 162, 76, 42, 202, 164, 0, 57, 10, 60, 177, 166, 162, 153, 118, 82, 0, 58, 6, 161, 113, 18, 3, 59, 7, 42, 111, 150, 161, 2, 60, 10, 60, 177, 30, 219, 40, 163, 140, 2, 61, 8, 28, 179, 50, 50, 26, 1, 62, 11, 60, 177, 146, 81, 70, 25, 197, 54, 0, 63, 10, 60, 177, 178, 81, 172, 51, 21, 1, 64, 12, 61, 209, 182, 178, 26, 106, 169, 156, 22, 0, 65, 9, 60, 177, 166, 162, 57, 166, 12, 66, 10, 60, 177, 178, 226, 72, 209, 28, 9, 67, 8, 60, 177, 54, 114, 55, 26, 68, 9, 60, 177, 178, 162, 231, 72, 0, 69, 9, 60, 177, 50, 244, 202, 245, 8, 70, 9, 60, 177, 50, 244, 202, 109, 0, 71, 9, 60, 177, 54, 114, 154, 102, 26, 72, 9, 60, 177, 18, 229, 152, 206, 0, 73, 6, 57, 81, 114, 4, 74, 7, 59, 145, 218, 115, 1, 75, 10, 60, 177, 18, 85, 146, 89, 166, 50, 76, 8, 60, 177, 146, 123, 143, 0, 77, 10, 61, 209, 146, 215, 74, 42, 105, 59, 78, 10, 61, 209, 146, 167, 74, 114, 219, 1, 79, 9, 61, 209, 182, 178, 119, 90, 0, 80, 10, 60, 177, 178, 162, 57, 82, 206, 0, 81, 10, 69, 207, 182, 178, 87, 181, 97, 0, 82, 9, 60, 177, 178, 162, 57, 82, 51, 83, 11, 60, 177, 54, 114, 70, 26, 229, 145, 0, 84, 13, 61, 209, 50, 100, 70, 25, 101, 148, 81, 70, 17, 85, 8, 60, 177, 18, 125, 38, 5, 86, 11, 61, 209, 146, 237, 84, 166, 156, 81, 4, 87, 15, 63, 17, 147, 161, 134, 41, 199, 84, 166, 58, 101, 84, 2, 88, 10, 61, 209, 146, 117, 170, 171, 90, 7, 89, 12, 61, 209, 146, 117, 170, 51, 202, 40, 163, 8, 90, 9, 60, 177, 50, 114, 108, 231, 17, 92, 9, 59, 145, 18, 115, 172, 99, 0, 95, 6, 13, 207, 50, 4, 97, 9, 44, 177, 166, 81, 26, 49, 13, 98, 10, 60, 177, 146, 243, 138, 230, 72, 0, 99, 7, 43, 145, 54, 107, 1, 100, 9, 60, 177, 94, 141, 104, 166, 1, 101, 9, 44, 177, 166, 226, 216, 72, 1, 102, 7, 58, 113, 166, 86, 11, 103, 10, 52, 175, 54, 162, 76, 59, 41, 0, 104, 9, 60, 177, 146, 243, 138, 206, 0, 105, 7, 57, 81, 146, 134, 0, 106, 8, 66, 111, 150, 83, 75, 1, 107, 10, 60, 177, 146, 75, 149, 100, 42, 3, 108, 6, 57, 81, 114, 4, 109, 11, 45, 209, 50, 82, 165, 146, 74, 170, 0, 110, 7, 44, 177, 178, 162, 51, 111, 9, 44, 177, 166, 162, 153, 20, 0, 112, 10, 52, 175, 178, 162, 28, 41, 103, 0, 113, 9, 52, 175, 54, 162, 76, 187, 0, 114, 8, 43, 145, 146, 134, 44, 1, 115, 7, 43, 145, 182, 243, 2, 116, 8, 58, 113, 146, 210, 42, 3, 117, 7, 44, 177, 18, 157, 105, 118, 10, 45, 209, 146, 117, 42, 83, 142, 0, 119, 10, 45, 209, 146, 85, 82, 157, 41, 1, 120, 9, 44, 177, 18, 101, 82, 81, 6, 121, 9, 52, 175, 18, 205, 180, 147, 2, 122, 8, 44, 177, 50, 98, 123, 4, 126, 8, 61, 209, 114, 123, 143, 1, 160, 5, 0, 113, 2, 161, 7, 57, 81, 146, 134, 0, 163, 10, 60, 177, 54, 114, 158, 57, 142, 0, 164, 10, 52, 177, 18, 147, 138, 50, 169, 24, 165, 12, 61, 209, 146, 83, 53, 100, 222, 25, 69, 0, 167, 10, 67, 143, 86, 173, 164, 210, 170, 0, 191, 9, 60, 177, 154, 169, 28, 107, 52, 192, 10, 60, 177, 150, 81, 84, 113, 76, 25, 193, 9, 60, 177, 90, 171, 56, 166, 12, 194, 10, 60, 177, 166, 98, 82, 113, 76, 25, 195, 10, 60, 177, 150, 84, 84, 113, 76, 25, 196, 10, 60, 177, 18, 51, 84, 113, 76, 25, 197, 9, 60, 177, 38, 173, 56, 166, 12, 198, 11, 62, 241, 58, 82, 157, 242, 72, 177, 115, 199, 9, 68, 175, 54, 114, 55, 154, 9, 200, 10, 60, 177, 150, 81, 26, 122, 229, 17, 201, 9, 60, 177, 90, 14, 189, 242, 8, 202, 9, 60, 177, 90, 141, 189, 242, 8, 203, 10, 60, 177, 86, 163, 161, 87, 30, 1, 204, 8, 58, 113, 18, 85, 23, 0, 205, 7, 58, 113, 166, 122, 1, 206, 7, 59, 145, 214, 217, 5, 207, 9, 59, 145, 146, 50, 138, 93, 0, 209, 10, 61, 209, 102, 166, 60, 85, 146, 59, 210, 10, 61, 209, 154, 97, 92, 217, 78, 11, 211, 9, 61, 209, 94, 175, 108, 167, 5, 212, 10, 61, 209, 154, 83, 26, 219, 78, 11, 214, 10, 61, 209, 86, 83, 43, 219, 105, 1, 216, 10, 61, 209, 54, 228, 149, 230, 161, 0, 217, 9, 60, 177, 150, 81, 233, 76, 10, 218, 8, 60, 177, 218, 116, 38, 5, 219, 10, 60, 177, 166, 98, 70, 209, 76, 10, 220, 10, 60, 177, 18, 51, 138, 206, 164, 0, 223, 10, 67, 143, 86, 169, 149, 212, 138, 0, 224, 11, 60, 177, 150, 81, 212, 40, 141, 152, 6, 225, 10, 60, 177, 30, 163, 70, 105, 196, 52, 226, 11, 60, 177, 166, 98, 210, 40, 141, 152, 6, 227, 11, 60, 177, 150, 84, 212, 40, 141, 152, 6, 228, 10, 60, 177, 86, 67, 141, 210, 136, 105, 229, 10, 60, 177, 90, 197, 140, 210, 136, 105, 230, 10, 46, 241, 166, 50, 170, 198, 204, 74, 231, 8, 51, 143, 54, 107, 149, 0, 232, 11, 60, 177, 150, 81, 84, 113, 108, 164, 0, 233, 10, 60, 177, 90, 171, 56, 54, 82, 0, 234, 11, 60, 177, 166, 98, 82, 113, 108, 164, 0, 235, 10, 60, 177, 86, 67, 21, 199, 70, 10, 236, 8, 58, 113, 18, 85, 23, 0, 237, 7, 58, 113, 166, 83, 11, 238, 7, 59, 145, 214, 217, 5, 239, 9, 59, 145, 146, 50, 138, 93, 0, 241, 9, 60, 177, 150, 84, 90, 209, 25, 242, 10, 60, 177, 150, 81, 84, 209, 76, 10, 243, 9, 60, 177, 90, 171, 104, 38, 5, 244, 10, 60, 177, 166, 98, 82, 209, 76, 10, 246, 10, 60, 177, 18, 51, 84, 209, 76, 10, 248, 10, 44, 177, 54, 162, 26, 105, 36, 0, 249, 9, 60, 177, 150, 81, 233, 76, 3, 250, 8, 60, 177, 218, 116, 166, 1, 251, 10, 60, 177, 166, 98, 70, 209, 76, 3, 252, 9, 60, 177, 18, 51, 138, 206, 52, 0, 0, 0, 4, 255, 255, 0, 0];
+
+// src/font/fonts/u8g2_font_smolfont_tf_u8g2font.ts
+var u8g2_font_smolfont_tf_u8g2font = [187, 0, 2, 2, 3, 4, 2, 4, 4, 5, 10, 255, 254, 5, 255, 5, 0, 0, 226, 1, 204, 5, 130, 32, 5, 0, 113, 1, 33, 6, 41, 81, 89, 1, 34, 6, 19, 151, 73, 42, 35, 7, 43, 145, 73, 253, 0, 36, 7, 59, 143, 139, 207, 4, 37, 8, 43, 145, 73, 49, 149, 41, 38, 9, 59, 143, 137, 35, 165, 67, 2, 39, 5, 17, 87, 17, 40, 7, 42, 113, 83, 101, 0, 41, 8, 42, 113, 137, 41, 41, 0, 42, 6, 27, 149, 73, 29, 43, 7, 27, 147, 75, 43, 1, 44, 5, 17, 79, 17, 45, 5, 11, 149, 25, 46, 5, 137, 113, 9, 47, 7, 43, 145, 173, 202, 8, 48, 7, 43, 145, 91, 214, 2, 49, 7, 43, 145, 75, 178, 26, 50, 8, 43, 145, 25, 41, 166, 52, 51, 8, 43, 145, 153, 41, 143, 0, 52, 8, 43, 145, 73, 106, 196, 2, 53, 8, 43, 145, 25, 81, 47, 0, 54, 8, 43, 145, 155, 35, 141, 0, 55, 6, 43, 145, 153, 29, 56, 8, 43, 145, 25, 169, 53, 2, 57, 8, 43, 145, 25, 105, 196, 17, 58, 6, 25, 83, 73, 1, 59, 7, 178, 78, 203, 148, 2, 60, 6, 26, 115, 147, 1, 61, 6, 27, 147, 217, 3, 62, 6, 26, 115, 137, 10, 63, 9, 51, 143, 81, 101, 202, 48, 1, 64, 7, 43, 145, 59, 212, 8, 65, 8, 43, 145, 91, 106, 168, 0, 66, 8, 43, 145, 81, 105, 165, 17, 67, 8, 43, 145, 91, 50, 141, 0, 68, 8, 43, 145, 81, 201, 26, 1, 69, 8, 43, 145, 25, 81, 197, 1, 70, 8, 43, 145, 25, 81, 197, 8, 71, 8, 51, 143, 155, 73, 141, 24, 72, 8, 43, 145, 137, 105, 40, 21, 73, 7, 43, 145, 89, 177, 26, 74, 7, 43, 145, 45, 213, 2, 75, 8, 43, 145, 73, 106, 37, 21, 76, 7, 43, 145, 137, 205, 1, 77, 8, 43, 145, 73, 99, 40, 21, 78, 7, 43, 145, 81, 201, 21, 79, 8, 43, 145, 25, 201, 26, 1, 80, 8, 43, 145, 25, 105, 197, 8, 81, 7, 51, 143, 91, 214, 14, 82, 8, 43, 145, 81, 73, 173, 2, 83, 7, 43, 145, 155, 23, 0, 84, 7, 43, 145, 89, 177, 5, 85, 7, 43, 145, 73, 174, 36, 86, 8, 43, 145, 137, 73, 149, 9, 87, 8, 43, 145, 85, 106, 168, 0, 88, 7, 43, 145, 73, 101, 85, 89, 7, 43, 145, 85, 101, 5, 90, 7, 43, 145, 153, 169, 28, 91, 7, 42, 113, 89, 149, 0, 92, 7, 43, 145, 137, 185, 12, 93, 7, 42, 113, 81, 213, 0, 94, 5, 19, 151, 107, 95, 5, 11, 145, 25, 96, 6, 18, 119, 137, 1, 97, 6, 27, 145, 59, 2, 98, 8, 35, 145, 137, 42, 141, 0, 99, 6, 27, 145, 25, 113, 100, 7, 35, 145, 77, 71, 0, 101, 6, 27, 145, 25, 106, 102, 7, 27, 145, 25, 42, 2, 103, 8, 35, 143, 25, 41, 201, 0, 104, 8, 35, 145, 137, 113, 164, 0, 105, 6, 33, 81, 73, 2, 106, 7, 42, 111, 203, 73, 1, 107, 7, 27, 145, 73, 171, 0, 108, 7, 34, 113, 73, 149, 0, 109, 7, 27, 145, 73, 67, 5, 110, 7, 27, 145, 81, 73, 5, 111, 7, 27, 145, 25, 105, 4, 112, 8, 35, 143, 25, 105, 69, 0, 113, 7, 35, 143, 171, 118, 0, 114, 6, 27, 145, 155, 17, 115, 7, 27, 145, 83, 73, 1, 116, 6, 34, 113, 73, 75, 117, 7, 27, 145, 73, 42, 9, 118, 7, 27, 145, 137, 169, 2, 119, 7, 35, 145, 85, 67, 5, 120, 6, 27, 145, 73, 29, 121, 7, 35, 145, 85, 101, 2, 122, 6, 27, 145, 145, 81, 123, 7, 43, 145, 83, 117, 20, 124, 6, 41, 81, 25, 2, 125, 8, 43, 145, 145, 57, 37, 5, 126, 6, 27, 147, 137, 51, 160, 5, 0, 113, 1, 161, 6, 49, 79, 73, 35, 162, 8, 43, 143, 29, 114, 68, 0, 163, 8, 51, 143, 155, 42, 206, 0, 164, 8, 43, 145, 73, 67, 13, 21, 165, 9, 51, 143, 73, 213, 74, 43, 1, 166, 6, 169, 113, 81, 2, 167, 8, 43, 145, 27, 169, 26, 9, 168, 6, 11, 153, 73, 1, 169, 8, 59, 143, 217, 35, 238, 1, 170, 7, 27, 149, 25, 105, 1, 171, 6, 27, 147, 27, 81, 172, 6, 19, 147, 153, 1, 174, 8, 51, 143, 25, 105, 213, 3, 175, 5, 11, 153, 25, 176, 6, 27, 149, 235, 2, 177, 8, 43, 145, 75, 43, 163, 1, 178, 6, 27, 149, 217, 3, 179, 6, 27, 149, 153, 35, 180, 6, 18, 119, 83, 0, 181, 8, 35, 143, 73, 106, 72, 0, 182, 8, 51, 143, 91, 42, 201, 2, 183, 5, 9, 89, 9, 184, 6, 146, 141, 137, 1, 185, 6, 26, 119, 91, 1, 186, 6, 18, 119, 25, 1, 187, 7, 27, 147, 145, 35, 1, 191, 9, 51, 143, 203, 48, 197, 148, 4, 192, 10, 67, 145, 201, 25, 46, 53, 84, 0, 193, 10, 67, 145, 77, 25, 46, 53, 84, 0, 194, 9, 67, 145, 107, 163, 165, 134, 10, 195, 10, 67, 145, 145, 26, 45, 53, 84, 0, 196, 10, 59, 145, 73, 25, 45, 53, 84, 0, 197, 9, 59, 145, 107, 45, 53, 84, 0, 198, 9, 44, 177, 27, 169, 26, 42, 9, 199, 9, 59, 141, 91, 50, 141, 84, 2, 200, 10, 67, 145, 201, 25, 141, 168, 226, 0, 201, 10, 67, 145, 77, 25, 141, 168, 226, 0, 202, 9, 67, 145, 107, 143, 168, 226, 0, 203, 9, 59, 145, 73, 121, 68, 21, 7, 204, 9, 67, 145, 201, 25, 173, 88, 13, 205, 9, 67, 145, 77, 25, 173, 88, 13, 206, 8, 67, 145, 107, 175, 88, 13, 207, 8, 59, 145, 73, 121, 197, 106, 208, 8, 172, 144, 147, 105, 168, 106, 209, 9, 67, 145, 145, 90, 37, 87, 0, 210, 10, 67, 145, 201, 25, 141, 100, 141, 0, 211, 10, 67, 145, 77, 25, 141, 100, 141, 0, 212, 9, 67, 145, 107, 143, 100, 141, 0, 213, 9, 67, 145, 145, 122, 36, 107, 4, 214, 9, 59, 145, 73, 121, 36, 107, 4, 215, 6, 27, 147, 73, 29, 216, 13, 189, 174, 71, 105, 166, 152, 98, 138, 43, 35, 0, 217, 9, 67, 145, 201, 25, 37, 87, 18, 218, 9, 67, 145, 77, 25, 37, 87, 18, 219, 8, 67, 145, 107, 39, 87, 18, 220, 9, 59, 145, 73, 57, 185, 146, 0, 221, 9, 67, 145, 77, 25, 171, 178, 2, 222, 8, 43, 145, 137, 42, 173, 8, 223, 10, 59, 141, 81, 105, 165, 33, 35, 0, 224, 8, 51, 145, 201, 25, 174, 17, 225, 8, 51, 145, 77, 25, 174, 17, 226, 7, 51, 145, 107, 163, 35, 227, 8, 51, 145, 145, 26, 29, 1, 228, 8, 43, 145, 73, 25, 29, 1, 229, 7, 51, 145, 235, 92, 35, 230, 8, 28, 177, 27, 41, 141, 0, 231, 8, 43, 141, 25, 113, 149, 0, 232, 8, 51, 145, 201, 25, 13, 53, 233, 8, 51, 145, 77, 25, 13, 53, 234, 7, 51, 145, 107, 15, 53, 235, 8, 43, 145, 73, 121, 168, 1, 236, 7, 178, 80, 137, 57, 21, 237, 7, 178, 80, 137, 57, 21, 238, 8, 179, 112, 107, 163, 88, 1, 239, 8, 171, 112, 73, 25, 197, 10, 240, 8, 43, 145, 203, 105, 85, 0, 241, 8, 51, 145, 145, 90, 37, 21, 242, 9, 51, 145, 201, 25, 141, 52, 2, 243, 9, 51, 145, 77, 25, 141, 52, 2, 244, 8, 51, 145, 107, 143, 52, 2, 245, 8, 51, 145, 145, 122, 164, 17, 246, 8, 43, 145, 73, 121, 164, 17, 247, 8, 43, 145, 203, 104, 163, 4, 248, 10, 173, 174, 71, 105, 166, 184, 50, 2, 249, 9, 51, 145, 201, 25, 37, 149, 4, 250, 9, 51, 145, 77, 25, 37, 149, 4, 251, 8, 51, 145, 107, 39, 149, 4, 252, 8, 43, 145, 73, 57, 169, 36, 253, 9, 59, 145, 77, 25, 171, 50, 1, 254, 8, 35, 145, 137, 99, 68, 0, 255, 9, 51, 145, 73, 25, 170, 50, 1, 0, 0, 0, 4, 255, 255, 0, 0];
+
+// src/font/fonts/u8g2_font_spleen12x24_me_u8g2font.ts
+var u8g2_font_spleen12x24_me_u8g2font = [122, 2, 4, 2, 4, 5, 1, 4, 5, 12, 24, 0, 251, 15, 251, 17, 253, 2, 163, 5, 225, 18, 235, 32, 9, 140, 15, 127, 254, 255, 55, 0, 33, 14, 140, 15, 127, 134, 82, 255, 123, 34, 169, 158, 43, 0, 34, 12, 140, 15, 127, 172, 200, 255, 247, 252, 159, 0, 35, 21, 140, 15, 127, 86, 34, 223, 12, 14, 50, 34, 255, 223, 12, 14, 50, 34, 223, 115, 2, 36, 39, 140, 15, 127, 184, 84, 56, 24, 104, 20, 50, 137, 76, 34, 147, 200, 36, 58, 133, 112, 48, 84, 232, 36, 50, 137, 76, 34, 147, 200, 20, 154, 193, 64, 40, 213, 211, 2, 37, 27, 140, 15, 127, 14, 69, 27, 11, 35, 133, 213, 66, 170, 148, 42, 165, 138, 149, 194, 72, 97, 163, 25, 233, 57, 4, 38, 32, 140, 15, 127, 118, 131, 156, 68, 37, 242, 87, 18, 221, 32, 38, 81, 137, 20, 17, 213, 68, 165, 81, 137, 52, 131, 208, 64, 162, 231, 0, 39, 11, 140, 15, 127, 184, 212, 239, 249, 191, 2, 40, 18, 140, 15, 127, 146, 161, 210, 169, 82, 234, 111, 165, 90, 183, 123, 40, 0, 41, 17, 140, 15, 127, 232, 214, 83, 173, 212, 95, 74, 149, 14, 247, 36, 0, 42, 24, 140, 15, 127, 62, 20, 169, 36, 186, 65, 82, 54, 56, 136, 41, 7, 57, 137, 74, 164, 231, 45, 0, 43, 15, 140, 15, 127, 126, 40, 181, 27, 12, 116, 82, 123, 190, 5, 44, 12, 140, 15, 127, 254, 159, 72, 45, 237, 169, 1, 45, 12, 140, 15, 127, 254, 237, 96, 160, 231, 207, 1, 46, 11, 140, 15, 127, 254, 159, 75, 245, 92, 1, 47, 25, 140, 15, 127, 34, 169, 82, 170, 148, 42, 165, 74, 169, 82, 170, 148, 42, 165, 74, 169, 82, 170, 167, 1, 48, 36, 140, 15, 127, 102, 131, 149, 72, 35, 147, 200, 36, 170, 137, 104, 16, 209, 40, 36, 94, 104, 36, 131, 144, 100, 37, 145, 73, 100, 26, 145, 106, 176, 231, 6, 49, 19, 140, 15, 127, 118, 195, 65, 78, 161, 139, 72, 253, 239, 6, 3, 61, 39, 0, 50, 20, 140, 15, 127, 102, 131, 149, 72, 35, 147, 186, 244, 207, 36, 131, 131, 60, 23, 0, 51, 24, 140, 15, 127, 102, 131, 149, 72, 35, 147, 90, 202, 6, 90, 83, 39, 50, 141, 72, 53, 216, 115, 3, 52, 17, 140, 15, 127, 70, 82, 71, 254, 63, 26, 28, 4, 165, 238, 185, 1, 53, 25, 140, 15, 127, 70, 131, 131, 136, 76, 34, 117, 58, 24, 104, 77, 157, 200, 52, 34, 213, 96, 207, 13, 0, 54, 31, 140, 15, 127, 102, 131, 65, 72, 37, 145, 58, 29, 12, 68, 42, 141, 76, 34, 147, 200, 36, 50, 137, 76, 35, 82, 13, 246, 220, 0, 55, 16, 140, 15, 127, 70, 131, 131, 136, 76, 234, 210, 167, 190, 231, 12, 56, 34, 140, 15, 127, 102, 131, 149, 72, 35, 147, 200, 36, 50, 141, 72, 53, 88, 137, 52, 50, 137, 76, 34, 147, 200, 36, 50, 141, 72, 53, 216, 115, 3, 57, 30, 140, 15, 127, 102, 131, 149, 72, 35, 147, 200, 36, 50, 137, 76, 34, 211, 168, 68, 131, 129, 212, 19, 149, 104, 48, 200, 115, 3, 58, 13, 140, 15, 127, 254, 74, 170, 231, 212, 158, 43, 0, 59, 14, 140, 15, 127, 254, 74, 170, 103, 45, 181, 180, 167, 6, 60, 12, 140, 15, 127, 198, 74, 127, 235, 239, 185, 0, 61, 16, 140, 15, 127, 126, 52, 56, 200, 179, 26, 28, 228, 249, 17, 0, 62, 12, 140, 15, 127, 86, 90, 127, 233, 239, 57, 4, 63, 19, 140, 15, 127, 102, 131, 149, 72, 35, 147, 90, 122, 106, 79, 36, 213, 115, 5, 64, 38, 140, 15, 127, 110, 6, 43, 145, 70, 38, 145, 73, 36, 11, 137, 100, 33, 145, 44, 36, 146, 133, 68, 178, 144, 72, 22, 18, 201, 96, 162, 213, 14, 6, 121, 78, 0, 65, 35, 140, 15, 127, 102, 131, 149, 72, 35, 147, 200, 36, 50, 137, 76, 34, 147, 12, 14, 34, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 211, 115, 1, 66, 37, 140, 15, 127, 70, 131, 129, 72, 165, 145, 73, 100, 18, 153, 68, 38, 81, 105, 6, 3, 145, 74, 35, 147, 200, 36, 50, 137, 76, 162, 210, 12, 6, 122, 110, 0, 67, 18, 140, 15, 127, 102, 131, 129, 70, 41, 245, 191, 213, 14, 6, 122, 46, 0, 68, 36, 140, 15, 127, 70, 131, 129, 72, 165, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 165, 25, 12, 244, 220, 0, 69, 22, 140, 15, 127, 102, 131, 129, 70, 41, 245, 116, 48, 16, 73, 189, 213, 14, 6, 122, 46, 0, 70, 19, 140, 15, 127, 102, 131, 129, 70, 41, 245, 116, 48, 16, 73, 253, 158, 75, 0, 71, 29, 140, 15, 127, 102, 131, 129, 70, 41, 245, 205, 64, 34, 147, 200, 36, 50, 137, 76, 34, 211, 168, 68, 131, 129, 158, 11, 0, 72, 35, 140, 15, 127, 70, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 147, 12, 14, 34, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 211, 115, 1, 73, 16, 140, 15, 127, 86, 131, 129, 78, 234, 255, 221, 96, 160, 231, 4, 74, 16, 140, 15, 127, 86, 131, 129, 78, 234, 255, 114, 54, 208, 115, 6, 75, 34, 140, 15, 127, 70, 50, 137, 76, 34, 147, 200, 36, 50, 137, 74, 35, 18, 13, 6, 41, 145, 149, 70, 38, 145, 73, 100, 18, 153, 68, 166, 231, 2, 76, 15, 140, 15, 127, 70, 82, 255, 111, 181, 131, 129, 158, 11, 0, 77, 36, 140, 15, 127, 70, 50, 201, 104, 50, 136, 12, 34, 138, 65, 66, 226, 153, 68, 38, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 166, 231, 2, 78, 39, 140, 15, 127, 70, 50, 201, 74, 178, 146, 12, 66, 146, 65, 72, 162, 208, 72, 20, 26, 137, 191, 81, 72, 52, 10, 137, 104, 16, 17, 13, 34, 170, 137, 106, 207, 5, 0, 79, 34, 140, 15, 127, 102, 131, 149, 72, 35, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 141, 72, 53, 216, 115, 3, 80, 26, 140, 15, 127, 70, 131, 129, 72, 165, 145, 73, 100, 18, 153, 68, 38, 81, 105, 6, 3, 145, 212, 239, 185, 4, 81, 36, 140, 15, 127, 102, 131, 149, 72, 35, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 226, 111, 20, 26, 201, 32, 53, 152, 106, 165, 122, 44, 0, 82, 36, 140, 15, 127, 70, 131, 129, 72, 165, 145, 73, 100, 18, 153, 68, 38, 81, 105, 6, 3, 145, 74, 35, 147, 200, 36, 50, 137, 76, 34, 147, 200, 244, 92, 0, 83, 22, 140, 15, 127, 102, 131, 129, 70, 41, 117, 171, 29, 108, 77, 93, 106, 6, 3, 61, 55, 0, 84, 14, 140, 15, 127, 70, 131, 131, 152, 212, 255, 247, 92, 1, 85, 35, 140, 15, 127, 70, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 141, 74, 52, 24, 232, 185, 0, 86, 32, 140, 15, 127, 70, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 35, 114, 37, 209, 13, 146, 122, 174, 0, 87, 36, 140, 15, 127, 70, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 226, 197, 32, 33, 25, 68, 6, 145, 209, 68, 166, 231, 2, 88, 34, 140, 15, 127, 70, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 35, 82, 13, 86, 34, 141, 76, 34, 147, 200, 36, 50, 137, 76, 34, 211, 115, 1, 89, 27, 140, 15, 127, 70, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 163, 18, 13, 6, 82, 95, 78, 6, 247, 156, 0, 90, 17, 140, 15, 127, 70, 131, 131, 168, 75, 255, 212, 116, 112, 144, 231, 2, 91, 15, 140, 15, 255, 193, 64, 36, 245, 255, 127, 58, 24, 168, 1, 92, 25, 140, 15, 127, 164, 84, 43, 213, 74, 181, 82, 173, 84, 43, 213, 74, 181, 82, 173, 84, 43, 213, 67, 1, 93, 14, 140, 15, 239, 193, 64, 234, 255, 255, 209, 96, 160, 7, 94, 18, 140, 15, 127, 120, 116, 168, 144, 105, 68, 42, 137, 78, 207, 255, 53, 0, 95, 11, 140, 15, 127, 254, 255, 233, 224, 32, 13, 96, 11, 140, 15, 127, 176, 214, 61, 255, 159, 2, 97, 27, 140, 15, 127, 62, 28, 12, 178, 166, 162, 193, 64, 163, 146, 200, 36, 50, 137, 76, 163, 18, 13, 6, 122, 46, 0, 98, 31, 140, 15, 127, 70, 82, 167, 131, 129, 72, 165, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 165, 25, 12, 244, 220, 0, 99, 18, 140, 15, 127, 190, 28, 12, 52, 74, 169, 223, 106, 7, 3, 61, 23, 0, 100, 31, 140, 15, 127, 198, 82, 71, 131, 129, 70, 37, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 70, 37, 26, 12, 244, 92, 0, 101, 26, 140, 15, 127, 190, 28, 12, 52, 42, 137, 76, 34, 147, 200, 36, 131, 131, 136, 212, 86, 59, 24, 232, 185, 0, 102, 18, 140, 15, 127, 134, 3, 217, 82, 234, 112, 48, 200, 73, 253, 61, 103, 0, 103, 34, 140, 15, 127, 190, 28, 12, 52, 42, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 147, 200, 52, 42, 209, 96, 16, 213, 74, 149, 162, 193, 32, 3, 104, 30, 140, 15, 127, 70, 82, 167, 131, 129, 72, 165, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 38, 145, 233, 185, 0, 105, 16, 140, 15, 127, 134, 82, 61, 201, 32, 234, 63, 29, 228, 185, 1, 106, 15, 140, 15, 127, 134, 82, 61, 145, 212, 255, 151, 186, 65, 14, 107, 26, 140, 15, 127, 86, 82, 143, 188, 81, 73, 100, 3, 157, 66, 39, 145, 105, 84, 34, 43, 141, 74, 207, 5, 0, 108, 13, 140, 15, 127, 118, 82, 255, 79, 15, 244, 156, 0, 109, 22, 140, 15, 127, 190, 27, 68, 68, 18, 133, 70, 226, 255, 127, 38, 145, 73, 100, 122, 46, 0, 110, 29, 140, 15, 127, 190, 27, 12, 68, 42, 141, 76, 34, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 207, 5, 0, 111, 28, 140, 15, 127, 190, 28, 172, 68, 26, 153, 68, 38, 145, 73, 100, 18, 153, 68, 38, 145, 105, 68, 170, 193, 158, 27, 0, 112, 30, 140, 15, 127, 190, 27, 12, 68, 42, 141, 76, 34, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 42, 205, 96, 32, 146, 122, 9, 113, 30, 140, 15, 127, 190, 28, 12, 52, 42, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 147, 200, 52, 42, 209, 96, 32, 245, 5, 0, 114, 15, 140, 15, 127, 190, 28, 12, 52, 42, 137, 212, 191, 231, 18, 115, 19, 140, 15, 127, 62, 28, 156, 72, 221, 14, 6, 90, 169, 147, 193, 61, 39, 0, 116, 17, 140, 15, 127, 118, 82, 135, 131, 65, 78, 234, 79, 15, 244, 156, 0, 117, 29, 140, 15, 127, 190, 147, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 38, 145, 105, 84, 162, 193, 64, 207, 5, 0, 118, 25, 140, 15, 127, 190, 147, 73, 100, 18, 153, 68, 38, 145, 73, 100, 26, 145, 43, 137, 110, 144, 212, 115, 5, 119, 22, 140, 15, 127, 190, 147, 73, 100, 18, 153, 196, 255, 255, 70, 33, 17, 73, 6, 121, 46, 0, 120, 26, 140, 15, 127, 190, 147, 73, 100, 26, 145, 74, 162, 27, 4, 7, 57, 137, 74, 228, 70, 38, 145, 233, 185, 0, 121, 32, 140, 15, 127, 190, 147, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 38, 145, 105, 84, 162, 193, 64, 234, 82, 51, 24, 104, 0, 122, 17, 140, 15, 127, 190, 27, 28, 68, 149, 254, 233, 224, 32, 207, 5, 0, 123, 19, 140, 15, 127, 208, 32, 167, 148, 250, 165, 112, 185, 53, 245, 91, 237, 32, 13, 124, 11, 140, 15, 127, 90, 169, 255, 255, 158, 22, 125, 19, 140, 15, 247, 65, 214, 212, 111, 181, 203, 161, 82, 234, 151, 186, 65, 30, 3, 126, 18, 140, 15, 127, 254, 100, 36, 81, 104, 20, 26, 133, 68, 180, 231, 111, 0, 127, 9, 140, 15, 127, 254, 255, 55, 0, 160, 9, 140, 15, 127, 254, 255, 55, 0, 161, 14, 140, 15, 127, 134, 82, 61, 145, 212, 255, 158, 43, 0, 162, 33, 140, 15, 127, 222, 72, 117, 131, 129, 70, 162, 210, 168, 52, 42, 141, 74, 163, 210, 168, 52, 42, 141, 76, 162, 27, 12, 116, 82, 61, 41, 0, 163, 25, 140, 15, 127, 134, 67, 133, 76, 163, 210, 168, 164, 134, 131, 129, 76, 234, 82, 170, 28, 28, 228, 185, 0, 164, 25, 140, 15, 127, 206, 69, 42, 137, 110, 144, 27, 172, 68, 254, 106, 176, 27, 228, 36, 42, 145, 158, 115, 0, 165, 30, 140, 15, 127, 70, 50, 137, 76, 35, 114, 37, 145, 73, 116, 131, 224, 32, 169, 27, 12, 116, 82, 221, 96, 160, 147, 234, 185, 2, 166, 14, 140, 15, 127, 90, 169, 191, 39, 146, 250, 123, 90, 0, 167, 29, 140, 15, 127, 210, 129, 76, 162, 210, 168, 180, 218, 225, 96, 37, 242, 87, 131, 225, 214, 74, 163, 146, 200, 6, 122, 82, 0, 168, 11, 140, 15, 127, 66, 145, 123, 254, 127, 5, 169, 35, 140, 15, 127, 30, 15, 86, 34, 141, 76, 34, 89, 72, 20, 26, 137, 66, 35, 81, 104, 36, 10, 141, 68, 178, 144, 200, 52, 34, 213, 96, 207, 13, 0, 170, 22, 140, 15, 127, 244, 32, 43, 213, 13, 100, 18, 153, 68, 55, 208, 99, 6, 123, 254, 75, 0, 171, 24, 140, 15, 127, 126, 40, 81, 73, 84, 18, 149, 68, 37, 209, 73, 116, 18, 157, 68, 39, 209, 115, 13, 172, 13, 140, 15, 127, 126, 52, 56, 136, 250, 158, 31, 1, 173, 10, 140, 15, 127, 254, 241, 96, 207, 63, 174, 37, 140, 15, 127, 30, 15, 86, 34, 141, 76, 162, 152, 88, 40, 18, 18, 133, 34, 33, 81, 76, 44, 20, 9, 137, 66, 145, 144, 200, 52, 34, 213, 96, 207, 13, 0, 175, 10, 140, 15, 127, 240, 96, 207, 255, 63, 176, 19, 140, 15, 127, 210, 65, 78, 34, 147, 200, 36, 50, 137, 110, 144, 231, 255, 8, 177, 18, 140, 15, 127, 158, 75, 237, 6, 3, 157, 212, 158, 98, 48, 208, 243, 22, 178, 17, 140, 15, 127, 210, 65, 78, 34, 85, 122, 34, 27, 236, 249, 47, 1, 179, 19, 140, 15, 127, 210, 65, 78, 34, 21, 110, 165, 50, 137, 110, 144, 231, 63, 5, 180, 11, 140, 15, 127, 10, 165, 123, 254, 63, 6, 181, 18, 140, 15, 127, 190, 19, 249, 255, 143, 38, 131, 140, 98, 98, 234, 37, 0, 182, 32, 140, 15, 127, 102, 131, 129, 70, 33, 241, 255, 141, 66, 34, 26, 68, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 38, 209, 115, 1, 183, 11, 140, 15, 127, 254, 137, 84, 207, 63, 1, 184, 11, 140, 15, 127, 254, 127, 41, 181, 52, 3, 185, 15, 140, 15, 127, 90, 229, 112, 16, 245, 114, 144, 231, 63, 5, 186, 22, 140, 15, 127, 210, 65, 78, 34, 147, 200, 36, 50, 137, 110, 144, 7, 13, 246, 252, 151, 0, 187, 24, 140, 15, 127, 126, 36, 209, 73, 116, 18, 157, 68, 39, 81, 73, 84, 18, 149, 68, 37, 209, 243, 4, 188, 26, 140, 15, 127, 176, 114, 56, 136, 58, 178, 17, 13, 18, 74, 63, 145, 90, 232, 20, 186, 193, 82, 170, 199, 2, 189, 26, 140, 15, 127, 176, 114, 56, 136, 58, 178, 17, 13, 18, 74, 159, 12, 50, 18, 83, 165, 39, 178, 193, 30, 10, 190, 28, 140, 15, 127, 236, 32, 39, 145, 10, 183, 50, 137, 205, 32, 162, 244, 39, 82, 11, 157, 66, 55, 88, 74, 245, 88, 0, 191, 19, 140, 15, 127, 134, 82, 61, 145, 212, 210, 83, 51, 141, 72, 53, 216, 115, 3, 192, 37, 140, 15, 31, 173, 61, 106, 176, 18, 105, 100, 18, 153, 68, 38, 145, 73, 100, 146, 193, 65, 68, 38, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 122, 46, 0, 193, 37, 140, 15, 63, 165, 61, 106, 176, 18, 105, 100, 18, 153, 68, 38, 145, 73, 100, 146, 193, 65, 68, 38, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 122, 46, 0, 194, 40, 140, 15, 167, 65, 78, 162, 18, 233, 33, 131, 149, 72, 35, 147, 200, 36, 50, 137, 76, 34, 147, 12, 14, 34, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 211, 115, 1, 195, 41, 140, 15, 159, 141, 102, 48, 208, 104, 246, 152, 193, 74, 164, 145, 73, 100, 18, 153, 68, 38, 145, 73, 6, 7, 17, 153, 68, 38, 145, 73, 100, 18, 153, 68, 38, 145, 233, 185, 0, 196, 37, 140, 15, 119, 145, 123, 200, 96, 37, 210, 200, 36, 50, 137, 76, 34, 147, 200, 36, 131, 131, 136, 76, 34, 147, 200, 36, 50, 137, 76, 34, 147, 200, 244, 92, 0, 197, 41, 140, 15, 167, 65, 78, 34, 147, 232, 6, 185, 193, 74, 164, 145, 73, 100, 18, 153, 68, 38, 145, 73, 6, 7, 17, 153, 68, 38, 145, 73, 100, 18, 153, 68, 38, 145, 233, 185, 0, 198, 35, 140, 15, 127, 102, 131, 129, 70, 162, 210, 168, 52, 42, 141, 74, 163, 210, 168, 6, 55, 86, 26, 149, 70, 165, 81, 105, 84, 26, 149, 102, 160, 231, 2, 199, 20, 140, 15, 127, 102, 131, 129, 70, 41, 245, 191, 213, 14, 6, 58, 169, 165, 25, 0, 200, 24, 140, 15, 31, 173, 61, 106, 48, 208, 40, 165, 158, 14, 6, 34, 169, 183, 218, 193, 64, 207, 5, 0, 201, 24, 140, 15, 63, 165, 61, 106, 48, 208, 40, 165, 158, 14, 6, 34, 169, 183, 218, 193, 64, 207, 5, 0, 202, 27, 140, 15, 167, 65, 78, 162, 18, 233, 33, 131, 129, 70, 41, 245, 116, 48, 16, 73, 189, 213, 14, 6, 122, 46, 0, 203, 24, 140, 15, 119, 145, 123, 200, 96, 160, 81, 74, 61, 29, 12, 68, 82, 111, 181, 131, 129, 158, 11, 0, 204, 18, 140, 15, 39, 173, 61, 102, 48, 208, 73, 253, 191, 27, 12, 244, 156, 0, 205, 18, 140, 15, 55, 165, 61, 106, 48, 208, 73, 253, 191, 27, 12, 244, 156, 0, 206, 21, 140, 15, 167, 65, 78, 162, 18, 233, 17, 131, 129, 78, 234, 255, 221, 96, 160, 231, 4, 207, 18, 140, 15, 119, 145, 123, 196, 96, 160, 147, 250, 127, 55, 24, 232, 57, 1, 208, 35, 140, 15, 127, 70, 131, 27, 213, 68, 38, 145, 73, 100, 18, 153, 68, 166, 24, 108, 36, 50, 137, 76, 34, 147, 200, 36, 50, 137, 106, 50, 184, 231, 4, 209, 44, 140, 15, 159, 141, 102, 48, 208, 104, 246, 8, 153, 100, 37, 89, 73, 6, 33, 201, 32, 36, 81, 104, 36, 10, 141, 196, 223, 40, 36, 26, 133, 68, 52, 136, 136, 6, 17, 213, 68, 181, 231, 2, 210, 36, 140, 15, 31, 173, 61, 106, 176, 18, 105, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 166, 17, 169, 6, 123, 110, 0, 211, 36, 140, 15, 63, 165, 61, 106, 176, 18, 105, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 166, 17, 169, 6, 123, 110, 0, 212, 39, 140, 15, 167, 65, 78, 162, 18, 233, 33, 131, 149, 72, 35, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 141, 72, 53, 216, 115, 3, 213, 40, 140, 15, 159, 141, 102, 48, 208, 104, 246, 152, 193, 74, 164, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 70, 164, 26, 236, 185, 1, 214, 36, 140, 15, 119, 145, 123, 200, 96, 37, 210, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 35, 82, 13, 246, 220, 0, 215, 24, 140, 15, 127, 158, 202, 52, 34, 149, 68, 55, 72, 74, 149, 131, 156, 68, 37, 210, 200, 244, 60, 5, 216, 42, 140, 15, 127, 122, 209, 96, 160, 17, 105, 84, 19, 209, 32, 34, 26, 68, 52, 10, 137, 70, 33, 241, 23, 26, 137, 66, 35, 25, 132, 36, 131, 144, 70, 36, 26, 12, 66, 82, 61, 3, 217, 37, 140, 15, 31, 173, 61, 70, 38, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 166, 81, 137, 6, 3, 61, 23, 0, 218, 37, 140, 15, 63, 165, 61, 70, 38, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 166, 81, 137, 6, 3, 61, 23, 0, 219, 39, 140, 15, 167, 65, 78, 162, 18, 233, 101, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 26, 149, 104, 48, 208, 115, 1, 220, 36, 140, 15, 119, 145, 123, 153, 68, 38, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 70, 37, 26, 12, 244, 92, 0, 221, 29, 140, 15, 63, 165, 61, 70, 38, 145, 73, 100, 18, 153, 68, 38, 145, 105, 84, 162, 193, 64, 234, 203, 201, 224, 158, 19, 0, 222, 33, 140, 15, 127, 46, 164, 166, 131, 129, 72, 165, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 165, 25, 12, 68, 82, 123, 26, 0, 223, 34, 140, 15, 127, 102, 131, 156, 68, 37, 242, 111, 84, 138, 65, 74, 51, 82, 105, 100, 18, 153, 68, 38, 81, 132, 36, 10, 137, 70, 50, 200, 51, 3, 224, 29, 140, 15, 127, 176, 214, 61, 126, 48, 200, 154, 138, 6, 3, 141, 74, 34, 147, 200, 36, 50, 141, 74, 52, 24, 232, 185, 0, 225, 29, 140, 15, 127, 10, 165, 123, 146, 193, 32, 107, 42, 26, 12, 52, 42, 137, 76, 34, 147, 200, 52, 42, 209, 96, 160, 231, 2, 226, 33, 140, 15, 127, 184, 114, 144, 147, 168, 68, 122, 244, 96, 144, 53, 21, 13, 6, 26, 149, 68, 38, 145, 73, 100, 26, 149, 104, 48, 208, 115, 1, 227, 35, 140, 15, 127, 176, 72, 51, 200, 72, 44, 52, 154, 61, 124, 48, 200, 154, 138, 6, 3, 141, 74, 34, 147, 200, 36, 50, 141, 74, 52, 24, 232, 185, 0, 228, 29, 140, 15, 127, 66, 145, 123, 210, 193, 32, 107, 42, 26, 12, 52, 42, 137, 76, 34, 147, 200, 52, 42, 209, 96, 160, 231, 2, 229, 34, 140, 15, 127, 244, 32, 39, 145, 73, 116, 131, 60, 126, 48, 200, 154, 138, 6, 3, 141, 74, 34, 147, 200, 36, 50, 141, 74, 52, 24, 232, 185, 0, 230, 31, 140, 15, 127, 62, 156, 12, 82, 131, 132, 76, 34, 147, 136, 6, 3, 141, 66, 38, 145, 73, 100, 18, 157, 98, 183, 24, 228, 185, 0, 231, 19, 140, 15, 127, 190, 28, 12, 52, 74, 169, 223, 106, 7, 3, 157, 212, 210, 12, 232, 29, 140, 15, 127, 176, 214, 61, 197, 96, 160, 81, 73, 100, 18, 153, 68, 38, 25, 28, 68, 164, 182, 218, 193, 64, 207, 5, 0, 233, 29, 140, 15, 127, 10, 165, 123, 154, 193, 64, 163, 146, 200, 36, 50, 137, 76, 50, 56, 136, 72, 109, 181, 131, 129, 158, 11, 0, 234, 33, 140, 15, 127, 184, 114, 144, 147, 168, 68, 122, 248, 96, 160, 81, 73, 100, 18, 153, 68, 38, 25, 28, 68, 164, 182, 218, 193, 64, 207, 5, 0, 235, 29, 140, 15, 127, 66, 145, 123, 218, 193, 64, 163, 146, 200, 36, 50, 137, 76, 50, 56, 136, 72, 109, 181, 131, 129, 158, 11, 0, 236, 16, 140, 15, 127, 176, 214, 61, 197, 32, 234, 63, 29, 228, 185, 1, 237, 16, 140, 15, 127, 10, 165, 123, 154, 65, 212, 127, 58, 200, 115, 3, 238, 20, 140, 15, 127, 184, 114, 144, 147, 168, 68, 122, 248, 32, 234, 63, 29, 228, 185, 1, 239, 16, 140, 15, 127, 66, 145, 123, 218, 65, 212, 127, 58, 200, 115, 3, 240, 36, 140, 15, 127, 168, 68, 55, 72, 42, 7, 57, 137, 86, 55, 88, 137, 52, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 211, 136, 84, 131, 61, 55, 0, 241, 37, 140, 15, 127, 176, 72, 51, 200, 72, 44, 52, 154, 61, 122, 48, 16, 169, 52, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 61, 23, 0, 242, 30, 140, 15, 127, 176, 214, 61, 197, 96, 37, 210, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 35, 82, 13, 246, 220, 0, 243, 30, 140, 15, 127, 10, 165, 123, 154, 193, 74, 164, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 70, 164, 26, 236, 185, 1, 244, 34, 140, 15, 127, 184, 114, 144, 147, 168, 68, 122, 248, 96, 37, 210, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 35, 82, 13, 246, 220, 0, 245, 36, 140, 15, 127, 176, 72, 51, 200, 72, 44, 52, 154, 61, 126, 176, 18, 105, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 166, 17, 169, 6, 123, 110, 0, 246, 30, 140, 15, 127, 66, 145, 123, 218, 193, 74, 164, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 70, 164, 26, 236, 185, 1, 247, 17, 140, 15, 127, 190, 149, 234, 41, 6, 3, 61, 133, 84, 207, 115, 0, 248, 33, 140, 15, 127, 190, 202, 12, 22, 18, 209, 68, 53, 17, 13, 34, 26, 133, 196, 11, 141, 100, 16, 146, 172, 36, 35, 137, 98, 176, 201, 51, 7, 249, 31, 140, 15, 127, 176, 214, 61, 92, 38, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 166, 81, 137, 6, 3, 61, 23, 0, 250, 31, 140, 15, 127, 10, 165, 123, 10, 153, 68, 38, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 70, 37, 26, 12, 244, 92, 0, 251, 35, 140, 15, 127, 184, 114, 144, 147, 168, 68, 122, 176, 76, 34, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 163, 18, 13, 6, 122, 46, 0, 252, 31, 140, 15, 127, 66, 145, 123, 74, 153, 68, 38, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 70, 37, 26, 12, 244, 92, 0, 253, 34, 140, 15, 127, 10, 165, 123, 10, 153, 68, 38, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 70, 37, 26, 12, 164, 46, 53, 131, 129, 6, 254, 32, 140, 15, 127, 58, 169, 167, 131, 129, 72, 165, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 165, 25, 12, 68, 82, 47, 1, 255, 34, 140, 15, 127, 66, 145, 123, 74, 153, 68, 38, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 70, 37, 26, 12, 164, 46, 53, 131, 129, 6, 0, 0, 0, 4, 255, 255, 1, 0, 39, 140, 15, 247, 193, 64, 15, 31, 172, 68, 26, 153, 68, 38, 145, 73, 100, 18, 153, 100, 112, 16, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 158, 11, 0, 1, 1, 31, 140, 15, 127, 78, 6, 3, 61, 122, 48, 200, 154, 138, 6, 3, 141, 74, 34, 147, 200, 36, 50, 141, 74, 52, 24, 232, 185, 0, 1, 2, 41, 140, 15, 31, 137, 76, 162, 27, 228, 65, 131, 149, 72, 35, 147, 200, 36, 50, 137, 76, 34, 147, 12, 14, 34, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 211, 115, 1, 1, 3, 34, 140, 15, 127, 74, 137, 76, 162, 27, 228, 241, 131, 65, 214, 84, 52, 24, 104, 84, 18, 153, 68, 38, 145, 105, 84, 162, 193, 64, 207, 5, 0, 1, 4, 39, 140, 15, 127, 102, 131, 149, 72, 35, 147, 200, 36, 50, 137, 76, 34, 147, 12, 14, 34, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 83, 14, 165, 218, 65, 24, 1, 5, 30, 140, 15, 127, 62, 28, 12, 178, 166, 162, 193, 64, 163, 146, 200, 36, 50, 137, 76, 163, 18, 13, 6, 74, 83, 237, 32, 12, 1, 6, 21, 140, 15, 63, 165, 61, 106, 48, 208, 40, 165, 254, 183, 218, 193, 64, 207, 5, 0, 1, 7, 21, 140, 15, 127, 10, 165, 123, 154, 193, 64, 163, 148, 250, 173, 118, 48, 208, 115, 1, 1, 8, 24, 140, 15, 167, 65, 78, 162, 18, 233, 33, 131, 129, 70, 41, 245, 191, 213, 14, 6, 122, 46, 0, 1, 9, 25, 140, 15, 127, 184, 114, 144, 147, 168, 68, 122, 248, 96, 160, 81, 74, 253, 86, 59, 24, 232, 185, 0, 1, 10, 21, 140, 15, 127, 136, 84, 143, 26, 12, 52, 74, 169, 255, 173, 118, 48, 208, 115, 1, 1, 11, 21, 140, 15, 127, 90, 169, 158, 124, 48, 208, 40, 165, 126, 171, 29, 12, 244, 92, 0, 1, 12, 24, 140, 15, 23, 145, 74, 162, 27, 228, 65, 131, 129, 70, 41, 245, 191, 213, 14, 6, 122, 46, 0, 1, 13, 25, 140, 15, 127, 172, 72, 37, 209, 13, 146, 122, 146, 193, 64, 163, 148, 250, 173, 118, 48, 208, 115, 1, 1, 14, 42, 140, 15, 23, 145, 74, 162, 27, 228, 33, 131, 129, 72, 165, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 165, 25, 12, 244, 220, 0, 1, 15, 36, 140, 15, 23, 145, 74, 162, 27, 228, 161, 30, 13, 6, 26, 149, 68, 38, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 26, 149, 104, 48, 208, 115, 1, 1, 16, 36, 140, 15, 127, 70, 131, 27, 213, 68, 38, 145, 73, 100, 18, 153, 68, 166, 24, 108, 36, 50, 137, 76, 34, 147, 200, 36, 50, 137, 106, 50, 184, 231, 4, 1, 17, 34, 140, 15, 127, 198, 186, 193, 82, 42, 26, 12, 52, 42, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 147, 200, 52, 42, 209, 96, 160, 231, 2, 1, 18, 25, 140, 15, 247, 193, 64, 15, 31, 12, 52, 74, 169, 167, 131, 129, 72, 234, 173, 118, 48, 208, 115, 1, 1, 19, 30, 140, 15, 127, 78, 6, 3, 61, 124, 48, 208, 168, 36, 50, 137, 76, 34, 147, 12, 14, 34, 82, 91, 237, 96, 160, 231, 2, 1, 20, 28, 140, 15, 31, 137, 76, 162, 27, 228, 65, 131, 129, 70, 41, 245, 116, 48, 16, 73, 189, 213, 14, 6, 122, 46, 0, 1, 21, 33, 140, 15, 127, 74, 137, 76, 162, 27, 228, 41, 6, 3, 141, 74, 34, 147, 200, 36, 50, 201, 224, 32, 34, 181, 213, 14, 6, 122, 46, 0, 1, 22, 25, 140, 15, 127, 136, 84, 143, 26, 12, 52, 74, 169, 167, 131, 129, 72, 234, 173, 118, 48, 208, 115, 1, 1, 23, 29, 140, 15, 127, 90, 169, 158, 124, 48, 208, 168, 36, 50, 137, 76, 34, 147, 12, 14, 34, 82, 91, 237, 96, 160, 231, 2, 1, 24, 25, 140, 15, 127, 102, 131, 129, 70, 41, 245, 116, 48, 16, 73, 189, 213, 14, 6, 74, 83, 237, 32, 12, 1, 25, 30, 140, 15, 127, 190, 28, 12, 52, 42, 137, 76, 34, 147, 200, 36, 131, 131, 136, 212, 86, 59, 24, 40, 77, 181, 131, 48, 0, 1, 26, 28, 140, 15, 23, 145, 74, 162, 27, 228, 65, 131, 129, 70, 41, 245, 116, 48, 16, 73, 189, 213, 14, 6, 122, 46, 0, 1, 27, 34, 140, 15, 127, 172, 72, 37, 209, 13, 146, 122, 146, 193, 64, 163, 146, 200, 36, 50, 137, 76, 50, 56, 136, 72, 109, 181, 131, 129, 158, 11, 0, 1, 28, 35, 140, 15, 167, 65, 78, 162, 18, 233, 33, 131, 129, 70, 41, 245, 205, 64, 34, 147, 200, 36, 50, 137, 76, 34, 211, 168, 68, 131, 129, 158, 11, 0, 1, 29, 42, 140, 15, 127, 184, 114, 144, 147, 168, 68, 122, 248, 96, 160, 81, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 166, 81, 137, 6, 131, 168, 86, 170, 20, 13, 6, 25, 0, 1, 30, 35, 140, 15, 31, 137, 76, 162, 27, 228, 65, 131, 129, 70, 41, 245, 205, 64, 34, 147, 200, 36, 50, 137, 76, 34, 211, 168, 68, 131, 129, 158, 11, 0, 1, 31, 41, 140, 15, 127, 74, 137, 76, 162, 27, 228, 41, 6, 3, 141, 74, 34, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 141, 74, 52, 24, 68, 181, 82, 165, 104, 48, 200, 0, 1, 32, 32, 140, 15, 127, 136, 84, 143, 26, 12, 52, 74, 169, 111, 6, 18, 153, 68, 38, 145, 73, 100, 18, 153, 70, 37, 26, 12, 244, 92, 0, 1, 33, 38, 140, 15, 127, 90, 169, 158, 124, 48, 208, 168, 36, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 211, 168, 68, 131, 65, 84, 43, 85, 138, 6, 131, 12, 0, 1, 34, 31, 140, 15, 127, 102, 131, 129, 70, 41, 245, 205, 64, 34, 147, 200, 36, 50, 137, 76, 34, 211, 168, 68, 131, 129, 78, 106, 105, 6, 1, 35, 39, 140, 15, 127, 140, 212, 210, 158, 102, 48, 208, 168, 36, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 211, 168, 68, 131, 65, 84, 43, 85, 138, 6, 131, 12, 0, 1, 36, 40, 140, 15, 167, 65, 78, 162, 18, 233, 101, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 38, 25, 28, 68, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 166, 231, 2, 1, 37, 35, 140, 15, 167, 65, 78, 162, 18, 233, 165, 78, 7, 3, 145, 74, 35, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 211, 115, 1, 1, 38, 37, 140, 15, 127, 70, 50, 137, 76, 49, 120, 33, 147, 200, 36, 50, 137, 76, 50, 56, 136, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 207, 5, 0, 1, 39, 34, 140, 15, 127, 70, 202, 193, 78, 42, 29, 12, 68, 42, 141, 76, 34, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 207, 5, 0, 1, 40, 23, 140, 15, 159, 141, 102, 48, 208, 104, 246, 144, 193, 64, 39, 245, 255, 110, 48, 208, 115, 2, 1, 41, 23, 140, 15, 127, 176, 72, 51, 200, 72, 44, 52, 154, 61, 126, 16, 245, 159, 14, 242, 220, 0, 1, 42, 20, 140, 15, 247, 193, 64, 143, 30, 12, 116, 82, 255, 239, 6, 3, 61, 39, 0, 1, 43, 18, 140, 15, 127, 78, 6, 3, 61, 124, 16, 245, 159, 14, 242, 220, 0, 1, 44, 22, 140, 15, 31, 137, 76, 162, 27, 228, 49, 131, 129, 78, 234, 255, 221, 96, 160, 231, 4, 1, 45, 21, 140, 15, 127, 74, 137, 76, 162, 27, 228, 41, 6, 81, 255, 233, 32, 207, 13, 0, 1, 46, 20, 140, 15, 127, 86, 131, 129, 78, 234, 255, 221, 96, 160, 52, 213, 14, 210, 0, 1, 47, 20, 140, 15, 127, 134, 82, 61, 201, 32, 234, 63, 29, 36, 77, 181, 131, 56, 0, 1, 48, 20, 140, 15, 127, 136, 84, 15, 26, 12, 116, 82, 255, 239, 6, 3, 61, 39, 0, 1, 49, 15, 140, 15, 127, 190, 28, 68, 253, 167, 131, 60, 55, 0, 1, 50, 39, 140, 15, 127, 54, 131, 204, 32, 162, 210, 168, 52, 42, 141, 74, 163, 210, 168, 52, 42, 141, 74, 163, 210, 168, 52, 42, 141, 74, 35, 154, 12, 18, 131, 60, 55, 0, 1, 51, 22, 140, 15, 127, 86, 34, 247, 216, 65, 200, 255, 255, 209, 32, 34, 117, 169, 27, 132, 0, 1, 52, 22, 140, 15, 167, 65, 78, 162, 18, 233, 17, 131, 129, 78, 234, 255, 114, 54, 208, 115, 6, 1, 53, 21, 140, 15, 127, 184, 114, 144, 147, 168, 68, 122, 10, 169, 255, 47, 117, 131, 28, 0, 1, 54, 36, 140, 15, 127, 70, 50, 137, 76, 34, 147, 200, 36, 50, 137, 74, 35, 18, 13, 6, 41, 145, 149, 70, 38, 145, 73, 100, 18, 153, 68, 102, 106, 105, 7, 1, 55, 28, 140, 15, 127, 86, 82, 143, 188, 81, 73, 100, 3, 157, 66, 39, 145, 105, 84, 34, 43, 141, 74, 39, 181, 52, 3, 1, 56, 25, 140, 15, 127, 62, 20, 121, 163, 146, 200, 20, 186, 65, 80, 161, 147, 200, 52, 42, 145, 123, 78, 0, 1, 57, 17, 140, 15, 63, 165, 61, 70, 234, 255, 173, 118, 48, 208, 115, 1, 1, 58, 16, 140, 15, 63, 165, 61, 76, 234, 255, 233, 129, 158, 19, 0, 1, 59, 17, 140, 15, 127, 70, 82, 255, 111, 181, 131, 129, 78, 106, 105, 6, 1, 60, 16, 140, 15, 127, 118, 82, 255, 79, 15, 132, 82, 75, 51, 0, 1, 61, 21, 140, 15, 23, 145, 74, 162, 27, 228, 33, 82, 255, 111, 181, 131, 129, 158, 11, 0, 1, 62, 19, 140, 15, 23, 145, 74, 162, 27, 228, 81, 82, 255, 79, 15, 244, 156, 0, 1, 63, 17, 140, 15, 127, 70, 82, 127, 228, 169, 91, 237, 96, 160, 231, 2, 1, 64, 18, 140, 15, 127, 118, 82, 127, 34, 147, 200, 164, 78, 15, 244, 156, 0, 1, 65, 21, 140, 15, 127, 70, 82, 47, 116, 131, 224, 82, 121, 234, 86, 59, 24, 232, 185, 0, 1, 66, 20, 140, 15, 127, 118, 82, 47, 116, 131, 224, 82, 121, 234, 244, 64, 207, 9, 0, 1, 67, 41, 140, 15, 55, 165, 61, 72, 38, 89, 73, 86, 146, 65, 72, 50, 8, 73, 20, 26, 137, 66, 35, 241, 55, 10, 137, 70, 33, 17, 13, 34, 162, 65, 68, 53, 81, 237, 185, 0, 1, 68, 32, 140, 15, 127, 10, 165, 123, 138, 193, 64, 164, 210, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 147, 200, 244, 92, 0, 1, 69, 41, 140, 15, 127, 70, 50, 201, 74, 178, 146, 12, 66, 146, 65, 72, 162, 208, 72, 20, 26, 137, 191, 81, 72, 52, 10, 137, 104, 16, 17, 13, 34, 170, 137, 106, 38, 181, 180, 3, 1, 70, 30, 140, 15, 127, 190, 27, 12, 68, 42, 141, 76, 34, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 137, 204, 212, 210, 14, 1, 71, 45, 140, 15, 23, 145, 74, 162, 27, 228, 33, 50, 201, 74, 178, 146, 12, 66, 146, 65, 72, 162, 208, 72, 20, 26, 137, 191, 81, 72, 52, 10, 137, 104, 16, 17, 13, 34, 170, 137, 106, 207, 5, 0, 1, 72, 36, 140, 15, 127, 172, 72, 37, 209, 13, 146, 122, 252, 96, 32, 82, 105, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 122, 46, 0, 1, 73, 32, 140, 15, 119, 169, 165, 61, 213, 96, 32, 82, 105, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 122, 46, 0, 1, 74, 41, 140, 15, 127, 70, 50, 201, 74, 178, 146, 12, 66, 146, 65, 72, 162, 208, 72, 20, 26, 137, 191, 81, 72, 52, 10, 137, 104, 16, 17, 13, 34, 170, 137, 106, 106, 41, 220, 3, 1, 75, 31, 140, 15, 127, 190, 27, 12, 68, 42, 141, 76, 34, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 106, 41, 220, 3, 1, 76, 38, 140, 15, 247, 193, 64, 15, 31, 172, 68, 26, 153, 68, 38, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 38, 145, 105, 68, 170, 193, 158, 27, 0, 1, 77, 32, 140, 15, 127, 78, 6, 3, 61, 124, 176, 18, 105, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 166, 17, 169, 6, 123, 110, 0, 1, 78, 40, 140, 15, 31, 137, 76, 162, 27, 228, 65, 131, 149, 72, 35, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 141, 72, 53, 216, 115, 3, 1, 79, 34, 140, 15, 127, 74, 137, 76, 162, 27, 228, 41, 6, 43, 145, 70, 38, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 26, 145, 106, 176, 231, 6, 1, 80, 40, 140, 15, 47, 137, 74, 162, 146, 232, 49, 131, 149, 72, 35, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 141, 72, 53, 216, 115, 3, 1, 81, 36, 140, 15, 127, 184, 68, 37, 81, 73, 84, 18, 61, 197, 96, 37, 210, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 35, 82, 13, 246, 220, 0, 1, 82, 37, 140, 15, 127, 102, 131, 129, 70, 162, 210, 168, 52, 42, 141, 74, 163, 210, 168, 52, 131, 140, 149, 70, 165, 81, 105, 84, 26, 153, 68, 55, 24, 232, 185, 0, 1, 83, 29, 140, 15, 127, 190, 92, 12, 50, 138, 133, 196, 159, 12, 38, 102, 18, 153, 68, 38, 209, 41, 118, 139, 65, 158, 11, 0, 1, 84, 39, 140, 15, 63, 165, 61, 102, 48, 16, 169, 52, 50, 137, 76, 34, 147, 200, 36, 42, 205, 96, 32, 82, 105, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 158, 11, 0, 1, 85, 19, 140, 15, 127, 10, 165, 123, 154, 193, 64, 163, 146, 72, 253, 123, 46, 1, 1, 86, 38, 140, 15, 127, 70, 131, 129, 72, 165, 145, 73, 100, 18, 153, 68, 38, 81, 105, 6, 3, 145, 74, 35, 147, 200, 36, 50, 137, 76, 34, 147, 200, 76, 45, 237, 0, 1, 87, 18, 140, 15, 127, 190, 28, 12, 52, 42, 137, 212, 63, 151, 90, 218, 1, 1, 88, 42, 140, 15, 23, 145, 74, 162, 27, 228, 33, 131, 129, 72, 165, 145, 73, 100, 18, 153, 68, 38, 81, 105, 6, 3, 145, 74, 35, 147, 200, 36, 50, 137, 76, 34, 147, 200, 244, 92, 0, 1, 89, 23, 140, 15, 127, 172, 72, 37, 209, 13, 146, 122, 146, 193, 64, 163, 146, 72, 253, 123, 46, 1, 1, 90, 24, 140, 15, 63, 165, 61, 106, 48, 208, 40, 165, 110, 181, 131, 173, 169, 75, 205, 96, 160, 231, 6, 1, 91, 22, 140, 15, 127, 10, 165, 123, 146, 193, 137, 212, 237, 96, 160, 149, 58, 25, 220, 115, 2, 1, 92, 28, 140, 15, 167, 65, 78, 162, 18, 233, 33, 131, 129, 70, 41, 117, 171, 29, 108, 77, 93, 106, 6, 3, 61, 55, 0, 1, 93, 26, 140, 15, 127, 184, 114, 144, 147, 168, 68, 122, 244, 224, 68, 234, 118, 48, 208, 74, 157, 12, 238, 57, 1, 1, 94, 24, 140, 15, 127, 102, 131, 129, 70, 41, 117, 171, 29, 108, 77, 93, 106, 6, 3, 161, 212, 210, 14, 1, 95, 21, 140, 15, 127, 62, 28, 156, 72, 221, 14, 6, 90, 169, 147, 193, 157, 212, 210, 14, 1, 96, 28, 140, 15, 23, 145, 74, 162, 27, 228, 65, 131, 129, 70, 41, 117, 171, 29, 108, 77, 93, 106, 6, 3, 61, 55, 0, 1, 97, 26, 140, 15, 127, 172, 72, 37, 209, 13, 146, 122, 138, 193, 137, 212, 237, 96, 160, 149, 58, 25, 220, 115, 2, 1, 98, 17, 140, 15, 127, 70, 131, 131, 152, 212, 255, 183, 82, 75, 51, 0, 1, 99, 20, 140, 15, 127, 118, 82, 135, 131, 65, 78, 234, 79, 15, 132, 82, 75, 51, 0, 1, 100, 20, 140, 15, 23, 145, 74, 162, 27, 228, 33, 131, 131, 152, 212, 255, 247, 92, 1, 1, 101, 23, 140, 15, 23, 145, 74, 162, 27, 228, 81, 82, 135, 131, 65, 78, 234, 79, 15, 244, 156, 0, 1, 102, 18, 140, 15, 127, 70, 131, 131, 152, 212, 135, 131, 161, 212, 239, 185, 2, 1, 103, 22, 140, 15, 127, 118, 82, 135, 131, 65, 78, 42, 28, 12, 114, 82, 79, 15, 244, 156, 0, 1, 104, 42, 140, 15, 159, 141, 102, 48, 208, 104, 246, 8, 153, 68, 38, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 70, 37, 26, 12, 244, 92, 0, 1, 105, 38, 140, 15, 127, 176, 72, 51, 200, 72, 44, 52, 154, 61, 90, 38, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 166, 81, 137, 6, 3, 61, 23, 0, 1, 106, 39, 140, 15, 247, 193, 64, 15, 150, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 38, 145, 105, 84, 162, 193, 64, 207, 5, 0, 1, 107, 33, 140, 15, 127, 78, 6, 3, 61, 88, 38, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 166, 81, 137, 6, 3, 61, 23, 0, 1, 108, 41, 140, 15, 31, 137, 76, 162, 27, 228, 33, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 141, 74, 52, 24, 232, 185, 0, 1, 109, 35, 140, 15, 127, 74, 137, 76, 162, 27, 228, 225, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 141, 74, 52, 24, 232, 185, 0, 1, 110, 42, 140, 15, 167, 65, 78, 34, 147, 232, 6, 41, 153, 68, 38, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 70, 37, 26, 12, 244, 92, 0, 1, 111, 37, 140, 15, 127, 244, 32, 39, 145, 73, 116, 131, 60, 92, 38, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 166, 81, 137, 6, 3, 61, 23, 0, 1, 112, 41, 140, 15, 47, 137, 74, 162, 146, 232, 17, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 141, 74, 52, 24, 232, 185, 0, 1, 113, 37, 140, 15, 127, 184, 68, 37, 81, 73, 84, 18, 61, 92, 38, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 166, 81, 137, 6, 3, 61, 23, 0, 1, 114, 39, 140, 15, 127, 70, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 141, 74, 52, 24, 40, 77, 181, 131, 48, 0, 1, 115, 32, 140, 15, 127, 190, 147, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 38, 145, 105, 84, 162, 193, 64, 105, 170, 29, 132, 1, 1, 116, 42, 140, 15, 167, 65, 78, 162, 18, 233, 101, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 196, 139, 65, 66, 50, 136, 12, 34, 163, 137, 76, 207, 5, 0, 1, 117, 29, 140, 15, 127, 184, 114, 144, 147, 168, 68, 122, 176, 76, 34, 147, 200, 36, 254, 255, 55, 10, 137, 72, 50, 200, 115, 1, 1, 118, 32, 140, 15, 167, 65, 78, 162, 18, 233, 101, 18, 153, 68, 38, 145, 73, 100, 18, 153, 70, 37, 26, 12, 164, 190, 156, 12, 238, 57, 1, 1, 119, 39, 140, 15, 127, 184, 114, 144, 147, 168, 68, 122, 176, 76, 34, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 163, 18, 13, 6, 82, 151, 154, 193, 64, 3, 1, 120, 29, 140, 15, 119, 145, 123, 153, 68, 38, 145, 73, 100, 18, 153, 68, 166, 81, 137, 6, 3, 169, 47, 39, 131, 123, 78, 0, 1, 121, 20, 140, 15, 63, 165, 61, 102, 112, 16, 117, 233, 159, 154, 14, 14, 242, 92, 0, 1, 122, 20, 140, 15, 127, 10, 165, 123, 138, 193, 65, 84, 233, 159, 14, 14, 242, 92, 0, 1, 123, 21, 140, 15, 127, 136, 84, 143, 25, 28, 68, 93, 250, 167, 166, 131, 131, 60, 23, 0, 1, 124, 20, 140, 15, 127, 90, 169, 158, 120, 112, 16, 85, 250, 167, 131, 131, 60, 23, 0, 1, 125, 23, 140, 15, 23, 145, 74, 162, 27, 228, 33, 131, 131, 168, 75, 255, 212, 116, 112, 144, 231, 2, 1, 126, 24, 140, 15, 127, 172, 72, 37, 209, 13, 146, 122, 252, 224, 32, 170, 244, 79, 7, 7, 121, 46, 0, 1, 127, 17, 140, 15, 127, 134, 3, 217, 82, 42, 28, 68, 253, 239, 57, 3, 1, 205, 41, 140, 15, 23, 145, 74, 162, 27, 228, 65, 131, 149, 72, 35, 147, 200, 36, 50, 137, 76, 34, 147, 12, 14, 34, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 211, 115, 1, 1, 206, 34, 140, 15, 127, 172, 72, 37, 209, 13, 146, 122, 138, 193, 32, 107, 42, 26, 12, 52, 42, 137, 76, 34, 147, 200, 52, 42, 209, 96, 160, 231, 2, 1, 207, 22, 140, 15, 23, 145, 74, 162, 27, 228, 49, 131, 129, 78, 234, 255, 221, 96, 160, 231, 4, 1, 208, 21, 140, 15, 127, 172, 72, 37, 209, 13, 146, 122, 146, 65, 212, 127, 58, 200, 115, 3, 1, 209, 40, 140, 15, 23, 145, 74, 162, 27, 228, 65, 131, 149, 72, 35, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 141, 72, 53, 216, 115, 3, 1, 210, 35, 140, 15, 127, 172, 72, 37, 209, 13, 146, 122, 146, 193, 74, 164, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 70, 164, 26, 236, 185, 1, 1, 211, 41, 140, 15, 23, 145, 74, 162, 27, 228, 33, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 141, 74, 52, 24, 232, 185, 0, 1, 212, 36, 140, 15, 127, 172, 72, 37, 209, 13, 146, 122, 188, 76, 34, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 163, 18, 13, 6, 122, 46, 0, 1, 226, 39, 140, 15, 247, 193, 64, 15, 31, 12, 52, 18, 149, 70, 165, 81, 105, 84, 26, 149, 70, 53, 184, 177, 210, 168, 52, 42, 141, 74, 163, 210, 168, 52, 3, 61, 23, 0, 1, 227, 35, 140, 15, 127, 78, 6, 3, 61, 122, 50, 72, 13, 18, 50, 137, 76, 34, 26, 12, 52, 10, 153, 68, 38, 145, 73, 116, 138, 221, 98, 144, 231, 2, 1, 230, 35, 140, 15, 23, 145, 74, 162, 27, 228, 65, 131, 129, 70, 41, 245, 205, 64, 34, 147, 200, 36, 50, 137, 76, 34, 211, 168, 68, 131, 129, 158, 11, 0, 1, 231, 42, 140, 15, 127, 172, 72, 37, 209, 13, 146, 122, 146, 193, 64, 163, 146, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 163, 18, 13, 6, 81, 173, 84, 41, 26, 12, 50, 0, 1, 232, 40, 140, 15, 23, 145, 74, 162, 27, 228, 33, 50, 137, 76, 34, 147, 200, 36, 50, 137, 74, 35, 18, 13, 6, 41, 145, 149, 70, 38, 145, 73, 100, 18, 153, 68, 166, 231, 2, 1, 233, 32, 140, 15, 23, 145, 74, 162, 27, 228, 49, 82, 143, 188, 81, 73, 100, 3, 157, 66, 39, 145, 105, 84, 34, 43, 141, 74, 207, 5, 0, 1, 234, 38, 140, 15, 127, 102, 131, 149, 72, 35, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 141, 72, 53, 24, 42, 165, 218, 65, 30, 1, 235, 32, 140, 15, 127, 190, 28, 172, 68, 26, 153, 68, 38, 145, 73, 100, 18, 153, 68, 38, 145, 105, 68, 170, 193, 80, 41, 213, 14, 242, 0, 1, 236, 41, 140, 15, 247, 193, 64, 15, 31, 172, 68, 26, 153, 68, 38, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 38, 145, 105, 68, 170, 193, 80, 41, 213, 14, 242, 0, 1, 237, 35, 140, 15, 127, 78, 6, 3, 61, 124, 176, 18, 105, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 166, 17, 169, 6, 67, 165, 84, 59, 200, 3, 1, 240, 21, 140, 15, 127, 172, 72, 37, 209, 13, 146, 122, 34, 169, 255, 47, 117, 131, 28, 0, 1, 244, 31, 140, 15, 63, 165, 61, 106, 48, 208, 40, 165, 190, 25, 72, 100, 18, 153, 68, 38, 145, 73, 100, 26, 149, 104, 48, 208, 115, 1, 1, 245, 38, 140, 15, 127, 10, 165, 123, 154, 193, 64, 163, 146, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 163, 18, 13, 6, 81, 173, 84, 41, 26, 12, 50, 0, 1, 252, 38, 140, 15, 63, 165, 61, 106, 48, 208, 72, 84, 26, 149, 70, 165, 81, 105, 84, 26, 213, 224, 198, 74, 163, 210, 168, 52, 42, 141, 74, 163, 210, 12, 244, 92, 0, 1, 253, 34, 140, 15, 127, 10, 165, 123, 146, 131, 212, 32, 33, 147, 200, 36, 162, 193, 64, 163, 144, 73, 100, 18, 153, 68, 167, 216, 45, 6, 121, 46, 0, 1, 254, 44, 140, 15, 63, 165, 185, 104, 48, 208, 136, 52, 170, 137, 104, 16, 17, 13, 34, 26, 133, 68, 163, 144, 248, 11, 141, 68, 161, 145, 12, 66, 146, 65, 72, 35, 18, 13, 6, 33, 169, 158, 1, 1, 255, 37, 140, 15, 127, 10, 165, 123, 120, 102, 176, 144, 136, 38, 170, 137, 104, 16, 209, 40, 36, 94, 104, 36, 131, 144, 100, 37, 25, 73, 20, 131, 77, 158, 57, 0, 2, 0, 41, 140, 15, 15, 137, 78, 162, 147, 232, 49, 131, 149, 72, 35, 147, 200, 36, 50, 137, 76, 34, 147, 12, 14, 34, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 211, 115, 1, 2, 1, 35, 140, 15, 127, 168, 68, 39, 209, 73, 116, 18, 61, 122, 48, 200, 154, 138, 6, 3, 141, 74, 34, 147, 200, 36, 50, 141, 74, 52, 24, 232, 185, 0, 2, 2, 41, 140, 15, 167, 65, 78, 34, 147, 232, 49, 131, 149, 72, 35, 147, 200, 36, 50, 137, 76, 34, 147, 12, 14, 34, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 211, 115, 1, 2, 3, 34, 140, 15, 127, 210, 65, 78, 34, 147, 232, 225, 131, 65, 214, 84, 52, 24, 104, 84, 18, 153, 68, 38, 145, 105, 84, 162, 193, 64, 207, 5, 0, 2, 4, 28, 140, 15, 15, 137, 78, 162, 147, 232, 49, 131, 129, 70, 41, 245, 116, 48, 16, 73, 189, 213, 14, 6, 122, 46, 0, 2, 5, 34, 140, 15, 127, 168, 68, 39, 209, 73, 116, 18, 61, 124, 48, 208, 168, 36, 50, 137, 76, 34, 147, 12, 14, 34, 82, 91, 237, 96, 160, 231, 2, 2, 6, 28, 140, 15, 167, 65, 78, 34, 147, 232, 49, 131, 129, 70, 41, 245, 116, 48, 16, 73, 189, 213, 14, 6, 122, 46, 0, 2, 7, 33, 140, 15, 127, 210, 65, 78, 34, 147, 232, 241, 131, 129, 70, 37, 145, 73, 100, 18, 153, 100, 112, 16, 145, 218, 106, 7, 3, 61, 23, 0, 2, 8, 22, 140, 15, 15, 137, 78, 162, 147, 232, 33, 131, 129, 78, 234, 255, 221, 96, 160, 231, 4, 2, 9, 22, 140, 15, 127, 168, 68, 39, 209, 73, 116, 18, 61, 124, 16, 245, 159, 14, 242, 220, 0, 2, 10, 22, 140, 15, 167, 65, 78, 34, 147, 232, 33, 131, 129, 78, 234, 255, 221, 96, 160, 231, 4, 2, 11, 20, 140, 15, 127, 210, 65, 78, 34, 147, 232, 241, 131, 168, 255, 116, 144, 231, 6, 2, 12, 40, 140, 15, 15, 137, 78, 162, 147, 232, 49, 131, 149, 72, 35, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 141, 72, 53, 216, 115, 3, 2, 13, 36, 140, 15, 127, 168, 68, 39, 209, 73, 116, 18, 61, 124, 176, 18, 105, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 166, 17, 169, 6, 123, 110, 0, 2, 14, 40, 140, 15, 167, 65, 78, 34, 147, 232, 49, 131, 149, 72, 35, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 141, 72, 53, 216, 115, 3, 2, 15, 34, 140, 15, 127, 210, 65, 78, 34, 147, 232, 241, 131, 149, 72, 35, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 141, 72, 53, 216, 115, 3, 2, 16, 42, 140, 15, 15, 137, 78, 162, 147, 232, 17, 131, 129, 72, 165, 145, 73, 100, 18, 153, 68, 38, 81, 105, 6, 3, 145, 74, 35, 147, 200, 36, 50, 137, 76, 34, 147, 200, 244, 92, 0, 2, 17, 24, 140, 15, 127, 168, 68, 39, 209, 73, 116, 18, 61, 124, 48, 208, 168, 36, 82, 255, 158, 75, 0, 2, 18, 42, 140, 15, 167, 65, 78, 34, 147, 232, 17, 131, 129, 72, 165, 145, 73, 100, 18, 153, 68, 38, 81, 105, 6, 3, 145, 74, 35, 147, 200, 36, 50, 137, 76, 34, 147, 200, 244, 92, 0, 2, 19, 22, 140, 15, 127, 210, 65, 78, 34, 147, 232, 241, 131, 129, 70, 37, 145, 250, 247, 92, 2, 2, 20, 41, 140, 15, 15, 137, 78, 162, 147, 232, 17, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 141, 74, 52, 24, 232, 185, 0, 2, 21, 37, 140, 15, 127, 168, 68, 39, 209, 73, 116, 18, 61, 88, 38, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 166, 81, 137, 6, 3, 61, 23, 0, 2, 22, 41, 140, 15, 167, 65, 78, 34, 147, 232, 17, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 141, 74, 52, 24, 232, 185, 0, 2, 23, 35, 140, 15, 127, 210, 65, 78, 34, 147, 232, 209, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 147, 200, 36, 50, 141, 74, 52, 24, 232, 185, 0, 2, 30, 41, 140, 15, 23, 145, 74, 162, 27, 228, 33, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 147, 12, 14, 34, 50, 137, 76, 34, 147, 200, 36, 50, 137, 76, 34, 211, 115, 1, 2, 31, 36, 140, 15, 23, 145, 74, 162, 27, 228, 33, 82, 167, 131, 129, 72, 165, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 38, 145, 233, 185, 0, 2, 38, 39, 140, 15, 127, 136, 84, 143, 26, 172, 68, 26, 153, 68, 38, 145, 73, 100, 18, 153, 100, 112, 16, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 158, 11, 0, 2, 39, 30, 140, 15, 127, 90, 169, 158, 122, 48, 200, 154, 138, 6, 3, 141, 74, 34, 147, 200, 36, 50, 141, 74, 52, 24, 232, 185, 0, 2, 40, 25, 140, 15, 127, 102, 131, 129, 70, 41, 245, 116, 48, 16, 73, 189, 213, 14, 6, 58, 169, 165, 25, 0, 2, 41, 29, 140, 15, 127, 190, 28, 12, 52, 42, 137, 76, 34, 147, 200, 36, 131, 131, 136, 212, 86, 59, 24, 232, 164, 150, 102, 0, 2, 46, 38, 140, 15, 127, 136, 84, 143, 26, 172, 68, 26, 153, 68, 38, 145, 73, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 38, 145, 105, 68, 170, 193, 158, 27, 0, 2, 47, 31, 140, 15, 127, 90, 169, 158, 124, 176, 18, 105, 100, 18, 153, 68, 38, 145, 73, 100, 18, 153, 68, 166, 17, 169, 6, 123, 110, 0, 32, 172, 30, 140, 15, 127, 134, 131, 156, 68, 37, 114, 35, 21, 14, 6, 50, 169, 112, 48, 144, 73, 181, 34, 87, 18, 221, 32, 207, 12, 0, 0];
+
+// src/font/types.ts
+class MonoDisplayFont {
+ FONT_DATA;
+ constructor(font_data) {
+ this.FONT_DATA = new Uint8Array(font_data.length);
+ for (let i = 0;i < font_data.length; i++) {
+ this.FONT_DATA[i] = font_data[i] || 0;
+ }
+ }
+}
+// src/font/u8g2.ts
+var HDR_SIZE = 23;
+
+class BitReader {
+ data;
+ ptr;
+ bitPos;
+ constructor(data, startByte) {
+ this.data = data;
+ this.ptr = startByte;
+ this.bitPos = 0;
+ }
+ read(cnt) {
+ let val = this.data[this.ptr] >> this.bitPos & 255;
+ const newPos = this.bitPos + cnt;
+ if (newPos >= 8) {
+ this.ptr++;
+ val |= this.data[this.ptr] << 8 - this.bitPos;
+ this.bitPos = newPos - 8;
+ } else {
+ this.bitPos = newPos;
+ }
+ return val & (1 << cnt) - 1;
+ }
+ readSigned(cnt) {
+ return this.read(cnt) - (1 << cnt - 1);
+ }
+}
+function readU16BE(data, off) {
+ return (data[off] << 8 | data[off + 1]) >>> 0;
+}
+function parseHeader(font) {
+ return {
+ m0: font[2],
+ m1: font[3],
+ bitsPerW: font[4],
+ bitsPerH: font[5],
+ bitsPerX: font[6],
+ bitsPerY: font[7],
+ bitsPerDx: font[8],
+ ascentA: font[13],
+ startUpper: HDR_SIZE + readU16BE(font, 17),
+ startLower: HDR_SIZE + readU16BE(font, 19),
+ startUnicode: HDR_SIZE + readU16BE(font, 21)
+ };
+}
+function decodeRLE(br, w, h, m0, m1) {
+ const total = w * h;
+ const bits = new Uint8Array(total);
+ let filled = 0;
+ while (filled < total) {
+ const zeros = br.read(m0);
+ const ones = br.read(m1);
+ let reps = 1;
+ while (br.read(1) === 1)
+ reps++;
+ for (let r = 0;r < reps && filled < total; r++) {
+ for (let p = 0;p < zeros && filled < total; p++)
+ bits[filled++] = 0;
+ for (let p = 0;p < ones && filled < total; p++)
+ bits[filled++] = 1;
+ }
+ }
+ return bits;
+}
+function decodeGlyphAt(font, byteOffset, hdr) {
+ const br = new BitReader(font, byteOffset);
+ const w = br.read(hdr.bitsPerW);
+ const h = br.read(hdr.bitsPerH);
+ br.readSigned(hdr.bitsPerX);
+ const yB = br.readSigned(hdr.bitsPerY);
+ const dx = br.readSigned(hdr.bitsPerDx);
+ if (w === 0 || h === 0) {
+ return { cols: new Uint8Array(1), w: 1, h: 0, dx: Math.max(dx, 1), yOff: 0 };
+ }
+ const bits = decodeRLE(br, w, h, hdr.m0, hdr.m1);
+ const cols = new Uint32Array(w);
+ for (let row = 0;row < h; row++) {
+ for (let col = 0;col < w; col++) {
+ if (bits[row * w + col])
+ cols[col] |= 1 << row;
+ }
+ }
+ const yOff = hdr.ascentA - yB - h;
+ return { cols, w, h, dx, yOff };
+}
+function u8g2Glyph(cp, font) {
+ if (font.length < HDR_SIZE)
+ return null;
+ const hdr = parseHeader(font);
+ try {
+ if (cp >= 256) {
+ const tableBase = hdr.startUnicode;
+ if (tableBase + 4 > font.length)
+ return null;
+ let glyphStart = tableBase;
+ let pos = tableBase;
+ while (pos + 4 <= font.length) {
+ const delta = readU16BE(font, pos);
+ const lastEncoding = readU16BE(font, pos + 2);
+ pos += 4;
+ if (lastEncoding === 65535) {
+ glyphStart = tableBase + delta;
+ break;
+ }
+ if (lastEncoding >= cp) {
+ glyphStart = tableBase + delta;
+ break;
+ }
+ }
+ let recPos = glyphStart;
+ while (recPos + 3 <= font.length) {
+ const recStart = recPos;
+ const encoding = readU16BE(font, recPos);
+ const jump = font[recPos + 2];
+ if (jump === 0)
+ break;
+ recPos += 3;
+ if (encoding === cp)
+ return decodeGlyphAt(font, recPos, hdr);
+ recPos = recStart + jump;
+ }
+ return null;
+ } else {
+ let recPos;
+ if (cp >= 97)
+ recPos = hdr.startLower;
+ else if (cp >= 65)
+ recPos = hdr.startUpper;
+ else
+ recPos = HDR_SIZE;
+ while (recPos + 2 <= font.length) {
+ const recStart = recPos;
+ const encoding = font[recPos];
+ const jump = font[recPos + 1];
+ if (jump === 0)
+ break;
+ recPos += 2;
+ if (encoding === cp)
+ return decodeGlyphAt(font, recPos, hdr);
+ recPos = recStart + jump;
+ }
+ return null;
+ }
+ } catch {
+ return null;
+ }
+}
+function rasterizeText(text, cellHeight, font) {
+ const hdr = parseHeader(font.FONT_DATA);
+ const glyphs = [];
+ for (const char of text) {
+ const cp = char.codePointAt(0) ?? 63;
+ const g = u8g2Glyph(cp, font.FONT_DATA);
+ if (g)
+ glyphs.push(g);
+ }
+ if (glyphs.length === 0) {
+ return { pixels: new Uint8Array(cellHeight), width: 1, height: cellHeight };
+ }
+ const totalW = glyphs.reduce((sum, g, i) => sum + (i < glyphs.length - 1 ? g.dx : g.w), 0);
+ const pixels = new Uint8Array(totalW * cellHeight);
+ const descent = font.FONT_DATA[14] > 127 ? font.FONT_DATA[14] - 256 : font.FONT_DATA[14];
+ const fontH = hdr.ascentA - descent;
+ const topPad = Math.floor((cellHeight - fontH) / 2);
+ const baseline = topPad + hdr.ascentA;
+ let x = 0;
+ for (const g of glyphs) {
+ const top = baseline - hdr.ascentA + g.yOff;
+ for (let col = 0;col < g.w; col++) {
+ const colByte = g.cols[col] ?? 0;
+ for (let row = 0;row < g.h; row++) {
+ if (colByte >> row & 1) {
+ const py = top + row;
+ if (py >= 0 && py < cellHeight) {
+ pixels[py * totalW + x + col] = 1;
+ }
+ }
+ }
+ }
+ x += g.dx;
+ }
+ return { pixels, width: totalW, height: cellHeight };
+}
+
+// src/font/index.ts
+var u8g2_font_NokiaSmallPlain_tf = new MonoDisplayFont(u8g2_font_NokiaSmallPlain_tf_u8g2font);
+var u8g2_font_5x7_mf = new MonoDisplayFont(u8g2_font_5x7_mf_u8g2font);
+var u8g2_font_5x7_tf = new MonoDisplayFont(u8g2_font_5x7_tf_u8g2font);
+var u8g2_font_HelvetiPixel_tr = new MonoDisplayFont(u8g2_font_HelvetiPixel_tr_u8g2font);
+var u8g2_font_spleen12x24_me = new MonoDisplayFont(u8g2_font_spleen12x24_me_u8g2font);
+var u8g2_font_smolfont_tf = new MonoDisplayFont(u8g2_font_smolfont_tf_u8g2font);
+var u8g2_font_micropixel_tf = new MonoDisplayFont(u8g2_font_micropixel_tf_u8g2font);
+var u8g2_font_micropixel_tr = new MonoDisplayFont(u8g2_font_micropixel_tr_u8g2font);
+
+// src/renderer.ts
+class MonoDisplayRenderer {
+ ctx;
+ opts;
+ tickTimer = null;
+ tickCount = 0;
+ animState = new Map;
+ hScrollPos = new Map;
+ vScrollPos = new Map;
+ customFonts = [];
+ textCache = new Map;
+ static builtinFonts = {
+ NokiaSmallPlain_tf: u8g2_font_NokiaSmallPlain_tf,
+ "5x7_mf": u8g2_font_5x7_mf
+ };
+ constructor(canvas, opts) {
+ const ctx = canvas.getContext("2d");
+ if (!ctx)
+ throw new Error("Cannot get 2D canvas context");
+ this.ctx = ctx;
+ this.opts = opts;
+ }
+ stop() {
+ if (this.tickTimer !== null) {
+ clearInterval(this.tickTimer);
+ this.tickTimer = null;
+ }
+ this.tickCount = 0;
+ this.animState.clear();
+ this.hScrollPos.clear();
+ this.vScrollPos.clear();
+ this.textCache.clear();
+ }
+ render(file) {
+ this.stop();
+ const { displayWidth: dw, displayHeight: dh, scale: s } = this.opts;
+ this.ctx.canvas.width = dw * s;
+ this.ctx.canvas.height = dh * s;
+ this.customFonts = [];
+ for (const section of file.sections) {
+ if (section.sectionType === 32 /* CustomFont */) {
+ this.customFonts.push(section.fontData);
+ }
+ }
+ const tick = () => {
+ this.#renderFile(file);
+ this.tickCount++;
+ };
+ tick();
+ this.tickTimer = setInterval(tick, 1000 / this.opts.fps);
+ }
+ #renderFile(file) {
+ const now = BigInt(Math.floor(this.opts.now().getTime() / 1000));
+ let cleared = false;
+ let elementId = 0;
+ this.ctx.fillStyle = this.opts.offColor;
+ this.ctx.fillRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
+ for (const section of file.sections) {
+ if (section.sectionType === 2 /* ElementsTimespan */) {
+ if (now < section.startTimestamp || now >= section.endTimestamp) {
+ elementId += section.elements.length;
+ continue;
+ }
+ }
+ if (section.sectionType !== 1 /* ElementsAlways */ && section.sectionType !== 2 /* ElementsTimespan */) {
+ continue;
+ }
+ const elSection = section;
+ if (elSection.flags.clearBuffer && !cleared) {
+ this.ctx.fillStyle = this.opts.offColor;
+ this.ctx.fillRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
+ cleared = true;
+ }
+ for (const el of elSection.elements) {
+ this.#renderElement(el, elementId++);
+ }
+ }
+ }
+ #renderElement(el, id) {
+ switch (el.type) {
+ case 1 /* Image2D */:
+ this.#blitImage(el.image, el.xOffset, el.yOffset);
+ break;
+ case 2 /* Animation */:
+ this.#renderAnimation(el, id);
+ break;
+ case 3 /* HorizontalScroll */:
+ this.#renderHScroll(el, id);
+ break;
+ case 4 /* VerticalScroll */:
+ this.#renderVScroll(el, id);
+ break;
+ case 5 /* Line */:
+ this.#renderLine(el);
+ break;
+ case 16 /* ClippedText */:
+ this.#renderClippedText(el);
+ break;
+ case 17 /* HScrollText */:
+ this.#renderHScrollText(el, id);
+ break;
+ case 32 /* CurrentTime */:
+ this.#renderCurrentTime(el);
+ break;
+ }
+ }
+ #blitImage(img, ox, oy) {
+ const { ctx, opts: { onColor, scale: s } } = this;
+ ctx.fillStyle = onColor;
+ for (let y = 0;y < img.height; y++) {
+ for (let x = 0;x < img.width; x++) {
+ if (img.pixels[y * img.width + x]) {
+ ctx.fillRect((ox + x) * s, (oy + y) * s, s, s);
+ }
+ }
+ }
+ }
+ #blitClipped(img, ox, oy, viewW, viewH, srcX, srcY) {
+ const { ctx, opts: { onColor, scale: s } } = this;
+ ctx.fillStyle = onColor;
+ for (let y = 0;y < viewH; y++) {
+ for (let x = 0;x < viewW; x++) {
+ const sx = Math.floor(srcX) + x;
+ const sy = Math.floor(srcY) + y;
+ if (sx < 0 || sx >= img.width || sy < 0 || sy >= img.height)
+ continue;
+ if (img.pixels[sy * img.width + sx]) {
+ ctx.fillRect((ox + x) * s, (oy + y) * s, s, s);
+ }
+ }
+ }
+ }
+ #renderAnimation(el, id) {
+ if (el.frames.length === 0)
+ return;
+ let state = this.animState.get(id);
+ if (!state) {
+ state = { frame: 0, counter: 0 };
+ this.animState.set(id, state);
+ }
+ this.#blitImage(el.frames[state.frame], el.xOffset, el.yOffset);
+ state.counter++;
+ if (state.counter > el.updateInterval) {
+ state.counter = 0;
+ state.frame = (state.frame + 1) % el.frames.length;
+ if (!this.opts.loop && state.frame === 0)
+ state.frame = el.frames.length - 1;
+ }
+ }
+ #renderHScroll(el, id) {
+ let pos = this.hScrollPos.get(id) ?? 0;
+ this.#blitClipped(el.content, el.xOffset, el.yOffset, el.width, el.height, pos, 0);
+ const speed = (el.scrollSpeed + 1) / 16;
+ pos += el.flags.invertDirection ? -speed : speed;
+ if (el.flags.endless) {
+ if (pos >= el.contentWidth)
+ pos -= el.contentWidth;
+ if (pos < 0)
+ pos += el.contentWidth;
+ } else {
+ pos = Math.max(0, Math.min(pos, el.contentWidth - el.width));
+ }
+ this.hScrollPos.set(id, pos);
+ }
+ #renderVScroll(el, id) {
+ let pos = this.vScrollPos.get(id) ?? 0;
+ this.#blitClipped(el.content, el.xOffset, el.yOffset, el.width, el.height, 0, pos);
+ const speed = (el.scrollSpeed + 1) / 16;
+ pos += el.flags.invertDirection ? -speed : speed;
+ if (el.flags.endless) {
+ if (pos >= el.contentHeight)
+ pos -= el.contentHeight;
+ if (pos < 0)
+ pos += el.contentHeight;
+ } else {
+ pos = Math.max(0, Math.min(pos, el.contentHeight - el.height));
+ }
+ this.vScrollPos.set(id, pos);
+ }
+ #renderLine(el) {
+ const { ctx, opts: { onColor, offColor, scale: s } } = this;
+ ctx.fillStyle = el.invertPixels ? offColor : onColor;
+ let [x0, y0, x1, y1] = [el.xOrigin, el.yOrigin, el.xTarget, el.yTarget];
+ const dx = Math.abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
+ const dy = -Math.abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
+ let err = dx + dy;
+ while (true) {
+ ctx.fillRect(x0 * s, y0 * s, s, s);
+ if (x0 === x1 && y0 === y1)
+ break;
+ const e2 = 2 * err;
+ if (e2 >= dy) {
+ err += dy;
+ x0 += sx;
+ }
+ if (e2 <= dx) {
+ err += dx;
+ y0 += sy;
+ }
+ }
+ }
+ #getTextImage(text, height, fontIndex) {
+ const key = `${text}|${fontIndex}|${height}`;
+ const cached = this.textCache.get(key);
+ if (cached)
+ return cached;
+ fontIndex = Math.min(fontIndex, Object.keys(MonoDisplayRenderer.builtinFonts).length - 1);
+ const builtinFont = Object.values(MonoDisplayRenderer.builtinFonts).at(fontIndex) || u8g2_font_NokiaSmallPlain_tf;
+ const customFont = this.customFonts[fontIndex - 32768];
+ const selectedFont = fontIndex >= 32768 ? customFont || builtinFont : builtinFont;
+ const img = rasterizeText(text, height, selectedFont);
+ this.textCache.set(key, img);
+ return img;
+ }
+ #renderClippedText(el) {
+ const img = this.#getTextImage(el.text, el.height, el.fontIndex);
+ this.#blitClipped(img, el.xOffset, el.yOffset, el.width, el.height, 0, 0);
+ }
+ #renderHScrollText(el, id) {
+ const img = this.#getTextImage(el.text, el.height, el.fontIndex);
+ let pos = this.hScrollPos.get(id) ?? 0;
+ this.#blitClipped(img, el.xOffset, el.yOffset, el.width, el.height, pos, 0);
+ const speed = (el.scrollSpeed + 1) / 16;
+ pos += el.flags.invertDirection ? -speed : speed;
+ if (el.flags.endless) {
+ if (pos >= img.width)
+ pos -= img.width;
+ if (pos < 0)
+ pos += img.width;
+ } else {
+ pos = Math.max(0, Math.min(pos, img.width - el.width));
+ }
+ this.hScrollPos.set(id, pos);
+ }
+ #renderCurrentTime(el) {
+ const d = new Date(this.opts.now().getTime() + el.utcOffsetMinutes * 60000);
+ const { clock12h, showHours, showMinutes, showSeconds } = el.flags;
+ const parts = [];
+ if (showHours) {
+ const h = d.getUTCHours();
+ parts.push(String(clock12h ? h % 12 || 12 : h).padStart(2, "0"));
+ }
+ if (showMinutes)
+ parts.push(String(d.getUTCMinutes()).padStart(2, "0"));
+ if (showSeconds)
+ parts.push(String(d.getUTCSeconds()).padStart(2, "0"));
+ const text = parts.join(":");
+ if (!text)
+ return;
+ const img = this.#getTextImage(text, el.height, el.fontIndex);
+ this.#blitClipped(img, el.xOffset, el.yOffset, el.width, el.height, 0, 0);
+ }
+}
+// src/driver.ts
+class MonoDisplayDriver {
+ canvas;
+ opts;
+ parser;
+ renderer = null;
+ constructor(canvasId, options = {}) {
+ const el = document.getElementById(canvasId);
+ if (!el || el.tagName !== "CANVAS")
+ throw new Error(`#${canvasId} is not a