better css; image rotation; lolcat i18n; update about

the works are still incomplete. expecting more code cleaning
This commit is contained in:
NaitLee 2022-07-10 01:31:23 +08:00
parent 947cb9a5b9
commit 502a572183
13 changed files with 423 additions and 173 deletions

1
TODO
View File

@ -12,6 +12,7 @@ Note: not ordered. do whatever I/you want
+ Make a build guide for android: + Make a build guide for android:
Summary the hacks to p4a, bleak p4a recipe, p4a webview bootstrap, and AdvancedWebView Summary the hacks to p4a, bleak p4a recipe, p4a webview bootstrap, and AdvancedWebView
+ Try to implement enough without more dependencies + Try to implement enough without more dependencies
+ More funny i18n
+ Arch Linux package / AUR, package for other distros + Arch Linux package / AUR, package for other distros
+ Service for other init systems (a systemd unit file is there) + Service for other init systems (a systemd unit file is there)
+ ... + ...

View File

@ -153,3 +153,34 @@ That may involve another (partial) rewrite.
Afraid not, experiment always worth it. And remember: it's all about idea, everyone can make use of then. Afraid not, experiment always worth it. And remember: it's all about idea, everyone can make use of then.
Yawn... bed time... Yawn... bed time...
9th
Did some stylesheet fix.
Then go for image rotation. It shouldn't be that hard:
0 1 2 3 16 17 18 19 32 33 34 35 48 49 50 51
4 5 6 7 20 21 22 23 36 37 38 39 52 53 54 55
8 9 10 11 24 25 26 27 40 41 42 43 56 57 58 59
12 13 14 15 28 29 30 31 44 45 46 47 60 61 62 63
16 17 18 19 32 33 34 35 48 49 50 51 64 65 66 67
20 21 22 23 36 37 38 39 52 53 54 55 68 69 70 71
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
Think about HTML canvas ImageData, one dimensional [R,G,B,A,...] array.
The "big" problem is just make a procedure to transform from the first matrix to the second.
(and flip the result horizontally)
Know what happened? I produced a right procedure from very start, but the image screwed up.
Tried to "fix" it, used at least 4 hours, finally found it's a matter of didn't floor a floating number.
(Height is got by multiplying aspect ratio)
So the Internet JavaScript memes are damned true.
https://programmerhumor.io/javascript-memes/why-is-it-like-this-2/
https://programmerhumor.io/javascript-memes/sorry-dad-_-2/

View File

@ -635,7 +635,7 @@ def _main():
parser.add_argument('-f', '--fake', metavar='XY01', type=str, default='', parser.add_argument('-f', '--fake', metavar='XY01', type=str, default='',
help=i18n('virtual-run-on-specified-model')) help=i18n('virtual-run-on-specified-model'))
parser.add_argument('-m', '--dump', action='store_true', parser.add_argument('-m', '--dump', action='store_true',
help=i18n('dump-the-traffic')) help=i18n('dump-traffic'))
parser.add_argument('-n', '--nothing', action='store_true', parser.add_argument('-n', '--nothing', action='store_true',
help=i18n('do-nothing')) help=i18n('do-nothing'))

View File

@ -1 +1 @@
0.5.2 0.6.0.1

View File

@ -23,6 +23,7 @@
<a target="_blank" href="https://github.com/NaitLee">NaitLee</a> <a target="_blank" href="https://github.com/NaitLee">NaitLee</a>
</dt> </dt>
<dd data-i18n="developer">Developer</dd> <dd data-i18n="developer">Developer</dd>
<dd data-i18n="translator">Translator</dd>
</dl> </dl>
<dl> <dl>
<dt> <dt>
@ -32,7 +33,19 @@
<dd data-i18n="translator">Translator</dd> <dd data-i18n="translator">Translator</dd>
</dl> </dl>
<dl> <dl>
<dt data-i18n="all-testers-and-users">All testers & users</dt> <dt>
<a target="_blank" href="https://github.com/andypiper">andypiper</a>
</dt>
<dd data-i18n="minor-tweaks">Minor Tweaks</dd>
</dl>
<dl>
<dt>
<a target="_blank" href="https://github.com/sync1211">sync1211</a>
</dt>
<dd data-i18n="developer">Developer</dd>
</dl>
<dl>
<dt data-i18n="all-users-and-developers">All testers & users</dt>
<dd data-i18n="everyone-is-awesome">Everyone is awesome!</dd> <dd data-i18n="everyone-is-awesome">Everyone is awesome!</dd>
</dl> </dl>
</div> </div>

View File

@ -26,14 +26,14 @@
<span class="label-span-input"> <span class="label-span-input">
<span data-i18n="process-as-">Process as:</span> <span data-i18n="process-as-">Process as:</span>
<span> <span>
<label>
<input type="radio" name="algo" value="algo-direct" data-key />
<span data-i18n="text">Text</span>
</label>
<label> <label>
<input type="radio" name="algo" value="algo-steinberg" data-key checked /> <input type="radio" name="algo" value="algo-steinberg" data-key checked />
<span data-i18n="picture">Picture</span> <span data-i18n="picture">Picture</span>
</label> </label>
<label>
<input type="radio" name="algo" value="algo-direct" data-key />
<span data-i18n="text">Text</span>
</label>
</span> </span>
</span> </span>
<!-- "brightness" is historically "threshold" --> <!-- "brightness" is historically "threshold" -->
@ -52,6 +52,10 @@
<input type="range" min="24" max="36" value="32" step="4" name="quality" data-key data-default /> <input type="range" min="24" max="36" value="32" step="4" name="quality" data-key data-default />
</label> </label>
</div> </div>
<label class="label-input-span">
<input type="checkbox" name="rotate" data-key />
<span data-i18n="rotate-image">Rotate Image</span>
</label>
<label class="label-input-span" data-hide-as="print"> <label class="label-input-span" data-hide-as="print">
<input type="checkbox" name="transparent-as-white" data-key checked /> <input type="checkbox" name="transparent-as-white" data-key checked />
<span data-i18n="transparent-as-white">Transparent as White</span> <span data-i18n="transparent-as-white">Transparent as White</span>
@ -120,7 +124,7 @@
<div id="control-overlay"> <div id="control-overlay">
<div> <div>
<button id="insert-picture" data-i18n="insert-picture" data-key="Enter">Insert Picture</button> <button id="insert-picture" data-i18n="insert-picture" data-key="Enter">Insert Picture</button>
<button id="insert-text" data-i18n="insert-text" data-key="">Insert Text</button> <button id="insert-text" data-i18n="insert-text" data-key="\">Insert Text</button>
<p data-i18n="or-drag-file-to-below" class="hide-on-android">Or drag file to below</p> <p data-i18n="or-drag-file-to-below" class="hide-on-android">Or drag file to below</p>
</div> </div>
</div> </div>
@ -131,7 +135,6 @@
<div class="center buttons"> <div class="center buttons">
<!-- <button id="canvas-expand" data-i18n="expand">Expand</button> <!-- <button id="canvas-expand" data-i18n="expand">Expand</button>
<button id="canvas-crop" data-i18n="crop">Crop</button> --> <button id="canvas-crop" data-i18n="crop">Crop</button> -->
<!-- <button id="button-preview" data-i18n="preview">Preview</button> -->
<button id="button-reset" data-i18n="reset" data-key>Reset</button> <button id="button-reset" data-i18n="reset" data-key>Reset</button>
<button id="button-print" data-i18n="print" data-key=" ">Print</button> <button id="button-print" data-i18n="print" data-key=" ">Print</button>
</div> </div>
@ -141,6 +144,7 @@
<div id="hidden" class="hard-hidden"> <div id="hidden" class="hard-hidden">
<!-- Hidden area for putting elements --> <!-- Hidden area for putting elements -->
<input type="file" id="file" /> <input type="file" id="file" />
<img src="" id="img" alt="hidden-image" />
<div id="accessibility"> <div id="accessibility">
<div> <div>
<h2> <h2>
@ -180,42 +184,37 @@
<div id="text-input"> <div id="text-input">
<h1 data-i18n="insert-text">Insert Text</h1> <h1 data-i18n="insert-text">Insert Text</h1>
<div> <div>
<div id="text-settings"> <div id="text-settings">
<div name="font-and-size" class="text-settings-group"> <div name="font-and-size" class="text-settings-group">
<select id="text-font" contenteditable="true" data-key>
<select id="text-font"> <option value="serif" data-i18n="serif">Serif</option>
<option value="Arial">Arial (sans-serif)</option> <option value="sans-serif" data-i18n="sans-serif" selected>Sans Serif</option>
<option value="Brush Script MT">Brush Script MT (cursive)</option> <option value="monospace" data-i18n="monospace">Monospace</option>
<option value="Courier New">Courier New (monospace)</option> <option value="Unifont" data-i18n="unifont">Unifont</option>
<option value="Garamond">Garamond (serif)</option>
<option value="Georgia">Georgia (serif)</option>
<option value="Helvetica" selected>Helvetica (sans-serif)</option>
<option value="Tahoma">Tahoma (sans-serif)</option>
<option value="Times New Roman">Times New Roman (serif)</option>
<option value="Trebuchet MS">Trebuchet MS (sans-serif)</option>
<option value="Verdana">Verdana (sans-serif)</option>
</select> </select>
<input id="text-size" type="number" name="text-size" min="1" max="100" value="20" data-key style="margin: 0px;"/> <input id="text-size" type="number" name="text-size" min="1" max="100" value="20" data-key style="margin: 0px;"/>
</div> </div>
<div name="wrap-and-align" class="text-settings-group"> <div name="wrap-and-align" class="text-settings-group">
<label data-i18n="wrap-by-space" name="wrap-by-space-label"><input type="checkbox" name="wrap-by-space" data-key checked />Wrap text</label> <label>
<input type="checkbox" name="wrap-words-by-spaces" data-key checked />
<span data-i18n="wrap-words-by-spaces">Wrap words by spaces</span>
</label>
<span class="text-align-container"> <span class="text-align-container">
<input type="radio" name="text-align" value="left" checked/> <input type="radio" name="text-align" value="left" data-key checked />
<span class="text-align-checkmark text-align-left"></span> <span class="text-align-checkmark text-align-left"></span>
</span> </span>
<span class="text-align-container"> <span class="text-align-container">
<input type="radio" name="text-align" value="center"/> <input type="radio" name="text-align" value="center" data-key />
<span class="text-align-checkmark text-align-center"></span> <span class="text-align-checkmark text-align-center"></span>
</span> </span>
<span class="text-align-container"> <span class="text-align-container">
<input type="radio" name="text-align" value="right"/> <input type="radio" name="text-align" value="right" data-key />
<span class="text-align-checkmark text-align-right"></span> <span class="text-align-checkmark text-align-right"></span>
</span> </span>
</div> </div>
</div> </div>
<div id="text-textarea"> <div id="text-textarea">
<textarea id="insert-text-area"></textarea> <textarea id="insert-text-area" data-key="/"></textarea>
</div> </div>
</div> </div>
</div> </div>

View File

@ -52,7 +52,6 @@
"supported-models-": "Unterstützte Modelle:", "supported-models-": "Unterstützte Modelle:",
"path-to-input-file-dash-for-stdin": "Pfad zur Datei. '-' für stdin", "path-to-input-file-dash-for-stdin": "Pfad zur Datei. '-' für stdin",
"scan-for-specified-seconds": "Suchlauf für die angegebenen Sekunden", "scan-for-specified-seconds": "Suchlauf für die angegebenen Sekunden",
"dump-the-traffic": "Den Datenverkehr auf dem Drucker ausgeben und PBM-Bild beim Textdruck",
"text-printing-mode": "Textdruckmodus", "text-printing-mode": "Textdruckmodus",
"please-install-pyobjc-via-pip": "Bitte installieren Sie `pyobjc` über pip", "please-install-pyobjc-via-pip": "Bitte installieren Sie `pyobjc` über pip",
"please-install-bleak-via-pip": "Bitte installieren Sie `bleak` über pip", "please-install-bleak-via-pip": "Bitte installieren Sie `bleak` über pip",
@ -94,7 +93,7 @@
"contributors": "Beitragende", "contributors": "Beitragende",
"developer": "Entwickler", "developer": "Entwickler",
"translator": "Übersetzer", "translator": "Übersetzer",
"all-testers-and-users": "All Tester & Benutzer", "all-users-and-developers": "All Tester & Benutzer",
"everyone-is-awesome": "Jeder ist super", "everyone-is-awesome": "Jeder ist super",
"license": "Lizenz", "license": "Lizenz",
"exiting": "Exiting…", "exiting": "Exiting…",
@ -107,5 +106,5 @@
"text-font": "Schriftart", "text-font": "Schriftart",
"text-size": "Textgröße", "text-size": "Textgröße",
"enter-text": "Text eingeben", "enter-text": "Text eingeben",
"wrap-by-space": "Autom. Zeilenumbruch" "wrap-words-by-spaces": "Autom. Zeilenumbruch"
} }

View File

@ -91,7 +91,7 @@
"contributors": "Contributors", "contributors": "Contributors",
"developer": "Developer", "developer": "Developer",
"translator": "Translator", "translator": "Translator",
"all-testers-and-users": "All testers & users", "all-users-and-developers": "All users & developers",
"everyone-is-awesome": "Everyone is awesome!", "everyone-is-awesome": "Everyone is awesome!",
"license": "License", "license": "License",
"exiting": "Exiting…", "exiting": "Exiting…",
@ -132,5 +132,10 @@
"text-font": "Font", "text-font": "Font",
"text-size": "Size", "text-size": "Size",
"enter-text": "Enter text", "enter-text": "Enter text",
"wrap-by-space": "Wrap words by spaces" "wrap-words-by-spaces": "Wrap words by spaces",
"minor-tweaks": "Minor Tweaks",
"serif": "Serif",
"sans-serif": "Sans Serif",
"monospace": "Monospace",
"rotate-image": "Rotate Image"
} }

View File

@ -1,5 +1,6 @@
{ {
"en-US": "English (US)", "en-US": "English (US)",
"zh-CN": "中文(简体)", "zh-CN": "中文(简体)",
"de-DE": "Deutsch" "de-DE": "Deutsch",
"lolcat": "LOLCAT"
} }

136
www/lang/lolcat.json Normal file
View File

@ -0,0 +1,136 @@
{
"$language": "LOLCAT",
"cat-printer": "KITTE PAWS 🐾",
"printer": "PAWS",
"device-": "KITTE>",
"refresh": "FIND",
"mode-": "HOW>",
"canvas": "VIEW",
"document": "DOC",
"insert-picture": "PUT CAT PIC",
"insert-text": "SAY MEOW",
"help": "HALP",
"javascript-license-information": "BROWSR JUNK",
"settings": "CONFIGUR",
"image": "PIC",
"threshold-": "BLAK OR WHITE",
"transparent-as-white": "DONT POLUT PIC",
"misc": "OTHR",
"system": "SYS",
"disable-animation": "NO MOTION",
"exit": "BAK TO HOM",
"error-message": "SOMTHIN WRONG",
"preview": "LOOK",
"print": "PAW STEP",
"expand": "MORE",
"crop": "LESS",
"scanning-for-devices": "LUKIN FOR KITTEZ",
"scan-time-": "HOW LONG TO FIND",
"-seconds": "SECS",
"no-available-devices-found": "NO KITTE FOUND",
"found-0-available-devices": {
"single": "THER IS {0} KITTE",
"multiple": "THER R {0} KITTEZ"
},
"please-check-if-the-printer-is-down": "CHEK IF KITTE IS SLIPIN",
"printing": "KITTE WALKIN…",
"finished": "K BYE!!",
"coming-soon": "COMIN SOON…",
"dry-run": "DONT STEP WITH INK",
"dry-run-test-print-process-only": "WONT REILLY STEP NOW",
"you-can-close-this-page-manually": "KITTE SLEP YA MAY GO",
"please-enable-bluetooth": "OPEN YA BLUETOOTH PLZ",
"error-happened-please-check-error-message": "SOMTHIN WRONG CHK ERR LOG PLZ",
"you-can-seek-for-help-with-detailed-info-below": "ASK OTHR KITTE WITH THEZ",
"or-try-to-scan-longer": "TRY TO FIND BIT LONGER",
"print-to-cat-printer": "PAWS TO STEP ON PAPR OF KITTE PRINTER!",
"supported-models-": "KNOWN KITTEZ>",
"path-to-input-file-dash-for-stdin": "WHAT FILE TO STEP '-' MEAN STDIN",
"please-install-pyobjc-via-pip": "INSTAL `pyobjc` VIA pip",
"please-install-bleak-via-pip": "INSTAL `bleak` VIA pip",
"folder-printer_lib-is-incomplete-or-missing-please-check": "DIR `printer_lib` HAV PROBLM CHK PLZ",
"input-is-not-pbm-image": "INPUT ISNT PBM PIC",
"unsuitable-image-width-expected-0-got-1": "WRONG PIC WIDZ {0} WANT {1}",
"broken-pbm-image": "BAD PBM PIC",
"input-is-not-text-file": "SAY MEOW MEOW NOT PIC",
"match-printer-with-this-name-or-address": "PICK TIS KITTE NAM,ADDR",
"virtual-run-on-specified-model": "DREAM ABOUT TIS KITTE YA DONT HAVE",
"font-size-0": "PAW BIG AS {0}",
"stopping": "LEAVIN",
"connecting": "IM COMIN",
"model-0-is-not-supported-yet": "KITTE '{0}' UNKNON",
"invalid-address-0": "THER ISNT A KITTE AT '{0}'",
"will-listen-on-all-addresses": "GATHER CONN ON ALL ADDRS",
"serving-at-0": "IM AT {0}",
"disconnecting-from-printer": "LEAVIN KITTE",
"connected-to-0-1": "PICKD TIS {0} {1}",
"flip-horizontally": "FLIP <>",
"flip-vertically": "FLIP ^v",
"dump-traffic": "WACH KITTE PAWS",
"right-to-left-text-order": "YA READ RTL",
"auto-wrap-line": "WRAP MEOW",
"process-as-": "HOW TO STEP>",
"text": "MEOW",
"picture": "PICS",
"pattern": "SPOTS",
"large-font": "BIGGR PAW",
"accessibility": "IM SPECIAL",
"language": "WHAT YA SAY",
"layout": "WHAT YA LOOK",
"ok": "GO",
"cancel": "BACK",
"yes": "YEAH",
"no": "NOPE",
"about": "KITTE INFO",
"home-page-": "CAT HOME>",
"contributors": "GOOD PEEPL",
"developer": "H4CKR",
"translator": "LOLCAT",
"all-users-and-developers": "ALL LITL N BIG CATS",
"everyone-is-awesome": "YA LL AWSOM!!",
"license": "SERIOS THINY",
"exiting": "STOPIN…",
"dark-theme": "BLAK EYES",
"high-contrast": "WEAK EYES",
"welcome": "HAI THER!!",
"copyright-and-license": "SERIOS THINY",
"some-rights-reserved": "Some rights reserved.",
"ENTER": "ENTR",
"SPACE": "SPAC",
"ESCAPE": "ESC",
"TAB": "TAB",
"COMMA": "COMA",
"DOT": "DOT",
"to-enter-keyboard-mode-press-tab": "KEYBORD G33KS PRES TAB",
"usage-": "HOW TO USE>",
"positional-arguments-": "ARGS>",
"options-": "OPTS>",
"show-this-help-message": "SHOW WHAT YA LOOKIN NOW",
"do-nothing": "SLEEPY KITTE",
"scan-for-a-printer": "FIND A KITTE",
"text-printing-mode-with-options": "NO STEP PIC WANT MEOW",
"image-printing-options": "HOW TO STEP A PIC",
"convert-input-image-with-imagemagick": "HLP WITH ImageMagick",
"reset-configuration-": "RLY SCREW CFG?",
"brightness-": "BLAK OR WHITE>",
"text-printing-mode": "MEOW MEOW",
"internal-error-please-see-terminal": "ERR IN CONSOL PLZ SI",
"control-printer-thermal-strength": "HOW MUCH INK",
"strength-": "HOW MUCH INK>",
"or-drag-file-to-below": "THRW PIC OR MEOW HERE",
"reset": "STRT OVR",
"cat-face-toward": "WANT KITTE FACE NOT BUTT",
"quality-": "CAREFUL STEP>",
"print-quality": "HOW CAREFUL R STEPS",
"show-more-options": "LOT CFGS HERE",
"text-font": "PAW SHAP",
"text-size": "BIG OR SMAL",
"enter-text": "MEOW HERE",
"wrap-words-by-spaces": "NO HAF A WORD",
"minor-tweaks": "LITTL TRIKS",
"serif": "SHARP PAW",
"sans-serif": "SOFT PAW",
"monospace": "H4CKY PAW",
"rotate-image": "ROLL PIC"
}

View File

@ -8,14 +8,12 @@
"canvas": "画布", "canvas": "画布",
"document": "文档", "document": "文档",
"insert-picture": "插入图片", "insert-picture": "插入图片",
"insert-text": "输入文本",
"help": "帮助", "help": "帮助",
"javascript-license-information": "JavaScript 许可证信息", "javascript-license-information": "JavaScript 许可证信息",
"settings": "设置", "settings": "设置",
"image": "图像",
"threshold-": "阈值:", "threshold-": "阈值:",
"transmission-speed-": "传输速度:",
"low": "低",
"moderate": "适中",
"high": "高",
"transparent-as-white": "透明为白色", "transparent-as-white": "透明为白色",
"misc": "杂项", "misc": "杂项",
"system": "系统", "system": "系统",
@ -45,7 +43,6 @@
"print-to-cat-printer": "打印到猫咪打印机。", "print-to-cat-printer": "打印到猫咪打印机。",
"supported-models-": "支持的型号:", "supported-models-": "支持的型号:",
"path-to-input-file-dash-for-stdin": "输入文件的位置。使用 '-' 作为标准输入", "path-to-input-file-dash-for-stdin": "输入文件的位置。使用 '-' 作为标准输入",
"dump-the-traffic": "转储数据",
"please-install-pyobjc-via-pip": "请从 pip 安装 `pyobjc`", "please-install-pyobjc-via-pip": "请从 pip 安装 `pyobjc`",
"please-install-bleak-via-pip": "请从 pip 安装 `bleak`", "please-install-bleak-via-pip": "请从 pip 安装 `bleak`",
"folder-printer_lib-is-incomplete-or-missing-please-check": "文件夹 `printer_lib` 不完整或丢失,请检查", "folder-printer_lib-is-incomplete-or-missing-please-check": "文件夹 `printer_lib` 不完整或丢失,请检查",
@ -85,7 +82,7 @@
"contributors": "贡献者", "contributors": "贡献者",
"developer": "开发者", "developer": "开发者",
"translator": "翻译", "translator": "翻译",
"all-testers-and-users": "所有测试及正式用户", "all-users-and-developers": "每位用户及开发者",
"everyone-is-awesome": "每个人都是好样的!", "everyone-is-awesome": "每个人都是好样的!",
"license": "许可证", "license": "许可证",
"exiting": "退出中……", "exiting": "退出中……",
@ -122,5 +119,14 @@
"cat-face-toward": "猫脸朝上", "cat-face-toward": "猫脸朝上",
"quality-": "质量:", "quality-": "质量:",
"print-quality": "打印质量", "print-quality": "打印质量",
"show-more-options": "显示更多选项" "show-more-options": "显示更多选项",
"text-font": "字体",
"text-size": "大小",
"enter-text": "在此处输入文本",
"wrap-words-by-spaces": "空格处换行(不建议用于汉语)",
"minor-tweaks": "小优化",
"serif": "衬线字体",
"sans-serif": "无衬线字体",
"monospace": "等宽字体",
"rotate-image": "旋转图像"
} }

View File

@ -36,6 +36,9 @@ body {
margin: 1em 0; margin: 1em 0;
user-select: none; user-select: none;
} }
* {
box-sizing: border-box;
}
body.android .hide-on-android { body.android .hide-on-android {
display: none; display: none;
} }
@ -90,9 +93,12 @@ input[type="number"], input[type="text"] {
cursor: text; cursor: text;
} }
button:hover { button:hover {
/*
margin: 0; margin: 0;
padding: var(--span) var(--span-double); padding: var(--span) var(--span-double);
min-width: calc(6em + var(--span-double)); min-width: calc(6em + var(--span-double));
*/
transform: scale(1.1);
} }
button:active { button:active {
box-shadow: 0 0 var(--span) inset var(--fore-color); box-shadow: 0 0 var(--span) inset var(--fore-color);
@ -103,7 +109,6 @@ button:active {
} }
.label-span-input { .label-span-input {
display: table-row; display: table-row;
box-sizing: border-box;
margin: var(--span-half) var(--span); margin: var(--span-half) var(--span);
} }
.label-span-input>:nth-child(1) { .label-span-input>:nth-child(1) {
@ -119,7 +124,6 @@ button:active {
} }
.label-input-span { .label-input-span {
display: block; display: block;
box-sizing: border-box;
margin: var(--span-half) var(--span); margin: var(--span-half) var(--span);
} }
.label-input-span>:nth-child(1) { .label-input-span>:nth-child(1) {
@ -173,11 +177,14 @@ main, header, footer {
max-width: 45em; max-width: 45em;
margin: 1em auto; margin: 1em auto;
display: flex; display: flex;
justify-content: space-between;
flex-direction: row; flex-direction: row;
overflow-x: hidden;
} }
.canvas-side { .canvas-side {
flex-grow: 0; flex-grow: 0;
min-width: calc(var(--paper-width) + var(--border-double) + var(--span-double)); width: var(--paper-width);
margin: var(--span) calc((50% - var(--paper-width)) / 2);
} }
.canvas-side>* { .canvas-side>* {
text-align: center; text-align: center;
@ -188,8 +195,9 @@ main, header, footer {
top: 0; top: 0;
height: 100%; height: 100%;
/* overflow: auto; */ /* overflow: auto; */
margin: var(--span); margin: var(--span) 0;
min-width: 12em; min-width: 20em;
/* width: 50%; */
} }
.menu-side>.menu { .menu-side>.menu {
border: var(--border) solid var(--fore-color); border: var(--border) solid var(--fore-color);
@ -216,6 +224,7 @@ main, header, footer {
margin: 0; margin: 0;
} }
.compact-button:hover { .compact-button:hover {
transform: unset;
margin: 0; margin: 0;
padding: 0 var(--span) calc(var(--span-double)); padding: 0 var(--span) calc(var(--span-double));
min-width: 6em; min-width: 6em;
@ -249,9 +258,9 @@ main, header, footer {
position: absolute; position: absolute;
z-index: 2; z-index: 2;
display: inline-block; display: inline-block;
width: calc(var(--paper-width) + var(--border-double)); /* width: calc(var(--paper-width) + var(--border-double)); */
margin: var(--span); width: var(--paper-width);
margin-bottom: 0; margin-top: 0.5em;
} }
p { p {
margin: var(--span) 0; margin: var(--span) 0;
@ -263,7 +272,6 @@ p {
.panel.active { .panel.active {
height: calc(var(--panel-height) - var(--compact-menu-height)); height: calc(var(--panel-height) - var(--compact-menu-height));
padding: var(--span-double) var(--span); padding: var(--span-double) var(--span);
box-sizing: border-box;
overflow-y: auto; overflow-y: auto;
} }
.panel.sub.active { .panel.sub.active {
@ -313,6 +321,11 @@ table#jslicense-labels1 {
} }
table#jslicense-labels1 td { table#jslicense-labels1 td {
padding: var(--span-half) var(--span); padding: var(--span-half) var(--span);
white-space: nowrap;
}
table#jslicense-labels1 td:nth-child(4) {
white-space: normal;
width: 50vw;
} }
*:target { *:target {
background-color: var(--target-color); background-color: var(--target-color);
@ -335,7 +348,7 @@ hr {
} }
iframe#frame { iframe#frame {
width: 100%; width: 100%;
height: 12em; height: 60vh;
border: none; border: none;
background-color: transparent; background-color: transparent;
} }
@ -355,33 +368,32 @@ iframe#frame {
#dialog { #dialog {
position: fixed; position: fixed;
width: 100%; width: 100%;
top: 16%; height: 100%;
top: 0;
text-align: center; text-align: center;
z-index: 2; z-index: 2;
opacity: 2; opacity: 1;
transition: opacity var(--anim-time) var(--curve); transition: opacity var(--anim-time) var(--curve);
} }
#dialog>.content { #dialog>.content {
max-width: 100%; max-width: 100%;
width: 40em; width: 42em;
margin: auto; max-height: 80vh;
box-sizing: border-box; margin: 12vh auto;
border: var(--border) solid var(--fore-color); border: var(--border) solid var(--fore-color);
transition: transform var(--anim-time) var(--curve); transition: transform var(--anim-time) var(--curve);
transform-origin: center 33%; transform-origin: center 33%;
} }
#dialog.hidden { #dialog.hidden {
opacity: 0; opacity: 0;
height: unset;
} }
#dialog.hidden>.content { #dialog.hidden>.content {
transform: scaleY(0); transform: scaleY(0);
} }
#dialog-content { #dialog-content {
/* height: 12em; */ max-height: 60vh;
max-height: 640px;
margin: auto; margin: auto;
padding: var(--span); padding: var(--span-double);
padding-bottom: 0; padding-bottom: 0;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -405,12 +417,12 @@ iframe#frame {
flex-grow: 1; flex-grow: 1;
} }
#select-language { #select-language {
width: calc(100% - var(--span-double)); /* width: calc(100% - var(--span-double)); */
width: 12em;
height: 8em; height: 8em;
border: var(--border) solid var(--fore-color); border: var(--border) solid var(--fore-color);
padding: var(--span); padding: var(--span);
margin: 0 var(--span); margin: 0 var(--span);
box-sizing: border-box;
} }
#select-language option { #select-language option {
cursor: pointer; cursor: pointer;
@ -438,7 +450,7 @@ iframe#frame {
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
text-align: center; text-align: center;
z-index: 1; z-index: 3;
opacity: 1; opacity: 1;
transition: opacity var(--anim-time) var(--curve); transition: opacity var(--anim-time) var(--curve);
} }
@ -467,7 +479,7 @@ iframe#frame {
height: var(--font-size); height: var(--font-size);
margin: var(--font-size); margin: var(--font-size);
background-color: var(--fore-color); background-color: var(--fore-color);
border-radius: calc(var(--font-size) / 2); border-radius: var(--font-size);
animation: jump 1s ease 0s infinite; animation: jump 1s ease 0s infinite;
} }
#loading-screen>.dots>span:nth-child(1) { animation-delay: 0s; } #loading-screen>.dots>span:nth-child(1) { animation-delay: 0s; }
@ -519,6 +531,7 @@ a {
#title { display: none; } #title { display: none; }
.canvas-side { .canvas-side {
min-width: unset; min-width: unset;
margin: 0;
width: 100%; width: 100%;
overflow-x: hidden; overflow-x: hidden;
overflow-y: auto; overflow-y: auto;
@ -530,7 +543,7 @@ a {
.canvas-side>.buttons, .canvas-side>.buttons,
.menu-side>.buttons { .menu-side>.buttons {
position: sticky; position: sticky;
bottom: var(--compact-menu-height); bottom: var(--span);
width: 100%; width: 100%;
z-index: 1; z-index: 1;
} }
@ -570,6 +583,9 @@ a {
width: 100%; width: 100%;
z-index: 0; z-index: 0;
} }
#dialog-content {
padding: var(--span) 0;
}
.blank { .blank {
height: var(--compact-menu-height); height: var(--compact-menu-height);
} }
@ -656,19 +672,22 @@ body.high-contrast #notice * { border: var(--border) dashed var(--fore-color); }
body.high-contrast a:any-link { color: #00f; } body.high-contrast a:any-link { color: #00f; }
body.high-contrast #control-overlay { background-color: var(--shade); } body.high-contrast #control-overlay { background-color: var(--shade); }
/*
@font-face { @font-face {
font-family: 'Unifont'; font-family: 'Unifont';
src: local('Unifont') url('unifont.ttf') url('unifont.otf'); src: local('Unifont') url('unifont.ttf') url('unifont.otf');
} }
*/
#insert-text-area { #insert-text-area {
white-space: pre; white-space: pre;
height: 50vh; height: calc(60vh - 8em);
width: var(--paper-width); width: var(--paper-width);
overflow: hidden auto; overflow: hidden auto;
white-space: break-spaces; white-space: break-spaces;
resize: none; resize: none;
padding-top: .65ex; padding-top: .65ex;
line-height: 1.25; line-height: 1.25;
border: var(--border) solid currentColor;
} }
.text-align-container span { .text-align-container span {
@ -703,7 +722,7 @@ body.high-contrast #control-overlay { background-color: var(--shade); }
.text-align-center { background-image: url("icons/text-center.svg")} .text-align-center { background-image: url("icons/text-center.svg")}
.text-align-right { background-image: url("icons/text-right.svg")} .text-align-right { background-image: url("icons/text-right.svg")}
input[name="wrap-by-space"] { margin-right: 5px; } input[name="wrap-words-by-spaces"] { margin-right: 5px; }
#text-settings { #text-settings {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -715,5 +734,5 @@ input[name="wrap-by-space"] { margin-right: 5px; }
display: flex; display: flex;
justify-content: center; justify-content: center;
} }
label[name="wrap-by-space-label"] { padding-right: 2%; } label[name="wrap-words-by-spaces-label"] { padding-right: 2%; }
#text-font { height: 100%; margin: 0px; } #text-font { height: 100%; margin: 0px; }

View File

@ -274,32 +274,38 @@ class CanvasController {
canvas; canvas;
imageUrl; imageUrl;
algorithm; algorithm;
_height; algoElements;
_threshold; textFont;
_energy; textSize;
_thresholdRange; textArea;
_energyRange;
transparentAsWhite; transparentAsWhite;
previewData; previewData;
rotate;
#height;
#threshold;
#energy;
#thresholdRange;
#energyRange;
#rotateCheck;
static defaultHeight = 384; static defaultHeight = 384;
static defaultThreshold = 256 / 3; static defaultThreshold = 256 / 3;
get threshold() { get threshold() {
return this._threshold; return this.#threshold;
} }
set threshold(value) { set threshold(value) {
this._threshold = this._thresholdRange.value = value; this.#threshold = this.#thresholdRange.value = value;
} }
get energy() { get energy() {
return this._energy; return this.#energy;
} }
set energy(value) { set energy(value) {
this._energy = this._energyRange.value = value; this.#energy = this.#energyRange.value = value;
} }
get height() { get height() {
return this._height; return this.#height;
} }
set height(value) { set height(value) {
this.canvas.height = this.preview.height = this._height = value; this.canvas.height = this.preview.height = this.#height = value;
} }
constructor() { constructor() {
this.preview = document.getElementById('preview'); this.preview = document.getElementById('preview');
@ -308,13 +314,13 @@ class CanvasController {
this.textSize = document.getElementById("text-size"); this.textSize = document.getElementById("text-size");
this.textFont = document.getElementById("text-font"); this.textFont = document.getElementById("text-font");
this.textArea = document.getElementById("insert-text-area"); this.textArea = document.getElementById("insert-text-area");
this.wrapBySpace = document.querySelector('input[name="wrap-by-space"]'); this.wrapBySpace = document.querySelector('input[name="wrap-words-by-spaces"]');
this.textAlgorithm = document.querySelector('input[name="algo"][value="algo-direct"]');
this.height = CanvasController.defaultHeight; this.height = CanvasController.defaultHeight;
this._thresholdRange = document.querySelector('[name="threshold"]'); this.#thresholdRange = document.querySelector('[name="threshold"]');
this._energyRange = document.querySelector('[name="energy"]'); this.#energyRange = document.querySelector('[name="energy"]');
this.imageUrl = null; this.imageUrl = null;
this.textAlign = "left"; this.textAlign = "left";
this.rotate = false;
for (let elem of document.querySelectorAll("input[name=text-align]")){ for (let elem of document.querySelectorAll("input[name=text-align]")){
if (elem.checked) { this.textAlign = elem.value; } if (elem.checked) { this.textAlign = elem.value; }
@ -336,7 +342,7 @@ class CanvasController {
}; };
file_reader.readAsText(event.dataTransfer.files[0]); file_reader.readAsText(event.dataTransfer.files[0]);
} else { } else {
this.insertPicture(event.dataTransfer.files); this.useFiles(event.dataTransfer.files);
} }
return prevent_default(event); return prevent_default(event);
}); });
@ -345,8 +351,10 @@ class CanvasController {
this.textArea.style["font-family"] = this.textFont.value; this.textArea.style["font-family"] = this.textFont.value;
this.textArea.style["word-break"] = this.wrapBySpace.checked ? "break-word" : "break-all"; this.textArea.style["word-break"] = this.wrapBySpace.checked ? "break-word" : "break-all";
this.algoElements = document.querySelectorAll('input[name="algo"]');
putEvent('input[name="algo"]', 'change', (event) => this.useAlgorithm(event.currentTarget.value), this); putEvent('input[name="algo"]', 'change', (event) => this.useAlgorithm(event.currentTarget.value), this);
putEvent('#insert-picture' , 'click', () => this.insertPicture(), this); putEvent('#insert-picture' , 'click', () => this.useFiles(), this);
putEvent('#insert-text' , 'click', () => Dialog.alert("#text-input", () => this.insertText(this.textArea.value))); putEvent('#insert-text' , 'click', () => Dialog.alert("#text-input", () => this.insertText(this.textArea.value)));
putEvent('#text-size' , 'change', () => this.textArea.style["font-size"] = this.textSize.value + "px"); putEvent('#text-size' , 'change', () => this.textArea.style["font-size"] = this.textSize.value + "px");
putEvent('#text-font' , 'change', () => this.textArea.style["font-family"] = this.textFont.value); putEvent('#text-font' , 'change', () => this.textArea.style["font-family"] = this.textFont.value);
@ -354,11 +362,13 @@ class CanvasController {
this.textAlign = event.currentTarget.value this.textAlign = event.currentTarget.value
this.textArea.style["text-align"] = this.textAlign; this.textArea.style["text-align"] = this.textAlign;
}, this); }, this);
putEvent('input[name="wrap-by-space"]' , 'change', () => this.textArea.style["word-break"] = this.wrapBySpace.checked ? "break-word" : "break-all"); putEvent('input[name="wrap-words-by-spaces"]' , 'change', () => this.textArea.style["word-break"] = this.wrapBySpace.checked ? "break-word" : "break-all");
putEvent('#button-preview' , 'click', this.activatePreview , this); putEvent('#button-preview' , 'click', this.activatePreview , this);
putEvent('#button-reset' , 'click', this.reset , this); putEvent('#button-reset' , 'click', this.reset , this);
putEvent('#canvas-expand' , 'click', this.expand , this); putEvent('#canvas-expand' , 'click', this.expand , this);
putEvent('#canvas-crop' , 'click', this.crop , this); putEvent('#canvas-crop' , 'click', this.crop , this);
putEvent('[name="rotate"]' , 'change', e => this.setRotate(e.currentTarget.checked), this);
this.#rotateCheck = document.querySelector('[name="rotate"]');
putEvent('[name="threshold"]', 'change', (event) => { putEvent('[name="threshold"]', 'change', (event) => {
this.threshold = parseInt(event.currentTarget.value); this.threshold = parseInt(event.currentTarget.value);
@ -374,17 +384,27 @@ class CanvasController {
}, this); }, this);
} }
useAlgorithm(name) { useAlgorithm(name) {
for (let e of this.algoElements) {
e.checked = e.value === name;
}
this.algorithm = name; this.algorithm = name;
this.threshold = CanvasController.defaultThreshold; this.threshold = CanvasController.defaultThreshold;
this._thresholdRange.dispatchEvent(new Event('change')); this.#thresholdRange.dispatchEvent(new Event('change'));
this.energy = name == 'algo-direct' ? 96 : 64; this.energy = name == 'algo-direct' ? 96 : 64;
this._energyRange.dispatchEvent(new Event('change')); this.#energyRange.dispatchEvent(new Event('change'));
this.activatePreview(); this.activatePreview();
} }
expand(length = CanvasController.defaultHeight) { expand(length = CanvasController.defaultHeight) {
this.height += length; this.height += length;
} }
crop() {} crop() {
// STUB
}
setRotate(value) {
this.#rotateCheck.checked = value;
this.rotate = value;
if (this.imageUrl !== null) this.putImage(this.imageUrl);
}
visualEnergy(amount) { visualEnergy(amount) {
let rate = amount / 256; let rate = amount / 256;
let brightness = Math.max(1.6 - rate * 1.5, 0.75), let brightness = Math.max(1.6 - rate * 1.5, 0.75),
@ -437,32 +457,55 @@ class CanvasController {
this.previewData = mono_data; this.previewData = mono_data;
context_p.putImageData(new_data, 0, 0); context_p.putImageData(new_data, 0, 0);
} }
insertPicture(files) { putImage(url) {
const put_image = (url) => { let img = document.getElementById('img');
this.imageUrl = url;
let img = document.createElement('img');
img.src = url; img.src = url;
hidden_area.appendChild(img);
img.addEventListener('load', () => { img.addEventListener('load', () => {
let canvas = this.canvas; let canvas = this.canvas;
let rate = img.height / img.width; let ctx = canvas.getContext('2d');
this.height = canvas.width * rate; if (this.rotate) {
let context = canvas.getContext('2d'); let intermediate_canvas = document.createElement('canvas');
context.drawImage(img, 0, 0, canvas.width, canvas.height); /**
* w h
* +------+ +---+
* h | | | | w
* +------+ | | intermediate_canvas
* canvas +---+
*/
let w = canvas.width;
let h = this.height = Math.floor(canvas.width * img.width / img.height);
intermediate_canvas.width = h;
intermediate_canvas.height = w;
let i_ctx = intermediate_canvas.getContext('2d');
i_ctx.drawImage(img, 0, 0, h, w);
let i_data = i_ctx.getImageData(0, 0, h, w);
let data = ctx.createImageData(w, h);
for (let j = 0; j < h; j++) {
for (let i = 0; i < w; i++) {
for (let d = 0; d < 4; d++)
data.data[(i * 4 + d) + (j * w * 4)] = i_data.data[(j * 4 + d) + ((w - i) * 4 * h)];
}
}
ctx.putImageData(data, 0, 0);
} else {
this.height = Math.floor(canvas.width * img.height / img.width);
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
}
this.crop(); this.crop();
this.activatePreview(); this.activatePreview();
hint('#button-print'); hint('#button-print');
}); });
} }
let use_files = (files) => { useFiles(files) {
const use_files = (files) => {
let file = files[0]; let file = files[0];
if (!file) return; if (!file) return;
let url = URL.createObjectURL(file); let url = URL.createObjectURL(file);
put_image(url); this.imageUrl = url;
this.putImage(url);
this.controls.classList.add('hidden'); this.controls.classList.add('hidden');
} }
if (files) use_files(files); if (files) { use_files(files); return; }
else {
document.querySelectorAll('.dummy').forEach(e => e.remove()); document.querySelectorAll('.dummy').forEach(e => e.remove());
let input = document.createElement('input'); let input = document.createElement('input');
input.classList.add('dummy'); input.classList.add('dummy');
@ -474,9 +517,8 @@ class CanvasController {
hidden_area.appendChild(input); hidden_area.appendChild(input);
input.click(); input.click();
} }
}
insertText(text) { insertText(text) {
if (text == null || text.trim() == "") { return; } if (text == null || text.trim() == "") { return false; }
const text_size = parseInt(this.textSize.value); const text_size = parseInt(this.textSize.value);
const text_font = this.textFont.value; const text_font = this.textFont.value;
@ -550,11 +592,11 @@ class CanvasController {
this.crop(); this.crop();
this.textAlgorithm.checked = true; this.useAlgorithm('algo-direct');
this.textAlgorithm.dispatchEvent(new Event("change")); this.threshold = 16;
this.imageUrl = this.canvas.toDataURL(); this.setRotate(false);
this.activatePreview(); this.putImage(this.imageUrl = this.canvas.toDataURL());
this.controls.classList.add('hidden'); this.controls.classList.add('hidden');
@ -597,7 +639,7 @@ function applyI18nToDom(doc) {
async function initI18n(current_language) { async function initI18n(current_language) {
if (typeof i18n === 'undefined') return; if (typeof i18n === 'undefined') return;
/** @type {HTMLSelectElement} */ /** @type {HTMLSelectElement} */
let language_options = document.getElementById('select-language'); let language_select = document.getElementById('select-language');
/** @type {{ [code: string]: string }} */ /** @type {{ [code: string]: string }} */
let list = await fetch('/lang/list.json').then(r => r.json()); let list = await fetch('/lang/list.json').then(r => r.json());
let use_language = async (value) => { let use_language = async (value) => {
@ -605,6 +647,7 @@ async function initI18n(current_language) {
i18n.add(value, await fetch(`/lang/${value}.json`).then(r => r.json()), true); i18n.add(value, await fetch(`/lang/${value}.json`).then(r => r.json()), true);
applyI18nToDom(); applyI18nToDom();
} }
language_select.addEventListener('change', () => use_language(language_select.value));
for (let code in list) { for (let code in list) {
let option = document.createElement('option'); let option = document.createElement('option');
option.value = code; option.value = code;
@ -614,22 +657,22 @@ async function initI18n(current_language) {
let option = event.currentTarget; let option = event.currentTarget;
let value = option.value; let value = option.value;
option.selected = true; option.selected = true;
language_options.selectedIndex = option.index; language_select.selectedIndex = option.index;
use_language(value); use_language(value);
Notice.note('welcome'); Notice.note('welcome');
}); });
language_options.appendChild(option); language_select.appendChild(option);
} }
if (!navigator.languages) { if (!navigator.languages) {
if (!navigator.language) return; if (!navigator.language) return;
else navigator.languages = [navigator.language, 'en-US']; else navigator.languages = [navigator.language, 'en-US'];
} }
if (current_language) { if (current_language) {
for (let option of language_options.children) for (let option of language_select.children)
if (option.value === current_language) if (option.value === current_language)
option.click(); option.click();
} else for (let code of navigator.languages) } else for (let code of navigator.languages)
if (list[code]) for (let option of language_options.children) if (list[code]) for (let option of language_select.children)
if (option.value === code) { if (option.value === code) {
option.setAttribute('data-default', ''); option.setAttribute('data-default', '');
if (!current_language) option.click(); if (!current_language) option.click();
@ -698,21 +741,21 @@ class Main {
this.canvasController = new CanvasController(); this.canvasController = new CanvasController();
putEvent('#button-exit' , 'click', () => this.exit(false), this); putEvent('#button-exit' , 'click', () => this.exit(false), this);
putEvent('#button-exit', 'contextmenu',
(event) => (event.preventDefault(), this.exit(true)), this);
putEvent('#button-print' , 'click', this.print, this); putEvent('#button-print' , 'click', this.print, this);
putEvent('#device-refresh' , 'click', this.searchDevices, this); putEvent('#device-refresh' , 'click', this.searchDevices, this);
putEvent('#button-exit' , 'contextmenu', (event) => (event.preventDefault(), this.exit(true)), this);
putEvent('#set-accessibility', 'click', () => Dialog.alert('#accessibility')); putEvent('#set-accessibility', 'click', () => Dialog.alert('#accessibility'));
this.attachSetter('#device-options', 'input', 'printer', this.attachSetter('#device-options', 'input', 'printer',
(value) => callApi('/connect', { device: value }) (value) => callApi('/connect', { device: value })
); );
putEvent('a[target="frame"]', 'click', () => Dialog.alert('#frame')); putEvent('a[target="frame"]', 'click', () => Dialog.alert('#frame'));
this.attachSetter('[name="scan-time"]' , 'change', 'scan_timeout'); this.attachSetter('[name="scan-time"]' , 'change', 'scan_timeout');
this.attachSetter('[name="rotate"]' , 'change', 'rotate');
this.attachSetter('input[name="algo"]' , 'change', 'mono_algorithm', this.attachSetter('input[name="algo"]' , 'change', 'mono_algorithm',
(value) => this.settings['text_mode'] = (value === 'algo-direct') (value) => this.settings['text_mode'] = (value === 'algo-direct')
); );
this.attachSetter('[name="transparent-as-white"]', 'change', 'transparent_as_white'); this.attachSetter('[name="transparent-as-white"]', 'change', 'transparent_as_white');
this.attachSetter('[name="wrap-by-space"]', 'change', 'wrap_by_space'); this.attachSetter('[name="wrap-words-by-spaces"]', 'change', 'wrap_by_space');
this.attachSetter('[name="dry-run"]', 'change', 'dry_run', this.attachSetter('[name="dry-run"]', 'change', 'dry_run',
(checked) => checked && Notice.note('dry-run-test-print-process-only') (checked) => checked && Notice.note('dry-run-test-print-process-only')
); );
@ -735,12 +778,9 @@ class Main {
this.attachSetter('[name="energy"]' , 'change', 'energy'); this.attachSetter('[name="energy"]' , 'change', 'energy');
this.attachSetter('[name="quality"]' , 'change', 'quality'); this.attachSetter('[name="quality"]' , 'change', 'quality');
this.attachSetter('[name="flip"]' , 'change', 'flip'); this.attachSetter('[name="flip"]' , 'change', 'flip');
// this.attachSetter('[name="flip-h"]', 'change', 'flip_h');
// this.attachSetter('[name="flip-v"]', 'change', 'flip_v');
// this.attachSetter('[name="dump"]', 'change', 'dump');
await this.activateConfig(); await this.activateConfig();
// one exception // one exception
this.attachSetter('#select-language option', 'click', 'language'); this.attachSetter('#select-language', 'change', 'language');
if (this.settings['is_android']) { if (this.settings['is_android']) {
document.body.classList.add('android'); document.body.classList.add('android');
@ -856,24 +896,29 @@ class Main {
Notice.note('you-can-close-this-page-manually'); Notice.note('you-can-close-this-page-manually');
} }
/** @param {Response} response */ /** @param {Response} response */
async bluetoothProblemHandler(response) { async handleBluetoothProblem(response) {
// Not complete yet, it's different across other platforms // Not complete yet, it's different across other platforms
let error_details = await response.json(); let error_details = await response.json();
if ( if (
error_details.name === 'org.bluez.Error.NotReady' || error_details.name === 'org.bluez.Error.NotReady' ||
error_details.name === 'org.freedesktop.DBus.Error.UnknownObject' || error_details.name === 'org.freedesktop.DBus.Error.UnknownObject' ||
error_details.name === 'org.bluez.Error.NotReady' ||
error_details.details.includes('not turned on') || error_details.details.includes('not turned on') ||
error_details.details.includes('WinError -2147020577') error_details.details.includes('WinError -2147020577')
) Notice.warn('please-enable-bluetooth'); ) Notice.warn('please-enable-bluetooth');
else if ( else if (
error_details.details.includes('no running event loop') error_details.details.includes('no running event loop')
) Notice.error('internal-error-please-see-terminal'); ) Notice.error('internal-error-please-see-terminal');
else throw new Error('Unknown Bluetooth Problem'); else
ErrorHandler.report(
new Error('API Failure'),
JSON.stringify(await response.json(), undefined, 4)
)
return null; return null;
} }
async searchDevices() { async searchDevices() {
Notice.wait('scanning-for-devices'); Notice.wait('scanning-for-devices');
let search_result = await callApi('/devices', null, this.bluetoothProblemHandler); let search_result = await callApi('/devices', null, this.handleBluetoothProblem);
if (search_result === null) return false; if (search_result === null) return false;
let devices = search_result.devices; let devices = search_result.devices;
Array.from(this.deviceOptions.children).forEach(e => e.remove()); Array.from(this.deviceOptions.children).forEach(e => e.remove());
@ -901,8 +946,7 @@ class Main {
method: 'POST', method: 'POST',
body: this.canvasController.makePbm() body: this.canvasController.makePbm()
}).then(async (response) => { }).then(async (response) => {
if (response.ok) Notice.note('finished') if (response.ok) { Notice.note('finished'); return; }
else {
let json = response.json(); let json = response.json();
response.json = () => json; response.json = () => json;
let error_data = await response.json(); let error_data = await response.json();
@ -913,11 +957,7 @@ class Main {
} else if (error_data.name === 'no-available-devices-found') } else if (error_data.name === 'no-available-devices-found')
Notice.warn('no-available-devices-found'); Notice.warn('no-available-devices-found');
else else
ErrorHandler.report( this.handleBluetoothProblem(response);
new Error('API Failure'),
JSON.stringify(await response.json(), undefined, 4)
)
}
}); });
} }
} }